X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32.c;h=ce94b049106dd4b1a57ed060b9be45fa1bfb9802;hp=ba3dc588dc0dcb6149260aa99ae56503e0d5a75d;hb=51996593b805cc79ba3a67ea4c721baa60e1c70e;hpb=eea32b7418fab652606ca47320b0bc1277908b5c diff --git a/src/win32.c b/src/win32.c index ba3dc588..ce94b049 100644 --- a/src/win32.c +++ b/src/win32.c @@ -44,6 +44,70 @@ #include + + +/* 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 */ +static HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, + STREAM_INFO_LEVELS InfoLevel, + LPVOID lpFindStreamData, + DWORD dwFlags) = NULL; + +/* Vista and later */ +static BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream, + LPVOID lpFindStreamData) = NULL; + +static HMODULE hKernel32 = NULL; + +/* Try to dynamically load some functions */ +void +win32_global_init() +{ + DWORD err; + bool warned; + + + if (hKernel32 == NULL) { + DEBUG("Loading Kernel32.dll"); + hKernel32 = LoadLibraryA("Kernel32.dll"); + if (hKernel32 == NULL) { + err = GetLastError(); + WARNING("Can't load Kernel32.dll"); + win32_error(err); + return; + } + } + + DEBUG("Looking for FindFirstStreamW"); + win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, "FindFirstStreamW"); + if (!win32func_FindFirstStreamW) { + WARNING("Could not find function FindFirstStreamW() in Kernel32.dll!"); + WARNING("Capturing alternate data streams will not be supported."); + return; + } + + DEBUG("Looking for FindNextStreamW"); + win32func_FindNextStreamW = (void*)GetProcAddress(hKernel32, "FindNextStreamW"); + if (!win32func_FindNextStreamW) { + WARNING("Could not find function FindNextStreamW() in Kernel32.dll!"); + WARNING("Capturing alternate data streams will not be supported."); + win32func_FindFirstStreamW = NULL; + } +} + +void +win32_global_cleanup() +{ + if (hKernel32 != NULL) { + DEBUG("Closing Kernel32.dll"); + FreeLibrary(hKernel32); + hKernel32 = NULL; + } +} + static const char *access_denied_msg = " 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" @@ -75,12 +139,9 @@ win32_error_last() } #endif -HANDLE -win32_open_file_readonly(const wchar_t *path, bool data_only) +static HANDLE +win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) { - DWORD dwDesiredAccess = FILE_READ_DATA; - if (!data_only) - dwDesiredAccess |= FILE_READ_ATTRIBUTES | READ_CONTROL | ACCESS_SYSTEM_SECURITY; return CreateFileW(path, dwDesiredAccess, FILE_SHARE_READ, @@ -91,6 +152,12 @@ win32_open_file_readonly(const wchar_t *path, bool data_only) NULL /* hTemplateFile */); } +HANDLE +win32_open_file_data_only(const wchar_t *path) +{ + return win32_open_existing_file(path, FILE_READ_DATA); +} + int win32_read_file(const mbchar *filename, void *handle, u64 offset, size_t size, void *buf) @@ -346,7 +413,7 @@ win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE]) DWORD bytesRead; int ret; - hFile = win32_open_file_readonly(path, false); + hFile = win32_open_file_data_only(path); if (hFile == INVALID_HANDLE_VALUE) return WIMLIB_ERR_OPEN; @@ -521,23 +588,33 @@ out_invalid_stream_name: * * @lookup_table: Stream lookup table for the WIM. * + * @file_size: Size of unnamed data stream. (Used only if alternate + * data streams API appears to be unavailable.) + * * Returns 0 on success; nonzero on failure. */ static int win32_capture_streams(const wchar_t *path_utf16, size_t path_utf16_nchars, struct wim_inode *inode, - struct wim_lookup_table *lookup_table) + struct wim_lookup_table *lookup_table, + u64 file_size) { WIN32_FIND_STREAM_DATA dat; int ret; HANDLE hFind; DWORD err; - hFind = FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0); + if (win32func_FindFirstStreamW == NULL) + goto unnamed_only; + + hFind = win32func_FindFirstStreamW(path_utf16, FindStreamInfoStandard, &dat, 0); if (hFind == INVALID_HANDLE_VALUE) { err = GetLastError(); + if (err == ERROR_CALL_NOT_IMPLEMENTED) + goto unnamed_only; + /* Seems legal for this to return ERROR_HANDLE_EOF on reparse * points and directories */ if ((inode->i_attributes & @@ -566,7 +643,7 @@ win32_capture_streams(const wchar_t *path_utf16, &dat); if (ret) goto out_find_close; - } while (FindNextStreamW(hFind, &dat)); + } while (win32func_FindNextStreamW(hFind, &dat)); err = GetLastError(); if (err != ERROR_HANDLE_EOF) { ERROR("Win32 API: Error reading data streams from \"%ls\"", path_utf16); @@ -576,6 +653,20 @@ win32_capture_streams(const wchar_t *path_utf16, out_find_close: FindClose(hFind); return ret; +unnamed_only: + if (inode->i_attributes & + (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) + { + ret = 0; + } else { + wcscpy(dat.cStreamName, L"::$DATA"); + dat.StreamSize.QuadPart = file_size; + ret = win32_capture_stream(path_utf16, + path_utf16_nchars, + inode, lookup_table, + &dat); + } + return ret; } /* Win32 version of capturing a directory tree */ @@ -598,6 +689,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, size_t path_utf16_nchars; struct sd_set *sd_set; DWORD err; + u64 file_size; if (exclude_path(root_disk_path, config, true)) { if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) { @@ -639,7 +731,8 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, goto out_destroy_sd_set; path_utf16_nchars = path_utf16_nbytes / sizeof(wchar_t); - HANDLE hFile = win32_open_file_readonly(path_utf16, false); + HANDLE hFile = win32_open_existing_file(path_utf16, + FILE_READ_DATA | FILE_READ_ATTRIBUTES); if (hFile == INVALID_HANDLE_VALUE) { err = GetLastError(); ERROR("Win32 API: Failed to open \"%s\"", root_disk_path); @@ -678,12 +771,18 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, /* Get DOS name and security descriptor (if any). */ ret = win32_get_short_name(root, path_utf16); - if (ret) - goto out_close_handle; - ret = win32_get_security_descriptor(root, sd_set, path_utf16); if (ret) goto out_close_handle; + if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)) { + ret = win32_get_security_descriptor(root, sd_set, path_utf16); + if (ret) + goto out_close_handle; + } + + file_size = ((u64)file_info.nFileSizeHigh << 32) | + (u64)file_info.nFileSizeLow; + if (inode_is_directory(inode)) { /* Directory (not a reparse point) --- recurse to children */ @@ -692,7 +791,8 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, ret = win32_capture_streams(path_utf16, path_utf16_nchars, inode, - lookup_table); + lookup_table, + file_size); if (ret) goto out_close_handle; ret = win32_recurse_directory(root, @@ -717,7 +817,8 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, ret = win32_capture_streams(path_utf16, path_utf16_nchars, inode, - lookup_table); + lookup_table, + file_size); } out_close_handle: CloseHandle(hFile); @@ -842,7 +943,8 @@ static int win32_extract_stream(const struct wim_inode *inode, const wchar_t *path, const wchar_t *stream_name_utf16, - struct wim_lookup_table_entry *lte) + struct wim_lookup_table_entry *lte, + const struct wim_security_data *security_data) { wchar_t *stream_path; HANDLE h; @@ -850,6 +952,17 @@ win32_extract_stream(const struct wim_inode *inode, DWORD err; DWORD creationDisposition = CREATE_ALWAYS; + SECURITY_ATTRIBUTES *secattr; + + if (security_data && inode->i_security_id != -1) { + secattr = alloca(sizeof(*secattr)); + secattr->nLength = sizeof(*secattr); + secattr->lpSecurityDescriptor = security_data->descriptors[inode->i_security_id]; + secattr->bInheritHandle = FALSE; + } else { + secattr = NULL; + } + if (stream_name_utf16) { /* Named stream. Create a buffer that contains the UTF-16LE * string [.\]@path:@stream_name_utf16. This is needed to @@ -888,7 +1001,7 @@ win32_extract_stream(const struct wim_inode *inode, * the call to CreateFileW() will merely open the directory that * was already created rather than creating a new file. */ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { - if (!CreateDirectoryW(stream_path, NULL)) { + if (!CreateDirectoryW(stream_path, secattr)) { err = GetLastError(); if (err != ERROR_ALREADY_EXISTS) { ERROR("Failed to create directory \"%ls\"", @@ -909,9 +1022,9 @@ win32_extract_stream(const struct wim_inode *inode, DEBUG("Opening \"%ls\"", stream_path); h = CreateFileW(stream_path, - GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, + GENERIC_WRITE, 0, - NULL, + secattr, creationDisposition, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | @@ -972,13 +1085,15 @@ out: */ static int win32_extract_streams(const struct wim_inode *inode, - const wchar_t *path, u64 *completed_bytes_p) + const wchar_t *path, u64 *completed_bytes_p, + const struct wim_security_data *security_data) { struct wim_lookup_table_entry *unnamed_lte; int ret; unnamed_lte = inode_unnamed_lte_resolved(inode); - ret = win32_extract_stream(inode, path, NULL, unnamed_lte); + ret = win32_extract_stream(inode, path, NULL, unnamed_lte, + security_data); if (ret) goto out; if (unnamed_lte) @@ -996,7 +1111,8 @@ win32_extract_streams(const struct wim_inode *inode, ret = win32_extract_stream(inode, path, ads_entry->stream_name, - ads_entry->lte); + ads_entry->lte, + NULL); if (ret) break; if (ads_entry->lte) @@ -1007,35 +1123,6 @@ out: return ret; } -/* - * Sets the security descriptor on an extracted file. This is Win32-specific - * code. - * - * @inode: The WIM inode that was extracted and has a security descriptor. - * @path: UTF-16LE external path that the inode was extracted to. - * @sd: Security data for the WIM image. - * - * Returns 0 on success; nonzero on failure. - */ -static int win32_set_security_data(const struct wim_inode *inode, - const wchar_t *path, - const struct wim_security_data *sd) -{ - SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION | - SACL_SECURITY_INFORMATION | - OWNER_SECURITY_INFORMATION | - GROUP_SECURITY_INFORMATION; - if (!SetFileSecurityW(path, securityInformation, - (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id])) - { - DWORD err = GetLastError(); - ERROR("Can't set security descriptor on \"%ls\"", path); - win32_error(err); - return WIMLIB_ERR_WRITE; - } - return 0; -} - /* Extract a file, directory, reparse point, or hard link to an * already-extracted file using the Win32 API */ int win32_do_apply_dentry(const mbchar *output_path, @@ -1070,21 +1157,18 @@ int win32_do_apply_dentry(const mbchar *output_path, } else { /* Create the file, directory, or reparse point, and extract the * data streams. */ + const struct wim_security_data *security_data; + if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NOACLS) + security_data = NULL; + else + security_data = wim_const_security_data(args->w); + ret = win32_extract_streams(inode, utf16le_path, - &args->progress.extract.completed_bytes); + &args->progress.extract.completed_bytes, + security_data); if (ret) goto out_free_utf16_path; - /* Set security descriptor if present */ - if (inode->i_security_id != -1) { - DEBUG("Setting security descriptor %d on %s", - inode->i_security_id, output_path); - ret = win32_set_security_data(inode, - utf16le_path, - wim_const_security_data(args->w)); - if (ret) - goto out_free_utf16_path; - } if (inode->i_nlink > 1) { /* Save extracted path for a later call to * CreateHardLinkW() if this inode has multiple links. @@ -1120,13 +1204,7 @@ win32_do_apply_dentry_timestamps(const mbchar *output_path, return ret; DEBUG("Opening \"%s\" to set timestamps", output_path); - h = CreateFileW(utf16le_path, - GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - NULL); + h = win32_open_existing_file(utf16le_path, FILE_WRITE_ATTRIBUTES); if (h == INVALID_HANDLE_VALUE) err = GetLastError();