]> wimlib.net Git - wimlib/commitdiff
Capture and apply extended attributes on Windows
authorEric Biggers <ebiggers3@gmail.com>
Sun, 21 Jan 2018 21:47:09 +0000 (13:47 -0800)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 21 Jan 2018 21:47:09 +0000 (13:47 -0800)
DISM recently started supporting capturing and applying xattrs on
Windows (though, it is broken when applying multiple xattrs per file).
Make wimlib support the same, using the same on-disk format.  Unlike
DISM it is on by default, not controlled by an option, since there
doesn't seem to be a good reason to make it an option.

Also deprecate the tagged item wimlib was using to store xattrs on Linux
and switch over to the format used by WIMGAPI to store xattrs on
Windows, so that new WIM images use the same xattr format on both
platforms.  One caveat is that on Linux XATTR_SIZE_MAX is 65536 whereas
in the new WIM tagged item format we can only store up to 65535 bytes.
That is unlikely to matter though.

As future work, the NTFS-3G capture and apply backends should be updated
to support xattrs too.

16 files changed:
doc/man1/wimapply.1
doc/man1/wimcapture.1
doc/man1/wimmount.1
include/wimlib/apply.h
include/wimlib/tagged_items.h
include/wimlib/win32_common.h
include/wimlib/xattr.h
src/extract.c
src/test_support.c
src/unix_apply.c
src/unix_capture.c
src/win32_apply.c
src/win32_capture.c
tests/wims/README
tests/wims/linux_xattrs_old.wim [new file with mode: 0644]
tests/wlfuzz.c

index c59ad2562f5c61cf2cd839639177d0fee1ae218b..5c160d0e9964cb6d2d946b98e60c76486551573b 100644 (file)
@@ -88,6 +88,8 @@ Object IDs.
 .PP
 However, encrypted files will not be extracted.
 .PP
 .PP
 However, encrypted files will not be extracted.
 .PP
+Restoring extended attributes (EAs) is also not yet supported in this mode.
+.PP
 Regardless, since almost all information from the WIM image is restored in this
 mode, it is possible (and fully supported) to restore an image of an actual
 Windows installation using \fBwimapply\fR on a UNIX-like system as an
 Regardless, since almost all information from the WIM image is restored in this
 mode, it is possible (and fully supported) to restore an image of an actual
 Windows installation using \fBwimapply\fR on a UNIX-like system as an
@@ -141,6 +143,8 @@ Short filenames (also known as 8.3 names or DOS names).
 Hard links, if supported by the target filesystem.
 .IP \[bu]
 Object IDs, if supported by the target filesystem.
 Hard links, if supported by the target filesystem.
 .IP \[bu]
 Object IDs, if supported by the target filesystem.
+.IP \[bu]
+Extended attributes (EAs), if supported by the target filesystem.
 .PP
 Additional notes about extracting files on Windows:
 .IP \[bu] 4
 .PP
 Additional notes about extracting files on Windows:
 .IP \[bu] 4
index 53bd6cf82c105658d0fc56d1a39636ae4e2cbdb3..0830ffe296263c09602d7e2977954af417a6e56e 100644 (file)
@@ -90,6 +90,8 @@ However, the main limitations of this mode are:
 .IP \[bu] 4
 Encrypted files are excluded.
 .IP \[bu]
 .IP \[bu] 4
 Encrypted files are excluded.
 .IP \[bu]
+Extended attributes (EAs) are not stored yet.
+.IP \[bu]
 Sparse files will be flagged as such, but their full data will still be stored,
 subject to the usual compression.
 .IP \[bu]
 Sparse files will be flagged as such, but their full data will still be stored,
 subject to the usual compression.
 .IP \[bu]
@@ -137,8 +139,8 @@ considered an error condition.
 Hard links, if supported by the source filesystem.
 .IP \[bu]
 Object IDs, if supported by the source filesystem.
 Hard links, if supported by the source filesystem.
 .IP \[bu]
 Object IDs, if supported by the source filesystem.
-.PP
-There is no support yet for capturing NTFS extended attributes.
+.IP \[bu]
+Extended attributes (EAs), if supported by the source filesystem.
 .SH REPARSE POINTS AND SYMLINKS
 A "symbolic link" (or "symlink") is a special file which "points to" some other
 file or directory.  On Windows, a "reparse point" is a generalization of a
 .SH REPARSE POINTS AND SYMLINKS
 A "symbolic link" (or "symlink") is a special file which "points to" some other
 file or directory.  On Windows, a "reparse point" is a generalization of a
index b42b3491dfb111f2303f282c275ab4bd3f73d246..9b33445e4c33bbe0aa9f651914f4cabdb11a7656 100644 (file)
@@ -57,11 +57,10 @@ Object IDs.  New files are not given object IDs.
 EFS-encrypted files.  The files themselves will be visible in mounted WIM images
 but their data will not be available.
 .IP \[bu]
 EFS-encrypted files.  The files themselves will be visible in mounted WIM images
 but their data will not be available.
 .IP \[bu]
-Extended attributes.  Although wimlib now supports creating a WIM image
-containing Linux-style extended attributes, these are not yet exposed in mounted
-WIM images.  (This may be implemented in the future, though it would conflict
-with the use of extended attributes to expose Windows concepts like named data
-streams.)
+Extended attributes.  Although wimlib supports WIM images containing extended
+attributes, these are not yet exposed in mounted WIM images.  (This may be
+implemented in the future, though it would conflict with the use of extended
+attributes to expose Windows concepts like named data streams.)
 .SH SPLIT WIMS
 You may use \fBwimmount\fR to mount an image from a split WIM read-only.
 However, you may not mount an image from a split WIM read-write.
 .SH SPLIT WIMS
 You may use \fBwimmount\fR to mount an image from a split WIM read-only.
 However, you may not mount an image from a split WIM read-write.
index cfddd1ed57ef8b601257de1106a5a09c70a06b50..2592fae6fd90da5bcd040de7b46313bcf7aa3d39 100644 (file)
@@ -31,7 +31,7 @@ struct wim_features {
        unsigned long object_ids;
        unsigned long timestamps;
        unsigned long case_sensitive_filenames;
        unsigned long object_ids;
        unsigned long timestamps;
        unsigned long case_sensitive_filenames;
-       unsigned long linux_xattrs;
+       unsigned long xattrs;
 };
 
 struct blob_descriptor;
 };
 
 struct blob_descriptor;
index c8ddb341b343bcc0ece73b82855bd8ec23b5b88f..dfbae30bb9839d7c676b6b2937ed01b608feb1b4 100644 (file)
@@ -8,10 +8,16 @@ struct wim_inode;
 /* Windows-style object ID */
 #define TAG_OBJECT_ID                  0x00000001
 
 /* Windows-style object ID */
 #define TAG_OBJECT_ID                  0x00000001
 
+/* Extended attributes */
+#define TAG_XATTRS                     0x00000002
+
 /* [wimlib extension] Standard UNIX metadata: uid, gid, mode, and rdev */
 #define TAG_WIMLIB_UNIX_DATA           0x337DD873
 
 /* [wimlib extension] Standard UNIX metadata: uid, gid, mode, and rdev */
 #define TAG_WIMLIB_UNIX_DATA           0x337DD873
 
-/* [wimlib extension] Linux-style xattrs */
+/*
+ * [wimlib extension] Linux-style extended attributes
+ * (deprecated in favor of TAG_XATTRS)
+ */
 #define TAG_WIMLIB_LINUX_XATTRS                0x337DD874
 
 extern void *
 #define TAG_WIMLIB_LINUX_XATTRS                0x337DD874
 
 extern void *
index cbdacd320cd64507d4529fd155ff723db9c1bddb..70114c8dac7aa86938c3e5bac0db6f0d9d73a352 100644 (file)
@@ -101,6 +101,24 @@ NtOpenSymbolicLinkObject(PHANDLE LinkHandle,
                         ACCESS_MASK DesiredAccess,
                         POBJECT_ATTRIBUTES ObjectAttributes);
 
                         ACCESS_MASK DesiredAccess,
                         POBJECT_ATTRIBUTES ObjectAttributes);
 
+NTSTATUS
+NTAPI
+NtQueryEaFile(IN HANDLE FileHandle,
+             OUT PIO_STATUS_BLOCK IoStatusBlock,
+             OUT PVOID Buffer,
+             IN ULONG Length,
+             IN BOOLEAN ReturnSingleEntry,
+             IN PVOID EaList OPTIONAL,
+             IN ULONG EaListLength,
+             IN PULONG EaIndex OPTIONAL,
+             IN BOOLEAN RestartScan);
+
+NTSTATUS
+NTAPI
+NtSetEaFile(IN HANDLE FileHandle,
+           OUT PIO_STATUS_BLOCK IoStatusBlock,
+           OUT PVOID Buffer,
+           IN ULONG Length);
 
 /* Dynamically loaded ntdll functions */
 
 
 /* Dynamically loaded ntdll functions */
 
