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 (!is_any_path_separator(*p))
return false;
return true;
-
- /* XXX This function does not handle paths like "c:" where the working
- * directory on "c:" is actually "c:\", or weird paths like "\.". But
- * currently the capture and apply code always prefixes the paths with
- * \\?\ anyway so this is irrelevant... */
}
/* 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 (supports_SetFileShortName_ret)
+ *supports_SetFileShortName_ret = false;
drive_spec_len = win32_path_drive_spec_len(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)
{
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. */
/* 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),
static HMODULE hKernel32 = NULL;
+static bool acquired_privileges = false;
+
bool
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) {
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;
}