diff options
Diffstat (limited to 'win32/mingw.c')
-rw-r--r-- | win32/mingw.c | 2571 |
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 | ||
12 | extern int _setargv(void); | ||
13 | int _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 | ||
30 | int _dowildcard = 0; | ||
31 | #endif | ||
32 | |||
33 | #undef _fmode | ||
34 | int _fmode = _O_BINARY; | ||
35 | #endif | ||
36 | |||
37 | #if !defined(__MINGW64_VERSION_MAJOR) | ||
38 | #if ENABLE_GLOBBING | ||
39 | int _CRT_glob = 1; | ||
40 | #else | ||
41 | int _CRT_glob = 0; | ||
42 | #endif | ||
43 | |||
44 | unsigned int _CRT_fmode = _O_BINARY; | ||
45 | #endif | ||
46 | |||
47 | smallint bb_got_signal; | ||
48 | static mode_t current_umask = DEFAULT_UMASK; | ||
49 | |||
50 | #pragma GCC optimize ("no-if-conversion") | ||
51 | int 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 | ||
170 | char *mingw_strerror(int errnum) | ||
171 | { | ||
172 | if (errnum == ELOOP) | ||
173 | return (char *)"Too many levels of symbolic links"; | ||
174 | return strerror(errnum); | ||
175 | } | ||
176 | |||
177 | char *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 | |||
186 | static int zero_fd = -1; | ||
187 | static 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 | */ | ||
194 | int 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 | |||
202 | void 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) | ||
211 | static 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 | |||
223 | static int mingw_is_directory(const char *path); | ||
224 | #undef open | ||
225 | int 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 | |||
262 | int 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 | |||
274 | ssize_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 | ||
284 | FILE *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 | ||
296 | ssize_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 | ||
309 | int 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 | ||
321 | int 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 | */ | ||
331 | static 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 | |||
338 | static 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 | |||
349 | static 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 | |||
363 | static 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 | ||
422 | mode_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 | */ | ||
441 | static 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 | ||
519 | static 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 | |||
606 | static 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 | |||
626 | static 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 | |||
636 | static 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 | */ | ||
651 | int 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 | */ | ||
683 | static 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 | |||
790 | int mingw_lstat(const char *file_name, struct mingw_stat *buf) | ||
791 | { | ||
792 | return do_lstat(0, file_name, buf); | ||
793 | } | ||
794 | |||
795 | int 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 | ||
803 | int 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 | |||
870 | static 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 | |||
878 | static 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 | |||
910 | int 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 | |||
923 | int 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 | |||
950 | int 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 | |||
968 | unsigned int sleep (unsigned int seconds) | ||
969 | { | ||
970 | Sleep(seconds*1000); | ||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | int 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 | ||
997 | char *mingw_mktemp(char *template) | ||
998 | { | ||
999 | if ( mktemp(template) == NULL ) { | ||
1000 | template[0] = '\0'; | ||
1001 | } | ||
1002 | |||
1003 | return template; | ||
1004 | } | ||
1005 | |||
1006 | int 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 | |||
1014 | int 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 | |||
1026 | int 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 | |||
1039 | int 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 | |||
1062 | int pipe(int filedes[2]) | ||
1063 | { | ||
1064 | if (_pipe(filedes, PIPE_BUF, 0) < 0) | ||
1065 | return -1; | ||
1066 | return 0; | ||
1067 | } | ||
1068 | |||
1069 | struct 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 | |||
1076 | struct 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 | ||
1084 | char *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 | ||
1093 | int 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 | |||
1129 | static 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 | ||
1151 | char *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 | */ | ||
1183 | int | ||
1184 | elevation_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 | |||
1217 | int getuid(void) | ||
1218 | { | ||
1219 | return elevation_state() == (ELEVATED_PRIVILEGE | ADMIN_ENABLED) ? | ||
1220 | 0 : DEFAULT_UID; | ||
1221 | } | ||
1222 | |||
1223 | struct 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 | |||
1238 | struct 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 | |||
1257 | struct 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 | ||
1277 | int 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 | |||
1290 | int 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 | |||
1301 | int 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 | |||
1318 | long sysconf(int name) | ||
1319 | { | ||
1320 | if ( name == _SC_CLK_TCK ) { | ||
1321 | return TICKS_PER_SECOND; | ||
1322 | } | ||
1323 | errno = EINVAL; | ||
1324 | return -1; | ||
1325 | } | ||
1326 | |||
1327 | clock_t times(struct tms *buf) | ||
1328 | { | ||
1329 | memset(buf, 0, sizeof(*buf)); | ||
1330 | return 0; | ||
1331 | } | ||
1332 | |||
1333 | int 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 | |||
1356 | int 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 | ||
1399 | static 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 | |||
1438 | static 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 | |||
1478 | int 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 | |||
1532 | static 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 | |||
1551 | static 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 | */ | ||
1607 | char *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 | |||
1629 | static 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 | */ | ||
1662 | typedef 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 | ||
1671 | char * 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 | |||
1732 | const 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 | ||
1748 | int 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 | ||
1767 | int 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 | ||
1787 | int 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 | |||
1795 | int 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 | ||
1840 | int 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 | |||
1858 | struct pagefile_info { | ||
1859 | SIZE_T total; | ||
1860 | SIZE_T in_use; | ||
1861 | }; | ||
1862 | |||
1863 | static BOOL CALLBACK | ||
1864 | pagefile_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 | |||
1874 | int 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 | ||
1911 | size_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 | ||
2002 | int 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 | |||
2025 | int 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 | |||
2038 | void 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 | ||
2062 | static const char win_suffix[NUMEXT][4] = { "com", "exe", "sh", "bat", "cmd" }; | ||
2063 | |||
2064 | static 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 | |||
2079 | int has_bat_suffix(const char *name) | ||
2080 | { | ||
2081 | return has_win_suffix(name, 3); | ||
2082 | } | ||
2083 | |||
2084 | int has_exe_suffix(const char *name) | ||
2085 | { | ||
2086 | return has_win_suffix(name, 0); | ||
2087 | } | ||
2088 | |||
2089 | int 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. */ | ||
2096 | char *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 | */ | ||
2110 | int | ||
2111 | add_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 | */ | ||
2134 | char * | ||
2135 | file_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 | |||
2146 | char * 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 | ||
2159 | MINGW_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 | |||
2172 | void 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. */ | ||
2184 | void 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 | |||
2199 | size_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 | |||
2211 | off_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 | ||
2228 | ULONGLONG 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 | */ | ||
2246 | int 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. */ | ||
2280 | int 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. */ | ||
2301 | int 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 | |||
2310 | const 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 | |||
2327 | int 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 | */ | ||
2345 | char * 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 | |||
2357 | char * 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 | |||
2368 | void 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 | |||
2390 | void 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 | |||
2407 | void *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 | |||
2435 | int 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. */ | ||
2448 | int 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 | */ | ||
2463 | int 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 | */ | ||
2476 | const 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 | */ | ||
2494 | char * 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 | */ | ||
2508 | int | ||
2509 | windows_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 | |||
2527 | void FAST_FUNC | ||
2528 | change_critical_error_dialogs(const char *newval) | ||
2529 | { | ||
2530 | SetErrorMode(newval && newval[0] == '1' && newval[1] == '\0' ? | ||
2531 | 0 : SEM_FAILCRITICALERRORS); | ||
2532 | } | ||
2533 | |||
2534 | char * 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 | |||
2542 | int 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 | ||
2565 | void 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 | ||