index 2fc5f6dc79ba6b2d17aae19e8e8df88af2521086..2d297245aaf3a808115989d05d21d885fd876b3c 100644 (file)
@@ -7,24 +7,82 @@
 #include "wimlib/tagged_items.h"
 #include "wimlib/util.h"
 
 #include "wimlib/tagged_items.h"
 #include "wimlib/util.h"
 
-#undef HAVE_XATTR_SUPPORT
+#undef HAVE_LINUX_XATTR_SUPPORT
 #if defined(HAVE_SYS_XATTR_H) && \
        defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR) && \
        defined(HAVE_FSETXATTR) && defined(HAVE_LSETXATTR)
 #if defined(HAVE_SYS_XATTR_H) && \
        defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR) && \
        defined(HAVE_FSETXATTR) && defined(HAVE_LSETXATTR)
-#  define HAVE_XATTR_SUPPORT 1
+#  define HAVE_LINUX_XATTR_SUPPORT 1
 #endif
 
 #endif
 
+#define WIM_XATTR_NAME_MAX 255
+#define WIM_XATTR_SIZE_MAX 65535
+
+/*
+ * On-disk format of each extended attribute (xattr, or EA) entry in a metadata
+ * item tagged with TAG_XATTRS.  This is the preferred xattr format, since it is
+ * also used by WIMGAPI and DISM starting in Windows 10 version 1607.
+ */
+struct wim_xattr_entry {
+
+       /* length of xattr value in bytes */
+       le16 value_len;
+
+       /* length of xattr name in bytes, excluding the null terminator */
+       u8 name_len;
+
+       /* flags: 0 or 0x80 (FILE_NEED_EA) */
+       u8 flags;
+
+       /* followed by the xattr name *with* a null terminator */
+       char name[0];
+
+       /* followed by the xattr value */
+       /* u8 value[0]; */
+
+       /* no padding at end! */
+} _packed_attribute;
+
+static inline size_t
+xattr_entry_size(const struct wim_xattr_entry *entry)
+{
+       STATIC_ASSERT(sizeof(*entry) == 4);
+
+       return sizeof(*entry) + entry->name_len + 1 +
+               le16_to_cpu(entry->value_len);
+}
+
+/* minimum is a 1-byte name (plus null terminator) and an empty value */
+#define XATTR_ENTRY_MIN_SIZE (sizeof(struct wim_xattr_entry) + 2)
+
+static inline struct wim_xattr_entry *
+xattr_entry_next(const struct wim_xattr_entry *entry)
+{
+       return (void *)entry + xattr_entry_size(entry);
+}
+
+static inline bool
+valid_xattr_entry(const struct wim_xattr_entry *entry, size_t avail)
+{
+       if (avail < sizeof(*entry))
+               return false;
+
+       return entry->name_len > 0 && entry->name_len <= WIM_XATTR_NAME_MAX &&
+               le16_to_cpu(entry->value_len) <= WIM_XATTR_SIZE_MAX &&
+               avail >= xattr_entry_size(entry) &&
+               memchr(entry->name, '\0', entry->name_len) == NULL &&
+               entry->name[entry->name_len] == '\0';
+}
+
 /*
 /*
- * On-disk format of an entry in an extended attributes tagged item (wimlib
- * extension).  An xattr item consists of a series of variable-length xattr
- * name/value pairs, each of which begins with this header.
- *
- * Currently this is only used for Linux-style xattrs, but in the future we may
- * use this for Windows-style xattrs too.
+ * On-disk format of each extended attribute entry in a metadata item tagged
+ * with TAG_WIMLIB_LINUX_XATTRS.  This is a deprecated format which wimlib
+ * v1.11-v1.12 used to store extended attributes on Linux (predating the Windows
+ * xattr support in both WIMGAPI and wimlib).  Now we use TAG_XATTRS for both
+ * Windows and Linux xattrs.
  */
  */
-struct wimlib_xattr_entry {
+struct wimlib_xattr_entry_old {
 
 
-       /* length of xattr name in bytes */
+       /* length of xattr name in bytes, excluding a null terminator */
        le16 name_len;
 
        /* reserved, must be 0 */
        le16 name_len;
 
        /* reserved, must be 0 */
@@ -33,17 +91,17 @@ struct wimlib_xattr_entry {
        /* length of xattr value in bytes */
        le32 value_len;
 
        /* length of xattr value in bytes */
        le32 value_len;
 
-       /* followed by the xattr name with no terminating null */
+       /* followed by the xattr name *without* a null terminator */
        char name[0];
 
        char name[0];
 
-       /* followed by the xattr value with no terminating null */
+       /* followed by the xattr value */
        /* u8 value[0]; */
 
        /* then zero-padded to a 4-byte boundary */
 } _aligned_attribute(4);
 
 static inline size_t
        /* u8 value[0]; */
 
        /* then zero-padded to a 4-byte boundary */
 } _aligned_attribute(4);
 
 static inline size_t
-xattr_entry_size(const struct wimlib_xattr_entry *entry)
+old_xattr_entry_size(const struct wimlib_xattr_entry_old *entry)
 {
        STATIC_ASSERT(sizeof(*entry) == 8);
 
 {
        STATIC_ASSERT(sizeof(*entry) == 8);
 
@@ -51,44 +109,34 @@ xattr_entry_size(const struct wimlib_xattr_entry *entry)
                     le32_to_cpu(entry->value_len), 4);
 }
 
                     le32_to_cpu(entry->value_len), 4);
 }
 
-static inline struct wimlib_xattr_entry *
-xattr_entry_next(const struct wimlib_xattr_entry *entry)
+/* minimum is a 1-byte name and an empty value */
+#define OLD_XATTR_ENTRY_MIN_SIZE \
+       (ALIGN(sizeof(struct wimlib_xattr_entry_old) + 1, 4))
+
+static inline struct wimlib_xattr_entry_old *
+old_xattr_entry_next(const struct wimlib_xattr_entry_old *entry)
 {
 {
-       return (void *)entry + xattr_entry_size(entry);
+       return (void *)entry + old_xattr_entry_size(entry);
 }
 
 }
 
-/* Currently we use the Linux limits when validating xattr names and values */
-#define XATTR_NAME_MAX 255
-#define XATTR_SIZE_MAX 65536
-
 static inline bool
 static inline bool
-valid_xattr_entry(const struct wimlib_xattr_entry *entry, size_t avail)
+old_valid_xattr_entry(const struct wimlib_xattr_entry_old *entry, size_t avail)
 {
 {
-       if (avail < sizeof(*entry))
-               return false;
-
-       if (entry->name_len == 0 ||
-           le16_to_cpu(entry->name_len) > XATTR_NAME_MAX)
-               return false;
-
-       if (entry->reserved != 0)
-               return false;
+       u16 name_len;
 
 
-       if (le32_to_cpu(entry->value_len) > XATTR_SIZE_MAX)
-               return false;
-
-       if (avail < xattr_entry_size(entry))
-               return false;
-
-       if (memchr(entry->name, '\0', le16_to_cpu(entry->name_len)))
+       if (avail < sizeof(*entry))
                return false;
 
                return false;
 
-       return true;
+       name_len = le16_to_cpu(entry->name_len);
+       return name_len > 0 && name_len <= WIM_XATTR_NAME_MAX &&
+               le32_to_cpu(entry->value_len) <= WIM_XATTR_SIZE_MAX &&
+               avail >= old_xattr_entry_size(entry) &&
+               memchr(entry->name, '\0', name_len) == NULL;
 }
 
 }
 
-/* Is the xattr of the specified name security-related? */
+/* Is the xattr of the specified name security-related on Linux? */
 static inline bool
 static inline bool
-is_security_xattr(const char *name)
+is_linux_security_xattr(const char *name)
 {
 #define XATTR_SECURITY_PREFIX "security."
 #define XATTR_SYSTEM_PREFIX "system."
 {
 #define XATTR_SECURITY_PREFIX "security."
 #define XATTR_SYSTEM_PREFIX "system."
@@ -104,23 +152,49 @@ is_security_xattr(const char *name)
 }
 
 static inline const void *
 }
 
 static inline const void *
