X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Ftagged_items.c;h=82beb48d33e1e6cfe9963ac30b13d5d8d48c40c6;hp=0eee81d91409432f081305ef1395f6dec0e0f9e6;hb=4fb86d6254e7be4da455fa9da0f1032621bb3c96;hpb=6307cde518579ed20e2d6e6488bec204a4a22555 diff --git a/src/tagged_items.c b/src/tagged_items.c index 0eee81d9..82beb48d 100644 --- a/src/tagged_items.c +++ b/src/tagged_items.c @@ -6,165 +6,176 @@ */ /* - * Copyright (C) 2014 Eric Biggers + * Copyright (C) 2014-2016 Eric Biggers * - * This file is part of wimlib, a library for working with WIM files. + * 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 + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. * - * wimlib is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. - * - * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * - * You should have received a copy of the GNU General Public License - * along with wimlib; if not, see http://www.gnu.org/licenses/. + * You should have received a copy of the GNU Lesser General Public License + * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # 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. - * - * (Actually, the MS implementation seems to round this up to an 8 byte - * boundary when calculating the offset to the next tagged item, but - * uses this length unmodified when validating the item. We might as - * well do the same.) */ + /* 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; + struct tagged_item_header *hdr; + size_t len_remaining; - /* Iterate through the tagged items. */ - while (len_remaining >= minlen_with_hdr) { - struct tagged_item_header *hdr; - u32 tag; - u32 len; + if (!inode->i_extra) + return NULL; - hdr = (struct tagged_item_header *)p; - tag = le32_to_cpu(hdr->tag); - len = le32_to_cpu(hdr->length); + hdr = (struct tagged_item_header *)inode->i_extra->data; + len_remaining = inode->i_extra->size; - if (tag == desired_tag && len >= min_data_len) - return hdr->data; + /* 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); - len = (len + 7) & ~7; - if (len_remaining <= sizeof(struct tagged_item_header) + len) + /* Length overflow (corrupted item list)? */ + if (unlikely(full_len < len || full_len > len_remaining)) return NULL; - len_remaining -= sizeof(struct tagged_item_header) + len; - p += sizeof(struct tagged_item_header) + 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 -= 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) + len; - 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); + 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) @@ -182,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) @@ -198,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;