a851cfd64058604cbb81e2c90ccc2851a042cbd6
[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/endianness.h"
30 #include "wimlib/inode.h"
31 #include "wimlib/object_id.h"
32 #include "wimlib/types.h"
33 #include "wimlib/unix_data.h"
34
35 /* Object ID tag; this is also used by the Microsoft implementation.  */
36 #define TAG_OBJECT_ID           0x00000001
37
38 /* Random number that we'll use for tagging our UNIX data items.  */
39 #define TAG_WIMLIB_UNIX_DATA    0x337DD873
40
41 /* Header that begins each tagged metadata item in the metadata resource  */
42 struct tagged_item_header {
43
44         /* Unique identifier for this item.  */
45         le32 tag;
46
47         /* Size of the data of this tagged item, in bytes.  This excludes this
48          * header and should be a multiple of 8.  */
49         le32 length;
50
51         /* Variable length data  */
52         u8 data[];
53 };
54
55 /* Unconfirmed: are all 64 bytes of the object ID always present?  Since NTFS-3G
56  * permits shorter object IDs, we'll do the same for now.  */
57 #define OBJECT_ID_MIN_LENGTH    16
58
59 struct object_id_disk {
60         u8 object_id[16];
61         u8 birth_volume_id[16];
62         u8 birth_object_id[16];
63         u8 domain_id[16];
64 };
65
66 struct wimlib_unix_data_disk {
67         le32 uid;
68         le32 gid;
69         le32 mode;
70         le32 rdev;
71 };
72
73 /* Retrieves the first tagged item with the specified tag and minimum length
74  * from the WIM inode.  Returns a pointer to the tagged data, which can be read
75  * and/or modified in place.  Or, if no matching tagged item is found, returns
76  * NULL.  */
77 static void *
78 inode_get_tagged_item(const struct wim_inode *inode,
79                       u32 desired_tag, u32 min_data_len, u32 *actual_len_ret)
80 {
81         size_t minlen_with_hdr = sizeof(struct tagged_item_header) + min_data_len;
82         size_t len_remaining;
83         u8 *p;
84
85         if (!inode->i_extra)
86                 return NULL;
87
88         len_remaining = inode->i_extra->size;
89         p = inode->i_extra->data;
90
91         /* Iterate through the tagged items.  */
92         while (len_remaining >= minlen_with_hdr) {
93                 struct tagged_item_header *hdr;
94                 u32 tag;
95                 u32 len;
96
97                 hdr = (struct tagged_item_header *)p;
98                 tag = le32_to_cpu(hdr->tag);
99                 len = ALIGN(le32_to_cpu(hdr->length), 8);
100
101                 /* Length overflow?  */
102                 if (unlikely(len > len_remaining - sizeof(struct tagged_item_header)))
103                         return NULL;
104
105                 /* Matches the item we wanted?  */
106                 if (tag == desired_tag && len >= min_data_len) {
107                         if (actual_len_ret)
108                                 *actual_len_ret = len;
109                         return hdr->data;
110                 }
111
112                 len_remaining -= sizeof(struct tagged_item_header) + len;
113                 p += sizeof(struct tagged_item_header) + len;
114         }
115         return NULL;
116 }
117
118 /* Adds a tagged item to a WIM inode and returns a pointer to its uninitialized
119  * data, which must be initialized in-place by the caller.  */
120 static void *
121 inode_add_tagged_item(struct wim_inode *inode, u32 tag, u32 len)
122 {
123         size_t itemsize;
124         size_t newsize;
125         struct wim_inode_extra *extra;
126         struct tagged_item_header *hdr;
127
128         /* We prepend the item instead of appending it because it's easier.  */
129
130         itemsize = sizeof(struct tagged_item_header) + ALIGN(len, 8);
131         newsize = itemsize;
132         if (inode->i_extra)
133                 newsize += inode->i_extra->size;
134
135         extra = MALLOC(sizeof(struct wim_inode_extra) + newsize);
136         if (!extra)
137                 return NULL;
138         if (inode->i_extra) {
139                 memcpy(&extra->data[itemsize], inode->i_extra->data,
140                        inode->i_extra->size);
141                 FREE(inode->i_extra);
142         }
143         extra->size = newsize;
144         inode->i_extra = extra;
145
146         hdr = (struct tagged_item_header *)extra->data;
147         hdr->tag = cpu_to_le32(tag);
148         hdr->length = cpu_to_le32(len);
149         return memset(hdr->data, 0, ALIGN(len, 8));
150 }
151
152 static inline struct wimlib_unix_data_disk *
153 inode_get_unix_data_disk(const struct wim_inode *inode)
154 {
155         return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
156                                      sizeof(struct wimlib_unix_data_disk),
157                                      NULL);
158 }
159
160 static inline struct wimlib_unix_data_disk *
161 inode_add_unix_data_disk(struct wim_inode *inode)
162 {
163         return inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
164                                      sizeof(struct wimlib_unix_data_disk));
165 }
166
167 /* Returns %true if the specified WIM inode has UNIX data; otherwise %false.
168  * This is a wimlib extension.  */
169 bool
170 inode_has_unix_data(const struct wim_inode *inode)
171 {
172         return inode_get_unix_data_disk(inode) != NULL;
173 }
174
175 /* Retrieves UNIX data from the specified WIM inode.
176  * This is a wimlib extension.
177  *
178  * Returns %true and fills @unix_data if the inode has UNIX data.
179  * Otherwise returns %false.  */
180 bool
181 inode_get_unix_data(const struct wim_inode *inode,
182                     struct wimlib_unix_data *unix_data)
183 {
184         const struct wimlib_unix_data_disk *p;
185
186         p = inode_get_unix_data_disk(inode);
187         if (!p)
188                 return false;
189
190         unix_data->uid = le32_to_cpu(p->uid);
191         unix_data->gid = le32_to_cpu(p->gid);
192         unix_data->mode = le32_to_cpu(p->mode);
193         unix_data->rdev = le32_to_cpu(p->rdev);
194         return true;
195 }
196
197 /* Sets UNIX data on the specified WIM inode.
198  * This is a wimlib extension.
199  *
200  * Callers must specify all members in @unix_data.  If the inode does not yet
201  * have UNIX data, it is given these values.  Otherwise, only the values that
202  * also have the corresponding flags in @which set are changed.
203  *
204  * Returns %true if successful, %false if failed (out of memory).  */
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_unix_data_disk(inode);
214                 if (!p)
215                         return false;
216                 which = UNIX_DATA_ALL;
217         }
218         if (which & UNIX_DATA_UID)
219                 p->uid = cpu_to_le32(unix_data->uid);
220         if (which & UNIX_DATA_GID)
221                 p->gid = cpu_to_le32(unix_data->gid);
222         if (which & UNIX_DATA_MODE)
223                 p->mode = cpu_to_le32(unix_data->mode);
224         if (which & UNIX_DATA_RDEV)
225                 p->rdev = cpu_to_le32(unix_data->rdev);
226         return true;
227 }
228
229 /* Return %true iff the specified inode has an object ID.  */
230 bool
231 inode_has_object_id(const struct wim_inode *inode)
232 {
233         return inode_get_object_id(inode, NULL) != NULL;
234 }
235
236 /* Retrieve a pointer to the object ID of the specified inode and write its
237  * length to @len_ret.  Return NULL if the inode does not have an object ID.  */
238 const void *
239 inode_get_object_id(const struct wim_inode *inode, u32 *len_ret)
240 {
241         return inode_get_tagged_item(inode, TAG_OBJECT_ID,
242                                      OBJECT_ID_MIN_LENGTH, len_ret);
243 }
244
245 /* Set the inode's object ID to the value specified by @object_id and @len.
246  * Assumes the inode didn't already have an object ID set.  Returns %true if
247  * successful, %false if failed (out of memory).  */
248 bool
249 inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len)
250 {
251         void *p;
252
253         p = inode_add_tagged_item(inode, TAG_OBJECT_ID, len);
254         if (!p)
255                 return false;
256
257         memcpy(p, object_id, len);
258         return true;
259 }