-inode_get_linux_xattrs(const struct wim_inode *inode, u32 *len_ret)
+inode_get_xattrs(const struct wim_inode *inode, u32 *len_ret)
+{
+       return inode_get_tagged_item(inode, TAG_XATTRS,
+                                    XATTR_ENTRY_MIN_SIZE, len_ret);
+}
+
+static inline const void *
+inode_get_xattrs_old(const struct wim_inode *inode, u32 *len_ret)
+{
+       return inode_get_tagged_item(inode, TAG_WIMLIB_LINUX_XATTRS,
+                                    OLD_XATTR_ENTRY_MIN_SIZE, len_ret);
+}
+
+static inline const void *
+inode_get_linux_xattrs(const struct wim_inode *inode, u32 *len_ret,
+                      bool *is_old_format_ret)
 {
 {
-       return inode_get_tagged_item(inode, TAG_WIMLIB_LINUX_XATTRS, 0,
-                                    len_ret);
+       const void *entries;
+
+       entries = inode_get_xattrs(inode, len_ret);
+       if (entries) {
+               *is_old_format_ret = false;
+               return entries;
+       }
+       entries = inode_get_xattrs_old(inode, len_ret);
+       if (entries) {
+               *is_old_format_ret = true;
+               return entries;
+       }
+       return NULL;
 }
 
 static inline bool
 }
 
 static inline bool
-inode_has_linux_xattrs(const struct wim_inode *inode)
+inode_has_xattrs(const struct wim_inode *inode)
 {
 {
-       return inode_get_linux_xattrs(inode, NULL) != NULL;
+       return inode_get_xattrs(inode, NULL) != NULL ||
+              inode_get_xattrs_old(inode, NULL) != NULL;
 }
 
 static inline bool
 }
 
 static inline bool
-inode_set_linux_xattrs(struct wim_inode *inode, const void *entries, u32 len)
+inode_set_xattrs(struct wim_inode *inode, const void *entries, u32 len)
 {
 {
-       return inode_set_tagged_item(inode, TAG_WIMLIB_LINUX_XATTRS,
-                                    entries, len);
+       return inode_set_tagged_item(inode, TAG_XATTRS, entries, len);
 }
 
 #endif /* _WIMLIB_XATTR_H  */
 }
 
 #endif /* _WIMLIB_XATTR_H  */
index f59cffb55347acc014da0feb0aad58f42d2150b8..cd1dd2c1ca0ebb319066db5c02c5fcacff3ba84b 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
  */
 
 /*
- * Copyright (C) 2012-2017 Eric Biggers
+ * Copyright (C) 2012-2018 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
  *
  * 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
@@ -1225,8 +1225,8 @@ inode_tally_features(const struct wim_inode *inode,
                features->unix_data++;
        if (inode_has_object_id(inode))
                features->object_ids++;
                features->unix_data++;
        if (inode_has_object_id(inode))
                features->object_ids++;
-       if (inode_has_linux_xattrs(inode))
-               features->linux_xattrs++;
+       if (inode_has_xattrs(inode))
+               features->xattrs++;
 }
 
 /* Tally features necessary to extract a dentry and the corresponding inode.  */
 }
 
 /* Tally features necessary to extract a dentry and the corresponding inode.  */
@@ -1380,14 +1380,15 @@ do_feature_check(const struct wim_features *required_features,
                         T("\n          (use --unix-data mode to extract these)") : T("")));
        }
 
                         T("\n          (use --unix-data mode to extract these)") : T("")));
        }
 
-       /* Linux-style extended attributes */
-       if (required_features->linux_xattrs &&
-           (!supported_features->linux_xattrs ||
-            !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)))
+       /* Extended attributes */
+       if (required_features->xattrs &&
+           (!supported_features->xattrs ||
+            (supported_features->unix_data &&
+             !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))))
        {
        {
-               WARNING("Ignoring Linux-style extended attributes of %lu files%"TS,
-                       required_features->linux_xattrs,
-                       (supported_features->linux_xattrs ?
+               WARNING("Ignoring extended attributes of %lu files%"TS,
+                       required_features->xattrs,
+                       (supported_features->xattrs ?
                         T("\n          (use --unix-data mode to extract these)") : T("")));
        }
 
                         T("\n          (use --unix-data mode to extract these)") : T("")));
        }
 
