]> wimlib.net Git - wimlib/blob - src/tagged_items.c
timestamp.c: correctly convert negative UNIX timestamps
[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         STATIC_ASSERT(sizeof(*hdr) == 8);
67
68         if (!inode->i_extra)
69                 return NULL;
70
71         hdr = (struct tagged_item_header *)inode->i_extra->data;
72         len_remaining = inode->i_extra->size;
73
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);
78
79                 /* Length overflow (corrupted item list)? */
80                 if (unlikely(full_len < len || full_len > len_remaining))
81                         return NULL;
82
83                 /* Matches the item we wanted? */
84                 if (le32_to_cpu(hdr->tag) == tag && len >= min_len) {
85                         if (actual_len_ret)
86                                 *actual_len_ret = len;
87                         return hdr->data;
88                 }
89
90                 len_remaining -= full_len;
91                 hdr = (struct tagged_item_header *)((u8 *)hdr + full_len);
92         }
93         return NULL;
94 }
95
96 /*
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.
100  */
101 static void *
102 inode_add_tagged_item(struct wim_inode *inode, u32 tag, u32 len)
103 {
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);
108
109         wimlib_assert(oldsize % 8 == 0);
110
111         extra = REALLOC(inode->i_extra, sizeof(*extra) + newsize);
112         if (!extra)
113                 return NULL;
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 */
120         return hdr->data;
121 }
122
123 /*
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).
127  */
128 bool
129 inode_set_tagged_item(struct wim_inode *inode, u32 tag,
130                       const void *data, u32 len)
131 {
132         u8 *p;
133         u32 old_len;
134
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;
143         }
144
145         /* Add the new item */
146         p = inode_add_tagged_item(inode, tag, len);
147         if (!p)
148                 return false;
149         memcpy(p, data, len);
150         return true;
151 }
152
153 struct wimlib_unix_data_disk {
154         le32 uid;
155         le32 gid;
156         le32 mode;
157         le32 rdev;
158 };
159
160 static inline struct wimlib_unix_data_disk *
161 inode_get_unix_data_disk(const struct wim_inode *inode)
162 {
163         return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
164                                      sizeof(struct wimlib_unix_data_disk),
165                                      NULL);
166 }
167
168 /* Return %true iff the specified inode has standard UNIX metadata. */
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 /*
176  * Get an inode's standard UNIX metadata.
177  *
178  * If the inode has standard UNIX metadata, returns %true and fills @unix_data.
179  * Otherwise returns %false.
180  */
181 bool
182 inode_get_unix_data(const struct wim_inode *inode,
183                     struct wimlib_unix_data *unix_data)
184 {
185         const struct wimlib_unix_data_disk *p;
186
187         p = inode_get_unix_data_disk(inode);
188         if (!p)
189                 return false;
190
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);
195         return true;
196 }
197
198 /*
199  * Set an inode's standard UNIX metadata.
200  *
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.
204  *
205  * Returns %true if successful, %false if failed (out of memory).
206  */
207 bool
208 inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data,
209                     int which)
210 {
211         struct wimlib_unix_data_disk *p;
212
213         p = inode_get_unix_data_disk(inode);
214         if (!p) {
215                 p = inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA,
216                                           sizeof(*p));
217                 if (!p)
218                         return false;
219                 which = UNIX_DATA_ALL;
220         }
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);
229         return true;
230 }