unsquashfs.c revision b54566f5c433764830c29c83151691d0034de094
1/* 2 * Unsquash a squashfs filesystem. This is a highly compressed read only filesystem. 3 * 4 * Copyright (c) 2002, 2003, 2004, 2005, 2006 5 * Phillip Lougher <phillip@lougher.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2, 10 * or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 * 21 * unsquash.c 22 */ 23 24#define TRUE 1 25#define FALSE 0 26#include <stdio.h> 27#include <sys/types.h> 28#include <sys/stat.h> 29#include <fcntl.h> 30#include <errno.h> 31#include <string.h> 32#include <zlib.h> 33#include <sys/mman.h> 34#include <utime.h> 35 36#ifndef linux 37#define __BYTE_ORDER BYTE_ORDER 38#define __BIG_ENDIAN BIG_ENDIAN 39#define __LITTLE_ENDIAN LITTLE_ENDIAN 40#else 41#include <endian.h> 42#endif 43 44#include <squashfs_fs.h> 45#include "read_fs.h" 46#include "global.h" 47 48#include <stdlib.h> 49 50#ifdef SQUASHFS_TRACE 51#define TRACE(s, args...) do { \ 52 printf("mksquashfs: "s, ## args); \ 53 } while(0) 54#else 55#define TRACE(s, args...) 56#endif 57 58#define ERROR(s, args...) do { \ 59 fprintf(stderr, s, ## args); \ 60 } while(0) 61 62#define EXIT_UNSQUASH(s, args...) do { \ 63 fprintf(stderr, "FATAL ERROR aborting: "s, ## args); \ 64 } while(0) 65 66struct hash_table_entry { 67 int start; 68 int bytes; 69 struct hash_table_entry *next; 70}; 71 72int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, dev_count = 0, fifo_count = 0; 73char *inode_table = NULL, *directory_table = NULL; 74struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536]; 75int fd; 76squashfs_fragment_entry *fragment_table; 77unsigned int *uid_table, *guid_table; 78unsigned int cached_frag = SQUASHFS_INVALID_FRAG; 79char *fragment_data; 80char *file_data; 81char *data; 82unsigned int block_size; 83int lsonly = FALSE, info = FALSE; 84char **created_inode; 85 86#define CALCULATE_HASH(start) (start & 0xffff) 87 88int add_entry(struct hash_table_entry *hash_table[], int start, int bytes) 89{ 90 int hash = CALCULATE_HASH(start); 91 struct hash_table_entry *hash_table_entry; 92 93 if((hash_table_entry = malloc(sizeof(struct hash_table_entry))) == NULL) { 94 ERROR("add_hash: out of memory in malloc\n"); 95 return FALSE; 96 } 97 98 hash_table_entry->start = start; 99 hash_table_entry->bytes = bytes; 100 hash_table_entry->next = hash_table[hash]; 101 hash_table[hash] = hash_table_entry; 102 103 return TRUE; 104} 105 106 107int lookup_entry(struct hash_table_entry *hash_table[], int start) 108{ 109 int hash = CALCULATE_HASH(start); 110 struct hash_table_entry *hash_table_entry; 111 112 for(hash_table_entry = hash_table[hash]; hash_table_entry; hash_table_entry = hash_table_entry->next) 113 if(hash_table_entry->start == start) 114 return hash_table_entry->bytes; 115 116 return -1; 117} 118 119 120int read_bytes(long long byte, int bytes, char *buff) 121{ 122 off_t off = byte; 123 124 TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, bytes); 125 126 if(lseek(fd, off, SEEK_SET) == -1) { 127 ERROR("Lseek failed because %s\b", strerror(errno)); 128 return FALSE; 129 } 130 131 if(read(fd, buff, bytes) == -1) { 132 ERROR("Read on destination failed because %s\n", strerror(errno)); 133 return FALSE; 134 } 135 136 return TRUE; 137} 138 139 140int read_block(long long start, long long *next, char *block, squashfs_super_block *sBlk) 141{ 142 unsigned short c_byte; 143 int offset = 2; 144 145 if(swap) { 146 if(read_bytes(start, 2, block) == FALSE) 147 goto failed; 148 ((unsigned char *) &c_byte)[1] = block[0]; 149 ((unsigned char *) &c_byte)[0] = block[1]; 150 } else 151 if(read_bytes(start, 2, (char *)&c_byte) == FALSE) 152 goto failed; 153 154 TRACE("read_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? "compressed" : "uncompressed"); 155 156 if(SQUASHFS_CHECK_DATA(sBlk->flags)) 157 offset = 3; 158 if(SQUASHFS_COMPRESSED(c_byte)) { 159 char buffer[SQUASHFS_METADATA_SIZE]; 160 int res; 161 unsigned long bytes = SQUASHFS_METADATA_SIZE; 162 163 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); 164 if(read_bytes(start + offset, c_byte, buffer) == FALSE) 165 goto failed; 166 167 if((res = uncompress((unsigned char *) block, &bytes, (const unsigned char *) buffer, c_byte)) != Z_OK) { 168 if(res == Z_MEM_ERROR) 169 ERROR("zlib::uncompress failed, not enough memory\n"); 170 else if(res == Z_BUF_ERROR) 171 ERROR("zlib::uncompress failed, not enough room in output buffer\n"); 172 else 173 ERROR("zlib::uncompress failed, unknown error %d\n", res); 174 goto failed; 175 } 176 if(next) 177 *next = start + offset + c_byte; 178 return bytes; 179 } else { 180 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); 181 if(read_bytes(start + offset, c_byte, block) == FALSE) 182 goto failed; 183 if(next) 184 *next = start + offset + c_byte; 185 return c_byte; 186 } 187 188failed: 189 return FALSE; 190} 191 192 193int read_data_block(long long start, unsigned int size, char *block) 194{ 195 int res; 196 unsigned long bytes = block_size; 197 int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size); 198 199 TRACE("read_data_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte), SQUASHFS_COMPRESSED_BLOCK(c_byte) ? "compressed" : "uncompressed"); 200 201 if(SQUASHFS_COMPRESSED_BLOCK(size)) { 202 if(read_bytes(start, c_byte, data) == FALSE) 203 return 0; 204 205 if((res = uncompress((unsigned char *) block, &bytes, (const unsigned char *) data, c_byte)) != Z_OK) { 206 if(res == Z_MEM_ERROR) 207 ERROR("zlib::uncompress failed, not enough memory\n"); 208 else if(res == Z_BUF_ERROR) 209 ERROR("zlib::uncompress failed, not enough room in output buffer\n"); 210 else 211 ERROR("zlib::uncompress failed, unknown error %d\n", res); 212 return 0; 213 } 214 215 return bytes; 216 } else { 217 if(read_bytes(start, c_byte, block) == FALSE) 218 return 0; 219 220 return c_byte; 221 } 222} 223 224 225void uncompress_inode_table(long long start, long long end, squashfs_super_block *sBlk) 226{ 227 int size = 0, bytes = 0, res; 228 229 while(start < end) { 230 if((size - bytes < SQUASHFS_METADATA_SIZE) && 231 ((inode_table = realloc(inode_table, size += SQUASHFS_METADATA_SIZE)) == NULL)) 232 EXIT_UNSQUASH("uncompress_inode_table: out of memory in realloc\n"); 233 TRACE("uncompress_inode_table: reading block 0x%llx\n", start); 234 add_entry(inode_table_hash, start, bytes); 235 if((res = read_block(start, &start, inode_table + bytes, sBlk)) == 0) { 236 free(inode_table); 237 EXIT_UNSQUASH("uncompress_inode_table: failed to read block\n"); 238 } 239 bytes += res; 240 } 241} 242 243 244int set_attributes(char *pathname, unsigned int mode, unsigned int uid, unsigned int guid, 245unsigned int mtime, unsigned int set_mode) 246{ 247 struct utimbuf times = { (time_t) mtime, (time_t) mtime }; 248 249 if(utime(pathname, ×) == -1) { 250 ERROR("set_attributes: failed to set time on %s, because %s\n", pathname, strerror(errno)); 251 return FALSE; 252 } 253 254 if(set_mode && chmod(pathname, (mode_t) mode) == -1) { 255 ERROR("set_attributes: failed to change mode %s, because %s\n", pathname, strerror(errno)); 256 return FALSE; 257 } 258 259 if(geteuid() == 0) { 260 uid_t uid_value = (uid_t) uid_table[uid]; 261 uid_t guid_value = guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[guid]; 262 263 if(chown(pathname, uid_value, guid_value) == -1) { 264 ERROR("set_attributes: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno)); 265 return FALSE; 266 } 267 } 268 269 return TRUE; 270} 271 272 273void read_uids_guids(squashfs_super_block *sBlk) 274{ 275 if((uid_table = malloc((sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int))) == NULL) 276 EXIT_UNSQUASH("read_uids_guids: failed to allocate uid/gid table\n"); 277 278 guid_table = uid_table + sBlk->no_uids; 279 280 if(swap) { 281 unsigned int suid_table[sBlk->no_uids + sBlk->no_guids]; 282 283 if(read_bytes(sBlk->uid_start, (sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int), (char *) suid_table) == 284 FALSE) 285 EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n"); 286 SQUASHFS_SWAP_INTS(uid_table, suid_table, sBlk->no_uids + sBlk->no_guids); 287 } else 288 if(read_bytes(sBlk->uid_start, (sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int), (char *) uid_table) == 289 FALSE) 290 EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n"); 291} 292 293 294void read_fragment_table(squashfs_super_block *sBlk) 295{ 296 int i, indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments); 297 squashfs_fragment_index fragment_table_index[indexes]; 298 299 TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%llx\n", sBlk->fragments, indexes, sBlk->fragment_table_start); 300 if(sBlk->fragments == 0) 301 return; 302 303 if((fragment_table = (squashfs_fragment_entry *) malloc(sBlk->fragments * sizeof(squashfs_fragment_entry))) == NULL) 304 EXIT_UNSQUASH("read_fragment_table: failed to allocate fragment table\n"); 305 306 if(swap) { 307 squashfs_fragment_index sfragment_table_index[indexes]; 308 309 read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) sfragment_table_index); 310 SQUASHFS_SWAP_FRAGMENT_INDEXES(fragment_table_index, sfragment_table_index, indexes); 311 } else 312 read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) fragment_table_index); 313 314 for(i = 0; i < indexes; i++) { 315 int length = read_block(fragment_table_index[i], NULL, ((char *) fragment_table) + (i * SQUASHFS_METADATA_SIZE), sBlk); 316 TRACE("Read fragment table block %d, from 0x%llx, length %d\n", i, fragment_table_index[i], length); 317 } 318 319 if(swap) { 320 squashfs_fragment_entry sfragment; 321 for(i = 0; i < sBlk->fragments; i++) { 322 SQUASHFS_SWAP_FRAGMENT_ENTRY((&sfragment), (&fragment_table[i])); 323 memcpy((char *) &fragment_table[i], (char *) &sfragment, sizeof(squashfs_fragment_entry)); 324 } 325 } 326} 327 328 329char *read_fragment(unsigned int fragment) 330{ 331 TRACE("read_fragment: reading fragment %d\n", fragment); 332 333 if(cached_frag == SQUASHFS_INVALID_FRAG || fragment != cached_frag) { 334 squashfs_fragment_entry *fragment_entry = &fragment_table[fragment]; 335 if(read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data) == 0) { 336 ERROR("read_fragment: failed to read fragment %d\n", fragment); 337 cached_frag = SQUASHFS_INVALID_FRAG; 338 return NULL; 339 } 340 cached_frag = fragment; 341 } 342 343 return fragment_data; 344} 345 346 347int write_file(char *pathname, unsigned int fragment, unsigned int frag_bytes, unsigned int offset, 348unsigned int blocks, long long start, char *block_ptr, unsigned int mode) 349{ 350 unsigned int file_fd, bytes, i; 351 unsigned int *block_list; 352 353 TRACE("write_file: regular file, blocks %d\n", blocks); 354 355 if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) { 356 ERROR("write_file: unable to malloc block list\n"); 357 return FALSE; 358 } 359 360 if(swap) { 361 unsigned int sblock_list[blocks]; 362 memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int)); 363 SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks); 364 } else 365 memcpy(block_list, block_ptr, blocks * sizeof(unsigned int)); 366 367 if((file_fd = open(pathname, O_CREAT | O_WRONLY, (mode_t) mode)) == -1) { 368 ERROR("write_file: failed to create file %s, because %s\n", pathname, 369 strerror(errno)); 370 free(block_list); 371 return FALSE; 372 } 373 374 for(i = 0; i < blocks; i++) { 375 if((bytes = read_data_block(start, block_list[i], file_data)) == 0) { 376 ERROR("write_file: failed to read data block 0x%llx\n", start); 377 goto failure; 378 } 379 380 if(write(file_fd, file_data, bytes) < bytes) { 381 ERROR("write_file: failed to write data block 0x%llx\n", start); 382 goto failure; 383 } 384 385 start += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]); 386 } 387 388 if(frag_bytes != 0) { 389 char *fragment_data = read_fragment(fragment); 390 391 if(fragment_data == NULL) 392 goto failure; 393 394 if(write(file_fd, fragment_data + offset, frag_bytes) < frag_bytes) { 395 ERROR("write_file: failed to write fragment %d\n", fragment); 396 goto failure; 397 } 398 } 399 400 close(file_fd); 401 return TRUE; 402 403failure: 404 close(file_fd); 405 free(block_list); 406 return FALSE; 407} 408 409 410int create_inode(char *pathname, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk) 411{ 412 long long start = sBlk->inode_table_start + start_block; 413 squashfs_inode_header header; 414 char *block_ptr; 415 int bytes = lookup_entry(inode_table_hash, start), file_fd; 416 417 TRACE("create_inode: pathname %s, start 0x%llx, offset %d\n", pathname, start, offset); 418 419 if(bytes == -1) { 420 ERROR("create_inode: inode block 0x%llx out of range!\n", start); 421 return FALSE; 422 } 423 block_ptr = inode_table + bytes + offset; 424 425 if(swap) { 426 squashfs_base_inode_header sinode; 427 memcpy(&sinode, block_ptr, sizeof(header.base)); 428 SQUASHFS_SWAP_BASE_INODE_HEADER(&header.base, &sinode, sizeof(squashfs_base_inode_header)); 429 } else 430 memcpy(&header.base, block_ptr, sizeof(header.base)); 431 432 if(created_inode[header.base.inode_number - 1]) { 433 TRACE("create_inode: hard link\n"); 434 if(link(created_inode[header.base.inode_number - 1], pathname) == -1) { 435 ERROR("create_inode: failed to create hardlink, because %s\n", strerror(errno)); 436 return FALSE; 437 } 438 439 return TRUE; 440 } 441 442 switch(header.base.inode_type) { 443 case SQUASHFS_FILE_TYPE: { 444 unsigned int frag_bytes; 445 unsigned int blocks; 446 unsigned int offset; 447 long long start; 448 squashfs_reg_inode_header *inode = &header.reg; 449 450 if(swap) { 451 squashfs_reg_inode_header sinode; 452 memcpy(&sinode, block_ptr, sizeof(sinode)); 453 SQUASHFS_SWAP_REG_INODE_HEADER(inode, &sinode); 454 } else 455 memcpy(inode, block_ptr, sizeof(*inode)); 456 457 frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size; 458 offset = inode->offset; 459 blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size 460 + sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >> 461 sBlk->block_log; 462 start = inode->start_block; 463 464 TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks); 465 466 if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start, 467 block_ptr + sizeof(*inode), inode->mode)) { 468 set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE); 469 file_count ++; 470 } 471 break; 472 } 473 case SQUASHFS_LREG_TYPE: { 474 unsigned int frag_bytes; 475 unsigned int blocks; 476 unsigned int offset; 477 long long start; 478 squashfs_lreg_inode_header *inode = &header.lreg; 479 480 if(swap) { 481 squashfs_lreg_inode_header sinode; 482 memcpy(&sinode, block_ptr, sizeof(sinode)); 483 SQUASHFS_SWAP_LREG_INODE_HEADER(inode, &sinode); 484 } else 485 memcpy(inode, block_ptr, sizeof(*inode)); 486 487 frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size; 488 offset = inode->offset; 489 blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size 490 + sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >> 491 sBlk->block_log; 492 start = inode->start_block; 493 494 TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks); 495 496 if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start, 497 block_ptr + sizeof(*inode), inode->mode)) { 498 set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE); 499 file_count ++; 500 } 501 break; 502 } 503 case SQUASHFS_SYMLINK_TYPE: { 504 squashfs_symlink_inode_header *inodep = &header.symlink; 505 char name[65536]; 506 507 if(swap) { 508 squashfs_symlink_inode_header sinodep; 509 memcpy(&sinodep, block_ptr, sizeof(sinodep)); 510 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, &sinodep); 511 } else 512 memcpy(inodep, block_ptr, sizeof(*inodep)); 513 514 TRACE("create_inode: symlink, symlink_size %d\n", inodep->symlink_size); 515 516 strncpy(name, block_ptr + sizeof(squashfs_symlink_inode_header), inodep->symlink_size); 517 name[inodep->symlink_size] = '\0'; 518 519 if(symlink(name, pathname) == -1) { 520 ERROR("create_inode: failed to create symlink %s, because %s\n", pathname, 521 strerror(errno)); 522 break; 523 } 524 525 if(geteuid() == 0) { 526 uid_t uid_value = (uid_t) uid_table[inodep->uid]; 527 uid_t guid_value = inodep->guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[inodep->guid]; 528 529 if(lchown(pathname, uid_value, guid_value) == -1) 530 ERROR("create_inode: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno)); 531 } 532 533 sym_count ++; 534 break; 535 } 536 case SQUASHFS_BLKDEV_TYPE: 537 case SQUASHFS_CHRDEV_TYPE: { 538 squashfs_dev_inode_header *inodep = &header.dev; 539 540 if(swap) { 541 squashfs_dev_inode_header sinodep; 542 memcpy(&sinodep, block_ptr, sizeof(sinodep)); 543 SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, &sinodep); 544 } else 545 memcpy(inodep, block_ptr, sizeof(*inodep)); 546 547 TRACE("create_inode: dev, rdev 0x%x\n", inodep->rdev); 548 549 if(geteuid() == 0) { 550 if(mknod(pathname, inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? S_IFCHR : S_IFBLK, 551 makedev((inodep->rdev >> 8) & 0xff, inodep->rdev & 0xff)) 552 == -1) { 553 ERROR("create_inode: failed to create %s device %s, because %s\n", 554 inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block", 555 pathname, strerror(errno)); 556 break; 557 } 558 set_attributes(pathname, inodep->mode, inodep->uid, inodep->guid, inodep->mtime, TRUE); 559 dev_count ++; 560 } else 561 ERROR("create_inode: could not create %s device %s, because you're not superuser!\n", 562 inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block", 563 pathname, strerror(errno)); 564 break; 565 } 566 case SQUASHFS_FIFO_TYPE: 567 TRACE("create_inode: fifo\n"); 568 569 if(mknod(pathname, S_IFIFO, 0) == -1) { 570 ERROR("create_inode: failed to create fifo %s, because %s\n", 571 pathname, strerror(errno)); 572 break; 573 } 574 set_attributes(pathname, header.base.mode, header.base.uid, header.base.guid, 575 header.base.mtime, TRUE); 576 fifo_count ++; 577 break; 578 case SQUASHFS_SOCKET_TYPE: 579 TRACE("create_inode: socket\n"); 580 ERROR("create_inode: socket %s ignored\n", pathname); 581 break; 582 default: 583 ERROR("Unknown inode type %d in create_inode_table!\n", header.base.inode_type); 584 return FALSE; 585 } 586 587 created_inode[header.base.inode_number - 1] = strdup(pathname); 588 589 return TRUE; 590} 591 592 593void uncompress_directory_table(long long start, long long end, squashfs_super_block *sBlk) 594{ 595 int bytes = 0, size = 0, res; 596 597 while(start < end) { 598 if(size - bytes < SQUASHFS_METADATA_SIZE && (directory_table = realloc(directory_table, size += SQUASHFS_METADATA_SIZE)) == NULL) 599 EXIT_UNSQUASH("uncompress_directory_table: out of memory in realloc\n"); 600 TRACE("uncompress_directory_table: reading block 0x%llx\n", start); 601 add_entry(directory_table_hash, start, bytes); 602 if((res = read_block(start, &start, directory_table + bytes, sBlk)) == 0) 603 EXIT_UNSQUASH("uncompress_directory_table: failed to read block\n"); 604 bytes += res; 605 } 606} 607 608 609#define DIR_ENT_SIZE 16 610 611struct dir_ent { 612 char name[SQUASHFS_NAME_LEN + 1]; 613 unsigned int start_block; 614 unsigned int offset; 615 unsigned int type; 616}; 617 618struct dir { 619 int dir_count; 620 int cur_entry; 621 unsigned int mode; 622 unsigned int uid; 623 unsigned int guid; 624 unsigned int mtime; 625 struct dir_ent *dirs; 626}; 627 628 629struct dir *squashfs_openddir(unsigned int block_start, unsigned int offset, squashfs_super_block *sBlk) 630{ 631 squashfs_dir_header dirh; 632 char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; 633 squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer; 634 long long start = sBlk->inode_table_start + block_start; 635 char *block_ptr; 636 int bytes = lookup_entry(inode_table_hash, start); 637 squashfs_inode_header header; 638 int dir_count, size; 639 struct dir_ent *new_dir; 640 struct dir *dir; 641 642 TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start, offset); 643 644 if(bytes == -1) { 645 ERROR("squashfs_opendir: inode block %d not found!\n", block_start); 646 return NULL; 647 } 648 block_ptr = inode_table + bytes + offset; 649 650 if(swap) { 651 squashfs_dir_inode_header sinode; 652 memcpy(&sinode, block_ptr, sizeof(header.dir)); 653 SQUASHFS_SWAP_DIR_INODE_HEADER(&header.dir, &sinode); 654 } else 655 memcpy(&header.dir, block_ptr, sizeof(header.dir)); 656 657 switch(header.dir.inode_type) { 658 case SQUASHFS_DIR_TYPE: 659 block_start = header.dir.start_block; 660 offset = header.dir.offset; 661 size = header.dir.file_size; 662 break; 663 case SQUASHFS_LDIR_TYPE: 664 if(swap) { 665 squashfs_ldir_inode_header sinode; 666 memcpy(&sinode, block_ptr, sizeof(header.ldir)); 667 SQUASHFS_SWAP_LDIR_INODE_HEADER(&header.ldir, &sinode); 668 } else 669 memcpy(&header.ldir, block_ptr, sizeof(header.ldir)); 670 block_start = header.ldir.start_block; 671 offset = header.ldir.offset; 672 size = header.ldir.file_size; 673 break; 674 default: 675 ERROR("squashfs_opendir: inode not a directory\n"); 676 return NULL; 677 } 678 679 start = sBlk->directory_table_start + block_start; 680 bytes = lookup_entry(directory_table_hash, start); 681 682 if(bytes == -1) { 683 ERROR("squashfs_opendir: directory block %d not found!\n", block_start); 684 return NULL; 685 } 686 bytes += offset; 687 size += bytes - 3; 688 689 if((dir = malloc(sizeof(struct dir))) == NULL) { 690 ERROR("squashfs_opendir: malloc failed!\n"); 691 return NULL; 692 } 693 694 dir->dir_count = 0; 695 dir->cur_entry = 0; 696 dir->mode = header.dir.mode; 697 dir->uid = header.dir.uid; 698 dir->guid = header.dir.guid; 699 dir->mtime = header.dir.mtime; 700 dir->dirs = NULL; 701 702 while(bytes < size) { 703 if(swap) { 704 squashfs_dir_header sdirh; 705 memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); 706 SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); 707 } else 708 memcpy(&dirh, directory_table + bytes, sizeof(dirh)); 709 710 dir_count = dirh.count + 1; 711 TRACE("squashfs_opendir: Read directory header @ byte position %d, %d directory entries\n", bytes, dir_count); 712 bytes += sizeof(dirh); 713 714 while(dir_count--) { 715 if(swap) { 716 squashfs_dir_entry sdire; 717 memcpy(&sdire, directory_table + bytes, sizeof(sdire)); 718 SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); 719 } else 720 memcpy(dire, directory_table + bytes, sizeof(dire)); 721 bytes += sizeof(*dire); 722 723 memcpy(dire->name, directory_table + bytes, dire->size + 1); 724 dire->name[dire->size + 1] = '\0'; 725 TRACE("squashfs_opendir: directory entry %s, inode %d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); 726 if((dir->dir_count % DIR_ENT_SIZE) == 0) { 727 if((new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent))) == NULL) { 728 ERROR("squashfs_opendir: realloc failed!\n"); 729 free(dir->dirs); 730 free(dir); 731 return NULL; 732 } 733 dir->dirs = new_dir; 734 } 735 strcpy(dir->dirs[dir->dir_count].name, dire->name); 736 dir->dirs[dir->dir_count].start_block = dirh.start_block; 737 dir->dirs[dir->dir_count].offset = dire->offset; 738 dir->dirs[dir->dir_count].type = dire->type; 739 dir->dir_count ++; 740 bytes += dire->size + 1; 741 } 742 } 743 744 return dir; 745} 746 747 748int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, unsigned int *offset, unsigned int *type) 749{ 750 if(dir->cur_entry == dir->dir_count) 751 return FALSE; 752 753 *name = dir->dirs[dir->cur_entry].name; 754 *start_block = dir->dirs[dir->cur_entry].start_block; 755 *offset = dir->dirs[dir->cur_entry].offset; 756 *type = dir->dirs[dir->cur_entry].type; 757 dir->cur_entry ++; 758 759 return TRUE; 760} 761 762 763void squashfs_closedir(struct dir *dir) 764{ 765 free(dir->dirs); 766 free(dir); 767} 768 769 770char *get_component(char *target, char *targname) 771{ 772 while(*target == '/') 773 *target ++; 774 775 while(*target != '/' && *target!= '\0') 776 *targname ++ = *target ++; 777 778 *targname = '\0'; 779 780 return target; 781} 782 783 784int matches(char *targname, char *name) 785{ 786 if(*targname == '\0' || strcmp(targname, name) == 0) 787 return TRUE; 788 789 return FALSE; 790} 791 792 793int dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk, char *target) 794{ 795 struct dir *dir = squashfs_openddir(start_block, offset, sBlk); 796 unsigned int type; 797 char *name, pathname[1024]; 798 char targname[1024]; 799 800 target = get_component(target, targname); 801 802 if(dir == NULL) { 803 ERROR("dir_scan: Failed to read directory %s (%x:%x)\n", parent_name, start_block, offset); 804 return FALSE; 805 } 806 807 if(!lsonly && mkdir(parent_name, (mode_t) dir->mode) == -1) { 808 ERROR("dir_scan: failed to open directory %s, because %s\n", parent_name, strerror(errno)); 809 return FALSE; 810 } 811 812 while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) { 813 TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", name, start_block, offset, type); 814 815 816 if(!matches(targname, name)) 817 continue; 818 819 strcat(strcat(strcpy(pathname, parent_name), "/"), name); 820 821 if(lsonly || info) 822 printf("%s\n", pathname); 823 824 if(type == SQUASHFS_DIR_TYPE) 825 dir_scan(pathname, start_block, offset, sBlk, target); 826 else 827 if(!lsonly) 828 create_inode(pathname, start_block, offset, sBlk); 829 } 830 831 !lsonly && set_attributes(parent_name, dir->mode, dir->uid, dir->guid, dir->mtime, TRUE); 832 833 squashfs_closedir(dir); 834 dir_count ++; 835 836 return TRUE; 837} 838 839 840int read_super(squashfs_super_block *sBlk, char *source) 841{ 842 read_bytes(SQUASHFS_START, sizeof(squashfs_super_block), (char *) sBlk); 843 844 /* Check it is a SQUASHFS superblock */ 845 swap = 0; 846 if(sBlk->s_magic != SQUASHFS_MAGIC) { 847 if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) { 848 squashfs_super_block sblk; 849 ERROR("Reading a different endian SQUASHFS filesystem on %s\n", source); 850 SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk); 851 memcpy(sBlk, &sblk, sizeof(squashfs_super_block)); 852 swap = 1; 853 } else { 854 ERROR("Can't find a SQUASHFS superblock on %s\n", source); 855 goto failed_mount; 856 } 857 } 858 859 /* Check the MAJOR & MINOR versions */ 860 if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) { 861 ERROR("Major/Minor mismatch, filesystem on %s is (%d:%d)\n", 862 source, sBlk->s_major, sBlk->s_minor); 863 ERROR("I only support Squashfs 3.0 filesystems! Later releases will support older Squashfs filesystems\n"); 864 goto failed_mount; 865 } 866 867#if __BYTE_ORDER == __BIG_ENDIAN 868 TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "little" : "big", source); 869#else 870 TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "big" : "little", source); 871#endif 872 873 TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : ""); 874 TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : ""); 875 TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : ""); 876 TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not"); 877 TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not" : ""); 878 TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not"); 879 TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not"); 880 TRACE("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n", sBlk->bytes_used / 1024.0, sBlk->bytes_used / (1024.0 * 1024.0)); 881 TRACE("\tBlock size %d\n", sBlk->block_size); 882 TRACE("\tNumber of fragments %d\n", sBlk->fragments); 883 TRACE("\tNumber of inodes %d\n", sBlk->inodes); 884 TRACE("\tNumber of uids %d\n", sBlk->no_uids); 885 TRACE("\tNumber of gids %d\n", sBlk->no_guids); 886 TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start); 887 TRACE("sBlk->directory_table_start 0x%llx\n", sBlk->directory_table_start); 888 TRACE("sBlk->uid_start 0x%llx\n", sBlk->uid_start); 889 TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start); 890 TRACE("\n"); 891 892 return TRUE; 893 894failed_mount: 895 return FALSE; 896} 897 898 899#define VERSION() \ 900 printf("unsquashfs version 1.1 (2006/07/09)\n");\ 901 printf("copyright (C) 2006 Phillip Lougher <phillip@lougher.org.uk>\n\n"); \ 902 printf("This program is free software; you can redistribute it and/or\n");\ 903 printf("modify it under the terms of the GNU General Public License\n");\ 904 printf("as published by the Free Software Foundation; either version 2,\n");\ 905 printf("or (at your option) any later version.\n\n");\ 906 printf("This program is distributed in the hope that it will be useful,\n");\ 907 printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");\ 908 printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");\ 909 printf("GNU General Public License for more details.\n"); 910int main(int argc, char *argv[]) 911{ 912 squashfs_super_block sBlk; 913 char *dest = "squashfs-root"; 914 int i, version = FALSE; 915 char *target = ""; 916 917 for(i = 1; i < argc; i++) { 918 if(*argv[i] != '-') 919 break; 920 if(strcmp(argv[i], "-version") == 0) { 921 VERSION(); 922 version = TRUE; 923 } else if(strcmp(argv[i], "-info") == 0) 924 info = TRUE; 925 else if(strcmp(argv[i], "-ls") == 0) 926 lsonly = TRUE; 927 else if(strcmp(argv[i], "-dest") == 0) { 928 if(++i == argc) 929 goto options; 930 dest = argv[i]; 931 } 932 } 933 934 if(i == argc) { 935 if(!version) { 936options: 937 ERROR("SYNTAX: %s [-ls | -dest] filesystem [directory or filename to be extracted]\n", argv[0]); 938 ERROR("\t-version\t\tprint version, licence and copyright information\n"); 939 ERROR("\t-info\t\t\tprint files as they are unsquashed\n"); 940 ERROR("\t-ls\t\t\tlist filesystem only\n"); 941 ERROR("\t-dest <pathname>\tunsquash to <pathname>, default \"squashfs-root\"\n"); 942 } 943 exit(1); 944 } 945 946 if((i + 1) < argc) 947 target = argv[i + 1]; 948 949 if((fd = open(argv[i], O_RDONLY)) == -1) { 950 ERROR("Could not open %s, because %s\n", argv[i], strerror(errno)); 951 exit(1); 952 } 953 954 if(read_super(&sBlk, argv[i]) == FALSE) 955 exit(1); 956 957 block_size = sBlk.block_size; 958 if((fragment_data = malloc(block_size)) == NULL) 959 EXIT_UNSQUASH("failed to allocate fragment_data\n"); 960 961 if((file_data = malloc(block_size)) == NULL) 962 EXIT_UNSQUASH("failed to allocate file_data"); 963 964 if((data = malloc(block_size)) == NULL) 965 EXIT_UNSQUASH("failed to allocate datan\n"); 966 967 if((created_inode = malloc(sBlk.inodes * sizeof(char *))) == NULL) 968 EXIT_UNSQUASH("failed to allocate created_inode\n"); 969 970 memset(created_inode, 0, sBlk.inodes * sizeof(char *)); 971 972 read_uids_guids(&sBlk); 973 read_fragment_table(&sBlk); 974 uncompress_inode_table(sBlk.inode_table_start, sBlk.directory_table_start, &sBlk); 975 uncompress_directory_table(sBlk.directory_table_start, sBlk.fragment_table_start, &sBlk); 976 977 dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.root_inode), SQUASHFS_INODE_OFFSET(sBlk.root_inode), &sBlk, target); 978 979 if(!lsonly) { 980 printf("\n"); 981 printf("created %d files\n", file_count); 982 printf("created %d directories\n", dir_count); 983 printf("created %d symlinks\n", sym_count); 984 printf("created %d devices\n", dev_count); 985 printf("created %d fifos\n", fifo_count); 986 } 987 988} 989