#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; } 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; } 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 = EINVAL; return -1; }