X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=3f4a4562567ba223183636f2836fde07fb0bf660;hp=3c09f9a8464bc67671540df750c303d37f7774b5;hb=036c7da59a54e9a24b1afb917b4f9f10eee176ee;hpb=898222314c16df0f6001cfe4fbbe67b678b86455 diff --git a/src/win32_apply.c b/src/win32_apply.c index 3c09f9a8..3f4a4562 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -147,6 +147,125 @@ hash_lookup_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE]) return wim_reshdr_to_hash(&wim->hdr.lookup_table_reshdr, wim, hash); } +/* 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 + * drive is relative (or at least on the current drive, in the case of + * absolute-but-not-really-absolute paths like \Windows\System32) */ +static size_t +win32_path_drive_spec_len(const wchar_t *path) +{ + size_t n = 0; + + if (!wcsncmp(path, L"\\\\?\\", 4)) { + /* \\?\-prefixed path. Check for following drive letter and + * path separator. */ + if (path[4] != L'\0' && path[5] == L':' && + is_any_path_separator(path[6])) + n = 7; + } else { + /* Not a \\?\-prefixed path. Check for an initial drive letter + * and path separator. */ + if (path[0] != L'\0' && path[1] == L':' && + is_any_path_separator(path[2])) + n = 3; + } + /* Include any additional path separators.*/ + if (n > 0) + while (is_any_path_separator(path[n])) + n++; + return n; +} + +static bool +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 (drive_spec_len > 0 && path[drive_spec_len] == L'\0') + return true; + + /* All path separators? */ + for (const wchar_t *p = path; *p != L'\0'; p++) + if (!is_any_path_separator(*p)) + return false; + return true; +} + +/* Given a path, which may not yet exist, get a set of flags that describe the + * features of the volume the path is on. */ +static int +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); + + if (drive_spec_len == 0) + if (path[0] != L'\0' && path[1] == L':') /* Drive-relative path? */ + drive_spec_len = 2; + + if (drive_spec_len == 0) { + /* Path does not start with a drive letter; use the volume of + * the current working directory. */ + volume = NULL; + } else { + /* Path starts with a drive letter (or \\?\ followed by a drive + * letter); use it. */ + volume = alloca((drive_spec_len + 2) * sizeof(wchar_t)); + wmemcpy(volume, path, drive_spec_len); + /* Add trailing backslash in case this was a drive-relative + * path. */ + volume[drive_spec_len] = L'\\'; + volume[drive_spec_len + 1] = L'\0'; + } + 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) { + set_errno_from_GetLastError(); + WARNING_WITH_ERRNO("Failed to get volume information for " + "path \"%ls\"", path); + 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 int win32_start_extract(const wchar_t *path, struct apply_ctx *ctx) { @@ -173,8 +292,10 @@ win32_start_extract(const wchar_t *path, struct apply_ctx *ctx) ctx->supported_features.not_context_indexed_files = 1; +#if 0 if (vol_flags & FILE_SUPPORTS_SPARSE_FILES) ctx->supported_features.sparse_files = 1; +#endif if (vol_flags & FILE_NAMED_STREAMS) ctx->supported_features.named_data_streams = 1; @@ -551,12 +672,27 @@ win32_set_special_file_attributes(const wchar_t *path, u32 attributes) if (h == INVALID_HANDLE_VALUE) goto error; + /* Don't make extracted files sparse. It is pointless without also + * skipping over runs of zeroes when writing the file, and in fact + * increases disk usage --- apparently, allocation sizes in sparse files + * are rounded up to multiples of 131072 bytes rather than 4096 bytes. + * And in some Windows 7 images, *all* files are set as sparse for some + * reason, which causes 1 GB+ of disk space to be wasted on the target + * drive of a full extraction. + * + * WIMGAPI seemingly does not make extracted files sparse either. + * + * XXX: We really ought to do a proper sparse extraction anyway if the + * file meets some heuristic that indicates this would be beneficial. + */ +#if 0 if (attributes & FILE_ATTRIBUTE_SPARSE_FILE) if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytes_returned, NULL)) goto error_close_handle; +#endif if (attributes & FILE_ATTRIBUTE_COMPRESSED) if (!DeviceIoControl(h, FSCTL_SET_COMPRESSION,