index 4df34d4cc8a8f06278313ddac92da82333121ecf..d2fecc8274f6b8fd28875f39687d4313d85b7c82 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
  */
 
 /*
- * Copyright (C) 2015-2017 Eric Biggers
+ * Copyright (C) 2015-2018 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
  *
  * 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
@@ -471,8 +471,8 @@ static noinline_for_stack int
 set_random_xattrs(struct wim_inode *inode)
 {
        int num_xattrs = 1 + rand32() % 16;
 set_random_xattrs(struct wim_inode *inode)
 {
        int num_xattrs = 1 + rand32() % 16;
-       char entries[8192] _aligned_attribute(4);
-       struct wimlib_xattr_entry *entry = (void *)entries;
+       char entries[8192];
+       struct wim_xattr_entry *entry = (void *)entries;
        size_t entries_size;
        struct wimlib_unix_data unix_data;
        const char *prefix = "user.";
        size_t entries_size;
        struct wimlib_unix_data unix_data;
        const char *prefix = "user.";
@@ -497,19 +497,19 @@ set_random_xattrs(struct wim_inode *inode)
                int value_len = rand32() % 64;
                u8 *p;
 
                int value_len = rand32() % 64;
                u8 *p;
 
-               entry->reserved = 0;
-               entry->value_len = cpu_to_le32(value_len);
+               entry->value_len = cpu_to_le16(value_len);
+               entry->flags = 0;
 
                if (rand32() % 16 == 0 && am_root() &&
                    !generated_capability_xattr) {
                        int name_len = sizeof(capability_name) - 1;
 
                if (rand32() % 16 == 0 && am_root() &&
                    !generated_capability_xattr) {
                        int name_len = sizeof(capability_name) - 1;
-                       entry->name_len = cpu_to_le16(name_len);
-                       p = mempcpy(entry->name, capability_name, name_len);
+                       entry->name_len = name_len;
+                       p = mempcpy(entry->name, capability_name, name_len + 1);
                        generated_capability_xattr = true;
                } else {
                        int name_len = 1 + rand32() % 64;
 
                        generated_capability_xattr = true;
                } else {
                        int name_len = 1 + rand32() % 64;
 
-                       entry->name_len = cpu_to_le16(strlen(prefix) + name_len);
+                       entry->name_len = strlen(prefix) + name_len;
                        p = mempcpy(entry->name, prefix, strlen(prefix));
                        *p++ = 'a' + i;
                        for (int j = 1; j < name_len; j++) {
                        p = mempcpy(entry->name, prefix, strlen(prefix));
                        *p++ = 'a' + i;
                        for (int j = 1; j < name_len; j++) {
@@ -518,21 +518,18 @@ set_random_xattrs(struct wim_inode *inode)
                                } while (*p == '\0');
                                p++;
                        }
                                } while (*p == '\0');
                                p++;
                        }
+                       *p++ = '\0';
                }
                for (int j = 0; j < value_len; j++)
                        *p++ = rand8();
 
                }
                for (int j = 0; j < value_len; j++)
                        *p++ = rand8();
 
-               while ((uintptr_t)p % 4)
-                       *p++ = 0;
-
                entry = (void *)p;
        }
 
        entries_size = (char *)entry - entries;
                entry = (void *)p;
        }
 
        entries_size = (char *)entry - entries;
-       wimlib_assert(entries_size > 0 && entries_size % 4 == 0 &&
-                     entries_size <= sizeof(entries));
+       wimlib_assert(entries_size > 0 && entries_size <= sizeof(entries));
 
 
-       if (!inode_set_linux_xattrs(inode, entries, entries_size))
+       if (!inode_set_xattrs(inode, entries, entries_size))
                return WIMLIB_ERR_NOMEM;
 
        return 0;
                return WIMLIB_ERR_NOMEM;
 
        return 0;
@@ -1393,28 +1390,26 @@ cmp_unix_metadata(const struct wim_inode *inode1,
 static int
 cmp_xattr_names(const void *p1, const void *p2)
 {
 static int
 cmp_xattr_names(const void *p1, const void *p2)
 {
-       const struct wimlib_xattr_entry *entry1 = *(const struct wimlib_xattr_entry **)p1;
-       const struct wimlib_xattr_entry *entry2 = *(const struct wimlib_xattr_entry **)p2;
-       u16 name_len1 = le16_to_cpu(entry1->name_len);
-       u16 name_len2 = le16_to_cpu(entry2->name_len);
+       const struct wim_xattr_entry *entry1 = *(const struct wim_xattr_entry **)p1;
+       const struct wim_xattr_entry *entry2 = *(const struct wim_xattr_entry **)p2;
        int res;
 
        int res;
 
-       res = cmp_u32(name_len1, name_len2);
+       res = entry1->name_len - entry2->name_len;
        if (res)
                return res;
 
        if (res)
                return res;
 
-       return memcmp(entry1->name, entry2->name, name_len1);
+       return memcmp(entry1->name, entry2->name, entry1->name_len);
 }
 
 /* Validate and sort by name a list of extended attributes */
 static int
 parse_xattrs(const void *xattrs, u32 len,
 }
 
 /* Validate and sort by name a list of extended attributes */
 static int
 parse_xattrs(const void *xattrs, u32 len,
-            const struct wimlib_xattr_entry *entries[],
+            const struct wim_xattr_entry *entries[],
             u32 *num_entries_p)
 {
        u32 limit = *num_entries_p;
        u32 num_entries = 0;
             u32 *num_entries_p)
 {
        u32 limit = *num_entries_p;
        u32 num_entries = 0;
-       const struct wimlib_xattr_entry *entry = xattrs;
+       const struct wim_xattr_entry *entry = xattrs;
 
        while ((void *)entry < xattrs + len) {
                if (!valid_xattr_entry(entry, xattrs + len - (void *)entry)) {
 
        while ((void *)entry < xattrs + len) {
                if (!valid_xattr_entry(entry, xattrs + len - (void *)entry)) {
@@ -1448,14 +1443,14 @@ parse_xattrs(const void *xattrs, u32 len,
 }
 
 static int
 }
 
 static int
-cmp_linux_xattrs(const struct wim_inode *inode1,
-                const struct wim_inode *inode2, int cmp_flags)
+cmp_xattrs(const struct wim_inode *inode1, const struct wim_inode *inode2,
+          int cmp_flags)
 {
        const void *xattrs1, *xattrs2;
        u32 len1, len2;
 
 {
        const void *xattrs1, *xattrs2;
        u32 len1, len2;
 
-       xattrs1 = inode_get_linux_xattrs(inode1, &len1);
-       xattrs2 = inode_get_linux_xattrs(inode2, &len2);
+       xattrs1 = inode_get_xattrs(inode1, &len1);
+       xattrs2 = inode_get_xattrs(inode2, &len2);
 
        if (!xattrs1 && !xattrs2) {
                return 0;
 
        if (!xattrs1 && !xattrs2) {
                return 0;
@@ -1472,8 +1467,8 @@ cmp_linux_xattrs(const struct wim_inode *inode1,
                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
        } else {
                const int max_entries = 64;
                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
        } else {
                const int max_entries = 64;
-               const struct wimlib_xattr_entry *entries1[max_entries];
-               const struct wimlib_xattr_entry *entries2[max_entries];
+               const struct wim_xattr_entry *entries1[max_entries];
+               const struct wim_xattr_entry *entries2[max_entries];
                u32 xattr_count1 = max_entries;
                u32 xattr_count2 = max_entries;
                int ret;
                u32 xattr_count1 = max_entries;
                u32 xattr_count2 = max_entries;
                int ret;
@@ -1496,21 +1491,21 @@ cmp_linux_xattrs(const struct wim_inode *inode1,
                              xattr_count1, xattr_count2);
                }
                for (u32 i = 0; i < xattr_count1; i++) {
                              xattr_count1, xattr_count2);
                }
                for (u32 i = 0; i < xattr_count1; i++) {
-                       const struct wimlib_xattr_entry *entry1 = entries1[i];
-                       const struct wimlib_xattr_entry *entry2 = entries2[i];
+                       const struct wim_xattr_entry *entry1 = entries1[i];
+                       const struct wim_xattr_entry *entry2 = entries2[i];
 
 
-                       if (entry1->name_len != entry2->name_len ||
-                           entry1->value_len != entry2->value_len ||
-                           entry1->reserved != entry2->reserved ||
+                       if (entry1->value_len != entry2->value_len ||
+                           entry1->name_len != entry2->name_len ||
+                           entry1->flags != entry2->flags ||
                            memcmp(entry1->name, entry2->name,
                            memcmp(entry1->name, entry2->name,
-                                  le16_to_cpu(entry1->name_len)) ||
-                           memcmp(entry1->name + le16_to_cpu(entry1->name_len),
-                                  entry2->name + le16_to_cpu(entry1->name_len),
-                                  le32_to_cpu(entry1->value_len)))
+                                  entry1->name_len) ||
+                           memcmp(entry1->name + entry1->name_len + 1,
+                                  entry2->name + entry2->name_len + 1,
+                                  le16_to_cpu(entry1->value_len)))
                        {
                                ERROR("xattr %.*s of %"TS" differs",
                        {
                                ERROR("xattr %.*s of %"TS" differs",
-                                     le16_to_cpu(entry1->name_len),
-                                     entry1->name, inode_any_full_path(inode1));
+                                     entry1->name_len, entry1->name,
+                                     inode_any_full_path(inode1));
                                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
                        }
                }
                                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
                        }
                }
@@ -1634,8 +1629,8 @@ cmp_inodes(const struct wim_inode *inode1, const struct wim_inode *inode2,
        if (ret)
                return ret;
 
        if (ret)
                return ret;
 
-       /* Compare Linux-style xattrs  */
-       ret = cmp_linux_xattrs(inode1, inode2, cmp_flags);
+       /* Compare extended attributes  */
+       ret = cmp_xattrs(inode1, inode2, cmp_flags);
        if (ret)
                return ret;
 
        if (ret)
                return ret;
 
index 93e7b56b14a167e7dc06ec02bc61ba8add6347e0..43215e03d094ad945507847ecc1b25719002b38b 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright (C) 2012-2018 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
  *
  * 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
@@ -64,8 +64,8 @@ unix_get_supported_features(const char *target,
        supported_features->unix_data = 1;
        supported_features->timestamps = 1;
        supported_features->case_sensitive_filenames = 1;
        supported_features->unix_data = 1;
        supported_features->timestamps = 1;
        supported_features->case_sensitive_filenames = 1;
-#ifdef HAVE_XATTR_SUPPORT
-       supported_features->linux_xattrs = 1;
+#ifdef HAVE_LINUX_XATTR_SUPPORT
+       supported_features->xattrs = 1;
 #endif
        return 0;
 }
 #endif
        return 0;
 }
@@ -268,38 +268,58 @@ unix_set_mode(int fd, const char *path, mode_t mode)
        return WIMLIB_ERR_SET_SECURITY;
 }
 
        return WIMLIB_ERR_SET_SECURITY;
 }
 
-#ifdef HAVE_XATTR_SUPPORT
+#ifdef HAVE_LINUX_XATTR_SUPPORT
 /* Apply extended attributes to a file */
 static int
 apply_linux_xattrs(int fd, const struct wim_inode *inode,
                   const char *path, struct unix_apply_ctx *ctx,
 /* Apply extended attributes to a file */
 static int
 apply_linux_xattrs(int fd, const struct wim_inode *inode,
                   const char *path, struct unix_apply_ctx *ctx,
-                  const void *entries, size_t entries_size)
+                  const void *entries, size_t entries_size, bool is_old_format)
 {
        const void * const entries_end = entries + entries_size;
 {
        const void * const entries_end = entries + entries_size;
-       char name[XATTR_NAME_MAX + 1];
+       char name[WIM_XATTR_NAME_MAX + 1];
 
 
-       for (const struct wimlib_xattr_entry *entry = entries;
-            (void *)entry < entries_end; entry = xattr_entry_next(entry))
+       for (const void *entry = entries;
+            entry < entries_end;
+            entry = is_old_format ? (const void *)old_xattr_entry_next(entry) :
+                                    (const void *)xattr_entry_next(entry))
        {
        {
+               bool valid;
                u16 name_len;
                const void *value;
                u32 value_len;
                int res;
 
                u16 name_len;
                const void *value;
                u32 value_len;
                int res;
 
-               if (!valid_xattr_entry(entry, entries_end - (void *)entry)) {
+               if (is_old_format) {
+                       valid = old_valid_xattr_entry(entry,
+                                                     entries_end - entry);
+               } else {
+                       valid = valid_xattr_entry(entry, entries_end - entry);
+               }
+               if (!valid) {
                        if (!path) {
                                path = unix_build_inode_extraction_path(inode,
                                                                        ctx);
                        }
                        if (!path) {
                                path = unix_build_inode_extraction_path(inode,
                                                                        ctx);
                        }
-                       ERROR("\"%s\": extended attribute is corrupt", path);
+                       ERROR("\"%s\": extended attribute is corrupt or unsupported",
+                             path);
                        return WIMLIB_ERR_INVALID_XATTR;
                }
                        return WIMLIB_ERR_INVALID_XATTR;
                }
-               name_len = le16_to_cpu(entry->name_len);
-               memcpy(name, entry->name, name_len);
-               name[name_len] = '\0';
+               if (is_old_format) {
+                       const struct wimlib_xattr_entry_old *e = entry;
 
 
-               value = entry->name + name_len;
-               value_len = le32_to_cpu(entry->value_len);
+                       name_len = le16_to_cpu(e->name_len);
+                       memcpy(name, e->name, name_len);
+                       value = e->name + name_len;
+                       value_len = le32_to_cpu(e->value_len);
+               } else {
+                       const struct wim_xattr_entry *e = entry;
+
+                       name_len = e->name_len;
+                       memcpy(name, e->name, name_len);
+                       value = e->name + name_len + 1;
+                       value_len = le16_to_cpu(e->value_len);
+               }
+               name[name_len] = '\0';
 
                if (fd >= 0)
                        res = fsetxattr(fd, name, value, value_len, 0);
 
                if (fd >= 0)
                        res = fsetxattr(fd, name, value, value_len, 0);
@@ -311,7 +331,7 @@ apply_linux_xattrs(int fd, const struct wim_inode *inode,
                                path = unix_build_inode_extraction_path(inode,
                                                                        ctx);
                        }
                                path = unix_build_inode_extraction_path(inode,
                                                                        ctx);
                        }
-                       if (is_security_xattr(name) &&
+                       if (is_linux_security_xattr(name) &&
                            (ctx->common.extract_flags &
                             WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
                        {
                            (ctx->common.extract_flags &
                             WIMLIB_EXTRACT_FLAG_STRICT_ACLS))
                        {
@@ -325,7 +345,7 @@ apply_linux_xattrs(int fd, const struct wim_inode *inode,
        }
        return 0;
 }
        }
        return 0;
 }
-#endif /* HAVE_XATTR_SUPPORT */
+#endif /* HAVE_LINUX_XATTR_SUPPORT */
 
 /*
  * Apply UNIX-specific metadata to a file if available.  This includes standard
 
 /*
  * Apply UNIX-specific metadata to a file if available.  This includes standard
@@ -348,9 +368,10 @@ apply_unix_metadata(int fd, const struct wim_inode *inode,
 {
        bool have_dat;
        struct wimlib_unix_data dat;
 {
        bool have_dat;
        struct wimlib_unix_data dat;
-#ifdef HAVE_XATTR_SUPPORT
+#ifdef HAVE_LINUX_XATTR_SUPPORT
        const void *entries;
        u32 entries_size;
        const void *entries;
        u32 entries_size;
+       bool is_old_format;
 #endif
        int ret;
 
 #endif
        int ret;
 
@@ -373,11 +394,11 @@ apply_unix_metadata(int fd, const struct wim_inode *inode,
                }
        }
 
                }
        }
 
-#ifdef HAVE_XATTR_SUPPORT
-       entries = inode_get_linux_xattrs(inode, &entries_size);
+#ifdef HAVE_LINUX_XATTR_SUPPORT
+       entries = inode_get_linux_xattrs(inode, &entries_size, &is_old_format);
        if (entries) {
                ret = apply_linux_xattrs(fd, inode, path, ctx,
        if (entries) {
                ret = apply_linux_xattrs(fd, inode, path, ctx,
-                                        entries, entries_size);
+                                        entries, entries_size, is_old_format);
                if (ret)
                        return ret;
        }
                if (ret)
                        return ret;
        }
index 5ae8209bf9ecab5b2ab31fd276195706df93fc11..f9fad4a33ce1a1ece10237d9c90d452743019a3e 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
  */
 
 /*
- * Copyright (C) 2012-2017 Eric Biggers
+ * Copyright (C) 2012-2018 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
  *
  * 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
@@ -103,7 +103,7 @@ my_fdopendir(int *dirfd_p)
 #  define AT_SYMLINK_NOFOLLOW  0x100
 #endif
 
 #  define AT_SYMLINK_NOFOLLOW  0x100
 #endif
 
-#ifdef HAVE_XATTR_SUPPORT
+#ifdef HAVE_LINUX_XATTR_SUPPORT
 /*
  * Retrieves the values of the xattrs named by the null-terminated @names of the
  * file at @path and serializes the xattr names and values into @entries.  If
 /*
  * Retrieves the values of the xattrs named by the null-terminated @names of the
  * file at @path and serializes the xattr names and values into @entries.  If
@@ -117,35 +117,39 @@ gather_xattr_entries(const char *path, const char *names, size_t names_size,
        const char * const names_end = names + names_size;
        void * const entries_end = entries + entries_size;
        const char *name = names;
        const char * const names_end = names + names_size;
        void * const entries_end = entries + entries_size;
        const char *name = names;
-       struct wimlib_xattr_entry *entry = entries;
+       struct wim_xattr_entry *entry = entries;
 
 
-       wimlib_assert((uintptr_t)entries % 4 == 0 &&
-                     entries_size % 4 == 0 && names_size != 0);
        do {
                size_t name_len = strnlen(name, names_end - name);
                void *value;
                ssize_t value_len;
 
        do {
                size_t name_len = strnlen(name, names_end - name);
                void *value;
                ssize_t value_len;
 
-               if (name_len == 0 || name_len >= names_end - name ||
-                   (u16)name_len != name_len) {
+               if (name_len == 0 || name_len >= names_end - name) {
                        ERROR("\"%s\": malformed extended attribute names list",
                              path);
                        errno = EINVAL;
                        return -1;
                }
 
                        ERROR("\"%s\": malformed extended attribute names list",
                              path);
                        errno = EINVAL;
                        return -1;
                }
 
+               if (name_len > WIM_XATTR_NAME_MAX) {
+                       WARNING("\"%s\": name of extended attribute \"%s\" is too long to store",
+                               path, name);
+                       goto next_name;
+               }
+
                /*
                /*
-                * Note: we take care to always call lgetxattr() with a nonzero
-                * size, since zero size means to return the value length only.
+                * Take care to always call lgetxattr() with a nonzero size,
+                * since zero size means to return the value length only.
                 */
                 */
-               if (entries_end - (void *)entry <= sizeof(*entry) + name_len) {
+               if (entries_end - (void *)entry <=
+                   sizeof(*entry) + name_len + 1) {
                        errno = ERANGE;
                        return -1;
                }
 
                        errno = ERANGE;
                        return -1;
                }
 
-               entry->name_len = cpu_to_le16(name_len);
-               entry->reserved = 0;
-               value = mempcpy(entry->name, name, name_len);
+               entry->name_len = name_len;
+               entry->flags = 0;
+               value = mempcpy(entry->name, name, name_len + 1);
 
                value_len = lgetxattr(path, name, value, entries_end - value);
                if (value_len < 0) {
 
                value_len = lgetxattr(path, name, value, entries_end - value);
                if (value_len < 0) {
@@ -155,26 +159,14 @@ gather_xattr_entries(const char *path, const char *names, size_t names_size,
                        }
                        return -1;
                }
                        }
                        return -1;
                }
-               if ((u32)value_len != value_len) {
-                       ERROR("\"%s\": value of extended attribute \"%s\" is too large",
-                             path, name);
-                       errno = EINVAL;
-                       return -1;
+               if (value_len > WIM_XATTR_SIZE_MAX) {
+                       WARNING("\"%s\": value of extended attribute \"%s\" is too large to store",
+                               path, name);
+                       goto next_name;
                }
                }
-               entry->value_len = cpu_to_le32(value_len);
-
-               /*
-                * Zero-pad the entry to the next 4-byte boundary.
-                * Note: because we've guaranteed that @entries_size is a
-                * multiple of 4, this cannot overflow the @entries buffer.
-                */
-               value += value_len;
-               while ((uintptr_t)value & 3) {
-                       *(u8 *)value = 0;
-                       value++;
-               }
-
-               entry = value;
+               entry->value_len = cpu_to_le16(value_len);
+               entry = value + value_len;
+       next_name:
                name += name_len + 1;
        } while (name < names_end);
 
                name += name_len + 1;
        } while (name < names_end);
 
@@ -185,7 +177,7 @@ static int
 create_xattr_item(const char *path, struct wim_inode *inode,
                  const char *names, size_t names_size)
 {
 create_xattr_item(const char *path, struct wim_inode *inode,
                  const char *names, size_t names_size)
 {
-       char _entries[1024] _aligned_attribute(4);
+       char _entries[1024];
        char *entries = _entries;
        size_t entries_avail = ARRAY_LEN(_entries);
        ssize_t entries_size;
        char *entries = _entries;
        size_t entries_avail = ARRAY_LEN(_entries);
        ssize_t entries_size;
@@ -217,7 +209,7 @@ retry:
                goto out;
        }
        ret = WIMLIB_ERR_NOMEM;
                goto out;
        }
        ret = WIMLIB_ERR_NOMEM;
-       if (!inode_set_linux_xattrs(inode, entries, entries_size))
+       if (!inode_set_xattrs(inode, entries, entries_size))
                goto out;
 
        ret = 0;
                goto out;
 
        ret = 0;
@@ -284,7 +276,7 @@ out:
                FREE(names);
        return ret;
 }
                FREE(names);
        return ret;
 }
-#endif /* HAVE_XATTR_SUPPORT */
+#endif /* HAVE_LINUX_XATTR_SUPPORT */
 
 static int
 unix_scan_regular_file(const char *path, u64 blocks, u64 size,
 
 static int
 unix_scan_regular_file(const char *path, u64 blocks, u64 size,
@@ -622,7 +614,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
                        ret = WIMLIB_ERR_NOMEM;
                        goto out;
                }
                        ret = WIMLIB_ERR_NOMEM;
                        goto out;
                }
-#ifdef HAVE_XATTR_SUPPORT
+#ifdef HAVE_LINUX_XATTR_SUPPORT
                ret = scan_linux_xattrs(params->cur_path, inode);
                if (ret)
                        goto out;
                ret = scan_linux_xattrs(params->cur_path, inode);
                if (ret)
                        goto out;
index 94285aca793cf763cd1cf377ecaace6be4eb1bcf..1434bd22d8d656969d03cc1fe829538199d77096 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
  */
 
 /*
- * Copyright (C) 2013-2016 Eric Biggers
+ * Copyright (C) 2013-2018 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
  *
  * 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
 #include "wimlib/reparse.h"
 #include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list()  */
 #include "wimlib/textfile.h"
 #include "wimlib/reparse.h"
 #include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list()  */
 #include "wimlib/textfile.h"
-#include "wimlib/xml.h"
 #include "wimlib/wimboot.h"
 #include "wimlib/wof.h"
 #include "wimlib/wimboot.h"
 #include "wimlib/wof.h"
+#include "wimlib/xattr.h"
+#include "wimlib/xml.h"
 
 struct win32_apply_ctx {
 
 
 struct win32_apply_ctx {
 
@@ -169,6 +170,9 @@ struct win32_apply_ctx {
        /* Number of files for which we couldn't set the object ID.  */
        unsigned long num_object_id_failures;
 
        /* Number of files for which we couldn't set the object ID.  */
        unsigned long num_object_id_failures;
 
+       /* Number of files for which we couldn't set extended attributes.  */
+       unsigned long num_xattr_failures;
+
        /* The Windows build number of the image being applied, or 0 if unknown.
         */
        u64 windows_build_number;
        /* The Windows build number of the image being applied, or 0 if unknown.
         */
        u64 windows_build_number;
@@ -225,9 +229,14 @@ get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
        }
 
        if (wcsstr(filesystem_name, L"NTFS")) {
        }
 
        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.  */
+               /*
+                * FILE_SUPPORTS_HARD_LINKS and
+                * FILE_SUPPORTS_EXTENDED_ATTRIBUTES are only supported on
+                * Windows 7 and later.  Force them on anyway if the filesystem
+                * is NTFS.
+                */
                *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
                *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS;
+               *vol_flags_ret |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES;
 
                /* There's no volume flag for short names, but according to the
                 * MS documentation they are only user-settable on NTFS.  */
 
                /* There's no volume flag for short names, but according to the
                 * MS documentation they are only user-settable on NTFS.  */
@@ -339,6 +348,9 @@ win32_get_supported_features(const wchar_t *target,
                        supported_features->case_sensitive_filenames = 1;
        }
 
                        supported_features->case_sensitive_filenames = 1;
        }
 
+       if (vol_flags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
+               supported_features->xattrs = 1;
+
        return 0;
 }
 
        return 0;
 }
 
@@ -2805,6 +2817,105 @@ set_object_id(HANDLE h, const struct wim_inode *inode,
        }
 }
 
        }
 }
 
