X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_common.c;h=e3dace38946f8f912b4ded844662c49bc8bef4ad;hp=e0d39ac3e1ce8be16b62eba5e84fe070507fe509;hb=8fc9b6f370468126f775fe5d0845dd4417c15a42;hpb=e8c3ca2d1d0cac3d64985b45a9f654d2029a7518 diff --git a/src/win32_common.c b/src/win32_common.c index e0d39ac3..e3dace38 100644 --- a/src/win32_common.c +++ b/src/win32_common.c @@ -33,6 +33,7 @@ #include "wimlib/assert.h" #include "wimlib/error.h" +#include "wimlib/util.h" #ifdef ENABLE_ERROR_MESSAGES void @@ -329,49 +330,177 @@ set_errno_from_GetLastError(void) errno = win32_error_to_errno(GetLastError()); } +/* 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) +{ + 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; +} + + /* 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) +win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret, + bool *supports_SetFileShortName_ret) { wchar_t *volume; BOOL bret; DWORD vol_flags; + size_t drive_spec_len; + wchar_t filesystem_name[MAX_PATH + 1]; - 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 { + 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 = GetVolumeInformationW(volume, /* lpRootPathName */ - NULL, /* lpVolumeNameBuffer */ - 0, /* nVolumeNameSize */ - NULL, /* lpVolumeSerialNumber */ - NULL, /* lpMaximumComponentLength */ - &vol_flags, /* lpFileSystemFlags */ - NULL, /* lpFileSystemNameBuffer */ - 0); /* nFileSystemNameSize */ + 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; } +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); +} + +static bool +win32_modify_apply_privileges(bool enable) +{ + return win32_modify_privilege(SE_RESTORE_NAME, enable) + && win32_modify_privilege(SE_SECURITY_NAME, enable) + && win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable); +} + +static void +win32_release_capture_and_apply_privileges(void) +{ + win32_modify_capture_privileges(false); + win32_modify_apply_privileges(false); +} + HANDLE win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) { @@ -391,6 +520,7 @@ win32_open_file_data_only(const wchar_t *path) return win32_open_existing_file(path, FILE_READ_DATA); } +#ifndef WITH_NTDLL /* 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. */ @@ -404,6 +534,12 @@ HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, /* Vista and later */ BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream, LPVOID lpFindStreamData) = NULL; +#endif /* !WITH_NTDLL */ + +/* Vista and later */ +BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName, + const wchar_t *lpTargetFileName, + DWORD dwFlags) = NULL; static OSVERSIONINFO windows_version_info = { .dwOSVersionInfoSize = sizeof(OSVERSIONINFO), @@ -411,6 +547,8 @@ static OSVERSIONINFO windows_version_info = { static HMODULE hKernel32 = NULL; +static bool acquired_privileges = false; + bool windows_version_is_at_least(unsigned major, unsigned minor) { @@ -419,23 +557,30 @@ windows_version_is_at_least(unsigned major, unsigned minor) windows_version_info.dwMinorVersion >= minor); } -/* Try to dynamically load some functions */ -void -win32_global_init(void) +/* One-time initialization for Windows capture/apply code. */ +int +win32_global_init(int init_flags) { - 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); - } + /* Try to acquire useful privileges. */ + if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) { + if (!win32_modify_capture_privileges(true)) + if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES) + goto insufficient_privileges; + if (!win32_modify_apply_privileges(true)) + if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES) + goto insufficient_privileges; + acquired_privileges = true; } + /* Get Windows version information. */ + GetVersionEx(&windows_version_info); + + /* Try to dynamically load some functions. */ + if (hKernel32 == NULL) + hKernel32 = LoadLibrary(L"Kernel32.dll"); + if (hKernel32) { + #ifndef WITH_NTDLL win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, "FindFirstStreamW"); if (win32func_FindFirstStreamW) { @@ -444,16 +589,23 @@ win32_global_init(void) if (!win32func_FindNextStreamW) win32func_FindFirstStreamW = NULL; } + #endif /* !WITH_NTDLL */ + win32func_CreateSymbolicLinkW = (void*)GetProcAddress(hKernel32, + "CreateSymbolicLinkW"); } + return 0; - GetVersionEx(&windows_version_info); +insufficient_privileges: + win32_release_capture_and_apply_privileges(); + return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES; } void win32_global_cleanup(void) { + if (acquired_privileges) + win32_release_capture_and_apply_privileges(); if (hKernel32 != NULL) { - DEBUG("Closing Kernel32.dll"); FreeLibrary(hKernel32); hKernel32 = NULL; }