Home | History | Annotate | Download | only in squashfs-tools
      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 (at) 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  */
     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  */
     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  */
    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  */
    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 
    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  */
    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