From 1fbda1dbe72721908b1f99f9f8abf1749e43f4c5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 27 Dec 2016 17:24:55 -0600 Subject: [PATCH] tagged_items updates - Expose tagged_item functions in new header tagged_items.h - Make object_id functions inline functions in object_id.h - Make inode_get_tagged_item() return stored length, not aligned length - Add a new function inode_set_tagged_data() which removes existing items before setting the new one, and use it for inode_set_object_id() - Make inode_add_tagged_item() append item rather than prepend - Keep items 8-byte aligned in memory --- Makefile.am | 1 + include/wimlib/inode.h | 2 +- include/wimlib/object_id.h | 28 +++-- include/wimlib/tagged_items.h | 22 ++++ src/tagged_items.c | 225 +++++++++++++++------------------- 5 files changed, 142 insertions(+), 136 deletions(-) create mode 100644 include/wimlib/tagged_items.h diff --git a/Makefile.am b/Makefile.am index e65172eb..fbaecee8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -143,6 +143,7 @@ libwim_la_SOURCES = \ include/wimlib/security_descriptor.h \ include/wimlib/sha1.h \ include/wimlib/solid.h \ + include/wimlib/tagged_items.h \ include/wimlib/textfile.h \ include/wimlib/timestamp.h \ include/wimlib/types.h \ diff --git a/include/wimlib/inode.h b/include/wimlib/inode.h index 7652ef52..61022098 100644 --- a/include/wimlib/inode.h +++ b/include/wimlib/inode.h @@ -229,7 +229,7 @@ struct wim_inode { /* Optional extra data for a WIM inode */ struct wim_inode_extra { size_t size; /* Size of the extra data in bytes */ - u8 data[]; /* The extra data */ + u8 data[] _aligned_attribute(8); /* The extra data */ }; /* diff --git a/include/wimlib/object_id.h b/include/wimlib/object_id.h index 21c1b4a7..b3fd8abc 100644 --- a/include/wimlib/object_id.h +++ b/include/wimlib/object_id.h @@ -1,15 +1,29 @@ #ifndef _WIMLIB_OBJECT_ID_H #define _WIMLIB_OBJECT_ID_H -#include "wimlib/types.h" +#include "wimlib/tagged_items.h" -extern bool -inode_has_object_id(const struct wim_inode *inode); +/* Unconfirmed: are all 64 bytes of the object ID always present? Since NTFS-3G + * permits shorter object IDs, we'll do the same for now. */ +#define OBJECT_ID_MIN_LENGTH 16 -extern const void * -inode_get_object_id(const struct wim_inode *inode, u32 *len_ret); +static inline const void * +inode_get_object_id(const struct wim_inode *inode, u32 *len_ret) +{ + return inode_get_tagged_item(inode, TAG_OBJECT_ID, OBJECT_ID_MIN_LENGTH, + len_ret); +} -extern bool -inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len); +static inline bool +inode_has_object_id(const struct wim_inode *inode) +{ + return inode_get_object_id(inode, NULL) != NULL; +} + +static inline bool +inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len) +{ + return inode_set_tagged_data(inode, TAG_OBJECT_ID, object_id, len); +} #endif /* _WIMLIB_OBJECT_ID_H */ diff --git a/include/wimlib/tagged_items.h b/include/wimlib/tagged_items.h new file mode 100644 index 00000000..419504ce --- /dev/null +++ b/include/wimlib/tagged_items.h @@ -0,0 +1,22 @@ +#ifndef _WIMLIB_TAGGED_ITEMS_H +#define _WIMLIB_TAGGED_ITEMS_H + +#include "wimlib/types.h" + +struct wim_inode; + +/* Windows-style object ID */ +#define TAG_OBJECT_ID 0x00000001 + +/* [wimlib extension] Standard UNIX metadata: uid, gid, mode, and rdev */ +#define TAG_WIMLIB_UNIX_DATA 0x337DD873 + +extern void * +inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len, + u32 *actual_len_ret); + +extern bool +inode_set_tagged_data(struct wim_inode *inode, u32 tag, + const void *data, u32 len); + +#endif /* _WIMLIB_TAGGED_ITEMS_H */ diff --git a/src/tagged_items.c b/src/tagged_items.c index a851cfd6..95c986b3 100644 --- a/src/tagged_items.c +++ b/src/tagged_items.c @@ -26,129 +26,135 @@ # include "config.h" #endif +#include "wimlib/assert.h" #include "wimlib/endianness.h" #include "wimlib/inode.h" -#include "wimlib/object_id.h" -#include "wimlib/types.h" +#include "wimlib/tagged_items.h" #include "wimlib/unix_data.h" -/* Object ID tag; this is also used by the Microsoft implementation. */ -#define TAG_OBJECT_ID 0x00000001 - -/* Random number that we'll use for tagging our UNIX data items. */ -#define TAG_WIMLIB_UNIX_DATA 0x337DD873 - -/* Header that begins each tagged metadata item in the metadata resource */ +/* + * Header that begins each tagged metadata item associated with a file in a WIM + * metadata resource + */ struct tagged_item_header { - /* Unique identifier for this item. */ + /* identifies the type of metadata item (see TAG_* constants) */ le32 tag; - /* Size of the data of this tagged item, in bytes. This excludes this - * header and should be a multiple of 8. */ + /* size of this item's data in bytes, excluding this header */ le32 length; - /* Variable length data */ - u8 data[]; -}; + /* followed by the item's data */ + u8 data[0]; -/* Unconfirmed: are all 64 bytes of the object ID always present? Since NTFS-3G - * permits shorter object IDs, we'll do the same for now. */ -#define OBJECT_ID_MIN_LENGTH 16 - -struct object_id_disk { - u8 object_id[16]; - u8 birth_volume_id[16]; - u8 birth_object_id[16]; - u8 domain_id[16]; -}; - -struct wimlib_unix_data_disk { - le32 uid; - le32 gid; - le32 mode; - le32 rdev; -}; + /* then zero-padded to an 8-byte boundary */ +} _aligned_attribute(8); -/* Retrieves the first tagged item with the specified tag and minimum length - * from the WIM inode. Returns a pointer to the tagged data, which can be read - * and/or modified in place. Or, if no matching tagged item is found, returns - * NULL. */ -static void * -inode_get_tagged_item(const struct wim_inode *inode, - u32 desired_tag, u32 min_data_len, u32 *actual_len_ret) +/* + * Retrieve from @inode the first metadata item that is tagged with @tag and + * contains at least @min_len bytes of data. If found, return a pointer to the + * item's data and write its actual length to @actual_len_ret if not NULL. If + * not found, return NULL. + */ +void * +inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len, + u32 *actual_len_ret) { - size_t minlen_with_hdr = sizeof(struct tagged_item_header) + min_data_len; + struct tagged_item_header *hdr; size_t len_remaining; - u8 *p; if (!inode->i_extra) return NULL; + hdr = (struct tagged_item_header *)inode->i_extra->data; len_remaining = inode->i_extra->size; - p = inode->i_extra->data; - - /* Iterate through the tagged items. */ - while (len_remaining >= minlen_with_hdr) { - struct tagged_item_header *hdr; - u32 tag; - u32 len; - hdr = (struct tagged_item_header *)p; - tag = le32_to_cpu(hdr->tag); - len = ALIGN(le32_to_cpu(hdr->length), 8); + /* Iterate through the tagged items. */ + while (len_remaining >= sizeof(*hdr) + min_len) { + u32 len = le32_to_cpu(hdr->length); + u32 full_len = sizeof(*hdr) + ALIGN(len, 8); - /* Length overflow? */ - if (unlikely(len > len_remaining - sizeof(struct tagged_item_header))) + /* Length overflow (corrupted item list)? */ + if (unlikely(full_len < len || full_len > len_remaining)) return NULL; - /* Matches the item we wanted? */ - if (tag == desired_tag && len >= min_data_len) { + /* Matches the item we wanted? */ + if (le32_to_cpu(hdr->tag) == tag && len >= min_len) { if (actual_len_ret) *actual_len_ret = len; return hdr->data; } - len_remaining -= sizeof(struct tagged_item_header) + len; - p += sizeof(struct tagged_item_header) + len; + len_remaining -= full_len; + hdr = (struct tagged_item_header *)((u8 *)hdr + full_len); } return NULL; } -/* Adds a tagged item to a WIM inode and returns a pointer to its uninitialized - * data, which must be initialized in-place by the caller. */ +/* + * Add a tagged item to the specified inode and return a pointer to its + * uninitialized data, which the caller must initialize. No check is made for + * whether the inode already has item(s) with the specified tag. + */ static void * inode_add_tagged_item(struct wim_inode *inode, u32 tag, u32 len) { - size_t itemsize; - size_t newsize; struct wim_inode_extra *extra; struct tagged_item_header *hdr; + size_t oldsize = (inode->i_extra ? inode->i_extra->size : 0); + size_t newsize = oldsize + sizeof(*hdr) + ALIGN(len, 8); - /* We prepend the item instead of appending it because it's easier. */ - - itemsize = sizeof(struct tagged_item_header) + ALIGN(len, 8); - newsize = itemsize; - if (inode->i_extra) - newsize += inode->i_extra->size; + wimlib_assert(oldsize % 8 == 0); - extra = MALLOC(sizeof(struct wim_inode_extra) + newsize); + extra = REALLOC(inode->i_extra, sizeof(*extra) + newsize); if (!extra) return NULL; - if (inode->i_extra) { - memcpy(&extra->data[itemsize], inode->i_extra->data, - inode->i_extra->size); - FREE(inode->i_extra); - } - extra->size = newsize; inode->i_extra = extra; - - hdr = (struct tagged_item_header *)extra->data; + extra->size = newsize; + hdr = (struct tagged_item_header *)&extra->data[oldsize]; hdr->tag = cpu_to_le32(tag); hdr->length = cpu_to_le32(len); - return memset(hdr->data, 0, ALIGN(len, 8)); + memset(hdr->data + len, 0, -len & 7); /* pad to next 8-byte boundary */ + return hdr->data; +} + +/* + * Add a tagged item containing the specified data to the specified inode, first + * removing any existing items with the same tag. Returns %true if successful, + * %false if failed (out of memory). + */ +bool +inode_set_tagged_data(struct wim_inode *inode, u32 tag, + const void *data, u32 len) +{ + u8 *p; + u32 old_len; + + /* Remove any existing items with the same tag */ + while ((p = inode_get_tagged_item(inode, tag, 0, &old_len)) != NULL) { + p -= sizeof(struct tagged_item_header); + old_len += sizeof(struct tagged_item_header); + old_len = ALIGN(old_len, 8); + memmove(p, p + old_len, (inode->i_extra->data + + inode->i_extra->size) - (p + old_len)); + inode->i_extra->size -= old_len; + } + + /* Add the new item */ + p = inode_add_tagged_item(inode, tag, len); + if (!p) + return false; + memcpy(p, data, len); + return true; } +struct wimlib_unix_data_disk { + le32 uid; + le32 gid; + le32 mode; + le32 rdev; +}; + static inline struct wimlib_unix_data_disk * inode_get_unix_data_disk(const struct wim_inode *inode) { @@ -157,26 +163,19 @@ inode_get_unix_data_disk(const struct wim_inode *inode) NULL); } -static inline struct wimlib_unix_data_disk * -inode_add_unix_data_disk(struct wim_inode *inode) -{ - return inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, - sizeof(struct wimlib_unix_data_disk)); -} - -/* Returns %true if the specified WIM inode has UNIX data; otherwise %false. - * This is a wimlib extension. */ +/* Return %true iff the specified inode has standard UNIX metadata. */ bool inode_has_unix_data(const struct wim_inode *inode) { return inode_get_unix_data_disk(inode) != NULL; } -/* Retrieves UNIX data from the specified WIM inode. - * This is a wimlib extension. +/* + * Get an inode's standard UNIX metadata. * - * Returns %true and fills @unix_data if the inode has UNIX data. - * Otherwise returns %false. */ + * If the inode has standard UNIX metadata, returns %true and fills @unix_data. + * Otherwise returns %false. + */ bool inode_get_unix_data(const struct wim_inode *inode, struct wimlib_unix_data *unix_data) @@ -194,14 +193,15 @@ inode_get_unix_data(const struct wim_inode *inode, return true; } -/* Sets UNIX data on the specified WIM inode. - * This is a wimlib extension. +/* + * Set an inode's standard UNIX metadata. * * Callers must specify all members in @unix_data. If the inode does not yet - * have UNIX data, it is given these values. Otherwise, only the values that - * also have the corresponding flags in @which set are changed. + * have standard UNIX metadata, it is given these values. Otherwise, only the + * values that also have the corresponding flags in @which set are changed. * - * Returns %true if successful, %false if failed (out of memory). */ + * Returns %true if successful, %false if failed (out of memory). + */ bool inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data, int which) @@ -210,7 +210,8 @@ inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data, p = inode_get_unix_data_disk(inode); if (!p) { - p = inode_add_unix_data_disk(inode); + p = inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, + sizeof(*p)); if (!p) return false; which = UNIX_DATA_ALL; @@ -225,35 +226,3 @@ inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data, p->rdev = cpu_to_le32(unix_data->rdev); return true; } - -/* Return %true iff the specified inode has an object ID. */ -bool -inode_has_object_id(const struct wim_inode *inode) -{ - return inode_get_object_id(inode, NULL) != NULL; -} - -/* Retrieve a pointer to the object ID of the specified inode and write its - * length to @len_ret. Return NULL if the inode does not have an object ID. */ -const void * -inode_get_object_id(const struct wim_inode *inode, u32 *len_ret) -{ - return inode_get_tagged_item(inode, TAG_OBJECT_ID, - OBJECT_ID_MIN_LENGTH, len_ret); -} - -/* Set the inode's object ID to the value specified by @object_id and @len. - * Assumes the inode didn't already have an object ID set. Returns %true if - * successful, %false if failed (out of memory). */ -bool -inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len) -{ - void *p; - - p = inode_add_tagged_item(inode, TAG_OBJECT_ID, len); - if (!p) - return false; - - memcpy(p, object_id, len); - return true; -} -- 2.43.0