+static int
+set_xattrs(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx)
+{
+       const void *entries, *entries_end;
+       u32 len;
+       const struct wim_xattr_entry *entry;
+       size_t bufsize = 0;
+       u8 _buf[1024] _aligned_attribute(4);
+       u8 *buf = _buf;
+       FILE_FULL_EA_INFORMATION *ea, *ea_prev;
+       NTSTATUS status;
+       int ret;
+
+       if (!ctx->common.supported_features.xattrs)
+               return 0;
+
+       entries = inode_get_xattrs(inode, &len);
+       if (likely(entries == NULL || len == 0))  /* No extended attributes? */
+               return 0;
+       entries_end = entries + len;
+
+       entry = entries;
+       for (entry = entries; (void *)entry < entries_end;
+            entry = xattr_entry_next(entry)) {
+               if (!valid_xattr_entry(entry, entries_end - (void *)entry)) {
+                       ERROR("\"%"TS"\": extended attribute is corrupt or unsupported",
+                             inode_any_full_path(inode));
+                       return WIMLIB_ERR_INVALID_XATTR;
+               }
+
+               bufsize += ALIGN(offsetof(FILE_FULL_EA_INFORMATION, EaName) +
+                                entry->name_len + 1 +
+                                le16_to_cpu(entry->value_len), 4);
+       }
+
+       if (unlikely(bufsize != (u32)bufsize)) {
+               ERROR("\"%"TS"\": too many extended attributes to extract!",
+                     inode_any_full_path(inode));
+               return WIMLIB_ERR_INVALID_XATTR;
+       }
+
+       if (unlikely(bufsize > sizeof(_buf))) {
+               buf = MALLOC(bufsize);
+               if (!buf)
+                       return WIMLIB_ERR_NOMEM;
+       }
+
+       ea_prev = NULL;
+       ea = (FILE_FULL_EA_INFORMATION *)buf;
+       for (entry = entries; (void *)entry < entries_end;
+            entry = xattr_entry_next(entry)) {
+               u8 *p;
+
+               if (ea_prev)
+                       ea_prev->NextEntryOffset = (u8 *)ea - (u8 *)ea_prev;
+               ea->Flags = entry->flags;
+               ea->EaNameLength = entry->name_len;
+               ea->EaValueLength = le16_to_cpu(entry->value_len);
+               p = mempcpy(ea->EaName, entry->name,
+                           ea->EaNameLength + 1 + ea->EaValueLength);
+               while ((uintptr_t)p & 3)
+                       *p++ = 0;
+               ea_prev = ea;
+               ea = (FILE_FULL_EA_INFORMATION *)p;
+       }
+       ea_prev->NextEntryOffset = 0;
+       wimlib_assert((u8 *)ea - buf == bufsize);
+
+       status = NtSetEaFile(h, &ctx->iosb, buf, bufsize);
+       if (unlikely(!NT_SUCCESS(status))) {
+               if (status == STATUS_EAS_NOT_SUPPORTED) {
+                       /* This happens with Samba. */
+                       WARNING("Filesystem advertised extended attribute (EA) support, but it doesn't\n"
+                               "          work.  EAs will not be extracted.");
+                       ctx->common.supported_features.xattrs = 0;
+               } else if (status == STATUS_INVALID_EA_NAME) {
+                       ctx->num_xattr_failures++;
+                       if (ctx->num_xattr_failures < 5) {
+                               winnt_warning(status,
+                                             L"Can't set extended attributes on \"%ls\"",
+                                             current_path(ctx));
+                       } else if (ctx->num_xattr_failures == 5) {
+                               WARNING("Suppressing further warnings about "
+                                       "failure to set extended attributes.");
+                       }
+               } else {
+                       winnt_error(status, L"Can't set extended attributes on \"%ls\"",
+                                   current_path(ctx));
+                       ret = WIMLIB_ERR_SET_XATTR;
+                       goto out;
+               }
+       }
+       ret = 0;
+out:
+       if (buf != _buf)
+               FREE(buf);
+       return ret;
+}
+
 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
  * handle @h.  */
 static NTSTATUS
 /* Set the security descriptor @desc, of @desc_size bytes, on the file with open
  * handle @h.  */
 static NTSTATUS
