X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fwin32.c;h=f2e7eeb08988464c43db05f4eb3f9850a381d150;hb=fd763f8533b72067aa38a08849e57f23aa300060;hp=3a914943ef70d4be1e2e8e7a163e2642aca7b447;hpb=f2db311f527e6037c836cf1b14debb841e09b440;p=wimlib diff --git a/src/win32.c b/src/win32.c index 3a914943..f2e7eeb0 100644 --- a/src/win32.c +++ b/src/win32.c @@ -23,9 +23,7 @@ * along with wimlib; if not, see http://www.gnu.org/licenses/. */ -#ifndef __WIN32__ -# error "This file contains Windows code" -#endif +#ifdef __WIN32__ #include "config.h" #include @@ -41,6 +39,7 @@ #include "lookup_table.h" #include "security.h" #include "endianness.h" +#include #include @@ -54,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. */ @@ -129,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) { @@ -175,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 @@ -220,6 +244,10 @@ win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path) memcpy(dentry->short_name, dat.cAlternateFileName, n); dentry->short_name_nbytes = short_name_nbytes; } + /* If we can't read the short filename for some reason, we just ignore + * the error and assume the file has no short name. I don't think this + * should be an issue, since the short names are essentially obsolete + * anyway. */ return 0; } @@ -307,8 +335,9 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, wchar_t *path, size_t path_num_chars, struct wim_lookup_table *lookup_table, + struct wim_inode_table *inode_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, struct win32_capture_state *state); @@ -320,8 +349,9 @@ win32_recurse_directory(struct wim_dentry *root, wchar_t *dir_path, size_t dir_path_num_chars, struct wim_lookup_table *lookup_table, + struct wim_inode_table *inode_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, struct win32_capture_state *state) @@ -372,6 +402,7 @@ win32_recurse_directory(struct wim_dentry *root, dir_path, path_len, lookup_table, + inode_table, sd_set, config, add_image_flags, @@ -445,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 @@ -585,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: @@ -675,6 +653,7 @@ win32_capture_streams(const wchar_t *path, return 0; } else { if (err == ERROR_ACCESS_DENIED) { + /* XXX This maybe should be an error. */ WARNING("Failed to look up data streams " "of \"%ls\": Access denied!\n%ls", path, capture_access_denied_msg); @@ -705,11 +684,16 @@ out_find_close: FindClose(hFind); return ret; unnamed_only: + /* FindFirstStreamW() API is not available. Only capture the unnamed + * data stream. */ if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) { ret = 0; } else { + /* Just create our own WIN32_FIND_STREAM_DATA for an unnamed + * stream to reduce the code to a call to the + * already-implemented win32_capture_stream() */ wcscpy(dat.cStreamName, L"::$DATA"); dat.StreamSize.QuadPart = file_size; ret = win32_capture_stream(path, @@ -725,8 +709,9 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, wchar_t *path, size_t path_num_chars, struct wim_lookup_table *lookup_table, + struct wim_inode_table *inode_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, struct win32_capture_state *state) @@ -737,13 +722,13 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, u64 file_size; int ret = 0; - if (exclude_path(path, config, true)) { + if (exclude_path(path, path_num_chars, config, true)) { if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) { ERROR("Cannot exclude the root directory from capture"); ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; goto out; } - if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) + if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE) && progress_func) { union wimlib_progress_info info; @@ -783,29 +768,32 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, goto out_close_handle; } - /* Create a WIM dentry */ - ret = new_dentry_with_timeless_inode(path_basename_with_len(path, path_num_chars), - &root); + /* Create a WIM dentry with an associated inode, which may be shared */ + ret = inode_table_new_dentry(inode_table, + path_basename_with_len(path, path_num_chars), + ((u64)file_info.nFileIndexHigh << 32) | + (u64)file_info.nFileIndexLow, + file_info.dwVolumeSerialNumber, + &root); + if (ret) + goto out_close_handle; + + ret = win32_get_short_name(root, path); if (ret) goto out_close_handle; - /* Start preparing the associated WIM inode */ inode = root->d_inode; + if (inode->i_nlink > 1) /* Shared inode; nothing more to do */ + goto out_close_handle; + inode->i_attributes = file_info.dwFileAttributes; inode->i_creation_time = FILETIME_to_u64(&file_info.ftCreationTime); inode->i_last_write_time = FILETIME_to_u64(&file_info.ftLastWriteTime); inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime); - inode->i_ino = ((u64)file_info.nFileIndexHigh << 32) | - (u64)file_info.nFileIndexLow; - inode->i_resolved = 1; - add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); - /* Get DOS name and security descriptor (if any). */ - ret = win32_get_short_name(root, path); - if (ret) - goto out_close_handle; + add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)) { ret = win32_get_security_descriptor(root, sd_set, path, state, @@ -833,13 +821,16 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, path, path_num_chars, lookup_table, + inode_table, sd_set, config, add_image_flags, progress_func, state); } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - /* Reparse point: save the reparse tag and data */ + /* Reparse point: save the reparse tag and data. Alternate data + * streams are not captured, if it's even possible for a reparse + * point to have alternate data streams... */ ret = win32_capture_reparse_point(hFile, inode, lookup_table, @@ -900,8 +891,9 @@ int win32_build_dentry_tree(struct wim_dentry **root_ret, const wchar_t *root_disk_path, struct wim_lookup_table *lookup_table, + struct wim_inode_table *inode_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, void *extra_arg) @@ -930,6 +922,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, path, path_nchars, lookup_table, + inode_table, sd_set, config, add_image_flags, @@ -1008,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. */ @@ -1029,12 +1057,9 @@ win32_set_security_data(const struct wim_inode *inode, again: if (SetFileSecurityW(path, securityInformation, descriptor)) return 0; - err = GetLastError(); - if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) goto fail; - switch (err) { case ERROR_PRIVILEGE_NOT_HELD: if (securityInformation & SACL_SECURITY_INFORMATION) { @@ -1081,7 +1106,6 @@ fail: win32_error(err); return WIMLIB_ERR_WRITE; } - return 0; } @@ -1128,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, @@ -1196,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; } @@ -1206,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(); @@ -1222,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)); @@ -1404,16 +1510,21 @@ out: int fsync(int fd) { - HANDLE h = (HANDLE)_get_osfhandle(fd); + DWORD err; + HANDLE h; + + h = (HANDLE)_get_osfhandle(fd); if (h == INVALID_HANDLE_VALUE) { + err = GetLastError(); ERROR("Could not get Windows handle for file descriptor"); - win32_error(GetLastError()); + win32_error(err); errno = EBADF; return -1; } if (!FlushFileBuffers(h)) { + err = GetLastError(); ERROR("Could not flush file buffers to disk"); - win32_error(GetLastError()); + win32_error(err); errno = EIO; return -1; } @@ -1437,33 +1548,30 @@ realpath(const wchar_t *path, wchar_t *resolved_path) { DWORD ret; wimlib_assert(resolved_path == NULL); + DWORD err; ret = GetFullPathNameW(path, 0, NULL, NULL); - if (!ret) + if (!ret) { + err = GetLastError(); goto fail_win32; + } resolved_path = TMALLOC(ret); if (!resolved_path) - goto fail; + goto out; ret = GetFullPathNameW(path, ret, resolved_path, NULL); if (!ret) { + err = GetLastError(); free(resolved_path); + resolved_path = NULL; goto fail_win32; } - return resolved_path; + goto out; fail_win32: - win32_error(GetLastError()); -fail: - return NULL; -} - -char * -nl_langinfo(nl_item item) -{ - wimlib_assert(item == CODESET); - static char buf[64]; - strcpy(buf, "Unknown"); - return buf; + win32_error(err); + errno = -1; +out: + return resolved_path; } /* rename() on Windows fails if the destination file exists. And we need to @@ -1479,7 +1587,7 @@ win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath) ERROR("MoveFileEx(): Can't rename \"%ls\" to \"%ls\"", oldpath, newpath); win32_error(err); - errno = 0; + errno = -1; return -1; } } @@ -1520,8 +1628,26 @@ fail_close_handle: fail: if (err == NO_ERROR) err = GetLastError(); - ERROR("Can't truncate %ls to %"PRIu64" bytes", path, size); + ERROR("Can't truncate \"%ls\" to %"PRIu64" bytes", path, size); win32_error(err); errno = -1; return -1; } + + +/* This really could be replaced with _wcserror_s, but this doesn't seem to + * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically + * linked in by Visual Studio...?). */ +extern int +win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen) +{ + static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&strerror_lock); + mbstowcs(buf, strerror(errnum), buflen); + buf[buflen - 1] = '\0'; + pthread_mutex_unlock(&strerror_lock); + return 0; +} + +#endif /* __WIN32__ */