.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
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
.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]
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
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.
unsigned long object_ids;
unsigned long timestamps;
unsigned long case_sensitive_filenames;
- unsigned long linux_xattrs;
+ unsigned long xattrs;
};
struct blob_descriptor;
/* 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] Linux-style xattrs */
+/*
+ * [wimlib extension] Linux-style extended attributes
+ * (deprecated in favor of TAG_XATTRS)
+ */
#define TAG_WIMLIB_LINUX_XATTRS 0x337DD874
extern void *
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 */
#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)
-# define HAVE_XATTR_SUPPORT 1
+# define HAVE_LINUX_XATTR_SUPPORT 1
#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 */
/* 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];
- /* 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
-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);
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
-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 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
-is_security_xattr(const char *name)
+is_linux_security_xattr(const char *name)
{
#define XATTR_SECURITY_PREFIX "security."
#define XATTR_SYSTEM_PREFIX "system."
}
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
-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
-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 */
*/
/*
- * 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
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. */
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("")));
}
*/
/*
- * 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
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.";
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;
- 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;
- 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++) {
} while (*p == '\0');
p++;
}
+ *p++ = '\0';
}
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;
- 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;
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;
- res = cmp_u32(name_len1, name_len2);
+ res = entry1->name_len - entry2->name_len;
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,
- 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;
- 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)) {
}
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;
- 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;
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;
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,
- 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",
- 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;
}
}
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;
*/
/*
- * 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
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;
}
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,
- 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;
- 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;
- 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);
}
- ERROR("\"%s\": extended attribute is corrupt", path);
+ ERROR("\"%s\": extended attribute is corrupt or unsupported",
+ path);
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);
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))
{
}
return 0;
}
-#endif /* HAVE_XATTR_SUPPORT */
+#endif /* HAVE_LINUX_XATTR_SUPPORT */
/*
* Apply UNIX-specific metadata to a file if available. This includes standard
{
bool have_dat;
struct wimlib_unix_data dat;
-#ifdef HAVE_XATTR_SUPPORT
+#ifdef HAVE_LINUX_XATTR_SUPPORT
const void *entries;
u32 entries_size;
+ bool is_old_format;
#endif
int ret;
}
}
-#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,
- entries, entries_size);
+ entries, entries_size, is_old_format);
if (ret)
return ret;
}
*/
/*
- * 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
# 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
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;
- 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;
}
+ 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;
}
- 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) {
}
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);
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;
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;
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,
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;
*/
/*
- * 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
#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/xattr.h"
+#include "wimlib/xml.h"
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 extended attributes. */
+ unsigned long num_xattr_failures;
+
/* The Windows build number of the image being applied, or 0 if unknown.
*/
u64 windows_build_number;
}
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_EXTENDED_ATTRIBUTES;
/* There's no volume flag for short names, but according to the
* MS documentation they are only user-settable on NTFS. */
supported_features->case_sensitive_filenames = 1;
}
+ if (vol_flags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
+ supported_features->xattrs = 1;
+
return 0;
}
}
}
+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
{
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 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) &&
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);
*/
/*
- * 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
#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;
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,
u64 last_access_time;
u64 ino;
u64 end_of_file;
+ u32 ea_size;
};
static noinline_for_stack NTSTATUS
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;
}
* 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) {
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);
#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 {
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];
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
/* 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,
- bool *have_object_id_ret)
+ u32 *special_streams_ret)
{
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;
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;
}
*total_length_ret = total;
+ *special_streams_ret = special_streams;
return 0;
}
size_t n;
int ret;
void *p;
- bool have_object_id = false;
+ u32 special_streams = 0;
inode_size = ALIGN(sizeof(struct ntfs_inode), 8);
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;
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);
* 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,
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)
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;