X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fwin32_common.c;h=abfe0a82610c476bcf143099ea4846b5c278e788;hb=036c7da59a54e9a24b1afb917b4f9f10eee176ee;hp=5a2b79481f49c592858c2cd081fbc4ccf31c1597;hpb=761a138b6419c30f6213af46186f678600750404;p=wimlib diff --git a/src/win32_common.c b/src/win32_common.c index 5a2b7948..abfe0a82 100644 --- a/src/win32_common.c +++ b/src/win32_common.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2013 Eric Biggers + * Copyright (C) 2013, 2014 Eric Biggers * * This file is part of wimlib, a library for working with WIM files. * @@ -30,32 +30,10 @@ #include #include "wimlib/win32_common.h" - -#include "wimlib/assert.h" #include "wimlib/error.h" #include "wimlib/util.h" -#ifdef ENABLE_ERROR_MESSAGES -void -win32_error(DWORD err_code) -{ - wchar_t *buffer; - DWORD nchars; - nchars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, err_code, 0, - (wchar_t*)&buffer, 0, NULL); - if (nchars == 0) { - ERROR("Error printing error message! " - "Computer will self-destruct in 3 seconds."); - } else { - ERROR("Win32 error: %ls", buffer); - LocalFree(buffer); - } -} -#endif /* ENABLE_ERROR_MESSAGES */ - -int +static int win32_error_to_errno(DWORD err_code) { /* This mapping is that used in Cygwin. @@ -325,129 +303,21 @@ win32_error_to_errno(DWORD err_code) } void -set_errno_from_GetLastError(void) +set_errno_from_win32_error(DWORD err) { - errno = win32_error_to_errno(GetLastError()); + errno = win32_error_to_errno(err); } -/* Given a Windows-style path, return the number of characters of the prefix - * that specify the path to the root directory of a drive, or return 0 if the - * drive is relative (or at least on the current drive, in the case of - * absolute-but-not-really-absolute paths like \Windows\System32) */ -static size_t -win32_path_drive_spec_len(const wchar_t *path) -{ - size_t n = 0; - - if (!wcsncmp(path, L"\\\\?\\", 4)) { - /* \\?\-prefixed path. Check for following drive letter and - * path separator. */ - if (path[4] != L'\0' && path[5] == L':' && - is_any_path_separator(path[6])) - n = 7; - } else { - /* Not a \\?\-prefixed path. Check for an initial drive letter - * and path separator. */ - if (path[0] != L'\0' && path[1] == L':' && - is_any_path_separator(path[2])) - n = 3; - } - /* Include any additional path separators.*/ - if (n > 0) - while (is_any_path_separator(path[n])) - n++; - return n; -} - -bool -win32_path_is_root_of_drive(const wchar_t *path) +void +set_errno_from_GetLastError(void) { - size_t drive_spec_len; - wchar_t full_path[32768]; - DWORD ret; - - ret = GetFullPathName(path, ARRAY_LEN(full_path), full_path, NULL); - if (ret > 0 && ret < ARRAY_LEN(full_path)) - path = full_path; - - /* Explicit drive letter and path separator? */ - drive_spec_len = win32_path_drive_spec_len(path); - if (drive_spec_len > 0 && path[drive_spec_len] == L'\0') - return true; - - /* All path separators? */ - for (const wchar_t *p = path; *p != L'\0'; p++) - if (!is_any_path_separator(*p)) - return false; - return true; + set_errno_from_win32_error(GetLastError()); } - -/* Given a path, which may not yet exist, get a set of flags that describe the - * features of the volume the path is on. */ -int -win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret, - bool *supports_SetFileShortName_ret) +void +set_errno_from_nt_status(NTSTATUS status) { - wchar_t *volume; - BOOL bret; - DWORD vol_flags; - size_t drive_spec_len; - wchar_t filesystem_name[MAX_PATH + 1]; - - if (supports_SetFileShortName_ret) - *supports_SetFileShortName_ret = false; - - drive_spec_len = win32_path_drive_spec_len(path); - - if (drive_spec_len == 0) - if (path[0] != L'\0' && path[1] == L':') /* Drive-relative path? */ - drive_spec_len = 2; - - if (drive_spec_len == 0) { - /* Path does not start with a drive letter; use the volume of - * the current working directory. */ - volume = NULL; - } else { - /* Path starts with a drive letter (or \\?\ followed by a drive - * letter); use it. */ - volume = alloca((drive_spec_len + 2) * sizeof(wchar_t)); - wmemcpy(volume, path, drive_spec_len); - /* Add trailing backslash in case this was a drive-relative - * path. */ - volume[drive_spec_len] = L'\\'; - volume[drive_spec_len + 1] = L'\0'; - } - bret = GetVolumeInformation( - volume, /* lpRootPathName */ - NULL, /* lpVolumeNameBuffer */ - 0, /* nVolumeNameSize */ - NULL, /* lpVolumeSerialNumber */ - NULL, /* lpMaximumComponentLength */ - &vol_flags, /* lpFileSystemFlags */ - filesystem_name, /* lpFileSystemNameBuffer */ - ARRAY_LEN(filesystem_name)); /* nFileSystemNameSize */ - if (!bret) { - DWORD err = GetLastError(); - WARNING("Failed to get volume information for path \"%ls\"", path); - win32_error(err); - vol_flags = 0xffffffff; - goto out; - } - - if (wcsstr(filesystem_name, L"NTFS")) { - /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and later. - * Force it on anyway if filesystem is NTFS. */ - vol_flags |= FILE_SUPPORTS_HARD_LINKS; - - if (supports_SetFileShortName_ret) - *supports_SetFileShortName_ret = true; - } - -out: - DEBUG("using vol_flags = %x", vol_flags); - *vol_flags_ret = vol_flags; - return 0; + set_errno_from_win32_error((*func_RtlNtStatusToDosError)(status)); } static bool @@ -504,42 +374,87 @@ win32_release_capture_and_apply_privileges(void) HANDLE win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) { - return CreateFileW(path, - dwDesiredAccess, - FILE_SHARE_READ, - NULL, /* lpSecurityAttributes */ - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | - FILE_FLAG_OPEN_REPARSE_POINT, - NULL /* hTemplateFile */); -} - -HANDLE -win32_open_file_data_only(const wchar_t *path) -{ - return win32_open_existing_file(path, FILE_READ_DATA); + return CreateFile(path, + dwDesiredAccess, + FILE_SHARE_READ, + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT, + NULL /* hTemplateFile */); } -/* Pointers to functions that are not available on all targetted versions of - * Windows (XP and later). NOTE: The WINAPI annotations seem to be important; I - * assume it specifies a certain calling convention. */ - -/* Vista and later */ -HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, - STREAM_INFO_LEVELS InfoLevel, - LPVOID lpFindStreamData, - DWORD dwFlags) = NULL; - -/* Vista and later */ -BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream, - LPVOID lpFindStreamData) = NULL; +/* Pointers to dynamically loaded functions */ + +/* kernel32.dll: Vista and later */ +BOOL (WINAPI *func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName, + const wchar_t *lpTargetFileName, + DWORD dwFlags); + +/* ntdll.dll */ + +NTSTATUS (WINAPI *func_NtOpenFile) (PHANDLE FileHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG ShareAccess, + ULONG OpenOptions); + +NTSTATUS (WINAPI *func_NtReadFile) (HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key); + +NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +NTSTATUS (WINAPI *func_NtQuerySecurityObject)(HANDLE handle, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR SecurityDescriptor, + ULONG Length, + PULONG LengthNeeded); + +NTSTATUS (WINAPI *func_NtQueryDirectoryFile) (HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass, + BOOLEAN ReturnSingleEntry, + PUNICODE_STRING FileName, + BOOLEAN RestartScan); + +NTSTATUS (WINAPI *func_NtQueryVolumeInformationFile) (HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FsInformation, + ULONG Length, + FS_INFORMATION_CLASS FsInformationClass); + +NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR SecurityDescriptor); + +NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle); + +DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status); + +NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder) + (PCUNICODE_STRING VolumeRootPath); static OSVERSIONINFO windows_version_info = { .dwOSVersionInfoSize = sizeof(OSVERSIONINFO), }; -static HMODULE hKernel32 = NULL; - static bool acquired_privileges = false; bool @@ -550,18 +465,106 @@ windows_version_is_at_least(unsigned major, unsigned minor) windows_version_info.dwMinorVersion >= minor); } +struct dll_sym { + void **func_ptr; + const char *name; + bool required; +}; + +#define DLL_SYM(name, required) { (void **)&func_##name, #name, required } + +#define for_each_sym(sym, spec) \ + for ((sym) = (spec)->syms; (sym)->name; (sym)++) + +struct dll_spec { + const wchar_t *name; + HMODULE handle; + const struct dll_sym syms[]; +}; + +struct dll_spec ntdll_spec = { + .name = L"ntdll.dll", + .syms = { + DLL_SYM(NtOpenFile, true), + DLL_SYM(NtReadFile, true), + DLL_SYM(NtQueryInformationFile, true), + DLL_SYM(NtQuerySecurityObject, true), + DLL_SYM(NtQueryDirectoryFile, true), + DLL_SYM(NtQueryVolumeInformationFile, true), + DLL_SYM(NtSetSecurityObject, true), + DLL_SYM(NtClose, true), + DLL_SYM(RtlNtStatusToDosError, true), + DLL_SYM(RtlCreateSystemVolumeInformationFolder, false), + {NULL, NULL}, + }, +}; + +struct dll_spec kernel32_spec = { + .name = L"kernel32.dll", + .syms = { + DLL_SYM(CreateSymbolicLinkW, false), + {NULL, NULL}, + }, +}; + +static int +init_dll(struct dll_spec *spec) +{ + const struct dll_sym *sym; + void *addr; + + if (!spec->handle) + spec->handle = LoadLibrary(spec->name); + if (!spec->handle) { + for_each_sym(sym, spec) { + if (sym->required) { + ERROR("%ls could not be loaded!", spec->name); + return WIMLIB_ERR_UNSUPPORTED; + } + } + return 0; + } + for_each_sym(sym, spec) { + addr = (void *)GetProcAddress(spec->handle, sym->name); + if (addr) { + *(sym->func_ptr) = addr; + } else if (sym->required) { + ERROR("Can't find %s in %ls", sym->name, spec->name); + return WIMLIB_ERR_UNSUPPORTED; + } + } + return 0; +} + +static void +cleanup_dll(struct dll_spec *spec) +{ + const struct dll_sym *sym; + + if (spec->handle) { + FreeLibrary(spec->handle); + spec->handle = NULL; + + for_each_sym(sym, spec) + *(sym->func_ptr) = NULL; + } +} + /* One-time initialization for Windows capture/apply code. */ int win32_global_init(int init_flags) { + int ret; + /* Try to acquire useful privileges. */ if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) { + ret = WIMLIB_ERR_INSUFFICIENT_PRIVILEGES; if (!win32_modify_capture_privileges(true)) if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES) - goto insufficient_privileges; + goto out_drop_privs; if (!win32_modify_apply_privileges(true)) if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES) - goto insufficient_privileges; + goto out_drop_privs; acquired_privileges = true; } @@ -569,24 +572,21 @@ win32_global_init(int init_flags) GetVersionEx(&windows_version_info); /* Try to dynamically load some functions. */ - if (hKernel32 == NULL) - hKernel32 = LoadLibrary(L"Kernel32.dll"); - - if (hKernel32) { - win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, - "FindFirstStreamW"); - if (win32func_FindFirstStreamW) { - win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32, - "FindNextStreamW"); - if (!win32func_FindNextStreamW) - win32func_FindFirstStreamW = NULL; - } - } + ret = init_dll(&kernel32_spec); + if (ret) + goto out_drop_privs; + + ret = init_dll(&ntdll_spec); + if (ret) + goto out_cleanup_kernel32; + return 0; -insufficient_privileges: +out_cleanup_kernel32: + cleanup_dll(&kernel32_spec); +out_drop_privs: win32_release_capture_and_apply_privileges(); - return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES; + return ret; } void @@ -594,10 +594,9 @@ win32_global_cleanup(void) { if (acquired_privileges) win32_release_capture_and_apply_privileges(); - if (hKernel32 != NULL) { - FreeLibrary(hKernel32); - hKernel32 = NULL; - } + + cleanup_dll(&kernel32_spec); + cleanup_dll(&ntdll_spec); } #endif /* __WIN32__ */