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