]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
Don't set FILE_ATTRIBUTE_SPARSE on Windows
[wimlib] / src / win32_apply.c
index b357dd8505159dd7650a76ba885a62f32acf4123..3f4a4562567ba223183636f2836fde07fb0bf660 100644 (file)
 #include "wimlib/win32_common.h"
 
 #include "wimlib/apply.h"
-#include "wimlib/capture.h" /* for mangle_pat()  */
+#include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list()  */
 #include "wimlib/dentry.h"
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
 #include "wimlib/resource.h"
 #include "wimlib/textfile.h"
 #include "wimlib/xml.h"
-#include "wimlib/wildcard.h"
 #include "wimlib/wim.h"
 #include "wimlib/wimboot.h"
 
@@ -130,7 +129,6 @@ in_prepopulate_list(struct wim_dentry *dentry, struct apply_ctx *ctx)
 {
        struct string_set *pats;
        const tchar *path;
-       size_t path_nchars;
 
        pats = get_private_data(ctx)->prepopulate_pats;
        if (!pats || !pats->num_strings)
@@ -140,14 +138,7 @@ in_prepopulate_list(struct wim_dentry *dentry, struct apply_ctx *ctx)
        if (!path)
                return false;
 
-       path_nchars = tstrlen(path);
-
-       for (size_t i = 0; i < pats->num_strings; i++)
-               if (match_path(path, path_nchars, pats->strings[i],
-                              OS_PREFERRED_PATH_SEPARATOR, true))
-                       return true;
-
-       return false;
+       return match_pattern_list(path, tstrlen(path), pats);
 }
 
 static int
@@ -156,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)
 {
@@ -182,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;
@@ -560,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,