#include "libbb.h" unsigned int _CRT_fmode = _O_BINARY; static int err_win_to_posix(DWORD winerr) { int error = ENOSYS; switch(winerr) { case ERROR_ACCESS_DENIED: error = EACCES; break; case ERROR_ACCOUNT_DISABLED: error = EACCES; break; case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; case ERROR_ALREADY_EXISTS: error = EEXIST; break; case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; case ERROR_BAD_COMMAND: error = EIO; break; case ERROR_BAD_DEVICE: error = ENODEV; break; case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; case ERROR_BAD_FORMAT: error = ENOEXEC; break; case ERROR_BAD_LENGTH: error = EINVAL; break; case ERROR_BAD_PATHNAME: error = ENOENT; break; case ERROR_BAD_PIPE: error = EPIPE; break; case ERROR_BAD_UNIT: error = ENODEV; break; case ERROR_BAD_USERNAME: error = EINVAL; break; case ERROR_BROKEN_PIPE: error = EPIPE; break; case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; case ERROR_BUSY: error = EBUSY; break; case ERROR_BUSY_DRIVE: error = EBUSY; break; case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; case ERROR_CANNOT_MAKE: error = EACCES; break; case ERROR_CANTOPEN: error = EIO; break; case ERROR_CANTREAD: error = EIO; break; case ERROR_CANTWRITE: error = EIO; break; case ERROR_CRC: error = EIO; break; case ERROR_CURRENT_DIRECTORY: error = EACCES; break; case ERROR_DEVICE_IN_USE: error = EBUSY; break; case ERROR_DEV_NOT_EXIST: error = ENODEV; break; case ERROR_DIRECTORY: error = EINVAL; break; case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; case ERROR_DISK_CHANGE: error = EIO; break; case ERROR_DISK_FULL: error = ENOSPC; break; case ERROR_DRIVE_LOCKED: error = EBUSY; break; case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; case ERROR_FILE_EXISTS: error = EEXIST; break; case ERROR_FILE_INVALID: error = ENODEV; break; case ERROR_FILE_NOT_FOUND: error = ENOENT; break; case ERROR_GEN_FAILURE: error = EIO; break; case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; case ERROR_INVALID_ACCESS: error = EACCES; break; case ERROR_INVALID_ADDRESS: error = EFAULT; break; case ERROR_INVALID_BLOCK: error = EFAULT; break; case ERROR_INVALID_DATA: error = EINVAL; break; case ERROR_INVALID_DRIVE: error = ENODEV; break; case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; case ERROR_INVALID_FLAGS: error = EINVAL; break; case ERROR_INVALID_FUNCTION: error = ENOSYS; break; case ERROR_INVALID_HANDLE: error = EBADF; break; case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; case ERROR_INVALID_NAME: error = EINVAL; break; case ERROR_INVALID_OWNER: error = EINVAL; break; case ERROR_INVALID_PARAMETER: error = EINVAL; break; case ERROR_INVALID_PASSWORD: error = EPERM; break; case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; case ERROR_INVALID_WORKSTATION: error = EACCES; break; case ERROR_IO_DEVICE: error = EIO; break; case ERROR_IO_INCOMPLETE: error = EINTR; break; case ERROR_LOCKED: error = EBUSY; break; case ERROR_LOCK_VIOLATION: error = EACCES; break; case ERROR_LOGON_FAILURE: error = EACCES; break; case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; case ERROR_MORE_DATA: error = EPIPE; break; case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; case ERROR_NOACCESS: error = EFAULT; break; case ERROR_NONE_MAPPED: error = EINVAL; break; case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; case ERROR_NOT_READY: error = EAGAIN; break; case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; case ERROR_NO_DATA: error = EPIPE; break; case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; case ERROR_OPEN_FAILED: error = EIO; break; case ERROR_OPEN_FILES: error = EBUSY; break; case ERROR_OPERATION_ABORTED: error = EINTR; break; case ERROR_OUTOFMEMORY: error = ENOMEM; break; case ERROR_PASSWORD_EXPIRED: error = EACCES; break; case ERROR_PATH_BUSY: error = EBUSY; break; case ERROR_PATH_NOT_FOUND: error = ENOENT; break; case ERROR_PIPE_BUSY: error = EBUSY; break; case ERROR_PIPE_CONNECTED: error = EPIPE; break; case ERROR_PIPE_LISTENING: error = EPIPE; break; case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; case ERROR_READ_FAULT: error = EIO; break; case ERROR_SEEK: error = EIO; break; case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; case ERROR_SHARING_VIOLATION: error = EACCES; break; case ERROR_STACK_OVERFLOW: error = ENOMEM; break; case ERROR_SWAPERROR: error = ENOENT; break; case ERROR_TOO_MANY_MODULES: error = EMFILE; break; case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; case ERROR_WRITE_FAULT: error = EIO; break; case ERROR_WRITE_PROTECT: error = EROFS; break; } return error; } #undef open int mingw_open (const char *filename, int oflags, ...) { va_list args; unsigned mode; int fd; va_start(args, oflags); mode = va_arg(args, int); va_end(args); if (oflags & O_NONBLOCK) { errno = ENOSYS; return -1; } if (!strcmp(filename, "/dev/null")) filename = "nul"; fd = open(filename, oflags, mode); if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) { DWORD attrs = GetFileAttributes(filename); if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) errno = EISDIR; } return fd; } #undef fopen FILE *mingw_fopen (const char *filename, const char *mode) { if (!strcmp(filename, "/dev/null")) filename = "nul"; return fopen(filename, mode); } #undef dup2 int mingw_dup2 (int fd, int fdto) { int ret = dup2(fd, fdto); return ret != -1 ? fdto : -1; } static inline time_t filetime_to_time_t(const FILETIME *ft) { long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ winTime /= 10000000; /* Nano to seconds resolution */ return (time_t)winTime; } static inline int file_attr_to_st_mode (DWORD attr) { int fMode = S_IREAD; if (attr & FILE_ATTRIBUTE_DIRECTORY) fMode |= S_IFDIR; else fMode |= S_IFREG; if (!(attr & FILE_ATTRIBUTE_READONLY)) fMode |= S_IWRITE; return fMode; } static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) { if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata)) return 0; switch (GetLastError()) { case ERROR_ACCESS_DENIED: case ERROR_SHARING_VIOLATION: case ERROR_LOCK_VIOLATION: case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES; case ERROR_BUFFER_OVERFLOW: return ENAMETOOLONG; case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; default: return ENOENT; } } /* We keep the do_lstat code in a separate function to avoid recursion. * When a path ends with a slash, the stat will fail with ENOENT. In * this case, we strip the trailing slashes and stat again. */ static int do_lstat(const char *file_name, struct stat *buf) { WIN32_FILE_ATTRIBUTE_DATA fdata; if (!(errno = get_file_attr(file_name, &fdata))) { int len = strlen(file_name); buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); if (len > 4 && !strcmp(file_name+len-4, ".exe")) buf->st_mode |= S_IEXEC; buf->st_size = fdata.nFileSizeLow | (((off64_t)fdata.nFileSizeHigh)<<32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); return 0; } return -1; } /* We provide our own lstat/fstat functions, since the provided * lstat/fstat functions are so slow. These stat functions are * tailored for Git's usage (read: fast), and are not meant to be * complete. Note that Git stat()s are redirected to mingw_lstat() * too, since Windows doesn't really handle symlinks that well. */ int mingw_lstat(const char *file_name, struct stat *buf) { int namelen; static char alt_name[PATH_MAX]; if (!do_lstat(file_name, buf)) return 0; /* if file_name ended in a '/', Windows returned ENOENT; * try again without trailing slashes */ if (errno != ENOENT) return -1; namelen = strlen(file_name); if (namelen && file_name[namelen-1] != '/') return -1; while (namelen && file_name[namelen-1] == '/') --namelen; if (!namelen || namelen >= PATH_MAX) return -1; memcpy(alt_name, file_name, namelen); alt_name[namelen] = 0; return do_lstat(alt_name, buf); } #undef fstat int mingw_fstat(int fd, struct stat *buf) { HANDLE fh = (HANDLE)_get_osfhandle(fd); BY_HANDLE_FILE_INFORMATION fdata; if (fh == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; } /* direct non-file handles to MS's fstat() */ if (GetFileType(fh) != FILE_TYPE_DISK) return _fstati64(fd, buf); if (GetFileInformationByHandle(fh, &fdata)) { buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); buf->st_size = fdata.nFileSizeLow | (((off64_t)fdata.nFileSizeHigh)<<32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); return 0; } errno = EBADF; return -1; } static inline void time_t_to_filetime(time_t t, FILETIME *ft) { long long winTime = t * 10000000LL + 116444736000000000LL; ft->dwLowDateTime = winTime; ft->dwHighDateTime = winTime >> 32; } int mingw_utime (const char *file_name, const struct utimbuf *times) { FILETIME mft, aft; int fh, rc; /* must have write permission */ if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) return -1; if (times) { time_t_to_filetime(times->modtime, &mft); time_t_to_filetime(times->actime, &aft); } else { SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, &aft); mft = aft; } if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) { errno = EINVAL; rc = -1; } else rc = 0; close(fh); return rc; } static inline void timeval_to_filetime(const struct timeval tv, FILETIME *ft) { long long winTime = ((tv.tv_sec * 1000000LL) + tv.tv_usec) * 10LL + 116444736000000000LL; ft->dwLowDateTime = winTime; ft->dwHighDateTime = winTime >> 32; } int utimes(const char *file_name, const struct timeval times[2]) { FILETIME mft, aft; int fh, rc; /* must have write permission */ if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) return -1; if (times) { timeval_to_filetime(times[0], &aft); timeval_to_filetime(times[1], &mft); } else { SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, &aft); mft = aft; } if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) { errno = EINVAL; rc = -1; } else rc = 0; close(fh); return rc; } unsigned int sleep (unsigned int seconds) { Sleep(seconds*1000); return 0; } int mkstemp(char *template) { char *filename = mktemp(template); if (filename == NULL) return -1; return open(filename, O_RDWR | O_CREAT, 0600); } /* * This is like mktime, but without normalization of tm_wday and tm_yday. */ static time_t tm_to_time_t(const struct tm *tm) { static const int mdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int year = tm->tm_year - 70; int month = tm->tm_mon; int day = tm->tm_mday; if (year < 0 || year > 129) /* algo only works for 1970-2099 */ return -1; if (month < 0 || month > 11) /* array bounds */ return -1; if (month < 2 || (year + 2) % 4) day--; return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL + tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec; } int gettimeofday(struct timeval *tv, void *tz) { SYSTEMTIME st; struct tm tm; GetSystemTime(&st); tm.tm_year = st.wYear-1900; tm.tm_mon = st.wMonth-1; tm.tm_mday = st.wDay; tm.tm_hour = st.wHour; tm.tm_min = st.wMinute; tm.tm_sec = st.wSecond; tv->tv_sec = tm_to_time_t(&tm); if (tv->tv_sec < 0) return -1; tv->tv_usec = st.wMilliseconds*1000; return 0; } int pipe(int filedes[2]) { if (_pipe(filedes, PIPE_BUF, 0) < 0) return -1; return 0; } int poll(struct pollfd *ufds, unsigned int nfds, int timeout) { int i, pending; if (timeout >= 0) { if (nfds == 0) { Sleep(timeout); return 0; } errno = EINVAL; return -1; } /* When there is only one fd to wait for, then we pretend that * input is available and let the actual wait happen when the * caller invokes read(). */ if (nfds == 1) { if (!(ufds[0].events & POLLIN)) { errno = EINVAL; return -1; } ufds[0].revents = POLLIN; return 0; } repeat: pending = 0; for (i = 0; i < nfds; i++) { DWORD avail = 0; HANDLE h = (HANDLE) _get_osfhandle(ufds[i].fd); if (h == INVALID_HANDLE_VALUE) return -1; /* errno was set */ if (!(ufds[i].events & POLLIN)) { errno = EINVAL; return -1; } /* this emulation works only for pipes */ if (!PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL)) { int err = GetLastError(); if (err == ERROR_BROKEN_PIPE) { ufds[i].revents = POLLHUP; pending++; } else { errno = EINVAL; return -1; } } else if (avail) { ufds[i].revents = POLLIN; pending++; } else ufds[i].revents = 0; } if (!pending) { /* The only times that we spin here is when the process * that is connected through the pipes is waiting for * its own input data to become available. But since * the process (pack-objects) is itself CPU intensive, * it will happily pick up the time slice that we are * relinguishing here. */ Sleep(0); goto repeat; } return 0; } struct tm *gmtime_r(const time_t *timep, struct tm *result) { /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */ memcpy(result, gmtime(timep), sizeof(struct tm)); return result; } struct tm *localtime_r(const time_t *timep, struct tm *result) { /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */ memcpy(result, localtime(timep), sizeof(struct tm)); return result; } #undef getcwd char *mingw_getcwd(char *pointer, int len) { int i; char *ret = getcwd(pointer, len); if (!ret) return ret; for (i = 0; ret[i]; i++) if (ret[i] == '\\') ret[i] = '/'; return ret; } #undef rename int mingw_rename(const char *pold, const char *pnew) { DWORD attrs; /* * Try native rename() first to get errno right. * It is based on MoveFile(), which cannot overwrite existing files. */ if (!rename(pold, pnew)) return 0; if (errno != EEXIST) return -1; if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) return 0; /* TODO: translate more errors */ if (GetLastError() == ERROR_ACCESS_DENIED && (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) { if (attrs & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; return -1; } if ((attrs & FILE_ATTRIBUTE_READONLY) && SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) return 0; /* revert file attributes on failure */ SetFileAttributes(pnew, attrs); } } errno = EACCES; return -1; } struct passwd *getpwuid(int uid) { static char user_name[100]; static struct passwd p; DWORD len = sizeof(user_name); if (!GetUserName(user_name, &len)) return NULL; p.pw_name = user_name; p.pw_gecos = "unknown"; p.pw_dir = NULL; return &p; } static HANDLE timer_event; static HANDLE timer_thread; static int timer_interval; static int one_shot; static sighandler_t timer_fn = SIG_DFL; /* The timer works like this: * The thread, ticktack(), is a trivial routine that most of the time * only waits to receive the signal to terminate. The main thread tells * the thread to terminate by setting the timer_event to the signalled * state. * But ticktack() interrupts the wait state after the timer's interval * length to call the signal handler. */ static __stdcall unsigned ticktack(void *dummy UNUSED_PARAM) { while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) { if (timer_fn == SIG_DFL) bb_error_msg_and_die("Alarm"); if (timer_fn != SIG_IGN) timer_fn(SIGALRM); if (one_shot) break; } return 0; } static int start_timer_thread(void) { timer_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (timer_event) { timer_thread = (HANDLE) _beginthreadex(NULL, 0, ticktack, NULL, 0, NULL); if (!timer_thread ) { errno = ENOMEM; return -1; } } else { errno = ENOMEM; return -1; } return 0; } static void stop_timer_thread(void) { if (timer_event) SetEvent(timer_event); /* tell thread to terminate */ if (timer_thread) { int rc = WaitForSingleObject(timer_thread, 1000); if (rc == WAIT_TIMEOUT) fprintf(stderr, "timer thread did not terminate timely"); else if (rc != WAIT_OBJECT_0) fprintf(stderr, "waiting for timer thread failed: %lu", GetLastError()); CloseHandle(timer_thread); } if (timer_event) CloseHandle(timer_event); timer_event = NULL; timer_thread = NULL; } static inline int is_timeval_eq(const struct timeval *i1, const struct timeval *i2) { return i1->tv_sec == i2->tv_sec && i1->tv_usec == i2->tv_usec; } int setitimer(int type UNUSED_PARAM, struct itimerval *in, struct itimerval *out) { static const struct timeval zero; static int atexit_done; if (out != NULL) { errno = EINVAL; return -1; } if (!is_timeval_eq(&in->it_interval, &zero) && !is_timeval_eq(&in->it_interval, &in->it_value)) { errno = EINVAL; return -1; } if (timer_thread) stop_timer_thread(); if (is_timeval_eq(&in->it_value, &zero) && is_timeval_eq(&in->it_interval, &zero)) return 0; timer_interval = in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000; one_shot = is_timeval_eq(&in->it_interval, &zero); if (!atexit_done) { atexit(stop_timer_thread); atexit_done = 1; } return start_timer_thread(); } int sigaction(int sig, struct sigaction *in, struct sigaction *out) { if (sig != SIGALRM) { errno = EINVAL; return -1; } if (out != NULL) { errno = EINVAL; return -1; } timer_fn = in->sa_handler; return 0; } #undef signal sighandler_t mingw_signal(int sig, sighandler_t handler) { sighandler_t old = timer_fn; if (sig != SIGALRM) return signal(sig, handler); timer_fn = handler; return old; } int link(const char *oldpath, const char *newpath) { typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES); static T create_hard_link = NULL; if (!create_hard_link) { create_hard_link = (T) GetProcAddress( GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); if (!create_hard_link) create_hard_link = (T)-1; } if (create_hard_link == (T)-1) { errno = ENOSYS; return -1; } if (!create_hard_link(newpath, oldpath, NULL)) { errno = err_win_to_posix(GetLastError()); return -1; } return 0; } char *strsep(char **stringp, const char *delim) { char *s, *old_stringp; if (!*stringp) return NULL; old_stringp = s = *stringp; while (*s) { if (strchr(delim, *s)) { *s = '\0'; *stringp = s+1; return old_stringp; } s++; } *stringp = NULL; return old_stringp; } char *realpath(const char *path, char *resolved_path) { /* FIXME: need normalization */ return strcpy(resolved_path, path); } const char *get_busybox_exec_path(void) { static char path[PATH_MAX] = ""; if (!*path) GetModuleFileName(NULL, path, PATH_MAX); return path; } #undef mkdir int mingw_mkdir(const char *path, int mode UNUSED_PARAM) { return mkdir(path); } int fcntl(int fd UNUSED_PARAM, int cmd, ...) { /* * F_GETFL needs to be dealt at higher level * Usually it does not matter if the call is * fcntl(fd, F_SETFL, fcntl(fd, F_GETFD) | something) * because F_SETFL is not supported */ if (cmd == F_GETFD || cmd == F_SETFD || cmd == F_GETFL) return 0; errno = ENOSYS; return -1; } #undef unlink int mingw_unlink(const char *pathname) { /* read-only files cannot be removed */ chmod(pathname, 0666); return unlink(pathname); } char *strptime(const char *s, const char *format, struct tm *tm) { return NULL; }