]> wimlib.net Git - wimlib/commitdiff
Implement basic handling of xattr streams
authorEric Biggers <ebiggers3@gmail.com>
Mon, 26 Dec 2016 21:14:41 +0000 (15:14 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 27 Dec 2016 03:15:13 +0000 (21:15 -0600)
Add a new stream type to represent a list of Linux-style extended
attributes, and add a new per-file tagged item type to represent the
hash of the xattr stream.

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

index fbaecee818db3a3253fe37934c21fefb5fbccc8f..f59f84110d99df5efa40d8b16032005bbbab1894 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 d7de0b0817356481d1c3b15719616a5646bf72a0..84d35c4ea99184e63f5c1965876fb329c7d7fe55 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])
 
 # Header checks, most of which are only here to satisfy conditional includes
 # made by the libntfs-3g headers.
index 9d9c3c97a5210f35400b298ba6293844550bf2b5..cfddd1ed57ef8b601257de1106a5a09c70a06b50 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 9fc917b57165a24c324c301456c1038a165b698e..7e71d18c115c4debc17d113733ddc08a9609dba6 100644 (file)
@@ -33,6 +33,9 @@ enum wim_inode_stream_type {
         * as well as the encrypted data of all the file's data streams.  */
        STREAM_TYPE_EFSRPC_RAW_DATA,
 
+       /* Extended attributes originating from Linux (wimlib extension) */
+       STREAM_TYPE_LINUX_XATTR,
+
        /* Stream type could not be determined  */
        STREAM_TYPE_UNKNOWN,
 };
@@ -54,6 +57,9 @@ extern const utf16lechar NO_STREAM_NAME[1];
  * one such stream, and it should be unnamed.  However, it is possible for an
  * inode to have both a reparse point stream and an unnamed data stream, and
  * even named data streams as well.
+ *
+ * On Linux, wimlib now also supports storing and restoring extended attributes.
+ * For this an unnamed stream of type STREAM_TYPE_LINUX_XATTR is used.
  */
 struct wim_inode_stream {
 
index 64aae86fde37cca32e9dcaf4cd6bb8fbe14afe84..9e4ed7cc1bd73bbd0d6b6efa724819be8cc7ef7d 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] Hash of stream containing Linux-style xattrs */
+#define TAG_WIMLIB_LINUX_XATTR_HASH    0x337DD874
+
 extern bool
 inode_set_tagged_data(struct wim_inode *inode, u32 tag,
                      const void *data, u32 len);
diff --git a/include/wimlib/xattr.h b/include/wimlib/xattr.h
new file mode 100644 (file)
index 0000000..3a02434
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef _WIMLIB_XATTR_H
+#define _WIMLIB_XATTR_H
+
+#include "wimlib/endianness.h"
+#include "wimlib/sha1.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)
+#  define HAVE_XATTR_SUPPORT 1
+#endif
+
+/*
+ * On-disk format of an entry in an extended attribute stream (wimlib
+ * extension).  An xattr stream consists of a series of variable-length xattr
+ * entries, each of which begins with this entry 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, not counting padding */
+       le32 value_len;
+
+       /* followed by the name with no terminating null */
+       char name[0];
+
+       /*
+        * directly followed by the value, zero-padded to the next 4-byte
+        * boundary if not already aligned
+        */
+       /* u8 value[0]; */
+};
+
+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;
+
+       return avail >= xattr_entry_size(entry);
+}
+
+static inline const u8 *
+inode_get_linux_xattr_hash(const struct wim_inode *inode)
+{
+       return inode_get_tagged_item(inode, TAG_WIMLIB_LINUX_XATTR_HASH,
+                                    SHA1_HASH_SIZE, NULL);
+}
+
+static inline bool
+inode_has_linux_xattr_hash(const struct wim_inode *inode)
+{
+       return inode_get_linux_xattr_hash(inode) != NULL;
+}
+
+static inline bool
+inode_set_linux_xattr_hash(struct wim_inode *inode, const u8 *hash)
+{
+       return inode_set_tagged_data(inode, TAG_WIMLIB_LINUX_XATTR_HASH,
+                                    hash, SHA1_HASH_SIZE);
+}
+
+#endif /* _WIMLIB_XATTR_H  */
index 9e175f7ea5391841ca95e8e26be574e7894b77ac..0bcc86859e64e83993e24ffee29a1662c4982f65 100644 (file)
@@ -68,6 +68,7 @@
 #include "wimlib/endianness.h"
 #include "wimlib/metadata.h"
 #include "wimlib/paths.h"
