]> wimlib.net Git - wimlib/blobdiff - src/extract.c
extract.c: recommend --unix-data mode when appropriate
[wimlib] / src / extract.c
index d3df861fb6ba5cef0a27c3998f8c0e51088d0b46..f59cffb55347acc014da0feb0aad58f42d2150b8 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright (C) 2012-2017 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -63,6 +63,7 @@
 #include "wimlib/unix_data.h"
 #include "wimlib/wim.h"
 #include "wimlib/win32.h" /* for realpath() equivalent */
+#include "wimlib/xattr.h"
 #include "wimlib/xml.h"
 
 #define WIMLIB_EXTRACT_FLAG_FROM_PIPE   0x80000000
@@ -141,6 +142,70 @@ end_file_metadata_phase(struct apply_ctx *ctx)
        return end_file_phase(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA);
 }
 
+/* Are all bytes in the specified buffer zero? */
+static bool
+is_all_zeroes(const u8 *p, const size_t size)
+{
+       const u8 * const end = p + size;
+
+       for (; (uintptr_t)p % WORDBYTES && p != end; p++)
+               if (*p)
+                       return false;
+
+       for (; end - p >= WORDBYTES; p += WORDBYTES)
+               if (*(const machine_word_t *)p)
+                       return false;
+
+       for (; p != end; p++)
+               if (*p)
+                       return false;
+
+       return true;
+}
+
+/*
+ * Sparse regions should be detected at the granularity of the filesystem block
+ * size.  For now just assume 4096 bytes, which is the default block size on
+ * NTFS and most Linux filesystems.
+ */
+#define SPARSE_UNIT 4096
+
+/*
+ * Detect whether the specified buffer begins with a region of all zero bytes.
+ * Return %true if a zero region was found or %false if a nonzero region was
+ * found, and sets *len_ret to the length of the region.  This operates at a
+ * granularity of SPARSE_UNIT bytes, meaning that to extend a zero region, there
+ * must be SPARSE_UNIT zero bytes with no interruption, but to extend a nonzero
+ * region, just one nonzero byte in the next SPARSE_UNIT bytes is sufficient.
+ *
+ * Note: besides compression, the WIM format doesn't yet have a way to
+ * efficiently represent zero regions, so that's why we need to detect them
+ * ourselves.  Things will still fall apart badly on extremely large sparse
+ * files, but this is a start...
+ */
+bool
+detect_sparse_region(const void *data, size_t size, size_t *len_ret)
+{
+       const void *p = data;
+       const void * const end = data + size;
+       size_t len = 0;
+       bool zeroes = false;
+
+       while (p != end) {
+               size_t n = min(end - p, SPARSE_UNIT);
+               bool z = is_all_zeroes(p, n);
+
+               if (len != 0 && z != zeroes)
+                       break;
+               zeroes = z;
+               len += n;
+               p += n;
+       }
+
+       *len_ret = len;
+       return zeroes;
+}
+
 #define PWM_FOUND_WIM_HDR (-1)
 
 /* Read the header for a blob in a pipable WIM.  If @pwm_hdr_ret is not NULL,
@@ -1160,6 +1225,8 @@ inode_tally_features(const struct wim_inode *inode,
                features->unix_data++;
        if (inode_has_object_id(inode))
                features->object_ids++;
+       if (inode_has_linux_xattrs(inode))
+               features->linux_xattrs++;
 }
 
 /* Tally features necessary to extract a dentry and the corresponding inode.  */
@@ -1297,19 +1364,31 @@ do_feature_check(const struct wim_features *required_features,
                WARNING("Ignoring Windows NT security descriptors of %lu files",
                        required_features->security_descriptors);
 
-       /* UNIX data.  */
-       if ((extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
-           required_features->unix_data && !supported_features->unix_data)
+       /* Standard UNIX metadata */
+       if (required_features->unix_data &&
+           (!supported_features->unix_data ||
+            !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)))
        {
-               ERROR("Extraction backend does not support UNIX data!");
-               return WIMLIB_ERR_UNSUPPORTED;
+               if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
+                       ERROR("Requested UNIX metadata extraction, but "
+                             "extraction backend does not support it!");
+                       return WIMLIB_ERR_UNSUPPORTED;
+               }
+               WARNING("Ignoring UNIX metadata (uid/gid/mode/rdev) of %lu files%"TS,
+                       required_features->unix_data,
+                       (supported_features->unix_data ?
+                        T("\n          (use --unix-data mode to extract these)") : T("")));
        }
 
-       if (required_features->unix_data &&
-           !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))
+       /* Linux-style extended attributes */
+       if (required_features->linux_xattrs &&
+           (!supported_features->linux_xattrs ||
+            !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)))
        {
-               WARNING("Ignoring UNIX metadata of %lu files",
-                       required_features->unix_data);
+               WARNING("Ignoring Linux-style extended attributes of %lu files%"TS,
+                       required_features->linux_xattrs,
+                       (supported_features->linux_xattrs ?
+                        T("\n          (use --unix-data mode to extract these)") : T("")));
        }
 
        /* Object IDs.  */