4 * Support for tagged metadata items that can be appended to WIM directory
9 * Copyright (C) 2014-2016 Eric Biggers
11 * This file is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU Lesser General Public License as published by the Free
13 * Software Foundation; either version 3 of the License, or (at your option) any
16 * This file is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this file; if not, see http://www.gnu.org/licenses/.
29 #include "wimlib/assert.h"
30 #include "wimlib/endianness.h"
31 #include "wimlib/inode.h"
32 #include "wimlib/tagged_items.h"
33 #include "wimlib/unix_data.h"
36 * Header that begins each tagged metadata item associated with a file in a WIM
39 struct tagged_item_header {
41 /* identifies the type of metadata item (see TAG_* constants) */
44 /* size of this item's data in bytes, excluding this header */
47 /* followed by the item's data */
50 /* then zero-padded to an 8-byte boundary */
51 } _aligned_attribute(8);
54 * Retrieve from @inode the first metadata item that is tagged with @tag and
55 * contains at least @min_len bytes of data. If found, return a pointer to the
56 * item's data and write its actual length to @actual_len_ret if not NULL. If
57 * not found, return NULL.
60 inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len,
63 struct tagged_item_header *hdr;
66 STATIC_ASSERT(sizeof(*hdr) == 8);
71 hdr = (struct tagged_item_header *)inode->i_extra->data;
72 len_remaining = inode->i_extra->size;
74 /* Iterate through the tagged items. */
75 while (len_remaining >= sizeof(*hdr) + min_len) {
76 u32 len = le32_to_cpu(hdr->length);
77 u32 full_len = sizeof(*hdr) + ALIGN(len, 8);
79 /* Length overflow (corrupted item list)? */
80 if (unlikely(full_len < len || full_len > len_remaining))
83 /* Matches the item we wanted? */
84 if (le32_to_cpu(hdr->tag) == tag && len >= min_len) {
86 *actual_len_ret = len;
90 len_remaining -= full_len;
91 hdr = (struct tagged_item_header *)((u8 *)hdr + full_len);
97 * Add a tagged item to the specified inode and return a pointer to its
98 * uninitialized data, which the caller must initialize. No check is made for
99 * whether the inode already has item(s) with the specified tag.
102 inode_add_tagged_item(struct wim_inode *inode, u32 tag, u32 len)
104 struct wim_inode_extra *extra;
105 struct tagged_item_header *hdr;
106 size_t oldsize = (inode->i_extra ? inode->i_extra->size : 0);
107 size_t newsize = oldsize + sizeof(*hdr) + ALIGN(len, 8);
109 wimlib_assert(oldsize % 8 == 0);
111 extra = REALLOC(inode->i_extra, sizeof(*extra) + newsize);
114 inode->i_extra = extra;
115 extra->size = newsize;
116 hdr = (struct tagged_item_header *)&extra->data[oldsize];
117 hdr->tag = cpu_to_le32(tag);
118 hdr->length = cpu_to_le32(len);
119 memset(hdr->data + len, 0, -len & 7); /* pad to next 8-byte boundary */
124 * Add a tagged item containing the specified data to the specified inode, first
125 * removing any existing items with the same tag. Returns %true if successful,
126 * %false if failed (out of memory).
129 inode_set_tagged_item(struct wim_inode *inode, u32 tag,
130 const void *data, u32 len)
135 /* Remove any existing items with the same tag */
136 while ((p = inode_get_tagged_item(inode, tag, 0, &old_len)) != NULL) {
137 p -= sizeof(struct tagged_item_header);
138 old_len += sizeof(struct tagged_item_header);
139 old_len = ALIGN(old_len, 8);
140 memmove(p, p + old_len, (inode->i_extra->data +
141 inode->i_extra->size) - (p + old_len));
142 inode->i_extra->size -= old_len;
145 /* Add the new item */
146 p = inode_add_tagged_item(inode, tag, len);
149 memcpy(p, data, len);
153 struct wimlib_unix_data_disk {
160 static inline struct wimlib_unix_data_disk *
161 inode_get_unix_data_disk(const struct wim_inode *inode)
163 return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
164 sizeof(struct wimlib_unix_data_disk),
168 /* Return %true iff the specified inode has standard UNIX metadata. */
170 inode_has_unix_data(const struct wim_inode *inode)
172 return inode_get_unix_data_disk(inode) != NULL;
176 * Get an inode's standard UNIX metadata.
178 * If the inode has standard UNIX metadata, returns %true and fills @unix_data.
179 * Otherwise returns %false.
182 inode_get_unix_data(const struct wim_inode *inode,
183 struct wimlib_unix_data *unix_data)
185 const struct wimlib_unix_data_disk *p;
187 p = inode_get_unix_data_disk(inode);
191 unix_data->uid = le32_to_cpu(p->uid);
192 unix_data->gid = le32_to_cpu(p->gid);
193 unix_data->mode = le32_to_cpu(p->mode);
194 unix_data->rdev = le32_to_cpu(p->rdev);
199 * Set an inode's standard UNIX metadata.
201 * Callers must specify all members in @unix_data. If the inode does not yet
202 * have standard UNIX metadata, it is given these values. Otherwise, only the
203 * values that also have the corresponding flags in @which set are changed.
205 * Returns %true if successful, %false if failed (out of memory).
208 inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data,
211 struct wimlib_unix_data_disk *p;
213 p = inode_get_unix_data_disk(inode);
215 p = inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
219 which = UNIX_DATA_ALL;
221 if (which & UNIX_DATA_UID)
222 p->uid = cpu_to_le32(unix_data->uid);
223 if (which & UNIX_DATA_GID)
224 p->gid = cpu_to_le32(unix_data->gid);
225 if (which & UNIX_DATA_MODE)
226 p->mode = cpu_to_le32(unix_data->mode);
227 if (which & UNIX_DATA_RDEV)
228 p->rdev = cpu_to_le32(unix_data->rdev);