diff options
Diffstat (limited to 'win32/mingw.c')
-rw-r--r-- | win32/mingw.c | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/win32/mingw.c b/win32/mingw.c new file mode 100644 index 000000000..1170cd9d5 --- /dev/null +++ b/win32/mingw.c | |||
@@ -0,0 +1,1064 @@ | |||
1 | #include "libbb.h" | ||
2 | #include <userenv.h> | ||
3 | |||
4 | #if defined(__MINGW64_VERSION_MAJOR) | ||
5 | #if ENABLE_GLOBBING | ||
6 | int _dowildcard = -1; | ||
7 | #else | ||
8 | int _dowildcard = 0; | ||
9 | #endif | ||
10 | |||
11 | #undef _fmode | ||
12 | int _fmode = _O_BINARY; | ||
13 | #endif | ||
14 | |||
15 | #if !defined(__MINGW64_VERSION_MAJOR) | ||
16 | #if ENABLE_GLOBBING | ||
17 | int _CRT_glob = 1; | ||
18 | #else | ||
19 | int _CRT_glob = 0; | ||
20 | #endif | ||
21 | |||
22 | unsigned int _CRT_fmode = _O_BINARY; | ||
23 | #endif | ||
24 | |||
25 | smallint bb_got_signal; | ||
26 | |||
27 | int err_win_to_posix(DWORD winerr) | ||
28 | { | ||
29 | int error = ENOSYS; | ||
30 | switch(winerr) { | ||
31 | case ERROR_ACCESS_DENIED: error = EACCES; break; | ||
32 | case ERROR_ACCOUNT_DISABLED: error = EACCES; break; | ||
33 | case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; | ||
34 | case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; | ||
35 | case ERROR_ALREADY_EXISTS: error = EEXIST; break; | ||
36 | case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; | ||
37 | case ERROR_BAD_COMMAND: error = EIO; break; | ||
38 | case ERROR_BAD_DEVICE: error = ENODEV; break; | ||
39 | case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; | ||
40 | case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; | ||
41 | case ERROR_BAD_FORMAT: error = ENOEXEC; break; | ||
42 | case ERROR_BAD_LENGTH: error = EINVAL; break; | ||
43 | case ERROR_BAD_PATHNAME: error = ENOENT; break; | ||
44 | case ERROR_BAD_PIPE: error = EPIPE; break; | ||
45 | case ERROR_BAD_UNIT: error = ENODEV; break; | ||
46 | case ERROR_BAD_USERNAME: error = EINVAL; break; | ||
47 | case ERROR_BROKEN_PIPE: error = EPIPE; break; | ||
48 | case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; | ||
49 | case ERROR_BUSY: error = EBUSY; break; | ||
50 | case ERROR_BUSY_DRIVE: error = EBUSY; break; | ||
51 | case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; | ||
52 | case ERROR_CANNOT_MAKE: error = EACCES; break; | ||
53 | case ERROR_CANTOPEN: error = EIO; break; | ||
54 | case ERROR_CANTREAD: error = EIO; break; | ||
55 | case ERROR_CANTWRITE: error = EIO; break; | ||
56 | case ERROR_CRC: error = EIO; break; | ||
57 | case ERROR_CURRENT_DIRECTORY: error = EACCES; break; | ||
58 | case ERROR_DEVICE_IN_USE: error = EBUSY; break; | ||
59 | case ERROR_DEV_NOT_EXIST: error = ENODEV; break; | ||
60 | case ERROR_DIRECTORY: error = EINVAL; break; | ||
61 | case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; | ||
62 | case ERROR_DISK_CHANGE: error = EIO; break; | ||
63 | case ERROR_DISK_FULL: error = ENOSPC; break; | ||
64 | case ERROR_DRIVE_LOCKED: error = EBUSY; break; | ||
65 | case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; | ||
66 | case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; | ||
67 | case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; | ||
68 | case ERROR_FILE_EXISTS: error = EEXIST; break; | ||
69 | case ERROR_FILE_INVALID: error = ENODEV; break; | ||
70 | case ERROR_FILE_NOT_FOUND: error = ENOENT; break; | ||
71 | case ERROR_GEN_FAILURE: error = EIO; break; | ||
72 | case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; | ||
73 | case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; | ||
74 | case ERROR_INVALID_ACCESS: error = EACCES; break; | ||
75 | case ERROR_INVALID_ADDRESS: error = EFAULT; break; | ||
76 | case ERROR_INVALID_BLOCK: error = EFAULT; break; | ||
77 | case ERROR_INVALID_DATA: error = EINVAL; break; | ||
78 | case ERROR_INVALID_DRIVE: error = ENODEV; break; | ||
79 | case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; | ||
80 | case ERROR_INVALID_FLAGS: error = EINVAL; break; | ||
81 | case ERROR_INVALID_FUNCTION: error = ENOSYS; break; | ||
82 | case ERROR_INVALID_HANDLE: error = EBADF; break; | ||
83 | case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; | ||
84 | case ERROR_INVALID_NAME: error = EINVAL; break; | ||
85 | case ERROR_INVALID_OWNER: error = EINVAL; break; | ||
86 | case ERROR_INVALID_PARAMETER: error = EINVAL; break; | ||
87 | case ERROR_INVALID_PASSWORD: error = EPERM; break; | ||
88 | case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; | ||
89 | case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; | ||
90 | case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; | ||
91 | case ERROR_INVALID_WORKSTATION: error = EACCES; break; | ||
92 | case ERROR_IO_DEVICE: error = EIO; break; | ||
93 | case ERROR_IO_INCOMPLETE: error = EINTR; break; | ||
94 | case ERROR_LOCKED: error = EBUSY; break; | ||
95 | case ERROR_LOCK_VIOLATION: error = EACCES; break; | ||
96 | case ERROR_LOGON_FAILURE: error = EACCES; break; | ||
97 | case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; | ||
98 | case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; | ||
99 | case ERROR_MORE_DATA: error = EPIPE; break; | ||
100 | case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; | ||
101 | case ERROR_NOACCESS: error = EFAULT; break; | ||
102 | case ERROR_NONE_MAPPED: error = EINVAL; break; | ||
103 | case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; | ||
104 | case ERROR_NOT_READY: error = EAGAIN; break; | ||
105 | case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; | ||
106 | case ERROR_NO_DATA: error = EPIPE; break; | ||
107 | case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; | ||
108 | case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; | ||
109 | case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; | ||
110 | case ERROR_OPEN_FAILED: error = EIO; break; | ||
111 | case ERROR_OPEN_FILES: error = EBUSY; break; | ||
112 | case ERROR_OPERATION_ABORTED: error = EINTR; break; | ||
113 | case ERROR_OUTOFMEMORY: error = ENOMEM; break; | ||
114 | case ERROR_PASSWORD_EXPIRED: error = EACCES; break; | ||
115 | case ERROR_PATH_BUSY: error = EBUSY; break; | ||
116 | case ERROR_PATH_NOT_FOUND: error = ENOENT; break; | ||
117 | case ERROR_PIPE_BUSY: error = EBUSY; break; | ||
118 | case ERROR_PIPE_CONNECTED: error = EPIPE; break; | ||
119 | case ERROR_PIPE_LISTENING: error = EPIPE; break; | ||
120 | case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; | ||
121 | case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; | ||
122 | case ERROR_READ_FAULT: error = EIO; break; | ||
123 | case ERROR_SEEK: error = EIO; break; | ||
124 | case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; | ||
125 | case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; | ||
126 | case ERROR_SHARING_VIOLATION: error = EACCES; break; | ||
127 | case ERROR_STACK_OVERFLOW: error = ENOMEM; break; | ||
128 | case ERROR_SWAPERROR: error = ENOENT; break; | ||
129 | case ERROR_TOO_MANY_LINKS: error = EMLINK; break; | ||
130 | case ERROR_TOO_MANY_MODULES: error = EMFILE; break; | ||
131 | case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; | ||
132 | case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; | ||
133 | case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; | ||
134 | case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; | ||
135 | case ERROR_WRITE_FAULT: error = EIO; break; | ||
136 | case ERROR_WRITE_PROTECT: error = EROFS; break; | ||
137 | } | ||
138 | return error; | ||
139 | } | ||
140 | |||
141 | #undef open | ||
142 | int mingw_open (const char *filename, int oflags, ...) | ||
143 | { | ||
144 | va_list args; | ||
145 | unsigned mode; | ||
146 | int fd; | ||
147 | |||
148 | va_start(args, oflags); | ||
149 | mode = va_arg(args, int); | ||
150 | va_end(args); | ||
151 | |||
152 | if (oflags & O_NONBLOCK) { | ||
153 | oflags &= ~O_NONBLOCK; | ||
154 | } | ||
155 | if (filename && !strcmp(filename, "/dev/null")) | ||
156 | filename = "nul"; | ||
157 | fd = open(filename, oflags, mode); | ||
158 | if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) { | ||
159 | DWORD attrs = GetFileAttributes(filename); | ||
160 | if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) | ||
161 | errno = EISDIR; | ||
162 | } | ||
163 | return fd; | ||
164 | } | ||
165 | |||
166 | #undef fopen | ||
167 | FILE *mingw_fopen (const char *filename, const char *otype) | ||
168 | { | ||
169 | if (filename && !strcmp(filename, "/dev/null")) | ||
170 | filename = "nul"; | ||
171 | return fopen(filename, otype); | ||
172 | } | ||
173 | |||
174 | #undef dup2 | ||
175 | int mingw_dup2 (int fd, int fdto) | ||
176 | { | ||
177 | int ret = dup2(fd, fdto); | ||
178 | return ret != -1 ? fdto : -1; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC. | ||
183 | * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch. | ||
184 | */ | ||
185 | static inline long long filetime_to_hnsec(const FILETIME *ft) | ||
186 | { | ||
187 | long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; | ||
188 | /* Windows to Unix Epoch conversion */ | ||
189 | return winTime - 116444736000000000LL; | ||
190 | } | ||
191 | |||
192 | static inline time_t filetime_to_time_t(const FILETIME *ft) | ||
193 | { | ||
194 | return (time_t)(filetime_to_hnsec(ft) / 10000000); | ||
195 | } | ||
196 | |||
197 | static inline int file_attr_to_st_mode (DWORD attr) | ||
198 | { | ||
199 | int fMode = S_IREAD; | ||
200 | if (attr & FILE_ATTRIBUTE_DIRECTORY) | ||
201 | fMode |= S_IFDIR|S_IWRITE|S_IEXEC; | ||
202 | else | ||
203 | fMode |= S_IFREG; | ||
204 | if (!(attr & FILE_ATTRIBUTE_READONLY)) | ||
205 | fMode |= S_IWRITE; | ||
206 | return fMode; | ||
207 | } | ||
208 | |||
209 | static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) | ||
210 | { | ||
211 | if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata)) | ||
212 | return 0; | ||
213 | |||
214 | switch (GetLastError()) { | ||
215 | case ERROR_ACCESS_DENIED: | ||
216 | case ERROR_SHARING_VIOLATION: | ||
217 | case ERROR_LOCK_VIOLATION: | ||
218 | case ERROR_SHARING_BUFFER_EXCEEDED: | ||
219 | return EACCES; | ||
220 | case ERROR_BUFFER_OVERFLOW: | ||
221 | return ENAMETOOLONG; | ||
222 | case ERROR_NOT_ENOUGH_MEMORY: | ||
223 | return ENOMEM; | ||
224 | default: | ||
225 | return ENOENT; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | /* We keep the do_lstat code in a separate function to avoid recursion. | ||
230 | * When a path ends with a slash, the stat will fail with ENOENT. In | ||
231 | * this case, we strip the trailing slashes and stat again. | ||
232 | * | ||
233 | * If follow is true then act like stat() and report on the link | ||
234 | * target. Otherwise report on the link itself. | ||
235 | */ | ||
236 | static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf) | ||
237 | { | ||
238 | int err; | ||
239 | WIN32_FILE_ATTRIBUTE_DATA fdata; | ||
240 | mode_t usermode; | ||
241 | |||
242 | if (!(err = get_file_attr(file_name, &fdata))) { | ||
243 | int len = strlen(file_name); | ||
244 | |||
245 | buf->st_ino = 0; | ||
246 | buf->st_uid = DEFAULT_UID; | ||
247 | buf->st_gid = DEFAULT_GID; | ||
248 | buf->st_nlink = 1; | ||
249 | buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); | ||
250 | if (len > 4 && (!strcasecmp(file_name+len-4, ".exe") || | ||
251 | !strcasecmp(file_name+len-4, ".com"))) | ||
252 | buf->st_mode |= S_IEXEC; | ||
253 | buf->st_size = fdata.nFileSizeLow | | ||
254 | (((off64_t)fdata.nFileSizeHigh)<<32); | ||
255 | buf->st_dev = buf->st_rdev = 0; /* not used by Git */ | ||
256 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | ||
257 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | ||
258 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | ||
259 | if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { | ||
260 | WIN32_FIND_DATAA findbuf; | ||
261 | HANDLE handle = FindFirstFileA(file_name, &findbuf); | ||
262 | if (handle != INVALID_HANDLE_VALUE) { | ||
263 | if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && | ||
264 | (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { | ||
265 | if (follow) { | ||
266 | char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | ||
267 | buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); | ||
268 | } else { | ||
269 | buf->st_mode = S_IFLNK; | ||
270 | } | ||
271 | buf->st_mode |= S_IREAD; | ||
272 | if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) | ||
273 | buf->st_mode |= S_IWRITE; | ||
274 | } | ||
275 | FindClose(handle); | ||
276 | } | ||
277 | } | ||
278 | usermode = buf->st_mode & S_IRWXU; | ||
279 | buf->st_mode |= (usermode >> 3) | ((usermode >> 6) & ~S_IWOTH); | ||
280 | |||
281 | /* | ||
282 | * Assume a block is 4096 bytes and calculate number of 512 byte | ||
283 | * sectors. | ||
284 | */ | ||
285 | buf->st_blksize = 4096; | ||
286 | buf->st_blocks = ((buf->st_size+4095)>>12)<<3; | ||
287 | return 0; | ||
288 | } | ||
289 | errno = err; | ||
290 | return -1; | ||
291 | } | ||
292 | |||
293 | /* We provide our own lstat/fstat functions, since the provided | ||
294 | * lstat/fstat functions are so slow. These stat functions are | ||
295 | * tailored for Git's usage (read: fast), and are not meant to be | ||
296 | * complete. Note that Git stat()s are redirected to mingw_lstat() | ||
297 | * too, since Windows doesn't really handle symlinks that well. | ||
298 | */ | ||
299 | static int do_stat_internal(int follow, const char *file_name, struct mingw_stat *buf) | ||
300 | { | ||
301 | int namelen; | ||
302 | char alt_name[PATH_MAX]; | ||
303 | |||
304 | if (!do_lstat(follow, file_name, buf)) | ||
305 | return 0; | ||
306 | |||
307 | /* if file_name ended in a '/', Windows returned ENOENT; | ||
308 | * try again without trailing slashes | ||
309 | */ | ||
310 | if (errno != ENOENT) | ||
311 | return -1; | ||
312 | |||
313 | namelen = strlen(file_name); | ||
314 | if (namelen && file_name[namelen-1] != '/') | ||
315 | return -1; | ||
316 | while (namelen && file_name[namelen-1] == '/') | ||
317 | --namelen; | ||
318 | if (!namelen || namelen >= PATH_MAX) | ||
319 | return -1; | ||
320 | |||
321 | memcpy(alt_name, file_name, namelen); | ||
322 | alt_name[namelen] = 0; | ||
323 | return do_lstat(follow, alt_name, buf); | ||
324 | } | ||
325 | |||
326 | int mingw_lstat(const char *file_name, struct mingw_stat *buf) | ||
327 | { | ||
328 | return do_stat_internal(0, file_name, buf); | ||
329 | } | ||
330 | int mingw_stat(const char *file_name, struct mingw_stat *buf) | ||
331 | { | ||
332 | return do_stat_internal(1, file_name, buf); | ||
333 | } | ||
334 | |||
335 | int mingw_fstat(int fd, struct mingw_stat *buf) | ||
336 | { | ||
337 | HANDLE fh = (HANDLE)_get_osfhandle(fd); | ||
338 | BY_HANDLE_FILE_INFORMATION fdata; | ||
339 | |||
340 | if (fh == INVALID_HANDLE_VALUE) { | ||
341 | errno = EBADF; | ||
342 | return -1; | ||
343 | } | ||
344 | /* direct non-file handles to MS's fstat() */ | ||
345 | if (GetFileType(fh) != FILE_TYPE_DISK) { | ||
346 | struct _stati64 buf64; | ||
347 | |||
348 | if ( _fstati64(fd, &buf64) != 0 ) { | ||
349 | return -1; | ||
350 | } | ||
351 | buf->st_dev = 0; | ||
352 | buf->st_ino = 0; | ||
353 | buf->st_mode = S_IREAD|S_IWRITE; | ||
354 | buf->st_nlink = 1; | ||
355 | buf->st_uid = DEFAULT_UID; | ||
356 | buf->st_gid = DEFAULT_GID; | ||
357 | buf->st_rdev = 0; | ||
358 | buf->st_size = buf64.st_size; | ||
359 | buf->st_atime = buf64.st_atime; | ||
360 | buf->st_mtime = buf64.st_mtime; | ||
361 | buf->st_ctime = buf64.st_ctime; | ||
362 | buf->st_blksize = 4096; | ||
363 | buf->st_blocks = ((buf64.st_size+4095)>>12)<<3; | ||
364 | } | ||
365 | |||
366 | if (GetFileInformationByHandle(fh, &fdata)) { | ||
367 | buf->st_ino = 0; | ||
368 | buf->st_uid = DEFAULT_UID; | ||
369 | buf->st_gid = DEFAULT_GID; | ||
370 | /* could use fdata.nNumberOfLinks but it's inconsistent with stat */ | ||
371 | buf->st_nlink = 1; | ||
372 | buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); | ||
373 | buf->st_size = fdata.nFileSizeLow | | ||
374 | (((off64_t)fdata.nFileSizeHigh)<<32); | ||
375 | buf->st_dev = buf->st_rdev = 0; /* not used by Git */ | ||
376 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | ||
377 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | ||
378 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | ||
379 | buf->st_blksize = 4096; | ||
380 | buf->st_blocks = ((buf->st_size+4095)>>12)<<3; | ||
381 | return 0; | ||
382 | } | ||
383 | errno = EBADF; | ||
384 | return -1; | ||
385 | } | ||
386 | |||
387 | static inline void timeval_to_filetime(const struct timeval tv, FILETIME *ft) | ||
388 | { | ||
389 | long long winTime = ((tv.tv_sec * 1000000LL) + tv.tv_usec) * 10LL + 116444736000000000LL; | ||
390 | ft->dwLowDateTime = winTime; | ||
391 | ft->dwHighDateTime = winTime >> 32; | ||
392 | } | ||
393 | |||
394 | int utimes(const char *file_name, const struct timeval tims[2]) | ||
395 | { | ||
396 | FILETIME mft, aft; | ||
397 | HANDLE fh; | ||
398 | DWORD flags, attrs; | ||
399 | int rc; | ||
400 | |||
401 | flags = FILE_ATTRIBUTE_NORMAL; | ||
402 | |||
403 | /* must have write permission */ | ||
404 | attrs = GetFileAttributes(file_name); | ||
405 | if ( attrs != INVALID_FILE_ATTRIBUTES ) { | ||
406 | if ( attrs & FILE_ATTRIBUTE_READONLY ) { | ||
407 | /* ignore errors here; open() will report them */ | ||
408 | SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY); | ||
409 | } | ||
410 | |||
411 | if ( attrs & FILE_ATTRIBUTE_DIRECTORY ) { | ||
412 | flags = FILE_FLAG_BACKUP_SEMANTICS; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | fh = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE, | ||
417 | FILE_SHARE_READ|FILE_SHARE_WRITE, | ||
418 | NULL, OPEN_EXISTING, flags, NULL); | ||
419 | if ( fh == INVALID_HANDLE_VALUE ) { | ||
420 | errno = err_win_to_posix(GetLastError()); | ||
421 | rc = -1; | ||
422 | goto revert_attrs; | ||
423 | } | ||
424 | |||
425 | if (tims) { | ||
426 | timeval_to_filetime(tims[0], &aft); | ||
427 | timeval_to_filetime(tims[1], &mft); | ||
428 | } | ||
429 | else { | ||
430 | GetSystemTimeAsFileTime(&mft); | ||
431 | aft = mft; | ||
432 | } | ||
433 | if (!SetFileTime(fh, NULL, &aft, &mft)) { | ||
434 | errno = EINVAL; | ||
435 | rc = -1; | ||
436 | } else | ||
437 | rc = 0; | ||
438 | CloseHandle(fh); | ||
439 | |||
440 | revert_attrs: | ||
441 | if (attrs != INVALID_FILE_ATTRIBUTES && | ||
442 | (attrs & FILE_ATTRIBUTE_READONLY)) { | ||
443 | /* ignore errors again */ | ||
444 | SetFileAttributes(file_name, attrs); | ||
445 | } | ||
446 | return rc; | ||
447 | } | ||
448 | |||
449 | unsigned int sleep (unsigned int seconds) | ||
450 | { | ||
451 | Sleep(seconds*1000); | ||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | int nanosleep(const struct timespec *req, struct timespec *rem) | ||
456 | { | ||
457 | if (req->tv_nsec < 0 || 1000000000 <= req->tv_nsec) { | ||
458 | errno = EINVAL; | ||
459 | return -1; | ||
460 | } | ||
461 | |||
462 | Sleep(req->tv_sec*1000 + req->tv_nsec/1000000); | ||
463 | |||
464 | /* Sleep is not interruptible. So there is no remaining delay. */ | ||
465 | if (rem != NULL) { | ||
466 | rem->tv_sec = 0; | ||
467 | rem->tv_nsec = 0; | ||
468 | } | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * Windows' mktemp returns NULL on error whereas POSIX always returns the | ||
475 | * template and signals an error by making it an empty string. | ||
476 | */ | ||
477 | #undef mktemp | ||
478 | char *mingw_mktemp(char *template) | ||
479 | { | ||
480 | if ( mktemp(template) == NULL ) { | ||
481 | template[0] = '\0'; | ||
482 | } | ||
483 | |||
484 | return template; | ||
485 | } | ||
486 | |||
487 | int mkstemp(char *template) | ||
488 | { | ||
489 | char *filename = mktemp(template); | ||
490 | if (filename == NULL) | ||
491 | return -1; | ||
492 | return open(filename, O_RDWR | O_CREAT, 0600); | ||
493 | } | ||
494 | |||
495 | int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM) | ||
496 | { | ||
497 | FILETIME ft; | ||
498 | long long hnsec; | ||
499 | |||
500 | GetSystemTimeAsFileTime(&ft); | ||
501 | hnsec = filetime_to_hnsec(&ft); | ||
502 | tv->tv_sec = hnsec / 10000000; | ||
503 | tv->tv_usec = (hnsec % 10000000) / 10; | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | int pipe(int filedes[2]) | ||
508 | { | ||
509 | if (_pipe(filedes, PIPE_BUF, 0) < 0) | ||
510 | return -1; | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | struct tm *gmtime_r(const time_t *timep, struct tm *result) | ||
515 | { | ||
516 | /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */ | ||
517 | memcpy(result, gmtime(timep), sizeof(struct tm)); | ||
518 | return result; | ||
519 | } | ||
520 | |||
521 | struct tm *localtime_r(const time_t *timep, struct tm *result) | ||
522 | { | ||
523 | /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */ | ||
524 | memcpy(result, localtime(timep), sizeof(struct tm)); | ||
525 | return result; | ||
526 | } | ||
527 | |||
528 | #undef getcwd | ||
529 | char *mingw_getcwd(char *pointer, int len) | ||
530 | { | ||
531 | int i; | ||
532 | char *ret = getcwd(pointer, len); | ||
533 | if (!ret) | ||
534 | return ret; | ||
535 | for (i = 0; ret[i]; i++) | ||
536 | if (ret[i] == '\\') | ||
537 | ret[i] = '/'; | ||
538 | return ret; | ||
539 | } | ||
540 | |||
541 | #undef rename | ||
542 | int mingw_rename(const char *pold, const char *pnew) | ||
543 | { | ||
544 | DWORD attrs; | ||
545 | |||
546 | /* | ||
547 | * Try native rename() first to get errno right. | ||
548 | * It is based on MoveFile(), which cannot overwrite existing files. | ||
549 | */ | ||
550 | if (!rename(pold, pnew)) | ||
551 | return 0; | ||
552 | if (errno != EEXIST) | ||
553 | return -1; | ||
554 | if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) | ||
555 | return 0; | ||
556 | /* TODO: translate more errors */ | ||
557 | if (GetLastError() == ERROR_ACCESS_DENIED && | ||
558 | (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) { | ||
559 | if (attrs & FILE_ATTRIBUTE_DIRECTORY) { | ||
560 | errno = EISDIR; | ||
561 | return -1; | ||
562 | } | ||
563 | if ((attrs & FILE_ATTRIBUTE_READONLY) && | ||
564 | SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { | ||
565 | if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) | ||
566 | return 0; | ||
567 | /* revert file attributes on failure */ | ||
568 | SetFileAttributes(pnew, attrs); | ||
569 | } | ||
570 | } | ||
571 | errno = EACCES; | ||
572 | return -1; | ||
573 | } | ||
574 | |||
575 | static char *gethomedir(void) | ||
576 | { | ||
577 | static char buf[PATH_MAX]; | ||
578 | DWORD len = sizeof(buf); | ||
579 | HANDLE h; | ||
580 | char *s; | ||
581 | |||
582 | buf[0] = '\0'; | ||
583 | if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h) ) | ||
584 | return buf; | ||
585 | |||
586 | if ( !GetUserProfileDirectory(h, buf, &len) ) { | ||
587 | CloseHandle(h); | ||
588 | return buf; | ||
589 | } | ||
590 | |||
591 | CloseHandle(h); | ||
592 | |||
593 | for ( s=buf; *s; ++s ) { | ||
594 | if ( *s == '\\' ) { | ||
595 | *s = '/'; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | return buf; | ||
600 | } | ||
601 | |||
602 | static char *get_user_name(void) | ||
603 | { | ||
604 | static char user_name[100] = ""; | ||
605 | char *s; | ||
606 | DWORD len = sizeof(user_name); | ||
607 | |||
608 | if ( user_name[0] != '\0' ) { | ||
609 | return user_name; | ||
610 | } | ||
611 | |||
612 | if ( !GetUserName(user_name, &len) ) { | ||
613 | return NULL; | ||
614 | } | ||
615 | |||
616 | for ( s=user_name; *s; ++s ) { | ||
617 | if ( *s == ' ' ) { | ||
618 | *s = '_'; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | return user_name; | ||
623 | } | ||
624 | |||
625 | struct passwd *getpwnam(const char *name) | ||
626 | { | ||
627 | const char *myname; | ||
628 | |||
629 | if ( (myname=get_user_name()) != NULL && | ||
630 | strcmp(myname, name) == 0 ) { | ||
631 | return getpwuid(DEFAULT_UID); | ||
632 | } | ||
633 | |||
634 | return NULL; | ||
635 | } | ||
636 | |||
637 | struct passwd *getpwuid(uid_t uid UNUSED_PARAM) | ||
638 | { | ||
639 | static struct passwd p; | ||
640 | |||
641 | if ( (p.pw_name=get_user_name()) == NULL ) { | ||
642 | return NULL; | ||
643 | } | ||
644 | p.pw_passwd = (char *)"secret"; | ||
645 | p.pw_gecos = (char *)"unknown"; | ||
646 | p.pw_dir = gethomedir(); | ||
647 | p.pw_shell = NULL; | ||
648 | p.pw_uid = DEFAULT_UID; | ||
649 | p.pw_gid = DEFAULT_GID; | ||
650 | |||
651 | return &p; | ||
652 | } | ||
653 | |||
654 | struct group *getgrgid(gid_t gid UNUSED_PARAM) | ||
655 | { | ||
656 | static char *members[2] = { NULL, NULL }; | ||
657 | static struct group g; | ||
658 | |||
659 | if ( (g.gr_name=get_user_name()) == NULL ) { | ||
660 | return NULL; | ||
661 | } | ||
662 | g.gr_passwd = (char *)"secret"; | ||
663 | g.gr_gid = DEFAULT_GID; | ||
664 | members[0] = g.gr_name; | ||
665 | g.gr_mem = members; | ||
666 | |||
667 | return &g; | ||
668 | } | ||
669 | |||
670 | int getgrouplist(const char *user UNUSED_PARAM, gid_t group UNUSED_PARAM, | ||
671 | gid_t *groups, int *ngroups) | ||
672 | { | ||
673 | if ( *ngroups == 0 ) { | ||
674 | *ngroups = 1; | ||
675 | return -1; | ||
676 | } | ||
677 | |||
678 | *ngroups = 1; | ||
679 | groups[0] = DEFAULT_GID; | ||
680 | return 1; | ||
681 | } | ||
682 | |||
683 | int getgroups(int n, gid_t *groups) | ||
684 | { | ||
685 | if ( n == 0 ) { | ||
686 | return 1; | ||
687 | } | ||
688 | |||
689 | groups[0] = DEFAULT_GID; | ||
690 | return 1; | ||
691 | } | ||
692 | |||
693 | int getlogin_r(char *buf, size_t len) | ||
694 | { | ||
695 | char *name; | ||
696 | |||
697 | if ( (name=get_user_name()) == NULL ) { | ||
698 | return -1; | ||
699 | } | ||
700 | |||
701 | if ( strlen(name) >= len ) { | ||
702 | errno = ERANGE; | ||
703 | return -1; | ||
704 | } | ||
705 | |||
706 | strcpy(buf, name); | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | long sysconf(int name) | ||
711 | { | ||
712 | if ( name == _SC_CLK_TCK ) { | ||
713 | return 100; | ||
714 | } | ||
715 | errno = EINVAL; | ||
716 | return -1; | ||
717 | } | ||
718 | |||
719 | clock_t times(struct tms *buf) | ||
720 | { | ||
721 | buf->tms_utime = 0; | ||
722 | buf->tms_stime = 0; | ||
723 | buf->tms_cutime = 0; | ||
724 | buf->tms_cstime = 0; | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | int link(const char *oldpath, const char *newpath) | ||
730 | { | ||
731 | typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES); | ||
732 | static T create_hard_link = NULL; | ||
733 | if (!create_hard_link) { | ||
734 | create_hard_link = (T) GetProcAddress( | ||
735 | GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); | ||
736 | if (!create_hard_link) | ||
737 | create_hard_link = (T)-1; | ||
738 | } | ||
739 | if (create_hard_link == (T)-1) { | ||
740 | errno = ENOSYS; | ||
741 | return -1; | ||
742 | } | ||
743 | if (!create_hard_link(newpath, oldpath, NULL)) { | ||
744 | errno = err_win_to_posix(GetLastError()); | ||
745 | return -1; | ||
746 | } | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | char *realpath(const char *path, char *resolved_path) | ||
751 | { | ||
752 | /* FIXME: need normalization */ | ||
753 | return strcpy(resolved_path, path); | ||
754 | } | ||
755 | |||
756 | const char *get_busybox_exec_path(void) | ||
757 | { | ||
758 | static char path[PATH_MAX] = ""; | ||
759 | |||
760 | if (!*path) | ||
761 | GetModuleFileName(NULL, path, PATH_MAX); | ||
762 | return path; | ||
763 | } | ||
764 | |||
765 | #undef mkdir | ||
766 | int mingw_mkdir(const char *path, int mode UNUSED_PARAM) | ||
767 | { | ||
768 | int ret; | ||
769 | struct stat st; | ||
770 | int lerrno = 0; | ||
771 | |||
772 | if ( (ret=mkdir(path)) < 0 ) { | ||
773 | lerrno = errno; | ||
774 | if ( lerrno == EACCES && stat(path, &st) == 0 ) { | ||
775 | ret = 0; | ||
776 | lerrno = 0; | ||
777 | } | ||
778 | } | ||
779 | |||
780 | errno = lerrno; | ||
781 | return ret; | ||
782 | } | ||
783 | |||
784 | #undef chmod | ||
785 | int mingw_chmod(const char *path, int mode) | ||
786 | { | ||
787 | WIN32_FILE_ATTRIBUTE_DATA fdata; | ||
788 | |||
789 | if ( get_file_attr(path, &fdata) == 0 && | ||
790 | fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { | ||
791 | mode |= 0222; | ||
792 | } | ||
793 | |||
794 | return chmod(path, mode); | ||
795 | } | ||
796 | |||
797 | int fcntl(int fd, int cmd, ...) | ||
798 | { | ||
799 | va_list arg; | ||
800 | int result = -1; | ||
801 | char *fds; | ||
802 | int target, i, newfd; | ||
803 | |||
804 | va_start(arg, cmd); | ||
805 | |||
806 | switch (cmd) { | ||
807 | case F_GETFD: | ||
808 | case F_SETFD: | ||
809 | case F_GETFL: | ||
810 | /* | ||
811 | * Our fake F_GETFL won't matter if the return value is used as | ||
812 | * fcntl(fd, F_SETFL, ret|something); | ||
813 | * because F_SETFL isn't supported either. | ||
814 | */ | ||
815 | result = 0; | ||
816 | break; | ||
817 | case F_DUPFD: | ||
818 | target = va_arg(arg, int); | ||
819 | fds = xzalloc(target); | ||
820 | while ((newfd = dup(fd)) < target && newfd >= 0) { | ||
821 | fds[newfd] = 1; | ||
822 | } | ||
823 | for (i = 0; i < target; ++i) { | ||
824 | if (fds[i]) { | ||
825 | close(i); | ||
826 | } | ||
827 | } | ||
828 | free(fds); | ||
829 | result = newfd; | ||
830 | break; | ||
831 | default: | ||
832 | errno = ENOSYS; | ||
833 | break; | ||
834 | } | ||
835 | |||
836 | va_end(arg); | ||
837 | return result; | ||
838 | } | ||
839 | |||
840 | #undef unlink | ||
841 | int mingw_unlink(const char *pathname) | ||
842 | { | ||
843 | /* read-only files cannot be removed */ | ||
844 | chmod(pathname, 0666); | ||
845 | return unlink(pathname); | ||
846 | } | ||
847 | |||
848 | #undef strftime | ||
849 | size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm) | ||
850 | { | ||
851 | size_t ret; | ||
852 | char day[3]; | ||
853 | char *t; | ||
854 | char *fmt, *newfmt; | ||
855 | struct tm tm2; | ||
856 | int m; | ||
857 | |||
858 | /* | ||
859 | * Emulate the '%e' and '%s' formats that Windows' strftime lacks. | ||
860 | * Happily, the string that replaces '%e' is two characters long. | ||
861 | * '%s' is a bit more complicated. | ||
862 | */ | ||
863 | fmt = xstrdup(format); | ||
864 | for ( t=fmt; *t; ++t ) { | ||
865 | if ( *t == '%' ) { | ||
866 | if ( t[1] == 'e' ) { | ||
867 | if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) { | ||
868 | sprintf(day, "%2d", tm->tm_mday); | ||
869 | } | ||
870 | else { | ||
871 | strcpy(day, " "); | ||
872 | } | ||
873 | memcpy(t++, day, 2); | ||
874 | } | ||
875 | else if ( t[1] == 's' ) { | ||
876 | *t = '\0'; | ||
877 | m = t - fmt; | ||
878 | tm2 = *tm; | ||
879 | newfmt = xasprintf("%s%d%s", fmt, (int)mktime(&tm2), t+2); | ||
880 | free(fmt); | ||
881 | t = newfmt + m + 1; | ||
882 | fmt = newfmt; | ||
883 | } | ||
884 | else if ( t[1] == 'z' ) { | ||
885 | char buffer[16] = ""; | ||
886 | |||
887 | *t = '\0'; | ||
888 | m = t - fmt; | ||
889 | _tzset(); | ||
890 | if ( tm->tm_isdst >= 0 ) { | ||
891 | int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0); | ||
892 | int hr, min; | ||
893 | |||
894 | if ( offset > 0 ) { | ||
895 | buffer[0] = '-'; | ||
896 | } | ||
897 | else { | ||
898 | buffer[0] = '+'; | ||
899 | offset = -offset; | ||
900 | } | ||
901 | |||
902 | hr = offset / 3600; | ||
903 | min = (offset % 3600) / 60; | ||
904 | sprintf(buffer+1, "%02d%02d", hr, min); | ||
905 | } | ||
906 | newfmt = xasprintf("%s%s%s", fmt, buffer, t+2); | ||
907 | free(fmt); | ||
908 | t = newfmt + m + 1; | ||
909 | fmt = newfmt; | ||
910 | } | ||
911 | else if ( t[1] != '\0' ) { | ||
912 | ++t; | ||
913 | } | ||
914 | } | ||
915 | } | ||
916 | |||
917 | ret = strftime(buf, max, fmt, tm); | ||
918 | free(fmt); | ||
919 | |||
920 | return ret; | ||
921 | } | ||
922 | |||
923 | int stime(time_t *t UNUSED_PARAM) | ||
924 | { | ||
925 | errno = EPERM; | ||
926 | return -1; | ||
927 | } | ||
928 | |||
929 | #undef access | ||
930 | int mingw_access(const char *name, int mode) | ||
931 | { | ||
932 | int ret; | ||
933 | struct stat s; | ||
934 | int fd, n, sig; | ||
935 | unsigned int offset; | ||
936 | unsigned char buf[1024]; | ||
937 | |||
938 | /* Windows can only handle test for existence, read or write */ | ||
939 | if (mode == F_OK || (mode & ~X_OK)) { | ||
940 | ret = _access(name, mode & ~X_OK); | ||
941 | if (ret < 0 || !(mode & X_OK)) { | ||
942 | return ret; | ||
943 | } | ||
944 | } | ||
945 | |||
946 | if (!mingw_stat(name, &s) && S_ISREG(s.st_mode)) { | ||
947 | |||
948 | /* stat marks .exe and .com files as executable */ | ||
949 | if ((s.st_mode&S_IEXEC)) { | ||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | fd = open(name, O_RDONLY); | ||
954 | if (fd < 0) | ||
955 | return -1; | ||
956 | n = read(fd, buf, sizeof(buf)-1); | ||
957 | close(fd); | ||
958 | if (n < 4) /* at least '#!/x' and not error */ | ||
959 | return -1; | ||
960 | |||
961 | /* shell script */ | ||
962 | if (buf[0] == '#' && buf[1] == '!') { | ||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | /* | ||
967 | * Poke about in file to see if it's a PE binary. I've just copied | ||
968 | * the magic from the file command. | ||
969 | */ | ||
970 | if (buf[0] == 'M' && buf[1] == 'Z') { | ||
971 | offset = (buf[0x19] << 8) + buf[0x18]; | ||
972 | if (offset > 0x3f) { | ||
973 | offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) + | ||
974 | (buf[0x3d] << 8) + buf[0x3c]; | ||
975 | if (offset < sizeof(buf)-100) { | ||
976 | if (memcmp(buf+offset, "PE\0\0", 4) == 0) { | ||
977 | sig = (buf[offset+25] << 8) + buf[offset+24]; | ||
978 | if (sig == 0x10b || sig == 0x20b) { | ||
979 | sig = (buf[offset+23] << 8) + buf[offset+22]; | ||
980 | if ((sig & 0x2000) != 0) { | ||
981 | /* DLL */ | ||
982 | return -1; | ||
983 | } | ||
984 | sig = buf[offset+92]; | ||
985 | return !(sig == 1 || sig == 2 || | ||
986 | sig == 3 || sig == 7); | ||
987 | } | ||
988 | } | ||
989 | } | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | return -1; | ||
995 | } | ||
996 | |||
997 | #undef rmdir | ||
998 | int mingw_rmdir(const char *path) | ||
999 | { | ||
1000 | /* read-only directories cannot be removed */ | ||
1001 | chmod(path, 0666); | ||
1002 | return rmdir(path); | ||
1003 | } | ||
1004 | |||
1005 | /* check if path can be made into an executable by adding a suffix; | ||
1006 | * return an allocated string containing the path if it can; | ||
1007 | * return NULL if not. | ||
1008 | * | ||
1009 | * if path already has a suffix don't even bother trying | ||
1010 | */ | ||
1011 | char *file_is_win32_executable(const char *p) | ||
1012 | { | ||
1013 | char *path; | ||
1014 | int len = strlen(p); | ||
1015 | |||
1016 | if (len > 4 && (!strcasecmp(p+len-4, ".exe") || | ||
1017 | !strcasecmp(p+len-4, ".com"))) { | ||
1018 | return NULL; | ||
1019 | } | ||
1020 | |||
1021 | if ( (path=malloc(len+5)) != NULL ) { | ||
1022 | memcpy(path, p, len); | ||
1023 | memcpy(path+len, ".exe", 5); | ||
1024 | if (file_is_executable(path)) { | ||
1025 | return path; | ||
1026 | } | ||
1027 | memcpy(path+len, ".com", 5); | ||
1028 | if (file_is_executable(path)) { | ||
1029 | return path; | ||
1030 | } | ||
1031 | free(path); | ||
1032 | } | ||
1033 | |||
1034 | return NULL; | ||
1035 | } | ||
1036 | |||
1037 | #undef opendir | ||
1038 | DIR *mingw_opendir(const char *path) | ||
1039 | { | ||
1040 | char name[4]; | ||
1041 | |||
1042 | if (isalpha(path[0]) && path[1] == ':' && path[2] == '\0') { | ||
1043 | strcpy(name, path); | ||
1044 | name[2] = '/'; | ||
1045 | name[3] = '\0'; | ||
1046 | path = name; | ||
1047 | } | ||
1048 | |||
1049 | return opendir(path); | ||
1050 | } | ||
1051 | |||
1052 | off_t mingw_lseek(int fd, off_t offset, int whence) | ||
1053 | { | ||
1054 | HANDLE h = (HANDLE)_get_osfhandle(fd); | ||
1055 | if (h == INVALID_HANDLE_VALUE) { | ||
1056 | errno = EBADF; | ||
1057 | return -1; | ||
1058 | } | ||
1059 | if (GetFileType(h) != FILE_TYPE_DISK) { | ||
1060 | errno = ESPIPE; | ||
1061 | return -1; | ||
1062 | } | ||
1063 | return _lseeki64(fd, offset, whence); | ||
1064 | } | ||