+#include "wimlib/xattr.h"
 
 /* On-disk format of a WIM dentry (directory entry), located in the metadata
  * resource for a WIM image.  */
@@ -1151,7 +1152,8 @@ assign_stream_types_encrypted(struct wim_inode *inode)
 {
        for (unsigned i = 0; i < inode->i_num_streams; i++) {
                struct wim_inode_stream *strm = &inode->i_streams[i];
-               if (!stream_is_named(strm) && !is_zero_hash(strm->_stream_hash))
+               if (strm->stream_type == STREAM_TYPE_UNKNOWN &&
+                   !stream_is_named(strm) && !is_zero_hash(strm->_stream_hash))
                {
                        strm->stream_type = STREAM_TYPE_EFSRPC_RAW_DATA;
                        return;
@@ -1182,6 +1184,8 @@ assign_stream_types_unencrypted(struct wim_inode *inode)
        for (unsigned i = 0; i < inode->i_num_streams; i++) {
                struct wim_inode_stream *strm = &inode->i_streams[i];
 
+               if (strm->stream_type != STREAM_TYPE_UNKNOWN)
+                       continue;
                if (stream_is_named(strm)) {
                        /* Named data stream  */
                        strm->stream_type = STREAM_TYPE_DATA;
@@ -1223,8 +1227,11 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
                    u64 *offset_p)
 {
        const u8 *orig_p = p;
+       const u8 *linux_xattr_hash = inode_get_linux_xattr_hash(inode);
+       unsigned i;
 
-       inode->i_num_streams = 1 + num_extra_streams;
+       inode->i_num_streams = 1 + num_extra_streams +
+                              (linux_xattr_hash != NULL);
 
        if (unlikely(inode->i_num_streams > ARRAY_LEN(inode->i_embedded_streams))) {
                inode->i_streams = CALLOC(inode->i_num_streams,
@@ -1240,7 +1247,7 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
        inode->i_streams[0].stream_id = 0;
 
        /* Read the extra stream entries  */
-       for (unsigned i = 1; i < inode->i_num_streams; i++) {
+       for (i = 1; i < 1 + num_extra_streams; i++) {
                struct wim_inode_stream *strm;
                const struct wim_extra_stream_entry_on_disk *disk_strm;
                u64 length;
@@ -1300,11 +1307,22 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
                p += length;
        }
 
-       inode->i_next_stream_id = inode->i_num_streams;
+       /* Set up the xattr stream if there is one (wimlib extension) */
+       if (linux_xattr_hash != NULL) {
+               struct wim_inode_stream *strm = &inode->i_streams[i];
+
+               strm->stream_id = i;
+               strm->stream_type = STREAM_TYPE_LINUX_XATTR;
+               strm->stream_name = (utf16lechar *)NO_STREAM_NAME;
+               copy_hash(strm->_stream_hash, linux_xattr_hash);
+               i++;
+       }
+
+       inode->i_next_stream_id = i;
 
        /* Now, assign a type to each stream.  Unfortunately this requires
         * various hacks because stream types aren't explicitly provided in the
-        * WIM on-disk format */
+        * WIM on-disk format (except for the wimlib-specific xattr stream) */
 
        if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED))
                assign_stream_types_encrypted(inode);
index a39c1c02210815b503f9c29e81a1806eda1a114c..e30e10de53cb7ae5f45b13a7e062d7bfef27be72 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
@@ -1125,6 +1126,11 @@ ref_stream_if_needed(struct wim_dentry *dentry, struct wim_inode *inode,
                                need_stream = true;
                }
                break;
+       case STREAM_TYPE_LINUX_XATTR:
+               if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
+                   ctx->supported_features.linux_xattrs)
+                       need_stream = true;
+               break;
        }
        if (need_stream)
                return ref_stream(strm, dentry, ctx);
@@ -1224,6 +1230,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_xattr_hash(inode))
+               features->linux_xattrs++;
 }
 
 /* Tally features necessary to extract a dentry and the corresponding inode.  */
@@ -1361,19 +1369,29 @@ 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("Extraction backend does not support standard UNIX "
+                     "metadata (uid/gid/mode/rdev)!");
                return WIMLIB_ERR_UNSUPPORTED;
        }
 
        if (required_features->unix_data &&
            !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))
        {
-               WARNING("Ignoring UNIX metadata of %lu files",
-                       required_features->unix_data);
+               WARNING("Ignoring standard 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.  */