aboutsummaryrefslogtreecommitdiff
path: root/win32/mingw.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/mingw.c')
-rw-r--r--win32/mingw.c1064
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
6int _dowildcard = -1;
7#else
8int _dowildcard = 0;
9#endif
10
11#undef _fmode
12int _fmode = _O_BINARY;
13#endif
14
15#if !defined(__MINGW64_VERSION_MAJOR)
16#if ENABLE_GLOBBING
17int _CRT_glob = 1;
18#else
19int _CRT_glob = 0;
20#endif
21
22unsigned int _CRT_fmode = _O_BINARY;
23#endif
24
25smallint bb_got_signal;
26
27int 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
142int 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
167FILE *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
175int 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 */
185static 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
192static inline time_t filetime_to_time_t(const FILETIME *ft)
193{
194 return (time_t)(filetime_to_hnsec(ft) / 10000000);
195}
196
197static 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
209static 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 */
236static 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 */
299static 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
326int mingw_lstat(const char *file_name, struct mingw_stat *buf)
327{
328 return do_stat_internal(0, file_name, buf);
329}
330int mingw_stat(const char *file_name, struct mingw_stat *buf)
331{
332 return do_stat_internal(1, file_name, buf);
333}
334
335int 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
387static 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
394int 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
440revert_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
449unsigned int sleep (unsigned int seconds)
450{
451 Sleep(seconds*1000);
452 return 0;
453}
454
455int 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
478char *mingw_mktemp(char *template)
479{
480 if ( mktemp(template) == NULL ) {
481 template[0] = '\0';
482 }
483
484 return template;
485}
486
487int 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
495int 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
507int pipe(int filedes[2])
508{
509 if (_pipe(filedes, PIPE_BUF, 0) < 0)
510 return -1;
511 return 0;
512}
513
514struct 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
521struct 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
529char *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
542int 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
575static 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
602static 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
625struct 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
637struct 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
654struct 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
670int 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
683int 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
693int 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
710long sysconf(int name)
711{
712 if ( name == _SC_CLK_TCK ) {
713 return 100;
714 }
715 errno = EINVAL;
716 return -1;
717}
718
719clock_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
729int 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
750char *realpath(const char *path, char *resolved_path)
751{
752 /* FIXME: need normalization */
753 return strcpy(resolved_path, path);
754}
755
756const 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
766int 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
785int 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
797int 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
841int 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
849size_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
923int stime(time_t *t UNUSED_PARAM)
924{
925 errno = EPERM;
926 return -1;
927}
928
929#undef access
930int 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
998int 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 */
1011char *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
1038DIR *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
1052off_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}