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 "squashfs_fs.h" 43 #include "squashfs_swap.h" 44 #include "xattr.h" 45 #include "error.h" 46 47 #include <stdlib.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