1/*
2 * Read a squashfs filesystem.  This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2010
6 * Phillip Lougher <phillip@lougher.demon.co.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 "squashfs_fs.h"
43#include "squashfs_swap.h"
44#include "read_fs.h"
45#include "xattr.h"
46
47#include <stdlib.h>
48
49#ifdef SQUASHFS_TRACE
50#define TRACE(s, args...) \
51		do { \
52			printf("read_xattrs: "s, ## args); \
53		} while(0)
54#else
55#define TRACE(s, args...)
56#endif
57
58#define ERROR(s, args...) \
59		do { \
60			fprintf(stderr, s, ## args); \
61		} while(0)
62
63extern int read_fs_bytes(int, long long, int, void *);
64extern int read_block(int, long long, long long *, void *);
65
66static struct hash_entry {
67	long long		start;
68	unsigned int		offset;
69	struct hash_entry	*next;
70} *hash_table[65536];
71
72static struct squashfs_xattr_id *xattr_ids;
73static void *xattrs = NULL;
74static long long xattr_table_start;
75
76/*
77 * Prefix lookup table, storing mapping to/from prefix string and prefix id
78 */
79struct prefix prefix_table[] = {
80	{ "user.", SQUASHFS_XATTR_USER },
81	{ "trusted.", SQUASHFS_XATTR_TRUSTED },
82	{ "security.", SQUASHFS_XATTR_SECURITY },
83	{ "", -1 }
84};
85
86/*
87 * store mapping from location of compressed block in fs ->
88 * location of uncompressed block in memory
89 */
90static int save_xattr_block(long long start, int offset)
91{
92	struct hash_entry *hash_entry = malloc(sizeof(*hash_entry));
93	int hash = start & 0xffff;
94
95	TRACE("save_xattr_block: start %lld, offset %d\n", start, offset);
96
97	if(hash_entry == NULL) {
98		ERROR("Failed to allocate hash entry\n");
99		return -1;
100	}
101
102	hash_entry->start = start;
103	hash_entry->offset = offset;
104	hash_entry->next = hash_table[hash];
105	hash_table[hash] = hash_entry;
106
107	return 1;
108}
109
110
111/*
112 * map from location of compressed block in fs ->
113 * location of uncompressed block in memory
114 */
115static int get_xattr_block(long long start)
116{
117	int hash = start & 0xffff;
118	struct hash_entry *hash_entry = hash_table[hash];
119
120	for(; hash_entry; hash_entry = hash_entry->next)
121		if(hash_entry->start == start)
122			break;
123
124	TRACE("get_xattr_block: start %lld, offset %d\n", start,
125		hash_entry ? hash_entry->offset : -1);
126
127	return hash_entry ? hash_entry->offset : -1;
128}
129
130
131/*
132 * construct the xattr_list entry from the fs xattr, including
133 * mapping name and prefix into a full name
134 */
135static int read_xattr_entry(struct xattr_list *xattr,
136	struct squashfs_xattr_entry *entry, void *name)
137{
138	int i, len, type = entry->type & XATTR_PREFIX_MASK;
139
140	for(i = 0; prefix_table[i].type != -1; i++)
141		if(prefix_table[i].type == type)
142			break;
143
144	if(prefix_table[i].type == -1) {
145		ERROR("Unrecognised type in read_xattr_entry\n");
146		return 0;
147	}
148
149	len = strlen(prefix_table[i].prefix);
150	xattr->full_name = malloc(len + entry->size + 1);
151	if(xattr->full_name == NULL) {
152		ERROR("Out of memory in read_xattr_entry\n");
153		return -1;
154	}
155	memcpy(xattr->full_name, prefix_table[i].prefix, len);
156	memcpy(xattr->full_name + len, name, entry->size);
157	xattr->full_name[len + entry->size] = '\0';
158	xattr->name = xattr->full_name + len;
159	xattr->size = entry->size;
160	xattr->type = type;
161
162	return 1;
163}
164
165
166/*
167 * Read and decompress the xattr id table and the xattr metadata.
168 * This is cached in memory for later use by get_xattr()
169 */
170int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk)
171{
172	int res, bytes, i, indexes, index_bytes, ids;
173	long long *index, start, end;
174	struct squashfs_xattr_table id_table;
175
176	TRACE("read_xattrs_from_disk\n");
177
178	if(sBlk->xattr_id_table_start == SQUASHFS_INVALID_BLK)
179		return SQUASHFS_INVALID_BLK;
180
181	/*
182	 * Read xattr id table, containing start of xattr metadata and the
183	 * number of xattrs in the file system
184	 */
185	res = read_fs_bytes(fd, sBlk->xattr_id_table_start, sizeof(id_table),
186		&id_table);
187	if(res == 0)
188		return 0;
189
190	SQUASHFS_INSWAP_XATTR_TABLE(&id_table);
191
192	/*
193	 * Allocate and read the index to the xattr id table metadata
194	 * blocks
195	 */
196	ids = id_table.xattr_ids;
197	xattr_table_start = id_table.xattr_table_start;
198	index_bytes = SQUASHFS_XATTR_BLOCK_BYTES(ids);
199	indexes = SQUASHFS_XATTR_BLOCKS(ids);
200	index = malloc(index_bytes);
201	if(index == NULL) {
202		ERROR("Failed to allocate index array\n");
203		return 0;
204	}
205
206	res = read_fs_bytes(fd, sBlk->xattr_id_table_start + sizeof(id_table),
207		index_bytes, index);
208	if(res ==0)
209		goto failed1;
210
211	SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
212
213	/*
214	 * Allocate enough space for the uncompressed xattr id table, and
215	 * read and decompress it
216	 */
217	bytes = SQUASHFS_XATTR_BYTES(ids);
218	xattr_ids = malloc(bytes);
219	if(xattr_ids == NULL) {
220		ERROR("Failed to allocate xattr id table\n");
221		goto failed1;
222	}
223
224	for(i = 0; i < indexes; i++) {
225		int length = read_block(fd, index[i], NULL,
226			((unsigned char *) xattr_ids) +
227			(i * SQUASHFS_METADATA_SIZE));
228		TRACE("Read xattr id table block %d, from 0x%llx, length "
229			"%d\n", i, index[i], length);
230		if(length == 0) {
231			ERROR("Failed to read xattr id table block %d, "
232				"from 0x%llx, length %d\n", i, index[i],
233				length);
234			goto failed2;
235		}
236	}
237
238	/*
239	 * Read and decompress the xattr metadata
240	 *
241	 * Note the first xattr id table metadata block is immediately after
242	 * the last xattr metadata block, so we can use index[0] to work out
243	 * the end of the xattr metadata
244	 */
245	start = xattr_table_start;
246	end = index[0];
247	for(i = 0; start < end; i++) {
248		int length;
249		void *x = realloc(xattrs, (i + 1) * SQUASHFS_METADATA_SIZE);
250		if(x == NULL) {
251			ERROR("Failed to realloc xattr data\n");
252			goto failed3;
253		}
254		xattrs = x;
255
256		/* store mapping from location of compressed block in fs ->
257		 * location of uncompressed block in memory */
258		res = save_xattr_block(start, i * SQUASHFS_METADATA_SIZE);
259		if(res == -1)
260			goto failed3;
261
262		length = read_block(fd, start, &start,
263			((unsigned char *) xattrs) +
264			(i * SQUASHFS_METADATA_SIZE));
265		TRACE("Read xattr block %d, length %d\n", i, length);
266		if(length == 0) {
267			ERROR("Failed to read xattr block %d\n", i);
268			goto failed3;
269		}
270	}
271
272	/* swap if necessary the xattr id entries */
273	for(i = 0; i < ids; i++)
274		SQUASHFS_INSWAP_XATTR_ID(&xattr_ids[i]);
275
276	free(index);
277
278	return ids;
279
280failed3:
281	free(xattrs);
282failed2:
283	free(xattr_ids);
284failed1:
285	free(index);
286
287	return 0;
288}
289
290
291/*
292 * Construct and return the list of xattr name:value pairs for the passed xattr
293 * id
294 */
295struct xattr_list *get_xattr(int i, unsigned int *count)
296{
297	long long start;
298	struct xattr_list *xattr_list = NULL;
299	unsigned int offset;
300	void *xptr;
301	int j;
302
303	TRACE("get_xattr\n");
304
305	*count = xattr_ids[i].count;
306	start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start;
307	offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr);
308	xptr = xattrs + get_xattr_block(start) + offset;
309
310	TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i,
311			*count, start, offset);
312
313	for(j = 0; j < *count; j++) {
314		struct squashfs_xattr_entry entry;
315		struct squashfs_xattr_val val;
316		int res;
317
318		xattr_list = realloc(xattr_list, (j + 1) *
319						sizeof(struct xattr_list));
320		if(xattr_list == NULL) {
321			ERROR("Out of memory in get_xattrs\n");
322			goto failed;
323		}
324
325		SQUASHFS_SWAP_XATTR_ENTRY(&entry, xptr);
326		xptr += sizeof(entry);
327		res = read_xattr_entry(&xattr_list[j], &entry, xptr);
328		if(res != 1)
329			goto failed;
330		xptr += entry.size;
331
332		TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j,
333			entry.type, entry.size, xattr_list[j].full_name);
334
335		if(entry.type & SQUASHFS_XATTR_VALUE_OOL) {
336			long long xattr;
337			void *ool_xptr;
338
339			xptr += sizeof(val);
340			SQUASHFS_SWAP_LONG_LONGS(&xattr, xptr, 1);
341			xptr += sizeof(xattr);
342			start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start;
343			offset = SQUASHFS_XATTR_OFFSET(xattr);
344			ool_xptr = xattrs + get_xattr_block(start) + offset;
345			SQUASHFS_SWAP_XATTR_VAL(&val, ool_xptr);
346			xattr_list[j].value = ool_xptr + sizeof(val);
347		} else {
348			SQUASHFS_SWAP_XATTR_VAL(&val, xptr);
349			xattr_list[j].value = xptr + sizeof(val);
350			xptr += sizeof(val) + val.vsize;
351		}
352
353		TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize);
354
355		xattr_list[j].vsize = val.vsize;
356	}
357
358	return xattr_list;
359
360failed:
361	free(xattr_list);
362
363	return NULL;
364}
365