@@ -2948,11 +3059,18 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode,
 {
        FILE_BASIC_INFORMATION info;
        NTSTATUS status;
 {
        FILE_BASIC_INFORMATION info;
        NTSTATUS status;
+       int ret;
 
        /* Set the file's object ID if present and object IDs are supported by
         * the filesystem.  */
        set_object_id(h, inode, ctx);
 
 
        /* Set the file's object ID if present and object IDs are supported by
         * the filesystem.  */
        set_object_id(h, inode, ctx);
 
+       /* Set the file's extended attributes (EAs) if present and EAs are
+        * supported by the filesystem.  */
+       ret = set_xattrs(h, inode, ctx);
+       if (ret)
+               return ret;
+
        /* Set the file's security descriptor if present and we're not in
         * NO_ACLS mode  */
        if (inode_has_security_descriptor(inode) &&
        /* Set the file's security descriptor if present and we're not in
         * NO_ACLS mode  */
        if (inode_has_security_descriptor(inode) &&
@@ -3017,7 +3135,7 @@ apply_metadata_to_file(const struct wim_dentry *dentry,
        NTSTATUS status;
        int ret;
 
        NTSTATUS status;
        int ret;
 
-       perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC |
+       perms = FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | WRITE_DAC |
                WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
 
        build_extraction_path(dentry, ctx);
                WRITE_OWNER | ACCESS_SYSTEM_SECURITY;
 
        build_extraction_path(dentry, ctx);
index 75c5e66f56132d494b59356f1619e02a3644e5bf..df908b02465ce539de6b7fcace3b9e6538373b4c 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
  */
 
 /*
- * Copyright (C) 2013-2017 Eric Biggers
+ * Copyright (C) 2013-2018 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
  *
  * 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
@@ -41,6 +41,7 @@
 #include "wimlib/scan.h"
 #include "wimlib/win32_vss.h"
 #include "wimlib/wof.h"
 #include "wimlib/scan.h"
 #include "wimlib/win32_vss.h"
 #include "wimlib/wof.h"
+#include "wimlib/xattr.h"
 
 struct winnt_scan_ctx {
        struct scan_params *params;
 
 struct winnt_scan_ctx {
        struct scan_params *params;
@@ -695,6 +696,111 @@ winnt_load_object_id(HANDLE h, struct wim_inode *inode,
        return 0;
 }
 
        return 0;
 }
 
+/* Load a file's extended attributes into the corresponding WIM inode.  */
+static noinline_for_stack int
+winnt_load_xattrs(HANDLE h, struct wim_inode *inode,
+                 struct winnt_scan_ctx *ctx, u32 ea_size)
+{
+       IO_STATUS_BLOCK iosb;
+       NTSTATUS status;
+       u8 _buf[1024] _aligned_attribute(4);
+       u8 *buf = _buf;
+       const FILE_FULL_EA_INFORMATION *ea;
+       struct wim_xattr_entry *entry;
+       int ret;
+
+
+       /*
+        * EaSize from FILE_EA_INFORMATION is apparently supposed to give the
+        * size of the buffer required for NtQueryEaFile(), but it doesn't
+        * actually work correctly; it can be off by about 4 bytes per xattr.
+        *
+        * So just start out by doubling the advertised size, and also handle
+        * STATUS_BUFFER_OVERFLOW just in case.
+        */
+retry:
+       if (unlikely(ea_size * 2 < ea_size))
+               ea_size = UINT32_MAX;
+       else
+               ea_size *= 2;
+       if (unlikely(ea_size > sizeof(_buf))) {
+               buf = MALLOC(ea_size);
+               if (!buf) {
+                       if (ea_size >= (1 << 20)) {
+                               WARNING("\"%ls\": EaSize was extremely large (%u)",
+                                       printable_path(ctx), ea_size);
+                       }
+                       return WIMLIB_ERR_NOMEM;
+               }
+       }
+
+       status = NtQueryEaFile(h, &iosb, buf, ea_size,
+                              FALSE, NULL, 0, NULL, TRUE);
+
+       if (unlikely(!NT_SUCCESS(status))) {
+               if (status == STATUS_BUFFER_OVERFLOW) {
+                       if (buf != _buf) {
+                               FREE(buf);
+                               buf = NULL;
+                       }
+                       goto retry;
+               }
+               if (status == STATUS_NO_EAS_ON_FILE) {
+                       /*
+                        * FILE_EA_INFORMATION.EaSize was nonzero so this
+                        * shouldn't happen, but just in case...
+                        */
+                       ret = 0;
+                       goto out;
+               }
+               winnt_error(status, L"\"%ls\": Can't read extended attributes",
+                           printable_path(ctx));
+               ret = WIMLIB_ERR_STAT;
+               goto out;
+       }
+
+       ea = (const FILE_FULL_EA_INFORMATION *)buf;
+       entry = (struct wim_xattr_entry *)buf;
+       for (;;) {
+               /*
+                * wim_xattr_entry is not larger than FILE_FULL_EA_INFORMATION,
+                * so we can reuse the same buffer by overwriting the
+                * FILE_FULL_EA_INFORMATION with the wim_xattr_entry in-place.
+                */
+               FILE_FULL_EA_INFORMATION _ea;
+
+               STATIC_ASSERT(offsetof(struct wim_xattr_entry, name) <=
+                             offsetof(FILE_FULL_EA_INFORMATION, EaName));
+               wimlib_assert((u8 *)entry <= (const u8 *)ea);
+
+               memcpy(&_ea, ea, sizeof(_ea));
+
+               entry->value_len = cpu_to_le16(_ea.EaValueLength);
+               entry->name_len = _ea.EaNameLength;
+               entry->flags = _ea.Flags;
+               memmove(entry->name, ea->EaName, _ea.EaNameLength);
+               entry->name[_ea.EaNameLength] = '\0';
+               memmove(&entry->name[_ea.EaNameLength + 1],
+                       &ea->EaName[_ea.EaNameLength + 1], _ea.EaValueLength);
+               entry = (struct wim_xattr_entry *)
+                        &entry->name[_ea.EaNameLength + 1 + _ea.EaValueLength];
+               if (_ea.NextEntryOffset == 0)
+                       break;
+               ea = (const FILE_FULL_EA_INFORMATION *)
+                       ((const u8 *)ea + _ea.NextEntryOffset);
+       }
+       wimlib_assert((u8 *)entry - buf <= ea_size);
+
+       ret = WIMLIB_ERR_NOMEM;
+       if (!inode_set_xattrs(inode, buf, (u8 *)entry - buf))
+               goto out;
+       ret = 0;
+out:
+       if (unlikely(buf != _buf))
+               FREE(buf);
+       return ret;
+}
+
 static int
 winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                  HANDLE cur_dir,
 static int
 winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                                  HANDLE cur_dir,
@@ -1518,6 +1624,7 @@ struct file_info {
        u64 last_access_time;
        u64 ino;
        u64 end_of_file;
        u64 last_access_time;
        u64 ino;
        u64 end_of_file;
+       u32 ea_size;
 };
 
 static noinline_for_stack NTSTATUS
 };
 
 static noinline_for_stack NTSTATUS
@@ -1540,6 +1647,7 @@ get_file_info(HANDLE h, struct file_info *info)
        info->last_access_time = all_info.BasicInformation.LastAccessTime.QuadPart;
        info->ino = all_info.InternalInformation.IndexNumber.QuadPart;
        info->end_of_file = all_info.StandardInformation.EndOfFile.QuadPart;
        info->last_access_time = all_info.BasicInformation.LastAccessTime.QuadPart;
        info->ino = all_info.InternalInformation.IndexNumber.QuadPart;
        info->end_of_file = all_info.StandardInformation.EndOfFile.QuadPart;
+       info->ea_size = all_info.EaInformation.EaSize;
        return STATUS_SUCCESS;
 }
 
        return STATUS_SUCCESS;
 }
 
@@ -1620,8 +1728,8 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
         * this permission on all nondirectories.  Perhaps it causes Windows to
         * start prefetching the file contents...  */
        status = winnt_openat(cur_dir, relative_path, relative_path_nchars,
         * this permission on all nondirectories.  Perhaps it causes Windows to
         * start prefetching the file contents...  */
        status = winnt_openat(cur_dir, relative_path, relative_path_nchars,
-                             FILE_READ_ATTRIBUTES | READ_CONTROL |
-                                       ACCESS_SYSTEM_SECURITY,
+                             FILE_READ_ATTRIBUTES | FILE_READ_EA |
+                               READ_CONTROL | ACCESS_SYSTEM_SECURITY,
                              &h);
        if (unlikely(!NT_SUCCESS(status))) {
                if (status == STATUS_DELETE_PENDING) {
                              &h);
        if (unlikely(!NT_SUCCESS(status))) {
                if (status == STATUS_DELETE_PENDING) {
@@ -1717,6 +1825,13 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        if (ret)
                goto out;
 
        if (ret)
                goto out;
 
+       /* Get the file's extended attributes.  */
+       if (unlikely(file_info.ea_size != 0)) {
+               ret = winnt_load_xattrs(h, inode, ctx, file_info.ea_size);
+               if (ret)
+                       goto out;
+       }
+
        /* If this is a reparse point, load the reparse data.  */
        if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
                ret = winnt_load_reparse_data(h, inode, ctx);
        /* If this is a reparse point, load the reparse data.  */
        if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
                ret = winnt_load_reparse_data(h, inode, ctx);
@@ -1961,6 +2076,10 @@ typedef struct {
 #define NTFS_IS_SPECIAL_FILE(ino)                      \
        (NTFS_MFT_NO(ino) <= 15 && !NTFS_IS_ROOT_FILE(ino))
 
 #define NTFS_IS_SPECIAL_FILE(ino)                      \
        (NTFS_MFT_NO(ino) <= 15 && !NTFS_IS_ROOT_FILE(ino))
 
+#define NTFS_SPECIAL_STREAM_OBJECT_ID          0x00000001
+#define NTFS_SPECIAL_STREAM_EA                 0x00000002
+#define NTFS_SPECIAL_STREAM_EA_INFORMATION     0x00000004
+
 /* Intermediate inode structure.  This is used to temporarily save information
  * from FSCTL_QUERY_FILE_LAYOUT before creating the full 'struct wim_inode'.  */
 struct ntfs_inode {
 /* Intermediate inode structure.  This is used to temporarily save information
  * from FSCTL_QUERY_FILE_LAYOUT before creating the full 'struct wim_inode'.  */
 struct ntfs_inode {
@@ -1973,8 +2092,8 @@ struct ntfs_inode {
        u32 attributes;
        u32 security_id;
        u32 num_aliases;
        u32 attributes;
        u32 security_id;
        u32 num_aliases;
-       u32 num_streams : 31;
-       u32 have_object_id : 1;
+       u32 num_streams;
+       u32 special_streams;
        u32 first_stream_offset;
        struct ntfs_dentry *first_child;
        wchar_t short_name[13];
        u32 first_stream_offset;
        struct ntfs_dentry *first_child;
        wchar_t short_name[13];
@@ -2145,13 +2264,10 @@ is_valid_stream_entry(const STREAM_LAYOUT_ENTRY *stream)
                         stream->StreamIdentifierLength / 2);
 }
 
                         stream->StreamIdentifierLength / 2);
 }
 
-static bool
-is_object_id_stream(const STREAM_LAYOUT_ENTRY *stream)
-{
-       return stream->StreamIdentifierLength == 24 &&
-               !wmemcmp(stream->StreamIdentifier, L"::$OBJECT_ID", 12);
-}
-
+/* assumes that 'id' is a wide string literal */
+#define stream_has_identifier(stream, id)                              \
+       ((stream)->StreamIdentifierLength == sizeof(id) - 2 &&          \
+        !memcmp((stream)->StreamIdentifier, id, sizeof(id) - 2))
 /*
  * If the specified STREAM_LAYOUT_ENTRY represents a DATA stream as opposed to
  * some other type of NTFS stream such as a STANDARD_INFORMATION stream, return
 /*
  * If the specified STREAM_LAYOUT_ENTRY represents a DATA stream as opposed to
  * some other type of NTFS stream such as a STANDARD_INFORMATION stream, return
@@ -2188,16 +2304,18 @@ use_stream(const FILE_LAYOUT_ENTRY *file, const STREAM_LAYOUT_ENTRY *stream,
 
 /* Validate the STREAM_LAYOUT_ENTRYs of the specified file and compute the total
  * length in bytes of the ntfs_stream structures needed to hold the stream
 
 /* Validate the STREAM_LAYOUT_ENTRYs of the specified file and compute the total
  * length in bytes of the ntfs_stream structures needed to hold the stream
- * information.  In addition, set *have_object_id_ret=true if the file has an
- * object ID stream.  */
+ * information.  In addition, set *special_streams_ret to a bitmask of special
+ * stream types that were found.  */
 static int
 validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file,
                                          size_t *total_length_ret,
 static int
 validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file,
                                          size_t *total_length_ret,
-                                         bool *have_object_id_ret)
+                                         u32 *special_streams_ret)
 {
        const STREAM_LAYOUT_ENTRY *stream =
                (const void *)file + file->FirstStreamOffset;
        size_t total = 0;
 {
        const STREAM_LAYOUT_ENTRY *stream =
                (const void *)file + file->FirstStreamOffset;
        size_t total = 0;
+       u32 special_streams = 0;
+
        for (;;) {
                const wchar_t *name;
                size_t name_nchars;
        for (;;) {
                const wchar_t *name;
                size_t name_nchars;
@@ -2217,8 +2335,12 @@ validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file,
                if (use_stream(file, stream, &name, &name_nchars)) {
                        total += ALIGN(sizeof(struct ntfs_stream) +
                                       (name_nchars + 1) * sizeof(wchar_t), 8);
                if (use_stream(file, stream, &name, &name_nchars)) {
                        total += ALIGN(sizeof(struct ntfs_stream) +
                                       (name_nchars + 1) * sizeof(wchar_t), 8);
-               } else if (is_object_id_stream(stream)) {
-                       *have_object_id_ret = true;
+               } else if (stream_has_identifier(stream, L"::$OBJECT_ID")) {
+                       special_streams |= NTFS_SPECIAL_STREAM_OBJECT_ID;
+               } else if (stream_has_identifier(stream, L"::$EA")) {
+                       special_streams |= NTFS_SPECIAL_STREAM_EA;
+               } else if (stream_has_identifier(stream, L"::$EA_INFORMATION")) {
+                       special_streams |= NTFS_SPECIAL_STREAM_EA_INFORMATION;
                }
                if (stream->NextStreamOffset == 0)
                        break;
                }
                if (stream->NextStreamOffset == 0)
                        break;
@@ -2226,6 +2348,7 @@ validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file,
        }
 
        *total_length_ret = total;
        }
 
        *total_length_ret = total;
+       *special_streams_ret = special_streams;
        return 0;
 }
 
        return 0;
 }
 
@@ -2322,7 +2445,7 @@ load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map)
        size_t n;
        int ret;
        void *p;
        size_t n;
        int ret;
        void *p;
-       bool have_object_id = false;
+       u32 special_streams = 0;
 
        inode_size = ALIGN(sizeof(struct ntfs_inode), 8);
 
 
        inode_size = ALIGN(sizeof(struct ntfs_inode), 8);
 
@@ -2340,7 +2463,7 @@ load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map)
 
        if (file_has_streams(file)) {
                ret = validate_streams_and_compute_total_length(file, &n,
 
        if (file_has_streams(file)) {
                ret = validate_streams_and_compute_total_length(file, &n,
-                                                               &have_object_id);
+                                                               &special_streams);
                if (ret)
                        return ret;
                inode_size += n;
                if (ret)
                        return ret;
                inode_size += n;
@@ -2358,7 +2481,7 @@ load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map)
        ni->last_write_time = info->BasicInformation.LastWriteTime;
        ni->last_access_time = info->BasicInformation.LastAccessTime;
        ni->security_id = info->SecurityId;
        ni->last_write_time = info->BasicInformation.LastWriteTime;
        ni->last_access_time = info->BasicInformation.LastAccessTime;
        ni->security_id = info->SecurityId;
-       ni->have_object_id = have_object_id;
+       ni->special_streams = special_streams;
 
        p = FIRST_DENTRY(ni);
 
 
        p = FIRST_DENTRY(ni);
 
@@ -2612,7 +2735,7 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
         * but not from FSCTL_QUERY_FILE_LAYOUT.  */
        if (ni->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
                              FILE_ATTRIBUTE_ENCRYPTED) ||
         * but not from FSCTL_QUERY_FILE_LAYOUT.  */
        if (ni->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
                              FILE_ATTRIBUTE_ENCRYPTED) ||
-           ni->have_object_id)
+           ni->special_streams != 0)
        {
                ret = winnt_build_dentry_tree_recursive(&root,
                                                        NULL,
        {
                ret = winnt_build_dentry_tree_recursive(&root,
                                                        NULL,
index 790f5d41b179dbd3c979fb31eb209782ca93b3c8..07cfc6bbb85d62d32518ed71a6d6e7c27409ef9d 100644 (file)
@@ -18,3 +18,6 @@ This should still be extracted successfully.
 empty_dacl.wim:  This WIM has an image containing file with a security
 descriptor having an empty DACL.  This is valid and should be extracted
 successfully.
 empty_dacl.wim:  This WIM has an image containing file with a security
 descriptor having an empty DACL.  This is valid and should be extracted
 successfully.
+
+linux_xattrs_old.wim: Includes Linux xattrs in old format
+(TAG_WIMLIB_LINUX_XATTRS)
diff --git a/tests/wims/linux_xattrs_old.wim b/tests/wims/linux_xattrs_old.wim
new file mode 100644 (file)
index 0000000..44e1e12
Binary files /dev/null and b/tests/wims/linux_xattrs_old.wim differ
index 080852fe37185384b257fa63a4f89d9f000f01e9..dc62d44d3b64ff688384c1b06e02df37c8a72cfa 100644 (file)
@@ -826,6 +826,7 @@ unregister_all_backing_wims(const tchar drive_letter)
                             overlay_list, 32768, &bytes_returned, NULL))
        {
                ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
                             overlay_list, 32768, &bytes_returned, NULL))
        {
                ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
+                      GetLastError() == ERROR_INVALID_PARAMETER ||
                       GetLastError() == ERROR_FILE_NOT_FOUND,
                       "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError());
                return;
                       GetLastError() == ERROR_FILE_NOT_FOUND,
                       "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError());
                return;