X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Ftagged_items.c;h=82beb48d33e1e6cfe9963ac30b13d5d8d48c40c6;hp=1ed3b4a914454b6938dd193bb31b2d400dd8eba3;hb=4fb86d6254e7be4da455fa9da0f1032621bb3c96;hpb=f50557a7095444c554a066b3837c2999ecd1be31 diff --git a/src/tagged_items.c b/src/tagged_items.c index 1ed3b4a9..82beb48d 100644 --- a/src/tagged_items.c +++ b/src/tagged_items.c @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2014 Eric Biggers + * Copyright (C) 2014-2016 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 @@ -26,140 +26,156 @@ # include "config.h" #endif +#include "wimlib/assert.h" #include "wimlib/endianness.h" #include "wimlib/inode.h" -#include "wimlib/types.h" +#include "wimlib/tagged_items.h" #include "wimlib/unix_data.h" -/* 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[]; -}; - -struct object_id_disk { - u8 object_id[16]; - u8 birth_volume_id[16]; - u8 birth_object_id[16]; - u8 domain_id[16]; -}; + /* followed by the item's data */ + u8 data[0]; -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) +/* + * 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; - size_t len_remaining = inode->i_extra_size; - u8 *p = inode->i_extra; - - /* 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); - - /* Length overflow? */ - if (unlikely(len > len_remaining - sizeof(struct tagged_item_header))) + struct tagged_item_header *hdr; + size_t len_remaining; + + if (!inode->i_extra) + return NULL; + + hdr = (struct tagged_item_header *)inode->i_extra->data; + len_remaining = inode->i_extra->size; + + /* 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 (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; - u8 *buf; + 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 + inode->i_extra_size; + wimlib_assert(oldsize % 8 == 0); - buf = MALLOC(newsize); - if (!buf) + extra = REALLOC(inode->i_extra, sizeof(*extra) + newsize); + if (!extra) return NULL; - - if (inode->i_extra_size) { - memcpy(buf + itemsize, inode->i_extra, inode->i_extra_size); - FREE(inode->i_extra); - } - inode->i_extra = buf; - inode->i_extra_size = newsize; - - hdr = (struct tagged_item_header *)buf; + inode->i_extra = extra; + 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; } -static inline struct wimlib_unix_data_disk * -inode_get_unix_data_disk(const struct wim_inode *inode) +/* + * 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_item(struct wim_inode *inode, u32 tag, + const void *data, u32 len) { - return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, - sizeof(struct wimlib_unix_data_disk)); + 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_add_unix_data_disk(struct wim_inode *inode) +inode_get_unix_data_disk(const struct wim_inode *inode) { - return inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, - sizeof(struct wimlib_unix_data_disk)); + return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, + sizeof(struct wimlib_unix_data_disk), + NULL); } -/* 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) @@ -177,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) @@ -193,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;