1 /*
2 * Read a squashfs filesystem. This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2010, 2012, 2013
6 * Phillip Lougher <phillip@squashfs.org.uk>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * read_xattrs.c
23 */
24
25 /*
26 * Common xattr read code shared between mksquashfs and unsquashfs
27 */
28
29 #define TRUE 1
30 #define FALSE 0
31 #include <stdio.h>
32 #include <string.h>
33
34 #ifndef linux
35 #define __BYTE_ORDER BYTE_ORDER
36 #define __BIG_ENDIAN BIG_ENDIAN
37 #define __LITTLE_ENDIAN LITTLE_ENDIAN
38 #else
39 #include <endian.h>
40 #endif
41
42 #include <stdlib.h>
43
44 #include "squashfs_fs.h"
45 #include "squashfs_swap.h"
46 #include "xattr.h"
47 #include "error.h"
48
49 extern int read_fs_bytes(int, long long, int, void *);
50 extern int read_block(int, long long, long long *, int, void *);
51
52 static struct hash_entry {
53 long long start;
54 unsigned int offset;
55 struct hash_entry *next;
56 } *hash_table[65536];
57
58 static struct squashfs_xattr_id *xattr_ids;
59 static void *xattrs = NULL;
60 static long long xattr_table_start;
61
62 /*
63 * Prefix lookup table, storing mapping to/from prefix string and prefix id
64 */
65 struct prefix prefix_table[] = {
66 { "user.", SQUASHFS_XATTR_USER },
67 { "trusted.", SQUASHFS_XATTR_TRUSTED },
68 { "security.", SQUASHFS_XATTR_SECURITY },
69 { "", -1 }
70 };
71
72 /*
73 * store mapping from location of compressed block in fs ->
74 * location of uncompressed block in memory
75 */
save_xattr_block(long long start,int offset)76 static void save_xattr_block(long long start, int offset)
77 {
78 struct hash_entry *hash_entry = malloc(sizeof(*hash_entry));
79 int hash = start & 0xffff;
80
81 TRACE("save_xattr_block: start %lld, offset %d\n", start, offset);
82
83 if(hash_entry == NULL)
84 MEM_ERROR();
85
86 hash_entry->start = start;
87 hash_entry->offset = offset;
88 hash_entry->next = hash_table[hash];
89 hash_table[hash] = hash_entry;
90 }
91
92
93 /*
94 * map from location of compressed block in fs ->
95 * location of uncompressed block in memory
96 */
get_xattr_block(long long start)97 static int get_xattr_block(long long start)
98 {
99 int hash = start & 0xffff;
100 struct hash_entry *hash_entry = hash_table[hash];
101
102 for(; hash_entry; hash_entry = hash_entry->next)
103 if(hash_entry->start == start)
104 break;
105
106 TRACE("get_xattr_block: start %lld, offset %d\n", start,
107 hash_entry ? hash_entry->offset : -1);
108
109 return hash_entry ? hash_entry->offset : -1;
110 }
111
112
113 /*
114 * construct the xattr_list entry from the fs xattr, including
115 * mapping name and prefix into a full name
116 */
read_xattr_entry(struct xattr_list * xattr,struct squashfs_xattr_entry * entry,void * name)117 static int read_xattr_entry(struct xattr_list *xattr,
118 struct squashfs_xattr_entry *entry, void *name)
119 {
120 int i, len, type = entry->type & XATTR_PREFIX_MASK;
121
122 for(i = 0; prefix_table[i].type != -1; i++)
123 if(prefix_table[i].type == type)
124 break;
125
126 if(prefix_table[i].type == -1) {
127 ERROR("Unrecognised type in read_xattr_entry\n");
128 return 0;
129 }
130
131 len = strlen(prefix_table[i].prefix);
132 xattr->full_name = malloc(len + entry->size + 1);
133 if(xattr->full_name == NULL)
134 MEM_ERROR();
135
136 memcpy(xattr->full_name, prefix_table[i].prefix, len);
137 memcpy(xattr->full_name + len, name, entry->size);
138 xattr->full_name[len + entry->size] = '\0';
139 xattr->name = xattr->full_name + len;
140 xattr->size = entry->size;
141 xattr->type = type;
142
143 return 1;
144 }
145
146
147 /*
148 * Read and decompress the xattr id table and the xattr metadata.
149 * This is cached in memory for later use by get_xattr()
150 */
read_xattrs_from_disk(int fd,struct squashfs_super_block * sBlk)151 int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk)
152 {
153 int res, bytes, i, indexes, index_bytes, ids;
154 long long *index, start, end;
155 struct squashfs_xattr_table id_table;
156
157 TRACE("read_xattrs_from_disk\n");
158
159 if(sBlk->xattr_id_table_start == SQUASHFS_INVALID_BLK)
160 return SQUASHFS_INVALID_BLK;
161
162 /*
163 * Read xattr id table, containing start of xattr metadata and the
164 * number of xattrs in the file system
165 */
166 res = read_fs_bytes(fd, sBlk->xattr_id_table_start, sizeof(id_table),
167 &id_table);
168 if(res == 0)
169 return 0;
170
171 SQUASHFS_INSWAP_XATTR_TABLE(&id_table);
172
173 /*
174 * Allocate and read the index to the xattr id table metadata
175 * blocks
176 */
177 ids = id_table.xattr_ids;
178 xattr_table_start = id_table.xattr_table_start;
179 index_bytes = SQUASHFS_XATTR_BLOCK_BYTES(ids);
180 indexes = SQUASHFS_XATTR_BLOCKS(ids);
181 index = malloc(index_bytes);
182 if(index == NULL)
183 MEM_ERROR();
184
185 res = read_fs_bytes(fd, sBlk->xattr_id_table_start + sizeof(id_table),
186 index_bytes, index);
187 if(res ==0)
188 goto failed1;
189
190 SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
191
192 /*
193 * Allocate enough space for the uncompressed xattr id table, and
194 * read and decompress it
195 */
196 bytes = SQUASHFS_XATTR_BYTES(ids);
197 xattr_ids = malloc(bytes);
198 if(xattr_ids == NULL)
199 MEM_ERROR();
200
201 for(i = 0; i < indexes; i++) {
202 int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
203 bytes & (SQUASHFS_METADATA_SIZE - 1);
204 int length = read_block(fd, index[i], NULL, expected,
205 ((unsigned char *) xattr_ids) +
206 (i * SQUASHFS_METADATA_SIZE));
207 TRACE("Read xattr id table block %d, from 0x%llx, length "
208 "%d\n", i, index[i], length);
209 if(length == 0) {
210 ERROR("Failed to read xattr id table block %d, "
211 "from 0x%llx, length %d\n", i, index[i],
212 length);
213 goto failed2;
214 }
215 }
216
217 /*
218 * Read and decompress the xattr metadata
219 *
220 * Note the first xattr id table metadata block is immediately after
221 * the last xattr metadata block, so we can use index[0] to work out
222 * the end of the xattr metadata
223 */
224 start = xattr_table_start;
225 end = index[0];
226 for(i = 0; start < end; i++) {
227 int length;
228 xattrs = realloc(xattrs, (i + 1) * SQUASHFS_METADATA_SIZE);
229 if(xattrs == NULL)
230 MEM_ERROR();
231
232 /* store mapping from location of compressed block in fs ->
233 * location of uncompressed block in memory */
234 save_xattr_block(start, i * SQUASHFS_METADATA_SIZE);
235
236 length = read_block(fd, start, &start, 0,
237 ((unsigned char *) xattrs) +
238 (i * SQUASHFS_METADATA_SIZE));
239 TRACE("Read xattr block %d, length %d\n", i, length);
240 if(length == 0) {
241 ERROR("Failed to read xattr block %d\n", i);
242 goto failed3;
243 }
244
245 /*
246 * If this is not the last metadata block in the xattr metadata
247 * then it should be SQUASHFS_METADATA_SIZE in size.
248 * Note, we can't use expected in read_block() above for this
249 * because we don't know if this is the last block until
250 * after reading.
251 */
252 if(start != end && length != SQUASHFS_METADATA_SIZE) {
253 ERROR("Xattr block %d should be %d bytes in length, "
254 "it is %d bytes\n", i, SQUASHFS_METADATA_SIZE,
255 length);
256 goto failed3;
257 }
258 }
259
260 /* swap if necessary the xattr id entries */
261 for(i = 0; i < ids; i++)
262 SQUASHFS_INSWAP_XATTR_ID(&xattr_ids[i]);
263
264 free(index);
265
266 return ids;
267
268 failed3:
269 free(xattrs);
270 failed2:
271 free(xattr_ids);
272 failed1:
273 free(index);
274
275 return 0;
276 }
277
278
free_xattr(struct xattr_list * xattr_list,int count)279 void free_xattr(struct xattr_list *xattr_list, int count)
280 {
281 int i;
282
283 for(i = 0; i < count; i++)
284 free(xattr_list[i].full_name);
285
286 free(xattr_list);
287 }
288
289
290 /*
291 * Construct and return the list of xattr name:value pairs for the passed xattr
292 * id
293 *
294 * There are two users for get_xattr(), Mksquashfs uses it to read the
295 * xattrs from the filesystem on appending, and Unsquashfs uses it
296 * to retrieve the xattrs for writing to disk.
297 *
298 * Unfortunately, the two users disagree on what to do with unknown
299 * xattr prefixes, Mksquashfs wants to treat this as fatal otherwise
300 * this will cause xattrs to be be lost on appending. Unsquashfs
301 * on the otherhand wants to retrieve the xattrs which are known and
302 * to ignore the rest, this allows Unsquashfs to cope more gracefully
303 * with future versions which may have unknown xattrs, as long as the
304 * general xattr structure is adhered to, Unsquashfs should be able
305 * to safely ignore unknown xattrs, and to write the ones it knows about,
306 * this is better than completely refusing to retrieve all the xattrs.
307 *
308 * If ignore is TRUE then don't treat unknown xattr prefixes as
309 * a failure to read the xattr.
310 */
get_xattr(int i,unsigned int * count,int ignore)311 struct xattr_list *get_xattr(int i, unsigned int *count, int ignore)
312 {
313 long long start;
314 struct xattr_list *xattr_list = NULL;
315 unsigned int offset;
316 void *xptr;
317 int j = 0, res = 1;
318
319 TRACE("get_xattr\n");
320
321 *count = xattr_ids[i].count;
322 start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start;
323 offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr);
324 xptr = xattrs + get_xattr_block(start) + offset;
325
326 TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i,
327 *count, start, offset);
328
329 while(j < *count) {
330 struct squashfs_xattr_entry entry;
331 struct squashfs_xattr_val val;
332
333 if(res != 0) {
334 xattr_list = realloc(xattr_list, (j + 1) *
335 sizeof(struct xattr_list));
336 if(xattr_list == NULL)
337 MEM_ERROR();
338 }
339
340 SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry);
341 xptr += sizeof(entry);
342
343 res = read_xattr_entry(&xattr_list[j], &entry, xptr);
344 if(ignore && res == 0) {
345 /* unknown prefix, but ignore flag is set */
346 (*count) --;
347 continue;
348 }
349
350 if(res != 1)
351 goto failed;
352
353 xptr += entry.size;
354
355 TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j,
356 entry.type, entry.size, xattr_list[j].full_name);
357
358 if(entry.type & SQUASHFS_XATTR_VALUE_OOL) {
359 long long xattr;
360 void *ool_xptr;
361
362 xptr += sizeof(val);
363 SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1);
364 xptr += sizeof(xattr);
365 start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start;
366 offset = SQUASHFS_XATTR_OFFSET(xattr);
367 ool_xptr = xattrs + get_xattr_block(start) + offset;
368 SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val);
369 xattr_list[j].value = ool_xptr + sizeof(val);
370 } else {
371 SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
372 xattr_list[j].value = xptr + sizeof(val);
373 xptr += sizeof(val) + val.vsize;
374 }
375
376 TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize);
377
378 xattr_list[j ++].vsize = val.vsize;
379 }
380
381 if(*count == 0)
382 goto failed;
383
384 return xattr_list;
385
386 failed:
387 free_xattr(xattr_list, j);
388
389 return NULL;
390 }
391