X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_replacements.c;h=c2fb556d15553228412a535a606960118989e2ea;hp=ebf6e20e6cc2d4414b6270df65f7a3a214893982;hb=db1b9ac63e5c0a1dffb51a1a162cf69a01a5cf24;hpb=3943498cc28fabf54b6da62297ff3db8b4306ed9 diff --git a/src/win32_replacements.c b/src/win32_replacements.c index ebf6e20e..c2fb556d 100644 --- a/src/win32_replacements.c +++ b/src/win32_replacements.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2013 Eric Biggers + * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -36,8 +36,290 @@ #include "wimlib/assert.h" #include "wimlib/glob.h" #include "wimlib/error.h" +#include "wimlib/timestamp.h" #include "wimlib/util.h" +static int +win32_error_to_errno(DWORD err_code) +{ + /* This mapping is that used in Cygwin. + * Some of these choices are arbitrary. */ + switch (err_code) { + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_ACTIVE_CONNECTIONS: + return EAGAIN; + case ERROR_ALREADY_EXISTS: + return EEXIST; + case ERROR_BAD_DEVICE: + return ENODEV; + case ERROR_BAD_EXE_FORMAT: + return ENOEXEC; + case ERROR_BAD_NETPATH: + return ENOENT; + case ERROR_BAD_NET_NAME: + return ENOENT; + case ERROR_BAD_NET_RESP: + return ENOSYS; + case ERROR_BAD_PATHNAME: + return ENOENT; + case ERROR_BAD_PIPE: + return EINVAL; + case ERROR_BAD_UNIT: + return ENODEV; + case ERROR_BAD_USERNAME: + return EINVAL; + case ERROR_BEGINNING_OF_MEDIA: + return EIO; + case ERROR_BROKEN_PIPE: + return EPIPE; + case ERROR_BUSY: + return EBUSY; + case ERROR_BUS_RESET: + return EIO; + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOSYS; + case ERROR_CANNOT_MAKE: + return EPERM; + case ERROR_CHILD_NOT_COMPLETE: + return EBUSY; + case ERROR_COMMITMENT_LIMIT: + return EAGAIN; + case ERROR_CRC: + return EIO; + case ERROR_DEVICE_DOOR_OPEN: + return EIO; + case ERROR_DEVICE_IN_USE: + return EAGAIN; + case ERROR_DEVICE_REQUIRES_CLEANING: + return EIO; + case ERROR_DIRECTORY: + return ENOTDIR; + case ERROR_DIR_NOT_EMPTY: + return ENOTEMPTY; + case ERROR_DISK_CORRUPT: + return EIO; + case ERROR_DISK_FULL: + return ENOSPC; +#ifdef ENOTUNIQ + case ERROR_DUP_NAME: + return ENOTUNIQ; +#endif + case ERROR_EAS_DIDNT_FIT: + return ENOSPC; +#ifdef ENOTSUP + case ERROR_EAS_NOT_SUPPORTED: + return ENOTSUP; +#endif + case ERROR_EA_LIST_INCONSISTENT: + return EINVAL; + case ERROR_EA_TABLE_FULL: + return ENOSPC; + case ERROR_END_OF_MEDIA: + return ENOSPC; + case ERROR_EOM_OVERFLOW: + return EIO; + case ERROR_EXE_MACHINE_TYPE_MISMATCH: + return ENOEXEC; + case ERROR_EXE_MARKED_INVALID: + return ENOEXEC; + case ERROR_FILEMARK_DETECTED: + return EIO; + case ERROR_FILENAME_EXCED_RANGE: + return ENAMETOOLONG; + case ERROR_FILE_CORRUPT: + return EEXIST; + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_FILE_INVALID: + return ENXIO; + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_HANDLE_DISK_FULL: + return ENOSPC; +#ifdef ENODATA + case ERROR_HANDLE_EOF: + return ENODATA; +#endif + case ERROR_INVALID_ADDRESS: + return EINVAL; + case ERROR_INVALID_AT_INTERRUPT_TIME: + return EINTR; + case ERROR_INVALID_BLOCK_LENGTH: + return EIO; + case ERROR_INVALID_DATA: + return EINVAL; + case ERROR_INVALID_DRIVE: + return ENODEV; + case ERROR_INVALID_EA_NAME: + return EINVAL; + case ERROR_INVALID_EXE_SIGNATURE: + return ENOEXEC; +#ifdef EBADRQC + case ERROR_INVALID_FUNCTION: + return EBADRQC; +#endif + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_INVALID_NAME: + return ENOENT; + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_INVALID_SIGNAL_NUMBER: + return EINVAL; + case ERROR_IOPL_NOT_ENABLED: + return ENOEXEC; + case ERROR_IO_DEVICE: + return EIO; + case ERROR_IO_INCOMPLETE: + return EAGAIN; + case ERROR_IO_PENDING: + return EAGAIN; + case ERROR_LOCK_VIOLATION: + return EBUSY; + case ERROR_MAX_THRDS_REACHED: + return EAGAIN; + case ERROR_META_EXPANSION_TOO_LONG: + return EINVAL; + case ERROR_MOD_NOT_FOUND: + return ENOENT; +#ifdef EMSGSIZE + case ERROR_MORE_DATA: + return EMSGSIZE; +#endif + case ERROR_NEGATIVE_SEEK: + return EINVAL; + case ERROR_NETNAME_DELETED: + return ENOENT; + case ERROR_NOACCESS: + return EFAULT; + case ERROR_NONE_MAPPED: + return EINVAL; + case ERROR_NONPAGED_SYSTEM_RESOURCES: + return EAGAIN; +#ifdef ENOLINK + case ERROR_NOT_CONNECTED: + return ENOLINK; +#endif + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + case ERROR_NOT_OWNER: + return EPERM; +#ifdef ENOMEDIUM + case ERROR_NOT_READY: + return ENOMEDIUM; +#endif + case ERROR_NOT_SAME_DEVICE: + return EXDEV; + case ERROR_NOT_SUPPORTED: + return ENOSYS; + case ERROR_NO_DATA: + return EPIPE; + case ERROR_NO_DATA_DETECTED: + return EIO; +#ifdef ENOMEDIUM + case ERROR_NO_MEDIA_IN_DRIVE: + return ENOMEDIUM; +#endif +#ifdef ENMFILE + case ERROR_NO_MORE_FILES: + return ENMFILE; +#endif +#ifdef ENMFILE + case ERROR_NO_MORE_ITEMS: + return ENMFILE; +#endif + case ERROR_NO_MORE_SEARCH_HANDLES: + return ENFILE; + case ERROR_NO_PROC_SLOTS: + return EAGAIN; + case ERROR_NO_SIGNAL_SENT: + return EIO; + case ERROR_NO_SYSTEM_RESOURCES: + return EFBIG; + case ERROR_NO_TOKEN: + return EINVAL; + case ERROR_OPEN_FAILED: + return EIO; + case ERROR_OPEN_FILES: + return EAGAIN; + case ERROR_OUTOFMEMORY: + return ENOMEM; + case ERROR_PAGED_SYSTEM_RESOURCES: + return EAGAIN; + case ERROR_PAGEFILE_QUOTA: + return EAGAIN; + case ERROR_PATH_NOT_FOUND: + return ENOENT; + case ERROR_PIPE_BUSY: + return EBUSY; + case ERROR_PIPE_CONNECTED: + return EBUSY; +#ifdef ECOMM + case ERROR_PIPE_LISTENING: + return ECOMM; + case ERROR_PIPE_NOT_CONNECTED: + return ECOMM; +#endif + case ERROR_POSSIBLE_DEADLOCK: + return EDEADLOCK; + case ERROR_PRIVILEGE_NOT_HELD: + return EPERM; + case ERROR_PROCESS_ABORTED: + return EFAULT; + case ERROR_PROC_NOT_FOUND: + return ESRCH; +#ifdef ENONET + case ERROR_REM_NOT_LIST: + return ENONET; +#endif + case ERROR_SECTOR_NOT_FOUND: + return EINVAL; + case ERROR_SEEK: + return EINVAL; + case ERROR_SETMARK_DETECTED: + return EIO; + case ERROR_SHARING_BUFFER_EXCEEDED: + return ENOLCK; + case ERROR_SHARING_VIOLATION: + return EBUSY; + case ERROR_SIGNAL_PENDING: + return EBUSY; + case ERROR_SIGNAL_REFUSED: + return EIO; +#ifdef ELIBBAD + case ERROR_SXS_CANT_GEN_ACTCTX: + return ELIBBAD; +#endif + case ERROR_THREAD_1_INACTIVE: + return EINVAL; + case ERROR_TOO_MANY_LINKS: + return EMLINK; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_WAIT_NO_CHILDREN: + return ECHILD; + case ERROR_WORKING_SET_QUOTA: + return EAGAIN; + case ERROR_WRITE_PROTECT: + return EROFS; + default: + return -1; + } +} + +static void +set_errno_from_win32_error(DWORD err) +{ + errno = win32_error_to_errno(err); +} + +static void +set_errno_from_GetLastError(void) +{ + set_errno_from_win32_error(GetLastError()); +} + /* Replacement for POSIX fsync() */ int fsync(int fd) @@ -58,7 +340,7 @@ err: /* Use the Win32 API to get the number of processors. */ unsigned -win32_get_number_of_processors(void) +get_available_cpus(void) { SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); @@ -67,7 +349,7 @@ win32_get_number_of_processors(void) /* Use the Win32 API to get the amount of available memory. */ u64 -win32_get_avail_memory(void) +get_available_memory(void) { MEMORYSTATUSEX status = { .dwLength = sizeof(status), @@ -158,7 +440,7 @@ win32_rename_replacement(const wchar_t *srcpath, const wchar_t *dstpath) p = tmpname; p = wmempcpy(p, dstpath, dstlen); p = wmempcpy(p, orig_suffix, ARRAY_LEN(orig_suffix)); - randomize_char_array_with_alnum(p, num_rand_chars); + get_random_alnum_chars(p, num_rand_chars); p += num_rand_chars; *p = L'\0'; } @@ -202,51 +484,70 @@ win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen) return 0; } +#define MAX_IO_AMOUNT 1048576 + static int do_pread_or_pwrite(int fd, void *buf, size_t count, off_t offset, bool is_pwrite) { HANDLE h; LARGE_INTEGER orig_offset; - DWORD bytes_read_or_written; + DWORD result = 0xFFFFFFFF; LARGE_INTEGER relative_offset; OVERLAPPED overlapped; BOOL bret; + DWORD err = 0; h = (HANDLE)_get_osfhandle(fd); if (h == INVALID_HANDLE_VALUE) - goto err; + goto error; if (GetFileType(h) == FILE_TYPE_PIPE) { errno = ESPIPE; - goto err; + goto error; } /* Get original position */ relative_offset.QuadPart = 0; - if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT)) - goto err_set_errno; + if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT)) { + err = GetLastError(); + win32_error(err, L"Failed to get original file position"); + goto error; + } memset(&overlapped, 0, sizeof(overlapped)); overlapped.Offset = offset; overlapped.OffsetHigh = offset >> 32; /* Do the read or write at the specified offset */ + count = min(count, MAX_IO_AMOUNT); + SetLastError(0); if (is_pwrite) - bret = WriteFile(h, buf, count, &bytes_read_or_written, &overlapped); + bret = WriteFile(h, buf, count, &result, &overlapped); else - bret = ReadFile(h, buf, count, &bytes_read_or_written, &overlapped); - if (!bret) - goto err_set_errno; + bret = ReadFile(h, buf, count, &result, &overlapped); + if (!bret) { + err = GetLastError(); + win32_error(err, L"Failed to %s %zu bytes at offset %"PRIu64, + (is_pwrite ? "write" : "read"), count, offset); + goto error; + } + + wimlib_assert(result <= count); /* Restore the original position */ - if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN)) - goto err_set_errno; + if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN)) { + err = GetLastError(); + win32_error(err, L"Failed to restore file position to %"PRIu64, + offset); + goto error; + } - return bytes_read_or_written; -err_set_errno: - set_errno_from_GetLastError(); -err: + return result; + +error: + if (err) + set_errno_from_win32_error(err); return -1; } @@ -254,7 +555,7 @@ err: * offset, so it is not safe to use with readers/writers on the same file * descriptor. */ ssize_t -pread(int fd, void *buf, size_t count, off_t offset) +win32_pread(int fd, void *buf, size_t count, off_t offset) { return do_pread_or_pwrite(fd, buf, count, offset, false); } @@ -263,11 +564,59 @@ pread(int fd, void *buf, size_t count, off_t offset) * offset, so it is not safe to use with readers/writers on the same file * descriptor. */ ssize_t -pwrite(int fd, const void *buf, size_t count, off_t offset) +win32_pwrite(int fd, const void *buf, size_t count, off_t offset) { return do_pread_or_pwrite(fd, (void*)buf, count, offset, true); } +/* Replacement for read() which doesn't hide the Win32 error code */ +ssize_t +win32_read(int fd, void *buf, size_t count) +{ + HANDLE h = (HANDLE)_get_osfhandle(fd); + DWORD result = 0xFFFFFFFF; + + if (h == INVALID_HANDLE_VALUE) + return -1; + + count = min(count, MAX_IO_AMOUNT); + SetLastError(0); + if (!ReadFile(h, buf, count, &result, NULL)) { + DWORD err = GetLastError(); + win32_error(err, + L"Error reading %zu bytes from fd %d", count, fd); + set_errno_from_win32_error(err); + return -1; + } + + wimlib_assert(result <= count); + return result; +} + +/* Replacement for write() which doesn't hide the Win32 error code */ +ssize_t +win32_write(int fd, const void *buf, size_t count) +{ + HANDLE h = (HANDLE)_get_osfhandle(fd); + DWORD result = 0xFFFFFFFF; + + if (h == INVALID_HANDLE_VALUE) + return -1; + + count = min(count, MAX_IO_AMOUNT); + SetLastError(0); + if (!WriteFile(h, buf, count, &result, NULL)) { + DWORD err = GetLastError(); + win32_error(err, + L"Error writing %zu bytes to fd %d", count, fd); + set_errno_from_win32_error(err); + return -1; + } + + wimlib_assert(result <= count); + return result; +} + /* Replacement for glob() in Windows native builds that operates on wide * characters. */ int @@ -400,4 +749,41 @@ win32_open_logfile(const wchar_t *path) return fp; } +#define RtlGenRandom SystemFunction036 +BOOLEAN WINAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); + +/* + * Generate @n cryptographically secure random bytes (thread-safe) + * + * This is the Windows version. It uses RtlGenRandom() (actually called + * SystemFunction036) from advapi32.dll. + */ +void +get_random_bytes(void *p, size_t n) +{ + while (n != 0) { + u32 count = min(n, UINT32_MAX); + + if (!RtlGenRandom(p, count)) { + win32_error(GetLastError(), + L"RtlGenRandom() failed (count=%u)", count); + wimlib_assert(0); + count = 0; + } + p += count; + n -= count; + } +} + +/* Retrieve the current time as a WIM timestamp. */ +u64 +now_as_wim_timestamp(void) +{ + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); + + return ((u64)ft.dwHighDateTime << 32) | ft.dwLowDateTime; +} + #endif /* __WIN32__ */