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