From 0e639be92660b408a20a1875eb1c1d609692999e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 27 Dec 2016 17:24:55 -0600 Subject: [PATCH] Add basic infrastructure for storing xattr items 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 | 1 + configure.ac | 3 +- include/wimlib/apply.h | 1 + include/wimlib/tagged_items.h | 3 + include/wimlib/xattr.h | 124 ++++++++++++++++++++++++++++++++++ src/extract.c | 20 ++++-- 6 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 include/wimlib/xattr.h diff --git a/Makefile.am b/Makefile.am index fbaecee8..f59f8411 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index d7de0b08..38ad3e0f 100644 --- a/configure.ac +++ b/configure.ac @@ -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. diff --git a/include/wimlib/apply.h b/include/wimlib/apply.h index 9d9c3c97..cfddd1ed 100644 --- a/include/wimlib/apply.h +++ b/include/wimlib/apply.h @@ -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; diff --git a/include/wimlib/tagged_items.h b/include/wimlib/tagged_items.h index 419504ce..96265942 100644 --- a/include/wimlib/tagged_items.h +++ b/include/wimlib/tagged_items.h @@ -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 index 00000000..56f7ab87 --- /dev/null +++ b/include/wimlib/xattr.h @@ -0,0 +1,124 @@ +#ifndef _WIMLIB_XATTR_H +#define _WIMLIB_XATTR_H + +#include + +#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 */ diff --git a/src/extract.c b/src/extract.c index a39c1c02..b59e2235 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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", -- 2.43.0