tagged_items updates
[wimlib] / src / tagged_items.c
1 /*
2  * tagged_items.c
3  *
4  * Support for tagged metadata items that can be appended to WIM directory
5  * entries.
6  */
7
8 /*
9  * Copyright (C) 2014-2016 Eric Biggers
10  *
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
14  * later version.
15  *
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
19  * details.
20  *
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/.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28
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"
34
35 /*
36  * Header that begins each tagged metadata item associated with a file in a WIM
37  * metadata resource
38  */
39 struct tagged_item_header {
40
41         /* identifies the type of metadata item (see TAG_* constants) */
42         le32 tag;
43
44         /* size of this item's data in bytes, excluding this header */
45         le32 length;
46
47         /* followed by the item's data */
48         u8 data[0];
49
50         /* then zero-padded to an 8-byte boundary */
51 } _aligned_attribute(8);
52
53 /*
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.
58  */
59 void *
60 inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len,
61                       u32 *actual_len_ret)
62 {
63         struct tagged_item_header *hdr;
64         size_t len_remaining;
65
66         if (!inode->i_extra)
67                 return NULL;
68
69         hdr = (struct tagged_item_header *)inode->i_extra->data;
70         len_remaining = inode->i_extra->size;
71
72         /* Iterate through the tagged items. */
73         while (len_remaining >= sizeof(*hdr) + min_len) {
74                 u32 len = le32_to_cpu(hdr->length);
75                 u32 full_len = sizeof(*hdr) + ALIGN(len, 8);
76
77                 /* Length overflow (corrupted item list)? */
78                 if (unlikely(full_len < len || full_len > len_remaining))
79                         return NULL;
80
81                 /* Matches the item we wanted? */
82                 if (le32_to_cpu(hdr->tag) == tag && len >= min_len) {
83                         if (actual_len_ret)
84                                 *actual_len_ret = len;
85                         return hdr->data;
86                 }
87
88                 len_remaining -= full_len;
89                 hdr = (struct tagged_item_header *)((u8 *)hdr + full_len);
90         }
91         return NULL;
92 }
93
94 /*
95  * Add a tagged item to the specified inode and return a pointer to its
96  * uninitialized data, which the caller must initialize.  No check is made for
97  * whether the inode already has item(s) with the specified tag.
98  */
99 static void *
100 inode_add_tagged_item(struct wim_inode *inode, u32 tag, u32 len)
101 {
102         struct wim_inode_extra *extra;
103         struct tagged_item_header *hdr;
104         size_t oldsize = (inode->i_extra ? inode->i_extra->size : 0);
105         size_t newsize = oldsize + sizeof(*hdr) + ALIGN(len, 8);
106
107         wimlib_assert(oldsize % 8 == 0);
108
109         extra = REALLOC(inode->i_extra, sizeof(*extra) + newsize);
110         if (!extra)
111                 return NULL;
112         inode->i_extra = extra;
113         extra->size = newsize;
114         hdr = (struct tagged_item_header *)&extra->data[oldsize];
115         hdr->tag = cpu_to_le32(tag);
116         hdr->length = cpu_to_le32(len);
117         memset(hdr->data + len, 0, -len & 7); /* pad to next 8-byte boundary */
118         return hdr->data;
119 }
120
121 /*
122  * Add a tagged item containing the specified data to the specified inode, first
123  * removing any existing items with the same tag.  Returns %true if successful,
124  * %false if failed (out of memory).
125  */
126 bool
127 inode_set_tagged_data(struct wim_inode *inode, u32 tag,
128                       const void *data, u32 len)
129 {
130         u8 *p;
131         u32 old_len;
132
133         /* Remove any existing items with the same tag */
134         while ((p = inode_get_tagged_item(inode, tag, 0, &old_len)) != NULL) {
135                 p -= sizeof(struct tagged_item_header);
136                 old_len += sizeof(struct tagged_item_header);
137                 old_len = ALIGN(old_len, 8);
138                 memmove(p, p + old_len, (inode->i_extra->data +
139                                          inode->i_extra->size) - (p + old_len));
140                 inode->i_extra->size -= old_len;
141         }
142
143         /* Add the new item */
144         p = inode_add_tagged_item(inode, tag, len);
145         if (!p)
146                 return false;
147         memcpy(p, data, len);
148         return true;
149 }
150
151 struct wimlib_unix_data_disk {
152         le32 uid;
153         le32 gid;
154         le32 mode;
155         le32 rdev;
156 };
157
158 static inline struct wimlib_unix_data_disk *
159 inode_get_unix_data_disk(const struct wim_inode *inode)
160 {
161         return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
162                                      sizeof(struct wimlib_unix_data_disk),
163                                      NULL);
164 }
165
166 /* Return %true iff the specified inode has standard UNIX metadata. */
167 bool
168 inode_has_unix_data(const struct wim_inode *inode)
169 {
170         return inode_get_unix_data_disk(inode) != NULL;
171 }
172
173 /*
174  * Get an inode's standard UNIX metadata.
175  *
176  * If the inode has standard UNIX metadata, returns %true and fills @unix_data.
177  * Otherwise returns %false.
178  */
179 bool
180 inode_get_unix_data(const struct wim_inode *inode,
181                     struct wimlib_unix_data *unix_data)
182 {
183         const struct wimlib_unix_data_disk *p;
184
185         p = inode_get_unix_data_disk(inode);
186         if (!p)
187                 return false;
188
189         unix_data->uid = le32_to_cpu(p->uid);
190         unix_data->gid = le32_to_cpu(p->gid);
191         unix_data->mode = le32_to_cpu(p->mode);
192         unix_data->rdev = le32_to_cpu(p->rdev);
193         return true;
194 }
195
196 /*
197  * Set an inode's standard UNIX metadata.
198  *
199  * Callers must specify all members in @unix_data.  If the inode does not yet
200  * have standard UNIX metadata, it is given these values.  Otherwise, only the
201  * values that also have the corresponding flags in @which set are changed.
202  *
203  * Returns %true if successful, %false if failed (out of memory).
204  */
205 bool
206 inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data,
207                     int which)
208 {
209         struct wimlib_unix_data_disk *p;
210
211         p = inode_get_unix_data_disk(inode);
212         if (!p) {
213                 p = inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
214                                           sizeof(*p));
215                 if (!p)
216                         return false;
217                 which = UNIX_DATA_ALL;
218         }
219         if (which & UNIX_DATA_UID)
220                 p->uid = cpu_to_le32(unix_data->uid);
221         if (which & UNIX_DATA_GID)
222                 p->gid = cpu_to_le32(unix_data->gid);
223         if (which & UNIX_DATA_MODE)
224                 p->mode = cpu_to_le32(unix_data->mode);
225         if (which & UNIX_DATA_RDEV)
226                 p->rdev = cpu_to_le32(unix_data->rdev);
227         return true;
228 }