aboutsummaryrefslogtreecommitdiff
path: root/win32/mingw.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--win32/mingw.c1046
1 files changed, 1046 insertions, 0 deletions
diff --git a/win32/mingw.c b/win32/mingw.c
new file mode 100644
index 000000000..23ca5d3dd
--- /dev/null
+++ b/win32/mingw.c
@@ -0,0 +1,1046 @@
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
455/*
456 * Windows' mktemp returns NULL on error whereas POSIX always returns the
457 * template and signals an error by making it an empty string.
458 */
459#undef mktemp
460char *mingw_mktemp(char *template)
461{
462 if ( mktemp(template) == NULL ) {
463 template[0] = '\0';
464 }
465
466 return template;
467}
468
469int mkstemp(char *template)
470{
471 char *filename = mktemp(template);
472 if (filename == NULL)
473 return -1;
474 return open(filename, O_RDWR | O_CREAT, 0600);
475}
476
477int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM)
478{
479 FILETIME ft;
480 long long hnsec;
481
482 GetSystemTimeAsFileTime(&ft);
483 hnsec = filetime_to_hnsec(&ft);
484 tv->tv_sec = hnsec / 10000000;
485 tv->tv_usec = (hnsec % 10000000) / 10;
486 return 0;
487}
488
489int pipe(int filedes[2])
490{
491 if (_pipe(filedes, PIPE_BUF, 0) < 0)
492 return -1;
493 return 0;
494}
495
496struct tm *gmtime_r(const time_t *timep, struct tm *result)
497{
498 /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
499 memcpy(result, gmtime(timep), sizeof(struct tm));
500 return result;
501}
502
503struct tm *localtime_r(const time_t *timep, struct tm *result)
504{
505 /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
506 memcpy(result, localtime(timep), sizeof(struct tm));
507 return result;
508}
509
510#undef getcwd
511char *mingw_getcwd(char *pointer, int len)
512{
513 int i;
514 char *ret = getcwd(pointer, len);
515 if (!ret)
516 return ret;
517 for (i = 0; ret[i]; i++)
518 if (ret[i] == '\\')
519 ret[i] = '/';
520 return ret;
521}
522
523#undef rename
524int mingw_rename(const char *pold, const char *pnew)
525{
526 DWORD attrs;
527
528 /*
529 * Try native rename() first to get errno right.
530 * It is based on MoveFile(), which cannot overwrite existing files.
531 */
532 if (!rename(pold, pnew))
533 return 0;
534 if (errno != EEXIST)
535 return -1;
536 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
537 return 0;
538 /* TODO: translate more errors */
539 if (GetLastError() == ERROR_ACCESS_DENIED &&
540 (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
541 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
542 errno = EISDIR;
543 return -1;
544 }
545 if ((attrs & FILE_ATTRIBUTE_READONLY) &&
546 SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
547 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
548 return 0;
549 /* revert file attributes on failure */
550 SetFileAttributes(pnew, attrs);
551 }
552 }
553 errno = EACCES;
554 return -1;
555}
556
557static char *gethomedir(void)
558{
559 static char buf[PATH_MAX];
560 DWORD len = sizeof(buf);
561 HANDLE h;
562 char *s;
563
564 buf[0] = '\0';
565 if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h) )
566 return buf;
567
568 if ( !GetUserProfileDirectory(h, buf, &len) ) {
569 CloseHandle(h);
570 return buf;
571 }
572
573 CloseHandle(h);
574
575 for ( s=buf; *s; ++s ) {
576 if ( *s == '\\' ) {
577 *s = '/';
578 }
579 }
580
581 return buf;
582}
583
584static char *get_user_name(void)
585{
586 static char user_name[100] = "";
587 char *s;
588 DWORD len = sizeof(user_name);
589
590 if ( user_name[0] != '\0' ) {
591 return user_name;
592 }
593
594 if ( !GetUserName(user_name, &len) ) {
595 return NULL;
596 }
597
598 for ( s=user_name; *s; ++s ) {
599 if ( *s == ' ' ) {
600 *s = '_';
601 }
602 }
603
604 return user_name;
605}
606
607struct passwd *getpwnam(const char *name)
608{
609 const char *myname;
610
611 if ( (myname=get_user_name()) != NULL &&
612 strcmp(myname, name) == 0 ) {
613 return getpwuid(DEFAULT_UID);
614 }
615
616 return NULL;
617}
618
619struct passwd *getpwuid(uid_t uid UNUSED_PARAM)
620{
621 static struct passwd p;
622
623 if ( (p.pw_name=get_user_name()) == NULL ) {
624 return NULL;
625 }
626 p.pw_passwd = (char *)"secret";
627 p.pw_gecos = (char *)"unknown";
628 p.pw_dir = gethomedir();
629 p.pw_shell = NULL;
630 p.pw_uid = DEFAULT_UID;
631 p.pw_gid = DEFAULT_GID;
632
633 return &p;
634}
635
636struct group *getgrgid(gid_t gid UNUSED_PARAM)
637{
638 static char *members[2] = { NULL, NULL };
639 static struct group g;
640
641 if ( (g.gr_name=get_user_name()) == NULL ) {
642 return NULL;
643 }
644 g.gr_passwd = (char *)"secret";
645 g.gr_gid = DEFAULT_GID;
646 members[0] = g.gr_name;
647 g.gr_mem = members;
648
649 return &g;
650}
651
652int getgrouplist(const char *user UNUSED_PARAM, gid_t group UNUSED_PARAM,
653 gid_t *groups, int *ngroups)
654{
655 if ( *ngroups == 0 ) {
656 *ngroups = 1;
657 return -1;
658 }
659
660 *ngroups = 1;
661 groups[0] = DEFAULT_GID;
662 return 1;
663}
664
665int getgroups(int n, gid_t *groups)
666{
667 if ( n == 0 ) {
668 return 1;
669 }
670
671 groups[0] = DEFAULT_GID;
672 return 1;
673}
674
675int getlogin_r(char *buf, size_t len)
676{
677 char *name;
678
679 if ( (name=get_user_name()) == NULL ) {
680 return -1;
681 }
682
683 if ( strlen(name) >= len ) {
684 errno = ERANGE;
685 return -1;
686 }
687
688 strcpy(buf, name);
689 return 0;
690}
691
692long sysconf(int name)
693{
694 if ( name == _SC_CLK_TCK ) {
695 return 100;
696 }
697 errno = EINVAL;
698 return -1;
699}
700
701clock_t times(struct tms *buf)
702{
703 buf->tms_utime = 0;
704 buf->tms_stime = 0;
705 buf->tms_cutime = 0;
706 buf->tms_cstime = 0;
707
708 return 0;
709}
710
711int link(const char *oldpath, const char *newpath)
712{
713 typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
714 static T create_hard_link = NULL;
715 if (!create_hard_link) {
716 create_hard_link = (T) GetProcAddress(
717 GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
718 if (!create_hard_link)
719 create_hard_link = (T)-1;
720 }
721 if (create_hard_link == (T)-1) {
722 errno = ENOSYS;
723 return -1;
724 }
725 if (!create_hard_link(newpath, oldpath, NULL)) {
726 errno = err_win_to_posix(GetLastError());
727 return -1;
728 }
729 return 0;
730}
731
732char *realpath(const char *path, char *resolved_path)
733{
734 /* FIXME: need normalization */
735 return strcpy(resolved_path, path);
736}
737
738const char *get_busybox_exec_path(void)
739{
740 static char path[PATH_MAX] = "";
741
742 if (!*path)
743 GetModuleFileName(NULL, path, PATH_MAX);
744 return path;
745}
746
747#undef mkdir
748int mingw_mkdir(const char *path, int mode UNUSED_PARAM)
749{
750 int ret;
751 struct stat st;
752 int lerrno = 0;
753
754 if ( (ret=mkdir(path)) < 0 ) {
755 lerrno = errno;
756 if ( lerrno == EACCES && stat(path, &st) == 0 ) {
757 ret = 0;
758 lerrno = 0;
759 }
760 }
761
762 errno = lerrno;
763 return ret;
764}
765
766#undef chmod
767int mingw_chmod(const char *path, int mode)
768{
769 WIN32_FILE_ATTRIBUTE_DATA fdata;
770
771 if ( get_file_attr(path, &fdata) == 0 &&
772 fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
773 mode |= 0222;
774 }
775
776 return chmod(path, mode);
777}
778
779int fcntl(int fd, int cmd, ...)
780{
781 va_list arg;
782 int result = -1;
783 char *fds;
784 int target, i, newfd;
785
786 va_start(arg, cmd);
787
788 switch (cmd) {
789 case F_GETFD:
790 case F_SETFD:
791 case F_GETFL:
792 /*
793 * Our fake F_GETFL won't matter if the return value is used as
794 * fcntl(fd, F_SETFL, ret|something);
795 * because F_SETFL isn't supported either.
796 */
797 result = 0;
798 break;
799 case F_DUPFD:
800 target = va_arg(arg, int);
801 fds = xzalloc(target);
802 while ((newfd = dup(fd)) < target && newfd >= 0) {
803 fds[newfd] = 1;
804 }
805 for (i = 0; i < target; ++i) {
806 if (fds[i]) {
807 close(i);
808 }
809 }
810 free(fds);
811 result = newfd;
812 break;
813 default:
814 errno = ENOSYS;
815 break;
816 }
817
818 va_end(arg);
819 return result;
820}
821
822#undef unlink
823int mingw_unlink(const char *pathname)
824{
825 /* read-only files cannot be removed */
826 chmod(pathname, 0666);
827 return unlink(pathname);
828}
829
830#undef strftime
831size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm)
832{
833 size_t ret;
834 char day[3];
835 char *t;
836 char *fmt, *newfmt;
837 struct tm tm2;
838 int m;
839
840 /*
841 * Emulate the '%e' and '%s' formats that Windows' strftime lacks.
842 * Happily, the string that replaces '%e' is two characters long.
843 * '%s' is a bit more complicated.
844 */
845 fmt = xstrdup(format);
846 for ( t=fmt; *t; ++t ) {
847 if ( *t == '%' ) {
848 if ( t[1] == 'e' ) {
849 if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) {
850 sprintf(day, "%2d", tm->tm_mday);
851 }
852 else {
853 strcpy(day, " ");
854 }
855 memcpy(t++, day, 2);
856 }
857 else if ( t[1] == 's' ) {
858 *t = '\0';
859 m = t - fmt;
860 tm2 = *tm;
861 newfmt = xasprintf("%s%d%s", fmt, (int)mktime(&tm2), t+2);
862 free(fmt);
863 t = newfmt + m + 1;
864 fmt = newfmt;
865 }
866 else if ( t[1] == 'z' ) {
867 char buffer[16] = "";
868
869 *t = '\0';
870 m = t - fmt;
871 _tzset();
872 if ( tm->tm_isdst >= 0 ) {
873 int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0);
874 int hr, min;
875
876 if ( offset > 0 ) {
877 buffer[0] = '-';
878 }
879 else {
880 buffer[0] = '+';
881 offset = -offset;
882 }
883
884 hr = offset / 3600;
885 min = (offset % 3600) / 60;
886 sprintf(buffer+1, "%02d%02d", hr, min);
887 }
888 newfmt = xasprintf("%s%s%s", fmt, buffer, t+2);
889 free(fmt);
890 t = newfmt + m + 1;
891 fmt = newfmt;
892 }
893 else if ( t[1] != '\0' ) {
894 ++t;
895 }
896 }
897 }
898
899 ret = strftime(buf, max, fmt, tm);
900 free(fmt);
901
902 return ret;
903}
904
905int stime(time_t *t UNUSED_PARAM)
906{
907 errno = EPERM;
908 return -1;
909}
910
911#undef access
912int mingw_access(const char *name, int mode)
913{
914 int ret;
915 struct stat s;
916 int fd, n, sig;
917 unsigned int offset;
918 unsigned char buf[1024];
919
920 /* Windows can only handle test for existence, read or write */
921 if (mode == F_OK || (mode & ~X_OK)) {
922 ret = _access(name, mode & ~X_OK);
923 if (ret < 0 || !(mode & X_OK)) {
924 return ret;
925 }
926 }
927
928 if (!mingw_stat(name, &s) && S_ISREG(s.st_mode)) {
929
930 /* stat marks .exe and .com files as executable */
931 if ((s.st_mode&S_IEXEC)) {
932 return 0;
933 }
934
935 fd = open(name, O_RDONLY);
936 if (fd < 0)
937 return -1;
938 n = read(fd, buf, sizeof(buf)-1);
939 close(fd);
940 if (n < 4) /* at least '#!/x' and not error */
941 return -1;
942
943 /* shell script */
944 if (buf[0] == '#' && buf[1] == '!') {
945 return 0;
946 }
947
948 /*
949 * Poke about in file to see if it's a PE binary. I've just copied
950 * the magic from the file command.
951 */
952 if (buf[0] == 'M' && buf[1] == 'Z') {
953 offset = (buf[0x19] << 8) + buf[0x18];
954 if (offset > 0x3f) {
955 offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) +
956 (buf[0x3d] << 8) + buf[0x3c];
957 if (offset < sizeof(buf)-100) {
958 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
959 sig = (buf[offset+25] << 8) + buf[offset+24];
960 if (sig == 0x10b || sig == 0x20b) {
961 sig = (buf[offset+23] << 8) + buf[offset+22];
962 if ((sig & 0x2000) != 0) {
963 /* DLL */
964 return -1;
965 }
966 sig = buf[offset+92];
967 return !(sig == 1 || sig == 2 ||
968 sig == 3 || sig == 7);
969 }
970 }
971 }
972 }
973 }
974 }
975
976 return -1;
977}
978
979#undef rmdir
980int mingw_rmdir(const char *path)
981{
982 /* read-only directories cannot be removed */
983 chmod(path, 0666);
984 return rmdir(path);
985}
986
987/* check if path can be made into an executable by adding a suffix;
988 * return an allocated string containing the path if it can;
989 * return NULL if not.
990 *
991 * if path already has a suffix don't even bother trying
992 */
993char *file_is_win32_executable(const char *p)
994{
995 char *path;
996 int len = strlen(p);
997
998 if (len > 4 && (!strcasecmp(p+len-4, ".exe") ||
999 !strcasecmp(p+len-4, ".com"))) {
1000 return NULL;
1001 }
1002
1003 if ( (path=malloc(len+5)) != NULL ) {
1004 memcpy(path, p, len);
1005 memcpy(path+len, ".exe", 5);
1006 if (file_is_executable(path)) {
1007 return path;
1008 }
1009 memcpy(path+len, ".com", 5);
1010 if (file_is_executable(path)) {
1011 return path;
1012 }
1013 free(path);
1014 }
1015
1016 return NULL;
1017}
1018
1019#undef opendir
1020DIR *mingw_opendir(const char *path)
1021{
1022 char name[4];
1023
1024 if (isalpha(path[0]) && path[1] == ':' && path[2] == '\0') {
1025 strcpy(name, path);
1026 name[2] = '/';
1027 name[3] = '\0';
1028 path = name;
1029 }
1030
1031 return opendir(path);
1032}
1033
1034off_t mingw_lseek(int fd, off_t offset, int whence)
1035{
1036 HANDLE h = (HANDLE)_get_osfhandle(fd);
1037 if (h == INVALID_HANDLE_VALUE) {
1038 errno = EBADF;
1039 return -1;
1040 }
1041 if (GetFileType(h) != FILE_TYPE_DISK) {
1042 errno = ESPIPE;
1043 return -1;
1044 }
1045 return _lseeki64(fd, offset, whence);
1046}