Add basic infrastructure for storing xattr items
authorEric Biggers <ebiggers3@gmail.com>
Tue, 27 Dec 2016 23:24:55 +0000 (17:24 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Wed, 28 Dec 2016 04:30:49 +0000 (22:30 -0600)
Define a new tagged metadata item to hold a list of names and values of
Linux-style extended attributes, and prepare for supporting
capture/apply of extended attributes.

I considered making the xattrs a stream instead, referenced from the
tagged item which would just hold a hash.  This would have allowed
xattrs to be deduplicated between files.  However, I ultimately decided
against this because WIMGAPI and older versions of wimlib would discard
the streams on optimize/export, and extraction would be much more
complicated because xattr streams could come up for extraction before
other streams --- which would be especially problematic for symlinks.

Makefile.am
configure.ac
include/wimlib/apply.h
include/wimlib/tagged_items.h
include/wimlib/xattr.h [new file with mode: 0644]
src/extract.c

index fbaecee..f59f841 100644 (file)
@@ -153,6 +153,7 @@ libwim_la_SOURCES =         \
        include/wimlib/wim.h            \
        include/wimlib/write.h          \
        include/wimlib/x86_cpu_features.h       \
+       include/wimlib/xattr.h          \
        include/wimlib/xml.h            \
        include/wimlib/xml_windows.h    \
        include/wimlib/xpress_constants.h
index d7de0b0..38ad3e0 100644 (file)
@@ -59,7 +59,8 @@ AM_CONDITIONAL([WINDOWS_NATIVE_BUILD], [test "$WINDOWS_NATIVE_BUILD" = "yes"])
 
 # Useful functions which we can do without.
 AC_CHECK_FUNCS([futimens utimensat flock mempcpy       \
-               openat fstatat readlinkat fdopendir posix_fallocate])
+               openat fstatat readlinkat fdopendir posix_fallocate \
+               llistxattr lgetxattr fsetxattr lsetxattr])
 
 # Header checks, most of which are only here to satisfy conditional includes
 # made by the libntfs-3g headers.
index 9d9c3c9..cfddd1e 100644 (file)
@@ -31,6 +31,7 @@ struct wim_features {
        unsigned long object_ids;
        unsigned long timestamps;
        unsigned long case_sensitive_filenames;
+       unsigned long linux_xattrs;
 };
 
 struct blob_descriptor;
index 419504c..9626594 100644 (file)
@@ -11,6 +11,9 @@ struct wim_inode;
 /* [wimlib extension] Standard UNIX metadata: uid, gid, mode, and rdev */
 #define TAG_WIMLIB_UNIX_DATA           0x337DD873
 
+/* [wimlib extension] Linux-style xattrs */
+#define TAG_WIMLIB_LINUX_XATTRS                0x337DD874
+
 extern void *
 inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len,
                      u32 *actual_len_ret);
diff --git a/include/wimlib/xattr.h b/include/wimlib/xattr.h
new file mode 100644 (file)
index 0000000..56f7ab8
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef _WIMLIB_XATTR_H
+#define _WIMLIB_XATTR_H
+
+#include <string.h>
+
+#include "wimlib/endianness.h"
+#include "wimlib/tagged_items.h"
+#include "wimlib/util.h"
+
+#undef HAVE_XATTR_SUPPORT
+#if defined(HAVE_SYS_XATTR_H) && \
+       defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR) && \
+       defined(HAVE_FSETXATTR) && defined(HAVE_LSETXATTR)
+#  define HAVE_XATTR_SUPPORT 1
+#endif
+
+/*
+ * 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.
+ */
+struct wimlib_xattr_entry {
+
+       /* length of xattr name in bytes */
+       le16 name_len;
+
+       /* reserved, must be 0 */
+       le16 reserved;
+
+       /* length of xattr value in bytes */
+       le32 value_len;
+
+       /* followed by the xattr name with no terminating null */
+       char name[0];
+
+       /* followed by the xattr value with no terminating null */
+       /* 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)
+{
+       return ALIGN(sizeof(*entry) + le16_to_cpu(entry->name_len) +
+                    le32_to_cpu(entry->value_len), 4);
+}
+
+static inline struct wimlib_xattr_entry *
+xattr_entry_next(const struct wimlib_xattr_entry *entry)
+{
+       return (void *)entry + 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
+valid_xattr_entry(const struct wimlib_xattr_entry *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;
+
+       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)))
+               return false;
+
+       return true;
+}
+
+/* Is the xattr of the specified name security-related? */
+static inline bool
+is_security_xattr(const char *name)
+{
+#define XATTR_SECURITY_PREFIX "security."
+#define XATTR_SYSTEM_PREFIX "system."
+#define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
+#define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
+#define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
+#define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
+
+       return !strncmp(name, XATTR_SECURITY_PREFIX,
+                       sizeof(XATTR_SECURITY_PREFIX) - 1) ||
+              !strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) ||
+              !strcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT);
+}
+
+static inline const void *
+inode_get_linux_xattrs(const struct wim_inode *inode, u32 *len_ret)
+{
+       return inode_get_tagged_item(inode, TAG_WIMLIB_LINUX_XATTRS, 0,
+                                    len_ret);
+}
+
+static inline bool
+inode_has_linux_xattrs(const struct wim_inode *inode)
+{
+       return inode_get_linux_xattrs(inode, NULL) != NULL;
+}
+
+static inline bool
+inode_set_linux_xattrs(struct wim_inode *inode, const void *entries, u32 len)
+{
+       return inode_set_tagged_data(inode, TAG_WIMLIB_LINUX_XATTRS,
+                                    entries, len);
+}
+
+#endif /* _WIMLIB_XATTR_H  */
index a39c1c0..b59e223 100644 (file)
@@ -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
@@ -1224,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.  */
@@ -1361,21 +1364,30 @@ do_feature_check(const struct wim_features *required_features,
                WARNING("Ignoring Windows NT security descriptors of %lu files",
                        required_features->security_descriptors);
 
-       /* UNIX data.  */
+       /* Standard UNIX metadata */
        if ((extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
            required_features->unix_data && !supported_features->unix_data)
        {
-               ERROR("Extraction backend does not support UNIX data!");
+               ERROR("Requested UNIX metadata extraction, but extraction "
+                     "backend does not support it!");
                return WIMLIB_ERR_UNSUPPORTED;
        }
-
        if (required_features->unix_data &&
            !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))
        {
-               WARNING("Ignoring UNIX metadata of %lu files",
+               WARNING("Ignoring UNIX metadata (uid/gid/mode/rdev) of %lu files",
                        required_features->unix_data);
        }
 
+       /* Linux-style extended attributes */
+       if (required_features->linux_xattrs &&
+           (!supported_features->linux_xattrs ||
+            !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)))
+       {
+               WARNING("Ignoring Linux-style extended attributes of %lu files",
+                       required_features->linux_xattrs);
+       }
+
        /* Object IDs.  */
        if (required_features->object_ids && !supported_features->object_ids) {
                WARNING("Ignoring object IDs of %lu files",