aboutsummaryrefslogtreecommitdiff
path: root/win32/mingw.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/mingw.c')
-rw-r--r--win32/mingw.c2571
1 files changed, 2571 insertions, 0 deletions
diff --git a/win32/mingw.c b/win32/mingw.c
new file mode 100644
index 000000000..061e7bac6
--- /dev/null
+++ b/win32/mingw.c
@@ -0,0 +1,2571 @@
1#include "libbb.h"
2#include <userenv.h>
3#include "lazyload.h"
4#if ENABLE_FEATURE_EXTRA_FILE_DATA
5#include <aclapi.h>
6#endif
7#include <ntdef.h>
8#include <psapi.h>
9
10#if defined(__MINGW64_VERSION_MAJOR)
11#if ENABLE_GLOBBING
12extern int _setargv(void);
13int _setargv(void)
14{
15 extern int _dowildcard;
16 char *glob;
17
18 _dowildcard = -1;
19 glob = getenv("BB_GLOBBING");
20 if (glob) {
21 if (strcmp(glob, "0") == 0)
22 _dowildcard = 0;
23 }
24 else {
25 setenv("BB_GLOBBING", "0", TRUE);
26 }
27 return 0;
28}
29#else
30int _dowildcard = 0;
31#endif
32
33#undef _fmode
34int _fmode = _O_BINARY;
35#endif
36
37#if !defined(__MINGW64_VERSION_MAJOR)
38#if ENABLE_GLOBBING
39int _CRT_glob = 1;
40#else
41int _CRT_glob = 0;
42#endif
43
44unsigned int _CRT_fmode = _O_BINARY;
45#endif
46
47smallint bb_got_signal;
48static mode_t current_umask = DEFAULT_UMASK;
49
50#pragma GCC optimize ("no-if-conversion")
51int err_win_to_posix(void)
52{
53 int error = ENOSYS;
54 switch(GetLastError()) {
55 case ERROR_ACCESS_DENIED: error = EACCES; break;
56 case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
57 case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
58 case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
59 case ERROR_ALREADY_EXISTS: error = EEXIST; break;
60 case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
61 case ERROR_BAD_COMMAND: error = EIO; break;
62 case ERROR_BAD_DEVICE: error = ENODEV; break;
63 case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
64 case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
65 case ERROR_BAD_FORMAT: error = ENOEXEC; break;
66 case ERROR_BAD_LENGTH: error = EINVAL; break;
67 case ERROR_BAD_PATHNAME: error = ENOENT; break;
68 case ERROR_BAD_NET_NAME: error = ENOENT; break;
69 case ERROR_BAD_NETPATH: error = ENOENT; break;
70 case ERROR_BAD_PIPE: error = EPIPE; break;
71 case ERROR_BAD_UNIT: error = ENODEV; break;
72 case ERROR_BAD_USERNAME: error = EINVAL; break;
73 case ERROR_BROKEN_PIPE: error = EPIPE; break;
74 case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
75 case ERROR_BUSY: error = EBUSY; break;
76 case ERROR_BUSY_DRIVE: error = EBUSY; break;
77 case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
78 case ERROR_CANNOT_MAKE: error = EACCES; break;
79 case ERROR_CANTOPEN: error = EIO; break;
80 case ERROR_CANTREAD: error = EIO; break;
81 case ERROR_CANTWRITE: error = EIO; break;
82 case ERROR_CRC: error = EIO; break;
83 case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
84 case ERROR_DEVICE_IN_USE: error = EBUSY; break;
85 case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
86 case ERROR_DIRECTORY: error = EINVAL; break;
87 case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
88 case ERROR_DISK_CHANGE: error = EIO; break;
89 case ERROR_DISK_FULL: error = ENOSPC; break;
90 case ERROR_DRIVE_LOCKED: error = EBUSY; break;
91 case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
92 case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
93 case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
94 case ERROR_FILE_EXISTS: error = EEXIST; break;
95 case ERROR_FILE_INVALID: error = ENODEV; break;
96 case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
97 case ERROR_GEN_FAILURE: error = EIO; break;
98 case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
99 case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
100 case ERROR_INVALID_ACCESS: error = EACCES; break;
101 case ERROR_INVALID_ADDRESS: error = EFAULT; break;
102 case ERROR_INVALID_BLOCK: error = EFAULT; break;
103 case ERROR_INVALID_DATA: error = EINVAL; break;
104 case ERROR_INVALID_DRIVE: error = ENODEV; break;
105 case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
106 case ERROR_INVALID_FLAGS: error = EINVAL; break;
107 case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
108 case ERROR_INVALID_HANDLE: error = EBADF; break;
109 case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
110 case ERROR_INVALID_NAME: error = EINVAL; break;
111 case ERROR_INVALID_OWNER: error = EINVAL; break;
112 case ERROR_INVALID_PARAMETER: error = EINVAL; break;
113 case ERROR_INVALID_PASSWORD: error = EPERM; break;
114 case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
115 case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
116 case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
117 case ERROR_INVALID_WORKSTATION: error = EACCES; break;
118 case ERROR_IO_DEVICE: error = EIO; break;
119 case ERROR_IO_INCOMPLETE: error = EINTR; break;
120 case ERROR_LOCKED: error = EBUSY; break;
121 case ERROR_LOCK_VIOLATION: error = EACCES; break;
122 case ERROR_LOGON_FAILURE: error = EACCES; break;
123 case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
124 case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
125 case ERROR_MORE_DATA: error = EPIPE; break;
126 case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
127 case ERROR_NOACCESS: error = EFAULT; break;
128 case ERROR_NONE_MAPPED: error = EINVAL; break;
129 case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
130 case ERROR_NOT_READY: error = EAGAIN; break;
131 case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
132 case ERROR_NO_DATA: error = EPIPE; break;
133 case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
134 case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
135 case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
136 case ERROR_OPEN_FAILED: error = EIO; break;
137 case ERROR_OPEN_FILES: error = EBUSY; break;
138 case ERROR_OPERATION_ABORTED: error = EINTR; break;
139 case ERROR_OUTOFMEMORY: error = ENOMEM; break;
140 case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
141 case ERROR_PATH_BUSY: error = EBUSY; break;
142 case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
143 case ERROR_PIPE_BUSY: error = EBUSY; break;
144 case ERROR_PIPE_CONNECTED: error = EPIPE; break;
145 case ERROR_PIPE_LISTENING: error = EPIPE; break;
146 case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
147 case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
148 case ERROR_READ_FAULT: error = EIO; break;
149 case ERROR_SEEK: error = EIO; break;
150 case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
151 case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
152 case ERROR_SHARING_VIOLATION: error = EACCES; break;
153 case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
154 case ERROR_SWAPERROR: error = ENOENT; break;
155 case ERROR_TOO_MANY_LINKS: error = EMLINK; break;
156 case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
157 case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
158 case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
159 case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
160 case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
161 case ERROR_WRITE_FAULT: error = EIO; break;
162 case ERROR_WRITE_PROTECT: error = EROFS; break;
163 case ERROR_CANT_RESOLVE_FILENAME: error = ELOOP; break;
164 }
165 return error;
166}
167#pragma GCC reset_options
168
169#undef strerror
170char *mingw_strerror(int errnum)
171{
172 if (errnum == ELOOP)
173 return (char *)"Too many levels of symbolic links";
174 return strerror(errnum);
175}
176
177char *strsignal(int sig)
178{
179 if (sig == SIGTERM)
180 return (char *)"Terminated";
181 else if (sig == SIGKILL)
182 return (char *)"Killed";
183 return (char *)get_signame(sig);
184}
185
186static int zero_fd = -1;
187static int rand_fd = -1;
188
189/*
190 * Determine if 'filename' corresponds to one of the supported
191 * device files. Constants for these are defined as an enum
192 * in mingw.h.
193 */
194int get_dev_type(const char *filename)
195{
196 if (filename && is_prefixed_with(filename, "/dev/"))
197 return index_in_strings("null\0zero\0urandom\0", filename+5);
198
199 return NOT_DEVICE;
200}
201
202void update_special_fd(int dev, int fd)
203{
204 if (dev == DEV_ZERO)
205 zero_fd = fd;
206 else if (dev == DEV_URANDOM)
207 rand_fd = fd;
208}
209
210#define PREFIX_LEN (sizeof(DEV_FD_PREFIX)-1)
211static int get_dev_fd(const char *filename)
212{
213 int fd;
214
215 if (filename && is_prefixed_with(filename, DEV_FD_PREFIX)) {
216 fd = bb_strtou(filename+PREFIX_LEN, NULL, 10);
217 if (errno == 0 && (HANDLE)_get_osfhandle(fd) != INVALID_HANDLE_VALUE)
218 return fd;
219 }
220 return -1;
221}
222
223static int mingw_is_directory(const char *path);
224#undef open
225int mingw_open (const char *filename, int oflags, ...)
226{
227 va_list args;
228 int pmode, mode = 0666;
229 int fd;
230 int special = (oflags & O_SPECIAL);
231 int dev = get_dev_type(filename);
232
233 /* /dev/null is always allowed, others only if O_SPECIAL is set */
234 if (dev == DEV_NULL || (special && dev != NOT_DEVICE)) {
235 filename = "nul";
236 oflags = O_RDWR;
237 }
238 else if ((fd=get_dev_fd(filename)) >= 0) {
239 return fd;
240 }
241
242 if ((oflags & O_CREAT)) {
243 va_start(args, oflags);
244 mode = va_arg(args, int);
245 va_end(args);
246 }
247
248 pmode = ((mode & S_IWUSR) ? _S_IWRITE : 0) |
249 ((mode & S_IRUSR) ? _S_IREAD : 0);
250
251 fd = open(filename, oflags&~O_SPECIAL, pmode);
252 if (fd >= 0) {
253 update_special_fd(dev, fd);
254 }
255 else if ((oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
256 if (mingw_is_directory(filename))
257 errno = EISDIR;
258 }
259 return fd;
260}
261
262int mingw_xopen(const char *pathname, int flags)
263{
264 int ret;
265
266 /* allow use of special devices */
267 ret = mingw_open(pathname, flags|O_SPECIAL);
268 if (ret < 0) {
269 bb_perror_msg_and_die("can't open '%s'", pathname);
270 }
271 return ret;
272}
273
274ssize_t FAST_FUNC mingw_open_read_close(const char *fn, void *buf, size_t size)
275{
276 /* allow use of special devices */
277 int fd = mingw_open(fn, O_RDONLY|O_SPECIAL);
278 if (fd < 0)
279 return fd;
280 return read_close(fd, buf, size);
281}
282
283#undef fopen
284FILE *mingw_fopen (const char *filename, const char *otype)
285{
286 int fd;
287
288 if (get_dev_type(filename) == DEV_NULL)
289 filename = "nul";
290 else if ((fd=get_dev_fd(filename)) >= 0)
291 return fdopen(fd, otype);
292 return fopen(filename, otype);
293}
294
295#undef read
296ssize_t mingw_read(int fd, void *buf, size_t count)
297{
298 if (fd == zero_fd) {
299 memset(buf, 0, count);
300 return count;
301 }
302 else if (fd == rand_fd) {
303 return get_random_bytes(buf, count);
304 }
305 return read(fd, buf, count);
306}
307
308#undef close
309int mingw_close(int fd)
310{
311 if (fd == zero_fd) {
312 zero_fd = -1;
313 }
314 if (fd == rand_fd) {
315 rand_fd = -1;
316 }
317 return close(fd);
318}
319
320#undef dup2
321int mingw_dup2 (int fd, int fdto)
322{
323 int ret = dup2(fd, fdto);
324 return ret != -1 ? fdto : -1;
325}
326
327/*
328 * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
329 * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
330 */
331static inline long long filetime_to_hnsec(const FILETIME *ft)
332{
333 long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
334 /* Windows to Unix Epoch conversion */
335 return winTime - 116444736000000000LL;
336}
337
338static inline struct timespec filetime_to_timespec(const FILETIME *ft)
339{
340 struct timespec ts;
341 long long winTime = filetime_to_hnsec(ft);
342
343 ts.tv_sec = (time_t)(winTime / 10000000);
344 ts.tv_nsec = (long)(winTime % 10000000) * 100;
345
346 return ts;
347}
348
349static inline mode_t file_attr_to_st_mode(DWORD attr)
350{
351 mode_t fMode = S_IRUSR|S_IRGRP|S_IROTH;
352 if (attr & FILE_ATTRIBUTE_DIRECTORY)
353 fMode |= (S_IFDIR|S_IRWXU|S_IRWXG|S_IRWXO) & ~(current_umask & 0022);
354 else if (attr & FILE_ATTRIBUTE_DEVICE)
355 fMode |= S_IFCHR|S_IWUSR|S_IWGRP|S_IWOTH;
356 else
357 fMode |= S_IFREG;
358 if (!(attr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_DEVICE)))
359 fMode |= (S_IWUSR|S_IWGRP|S_IWOTH) & ~(current_umask & 0022);
360 return fMode;
361}
362
363static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
364{
365 char *want_dir;
366
367 if (get_dev_type(fname) == DEV_NULL || get_dev_fd(fname) >= 0) {
368 /* Fake attributes for special devices */
369 /* Though not /dev/zero or /dev/urandom */
370 FILETIME epoch = {0xd53e8000, 0x019db1de}; // Unix epoch as FILETIME
371 fdata->dwFileAttributes = FILE_ATTRIBUTE_DEVICE;
372 fdata->ftCreationTime = fdata->ftLastAccessTime =
373 fdata->ftLastWriteTime = epoch;
374 fdata->nFileSizeHigh = fdata->nFileSizeLow = 0;
375 return 0;
376 }
377
378 want_dir = last_char_is_dir_sep(fname);
379 if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata)) {
380 if (!(fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && want_dir)
381 return ENOTDIR;
382 fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_DEVICE;
383 return 0;
384 }
385
386 if (GetLastError() == ERROR_SHARING_VIOLATION) {
387 HANDLE hnd;
388 WIN32_FIND_DATA fd;
389
390 if ((hnd=FindFirstFile(fname, &fd)) != INVALID_HANDLE_VALUE) {
391 fdata->dwFileAttributes =
392 fd.dwFileAttributes & ~FILE_ATTRIBUTE_DEVICE;
393 fdata->ftCreationTime = fd.ftCreationTime;
394 fdata->ftLastAccessTime = fd.ftLastAccessTime;
395 fdata->ftLastWriteTime = fd.ftLastWriteTime;
396 fdata->nFileSizeHigh = fd.nFileSizeHigh;
397 fdata->nFileSizeLow = fd.nFileSizeLow;
398 FindClose(hnd);
399 return 0;
400 }
401 }
402
403 switch (GetLastError()) {
404 case ERROR_ACCESS_DENIED:
405 case ERROR_SHARING_VIOLATION:
406 case ERROR_LOCK_VIOLATION:
407 case ERROR_SHARING_BUFFER_EXCEEDED:
408 return EACCES;
409 case ERROR_BUFFER_OVERFLOW:
410 return ENAMETOOLONG;
411 case ERROR_NOT_ENOUGH_MEMORY:
412 return ENOMEM;
413 case ERROR_INVALID_NAME:
414 if (want_dir)
415 return ENOTDIR;
416 default:
417 return ENOENT;
418 }
419}
420
421#undef umask
422mode_t mingw_umask(mode_t new_mode)
423{
424 mode_t tmp_mode;
425
426 tmp_mode = current_umask;
427 current_umask = new_mode & 0777;
428
429 umask((new_mode & S_IWUSR) ? _S_IWRITE : 0);
430
431 return tmp_mode;
432}
433
434/*
435 * Examine a file's contents to determine if it can be executed. This
436 * should be a last resort: in most cases it's much more efficient to
437 * check the file extension.
438 *
439 * We look for two types of file: shell scripts and binary executables.
440 */
441static int has_exec_format(const char *name)
442{
443 HANDLE fh;
444 int fd = -1;
445 ssize_t n;
446 int sig;
447 unsigned int offset;
448 unsigned char buf[1024];
449
450 /* special case: skip DLLs, there are thousands of them! */
451 if (is_suffixed_with_case(name, ".dll"))
452 return 0;
453
454 /* Open file and try to avoid updating access time */
455 fh = CreateFileA(name, GENERIC_READ | FILE_WRITE_ATTRIBUTES,
456 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
457 if (fh != INVALID_HANDLE_VALUE) {
458 FILETIME last_access = { 0xffffffff, 0xffffffff };
459
460 SetFileTime(fh, NULL, &last_access, NULL);
461 fd = _open_osfhandle((intptr_t)fh, O_RDONLY);
462 }
463
464 if (fd < 0)
465 n = open_read_close(name, buf, sizeof(buf));
466 else
467 n = read_close(fd, buf, sizeof(buf));
468
469 if (n < 4) /* Need at least a few bytes and no error */
470 return 0;
471
472 /* shell script */
473 if (buf[0] == '#' && buf[1] == '!') {
474 return 1;
475 }
476
477 /*
478 * Poke about in file to see if it's a PE binary. I've just copied
479 * the magic from the file command.
480 */
481 if (buf[0] == 'M' && buf[1] == 'Z') {
482/* Convert four unsigned bytes to an unsigned int (little-endian) */
483#define LE4(b, o) (((unsigned)b[o+3] << 24) + (b[o+2] << 16) + \
484 (b[o+1] << 8) + b[o])
485
486 /* Actually Portable Executable */
487 /* See ape/ape.S at https://github.com/jart/cosmopolitan */
488 const unsigned char *qFpD = (unsigned char *)"qFpD";
489 if (n > 6 && LE4(buf, 2) == LE4(qFpD, 0))
490 return 1;
491
492 if (n > 0x3f) {
493 offset = (buf[0x19] << 8) + buf[0x18];
494 if (offset > 0x3f) {
495 offset = LE4(buf, 0x3c);
496 if (offset < sizeof(buf)-100) {
497 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
498 sig = (buf[offset+25] << 8) + buf[offset+24];
499 if (sig == 0x10b || sig == 0x20b) {
500 sig = (buf[offset+23] << 8) + buf[offset+22];
501 if ((sig & 0x2000) != 0) {
502 /* DLL */
503 return 0;
504 }
505 sig = buf[offset+92];
506 return (sig == 1 || sig == 2 || sig == 3
507 || sig == 7);
508 }
509 }
510 }
511 }
512 }
513 }
514
515 return 0;
516}
517
518#if ENABLE_FEATURE_EXTRA_FILE_DATA
519static uid_t file_owner(HANDLE fh, struct mingw_stat *buf)
520{
521 PSID pSidOwner;
522 PACL pDACL;
523 PSECURITY_DESCRIPTOR pSD;
524 static PTOKEN_USER user = NULL;
525 static HANDLE impersonate = INVALID_HANDLE_VALUE;
526 static int initialised = 0;
527 uid_t uid = 0;
528 DWORD *ptr;
529 unsigned char prefix[] = {
530 0x01, 0x05, 0x00, 0x00,
531 0x00, 0x00, 0x00, 0x05,
532 0x15, 0x00, 0x00, 0x00
533 };
534 unsigned char nullsid[] = {
535 0x01, 0x01, 0x00, 0x00,
536 0x00, 0x00, 0x00, 0x01,
537 0x00, 0x00, 0x00, 0x00
538 };
539
540 /* get SID of current user */
541 if (!initialised) {
542 HANDLE token;
543 DWORD ret = 0;
544
545 initialised = 1;
546 if (OpenProcessToken(GetCurrentProcess(),
547 TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE |
548 STANDARD_RIGHTS_READ, &token)) {
549 GetTokenInformation(token, TokenUser, NULL, 0, &ret);
550 if (ret <= 0 || (user=malloc(ret)) == NULL ||
551 !GetTokenInformation(token, TokenUser, user, ret, &ret)) {
552 free(user);
553 user = NULL;
554 }
555 DuplicateToken(token, SecurityImpersonation, &impersonate);
556 CloseHandle(token);
557 }
558 }
559
560 if (user == NULL)
561 return DEFAULT_UID;
562
563 /* get SID of file's owner */
564 if (GetSecurityInfo(fh, SE_FILE_OBJECT,
565 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
566 DACL_SECURITY_INFORMATION,
567 &pSidOwner, NULL, &pDACL, NULL, &pSD) != ERROR_SUCCESS)
568 return 0;
569
570 if (EqualSid(pSidOwner, user->User.Sid)) {
571 uid = DEFAULT_UID;
572 } else if (memcmp(pSidOwner, nullsid, sizeof(nullsid)) == 0) {
573 uid = DEFAULT_UID;
574 } else if (memcmp(pSidOwner, prefix, sizeof(prefix)) == 0) {
575 /* for local or domain users use the RID as uid */
576 ptr = (DWORD *)pSidOwner;
577 if (ptr[6] >= 500 && ptr[6] < DEFAULT_UID)
578 uid = (uid_t)ptr[6];
579 }
580
581 if (uid != DEFAULT_UID && impersonate != INVALID_HANDLE_VALUE &&
582 getuid() != 0) {
583 static GENERIC_MAPPING mapping = {
584 FILE_GENERIC_READ, FILE_GENERIC_WRITE,
585 FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS
586 };
587 PRIVILEGE_SET privileges;
588 DWORD grantedAccess;
589 DWORD privilegesLength = sizeof(privileges);
590 DWORD genericAccessRights = MAXIMUM_ALLOWED;
591 BOOL result;
592
593 if (AccessCheck(pSD, impersonate, genericAccessRights,
594 &mapping, &privileges, &privilegesLength,
595 &grantedAccess, &result)) {
596 if (result && (grantedAccess & 0x1200af) == 0x1200af) {
597 buf->st_mode |= (buf->st_mode & S_IRWXU) >> 6;
598 }
599 }
600 }
601 LocalFree(pSD);
602 return uid;
603}
604#endif
605
606static DWORD get_symlink_data(DWORD attr, const char *pathname,
607 WIN32_FIND_DATAA *fbuf)
608{
609 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
610 HANDLE handle = FindFirstFileA(pathname, fbuf);
611 if (handle != INVALID_HANDLE_VALUE) {
612 FindClose(handle);
613 if ((fbuf->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
614 switch (fbuf->dwReserved0) {
615 case IO_REPARSE_TAG_SYMLINK:
616 case IO_REPARSE_TAG_MOUNT_POINT:
617 case IO_REPARSE_TAG_APPEXECLINK:
618 return fbuf->dwReserved0;
619 }
620 }
621 }
622 }
623 return 0;
624}
625
626static DWORD is_symlink(const char *pathname)
627{
628 WIN32_FILE_ATTRIBUTE_DATA fdata;
629 WIN32_FIND_DATAA fbuf;
630
631 if (!get_file_attr(pathname, &fdata))
632 return get_symlink_data(fdata.dwFileAttributes, pathname, &fbuf);
633 return 0;
634}
635
636static int mingw_is_directory(const char *path)
637{
638 WIN32_FILE_ATTRIBUTE_DATA fdata;
639
640 return get_file_attr(path, &fdata) == 0 &&
641 (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
642}
643
644#if ENABLE_FEATURE_EXTRA_FILE_DATA
645/*
646 * By default we don't count subdirectories. Counting can be enabled
647 * in specific cases by calling 'count_subdirs(NULL)' before making
648 * any calls to stat(2) or lstat(2) that require accurate values of
649 * st_nlink for directories.
650 */
651int count_subdirs(const char *pathname)
652{
653 int count = 0;
654 DIR *dirp;
655 struct dirent *dp;
656 static int do_count = FALSE;
657
658 if (pathname == NULL) {
659 do_count = TRUE;
660 return 0;
661 }
662
663 if (do_count && (dirp = opendir(pathname))) {
664 while ((dp = readdir(dirp)) != NULL) {
665 if (dp->d_type == DT_DIR)
666 count++;
667 }
668 closedir(dirp);
669 } else {
670 count = 2;
671 }
672 return count;
673}
674#endif
675
676#ifndef FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS
677# define FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS 0x00400000
678#endif
679
680/* If follow is true then act like stat() and report on the link
681 * target. Otherwise report on the link itself.
682 */
683static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf)
684{
685 int err;
686 WIN32_FILE_ATTRIBUTE_DATA fdata;
687 WIN32_FIND_DATAA findbuf;
688 DWORD low, high;
689 off64_t size;
690 char *lname = NULL;
691
692 while (!(err=get_file_attr(file_name, &fdata))) {
693 buf->st_ino = 0;
694 buf->st_uid = DEFAULT_UID;
695 buf->st_gid = DEFAULT_GID;
696 buf->st_dev = buf->st_rdev = 0;
697 buf->st_attr = fdata.dwFileAttributes;
698 buf->st_tag = get_symlink_data(buf->st_attr, file_name, &findbuf);
699
700 if (buf->st_tag) {
701 char *content;
702
703 if (follow) {
704 /* The file size and times are wrong when Windows follows
705 * a symlink. Use the symlink target instead. */
706 file_name = lname = xmalloc_follow_symlinks(file_name);
707 if (!lname)
708 return -1;
709 continue;
710 }
711
712 /* Get the contents of a symlink, not its target. */
713 buf->st_mode = S_IFLNK|S_IRWXU|S_IRWXG|S_IRWXO;
714 content = xmalloc_readlink(file_name);
715 buf->st_size = content ? strlen(content) : 0;
716 free(content);
717 buf->st_atim = filetime_to_timespec(&(findbuf.ftLastAccessTime));
718 buf->st_mtim = filetime_to_timespec(&(findbuf.ftLastWriteTime));
719 buf->st_ctim = filetime_to_timespec(&(findbuf.ftCreationTime));
720 }
721 else {
722 /* The file is not a symlink. */
723 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
724 if (S_ISREG(buf->st_mode) &&
725 (has_exe_suffix(file_name) ||
726 (!(buf->st_attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS) &&
727 has_exec_format(file_name))))
728 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH;
729 buf->st_size = fdata.nFileSizeLow |
730 (((off64_t)fdata.nFileSizeHigh)<<32);
731 buf->st_atim = filetime_to_timespec(&(fdata.ftLastAccessTime));
732 buf->st_mtim = filetime_to_timespec(&(fdata.ftLastWriteTime));
733 buf->st_ctim = filetime_to_timespec(&(fdata.ftCreationTime));
734 }
735 buf->st_nlink = (buf->st_attr & FILE_ATTRIBUTE_DIRECTORY) ? 2 : 1;
736
737#if ENABLE_FEATURE_EXTRA_FILE_DATA
738 if (!(buf->st_attr &
739 (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS))) {
740 DWORD flags;
741 HANDLE fh;
742 BY_HANDLE_FILE_INFORMATION hdata;
743
744 flags = FILE_FLAG_BACKUP_SEMANTICS;
745 if (S_ISLNK(buf->st_mode))
746 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
747 fh = CreateFile(file_name, READ_CONTROL, 0, NULL,
748 OPEN_EXISTING, flags, NULL);
749 if (fh != INVALID_HANDLE_VALUE) {
750 if (GetFileInformationByHandle(fh, &hdata)) {
751 buf->st_dev = hdata.dwVolumeSerialNumber;
752 buf->st_ino = hdata.nFileIndexLow |
753 (((ino_t)hdata.nFileIndexHigh)<<32);
754 buf->st_nlink = (buf->st_attr & FILE_ATTRIBUTE_DIRECTORY) ?
755 count_subdirs(file_name) :
756 hdata.nNumberOfLinks;
757 }
758 buf->st_uid = buf->st_gid = file_owner(fh, buf);
759 CloseHandle(fh);
760 } else {
761 buf->st_uid = buf->st_gid = 0;
762 buf->st_mode &= ~S_IRWXO;
763 }
764 }
765#endif
766
767 /* Get actual size of compressed/sparse files. Only regular
768 * files need to be considered. */
769 size = buf->st_size;
770 if (S_ISREG(buf->st_mode)) {
771 low = GetCompressedFileSize(file_name, &high);
772 if (low != INVALID_FILE_SIZE || GetLastError() == NO_ERROR) {
773 size = low | (((off64_t)high)<<32);
774 }
775 }
776
777 /*
778 * Assume a block is 4096 bytes and calculate number of 512 byte
779 * sectors.
780 */
781 buf->st_blksize = 4096;
782 buf->st_blocks = ((size+4095)>>12)<<3;
783 return 0;
784 }
785 free(lname);
786 errno = err;
787 return -1;
788}
789
790int mingw_lstat(const char *file_name, struct mingw_stat *buf)
791{
792 return do_lstat(0, file_name, buf);
793}
794
795int mingw_stat(const char *file_name, struct mingw_stat *buf)
796{
797 return do_lstat(1, file_name, buf);
798}
799
800#undef st_atime
801#undef st_mtime
802#undef st_ctime
803int mingw_fstat(int fd, struct mingw_stat *buf)
804{
805 HANDLE fh = (HANDLE)_get_osfhandle(fd);
806 BY_HANDLE_FILE_INFORMATION fdata;
807
808 if (fh == INVALID_HANDLE_VALUE)
809 goto fail;
810
811 /* direct non-file handles to MS's fstat() */
812 if (GetFileType(fh) != FILE_TYPE_DISK) {
813 struct _stati64 buf64;
814
815 if (_fstati64(fd, &buf64) != 0)
816 return -1;
817
818 buf->st_mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
819 & ~(current_umask & 0022);
820 buf->st_attr = FILE_ATTRIBUTE_NORMAL;
821 buf->st_size = buf64.st_size;
822 buf->st_atim.tv_sec = buf64.st_atime;
823 buf->st_atim.tv_nsec = 0;
824 buf->st_mtim.tv_sec = buf64.st_mtime;
825 buf->st_mtim.tv_nsec = 0;
826 buf->st_ctim.tv_sec = buf64.st_ctime;
827 buf->st_ctim.tv_nsec = 0;
828#if ENABLE_FEATURE_EXTRA_FILE_DATA
829 buf->st_dev = 0;
830 buf->st_ino = 0;
831 buf->st_nlink = 1;
832#endif
833 goto success;
834 }
835
836 if (GetFileInformationByHandle(fh, &fdata)) {
837 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
838 buf->st_attr = fdata.dwFileAttributes;
839 buf->st_size = fdata.nFileSizeLow |
840 (((off64_t)fdata.nFileSizeHigh)<<32);
841 buf->st_atim = filetime_to_timespec(&(fdata.ftLastAccessTime));
842 buf->st_mtim = filetime_to_timespec(&(fdata.ftLastWriteTime));
843 buf->st_ctim = filetime_to_timespec(&(fdata.ftCreationTime));
844#if ENABLE_FEATURE_EXTRA_FILE_DATA
845 buf->st_dev = fdata.dwVolumeSerialNumber;
846 buf->st_ino = fdata.nFileIndexLow |
847 (((uint64_t)fdata.nFileIndexHigh)<<32);
848 buf->st_nlink = (buf->st_attr & FILE_ATTRIBUTE_DIRECTORY) ?
849 2 : fdata.nNumberOfLinks;
850#endif
851 success:
852#if !ENABLE_FEATURE_EXTRA_FILE_DATA
853 buf->st_dev = 0;
854 buf->st_ino = 0;
855 buf->st_nlink = (buf->st_attr & FILE_ATTRIBUTE_DIRECTORY) ? 2 : 1;
856#endif
857 buf->st_tag = 0;
858 buf->st_rdev = 0;
859 buf->st_uid = DEFAULT_UID;
860 buf->st_gid = DEFAULT_GID;
861 buf->st_blksize = 4096;
862 buf->st_blocks = ((buf->st_size+4095)>>12)<<3;
863 return 0;
864 }
865 fail:
866 errno = EBADF;
867 return -1;
868}
869
870static inline void timespec_to_filetime(const struct timespec tv, FILETIME *ft)
871{
872 long long winTime = tv.tv_sec * 10000000LL + tv.tv_nsec / 100LL +
873 116444736000000000LL;
874 ft->dwLowDateTime = winTime;
875 ft->dwHighDateTime = winTime >> 32;
876}
877
878static int hutimens(HANDLE fh, const struct timespec times[2])
879{
880 FILETIME now, aft, mft;
881 FILETIME *pft[2] = {&aft, &mft};
882 int i;
883
884 GetSystemTimeAsFileTime(&now);
885
886 if (times) {
887 for (i = 0; i < 2; ++i) {
888 if (times[i].tv_nsec == UTIME_NOW)
889 *pft[i] = now;
890 else if (times[i].tv_nsec == UTIME_OMIT)
891 pft[i] = NULL;
892 else if (times[i].tv_nsec >= 0 && times[i].tv_nsec < 1000000000L)
893 timespec_to_filetime(times[i], pft[i]);
894 else {
895 errno = EINVAL;
896 return -1;
897 }
898 }
899 } else {
900 aft = mft = now;
901 }
902
903 if (!SetFileTime(fh, NULL, pft[0], pft[1])) {
904 errno = err_win_to_posix();
905 return -1;
906 }
907 return 0;
908}
909
910int futimens(int fd, const struct timespec times[2])
911{
912 HANDLE fh;
913
914 fh = (HANDLE)_get_osfhandle(fd);
915 if (fh == INVALID_HANDLE_VALUE) {
916 errno = EBADF;
917 return -1;
918 }
919
920 return hutimens(fh, times);
921}
922
923int utimensat(int fd, const char *path, const struct timespec times[2],
924 int flags)
925{
926 int rc = -1;
927 HANDLE fh;
928 DWORD cflag = FILE_FLAG_BACKUP_SEMANTICS;
929
930 if (is_relative_path(path) && fd != AT_FDCWD) {
931 errno = ENOSYS; // partial implementation
932 return rc;
933 }
934
935 if (flags & AT_SYMLINK_NOFOLLOW)
936 cflag |= FILE_FLAG_OPEN_REPARSE_POINT;
937
938 fh = CreateFile(path, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
939 cflag, NULL);
940 if (fh == INVALID_HANDLE_VALUE) {
941 errno = err_win_to_posix();
942 return rc;
943 }
944
945 rc = hutimens(fh, times);
946 CloseHandle(fh);
947 return rc;
948}
949
950int utimes(const char *file_name, const struct timeval tv[2])
951{
952 struct timespec ts[2];
953
954 if (tv) {
955 if (tv[0].tv_usec < 0 || tv[0].tv_usec >= 1000000 ||
956 tv[1].tv_usec < 0 || tv[1].tv_usec >= 1000000) {
957 errno = EINVAL;
958 return -1;
959 }
960 ts[0].tv_sec = tv[0].tv_sec;
961 ts[0].tv_nsec = tv[0].tv_usec * 1000;
962 ts[1].tv_sec = tv[1].tv_sec;
963 ts[1].tv_nsec = tv[1].tv_usec * 1000;
964 }
965 return utimensat(AT_FDCWD, file_name, tv ? ts : NULL, 0);
966}
967
968unsigned int sleep (unsigned int seconds)
969{
970 Sleep(seconds*1000);
971 return 0;
972}
973
974int nanosleep(const struct timespec *req, struct timespec *rem)
975{
976 if (req->tv_nsec < 0 || 1000000000 <= req->tv_nsec) {
977 errno = EINVAL;
978 return -1;
979 }
980
981 Sleep(req->tv_sec*1000 + req->tv_nsec/1000000);
982
983 /* Sleep is not interruptible. So there is no remaining delay. */
984 if (rem != NULL) {
985 rem->tv_sec = 0;
986 rem->tv_nsec = 0;
987 }
988
989 return 0;
990}
991
992/*
993 * Windows' mktemp returns NULL on error whereas POSIX always returns the
994 * template and signals an error by making it an empty string.
995 */
996#undef mktemp
997char *mingw_mktemp(char *template)
998{
999 if ( mktemp(template) == NULL ) {
1000 template[0] = '\0';
1001 }
1002
1003 return template;
1004}
1005
1006int mkstemp(char *template)
1007{
1008 char *filename = mktemp(template);
1009 if (filename == NULL)
1010 return -1;
1011 return open(filename, O_RDWR | O_CREAT, 0600);
1012}
1013
1014int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM)
1015{
1016 FILETIME ft;
1017 long long hnsec;
1018
1019 GetSystemTimeAsFileTime(&ft);
1020 hnsec = filetime_to_hnsec(&ft);
1021 tv->tv_sec = hnsec / 10000000;
1022 tv->tv_usec = (hnsec % 10000000) / 10;
1023 return 0;
1024}
1025
1026int clock_gettime(clockid_t clockid, struct timespec *tp)
1027{
1028 FILETIME ft;
1029
1030 if (clockid != CLOCK_REALTIME) {
1031 errno = ENOSYS;
1032 return -1;
1033 }
1034 GetSystemTimeAsFileTime(&ft);
1035 *tp = filetime_to_timespec(&ft);
1036 return 0;
1037}
1038
1039int clock_settime(clockid_t clockid, const struct timespec *tp)
1040{
1041 SYSTEMTIME st;
1042 FILETIME ft;
1043
1044 if (clockid != CLOCK_REALTIME) {
1045 errno = ENOSYS;
1046 return -1;
1047 }
1048
1049 timespec_to_filetime(*tp, &ft);
1050 if (FileTimeToSystemTime(&ft, &st) == 0) {
1051 errno = EINVAL;
1052 return -1;
1053 }
1054
1055 if (SetSystemTime(&st) == 0) {
1056 errno = EPERM;
1057 return -1;
1058 }
1059 return 0;
1060}
1061
1062int pipe(int filedes[2])
1063{
1064 if (_pipe(filedes, PIPE_BUF, 0) < 0)
1065 return -1;
1066 return 0;
1067}
1068
1069struct tm *gmtime_r(const time_t *timep, struct tm *result)
1070{
1071 /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
1072 memcpy(result, gmtime(timep), sizeof(struct tm));
1073 return result;
1074}
1075
1076struct tm *localtime_r(const time_t *timep, struct tm *result)
1077{
1078 /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
1079 memcpy(result, localtime(timep), sizeof(struct tm));
1080 return result;
1081}
1082
1083#undef getcwd
1084char *mingw_getcwd(char *pointer, int len)
1085{
1086 char *ret = getcwd(pointer, len);
1087 if (!ret)
1088 return ret;
1089 return bs_to_slash(ret);
1090}
1091
1092#undef rename
1093int mingw_rename(const char *pold, const char *pnew)
1094{
1095 DWORD attrs;
1096
1097 /*
1098 * For non-symlinks, try native rename() first to get errno right.
1099 * It is based on MoveFile(), which cannot overwrite existing files.
1100 */
1101 if (!is_symlink(pold)) {
1102 if (!rename(pold, pnew))
1103 return 0;
1104 if (errno != EEXIST)
1105 return -1;
1106 }
1107 if (MoveFileEx(pold, pnew,
1108 MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
1109 return 0;
1110 /* TODO: translate more errors */
1111 if (GetLastError() == ERROR_ACCESS_DENIED &&
1112 (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
1113 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
1114 errno = EISDIR;
1115 return -1;
1116 }
1117 if ((attrs & FILE_ATTRIBUTE_READONLY) &&
1118 SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
1119 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
1120 return 0;
1121 /* revert file attributes on failure */
1122 SetFileAttributes(pnew, attrs);
1123 }
1124 }
1125 errno = EACCES;
1126 return -1;
1127}
1128
1129static char *gethomedir(void)
1130{
1131 static char *buf = NULL;
1132 DECLARE_PROC_ADDR(BOOL, GetUserProfileDirectoryA, HANDLE, LPSTR, LPDWORD);
1133
1134 if (!buf) {
1135 DWORD len = PATH_MAX;
1136 HANDLE h;
1137
1138 buf = xzalloc(len);
1139 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) {
1140 if (INIT_PROC_ADDR(userenv.dll, GetUserProfileDirectoryA)) {
1141 GetUserProfileDirectoryA(h, buf, &len);
1142 bs_to_slash(buf);
1143 }
1144 CloseHandle(h);
1145 }
1146 }
1147 return buf;
1148}
1149
1150#define NAME_LEN 100
1151char *get_user_name(void)
1152{
1153 static char *user_name = NULL;
1154 char *s;
1155 DWORD len = NAME_LEN;
1156
1157 if ( user_name == NULL ) {
1158 user_name = xzalloc(NAME_LEN);
1159 }
1160
1161 if ( user_name[0] != '\0' ) {
1162 return user_name;
1163 }
1164
1165 if ( !GetUserName(user_name, &len) ) {
1166 return NULL;
1167 }
1168
1169 for ( s=user_name; *s; ++s ) {
1170 if ( *s == ' ' ) {
1171 *s = '_';
1172 }
1173 }
1174
1175 return user_name;
1176}
1177
1178/*
1179 * When 'drop' drops privileges TokenIsElevated is still TRUE.
1180 * Find out if we're really privileged by checking if the group
1181 * BUILTIN\Administrators is enabled.
1182 */
1183int
1184elevation_state(void)
1185{
1186 int elevated = FALSE;
1187 int enabled = TRUE;
1188 HANDLE h;
1189#if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP
1190 BOOL admin_enabled = TRUE;
1191 unsigned char admin[16] = {
1192 0x01, 0x02, 0x00, 0x00,
1193 0x00, 0x00, 0x00, 0x05,
1194 0x20, 0x00, 0x00, 0x00,
1195 0x20, 0x02, 0x00, 0x00
1196 };
1197#endif
1198
1199 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) {
1200 TOKEN_ELEVATION elevation = { 0 };
1201 DWORD size;
1202
1203 if (GetTokenInformation(h, TokenElevation, &elevation,
1204 sizeof(elevation), &size))
1205 elevated = elevation.TokenIsElevated != 0;
1206 CloseHandle(h);
1207 }
1208
1209#if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP
1210 if (CheckTokenMembership(NULL, (PSID)admin, &admin_enabled))
1211 enabled = admin_enabled != 0;
1212#endif
1213
1214 return elevated | (enabled << 1);
1215}
1216
1217int getuid(void)
1218{
1219 return elevation_state() == (ELEVATED_PRIVILEGE | ADMIN_ENABLED) ?
1220 0 : DEFAULT_UID;
1221}
1222
1223struct passwd *getpwnam(const char *name)
1224{
1225 const char *myname;
1226
1227 if ( (myname=get_user_name()) != NULL &&
1228 strcmp(myname, name) == 0 ) {
1229 return getpwuid(DEFAULT_UID);
1230 }
1231 else if (strcmp(name, "root") == 0) {
1232 return getpwuid(0);
1233 }
1234
1235 return NULL;
1236}
1237
1238struct passwd *getpwuid(uid_t uid)
1239{
1240 static struct passwd p;
1241
1242 if (uid == 0)
1243 p.pw_name = (char *)"root";
1244 else if (uid != DEFAULT_UID || (p.pw_name=get_user_name()) == NULL)
1245 return NULL;
1246
1247 p.pw_dir = gethomedir();
1248 p.pw_passwd = (char *)"";
1249 p.pw_gecos = p.pw_name;
1250 p.pw_shell = NULL;
1251 p.pw_uid = uid;
1252 p.pw_gid = uid;
1253
1254 return &p;
1255}
1256
1257struct group *getgrgid(gid_t gid)
1258{
1259 static char *members[2] = { NULL, NULL };
1260 static struct group g;
1261
1262 if (gid == 0) {
1263 g.gr_name = (char *)"root";
1264 }
1265 else if (gid != DEFAULT_GID || (g.gr_name=get_user_name()) == NULL) {
1266 return NULL;
1267 }
1268 g.gr_passwd = (char *)"";
1269 g.gr_gid = gid;
1270 members[0] = g.gr_name;
1271 g.gr_mem = members;
1272
1273 return &g;
1274}
1275
1276#if 0
1277int getgrouplist(const char *user UNUSED_PARAM, gid_t group,
1278 gid_t *groups, int *ngroups)
1279{
1280 if ( *ngroups == 0 ) {
1281 *ngroups = 1;
1282 return -1;
1283 }
1284
1285 *ngroups = 1;
1286 groups[0] = group;
1287 return 1;
1288}
1289
1290int getgroups(int n, gid_t *groups)
1291{
1292 if ( n == 0 ) {
1293 return 1;
1294 }
1295
1296 groups[0] = getgid();
1297 return 1;
1298}
1299#endif
1300
1301int getlogin_r(char *buf, size_t len)
1302{
1303 char *name;
1304
1305 if ( (name=get_user_name()) == NULL ) {
1306 return -1;
1307 }
1308
1309 if ( strlen(name) >= len ) {
1310 errno = ERANGE;
1311 return -1;
1312 }
1313
1314 strcpy(buf, name);
1315 return 0;
1316}
1317
1318long sysconf(int name)
1319{
1320 if ( name == _SC_CLK_TCK ) {
1321 return TICKS_PER_SECOND;
1322 }
1323 errno = EINVAL;
1324 return -1;
1325}
1326
1327clock_t times(struct tms *buf)
1328{
1329 memset(buf, 0, sizeof(*buf));
1330 return 0;
1331}
1332
1333int link(const char *oldpath, const char *newpath)
1334{
1335 DECLARE_PROC_ADDR(BOOL, CreateHardLinkA, LPCSTR, LPCSTR,
1336 LPSECURITY_ATTRIBUTES);
1337
1338 if (!INIT_PROC_ADDR(kernel32.dll, CreateHardLinkA)) {
1339 errno = ENOSYS;
1340 return -1;
1341 }
1342 if (!CreateHardLinkA(newpath, oldpath, NULL)) {
1343 errno = err_win_to_posix();
1344 return -1;
1345 }
1346 return 0;
1347}
1348
1349#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
1350# define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
1351#endif
1352#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1353# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
1354#endif
1355
1356int symlink(const char *target, const char *linkpath)
1357{
1358 DWORD flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1359 DECLARE_PROC_ADDR(BOOLEAN, CreateSymbolicLinkA, LPCSTR, LPCSTR, DWORD);
1360 char *targ, *relative = NULL;
1361
1362 if (!INIT_PROC_ADDR(kernel32.dll, CreateSymbolicLinkA)) {
1363 errno = ENOSYS;
1364 return -1;
1365 }
1366
1367 if (is_relative_path(target) && has_path(linkpath)) {
1368 /* make target's path relative to current directory */
1369 const char *name = bb_get_last_path_component_nostrip(linkpath);
1370 relative = xasprintf("%.*s%s",
1371 (int)(name - linkpath), linkpath, target);
1372 }
1373
1374 if (mingw_is_directory(relative ?: target))
1375 flag |= SYMBOLIC_LINK_FLAG_DIRECTORY;
1376 free(relative);
1377
1378 targ = auto_string(strdup(target));
1379 slash_to_bs(targ);
1380
1381 retry:
1382 if (!CreateSymbolicLinkA(linkpath, targ, flag)) {
1383 /* Old Windows versions see 'UNPRIVILEGED_CREATE' as an invalid
1384 * parameter. Retry without it. */
1385 if ((flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) &&
1386 GetLastError() == ERROR_INVALID_PARAMETER) {
1387 flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1388 goto retry;
1389 }
1390 errno = err_win_to_posix();
1391 return -1;
1392 }
1393 return 0;
1394}
1395
1396/* Create a directory junction */
1397#define MRPB rptr->MountPointReparseBuffer
1398#if 0
1399static void print_junction(REPARSE_DATA_BUFFER *rptr)
1400{
1401 int i;
1402#define MRPB_HEADER_SIZE \
1403 (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - \
1404 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer))
1405
1406 fprintf(stderr, "---\n");
1407 fprintf(stderr, "Tag: %lx\n", rptr->ReparseTag);
1408 fprintf(stderr, "ReparseDataLength: %d (%d + %d + %d + %d + %d = %d)\n",
1409 rptr->ReparseDataLength, MRPB_HEADER_SIZE,
1410 MRPB.SubstituteNameLength, sizeof(WCHAR),
1411 MRPB.PrintNameLength, sizeof(WCHAR),
1412 MRPB_HEADER_SIZE + MRPB.SubstituteNameLength + sizeof(WCHAR) +
1413 MRPB.PrintNameLength + sizeof(WCHAR));
1414 fprintf(stderr, "Reserved: %d\n", rptr->Reserved);
1415 fprintf(stderr, "---\n");
1416 fprintf(stderr, "SubstituteNameOffset: %d\n", MRPB.SubstituteNameOffset);
1417 fprintf(stderr, "SubstituteNameLength: %d\n", MRPB.SubstituteNameLength);
1418 fprintf(stderr, "PrintNameOffset: %d\n", MRPB.PrintNameOffset);
1419 fprintf(stderr, "PrintNameLength: %d\n", MRPB.PrintNameLength);
1420 fprintf(stderr, "SubstituteName: ");
1421 for (i = 0; i < MRPB.SubstituteNameLength/sizeof(WCHAR); i++)
1422 fprintf(stderr, "%c",
1423 MRPB.PathBuffer[MRPB.SubstituteNameOffset/sizeof(WCHAR) + i]);
1424 fprintf(stderr, " (%x)",
1425 MRPB.PathBuffer[MRPB.SubstituteNameOffset/sizeof(WCHAR) + i]);
1426 fprintf(stderr, "\n");
1427 fprintf(stderr, "PrintName: ");
1428 for (i = 0; i < MRPB.PrintNameLength/sizeof(WCHAR); i++)
1429 fprintf(stderr, "%c",
1430 MRPB.PathBuffer[MRPB.PrintNameOffset/sizeof(WCHAR) + i]);
1431 fprintf(stderr, " (%x)",
1432 MRPB.PathBuffer[MRPB.PrintNameOffset/sizeof(WCHAR) + i]);
1433 fprintf(stderr, "\n");
1434 fprintf(stderr, "---\n");
1435}
1436#endif
1437
1438static REPARSE_DATA_BUFFER *make_junction_data_buffer(char *rpath)
1439{
1440 WCHAR pbuf[PATH_MAX];
1441 int plen, slen, rbufsize;
1442 REPARSE_DATA_BUFFER *rptr;
1443
1444 /* We need two strings for the reparse data. The PrintName is the
1445 * target path in Win32 format, the SubstituteName is the same in
1446 * NT format.
1447 *
1448 * The return value from MultiByteToWideChar includes the trailing
1449 * L'\0' character.
1450 */
1451 slash_to_bs(rpath);
1452 plen = MultiByteToWideChar(CP_ACP, 0, rpath, -1, pbuf, PATH_MAX);
1453 if (plen == 0) {
1454 errno = err_win_to_posix();
1455 return NULL;
1456 }
1457 slen = plen + 4;
1458
1459 rbufsize = (slen + plen) * sizeof(WCHAR) +
1460 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer);
1461 rptr = xzalloc(rbufsize);
1462
1463 rptr->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1464 rptr->ReparseDataLength = rbufsize -
1465 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
1466 /* rptr->Reserved = 0; */
1467 /* MRPB.SubstituteNameOffset = 0; */
1468 MRPB.SubstituteNameLength = (slen - 1) * sizeof(WCHAR);
1469 MRPB.PrintNameOffset = MRPB.SubstituteNameLength + sizeof(WCHAR);
1470 MRPB.PrintNameLength = (plen - 1) * sizeof(WCHAR);
1471
1472 wcscpy(MRPB.PathBuffer, L"\\??\\");
1473 wcscpy(MRPB.PathBuffer + 4, pbuf);
1474 wcscpy(MRPB.PathBuffer + slen, pbuf);
1475 return rptr;
1476}
1477
1478int create_junction(const char *oldpath, const char *newpath)
1479{
1480 char rpath[PATH_MAX];
1481 struct stat statbuf;
1482 REPARSE_DATA_BUFFER *rptr = NULL;
1483 HANDLE h;
1484 int error = 0;
1485 DWORD bytes;
1486
1487 if (realpath(oldpath, rpath) == NULL || stat(rpath, &statbuf) < 0)
1488 return -1;
1489
1490 if (!has_dos_drive_prefix(rpath)) {
1491 errno = EINVAL;
1492 return -1;
1493 }
1494
1495 if (!S_ISDIR(statbuf.st_mode)) {
1496 errno = ENOTDIR;
1497 return -1;
1498 }
1499
1500 if (!(rptr = make_junction_data_buffer(rpath))) {
1501 return -1;
1502 }
1503
1504 if (mkdir(newpath, 0777) < 0) {
1505 free(rptr);
1506 return -1;
1507 }
1508
1509 h = CreateFileA(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
1510 OPEN_EXISTING,
1511 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1512 if (h != INVALID_HANDLE_VALUE) {
1513 if (DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, rptr,
1514 rptr->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE,
1515 NULL, 0, &bytes, NULL) != 0) {
1516 CloseHandle(h);
1517 free(rptr);
1518 return 0;
1519 }
1520 error = err_win_to_posix();
1521 CloseHandle(h);
1522 } else {
1523 error = err_win_to_posix();
1524 }
1525
1526 rmdir(newpath);
1527 free(rptr);
1528 errno = error;
1529 return -1;
1530}
1531
1532static char *normalize_ntpathA(char *buf)
1533{
1534 /* fix absolute path prefixes */
1535 if (buf[0] == '\\') {
1536 /* strip NT namespace prefixes */
1537 if (is_prefixed_with(buf, "\\??\\") ||
1538 is_prefixed_with(buf, "\\\\?\\"))
1539 buf += 4;
1540 else if (is_prefixed_with_case(buf, "\\DosDevices\\"))
1541 buf += 12;
1542 /* replace remaining '...UNC\' with '\\' */
1543 if (is_prefixed_with_case(buf, "UNC\\")) {
1544 buf += 2;
1545 *buf = '\\';
1546 }
1547 }
1548 return buf;
1549}
1550
1551static char *resolve_symlinks(char *path)
1552{
1553 HANDLE h;
1554 DWORD status;
1555 char *ptr = NULL;
1556 DECLARE_PROC_ADDR(DWORD, GetFinalPathNameByHandleA, HANDLE,
1557 LPSTR, DWORD, DWORD);
1558 char *resolve = NULL;
1559
1560 if (GetFileAttributesA(path) & FILE_ATTRIBUTE_REPARSE_POINT) {
1561 resolve = xmalloc_follow_symlinks(path);
1562 if (!resolve)
1563 return NULL;
1564 }
1565
1566 /* need a file handle to resolve symlinks */
1567 h = CreateFileA(resolve ?: path, 0, 0, NULL, OPEN_EXISTING,
1568 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1569 if (h != INVALID_HANDLE_VALUE) {
1570 if (!INIT_PROC_ADDR(kernel32.dll, GetFinalPathNameByHandleA)) {
1571 if (resolve)
1572 strcpy(path, resolve);
1573 ptr = path;
1574 goto end;
1575 }
1576
1577 /* normalize the path and return it on success */
1578 status = GetFinalPathNameByHandleA(h, path, MAX_PATH,
1579 FILE_NAME_NORMALIZED|VOLUME_NAME_DOS);
1580 if (status != 0 && status < MAX_PATH) {
1581 ptr = normalize_ntpathA(path);
1582 goto end;
1583 } else if (err_win_to_posix() == ENOSYS) {
1584 if (resolve)
1585 strcpy(path, resolve);
1586 ptr = path;
1587 goto end;
1588 }
1589 }
1590
1591 errno = err_win_to_posix();
1592 end:
1593 CloseHandle(h);
1594 free(resolve);
1595 return ptr;
1596}
1597
1598/*
1599 * Emulate realpath in two stages:
1600 *
1601 * - _fullpath handles './', '../' and extra '/' characters. The
1602 * resulting path may not refer to an actual file.
1603 *
1604 * - resolve_symlinks checks that the file exists (by opening it) and
1605 * resolves symlinks by calling GetFinalPathNameByHandleA.
1606 */
1607char *realpath(const char *path, char *resolved_path)
1608{
1609 char buffer[MAX_PATH];
1610 char *real_path, *p;
1611
1612 /* enforce glibc pre-2.3 behaviour */
1613 if (path == NULL || resolved_path == NULL) {
1614 errno = EINVAL;
1615 return NULL;
1616 }
1617
1618 if (_fullpath(buffer, path, MAX_PATH) &&
1619 (real_path=resolve_symlinks(buffer))) {
1620 bs_to_slash(strcpy(resolved_path, real_path));
1621 p = last_char_is(resolved_path, '/');
1622 if (p && p > resolved_path && p[-1] != ':')
1623 *p = '\0';
1624 return resolved_path;
1625 }
1626 return NULL;
1627}
1628
1629static wchar_t *normalize_ntpath(wchar_t *wbuf)
1630{
1631 int i;
1632 /* fix absolute path prefixes */
1633 if (wbuf[0] == '\\') {
1634 /* strip NT namespace prefixes */
1635 if (!wcsncmp(wbuf, L"\\??\\", 4) ||
1636 !wcsncmp(wbuf, L"\\\\?\\", 4))
1637 wbuf += 4;
1638 else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
1639 wbuf += 12;
1640 /* replace remaining '...UNC\' with '\\' */
1641 if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
1642 wbuf += 2;
1643 *wbuf = '\\';
1644 }
1645 }
1646 /* convert backslashes to slashes */
1647 for (i = 0; wbuf[i]; i++)
1648 if (wbuf[i] == '\\')
1649 wbuf[i] = '/';
1650 return wbuf;
1651}
1652
1653/*
1654 * This is the stucture required for reparse points with the tag
1655 * IO_REPARSE_TAG_APPEXECLINK. The Buffer member contains four
1656 * NUL-terminated, concatentated strings:
1657 *
1658 * package id, entry point, executable path and application type.
1659 *
1660 * https://www.tiraniddo.dev/2019/09/overview-of-windows-execution-aliases.html
1661 */
1662typedef struct {
1663 DWORD ReparseTag;
1664 USHORT ReparseDataLength;
1665 USHORT Reserved;
1666 ULONG Version;
1667 WCHAR Buffer[1];
1668} APPEXECLINK_BUFFER;
1669
1670#define SRPB rptr->SymbolicLinkReparseBuffer
1671char * FAST_FUNC xmalloc_readlink(const char *pathname)
1672{
1673 HANDLE h;
1674 char *buf;
1675 int bufsiz;
1676
1677 h = CreateFile(pathname, 0, 0, NULL, OPEN_EXISTING,
1678 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
1679 if (h != INVALID_HANDLE_VALUE) {
1680 DWORD nbytes;
1681 BYTE rbuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1682 PREPARSE_DATA_BUFFER rptr = (PREPARSE_DATA_BUFFER)rbuf;
1683 APPEXECLINK_BUFFER *aptr = (APPEXECLINK_BUFFER *)rptr;
1684 BOOL status;
1685 size_t len;
1686 WCHAR *name = NULL, *str[4], *s;
1687 int i;
1688
1689 status = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0,
1690 rptr, sizeof(rbuf), &nbytes, NULL);
1691 CloseHandle(h);
1692
1693 if (status && rptr->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
1694 len = SRPB.SubstituteNameLength/sizeof(WCHAR);
1695 name = SRPB.PathBuffer + SRPB.SubstituteNameOffset/sizeof(WCHAR);
1696 } else if (status && rptr->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
1697 len = MRPB.SubstituteNameLength/sizeof(WCHAR);
1698 name = MRPB.PathBuffer + MRPB.SubstituteNameOffset/sizeof(WCHAR);
1699 } else if (status && rptr->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
1700 // We only need the executable path but we determine all of
1701 // the strings as a sanity check.
1702 i = 0;
1703 s = aptr->Buffer;
1704 do {
1705 str[i] = s;
1706 while (*s++)
1707 ;
1708 } while (++i < 4);
1709
1710 if (s - aptr->Buffer < MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
1711 len = wcslen(str[2]);
1712 name = str[2];
1713 }
1714 }
1715
1716 if (name) {
1717 name[len] = 0;
1718 name = normalize_ntpath(name);
1719 bufsiz = WideCharToMultiByte(CP_ACP, 0, name, -1, NULL, 0, 0, 0);
1720 if (bufsiz) {
1721 buf = xmalloc(bufsiz);
1722 if (WideCharToMultiByte(CP_ACP, 0, name, -1, buf, bufsiz, 0, 0))
1723 return buf;
1724 free(buf);
1725 }
1726 }
1727 }
1728 errno = err_win_to_posix();
1729 return NULL;
1730}
1731
1732const char *get_busybox_exec_path(void)
1733{
1734 static char *path = NULL;
1735
1736 if (!path) {
1737 path = xzalloc(PATH_MAX);
1738 }
1739
1740 if (!*path) {
1741 GetModuleFileName(NULL, path, PATH_MAX);
1742 bs_to_slash(path);
1743 }
1744 return path;
1745}
1746
1747#undef mkdir
1748int mingw_mkdir(const char *path, int mode UNUSED_PARAM)
1749{
1750 int ret;
1751 struct stat st;
1752 int lerrno = 0;
1753
1754 if ( (ret=mkdir(path)) < 0 ) {
1755 lerrno = errno;
1756 if ( lerrno == EACCES && stat(path, &st) == 0 ) {
1757 ret = 0;
1758 lerrno = 0;
1759 }
1760 }
1761
1762 errno = lerrno;
1763 return ret;
1764}
1765
1766#undef chdir
1767int mingw_chdir(const char *dirname)
1768{
1769 int ret = -1;
1770 char *realdir;
1771
1772 if (is_symlink(dirname))
1773 realdir = xmalloc_realpath(dirname);
1774 else
1775 realdir = xstrdup(dirname);
1776
1777 if (realdir) {
1778 fix_path_case(realdir);
1779 ret = chdir(realdir);
1780 }
1781 free(realdir);
1782
1783 return ret;
1784}
1785
1786#undef chmod
1787int mingw_chmod(const char *path, int mode)
1788{
1789 if (mingw_is_directory(path))
1790 mode |= 0222;
1791
1792 return chmod(path, mode);
1793}
1794
1795int fcntl(int fd, int cmd, ...)
1796{
1797 va_list arg;
1798 int result = -1;
1799 char *fds;
1800 int target, i, newfd;
1801
1802 va_start(arg, cmd);
1803
1804 switch (cmd) {
1805 case F_GETFD:
1806 case F_SETFD:
1807 case F_GETFL:
1808 /*
1809 * Our fake F_GETFL won't matter if the return value is used as
1810 * fcntl(fd, F_SETFL, ret|something);
1811 * because F_SETFL isn't supported either.
1812 */
1813 result = 0;
1814 break;
1815 case F_DUPFD:
1816 target = va_arg(arg, int);
1817 fds = xzalloc(target);
1818 while ((newfd = dup(fd)) < target && newfd >= 0) {
1819 fds[newfd] = 1;
1820 }
1821 for (i = 0; i < target; ++i) {
1822 if (fds[i]) {
1823 close(i);
1824 }
1825 }
1826 free(fds);
1827 result = newfd;
1828 break;
1829 default:
1830 errno = ENOSYS;
1831 break;
1832 }
1833
1834 va_end(arg);
1835 return result;
1836}
1837
1838#undef unlink
1839#undef rmdir
1840int mingw_unlink(const char *pathname)
1841{
1842 int ret;
1843
1844 /* read-only files cannot be removed */
1845 chmod(pathname, 0666);
1846
1847 ret = unlink(pathname);
1848 if (ret == -1 && errno == EACCES) {
1849 /* a symlink to a directory needs to be removed by calling rmdir */
1850 /* (the *real* Windows rmdir, not mingw_rmdir) */
1851 if (is_symlink(pathname)) {
1852 return rmdir(pathname);
1853 }
1854 }
1855 return ret;
1856}
1857
1858struct pagefile_info {
1859 SIZE_T total;
1860 SIZE_T in_use;
1861};
1862
1863static BOOL CALLBACK
1864pagefile_cb(LPVOID context, PENUM_PAGE_FILE_INFORMATION info,
1865 LPCSTR name UNUSED_PARAM)
1866{
1867 struct pagefile_info *pfinfo = (struct pagefile_info *)context;
1868
1869 pfinfo->total += info->TotalSize;
1870 pfinfo->in_use += info->TotalInUse;
1871 return TRUE;
1872}
1873
1874int sysinfo(struct sysinfo *info)
1875{
1876 PERFORMANCE_INFORMATION perf;
1877 struct pagefile_info pfinfo;
1878 DECLARE_PROC_ADDR(BOOL, GetPerformanceInfo, PPERFORMANCE_INFORMATION,
1879 DWORD);
1880 DECLARE_PROC_ADDR(BOOL, EnumPageFilesA, PENUM_PAGE_FILE_CALLBACKA, LPVOID);
1881
1882 memset((void *)info, 0, sizeof(struct sysinfo));
1883 memset((void *)&perf, 0, sizeof(PERFORMANCE_INFORMATION));
1884 memset((void *)&pfinfo, 0, sizeof(struct pagefile_info));
1885 info->mem_unit = 4096;
1886
1887 if (INIT_PROC_ADDR(psapi.dll, GetPerformanceInfo)) {
1888 perf.cb = sizeof(PERFORMANCE_INFORMATION);
1889 GetPerformanceInfo(&perf, perf.cb);
1890 }
1891
1892 if (INIT_PROC_ADDR(psapi.dll, EnumPageFilesA)) {
1893 EnumPageFilesA((PENUM_PAGE_FILE_CALLBACK)pagefile_cb, (LPVOID)&pfinfo);
1894 }
1895
1896 info->totalram = perf.PhysicalTotal * perf.PageSize / 4096;
1897 info->bufferram = perf.SystemCache * perf.PageSize / 4096;
1898 if (perf.PhysicalAvailable > perf.SystemCache)
1899 info->freeram = perf.PhysicalAvailable * perf.PageSize / 4096 -
1900 info->bufferram;
1901 info->totalswap = pfinfo.total * perf.PageSize / 4096;
1902 info->freeswap = (pfinfo.total - pfinfo.in_use) * perf.PageSize / 4096;
1903
1904 info->uptime = GetTickCount64() / 1000;
1905 info->procs = perf.ProcessCount;
1906
1907 return 0;
1908}
1909
1910#undef strftime
1911size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm)
1912{
1913 size_t ret;
1914 char buffer[64];
1915 const char *replace;
1916 char *t;
1917 char *fmt;
1918 struct tm tm2;
1919
1920 /*
1921 * Emulate some formats that Windows' strftime lacks.
1922 * - '%e' day of the month with space padding
1923 * - '%s' number of seconds since the Unix epoch
1924 * - '%T' same as %H:%M:%S
1925 * - '%z' timezone offset
1926 * Also, permit the '-' modifier to omit padding. Windows uses '#'.
1927 */
1928 fmt = xstrdup(format);
1929 for ( t=fmt; *t; ++t ) {
1930 if ( *t == '%' ) {
1931 replace = NULL;
1932 if ( t[1] == 'e' ) {
1933 if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) {
1934 sprintf(buffer, "%2d", tm->tm_mday);
1935 }
1936 else {
1937 strcpy(buffer, " ");
1938 }
1939 replace = buffer;
1940 }
1941 else if ( t[1] == 's' ) {
1942 tm2 = *tm;
1943 sprintf(buffer, "%"LL_FMT"d", (long long)mktime(&tm2));
1944 replace = buffer;
1945 }
1946 else if ( t[1] == 'T' ) {
1947 replace = "%H:%M:%S";
1948 }
1949 else if ( t[1] == 'z' ) {
1950 _tzset();
1951 if ( tm->tm_isdst >= 0 ) {
1952 int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0);
1953 int hr, min;
1954
1955 if ( offset > 0 ) {
1956 buffer[0] = '-';
1957 }
1958 else {
1959 buffer[0] = '+';
1960 offset = -offset;
1961 }
1962
1963 hr = offset / 3600;
1964 min = (offset % 3600) / 60;
1965 sprintf(buffer+1, "%04d", hr*100 + min);
1966 }
1967 else {
1968 buffer[0] = '\0';
1969 }
1970 replace = buffer;
1971 }
1972 else if ( t[1] == '-' && t[2] != '\0' &&
1973 strchr("dHIjmMSUwWyY", t[2]) ) {
1974 /* Microsoft uses '#' rather than '-' to remove padding */
1975 t[1] = '#';
1976 }
1977 else if ( t[1] != '\0' ) {
1978 ++t;
1979 }
1980
1981 if (replace) {
1982 int m;
1983 char *newfmt;
1984
1985 *t = '\0';
1986 m = t - fmt;
1987 newfmt = xasprintf("%s%s%s", fmt, replace, t+2);
1988 free(fmt);
1989 t = newfmt + m + strlen(replace) - 1;
1990 fmt = newfmt;
1991 }
1992 }
1993 }
1994
1995 ret = strftime(buf, max, fmt, tm);
1996 free(fmt);
1997
1998 return ret;
1999}
2000
2001#undef access
2002int mingw_access(const char *name, int mode)
2003{
2004 int ret;
2005 struct stat s;
2006
2007 /* Windows can only handle test for existence, read or write */
2008 if (mode == F_OK || (mode & ~X_OK)) {
2009 ret = _access(name, mode & ~X_OK);
2010 if (ret < 0 || !(mode & X_OK)) {
2011 return ret;
2012 }
2013 }
2014
2015 if (!mingw_stat(name, &s)) {
2016 if ((s.st_mode&S_IXUSR)) {
2017 return 0;
2018 }
2019 errno = EACCES;
2020 }
2021
2022 return -1;
2023}
2024
2025int mingw_rmdir(const char *path)
2026{
2027 /* On Linux rmdir(2) doesn't remove symlinks */
2028 if (is_symlink(path)) {
2029 errno = ENOTDIR;
2030 return -1;
2031 }
2032
2033 /* read-only directories cannot be removed */
2034 chmod(path, 0666);
2035 return rmdir(path);
2036}
2037
2038void mingw_sync(void)
2039{
2040 HANDLE h;
2041 FILE *mnt;
2042 struct mntent *entry;
2043 char name[] = "\\\\.\\C:";
2044
2045 mnt = setmntent(bb_path_mtab_file, "r");
2046 if (mnt) {
2047 while ((entry=getmntent(mnt)) != NULL) {
2048 name[4] = entry->mnt_dir[0];
2049 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
2050 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2051 OPEN_EXISTING, 0, NULL);
2052 if (h != INVALID_HANDLE_VALUE) {
2053 FlushFileBuffers(h);
2054 CloseHandle(h);
2055 }
2056 }
2057 endmntent(mnt);
2058 }
2059}
2060
2061#define NUMEXT 5
2062static const char win_suffix[NUMEXT][4] = { "com", "exe", "sh", "bat", "cmd" };
2063
2064static int has_win_suffix(const char *name, int start)
2065{
2066 const char *dot = strrchr(bb_basename(name), '.');
2067 int i;
2068
2069 if (dot != NULL && strlen(dot) < 5) {
2070 for (i=start; i<NUMEXT; ++i) {
2071 if (!strcasecmp(dot+1, win_suffix[i])) {
2072 return 1;
2073 }
2074 }
2075 }
2076 return 0;
2077}
2078
2079int has_bat_suffix(const char *name)
2080{
2081 return has_win_suffix(name, 3);
2082}
2083
2084int has_exe_suffix(const char *name)
2085{
2086 return has_win_suffix(name, 0);
2087}
2088
2089int has_exe_suffix_or_dot(const char *name)
2090{
2091 return last_char_is(name, '.') || has_win_suffix(name, 0);
2092}
2093
2094/* Copy path to an allocated string long enough to allow a file extension
2095 * to be added. */
2096char *alloc_ext_space(const char *path)
2097{
2098 char *s = xmalloc(strlen(path) + 5);
2099 strcpy(s, path);
2100 return s;
2101}
2102
2103/* Check if path is an executable or can be made into one by adding
2104 * a suffix. The suffix is added to the end of the argument which
2105 * must be long enough to allow this.
2106 *
2107 * If the return value is TRUE the argument contains the new path,
2108 * if FALSE the argument is unchanged.
2109 */
2110int
2111add_win32_extension(char *p)
2112{
2113 if (file_is_executable(p))
2114 return TRUE;
2115
2116 if (!has_exe_suffix_or_dot(p)) {
2117 int i, len = strlen(p);
2118
2119 p[len] = '.';
2120 for (i = 0; i < NUMEXT; ++i) {
2121 strcpy(p + len + 1, win_suffix[i]);
2122 if (file_is_executable(p))
2123 return TRUE;
2124 }
2125 p[len] = '\0';
2126 }
2127 return FALSE;
2128}
2129
2130/*
2131 * Determine if a path represents a WIN32 executable, adding a suffix
2132 * if necessary. Returns an allocated string if it does, NULL if not.
2133 */
2134char *
2135file_is_win32_exe(const char *name)
2136{
2137 char *path = alloc_ext_space(name);
2138
2139 if (add_win32_extension(path))
2140 return path;
2141
2142 free(path);
2143 return NULL;
2144}
2145
2146char * FAST_FUNC bs_to_slash(char *str)
2147{
2148 char *p;
2149
2150 for (p=str; *p; ++p) {
2151 if ( *p == '\\' ) {
2152 *p = '/';
2153 }
2154 }
2155 return str;
2156}
2157
2158#if ENABLE_UNICODE_SUPPORT
2159MINGW_BB_WCHAR_T * FAST_FUNC bs_to_slash_u(MINGW_BB_WCHAR_T *str)
2160{
2161 MINGW_BB_WCHAR_T *p;
2162
2163 for (p=str; *p; ++p) {
2164 if ( *p == '\\' ) {
2165 *p = '/';
2166 }
2167 }
2168 return str;
2169}
2170#endif
2171
2172void FAST_FUNC slash_to_bs(char *p)
2173{
2174 for (; *p; ++p) {
2175 if ( *p == '/' ) {
2176 *p = '\\';
2177 }
2178 }
2179}
2180
2181/* Windows strips trailing dots and spaces from the last component of
2182 * a file path. This routine emulates that behaviour so we can preempt
2183 * Windows if necessary. */
2184void FAST_FUNC strip_dot_space(char *p)
2185{
2186 char *start = (char *)bb_basename(p);
2187 char *end = start + strlen(start);
2188
2189 while (end > start && (end[-1] == '.' || end[-1] == ' ')) {
2190 *--end = '\0';
2191 }
2192
2193 // Strip trailing slash, but not from a drive root (C:/)
2194 if (--end != start && (*end == '/' || *end == '\\') &&
2195 !(end == p + 2 && root_len(p) == 2))
2196 *end = '\0';
2197}
2198
2199size_t FAST_FUNC remove_cr(char *p, size_t len)
2200{
2201 ssize_t i, j;
2202
2203 for (i=j=0; i<len; ++i) {
2204 if (p[i] == '\r' && i < len - 1 && p[i+1] == '\n')
2205 continue;
2206 p[j++] = p[i];
2207 }
2208 return j;
2209}
2210
2211off_t mingw_lseek(int fd, off_t offset, int whence)
2212{
2213 DWORD ftype;
2214 HANDLE h = (HANDLE)_get_osfhandle(fd);
2215 if (h == INVALID_HANDLE_VALUE) {
2216 errno = EBADF;
2217 return -1;
2218 }
2219 ftype = GetFileType(h);
2220 if (ftype != FILE_TYPE_DISK && ftype != FILE_TYPE_CHAR) {
2221 errno = ESPIPE;
2222 return -1;
2223 }
2224 return _lseeki64(fd, offset, whence);
2225}
2226
2227#undef GetTickCount64
2228ULONGLONG CompatGetTickCount64(void)
2229{
2230 DECLARE_PROC_ADDR(ULONGLONG, GetTickCount64, void);
2231
2232 if (!INIT_PROC_ADDR(kernel32.dll, GetTickCount64)) {
2233 return (ULONGLONG)GetTickCount();
2234 }
2235
2236 return GetTickCount64();
2237}
2238
2239#if ENABLE_FEATURE_INSTALLER
2240/*
2241 * Enumerate the names of all hard links to a file. The first call
2242 * provides the file name as the first argument; subsequent calls must
2243 * set the first argument to NULL. Returns 0 on error or when there are
2244 * no more links.
2245 */
2246int enumerate_links(const char *file, char *name)
2247{
2248 static HANDLE h = INVALID_HANDLE_VALUE;
2249 char aname[PATH_MAX];
2250 wchar_t wname[PATH_MAX];
2251 DWORD length = PATH_MAX;
2252 DECLARE_PROC_ADDR(HANDLE, FindFirstFileNameW, LPCWSTR, DWORD, LPDWORD,
2253 PWSTR);
2254 DECLARE_PROC_ADDR(BOOL, FindNextFileNameW, HANDLE, LPDWORD, PWSTR);
2255
2256 if (!INIT_PROC_ADDR(kernel32.dll, FindFirstFileNameW) ||
2257 !INIT_PROC_ADDR(kernel32.dll, FindNextFileNameW))
2258 return 0;
2259
2260 if (file != NULL) {
2261 wchar_t wfile[PATH_MAX];
2262 MultiByteToWideChar(CP_ACP, 0, file, -1, wfile, PATH_MAX);
2263 h = FindFirstFileNameW(wfile, 0, &length, wname);
2264 if (h == INVALID_HANDLE_VALUE)
2265 return 0;
2266 }
2267 else if (!FindNextFileNameW(h, &length, wname)) {
2268 FindClose(h);
2269 h = INVALID_HANDLE_VALUE;
2270 return 0;
2271 }
2272 WideCharToMultiByte(CP_ACP, 0, wname, -1, aname, PATH_MAX, NULL, NULL);
2273 realpath(aname, name);
2274 return 1;
2275}
2276#endif
2277
2278/* Return the length of the root of a UNC path, i.e. the '//host/share'
2279 * component, or 0 if the path doesn't look like that. */
2280int FAST_FUNC unc_root_len(const char *dir)
2281{
2282 const char *s = dir + 2;
2283 int len;
2284
2285 if (!is_unc_path(dir))
2286 return 0;
2287 len = strcspn(s, "/\\");
2288 if (len == 0)
2289 return 0;
2290 s += len + 1;
2291 len = strcspn(s, "/\\");
2292 if (len == 0)
2293 return 0;
2294 s += len;
2295
2296 return s - dir;
2297}
2298
2299/* Return the length of the root of a path, i.e. either the drive or
2300 * UNC '//host/share', or 0 if the path doesn't look like that. */
2301int FAST_FUNC root_len(const char *path)
2302{
2303 if (path == NULL)
2304 return 0;
2305 if (isalpha(*path) && path[1] == ':')
2306 return 2;
2307 return unc_root_len(path);
2308}
2309
2310const char * FAST_FUNC get_system_drive(void)
2311{
2312 static const char *drive = NULL;
2313 char sysdir[PATH_MAX];
2314 int len;
2315
2316 if (drive == NULL) {
2317 UINT ret = GetSystemDirectory(sysdir, PATH_MAX);
2318 if ((ret != 0 && ret < PATH_MAX) && (len=root_len(sysdir)))
2319 drive = xstrndup(sysdir, len);
2320 else
2321 drive = "";
2322 }
2323
2324 return getenv(BB_SYSTEMROOT) ?: drive;
2325}
2326
2327int chdir_system_drive(void)
2328{
2329 const char *sd = get_system_drive();
2330 int ret = -1;
2331
2332 if (*sd)
2333 ret = chdir(auto_string(concat_path_file(sd, "")));
2334 return ret;
2335}
2336
2337/*
2338 * This function is used to make relative paths absolute before a call
2339 * to chdir_system_drive(). It's unlikely to be useful in other cases.
2340 *
2341 * If the argument is an absolute path return 'path', otherwise return
2342 * an allocated string containing the resolved path. Die on failure,
2343 * which is most likely because the file doesn't exist.
2344 */
2345char * FAST_FUNC xabsolute_path(char *path)
2346{
2347 char *rpath;
2348
2349 if (root_len(path) != 0)
2350 return path; // absolute path
2351 rpath = xmalloc_realpath(path);
2352 if (rpath)
2353 return rpath;
2354 bb_perror_msg_and_die("can't open '%s'", path);
2355}
2356
2357char * FAST_FUNC get_drive_cwd(const char *path, char *buffer, int size)
2358{
2359 char drive[3] = { *path, ':', '\0' };
2360 DWORD ret;
2361
2362 ret = GetFullPathName(drive, size, buffer, NULL);
2363 if (ret == 0 || ret > size)
2364 return NULL;
2365 return bs_to_slash(buffer);
2366}
2367
2368void FAST_FUNC fix_path_case(char *path)
2369{
2370 char resolved[PATH_MAX];
2371 int len;
2372
2373 // Canonicalise path: for physical drives this makes case match
2374 // what's stored on disk. For mapped drives, not so much.
2375 if (realpath(path, resolved) && strcasecmp(path, resolved) == 0)
2376 strcpy(path, resolved);
2377
2378 // make drive letter or UNC hostname uppercase
2379 len = root_len(path);
2380 if (len == 2) {
2381 *path = toupper(*path);
2382 }
2383 else if (len != 0) {
2384 for (path+=2; !is_dir_sep(*path); ++path) {
2385 *path = toupper(*path);
2386 }
2387 }
2388}
2389
2390void FAST_FUNC make_sparse(int fd, off_t start, off_t end)
2391{
2392 DWORD dwTemp;
2393 HANDLE fh;
2394 FILE_ZERO_DATA_INFORMATION fzdi;
2395
2396 if ((fh=(HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
2397 return;
2398
2399 DeviceIoControl(fh, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);
2400
2401 fzdi.FileOffset.QuadPart = start;
2402 fzdi.BeyondFinalZero.QuadPart = end;
2403 DeviceIoControl(fh, FSCTL_SET_ZERO_DATA, &fzdi, sizeof(fzdi),
2404 NULL, 0, &dwTemp, NULL);
2405}
2406
2407void *get_proc_addr(const char *dll, const char *function,
2408 struct proc_addr *proc)
2409{
2410 /* only do this once */
2411 if (!proc->initialized) {
2412 HANDLE hnd = LoadLibraryExA(dll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
2413
2414 /* The documentation for LoadLibraryEx says the above may fail
2415 * on Windows 7. If it does, retry using LoadLibrary with an
2416 * explicit, backslash-separated path. */
2417 if (!hnd) {
2418 char buf[PATH_MAX];
2419 UINT ret = GetSystemDirectory(buf, PATH_MAX);
2420 if (ret != 0 && ret < PATH_MAX) {
2421 char *path = concat_path_file(buf, dll);
2422 slash_to_bs(path);
2423 hnd = LoadLibrary(path);
2424 free(path);
2425 }
2426 }
2427
2428 if (hnd)
2429 proc->pfunction = GetProcAddress(hnd, function);
2430 proc->initialized = 1;
2431 }
2432 return proc->pfunction;
2433}
2434
2435int FAST_FUNC unix_path(const char *path)
2436{
2437 int i;
2438 char *p = xstrdup(path);
2439
2440#define UNIX_PATHS "/bin\0/usr/bin\0/sbin\0/usr/sbin\0"
2441 i = index_in_strings(UNIX_PATHS, dirname(p));
2442 free(p);
2443 return i >= 0;
2444}
2445
2446/* Return true if file is referenced using a path. This means a path
2447 * look-up isn't required. */
2448int FAST_FUNC has_path(const char *file)
2449{
2450 return strchr(file, '/') || strchr(file, '\\') ||
2451 has_dos_drive_prefix(file);
2452}
2453
2454/*
2455 * Test whether a path is relative to a known location (usually the
2456 * current working directory or a symlink). On Unix this is a path
2457 * that doesn't start with a slash but on Windows it also includes
2458 * paths that don't start with a backslash or a drive letter.
2459 *
2460 * Paths of the form /dir/file or c:dir/file aren't relative by this
2461 * definition.
2462 */
2463int FAST_FUNC is_relative_path(const char *path)
2464{
2465 return !is_dir_sep(path[0]) && !has_dos_drive_prefix(path);
2466}
2467
2468#if ENABLE_FEATURE_SH_STANDALONE
2469/*
2470 * In standalone shell mode it's possible there's no binary file
2471 * corresponding to an applet name. There's one case where it's
2472 * easy to determine the corresponding binary: if the applet name
2473 * matches the file name from bb_busybox_exec_path (with appropriate
2474 * allowance for 'busybox*.exe').
2475 */
2476const char * FAST_FUNC applet_to_exe(const char *name)
2477{
2478 const char *exefile = bb_basename(bb_busybox_exec_path);
2479 const char *exesuff = is_prefixed_with_case(exefile, name);
2480
2481 if (exesuff && (strcmp(name, "busybox") == 0 ||
2482 strcasecmp(exesuff, ".exe") == 0)) {
2483 return bb_busybox_exec_path;
2484 }
2485 return name;
2486}
2487#endif
2488
2489/*
2490 * Append a word to a space-separated string of words. The first
2491 * call should use a NULL pointer for str, subsequent calls should
2492 * pass an allocated string which will be freed.
2493 */
2494char * FAST_FUNC xappendword(const char *str, const char *word)
2495{
2496 char *newstr = str ? xasprintf("%s %s", str, word) : xstrdup(word);
2497 free((void *)str);
2498 return newstr;
2499}
2500
2501/*
2502 * Detect if the environment contains certain mixed-case names:
2503 *
2504 * Path is present in a standard Windows environment
2505 * ComSpec is present in WINE
2506 * ProgramData is present in Cygwin/MSYS2
2507 */
2508int
2509windows_env(void)
2510{
2511 const char *names = "PATH=\0""COMSPEC=\0""PROGRAMDATA=\0";
2512 const char *n;
2513
2514 for (char **envp = environ; envp && *envp; envp++) {
2515 for (n = names; *n; ) {
2516 if (is_prefixed_with_case(*envp, n) &&
2517 !is_prefixed_with(*envp, n)) {
2518 return TRUE;
2519 }
2520 while (*n++)
2521 ;
2522 }
2523 }
2524 return FALSE;
2525}
2526
2527void FAST_FUNC
2528change_critical_error_dialogs(const char *newval)
2529{
2530 SetErrorMode(newval && newval[0] == '1' && newval[1] == '\0' ?
2531 0 : SEM_FAILCRITICALERRORS);
2532}
2533
2534char * FAST_FUNC exe_relative_path(const char *tail)
2535{
2536 char *exepath = xstrdup(bb_busybox_exec_path);
2537 char *relpath = concat_path_file(dirname(exepath), tail);
2538 free(exepath);
2539 return relpath;
2540}
2541
2542int mingw_shell_execute(SHELLEXECUTEINFO *info)
2543{
2544 DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *);
2545 char *lpath;
2546 int ret;
2547
2548 if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) {
2549 errno = ENOSYS;
2550 return FALSE;
2551 }
2552
2553 // ShellExecuteEx() needs backslash as separator in UNC paths.
2554 lpath = xstrdup(info->lpFile);
2555 slash_to_bs(lpath);
2556 info->lpFile = lpath;
2557
2558 ret = ShellExecuteExA(info);
2559
2560 free(lpath);
2561 return ret;
2562}
2563
2564#if ENABLE_FEATURE_USE_CNG_API
2565void mingw_die_if_error(NTSTATUS status, const char *function_name) {
2566 if (!NT_SUCCESS(status)) {
2567 bb_error_msg_and_die("call to %s failed: 0x%08lX",
2568 function_name, (unsigned long)status);
2569 }
2570}
2571#endif