X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fwin32.c;h=f2e7eeb08988464c43db05f4eb3f9850a381d150;hb=fd763f8533b72067aa38a08849e57f23aa300060;hp=638380908b62091fe7b91dbb164adbecb152271d;hpb=c6a1140e085f633273fcf47a6462bd9382ce118a;p=wimlib diff --git a/src/win32.c b/src/win32.c index 63838090..f2e7eeb0 100644 --- a/src/win32.c +++ b/src/win32.c @@ -53,6 +53,28 @@ struct win32_capture_state { #define MAX_SET_SD_ACCESS_DENIED_WARNINGS 1 #define MAX_SET_SACL_PRIV_NOTHELD_WARNINGS 1 +#ifdef ENABLE_ERROR_MESSAGES +static void +win32_error(u32 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); + } +} +#else /* ENABLE_ERROR_MESSAGES */ +# define win32_error(err_code) +#endif /* !ENABLE_ERROR_MESSAGES */ + /* 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. */ @@ -128,32 +150,6 @@ L"If you are not running this program as the administrator, you may\n" " `wimlib-imagex apply' with the --no-acls flag instead.\n" ; -#ifdef ENABLE_ERROR_MESSAGES -void -win32_error(u32 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); - } -} - -void -win32_error_last() -{ - win32_error(GetLastError()); -} -#endif - static HANDLE win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) { @@ -174,29 +170,58 @@ win32_open_file_data_only(const wchar_t *path) } int -win32_read_file(const wchar_t *filename, - void *handle, u64 offset, size_t size, void *buf) +read_win32_file_prefix(const struct lookup_table_entry *lte, + u64 size, + consume_data_callback_t cb, + void *ctx_or_buf, + int _ignored_flags) { - HANDLE h = handle; + int ret; + void *out_buf; DWORD err; - DWORD bytesRead; - LARGE_INTEGER liOffset = {.QuadPart = offset}; + u64 bytes_remaining; - wimlib_assert(size <= 0xffffffff); + HANDLE hFile = win32_open_file_data_only(lte->file_on_disk); + if (hFile == INVALID_HANDLE_VALUE) { + err = GetLastError(); + ERROR("Failed to open \"%ls\"", lte->file_on_disk); + win32_error(err); + ret = WIMLIB_ERR_OPEN; + goto out; + } - if (SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN)) - if (ReadFile(h, buf, size, &bytesRead, NULL) && bytesRead == size) - return 0; - err = GetLastError(); - ERROR("Error reading \"%ls\"", filename); - win32_error(err); - return WIMLIB_ERR_READ; -} + if (cb) + out_buf = alloca(WIM_CHUNK_SIZE); + else + out_buf = ctx_or_buf; -void -win32_close_file(void *handle) -{ - CloseHandle((HANDLE)handle); + bytes_remaining = size; + while (bytes_remaining) { + DWORD bytesToRead, bytesRead; + + bytesToRead = min(WIM_CHUNK_SIZE, bytes_remaining); + if (!ReadFile(h, out_buf, bytesToRead, &bytesRead, NULL) || + bytesRead != bytesToRead) + { + err = GetLastError(); + ERROR("Failed to read data from \"%ls\"", lte->file_on_disk); + win32_error(err); + ret = WIMLIB_ERR_READ; + goto out_close_handle; + } + bytes_remaining -= bytesRead; + if (cb) { + ret = cb(out_buf, bytesRead, ctx_or_buf); + if (ret) + goto out_close_handle; + } else { + out_buf += bytesRead; + } + } +out_close_handle: + CloseHandle(hFile); +out: + return ret; } static u64 @@ -451,47 +476,6 @@ win32_capture_reparse_point(HANDLE hFile, bytesReturned - 8, lookup_table); } -/* Calculate the SHA1 message digest of a Win32 data stream, which may be either - * an unnamed or named data stream. - * - * @path: Path to the file, with the stream noted at the end for named - * streams. UTF-16LE encoding. - * - * @hash: On success, the SHA1 message digest of the stream is written to - * this location. - * - * Returns 0 on success; nonzero on failure. - */ -static int -win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE]) -{ - HANDLE hFile; - SHA_CTX ctx; - u8 buf[32768]; - DWORD bytesRead; - int ret; - - hFile = win32_open_file_data_only(path); - if (hFile == INVALID_HANDLE_VALUE) - return WIMLIB_ERR_OPEN; - - sha1_init(&ctx); - for (;;) { - if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) { - ret = WIMLIB_ERR_READ; - goto out_close_handle; - } - if (bytesRead == 0) - break; - sha1_update(&ctx, buf, bytesRead); - } - ret = 0; - sha1_final(hash, &ctx); -out_close_handle: - CloseHandle(hFile); - return ret; -} - /* Scans an unnamed or named stream of a Win32 file (not a reparse point * stream); calculates its SHA1 message digest and either creates a `struct * wim_lookup_table_entry' in memory for it, or uses an existing 'struct @@ -591,39 +575,27 @@ win32_capture_stream(const wchar_t *path, swprintf(spath, L"%ls%ls%ls%ls", relpath_prefix, path, colonchar, stream_name); - ret = win32_sha1sum(spath, hash); - if (ret) { - err = GetLastError(); - ERROR("Failed to read \"%ls\" to calculate SHA1sum", spath); - win32_error(err); + /* Make a new wim_lookup_table_entry */ + lte = new_lookup_table_entry(); + if (!lte) { + ret = WIMLIB_ERR_NOMEM; goto out_free_spath; } + lte->file_on_disk = spath; + spath = NULL; + lte->resource_location = RESOURCE_WIN32; + lte->resource_entry.original_size = (u64)dat->StreamSize.QuadPart; - lte = __lookup_resource(lookup_table, hash); - if (lte) { - /* Use existing wim_lookup_table_entry that has the same SHA1 - * message digest */ - lte->refcnt++; - } else { - /* Make a new wim_lookup_table_entry */ - lte = new_lookup_table_entry(); - if (!lte) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_spath; - } - lte->file_on_disk = spath; - lte->win32_file_on_disk_fp = INVALID_HANDLE_VALUE; - spath = NULL; - lte->resource_location = RESOURCE_WIN32; - lte->resource_entry.original_size = (uint64_t)dat->StreamSize.QuadPart; - lte->resource_entry.size = (uint64_t)dat->StreamSize.QuadPart; - copy_hash(lte->hash, hash); - lookup_table_insert(lookup_table, lte); - } - if (is_named_stream) + u32 stream_id; + if (is_named_stream) { + stream_id = ads_entry->stream_id; ads_entry->lte = lte; - else + } else { + stream_id = 0; inode->i_lte = lte; + } + + lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id); out_free_spath: FREE(spath); out: @@ -1029,6 +1001,41 @@ win32_set_reparse_data(HANDLE h, return 0; } +static int +win32_set_compressed(HANDLE hFile, const wchar_t *path) +{ + USHORT format = COMPRESSION_FORMAT_DEFAULT; + DWORD bytesReturned = 0; + if (!DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, + &format, sizeof(USHORT), + NULL, 0, + &bytesReturned, NULL)) + { + /* Warning only */ + DWORD err = GetLastError(); + WARNING("Failed to set compression flag on \"%ls\"", path); + win32_error(err); + } + return 0; +} + +static int +win32_set_sparse(HANDLE hFile, const wchar_t *path) +{ + DWORD bytesReturned = 0; + if (!DeviceIoControl(hFile, FSCTL_SET_SPARSE, + NULL, 0, + NULL, 0, + &bytesReturned, NULL)) + { + /* Warning only */ + DWORD err = GetLastError(); + WARNING("Failed to set sparse flag on \"%ls\"", path); + win32_error(err); + } + return 0; +} + /* * Sets the security descriptor on an extracted file. */ @@ -1145,6 +1152,91 @@ path_is_root_of_drive(const wchar_t *path) return (*path == L'\0'); } +static DWORD +win32_get_create_flags_and_attributes(DWORD i_attributes) +{ + DWORD attributes; + + /* + * Some attributes cannot be set by passing them to CreateFile(). In + * particular: + * + * FILE_ATTRIBUTE_DIRECTORY: + * CreateDirectory() must be called instead of CreateFile(). + * + * FILE_ATTRIBUTE_SPARSE_FILE: + * Needs an ioctl. + * See: win32_set_sparse(). + * + * FILE_ATTRIBUTE_COMPRESSED: + * Not clear from the documentation, but apparently this needs an + * ioctl as well. + * See: win32_set_compressed(). + * + * FILE_ATTRIBUTE_REPARSE_POINT: + * Needs an ioctl, with the reparse data specified. + * See: win32_set_reparse_data(). + * + * In addition, clear any file flags in the attributes that we don't + * want, but also specify FILE_FLAG_OPEN_REPARSE_POINT and + * FILE_FLAG_BACKUP_SEMANTICS as we are a backup application. + */ + attributes = i_attributes & ~(FILE_ATTRIBUTE_SPARSE_FILE | + FILE_ATTRIBUTE_COMPRESSED | + FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_DIRECTORY | + FILE_FLAG_DELETE_ON_CLOSE | + FILE_FLAG_NO_BUFFERING | + FILE_FLAG_OPEN_NO_RECALL | + FILE_FLAG_OVERLAPPED | + FILE_FLAG_RANDOM_ACCESS | + /*FILE_FLAG_SESSION_AWARE |*/ + FILE_FLAG_SEQUENTIAL_SCAN | + FILE_FLAG_WRITE_THROUGH); + return attributes | + FILE_FLAG_OPEN_REPARSE_POINT | + FILE_FLAG_BACKUP_SEMANTICS; +} + +static bool +inode_has_special_attributes(const struct wim_inode *inode) +{ + return (inode->i_attributes & (FILE_ATTRIBUTE_COMPRESSED | + FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_SPARSE_FILE)) != 0; +} + +static int +win32_set_special_attributes(HANDLE hFile, const struct wim_inode *inode, + struct wim_lookup_table_entry *unnamed_stream_lte, + const wchar_t *path) +{ + int ret; + + if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + DEBUG("Setting reparse data on \"%ls\"", path); + ret = win32_set_reparse_data(hFile, inode->i_reparse_tag, + unnamed_stream_lte, path); + if (ret) + return ret; + } + + if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) { + DEBUG("Setting compression flag on \"%ls\"", path); + ret = win32_set_compressed(hFile, path); + if (ret) + return ret; + } + + if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) { + DEBUG("Setting sparse flag on \"%ls\"", path); + ret = win32_set_sparse(hFile, path); + if (ret) + return ret; + } + return 0; +} + static int win32_extract_stream(const struct wim_inode *inode, const wchar_t *path, @@ -1213,7 +1305,7 @@ win32_extract_stream(const struct wim_inode *inode, } } DEBUG("Created directory \"%ls\"", stream_path); - if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + if (!inode_has_special_attributes(inode)) { ret = 0; goto out; } @@ -1223,13 +1315,11 @@ win32_extract_stream(const struct wim_inode *inode, DEBUG("Opening \"%ls\"", stream_path); h = CreateFileW(stream_path, - GENERIC_WRITE, + GENERIC_READ | GENERIC_WRITE, 0, NULL, creationDisposition, - FILE_FLAG_OPEN_REPARSE_POINT | - FILE_FLAG_BACKUP_SEMANTICS | - inode->i_attributes, + win32_get_create_flags_and_attributes(inode->i_attributes), NULL); if (h == INVALID_HANDLE_VALUE) { err = GetLastError(); @@ -1239,14 +1329,13 @@ win32_extract_stream(const struct wim_inode *inode, goto fail; } - if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT && - stream_name_utf16 == NULL) - { - DEBUG("Setting reparse data on \"%ls\"", path); - ret = win32_set_reparse_data(h, inode->i_reparse_tag, lte, path); + if (stream_name_utf16 == NULL && inode_has_special_attributes(inode)) { + ret = win32_set_special_attributes(h, inode, lte, path); if (ret) goto fail_close_handle; - } else { + } + + if (!(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { if (lte) { DEBUG("Extracting \"%ls\" (len = %"PRIu64")", stream_path, wim_resource_size(lte));