From: Eric Biggers Date: Tue, 13 May 2014 18:59:48 +0000 (-0500) Subject: Windows: Use ntdll unconditionally X-Git-Tag: v1.7.0~178 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=b2efb3c11ba10e2fc08dcf5ca7dab0ab9f51363e Windows: Use ntdll unconditionally The non-ntdll code isn't being tested, and there are known problems with it. For now, just rely on ntdll unconditionally. --- diff --git a/configure.ac b/configure.ac index 3f7f2b89..f4b143d0 100644 --- a/configure.ac +++ b/configure.ac @@ -206,25 +206,6 @@ case "$host" in ;; esac -AC_ARG_WITH([ntdll], - AS_HELP_STRING([--without-ntdll], [Windows only: do not try to - use functions from ntdll. By default, in some parts - of its code, wimlib uses Windows NT system calls to - work around flaws in the Win32 API. This support - can be omitted if needed, but some annoying bugs - will surface (e.g. permission denied errors even - when running as the Administrator).]), - [WITH_NTDLL=$withval], - [WITH_NTDLL=yes]) - -if test "x$WINDOWS_NATIVE_BUILD" = "xyes"; then - if test "x$WITH_NTDLL" = "xyes"; then - WINDOWS_LDADD="$WINDOWS_LDADD" - AC_DEFINE([WITH_NTDLL], [1], [Define to 1 to try to use ntdll - functions]) - fi -fi - AC_SUBST([VISIBILITY_CFLAGS], [$VISIBILITY_CFLAGS]) AC_SUBST([WINDOWS_LDFLAGS], [$WINDOWS_LDFLAGS]) AC_SUBST([WINDOWS_LDADD], [$WINDOWS_LDADD]) diff --git a/include/wimlib/win32_common.h b/include/wimlib/win32_common.h index fa10469b..5e108ec5 100644 --- a/include/wimlib/win32_common.h +++ b/include/wimlib/win32_common.h @@ -9,10 +9,8 @@ #include "wimlib/types.h" #include "wimlib/win32.h" -#ifdef WITH_NTDLL -# include -# include -#endif +#include +#include extern void set_errno_from_GetLastError(void); @@ -20,10 +18,8 @@ set_errno_from_GetLastError(void); extern void set_errno_from_win32_error(DWORD err); -#ifdef WITH_NTDLL extern void set_errno_from_nt_status(NTSTATUS status); -#endif extern bool win32_path_is_root_of_drive(const wchar_t *path); @@ -52,8 +48,6 @@ extern BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFile /* ntdll functions */ -#ifdef WITH_NTDLL - extern NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, @@ -87,8 +81,6 @@ extern DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status); extern NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder) (PCUNICODE_STRING VolumeRootPath); -#endif - extern bool diff --git a/src/ntfs-3g_capture.c b/src/ntfs-3g_capture.c index 228a68d8..48bbc482 100644 --- a/src/ntfs-3g_capture.c +++ b/src/ntfs-3g_capture.c @@ -394,12 +394,10 @@ set_dentry_dos_name(struct wim_dentry *dentry, const struct dos_name_map *map) if (dentry->is_win32_name) { node = lookup_dos_name(map, dentry->d_inode->i_ino); if (node) { - dentry->short_name = MALLOC(node->name_nbytes + 2); + dentry->short_name = utf16le_dupz(node->dos_node, + node->name_nbytes); if (!dentry->short_name) return WIMLIB_ERR_NOMEM; - memcpy(dentry->short_name, node->dos_name, - node->name_nbytes); - dentry->short_name[node->name_nbytes / 2] = 0; dentry->short_name_nbytes = node->name_nbytes; DEBUG("Assigned DOS name to ino %"PRIu64, dentry->d_inode->i_ino); diff --git a/src/win32_apply.c b/src/win32_apply.c index 56f82b6b..ac0bed01 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.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. * @@ -730,23 +730,6 @@ error: return WIMLIB_ERR_WRITE; /* XXX: need better error code */ } -static DWORD -do_win32_set_security_descriptor(HANDLE h, const wchar_t *path, - SECURITY_INFORMATION info, - PSECURITY_DESCRIPTOR desc) -{ -#ifdef WITH_NTDLL - if (func_NtSetSecurityObject) { - return (*func_RtlNtStatusToDosError)( - (*func_NtSetSecurityObject)(h, info, desc)); - } -#endif - if (SetFileSecurity(path, info, desc)) - return ERROR_SUCCESS; - else - return GetLastError(); -} - /* * Set an arbitrary security descriptor on an arbitrary file (or directory), * working around bugs and design flaws in the Windows operating system. @@ -761,7 +744,9 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc, size_t desc_size, struct apply_ctx *ctx) { SECURITY_INFORMATION info; + DWORD dwDesiredAccess; HANDLE h; + DWORD status; int ret; /* We really just want to set entire the security descriptor as-is, but @@ -773,8 +758,6 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc, info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; - h = INVALID_HANDLE_VALUE; - /* Prefer NtSetSecurityObject() to SetFileSecurity(). SetFileSecurity() * itself necessarily uses NtSetSecurityObject() as the latter is the * underlying system call for setting security information, but @@ -784,83 +767,69 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc, * Administrator can have access denied. (Of course, this not mentioned * in the MS "documentation".) */ -#ifdef WITH_NTDLL - if (func_NtSetSecurityObject) { - DWORD dwDesiredAccess; - - /* Open a handle for NtSetSecurityObject() with as many relevant - * access rights as possible. - * - * We don't know which rights will be actually granted. It - * could be less than what is needed to actually assign the full - * security descriptor, especially if the process is running as - * a non-Administrator. However, by default we just do the best - * we can, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS has been - * enabled. The MAXIMUM_ALLOWED access right is seemingly - * designed for this use case; however, it does not work - * properly in all cases: it can cause CreateFile() to fail with - * ERROR_ACCESS_DENIED, even though by definition - * MAXIMUM_ALLOWED access only requests access rights that are - * *not* denied. (Needless to say, MS does not document this - * bug.) */ - - dwDesiredAccess = WRITE_DAC | - WRITE_OWNER | - ACCESS_SYSTEM_SECURITY; - for (;;) { - DWORD err; - - h = win32_open_existing_file(path, dwDesiredAccess); - if (h != INVALID_HANDLE_VALUE) - break; - err = GetLastError(); - if (err == ERROR_ACCESS_DENIED || - err == ERROR_PRIVILEGE_NOT_HELD) - { - /* Don't increment partial_security_descriptors - * here or check WIMLIB_EXTRACT_FLAG_STRICT_ACLS - * here. It will be done later if needed; here - * we are just trying to get as many relevant - * access rights as possible. */ - if (dwDesiredAccess & ACCESS_SYSTEM_SECURITY) { - dwDesiredAccess &= ~ACCESS_SYSTEM_SECURITY; - continue; - } - if (dwDesiredAccess & WRITE_DAC) { - dwDesiredAccess &= ~WRITE_DAC; - continue; - } - if (dwDesiredAccess & WRITE_OWNER) { - dwDesiredAccess &= ~WRITE_OWNER; - continue; - } + /* Open a handle for NtSetSecurityObject() with as many relevant + * access rights as possible. + * + * We don't know which rights will be actually granted. It + * could be less than what is needed to actually assign the full + * security descriptor, especially if the process is running as + * a non-Administrator. However, by default we just do the best + * we can, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS has been + * enabled. The MAXIMUM_ALLOWED access right is seemingly + * designed for this use case; however, it does not work + * properly in all cases: it can cause CreateFile() to fail with + * ERROR_ACCESS_DENIED, even though by definition + * MAXIMUM_ALLOWED access only requests access rights that are + * *not* denied. (Needless to say, MS does not document this + * bug.) */ + + dwDesiredAccess = WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY; + while ((h = win32_open_existing_file(path, + dwDesiredAccess)) == INVALID_HANDLE_VALUE) + { + DWORD err; + + err = GetLastError(); + if (err == ERROR_ACCESS_DENIED || + err == ERROR_PRIVILEGE_NOT_HELD) + { + /* Don't increment partial_security_descriptors + * here or check WIMLIB_EXTRACT_FLAG_STRICT_ACLS + * here. It will be done later if needed; here + * we are just trying to get as many relevant + * access rights as possible. */ + if (dwDesiredAccess & ACCESS_SYSTEM_SECURITY) { + dwDesiredAccess &= ~ACCESS_SYSTEM_SECURITY; + continue; + } + if (dwDesiredAccess & WRITE_DAC) { + dwDesiredAccess &= ~WRITE_DAC; + continue; + } + if (dwDesiredAccess & WRITE_OWNER) { + dwDesiredAccess &= ~WRITE_OWNER; + continue; } - /* Other error, or couldn't open the file even with no - * access rights specified. Something else must be - * wrong. */ - set_errno_from_win32_error(err); - return WIMLIB_ERR_SET_SECURITY; } + /* Other error, or couldn't open the file even with no + * access rights specified. Something else must be + * wrong. */ + set_errno_from_win32_error(err); + return WIMLIB_ERR_SET_SECURITY; } -#endif /* Try setting the security descriptor. */ - for (;;) { - DWORD err; - - err = do_win32_set_security_descriptor(h, path, info, - (PSECURITY_DESCRIPTOR)desc); - if (err == ERROR_SUCCESS) { - ret = 0; - break; - } - + ret = 0; + while (!(NT_SUCCESS(status = (*func_NtSetSecurityObject)(h, + info, + (PSECURITY_DESCRIPTOR)desc)))) + { /* Failed to set the requested parts of the security descriptor. * If the error was permissions-related, try to set fewer parts * of the security descriptor, unless * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled. */ - if ((err == ERROR_PRIVILEGE_NOT_HELD || - err == ERROR_ACCESS_DENIED) && + if ((status == STATUS_PRIVILEGE_NOT_HELD || + status == STATUS_ACCESS_DENIED) && !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) { if (info & SACL_SECURITY_INFORMATION) { @@ -884,16 +853,13 @@ win32_set_security_descriptor(const wchar_t *path, const u8 *desc, * security descriptor could not be set. */ if (!(info & SACL_SECURITY_INFORMATION)) ctx->partial_security_descriptors--; - set_errno_from_win32_error(err); + set_errno_from_nt_status(status); ret = WIMLIB_ERR_SET_SECURITY; break; } /* Close handle opened for NtSetSecurityObject(). */ -#ifdef WITH_NTDLL - if (func_NtSetSecurityObject) - CloseHandle(h); -#endif + CloseHandle(h); return ret; } diff --git a/src/win32_capture.c b/src/win32_capture.c index feac7644..50191e6d 100644 --- a/src/win32_capture.c +++ b/src/win32_capture.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. * @@ -31,6 +31,7 @@ #include "wimlib/capture.h" #include "wimlib/dentry.h" +#include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/lookup_table.h" @@ -48,13 +49,6 @@ struct win32_capture_state { }; -static const wchar_t *capture_access_denied_msg = -L" If you are not running this program as the administrator, you may\n" - " need to do so, so that all data and metadata can be backed up.\n" - " Otherwise, there may be no way to access the desired data or\n" - " metadata without taking ownership of the file or directory.\n" - ; - int read_win32_file_prefix(const struct wim_lookup_table_entry *lte, u64 size, @@ -92,7 +86,6 @@ read_win32_file_prefix(const struct wim_lookup_table_entry *lte, if (ret) break; } -out_close_handle: CloseHandle(hFile); return ret; } @@ -136,7 +129,7 @@ read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte, void *file_ctx; int ret; - DEBUG("Reading %"PRIu64" bytes from encryted file \"%ls\"", + DEBUG("Reading %"PRIu64" bytes from encrypted file \"%ls\"", size, lte->file_on_disk); export_ctx.read_prefix_cb = cb; @@ -187,113 +180,28 @@ FILETIME_to_u64(const FILETIME *ft) * short names are essentially obsolete anyway. */ static int -win32_get_short_name(HANDLE hFile, const wchar_t *path, struct wim_dentry *dentry) +win32_get_short_name(HANDLE hFile, struct wim_dentry *dentry) { /* It's not any harder to just make the NtQueryInformationFile() system * call ourselves, and it saves a dumb call to FindFirstFile() which of * course has to create its own handle. */ -#ifdef WITH_NTDLL - if (func_NtQueryInformationFile) { - NTSTATUS status; - IO_STATUS_BLOCK io_status; - u8 buf[128] _aligned_attribute(8); - const FILE_NAME_INFORMATION *info; - - status = (*func_NtQueryInformationFile)(hFile, &io_status, buf, sizeof(buf), - FileAlternateNameInformation); - info = (const FILE_NAME_INFORMATION*)buf; - if (status == STATUS_SUCCESS && info->FileNameLength != 0) { - dentry->short_name = MALLOC(info->FileNameLength + 2); - if (!dentry->short_name) - return WIMLIB_ERR_NOMEM; - memcpy(dentry->short_name, info->FileName, - info->FileNameLength); - dentry->short_name[info->FileNameLength / 2] = L'\0'; - dentry->short_name_nbytes = info->FileNameLength; - } - return 0; - } -#endif - - WIN32_FIND_DATAW dat; - HANDLE hFind; - int ret = 0; - - hFind = FindFirstFile(path, &dat); - if (hFind != INVALID_HANDLE_VALUE) { - if (dat.cAlternateFileName[0] != L'\0') { - DEBUG("\"%ls\": short name \"%ls\"", path, dat.cAlternateFileName); - size_t short_name_nbytes = wcslen(dat.cAlternateFileName) * - sizeof(wchar_t); - size_t n = short_name_nbytes + sizeof(wchar_t); - dentry->short_name = MALLOC(n); - if (dentry->short_name) { - memcpy(dentry->short_name, dat.cAlternateFileName, n); - dentry->short_name_nbytes = short_name_nbytes; - } else { - ret = WIMLIB_ERR_NOMEM; - } - } - FindClose(hFind); - } - return ret; -} - -/* - * win32_query_security_descriptor() - Query a file's security descriptor - * - * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE - * format, and we currently have a handle opened with as many relevant - * permissions as possible. At this point, on Windows there are a number of - * options for reading a file's security descriptor: - * - * GetFileSecurity(): This takes in a path and returns the - * SECURITY_DESCRIPTOR_RELATIVE. Problem: this uses an internal handle, not - * ours, and the handle created internally doesn't specify - * FILE_FLAG_BACKUP_SEMANTICS. Therefore there can be access denied errors on - * some files and directories, even when running as the Administrator. - * - * GetSecurityInfo(): This takes in a handle and returns the security - * descriptor split into a bunch of different parts. This should work, but it's - * dumb because we have to put the security descriptor back together again. - * - * BackupRead(): This can read the security descriptor, but this is a - * difficult-to-use API, probably only works as the Administrator, and the - * format of the returned data is not well documented. - * - * NtQuerySecurityObject(): This is exactly what we need, as it takes in a - * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE - * format. Only problem is that it's a ntdll function and therefore not - * officially part of the Win32 API. Oh well. - */ -static DWORD -win32_query_security_descriptor(HANDLE hFile, const wchar_t *path, - SECURITY_INFORMATION requestedInformation, - SECURITY_DESCRIPTOR *buf, - DWORD bufsize, DWORD *lengthNeeded) -{ -#ifdef WITH_NTDLL - if (func_NtQuerySecurityObject) { - NTSTATUS status; - - status = (*func_NtQuerySecurityObject)(hFile, - requestedInformation, buf, - bufsize, lengthNeeded); - /* Since it queries an already-open handle, NtQuerySecurityObject() - * apparently returns STATUS_ACCESS_DENIED rather than - * STATUS_PRIVILEGE_NOT_HELD. */ - if (status == STATUS_ACCESS_DENIED) - return ERROR_PRIVILEGE_NOT_HELD; - else - return (*func_RtlNtStatusToDosError)(status); + NTSTATUS status; + IO_STATUS_BLOCK io_status; + u8 buf[128] _aligned_attribute(8); + const FILE_NAME_INFORMATION *info; + + status = (*func_NtQueryInformationFile)(hFile, &io_status, buf, sizeof(buf), + FileAlternateNameInformation); + info = (const FILE_NAME_INFORMATION*)buf; + if (NT_SUCCESS(status) && info->FileNameLength != 0) { + dentry->short_name = utf16le_dupz(info->FileName, + info->FileNameLength); + if (!dentry->short_name) + return WIMLIB_ERR_NOMEM; + dentry->short_name_nbytes = info->FileNameLength; } -#endif - if (GetFileSecurity(path, requestedInformation, buf, - bufsize, lengthNeeded)) - return ERROR_SUCCESS; - else - return GetLastError(); + return 0; } static int @@ -309,7 +217,7 @@ win32_get_security_descriptor(HANDLE hFile, u8 *buf; size_t bufsize; DWORD lenNeeded; - DWORD err; + NTSTATUS status; int ret; requestedInformation = DACL_SECURITY_INFORMATION | @@ -318,46 +226,67 @@ win32_get_security_descriptor(HANDLE hFile, GROUP_SECURITY_INFORMATION; buf = _buf; bufsize = sizeof(_buf); - for (;;) { - err = win32_query_security_descriptor(hFile, path, - requestedInformation, - (SECURITY_DESCRIPTOR*)buf, - bufsize, &lenNeeded); - switch (err) { - case ERROR_SUCCESS: - goto have_descriptor; - case ERROR_INSUFFICIENT_BUFFER: + + /* + * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE + * format, and we currently have a handle opened with as many relevant + * permissions as possible. At this point, on Windows there are a number of + * options for reading a file's security descriptor: + * + * GetFileSecurity(): This takes in a path and returns the + * SECURITY_DESCRIPTOR_RELATIVE. Problem: this uses an internal handle, not + * ours, and the handle created internally doesn't specify + * FILE_FLAG_BACKUP_SEMANTICS. Therefore there can be access denied errors on + * some files and directories, even when running as the Administrator. + * + * GetSecurityInfo(): This takes in a handle and returns the security + * descriptor split into a bunch of different parts. This should work, but it's + * dumb because we have to put the security descriptor back together again. + * + * BackupRead(): This can read the security descriptor, but this is a + * difficult-to-use API, probably only works as the Administrator, and the + * format of the returned data is not well documented. + * + * NtQuerySecurityObject(): This is exactly what we need, as it takes in a + * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE + * format. Only problem is that it's a ntdll function and therefore not + * officially part of the Win32 API. Oh well. + */ + while (!(NT_SUCCESS(status = (*func_NtQuerySecurityObject)(hFile, + requestedInformation, + (PSECURITY_DESCRIPTOR)buf, + bufsize, + &lenNeeded)))) + { + switch (status) { + case STATUS_BUFFER_OVERFLOW: wimlib_assert(buf == _buf); buf = MALLOC(lenNeeded); if (!buf) return WIMLIB_ERR_NOMEM; bufsize = lenNeeded; break; - case ERROR_PRIVILEGE_NOT_HELD: - if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS) - goto fail; + case STATUS_PRIVILEGE_NOT_HELD: + case STATUS_ACCESS_DENIED: + if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS) { + default: + set_errno_from_nt_status(status); + ERROR_WITH_ERRNO("\"%ls\": Failed to " + "read security descriptor", path); + ret = WIMLIB_ERR_READ; + goto out_free_buf; + } if (requestedInformation & SACL_SECURITY_INFORMATION) { state->num_get_sacl_priv_notheld++; requestedInformation &= ~SACL_SECURITY_INFORMATION; break; } - /* Fall through */ - case ERROR_ACCESS_DENIED: - if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS) - goto fail; state->num_get_sd_access_denied++; ret = 0; goto out_free_buf; - default: - fail: - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Failed to read security descriptor of \"%ls\"", path); - ret = WIMLIB_ERR_READ; - goto out_free_buf; } } -have_descriptor: inode->i_security_id = sd_set_add_sd(sd_set, buf, lenNeeded); if (inode->i_security_id < 0) ret = WIMLIB_ERR_NOMEM; @@ -393,48 +322,23 @@ win32_recurse_directory(HANDLE hDir, DEBUG("Recurse to directory \"%ls\"", dir_path); /* Using NtQueryDirectoryFile() we can re-use the same open handle, - * which we opened with FILE_FLAG_BACKUP_SEMANTICS (probably not the - * case for the FindFirstFile() API; it's not documented). */ -#ifdef WITH_NTDLL - if (!func_NtQueryDirectoryFile) - goto use_FindFirstFile; + * which we opened with FILE_FLAG_BACKUP_SEMANTICS. */ NTSTATUS status; IO_STATUS_BLOCK io_status; const size_t bufsize = 8192; - u8 *buf; - BOOL restartScan = TRUE; - const FILE_NAMES_INFORMATION *info; + void *buf; buf = MALLOC(bufsize); if (!buf) return WIMLIB_ERR_NOMEM; - for (;;) { - status = (*func_NtQueryDirectoryFile)(hDir, NULL, NULL, NULL, - &io_status, buf, bufsize, - FileNamesInformation, - FALSE, NULL, restartScan); - restartScan = FALSE; - if (status != STATUS_SUCCESS) { - if (status == STATUS_NO_MORE_FILES || - status == STATUS_NO_MORE_ENTRIES || - status == STATUS_NO_MORE_MATCHES) { - ret = 0; - } else if (status == STATUS_NOT_IMPLEMENTED || - status == STATUS_NOT_SUPPORTED || - status == STATUS_INVALID_INFO_CLASS) { - FREE(buf); - goto use_FindFirstFile; - } else { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Failed to read directory " - "\"%ls\"", dir_path); - ret = WIMLIB_ERR_READ; - } - goto out_free_buf; - } - wimlib_assert(io_status.Information != 0); - info = (const FILE_NAMES_INFORMATION*)buf; + + while (NT_SUCCESS(status = (*func_NtQueryDirectoryFile)(hDir, NULL, NULL, NULL, + &io_status, buf, bufsize, + FileNamesInformation, + FALSE, NULL, FALSE))) + { + const FILE_NAMES_INFORMATION *info = buf; for (;;) { if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') && !(info->FileNameLength == 4 && info->FileName[0] == L'.' && @@ -466,80 +370,18 @@ win32_recurse_directory(HANDLE hDir, } if (info->NextEntryOffset == 0) break; - info = (const FILE_NAMES_INFORMATION*) - ((const u8*)info + info->NextEntryOffset); + info = (const FILE_NAMES_INFORMATION *) + ((const u8 *)info + info->NextEntryOffset); } } -out_free_buf: - FREE(buf); - return ret; -#endif -use_FindFirstFile: - ; - WIN32_FIND_DATAW dat; - HANDLE hFind; - DWORD err; - - /* Begin reading the directory by calling FindFirstFileW. Unlike UNIX - * opendir(), FindFirstFileW has file globbing built into it. But this - * isn't what we actually want, so just add a dummy glob to get all - * entries. */ - dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR; - dir_path[dir_path_num_chars + 1] = L'*'; - dir_path[dir_path_num_chars + 2] = L'\0'; - hFind = FindFirstFile(dir_path, &dat); - dir_path[dir_path_num_chars] = L'\0'; - - if (hFind == INVALID_HANDLE_VALUE) { - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) { - return 0; - } else { - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Failed to read directory \"%ls\"", - dir_path); - return WIMLIB_ERR_READ; - } - } - ret = 0; - do { - /* Skip . and .. entries */ - if (dat.cFileName[0] == L'.' && - (dat.cFileName[1] == L'\0' || - (dat.cFileName[1] == L'.' && - dat.cFileName[2] == L'\0'))) - continue; - size_t filename_len = wcslen(dat.cFileName); - - dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR; - wmemcpy(dir_path + dir_path_num_chars + 1, - dat.cFileName, - filename_len + 1); - - struct wim_dentry *child; - size_t path_len = dir_path_num_chars + 1 + filename_len; - ret = win32_build_dentry_tree_recursive(&child, - dir_path, - path_len, - params, - state, - vol_flags); - dir_path[dir_path_num_chars] = L'\0'; - if (ret) - goto out_find_close; - if (child) - dentry_add_child(root, child); - } while (FindNextFile(hFind, &dat)); - err = GetLastError(); - if (err != ERROR_NO_MORE_FILES) { - set_errno_from_win32_error(err); + if (status != STATUS_NO_MORE_FILES) { + set_errno_from_nt_status(status); ERROR_WITH_ERRNO("Failed to read directory \"%ls\"", dir_path); - if (ret == 0) - ret = WIMLIB_ERR_READ; + ret = WIMLIB_ERR_READ; } -out_find_close: - FindClose(hFind); +out_free_buf: + FREE(buf); return ret; } @@ -932,8 +774,8 @@ out_invalid_stream_name: /* Load information about the streams of an open file into a WIM inode. * - * By default, we use the NtQueryInformationFile() system call instead of - * FindFirstStream() and FindNextStream(). This is done for two reasons: + * We use the NtQueryInformationFile() system call instead of FindFirstStream() + * and FindNextStream(). This is done for two reasons: * * - FindFirstStream() opens its own handle to the file or directory and * apparently does so without specifying FILE_FLAG_BACKUP_SEMANTICS, thereby @@ -952,59 +794,55 @@ win32_capture_streams(HANDLE *hFile_p, u64 file_size, unsigned vol_flags) { - WIN32_FIND_STREAM_DATA dat; int ret; -#ifdef WITH_NTDLL u8 _buf[8192] _aligned_attribute(8); u8 *buf; size_t bufsize; IO_STATUS_BLOCK io_status; NTSTATUS status; const FILE_STREAM_INFORMATION *info; -#endif - HANDLE hFind; - DWORD err; DEBUG("Capturing streams from \"%ls\"", path); - if (!(vol_flags & FILE_NAMED_STREAMS)) - goto unnamed_only; - -#ifdef WITH_NTDLL - if (!func_NtQueryInformationFile) - goto use_FindFirstStream; - buf = _buf; bufsize = sizeof(_buf); - /* Get a buffer containing the stream information. */ - for (;;) { - status = (*func_NtQueryInformationFile)(*hFile_p, &io_status, - buf, bufsize, - FileStreamInformation); - if (status == STATUS_SUCCESS) { - break; - } else if (status == STATUS_BUFFER_OVERFLOW) { - u8 *newbuf; + if (!(vol_flags & FILE_NAMED_STREAMS)) + goto unnamed_only; - bufsize *= 2; - if (buf == _buf) - newbuf = MALLOC(bufsize); - else - newbuf = REALLOC(buf, bufsize); + /* Get a buffer containing the stream information. */ + while (!NT_SUCCESS(status = (*func_NtQueryInformationFile)(*hFile_p, + &io_status, + buf, + bufsize, + FileStreamInformation))) + { - if (!newbuf) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_buf; + switch (status) { + case STATUS_BUFFER_OVERFLOW: + { + u8 *newbuf; + + bufsize *= 2; + if (buf == _buf) + newbuf = MALLOC(bufsize); + else + newbuf = REALLOC(buf, bufsize); + if (!newbuf) { + ret = WIMLIB_ERR_NOMEM; + goto out_free_buf; + } + buf = newbuf; } - buf = newbuf; - } else if (status == STATUS_NOT_IMPLEMENTED || - status == STATUS_NOT_SUPPORTED || - status == STATUS_INVALID_INFO_CLASS) { - goto use_FindFirstStream; - } else { + break; + case STATUS_NOT_IMPLEMENTED: + case STATUS_NOT_SUPPORTED: + case STATUS_INVALID_INFO_CLASS: + goto unnamed_only; + default: set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Failed to read streams of %ls", path); + ERROR_WITH_ERRNO("\"%ls\": Failed to query " + "stream information", path); ret = WIMLIB_ERR_READ; goto out_free_buf; } @@ -1025,8 +863,10 @@ win32_capture_streams(HANDLE *hFile_p, } /* Parse one or more stream information structures. */ - info = (const FILE_STREAM_INFORMATION*)buf; + info = (const FILE_STREAM_INFORMATION *)buf; for (;;) { + WIN32_FIND_STREAM_DATA dat; + if (info->StreamNameLength <= sizeof(dat.cStreamName) - 2) { dat.StreamSize = info->StreamSize; memcpy(dat.cStreamName, info->StreamName, info->StreamNameLength); @@ -1040,85 +880,39 @@ win32_capture_streams(HANDLE *hFile_p, } if (info->NextEntryOffset == 0) { /* No more stream information. */ - ret = 0; break; } /* Advance to next stream information. */ - info = (const FILE_STREAM_INFORMATION*) - ((const u8*)info + info->NextEntryOffset); - } -out_free_buf: - /* Free buffer if allocated on heap. */ - if (buf != _buf) - FREE(buf); - return ret; -#endif /* WITH_NTDLL */ - -use_FindFirstStream: - if (win32func_FindFirstStreamW == NULL) - goto unnamed_only; - hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0); - if (hFind == INVALID_HANDLE_VALUE) { - err = GetLastError(); - if (err == ERROR_CALL_NOT_IMPLEMENTED || - err == ERROR_NOT_SUPPORTED || - err == ERROR_INVALID_FUNCTION || - err == ERROR_INVALID_PARAMETER) - goto unnamed_only; - - /* Seems legal for this to return ERROR_HANDLE_EOF on reparse - * points and directories */ - if ((inode->i_attributes & - (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) - && err == ERROR_HANDLE_EOF) - { - DEBUG("ERROR_HANDLE_EOF (ok)"); - return 0; - } else { - if (err == ERROR_ACCESS_DENIED) { - WARNING("Failed to look up data streams " - "of \"%ls\": Access denied!\n%ls", - path, capture_access_denied_msg); - return 0; - } else { - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Failed to look up data streams " - "of \"%ls\"", path); - return WIMLIB_ERR_READ; - } - } - } - do { - ret = win32_capture_stream(path, - path_num_chars, - inode, unhashed_streams, - &dat); - if (ret) - goto out_find_close; - } while (win32func_FindNextStreamW(hFind, &dat)); - err = GetLastError(); - if (err != ERROR_HANDLE_EOF) { - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Error reading data streams from " - "\"%ls\"", path); - ret = WIMLIB_ERR_READ; + info = (const FILE_STREAM_INFORMATION *) + ((const u8 *)info + info->NextEntryOffset); } -out_find_close: - FindClose(hFind); - return ret; + ret = 0; + goto out_free_buf; unnamed_only: - /* FindFirstStream() API is not available, or the volume does not - * support named streams. Only capture the unnamed data stream. */ + /* The volume does not support named streams. Only capture the unnamed + * data stream. */ DEBUG("Only capturing unnamed data stream"); if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) - return 0; + { + ret = 0; + goto out_free_buf; + } - wcscpy(dat.cStreamName, L"::$DATA"); - dat.StreamSize.QuadPart = file_size; - return win32_capture_stream(path, path_num_chars, - inode, unhashed_streams, &dat); + { + WIN32_FIND_STREAM_DATA dat; + + wcscpy(dat.cStreamName, L"::$DATA"); + dat.StreamSize.QuadPart = file_size; + ret = win32_capture_stream(path, path_num_chars, + inode, unhashed_streams, &dat); + } +out_free_buf: + /* Free buffer if allocated on heap. */ + if (buf != _buf) + FREE(buf); + return ret; } static int @@ -1227,7 +1021,7 @@ again: if (ret) goto out; - ret = win32_get_short_name(hFile, path, root); + ret = win32_get_short_name(hFile, root); if (ret) goto out; @@ -1361,16 +1155,6 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, unsigned vol_flags; DWORD dret; - if (!win32func_FindFirstStreamW -#ifdef WITH_NTDLL - && !func_NtQueryInformationFile -#endif - ) - { - WARNING("Running on Windows XP or earlier; " - "alternate data streams will not be captured."); - } - path_nchars = wcslen(root_disk_path); if (path_nchars > WINDOWS_NT_MAX_PATH) return WIMLIB_ERR_INVALID_PARAM; @@ -1427,7 +1211,6 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, ret = win32_build_dentry_tree_recursive(root_ret, path, path_nchars, params, &state, vol_flags); -out_free_path: FREE(path); if (ret == 0) win32_do_capture_warnings(root_disk_path, &state, params->add_flags); diff --git a/src/win32_common.c b/src/win32_common.c index 67d208d2..d6f68883 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. * @@ -29,9 +29,7 @@ #include -#ifdef WITH_NTDLL -# include -#endif +#include #include "wimlib/win32_common.h" #include "wimlib/assert.h" @@ -320,13 +318,11 @@ set_errno_from_GetLastError(void) set_errno_from_win32_error(GetLastError()); } -#ifdef WITH_NTDLL void set_errno_from_nt_status(NTSTATUS status) { set_errno_from_win32_error((*func_RtlNtStatusToDosError)(status)); } -#endif /* 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 @@ -512,26 +508,14 @@ win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) 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 */ -/* Vista and later */ +/* kernel32.dll: Vista and later */ BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName, const wchar_t *lpTargetFileName, - DWORD dwFlags) = NULL; + DWORD dwFlags); -#ifdef WITH_NTDLL +/* ntdll.dll */ DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status); @@ -566,17 +550,13 @@ NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle, NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder) (PCUNICODE_STRING VolumeRootPath); -#endif /* WITH_NTDLL */ - static OSVERSIONINFO windows_version_info = { .dwOSVersionInfoSize = sizeof(OSVERSIONINFO), }; static HMODULE hKernel32 = NULL; -#ifdef WITH_NTDLL static HMODULE hNtdll = NULL; -#endif static bool acquired_privileges = false; @@ -588,6 +568,20 @@ windows_version_is_at_least(unsigned major, unsigned minor) windows_version_info.dwMinorVersion >= minor); } +#define NTDLL_SYM(name) { (void **)&func_##name, #name } +static const struct ntdll_sym { + void **func_ptr; + const char *name; +} ntdll_syms[] = { + NTDLL_SYM(RtlNtStatusToDosError), + NTDLL_SYM(NtQueryInformationFile), + NTDLL_SYM(NtQuerySecurityObject), + NTDLL_SYM(NtQueryDirectoryFile), + NTDLL_SYM(NtSetSecurityObject), + {NULL, NULL}, +}; +#undef NTDLL_SYM + /* One-time initialization for Windows capture/apply code. */ int win32_global_init(int init_flags) @@ -611,55 +605,29 @@ win32_global_init(int init_flags) 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; - } - win32func_CreateSymbolicLinkW = (void*)GetProcAddress(hKernel32, - "CreateSymbolicLinkW"); + win32func_CreateSymbolicLinkW = + (void *)GetProcAddress(hKernel32, "CreateSymbolicLinkW"); } -#ifdef WITH_NTDLL if (hNtdll == NULL) hNtdll = LoadLibrary(L"ntdll.dll"); - if (hNtdll) { - func_RtlNtStatusToDosError = - (void*)GetProcAddress(hNtdll, "RtlNtStatusToDosError"); - if (func_RtlNtStatusToDosError) { - - func_NtQuerySecurityObject = - (void*)GetProcAddress(hNtdll, "NtQuerySecurityObject"); - - func_NtQueryDirectoryFile = - (void*)GetProcAddress(hNtdll, "NtQueryDirectoryFile"); - - func_NtQueryInformationFile = - (void*)GetProcAddress(hNtdll, "NtQueryInformationFile"); + if (!hNtdll) { + ERROR("ntdll.dll could not be loaded!"); + return WIMLIB_ERR_UNSUPPORTED; + } - func_NtSetSecurityObject = - (void*)GetProcAddress(hNtdll, "NtSetSecurityObject"); - func_RtlCreateSystemVolumeInformationFolder = - (void*)GetProcAddress(hNtdll, "RtlCreateSystemVolumeInformationFolder"); + for (const struct ntdll_sym *sym = ntdll_syms; sym->name; sym++) { + void *addr = (void *)GetProcAddress(hNtdll, sym->name); + if (!addr) { + ERROR("Can't find %s() in ntdll.dll", sym->name); + return WIMLIB_ERR_UNSUPPORTED; } + *(sym->func_ptr) = addr; } - DEBUG("FindFirstStreamW @ %p", win32func_FindFirstStreamW); - DEBUG("FindNextStreamW @ %p", win32func_FindNextStreamW); - DEBUG("CreateSymbolicLinkW @ %p", win32func_CreateSymbolicLinkW); - DEBUG("RtlNtStatusToDosError @ %p", func_RtlNtStatusToDosError); - DEBUG("NtQuerySecurityObject @ %p", func_NtQuerySecurityObject); - DEBUG("NtQueryDirectoryFile @ %p", func_NtQueryDirectoryFile); - DEBUG("NtQueryInformationFile @ %p", func_NtQueryInformationFile); - DEBUG("NtSetSecurityObject @ %p", func_NtSetSecurityObject); - DEBUG("RtlCreateSystemVolumeInformationFolder @ %p", - func_RtlCreateSystemVolumeInformationFolder); -#endif - + func_RtlCreateSystemVolumeInformationFolder = + (void *)GetProcAddress(hNtdll, "RtlCreateSystemVolumeInformationFolder"); return 0; insufficient_privileges: @@ -674,14 +642,16 @@ win32_global_cleanup(void) win32_release_capture_and_apply_privileges(); if (hKernel32 != NULL) { FreeLibrary(hKernel32); + win32func_CreateSymbolicLinkW = NULL; hKernel32 = NULL; } -#ifdef WITH_NTDLL if (hNtdll != NULL) { FreeLibrary(hNtdll); + for (const struct ntdll_sym *sym = ntdll_syms; sym->name; sym++) + *(sym->func_ptr) = NULL; + func_RtlCreateSystemVolumeInformationFolder = NULL; hNtdll = NULL; } -#endif } #endif /* __WIN32__ */