X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fwin32_common.c;h=a1c3378e92ccb04e8384f83098f027c30315f545;hb=d9e9a20734344661617d935bdeef51a288ee2d0d;hp=e0d39ac3e1ce8be16b62eba5e84fe070507fe509;hpb=e8c3ca2d1d0cac3d64985b45a9f654d2029a7518;p=wimlib diff --git a/src/win32_common.c b/src/win32_common.c index e0d39ac3..a1c3378e 100644 --- a/src/win32_common.c +++ b/src/win32_common.c @@ -3,22 +3,20 @@ */ /* - * Copyright (C) 2013 Eric Biggers + * Copyright (C) 2013, 2014 Eric Biggers * - * This file is part of wimlib, a library for working with WIM files. + * 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 + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. * - * wimlib is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. - * - * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * - * You should have received a copy of the GNU General Public License - * along with wimlib; if not, see http://www.gnu.org/licenses/. + * You should have received a copy of the GNU Lesser General Public License + * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ @@ -30,31 +28,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. @@ -323,140 +300,368 @@ win32_error_to_errno(DWORD err_code) } } +void +set_errno_from_win32_error(DWORD err) +{ + errno = win32_error_to_errno(err); +} + void set_errno_from_GetLastError(void) { - errno = win32_error_to_errno(GetLastError()); + 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) +void +set_errno_from_nt_status(NTSTATUS status) { - wchar_t *volume; - BOOL bret; - DWORD vol_flags; - - if (path[0] != L'\0' && path[0] != L'\\' && - path[0] != L'/' && path[1] == L':') - { - /* Path starts with a drive letter; use it. */ - volume = alloca(4 * sizeof(wchar_t)); - volume[0] = path[0]; - volume[1] = path[1]; - volume[2] = L'\\'; - volume[3] = L'\0'; - } else { - /* Path does not start with a drive letter; use the volume of - * the current working directory. */ - volume = NULL; - } - bret = GetVolumeInformationW(volume, /* lpRootPathName */ - NULL, /* lpVolumeNameBuffer */ - 0, /* nVolumeNameSize */ - NULL, /* lpVolumeSerialNumber */ - NULL, /* lpMaximumComponentLength */ - &vol_flags, /* lpFileSystemFlags */ - NULL, /* lpFileSystemNameBuffer */ - 0); /* nFileSystemNameSize */ - if (!bret) { - DWORD err = GetLastError(); - WARNING("Failed to get volume information for path \"%ls\"", path); - win32_error(err); - vol_flags = 0xffffffff; - } + set_errno_from_win32_error((*func_RtlNtStatusToDosError)(status)); +} - DEBUG("using vol_flags = %x", vol_flags); - *vol_flags_ret = vol_flags; - return 0; +static bool +win32_modify_privilege(const wchar_t *privilege, bool enable) +{ + HANDLE hToken; + LUID luid; + TOKEN_PRIVILEGES newState; + bool ret = FALSE; + + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &hToken)) + goto out; + + if (!LookupPrivilegeValue(NULL, privilege, &luid)) + goto out_close_handle; + + newState.PrivilegeCount = 1; + newState.Privileges[0].Luid = luid; + newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0); + SetLastError(ERROR_SUCCESS); + ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL); + if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED) + ret = FALSE; +out_close_handle: + CloseHandle(hToken); +out: + return ret; +} + +static bool +win32_modify_capture_privileges(bool enable) +{ + return win32_modify_privilege(SE_BACKUP_NAME, enable) + && win32_modify_privilege(SE_SECURITY_NAME, enable); } -HANDLE -win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) +static bool +win32_modify_apply_privileges(bool enable) { - return CreateFileW(path, - dwDesiredAccess, - FILE_SHARE_READ, - NULL, /* lpSecurityAttributes */ - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | - FILE_FLAG_OPEN_REPARSE_POINT, - NULL /* hTemplateFile */); + return win32_modify_privilege(SE_RESTORE_NAME, enable) + && win32_modify_privilege(SE_SECURITY_NAME, enable) + && win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable); } -HANDLE -win32_open_file_data_only(const wchar_t *path) +static void +win32_release_capture_and_apply_privileges(void) { - return win32_open_existing_file(path, FILE_READ_DATA); + win32_modify_capture_privileges(false); + win32_modify_apply_privileges(false); } -/* 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. */ +/* Pointers to dynamically loaded functions */ + +/* ntdll.dll */ + +NTSTATUS (WINAPI *func_NtCreateFile)(PHANDLE FileHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + PLARGE_INTEGER AllocationSize, + ULONG FileAttributes, + ULONG ShareAccess, + ULONG CreateDisposition, + ULONG CreateOptions, + PVOID EaBuffer, + ULONG EaLength); + +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_NtWriteFile) (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_NtSetInformationFile)(HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR SecurityDescriptor); + +NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength); + +NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle); + +DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status); + +BOOLEAN (WINAPI *func_RtlDosPathNameToNtPathName_U) + (IN PCWSTR DosName, + OUT PUNICODE_STRING NtName, + OUT PCWSTR *PartName, + OUT PRTL_RELATIVE_NAME_U RelativeName); + +NTSTATUS (WINAPI *func_RtlDosPathNameToNtPathName_U_WithStatus) + (IN PCWSTR DosName, + OUT PUNICODE_STRING NtName, + OUT PCWSTR *PartName, + OUT PRTL_RELATIVE_NAME_U RelativeName); + +NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder) + (PCUNICODE_STRING VolumeRootPath); + +static bool acquired_privileges = false; -/* Vista and later */ -HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, - STREAM_INFO_LEVELS InfoLevel, - LPVOID lpFindStreamData, - DWORD dwFlags) = NULL; +struct dll_sym { + void **func_ptr; + const char *name; + bool required; +}; + +#define DLL_SYM(name, required) { (void **)&func_##name, #name, required } -/* Vista and later */ -BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream, - LPVOID lpFindStreamData) = NULL; +#define for_each_sym(sym, spec) \ + for ((sym) = (spec)->syms; (sym)->name; (sym)++) -static OSVERSIONINFO windows_version_info = { - .dwOSVersionInfoSize = sizeof(OSVERSIONINFO), +struct dll_spec { + const wchar_t *name; + HMODULE handle; + const struct dll_sym syms[]; }; -static HMODULE hKernel32 = NULL; +struct dll_spec ntdll_spec = { + .name = L"ntdll.dll", + .syms = { + DLL_SYM(NtCreateFile, true), + DLL_SYM(NtOpenFile, true), + DLL_SYM(NtReadFile, true), + DLL_SYM(NtWriteFile, true), + DLL_SYM(NtQueryInformationFile, true), + DLL_SYM(NtQuerySecurityObject, true), + DLL_SYM(NtQueryDirectoryFile, true), + DLL_SYM(NtQueryVolumeInformationFile, true), + DLL_SYM(NtSetInformationFile, true), + DLL_SYM(NtSetSecurityObject, true), + DLL_SYM(NtFsControlFile, true), + DLL_SYM(NtClose, true), + DLL_SYM(RtlNtStatusToDosError, true), + DLL_SYM(RtlCreateSystemVolumeInformationFolder, false), + DLL_SYM(RtlDosPathNameToNtPathName_U, true), + DLL_SYM(RtlDosPathNameToNtPathName_U_WithStatus, false), /* Not present on XP */ + {NULL, NULL}, + }, +}; -bool -windows_version_is_at_least(unsigned major, unsigned minor) +static int +init_dll(struct dll_spec *spec) { - return windows_version_info.dwMajorVersion > major || - (windows_version_info.dwMajorVersion == major && - windows_version_info.dwMinorVersion >= minor); + 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; } -/* Try to dynamically load some functions */ -void -win32_global_init(void) +static void +cleanup_dll(struct dll_spec *spec) { - DWORD err; - - if (hKernel32 == NULL) { - DEBUG("Loading Kernel32.dll"); - hKernel32 = LoadLibraryW(L"Kernel32.dll"); - if (hKernel32 == NULL) { - err = GetLastError(); - WARNING("Can't load Kernel32.dll"); - win32_error(err); - } + const struct dll_sym *sym; + + if (spec->handle) { + FreeLibrary(spec->handle); + spec->handle = NULL; + + for_each_sym(sym, spec) + *(sym->func_ptr) = NULL; } +} - if (hKernel32) { - win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, - "FindFirstStreamW"); - if (win32func_FindFirstStreamW) { - win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32, - "FindNextStreamW"); - if (!win32func_FindNextStreamW) - win32func_FindFirstStreamW = 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 out_drop_privs; + if (!win32_modify_apply_privileges(true)) + if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES) + goto out_drop_privs; + acquired_privileges = true; } - GetVersionEx(&windows_version_info); + ret = init_dll(&ntdll_spec); + if (ret) + goto out_drop_privs; + + return 0; + +out_drop_privs: + win32_release_capture_and_apply_privileges(); + return ret; } void win32_global_cleanup(void) { - if (hKernel32 != NULL) { - DEBUG("Closing Kernel32.dll"); - FreeLibrary(hKernel32); - hKernel32 = NULL; + if (acquired_privileges) + win32_release_capture_and_apply_privileges(); + + cleanup_dll(&ntdll_spec); +} + +/* + * Translates a Win32-namespace path into an NT-namespace path. + * + * On success, returns 0. The NT-namespace path will be stored in the + * UNICODE_STRING structure pointed to by nt_path. nt_path->Buffer will be set + * to a new buffer that must later be freed with HeapFree(). (Really + * RtlHeapFree(), but HeapFree() seems to be the same thing.) + * + * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM. + */ +int +win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path) +{ + NTSTATUS status; + + if (func_RtlDosPathNameToNtPathName_U_WithStatus) { + status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path, + nt_path, + NULL, NULL); + } else { + if ((*func_RtlDosPathNameToNtPathName_U)(win32_path, nt_path, + NULL, NULL)) + status = STATUS_SUCCESS; + else + status = STATUS_NO_MEMORY; + } + + if (likely(NT_SUCCESS(status))) + return 0; + + if (status == STATUS_NO_MEMORY) + return WIMLIB_ERR_NOMEM; + + ERROR("\"%ls\": invalid path name (status=0x%08"PRIx32")", + win32_path, (u32)status); + return WIMLIB_ERR_INVALID_PARAM; +} + +int +win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7]) +{ + tchar *file_abspath; + + file_abspath = realpath(file_path, NULL); + if (!file_abspath) + return WIMLIB_ERR_NOMEM; + + if (file_abspath[0] == L'\0' || file_abspath[1] != L':') { + ERROR("\"%ls\": Path format not recognized", file_abspath); + FREE(file_abspath); + return WIMLIB_ERR_UNSUPPORTED; } + + wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]); + FREE(file_abspath); + return 0; } + #endif /* __WIN32__ */