1/* 2 * Unsquash a squashfs filesystem. This is a highly compressed read only 3 * filesystem. 4 * 5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 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 * unsquashfs.c 23 */ 24 25#include "unsquashfs.h" 26#include "squashfs_swap.h" 27#include "squashfs_compat.h" 28#include "read_fs.h" 29#include "compressor.h" 30#include "xattr.h" 31 32#include <sys/sysinfo.h> 33#include <sys/types.h> 34 35struct cache *fragment_cache, *data_cache; 36struct queue *to_reader, *to_deflate, *to_writer, *from_writer; 37pthread_t *thread, *deflator_thread; 38pthread_mutex_t fragment_mutex; 39 40/* user options that control parallelisation */ 41int processors = -1; 42 43struct super_block sBlk; 44squashfs_operations s_ops; 45struct compressor *comp; 46 47int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, 48 dev_count = 0, fifo_count = 0; 49char *inode_table = NULL, *directory_table = NULL; 50struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536]; 51int fd; 52unsigned int *uid_table, *guid_table; 53unsigned int cached_frag = SQUASHFS_INVALID_FRAG; 54char *fragment_data; 55char *file_data; 56char *data; 57unsigned int block_size; 58unsigned int block_log; 59int lsonly = FALSE, info = FALSE, force = FALSE, short_ls = TRUE; 60int use_regex = FALSE; 61char **created_inode; 62int root_process; 63int columns; 64int rotate = 0; 65pthread_mutex_t screen_mutex; 66pthread_cond_t progress_wait; 67int progress = TRUE, progress_enabled = FALSE; 68unsigned int total_blocks = 0, total_files = 0, total_inodes = 0; 69unsigned int cur_blocks = 0; 70int inode_number = 1; 71int no_xattrs = XATTR_DEF; 72 73int lookup_type[] = { 74 0, 75 S_IFDIR, 76 S_IFREG, 77 S_IFLNK, 78 S_IFBLK, 79 S_IFCHR, 80 S_IFIFO, 81 S_IFSOCK, 82 S_IFDIR, 83 S_IFREG, 84 S_IFLNK, 85 S_IFBLK, 86 S_IFCHR, 87 S_IFIFO, 88 S_IFSOCK 89}; 90 91struct test table[] = { 92 { S_IFMT, S_IFSOCK, 0, 's' }, 93 { S_IFMT, S_IFLNK, 0, 'l' }, 94 { S_IFMT, S_IFBLK, 0, 'b' }, 95 { S_IFMT, S_IFDIR, 0, 'd' }, 96 { S_IFMT, S_IFCHR, 0, 'c' }, 97 { S_IFMT, S_IFIFO, 0, 'p' }, 98 { S_IRUSR, S_IRUSR, 1, 'r' }, 99 { S_IWUSR, S_IWUSR, 2, 'w' }, 100 { S_IRGRP, S_IRGRP, 4, 'r' }, 101 { S_IWGRP, S_IWGRP, 5, 'w' }, 102 { S_IROTH, S_IROTH, 7, 'r' }, 103 { S_IWOTH, S_IWOTH, 8, 'w' }, 104 { S_IXUSR | S_ISUID, S_IXUSR | S_ISUID, 3, 's' }, 105 { S_IXUSR | S_ISUID, S_ISUID, 3, 'S' }, 106 { S_IXUSR | S_ISUID, S_IXUSR, 3, 'x' }, 107 { S_IXGRP | S_ISGID, S_IXGRP | S_ISGID, 6, 's' }, 108 { S_IXGRP | S_ISGID, S_ISGID, 6, 'S' }, 109 { S_IXGRP | S_ISGID, S_IXGRP, 6, 'x' }, 110 { S_IXOTH | S_ISVTX, S_IXOTH | S_ISVTX, 9, 't' }, 111 { S_IXOTH | S_ISVTX, S_ISVTX, 9, 'T' }, 112 { S_IXOTH | S_ISVTX, S_IXOTH, 9, 'x' }, 113 { 0, 0, 0, 0} 114}; 115 116void progress_bar(long long current, long long max, int columns); 117void update_progress_bar(); 118 119void sigwinch_handler() 120{ 121 struct winsize winsize; 122 123 if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { 124 if(isatty(STDOUT_FILENO)) 125 ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " 126 "columns\n"); 127 columns = 80; 128 } else 129 columns = winsize.ws_col; 130} 131 132 133void sigalrm_handler() 134{ 135 rotate = (rotate + 1) % 4; 136} 137 138 139struct queue *queue_init(int size) 140{ 141 struct queue *queue = malloc(sizeof(struct queue)); 142 143 if(queue == NULL) 144 EXIT_UNSQUASH("Out of memory in queue_init\n"); 145 146 queue->data = malloc(sizeof(void *) * (size + 1)); 147 if(queue->data == NULL) 148 EXIT_UNSQUASH("Out of memory in queue_init\n"); 149 150 queue->size = size + 1; 151 queue->readp = queue->writep = 0; 152 pthread_mutex_init(&queue->mutex, NULL); 153 pthread_cond_init(&queue->empty, NULL); 154 pthread_cond_init(&queue->full, NULL); 155 156 return queue; 157} 158 159 160void queue_put(struct queue *queue, void *data) 161{ 162 int nextp; 163 164 pthread_mutex_lock(&queue->mutex); 165 166 while((nextp = (queue->writep + 1) % queue->size) == queue->readp) 167 pthread_cond_wait(&queue->full, &queue->mutex); 168 169 queue->data[queue->writep] = data; 170 queue->writep = nextp; 171 pthread_cond_signal(&queue->empty); 172 pthread_mutex_unlock(&queue->mutex); 173} 174 175 176void *queue_get(struct queue *queue) 177{ 178 void *data; 179 pthread_mutex_lock(&queue->mutex); 180 181 while(queue->readp == queue->writep) 182 pthread_cond_wait(&queue->empty, &queue->mutex); 183 184 data = queue->data[queue->readp]; 185 queue->readp = (queue->readp + 1) % queue->size; 186 pthread_cond_signal(&queue->full); 187 pthread_mutex_unlock(&queue->mutex); 188 189 return data; 190} 191 192 193/* Called with the cache mutex held */ 194void insert_hash_table(struct cache *cache, struct cache_entry *entry) 195{ 196 int hash = CALCULATE_HASH(entry->block); 197 198 entry->hash_next = cache->hash_table[hash]; 199 cache->hash_table[hash] = entry; 200 entry->hash_prev = NULL; 201 if(entry->hash_next) 202 entry->hash_next->hash_prev = entry; 203} 204 205 206/* Called with the cache mutex held */ 207void remove_hash_table(struct cache *cache, struct cache_entry *entry) 208{ 209 if(entry->hash_prev) 210 entry->hash_prev->hash_next = entry->hash_next; 211 else 212 cache->hash_table[CALCULATE_HASH(entry->block)] = 213 entry->hash_next; 214 if(entry->hash_next) 215 entry->hash_next->hash_prev = entry->hash_prev; 216 217 entry->hash_prev = entry->hash_next = NULL; 218} 219 220 221/* Called with the cache mutex held */ 222void insert_free_list(struct cache *cache, struct cache_entry *entry) 223{ 224 if(cache->free_list) { 225 entry->free_next = cache->free_list; 226 entry->free_prev = cache->free_list->free_prev; 227 cache->free_list->free_prev->free_next = entry; 228 cache->free_list->free_prev = entry; 229 } else { 230 cache->free_list = entry; 231 entry->free_prev = entry->free_next = entry; 232 } 233} 234 235 236/* Called with the cache mutex held */ 237void remove_free_list(struct cache *cache, struct cache_entry *entry) 238{ 239 if(entry->free_prev == NULL && entry->free_next == NULL) 240 /* not in free list */ 241 return; 242 else if(entry->free_prev == entry && entry->free_next == entry) { 243 /* only this entry in the free list */ 244 cache->free_list = NULL; 245 } else { 246 /* more than one entry in the free list */ 247 entry->free_next->free_prev = entry->free_prev; 248 entry->free_prev->free_next = entry->free_next; 249 if(cache->free_list == entry) 250 cache->free_list = entry->free_next; 251 } 252 253 entry->free_prev = entry->free_next = NULL; 254} 255 256 257struct cache *cache_init(int buffer_size, int max_buffers) 258{ 259 struct cache *cache = malloc(sizeof(struct cache)); 260 261 if(cache == NULL) 262 EXIT_UNSQUASH("Out of memory in cache_init\n"); 263 264 cache->max_buffers = max_buffers; 265 cache->buffer_size = buffer_size; 266 cache->count = 0; 267 cache->free_list = NULL; 268 memset(cache->hash_table, 0, sizeof(struct cache_entry *) * 65536); 269 cache->wait_free = FALSE; 270 cache->wait_pending = FALSE; 271 pthread_mutex_init(&cache->mutex, NULL); 272 pthread_cond_init(&cache->wait_for_free, NULL); 273 pthread_cond_init(&cache->wait_for_pending, NULL); 274 275 return cache; 276} 277 278 279struct cache_entry *cache_get(struct cache *cache, long long block, int size) 280{ 281 /* 282 * Get a block out of the cache. If the block isn't in the cache 283 * it is added and queued to the reader() and deflate() threads for 284 * reading off disk and decompression. The cache grows until max_blocks 285 * is reached, once this occurs existing discarded blocks on the free 286 * list are reused 287 */ 288 int hash = CALCULATE_HASH(block); 289 struct cache_entry *entry; 290 291 pthread_mutex_lock(&cache->mutex); 292 293 for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next) 294 if(entry->block == block) 295 break; 296 297 if(entry) { 298 /* 299 * found the block in the cache, increment used count and 300 * if necessary remove from free list so it won't disappear 301 */ 302 entry->used ++; 303 remove_free_list(cache, entry); 304 pthread_mutex_unlock(&cache->mutex); 305 } else { 306 /* 307 * not in the cache 308 * 309 * first try to allocate new block 310 */ 311 if(cache->count < cache->max_buffers) { 312 entry = malloc(sizeof(struct cache_entry)); 313 if(entry == NULL) 314 EXIT_UNSQUASH("Out of memory in cache_get\n"); 315 entry->data = malloc(cache->buffer_size); 316 if(entry->data == NULL) 317 EXIT_UNSQUASH("Out of memory in cache_get\n"); 318 entry->cache = cache; 319 entry->free_prev = entry->free_next = NULL; 320 cache->count ++; 321 } else { 322 /* 323 * try to get from free list 324 */ 325 while(cache->free_list == NULL) { 326 cache->wait_free = TRUE; 327 pthread_cond_wait(&cache->wait_for_free, 328 &cache->mutex); 329 } 330 entry = cache->free_list; 331 remove_free_list(cache, entry); 332 remove_hash_table(cache, entry); 333 } 334 335 /* 336 * initialise block and insert into the hash table 337 */ 338 entry->block = block; 339 entry->size = size; 340 entry->used = 1; 341 entry->error = FALSE; 342 entry->pending = TRUE; 343 insert_hash_table(cache, entry); 344 345 /* 346 * queue to read thread to read and ultimately (via the 347 * decompress threads) decompress the buffer 348 */ 349 pthread_mutex_unlock(&cache->mutex); 350 queue_put(to_reader, entry); 351 } 352 353 return entry; 354} 355 356 357void cache_block_ready(struct cache_entry *entry, int error) 358{ 359 /* 360 * mark cache entry as being complete, reading and (if necessary) 361 * decompression has taken place, and the buffer is valid for use. 362 * If an error occurs reading or decompressing, the buffer also 363 * becomes ready but with an error... 364 */ 365 pthread_mutex_lock(&entry->cache->mutex); 366 entry->pending = FALSE; 367 entry->error = error; 368 369 /* 370 * if the wait_pending flag is set, one or more threads may be waiting 371 * on this buffer 372 */ 373 if(entry->cache->wait_pending) { 374 entry->cache->wait_pending = FALSE; 375 pthread_cond_broadcast(&entry->cache->wait_for_pending); 376 } 377 378 pthread_mutex_unlock(&entry->cache->mutex); 379} 380 381 382void cache_block_wait(struct cache_entry *entry) 383{ 384 /* 385 * wait for this cache entry to become ready, when reading and (if 386 * necessary) decompression has taken place 387 */ 388 pthread_mutex_lock(&entry->cache->mutex); 389 390 while(entry->pending) { 391 entry->cache->wait_pending = TRUE; 392 pthread_cond_wait(&entry->cache->wait_for_pending, 393 &entry->cache->mutex); 394 } 395 396 pthread_mutex_unlock(&entry->cache->mutex); 397} 398 399 400void cache_block_put(struct cache_entry *entry) 401{ 402 /* 403 * finished with this cache entry, once the usage count reaches zero it 404 * can be reused and is put onto the free list. As it remains 405 * accessible via the hash table it can be found getting a new lease of 406 * life before it is reused. 407 */ 408 pthread_mutex_lock(&entry->cache->mutex); 409 410 entry->used --; 411 if(entry->used == 0) { 412 insert_free_list(entry->cache, entry); 413 414 /* 415 * if the wait_free flag is set, one or more threads may be 416 * waiting on this buffer 417 */ 418 if(entry->cache->wait_free) { 419 entry->cache->wait_free = FALSE; 420 pthread_cond_broadcast(&entry->cache->wait_for_free); 421 } 422 } 423 424 pthread_mutex_unlock(&entry->cache->mutex); 425} 426 427 428char *modestr(char *str, int mode) 429{ 430 int i; 431 432 strcpy(str, "----------"); 433 434 for(i = 0; table[i].mask != 0; i++) { 435 if((mode & table[i].mask) == table[i].value) 436 str[table[i].position] = table[i].mode; 437 } 438 439 return str; 440} 441 442 443#define TOTALCHARS 25 444int print_filename(char *pathname, struct inode *inode) 445{ 446 char str[11], dummy[100], dummy2[100], *userstr, *groupstr; 447 int padchars; 448 struct passwd *user; 449 struct group *group; 450 struct tm *t; 451 452 if(short_ls) { 453 printf("%s\n", pathname); 454 return 1; 455 } 456 457 user = getpwuid(inode->uid); 458 if(user == NULL) { 459 sprintf(dummy, "%d", inode->uid); 460 userstr = dummy; 461 } else 462 userstr = user->pw_name; 463 464 group = getgrgid(inode->gid); 465 if(group == NULL) { 466 sprintf(dummy2, "%d", inode->gid); 467 groupstr = dummy2; 468 } else 469 groupstr = group->gr_name; 470 471 printf("%s %s/%s ", modestr(str, inode->mode), userstr, groupstr); 472 473 switch(inode->mode & S_IFMT) { 474 case S_IFREG: 475 case S_IFDIR: 476 case S_IFSOCK: 477 case S_IFIFO: 478 case S_IFLNK: 479 padchars = TOTALCHARS - strlen(userstr) - 480 strlen(groupstr); 481 482 printf("%*lld ", padchars > 0 ? padchars : 0, 483 inode->data); 484 break; 485 case S_IFCHR: 486 case S_IFBLK: 487 padchars = TOTALCHARS - strlen(userstr) - 488 strlen(groupstr) - 7; 489 490 printf("%*s%3d,%3d ", padchars > 0 ? padchars : 0, " ", 491 (int) inode->data >> 8, (int) inode->data & 492 0xff); 493 break; 494 } 495 496 t = localtime(&inode->time); 497 498 printf("%d-%02d-%02d %02d:%02d %s", t->tm_year + 1900, t->tm_mon + 1, 499 t->tm_mday, t->tm_hour, t->tm_min, pathname); 500 if((inode->mode & S_IFMT) == S_IFLNK) 501 printf(" -> %s", inode->symlink); 502 printf("\n"); 503 504 return 1; 505} 506 507 508void add_entry(struct hash_table_entry *hash_table[], long long start, 509 int bytes) 510{ 511 int hash = CALCULATE_HASH(start); 512 struct hash_table_entry *hash_table_entry; 513 514 hash_table_entry = malloc(sizeof(struct hash_table_entry)); 515 if(hash_table_entry == NULL) 516 EXIT_UNSQUASH("Out of memory in add_entry\n"); 517 518 hash_table_entry->start = start; 519 hash_table_entry->bytes = bytes; 520 hash_table_entry->next = hash_table[hash]; 521 hash_table[hash] = hash_table_entry; 522} 523 524 525int lookup_entry(struct hash_table_entry *hash_table[], long long start) 526{ 527 int hash = CALCULATE_HASH(start); 528 struct hash_table_entry *hash_table_entry; 529 530 for(hash_table_entry = hash_table[hash]; hash_table_entry; 531 hash_table_entry = hash_table_entry->next) 532 533 if(hash_table_entry->start == start) 534 return hash_table_entry->bytes; 535 536 return -1; 537} 538 539 540int read_fs_bytes(int fd, long long byte, int bytes, void *buff) 541{ 542 off_t off = byte; 543 int res, count; 544 545 TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, 546 bytes); 547 548 if(lseek(fd, off, SEEK_SET) == -1) { 549 ERROR("Lseek failed because %s\n", strerror(errno)); 550 return FALSE; 551 } 552 553 for(count = 0; count < bytes; count += res) { 554 res = read(fd, buff + count, bytes - count); 555 if(res < 1) { 556 if(res == 0) { 557 ERROR("Read on filesystem failed because " 558 "EOF\n"); 559 return FALSE; 560 } else if(errno != EINTR) { 561 ERROR("Read on filesystem failed because %s\n", 562 strerror(errno)); 563 return FALSE; 564 } else 565 res = 0; 566 } 567 } 568 569 return TRUE; 570} 571 572 573int read_block(int fd, long long start, long long *next, void *block) 574{ 575 unsigned short c_byte; 576 int offset = 2; 577 578 if(swap) { 579 if(read_fs_bytes(fd, start, 2, &c_byte) == FALSE) 580 goto failed; 581 c_byte = (c_byte >> 8) | ((c_byte & 0xff) << 8); 582 } else 583 if(read_fs_bytes(fd, start, 2, &c_byte) == FALSE) 584 goto failed; 585 586 TRACE("read_block: block @0x%llx, %d %s bytes\n", start, 587 SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? 588 "compressed" : "uncompressed"); 589 590 if(SQUASHFS_CHECK_DATA(sBlk.s.flags)) 591 offset = 3; 592 if(SQUASHFS_COMPRESSED(c_byte)) { 593 char buffer[SQUASHFS_METADATA_SIZE]; 594 int error, res; 595 596 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); 597 if(read_fs_bytes(fd, start + offset, c_byte, buffer) == FALSE) 598 goto failed; 599 600 res = compressor_uncompress(comp, block, buffer, c_byte, 601 SQUASHFS_METADATA_SIZE, &error); 602 603 if(res == -1) { 604 ERROR("%s uncompress failed with error code %d\n", 605 comp->name, error); 606 goto failed; 607 } 608 if(next) 609 *next = start + offset + c_byte; 610 return res; 611 } else { 612 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); 613 if(read_fs_bytes(fd, start + offset, c_byte, block) == FALSE) 614 goto failed; 615 if(next) 616 *next = start + offset + c_byte; 617 return c_byte; 618 } 619 620failed: 621 ERROR("read_block: failed to read block @0x%llx\n", start); 622 return FALSE; 623} 624 625 626int read_data_block(long long start, unsigned int size, char *block) 627{ 628 int error, res; 629 int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size); 630 631 TRACE("read_data_block: block @0x%llx, %d %s bytes\n", start, 632 c_byte, SQUASHFS_COMPRESSED_BLOCK(size) ? "compressed" : 633 "uncompressed"); 634 635 if(SQUASHFS_COMPRESSED_BLOCK(size)) { 636 if(read_fs_bytes(fd, start, c_byte, data) == FALSE) 637 goto failed; 638 639 res = compressor_uncompress(comp, block, data, c_byte, 640 block_size, &error); 641 642 if(res == -1) { 643 ERROR("%s uncompress failed with error code %d\n", 644 comp->name, error); 645 goto failed; 646 } 647 648 return res; 649 } else { 650 if(read_fs_bytes(fd, start, c_byte, block) == FALSE) 651 goto failed; 652 653 return c_byte; 654 } 655 656failed: 657 ERROR("read_data_block: failed to read block @0x%llx, size %d\n", start, 658 c_byte); 659 return FALSE; 660} 661 662 663void uncompress_inode_table(long long start, long long end) 664{ 665 int size = 0, bytes = 0, res; 666 667 TRACE("uncompress_inode_table: start %lld, end %lld\n", start, end); 668 while(start < end) { 669 if(size - bytes < SQUASHFS_METADATA_SIZE) { 670 inode_table = realloc(inode_table, size += 671 SQUASHFS_METADATA_SIZE); 672 if(inode_table == NULL) 673 EXIT_UNSQUASH("Out of memory in " 674 "uncompress_inode_table"); 675 } 676 TRACE("uncompress_inode_table: reading block 0x%llx\n", start); 677 add_entry(inode_table_hash, start, bytes); 678 res = read_block(fd, start, &start, inode_table + bytes); 679 if(res == 0) { 680 free(inode_table); 681 EXIT_UNSQUASH("uncompress_inode_table: failed to read " 682 "block \n"); 683 } 684 bytes += res; 685 } 686} 687 688 689int set_attributes(char *pathname, int mode, uid_t uid, gid_t guid, time_t time, 690 unsigned int xattr, unsigned int set_mode) 691{ 692 struct utimbuf times = { time, time }; 693 694 write_xattr(pathname, xattr); 695 696 if(utime(pathname, ×) == -1) { 697 ERROR("set_attributes: failed to set time on %s, because %s\n", 698 pathname, strerror(errno)); 699 return FALSE; 700 } 701 702 if(root_process) { 703 if(chown(pathname, uid, guid) == -1) { 704 ERROR("set_attributes: failed to change uid and gids " 705 "on %s, because %s\n", pathname, 706 strerror(errno)); 707 return FALSE; 708 } 709 } else 710 mode &= ~07000; 711 712 if((set_mode || (mode & 07000)) && chmod(pathname, (mode_t) mode) == -1) { 713 ERROR("set_attributes: failed to change mode %s, because %s\n", 714 pathname, strerror(errno)); 715 return FALSE; 716 } 717 718 return TRUE; 719} 720 721 722int write_bytes(int fd, char *buff, int bytes) 723{ 724 int res, count; 725 726 for(count = 0; count < bytes; count += res) { 727 res = write(fd, buff + count, bytes - count); 728 if(res == -1) { 729 if(errno != EINTR) { 730 ERROR("Write on output file failed because " 731 "%s\n", strerror(errno)); 732 return -1; 733 } 734 res = 0; 735 } 736 } 737 738 return 0; 739} 740 741 742int lseek_broken = FALSE; 743char *zero_data = NULL; 744 745int write_block(int file_fd, char *buffer, int size, long long hole, int sparse) 746{ 747 off_t off = hole; 748 749 if(hole) { 750 if(sparse && lseek_broken == FALSE) { 751 int error = lseek(file_fd, off, SEEK_CUR); 752 if(error == -1) 753 /* failed to seek beyond end of file */ 754 lseek_broken = TRUE; 755 } 756 757 if((sparse == FALSE || lseek_broken) && zero_data == NULL) { 758 if((zero_data = malloc(block_size)) == NULL) 759 EXIT_UNSQUASH("write_block: failed to alloc " 760 "zero data block\n"); 761 memset(zero_data, 0, block_size); 762 } 763 764 if(sparse == FALSE || lseek_broken) { 765 int blocks = (hole + block_size -1) / block_size; 766 int avail_bytes, i; 767 for(i = 0; i < blocks; i++, hole -= avail_bytes) { 768 avail_bytes = hole > block_size ? block_size : 769 hole; 770 if(write_bytes(file_fd, zero_data, avail_bytes) 771 == -1) 772 goto failure; 773 } 774 } 775 } 776 777 if(write_bytes(file_fd, buffer, size) == -1) 778 goto failure; 779 780 return TRUE; 781 782failure: 783 return FALSE; 784} 785 786 787int write_file(struct inode *inode, char *pathname) 788{ 789 unsigned int file_fd, i; 790 unsigned int *block_list; 791 int file_end = inode->data / block_size; 792 long long start = inode->start; 793 struct squashfs_file *file; 794 795 TRACE("write_file: regular file, blocks %d\n", inode->blocks); 796 797 file_fd = open(pathname, O_CREAT | O_WRONLY | (force ? O_TRUNC : 0), 798 (mode_t) inode->mode & 0777); 799 if(file_fd == -1) { 800 ERROR("write_file: failed to create file %s, because %s\n", 801 pathname, strerror(errno)); 802 return FALSE; 803 } 804 805 block_list = malloc(inode->blocks * sizeof(unsigned int)); 806 if(block_list == NULL) 807 EXIT_UNSQUASH("write_file: unable to malloc block list\n"); 808 809 s_ops.read_block_list(block_list, inode->block_ptr, inode->blocks); 810 811 file = malloc(sizeof(struct squashfs_file)); 812 if(file == NULL) 813 EXIT_UNSQUASH("write_file: unable to malloc file\n"); 814 815 /* 816 * the writer thread is queued a squashfs_file structure describing the 817 * file. If the file has one or more blocks or a fragments they are 818 * queued separately (references to blocks in the cache). 819 */ 820 file->fd = file_fd; 821 file->file_size = inode->data; 822 file->mode = inode->mode; 823 file->gid = inode->gid; 824 file->uid = inode->uid; 825 file->time = inode->time; 826 file->pathname = strdup(pathname); 827 file->blocks = inode->blocks + (inode->frag_bytes > 0); 828 file->sparse = inode->sparse; 829 file->xattr = inode->xattr; 830 queue_put(to_writer, file); 831 832 for(i = 0; i < inode->blocks; i++) { 833 int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]); 834 struct file_entry *block = malloc(sizeof(struct file_entry)); 835 836 if(block == NULL) 837 EXIT_UNSQUASH("write_file: unable to malloc file\n"); 838 block->offset = 0; 839 block->size = i == file_end ? inode->data & (block_size - 1) : 840 block_size; 841 if(block_list[i] == 0) /* sparse file */ 842 block->buffer = NULL; 843 else { 844 block->buffer = cache_get(data_cache, start, 845 block_list[i]); 846 start += c_byte; 847 } 848 queue_put(to_writer, block); 849 } 850 851 if(inode->frag_bytes) { 852 int size; 853 long long start; 854 struct file_entry *block = malloc(sizeof(struct file_entry)); 855 856 if(block == NULL) 857 EXIT_UNSQUASH("write_file: unable to malloc file\n"); 858 s_ops.read_fragment(inode->fragment, &start, &size); 859 block->buffer = cache_get(fragment_cache, start, size); 860 block->offset = inode->offset; 861 block->size = inode->frag_bytes; 862 queue_put(to_writer, block); 863 } 864 865 free(block_list); 866 return TRUE; 867} 868 869 870int create_inode(char *pathname, struct inode *i) 871{ 872 TRACE("create_inode: pathname %s\n", pathname); 873 874 if(created_inode[i->inode_number - 1]) { 875 TRACE("create_inode: hard link\n"); 876 if(force) 877 unlink(pathname); 878 879 if(link(created_inode[i->inode_number - 1], pathname) == -1) { 880 ERROR("create_inode: failed to create hardlink, " 881 "because %s\n", strerror(errno)); 882 return FALSE; 883 } 884 885 return TRUE; 886 } 887 888 switch(i->type) { 889 case SQUASHFS_FILE_TYPE: 890 case SQUASHFS_LREG_TYPE: 891 TRACE("create_inode: regular file, file_size %lld, " 892 "blocks %d\n", i->data, i->blocks); 893 894 if(write_file(i, pathname)) 895 file_count ++; 896 break; 897 case SQUASHFS_SYMLINK_TYPE: 898 case SQUASHFS_LSYMLINK_TYPE: 899 TRACE("create_inode: symlink, symlink_size %lld\n", 900 i->data); 901 902 if(force) 903 unlink(pathname); 904 905 if(symlink(i->symlink, pathname) == -1) { 906 ERROR("create_inode: failed to create symlink " 907 "%s, because %s\n", pathname, 908 strerror(errno)); 909 break; 910 } 911 912 write_xattr(pathname, i->xattr); 913 914 if(root_process) { 915 if(lchown(pathname, i->uid, i->gid) == -1) 916 ERROR("create_inode: failed to change " 917 "uid and gids on %s, because " 918 "%s\n", pathname, 919 strerror(errno)); 920 } 921 922 sym_count ++; 923 break; 924 case SQUASHFS_BLKDEV_TYPE: 925 case SQUASHFS_CHRDEV_TYPE: 926 case SQUASHFS_LBLKDEV_TYPE: 927 case SQUASHFS_LCHRDEV_TYPE: { 928 int chrdev = i->type == SQUASHFS_CHRDEV_TYPE; 929 TRACE("create_inode: dev, rdev 0x%llx\n", i->data); 930 931 if(root_process) { 932 if(force) 933 unlink(pathname); 934 935 if(mknod(pathname, chrdev ? S_IFCHR : S_IFBLK, 936 makedev((i->data >> 8) & 0xff, 937 i->data & 0xff)) == -1) { 938 ERROR("create_inode: failed to create " 939 "%s device %s, because %s\n", 940 chrdev ? "character" : "block", 941 pathname, strerror(errno)); 942 break; 943 } 944 set_attributes(pathname, i->mode, i->uid, 945 i->gid, i->time, i->xattr, TRUE); 946 dev_count ++; 947 } else 948 ERROR("create_inode: could not create %s " 949 "device %s, because you're not " 950 "superuser!\n", chrdev ? "character" : 951 "block", pathname); 952 break; 953 } 954 case SQUASHFS_FIFO_TYPE: 955 case SQUASHFS_LFIFO_TYPE: 956 TRACE("create_inode: fifo\n"); 957 958 if(force) 959 unlink(pathname); 960 961 if(mknod(pathname, S_IFIFO, 0) == -1) { 962 ERROR("create_inode: failed to create fifo %s, " 963 "because %s\n", pathname, 964 strerror(errno)); 965 break; 966 } 967 set_attributes(pathname, i->mode, i->uid, i->gid, 968 i->time, i->xattr, TRUE); 969 fifo_count ++; 970 break; 971 case SQUASHFS_SOCKET_TYPE: 972 case SQUASHFS_LSOCKET_TYPE: 973 TRACE("create_inode: socket\n"); 974 ERROR("create_inode: socket %s ignored\n", pathname); 975 break; 976 default: 977 ERROR("Unknown inode type %d in create_inode_table!\n", 978 i->type); 979 return FALSE; 980 } 981 982 created_inode[i->inode_number - 1] = strdup(pathname); 983 984 return TRUE; 985} 986 987 988void uncompress_directory_table(long long start, long long end) 989{ 990 int bytes = 0, size = 0, res; 991 992 TRACE("uncompress_directory_table: start %lld, end %lld\n", start, end); 993 994 while(start < end) { 995 if(size - bytes < SQUASHFS_METADATA_SIZE) { 996 directory_table = realloc(directory_table, size += 997 SQUASHFS_METADATA_SIZE); 998 if(directory_table == NULL) 999 EXIT_UNSQUASH("Out of memory in " 1000 "uncompress_directory_table\n"); 1001 } 1002 TRACE("uncompress_directory_table: reading block 0x%llx\n", 1003 start); 1004 add_entry(directory_table_hash, start, bytes); 1005 res = read_block(fd, start, &start, directory_table + bytes); 1006 if(res == 0) 1007 EXIT_UNSQUASH("uncompress_directory_table: failed to " 1008 "read block\n"); 1009 bytes += res; 1010 } 1011} 1012 1013 1014int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, 1015unsigned int *offset, unsigned int *type) 1016{ 1017 if(dir->cur_entry == dir->dir_count) 1018 return FALSE; 1019 1020 *name = dir->dirs[dir->cur_entry].name; 1021 *start_block = dir->dirs[dir->cur_entry].start_block; 1022 *offset = dir->dirs[dir->cur_entry].offset; 1023 *type = dir->dirs[dir->cur_entry].type; 1024 dir->cur_entry ++; 1025 1026 return TRUE; 1027} 1028 1029 1030void squashfs_closedir(struct dir *dir) 1031{ 1032 free(dir->dirs); 1033 free(dir); 1034} 1035 1036 1037char *get_component(char *target, char *targname) 1038{ 1039 while(*target == '/') 1040 target ++; 1041 1042 while(*target != '/' && *target!= '\0') 1043 *targname ++ = *target ++; 1044 1045 *targname = '\0'; 1046 1047 return target; 1048} 1049 1050 1051void free_path(struct pathname *paths) 1052{ 1053 int i; 1054 1055 for(i = 0; i < paths->names; i++) { 1056 if(paths->name[i].paths) 1057 free_path(paths->name[i].paths); 1058 free(paths->name[i].name); 1059 if(paths->name[i].preg) { 1060 regfree(paths->name[i].preg); 1061 free(paths->name[i].preg); 1062 } 1063 } 1064 1065 free(paths); 1066} 1067 1068 1069struct pathname *add_path(struct pathname *paths, char *target, char *alltarget) 1070{ 1071 char targname[1024]; 1072 int i, error; 1073 1074 TRACE("add_path: adding \"%s\" extract file\n", target); 1075 1076 target = get_component(target, targname); 1077 1078 if(paths == NULL) { 1079 paths = malloc(sizeof(struct pathname)); 1080 if(paths == NULL) 1081 EXIT_UNSQUASH("failed to allocate paths\n"); 1082 1083 paths->names = 0; 1084 paths->name = NULL; 1085 } 1086 1087 for(i = 0; i < paths->names; i++) 1088 if(strcmp(paths->name[i].name, targname) == 0) 1089 break; 1090 1091 if(i == paths->names) { 1092 /* 1093 * allocate new name entry 1094 */ 1095 paths->names ++; 1096 paths->name = realloc(paths->name, (i + 1) * 1097 sizeof(struct path_entry)); 1098 if(paths->name == NULL) 1099 EXIT_UNSQUASH("Out of memory in add_path\n"); 1100 paths->name[i].name = strdup(targname); 1101 paths->name[i].paths = NULL; 1102 if(use_regex) { 1103 paths->name[i].preg = malloc(sizeof(regex_t)); 1104 if(paths->name[i].preg == NULL) 1105 EXIT_UNSQUASH("Out of memory in add_path\n"); 1106 error = regcomp(paths->name[i].preg, targname, 1107 REG_EXTENDED|REG_NOSUB); 1108 if(error) { 1109 char str[1024]; 1110 1111 regerror(error, paths->name[i].preg, str, 1024); 1112 EXIT_UNSQUASH("invalid regex %s in export %s, " 1113 "because %s\n", targname, alltarget, 1114 str); 1115 } 1116 } else 1117 paths->name[i].preg = NULL; 1118 1119 if(target[0] == '\0') 1120 /* 1121 * at leaf pathname component 1122 */ 1123 paths->name[i].paths = NULL; 1124 else 1125 /* 1126 * recurse adding child components 1127 */ 1128 paths->name[i].paths = add_path(NULL, target, alltarget); 1129 } else { 1130 /* 1131 * existing matching entry 1132 */ 1133 if(paths->name[i].paths == NULL) { 1134 /* 1135 * No sub-directory which means this is the leaf 1136 * component of a pre-existing extract which subsumes 1137 * the extract currently being added, in which case stop 1138 * adding components 1139 */ 1140 } else if(target[0] == '\0') { 1141 /* 1142 * at leaf pathname component and child components exist 1143 * from more specific extracts, delete as they're 1144 * subsumed by this extract 1145 */ 1146 free_path(paths->name[i].paths); 1147 paths->name[i].paths = NULL; 1148 } else 1149 /* 1150 * recurse adding child components 1151 */ 1152 add_path(paths->name[i].paths, target, alltarget); 1153 } 1154 1155 return paths; 1156} 1157 1158 1159struct pathnames *init_subdir() 1160{ 1161 struct pathnames *new = malloc(sizeof(struct pathnames)); 1162 if(new == NULL) 1163 EXIT_UNSQUASH("Out of memory in init_subdir\n"); 1164 new->count = 0; 1165 return new; 1166} 1167 1168 1169struct pathnames *add_subdir(struct pathnames *paths, struct pathname *path) 1170{ 1171 if(paths->count % PATHS_ALLOC_SIZE == 0) { 1172 paths = realloc(paths, sizeof(struct pathnames *) + 1173 (paths->count + PATHS_ALLOC_SIZE) * 1174 sizeof(struct pathname *)); 1175 if(paths == NULL) 1176 EXIT_UNSQUASH("Out of memory in add_subdir\n"); 1177 } 1178 1179 paths->path[paths->count++] = path; 1180 return paths; 1181} 1182 1183 1184void free_subdir(struct pathnames *paths) 1185{ 1186 free(paths); 1187} 1188 1189 1190int matches(struct pathnames *paths, char *name, struct pathnames **new) 1191{ 1192 int i, n; 1193 1194 if(paths == NULL) { 1195 *new = NULL; 1196 return TRUE; 1197 } 1198 1199 *new = init_subdir(); 1200 1201 for(n = 0; n < paths->count; n++) { 1202 struct pathname *path = paths->path[n]; 1203 for(i = 0; i < path->names; i++) { 1204 int match = use_regex ? 1205 regexec(path->name[i].preg, name, (size_t) 0, 1206 NULL, 0) == 0 : fnmatch(path->name[i].name, 1207 name, FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 1208 0; 1209 if(match && path->name[i].paths == NULL) 1210 /* 1211 * match on a leaf component, any subdirectories 1212 * will implicitly match, therefore return an 1213 * empty new search set 1214 */ 1215 goto empty_set; 1216 1217 if(match) 1218 /* 1219 * match on a non-leaf component, add any 1220 * subdirectories to the new set of 1221 * subdirectories to scan for this name 1222 */ 1223 *new = add_subdir(*new, path->name[i].paths); 1224 } 1225 } 1226 1227 if((*new)->count == 0) { 1228 /* 1229 * no matching names found, delete empty search set, and return 1230 * FALSE 1231 */ 1232 free_subdir(*new); 1233 *new = NULL; 1234 return FALSE; 1235 } 1236 1237 /* 1238 * one or more matches with sub-directories found (no leaf matches), 1239 * return new search set and return TRUE 1240 */ 1241 return TRUE; 1242 1243empty_set: 1244 /* 1245 * found matching leaf exclude, return empty search set and return TRUE 1246 */ 1247 free_subdir(*new); 1248 *new = NULL; 1249 return TRUE; 1250} 1251 1252 1253void pre_scan(char *parent_name, unsigned int start_block, unsigned int offset, 1254 struct pathnames *paths) 1255{ 1256 unsigned int type; 1257 char *name, pathname[1024]; 1258 struct pathnames *new; 1259 struct inode *i; 1260 struct dir *dir = s_ops.squashfs_opendir(start_block, offset, &i); 1261 1262 while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) { 1263 struct inode *i; 1264 1265 TRACE("pre_scan: name %s, start_block %d, offset %d, type %d\n", 1266 name, start_block, offset, type); 1267 1268 if(!matches(paths, name, &new)) 1269 continue; 1270 1271 strcat(strcat(strcpy(pathname, parent_name), "/"), name); 1272 1273 if(type == SQUASHFS_DIR_TYPE) 1274 pre_scan(parent_name, start_block, offset, new); 1275 else if(new == NULL) { 1276 if(type == SQUASHFS_FILE_TYPE || 1277 type == SQUASHFS_LREG_TYPE) { 1278 i = s_ops.read_inode(start_block, offset); 1279 if(created_inode[i->inode_number - 1] == NULL) { 1280 created_inode[i->inode_number - 1] = 1281 (char *) i; 1282 total_blocks += (i->data + 1283 (block_size - 1)) >> block_log; 1284 } 1285 total_files ++; 1286 } 1287 total_inodes ++; 1288 } 1289 1290 free_subdir(new); 1291 } 1292 1293 squashfs_closedir(dir); 1294} 1295 1296 1297void dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, 1298 struct pathnames *paths) 1299{ 1300 unsigned int type; 1301 char *name, pathname[1024]; 1302 struct pathnames *new; 1303 struct inode *i; 1304 struct dir *dir = s_ops.squashfs_opendir(start_block, offset, &i); 1305 1306 if(lsonly || info) 1307 print_filename(parent_name, i); 1308 1309 if(!lsonly && mkdir(parent_name, (mode_t) dir->mode) == -1 && 1310 (!force || errno != EEXIST)) { 1311 ERROR("dir_scan: failed to make directory %s, because %s\n", 1312 parent_name, strerror(errno)); 1313 squashfs_closedir(dir); 1314 return; 1315 } 1316 1317 while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) { 1318 TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", 1319 name, start_block, offset, type); 1320 1321 1322 if(!matches(paths, name, &new)) 1323 continue; 1324 1325 strcat(strcat(strcpy(pathname, parent_name), "/"), name); 1326 1327 if(type == SQUASHFS_DIR_TYPE) 1328 dir_scan(pathname, start_block, offset, new); 1329 else if(new == NULL) { 1330 i = s_ops.read_inode(start_block, offset); 1331 1332 if(lsonly || info) 1333 print_filename(pathname, i); 1334 1335 if(!lsonly) { 1336 create_inode(pathname, i); 1337 update_progress_bar(); 1338 } 1339 1340 if(i->type == SQUASHFS_SYMLINK_TYPE || 1341 i->type == SQUASHFS_LSYMLINK_TYPE) 1342 free(i->symlink); 1343 } 1344 1345 free_subdir(new); 1346 } 1347 1348 if(!lsonly) 1349 set_attributes(parent_name, dir->mode, dir->uid, dir->guid, 1350 dir->mtime, dir->xattr, force); 1351 1352 squashfs_closedir(dir); 1353 dir_count ++; 1354} 1355 1356 1357void squashfs_stat(char *source) 1358{ 1359 time_t mkfs_time = (time_t) sBlk.s.mkfs_time; 1360 char *mkfs_str = ctime(&mkfs_time); 1361 1362#if __BYTE_ORDER == __BIG_ENDIAN 1363 printf("Found a valid %sSQUASHFS %d:%d superblock on %s.\n", 1364 sBlk.s.s_major == 4 ? "" : swap ? "little endian " : 1365 "big endian ", sBlk.s.s_major, sBlk.s.s_minor, source); 1366#else 1367 printf("Found a valid %sSQUASHFS %d:%d superblock on %s.\n", 1368 sBlk.s.s_major == 4 ? "" : swap ? "big endian " : 1369 "little endian ", sBlk.s.s_major, sBlk.s.s_minor, source); 1370#endif 1371 1372 printf("Creation or last append time %s", mkfs_str ? mkfs_str : 1373 "failed to get time\n"); 1374 printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", 1375 sBlk.s.bytes_used / 1024.0, sBlk.s.bytes_used / 1376 (1024.0 * 1024.0)); 1377 1378 if(sBlk.s.s_major == 4) 1379 printf("Compression %s\n", comp->name); 1380 1381 printf("Block size %d\n", sBlk.s.block_size); 1382 printf("Filesystem is %sexportable via NFS\n", 1383 SQUASHFS_EXPORTABLE(sBlk.s.flags) ? "" : "not "); 1384 printf("Inodes are %scompressed\n", 1385 SQUASHFS_UNCOMPRESSED_INODES(sBlk.s.flags) ? "un" : ""); 1386 printf("Data is %scompressed\n", 1387 SQUASHFS_UNCOMPRESSED_DATA(sBlk.s.flags) ? "un" : ""); 1388 1389 if(sBlk.s.s_major > 1) { 1390 if(SQUASHFS_NO_FRAGMENTS(sBlk.s.flags)) 1391 printf("Fragments are not stored\n"); 1392 else { 1393 printf("Fragments are %scompressed\n", 1394 SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.s.flags) ? 1395 "un" : ""); 1396 printf("Always_use_fragments option is %sspecified\n", 1397 SQUASHFS_ALWAYS_FRAGMENTS(sBlk.s.flags) ? "" : 1398 "not "); 1399 } 1400 } 1401 1402 if(sBlk.s.s_major == 4) { 1403 if(SQUASHFS_NO_XATTRS(sBlk.s.flags)) 1404 printf("Xattrs are not stored\n"); 1405 else 1406 printf("Xattrs are %scompressed\n", 1407 SQUASHFS_UNCOMPRESSED_XATTRS(sBlk.s.flags) ? 1408 "un" : ""); 1409 } 1410 1411 if(sBlk.s.s_major < 4) 1412 printf("Check data is %spresent in the filesystem\n", 1413 SQUASHFS_CHECK_DATA(sBlk.s.flags) ? "" : 1414 "not "); 1415 1416 if(sBlk.s.s_major > 1) 1417 printf("Duplicates are %sremoved\n", 1418 SQUASHFS_DUPLICATES(sBlk.s.flags) ? "" : "not "); 1419 else 1420 printf("Duplicates are removed\n"); 1421 1422 if(sBlk.s.s_major > 1) 1423 printf("Number of fragments %d\n", sBlk.s.fragments); 1424 1425 printf("Number of inodes %d\n", sBlk.s.inodes); 1426 1427 if(sBlk.s.s_major == 4) 1428 printf("Number of ids %d\n", sBlk.s.no_ids); 1429 else { 1430 printf("Number of uids %d\n", sBlk.no_uids); 1431 printf("Number of gids %d\n", sBlk.no_guids); 1432 } 1433 1434 TRACE("sBlk.s.inode_table_start 0x%llx\n", sBlk.s.inode_table_start); 1435 TRACE("sBlk.s.directory_table_start 0x%llx\n", 1436 sBlk.s.directory_table_start); 1437 1438 if(sBlk.s.s_major == 4) { 1439 TRACE("sBlk.s.id_table_start 0x%llx\n", sBlk.s.id_table_start); 1440 TRACE("sBlk.s.xattr_id_table_start 0x%llx\n", 1441 sBlk.s.xattr_id_table_start); 1442 } else { 1443 TRACE("sBlk.uid_start 0x%llx\n", sBlk.uid_start); 1444 TRACE("sBlk.guid_start 0x%llx\n", sBlk.guid_start); 1445 } 1446 1447 if(sBlk.s.s_major > 1) 1448 TRACE("sBlk.s.fragment_table_start 0x%llx\n\n", 1449 sBlk.s.fragment_table_start); 1450} 1451 1452 1453int read_super(char *source) 1454{ 1455 squashfs_super_block_3 sBlk_3; 1456 struct squashfs_super_block sBlk_4; 1457 1458 /* 1459 * Try to read a Squashfs 4 superblock 1460 */ 1461 read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block), 1462 &sBlk_4); 1463 swap = sBlk_4.s_magic != SQUASHFS_MAGIC; 1464 SQUASHFS_INSWAP_SUPER_BLOCK(&sBlk_4); 1465 1466 if(sBlk_4.s_magic == SQUASHFS_MAGIC && sBlk_4.s_major == 4 && 1467 sBlk_4.s_minor == 0) { 1468 s_ops.squashfs_opendir = squashfs_opendir_4; 1469 s_ops.read_fragment = read_fragment_4; 1470 s_ops.read_fragment_table = read_fragment_table_4; 1471 s_ops.read_block_list = read_block_list_2; 1472 s_ops.read_inode = read_inode_4; 1473 s_ops.read_uids_guids = read_uids_guids_4; 1474 memcpy(&sBlk, &sBlk_4, sizeof(sBlk_4)); 1475 1476 /* 1477 * Check the compression type 1478 */ 1479 comp = lookup_compressor_id(sBlk.s.compression); 1480 return TRUE; 1481 } 1482 1483 /* 1484 * Not a Squashfs 4 superblock, try to read a squashfs 3 superblock 1485 * (compatible with 1 and 2 filesystems) 1486 */ 1487 read_fs_bytes(fd, SQUASHFS_START, sizeof(squashfs_super_block_3), 1488 &sBlk_3); 1489 1490 /* 1491 * Check it is a SQUASHFS superblock 1492 */ 1493 swap = 0; 1494 if(sBlk_3.s_magic != SQUASHFS_MAGIC) { 1495 if(sBlk_3.s_magic == SQUASHFS_MAGIC_SWAP) { 1496 squashfs_super_block_3 sblk; 1497 ERROR("Reading a different endian SQUASHFS filesystem " 1498 "on %s\n", source); 1499 SQUASHFS_SWAP_SUPER_BLOCK_3(&sblk, &sBlk_3); 1500 memcpy(&sBlk_3, &sblk, sizeof(squashfs_super_block_3)); 1501 swap = 1; 1502 } else { 1503 ERROR("Can't find a SQUASHFS superblock on %s\n", 1504 source); 1505 goto failed_mount; 1506 } 1507 } 1508 1509 sBlk.s.s_magic = sBlk_3.s_magic; 1510 sBlk.s.inodes = sBlk_3.inodes; 1511 sBlk.s.mkfs_time = sBlk_3.mkfs_time; 1512 sBlk.s.block_size = sBlk_3.block_size; 1513 sBlk.s.fragments = sBlk_3.fragments; 1514 sBlk.s.block_log = sBlk_3.block_log; 1515 sBlk.s.flags = sBlk_3.flags; 1516 sBlk.s.s_major = sBlk_3.s_major; 1517 sBlk.s.s_minor = sBlk_3.s_minor; 1518 sBlk.s.root_inode = sBlk_3.root_inode; 1519 sBlk.s.bytes_used = sBlk_3.bytes_used; 1520 sBlk.s.inode_table_start = sBlk_3.inode_table_start; 1521 sBlk.s.directory_table_start = sBlk_3.directory_table_start; 1522 sBlk.s.fragment_table_start = sBlk_3.fragment_table_start; 1523 sBlk.s.lookup_table_start = sBlk_3.lookup_table_start; 1524 sBlk.no_uids = sBlk_3.no_uids; 1525 sBlk.no_guids = sBlk_3.no_guids; 1526 sBlk.uid_start = sBlk_3.uid_start; 1527 sBlk.guid_start = sBlk_3.guid_start; 1528 sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK; 1529 1530 /* Check the MAJOR & MINOR versions */ 1531 if(sBlk.s.s_major == 1 || sBlk.s.s_major == 2) { 1532 sBlk.s.bytes_used = sBlk_3.bytes_used_2; 1533 sBlk.uid_start = sBlk_3.uid_start_2; 1534 sBlk.guid_start = sBlk_3.guid_start_2; 1535 sBlk.s.inode_table_start = sBlk_3.inode_table_start_2; 1536 sBlk.s.directory_table_start = sBlk_3.directory_table_start_2; 1537 1538 if(sBlk.s.s_major == 1) { 1539 sBlk.s.block_size = sBlk_3.block_size_1; 1540 sBlk.s.fragment_table_start = sBlk.uid_start; 1541 s_ops.squashfs_opendir = squashfs_opendir_1; 1542 s_ops.read_fragment_table = read_fragment_table_1; 1543 s_ops.read_block_list = read_block_list_1; 1544 s_ops.read_inode = read_inode_1; 1545 s_ops.read_uids_guids = read_uids_guids_1; 1546 } else { 1547 sBlk.s.fragment_table_start = 1548 sBlk_3.fragment_table_start_2; 1549 s_ops.squashfs_opendir = squashfs_opendir_1; 1550 s_ops.read_fragment = read_fragment_2; 1551 s_ops.read_fragment_table = read_fragment_table_2; 1552 s_ops.read_block_list = read_block_list_2; 1553 s_ops.read_inode = read_inode_2; 1554 s_ops.read_uids_guids = read_uids_guids_1; 1555 } 1556 } else if(sBlk.s.s_major == 3) { 1557 s_ops.squashfs_opendir = squashfs_opendir_3; 1558 s_ops.read_fragment = read_fragment_3; 1559 s_ops.read_fragment_table = read_fragment_table_3; 1560 s_ops.read_block_list = read_block_list_2; 1561 s_ops.read_inode = read_inode_3; 1562 s_ops.read_uids_guids = read_uids_guids_1; 1563 } else { 1564 ERROR("Filesystem on %s is (%d:%d), ", source, sBlk.s.s_major, 1565 sBlk.s.s_minor); 1566 ERROR("which is a later filesystem version than I support!\n"); 1567 goto failed_mount; 1568 } 1569 1570 /* 1571 * 1.x, 2.x and 3.x filesystems use gzip compression. 1572 */ 1573 comp = lookup_compressor("gzip"); 1574 return TRUE; 1575 1576failed_mount: 1577 return FALSE; 1578} 1579 1580 1581struct pathname *process_extract_files(struct pathname *path, char *filename) 1582{ 1583 FILE *fd; 1584 char name[16384]; 1585 1586 fd = fopen(filename, "r"); 1587 if(fd == NULL) 1588 EXIT_UNSQUASH("Could not open %s, because %s\n", filename, 1589 strerror(errno)); 1590 1591 while(fscanf(fd, "%16384[^\n]\n", name) != EOF) 1592 path = add_path(path, name, name); 1593 1594 fclose(fd); 1595 return path; 1596} 1597 1598 1599/* 1600 * reader thread. This thread processes read requests queued by the 1601 * cache_get() routine. 1602 */ 1603void *reader(void *arg) 1604{ 1605 while(1) { 1606 struct cache_entry *entry = queue_get(to_reader); 1607 int res = read_fs_bytes(fd, entry->block, 1608 SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size), 1609 entry->data); 1610 1611 if(res && SQUASHFS_COMPRESSED_BLOCK(entry->size)) 1612 /* 1613 * queue successfully read block to the deflate 1614 * thread(s) for further processing 1615 */ 1616 queue_put(to_deflate, entry); 1617 else 1618 /* 1619 * block has either been successfully read and is 1620 * uncompressed, or an error has occurred, clear pending 1621 * flag, set error appropriately, and wake up any 1622 * threads waiting on this buffer 1623 */ 1624 cache_block_ready(entry, !res); 1625 } 1626} 1627 1628 1629/* 1630 * writer thread. This processes file write requests queued by the 1631 * write_file() routine. 1632 */ 1633void *writer(void *arg) 1634{ 1635 int i; 1636 1637 while(1) { 1638 struct squashfs_file *file = queue_get(to_writer); 1639 int file_fd; 1640 long long hole = 0; 1641 int failed = FALSE; 1642 int error; 1643 1644 if(file == NULL) { 1645 queue_put(from_writer, NULL); 1646 continue; 1647 } 1648 1649 TRACE("writer: regular file, blocks %d\n", file->blocks); 1650 1651 file_fd = file->fd; 1652 1653 for(i = 0; i < file->blocks; i++, cur_blocks ++) { 1654 struct file_entry *block = queue_get(to_writer); 1655 1656 if(block->buffer == 0) { /* sparse file */ 1657 hole += block->size; 1658 free(block); 1659 continue; 1660 } 1661 1662 cache_block_wait(block->buffer); 1663 1664 if(block->buffer->error) 1665 failed = TRUE; 1666 1667 if(failed) 1668 continue; 1669 1670 error = write_block(file_fd, block->buffer->data + 1671 block->offset, block->size, hole, file->sparse); 1672 1673 if(error == FALSE) { 1674 ERROR("writer: failed to write data block %d\n", 1675 i); 1676 failed = TRUE; 1677 } 1678 1679 hole = 0; 1680 cache_block_put(block->buffer); 1681 free(block); 1682 } 1683 1684 if(hole && failed == FALSE) { 1685 /* 1686 * corner case for hole extending to end of file 1687 */ 1688 if(file->sparse == FALSE || 1689 lseek(file_fd, hole, SEEK_CUR) == -1) { 1690 /* 1691 * for files which we don't want to write 1692 * sparsely, or for broken lseeks which cannot 1693 * seek beyond end of file, write_block will do 1694 * the right thing 1695 */ 1696 hole --; 1697 if(write_block(file_fd, "\0", 1, hole, 1698 file->sparse) == FALSE) { 1699 ERROR("writer: failed to write sparse " 1700 "data block\n"); 1701 failed = TRUE; 1702 } 1703 } else if(ftruncate(file_fd, file->file_size) == -1) { 1704 ERROR("writer: failed to write sparse data " 1705 "block\n"); 1706 failed = TRUE; 1707 } 1708 } 1709 1710 close(file_fd); 1711 if(failed == FALSE) 1712 set_attributes(file->pathname, file->mode, file->uid, 1713 file->gid, file->time, file->xattr, force); 1714 else { 1715 ERROR("Failed to write %s, skipping\n", file->pathname); 1716 unlink(file->pathname); 1717 } 1718 free(file->pathname); 1719 free(file); 1720 1721 } 1722} 1723 1724 1725/* 1726 * decompress thread. This decompresses buffers queued by the read thread 1727 */ 1728void *deflator(void *arg) 1729{ 1730 char tmp[block_size]; 1731 1732 while(1) { 1733 struct cache_entry *entry = queue_get(to_deflate); 1734 int error, res; 1735 1736 res = compressor_uncompress(comp, tmp, entry->data, 1737 SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size), block_size, 1738 &error); 1739 1740 if(res == -1) 1741 ERROR("%s uncompress failed with error code %d\n", 1742 comp->name, error); 1743 else 1744 memcpy(entry->data, tmp, res); 1745 1746 /* 1747 * block has been either successfully decompressed, or an error 1748 * occurred, clear pending flag, set error appropriately and 1749 * wake up any threads waiting on this block 1750 */ 1751 cache_block_ready(entry, res == -1); 1752 } 1753} 1754 1755 1756void *progress_thread(void *arg) 1757{ 1758 struct timeval timeval; 1759 struct timespec timespec; 1760 struct itimerval itimerval; 1761 struct winsize winsize; 1762 1763 if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { 1764 if(isatty(STDOUT_FILENO)) 1765 ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " 1766 "columns\n"); 1767 columns = 80; 1768 } else 1769 columns = winsize.ws_col; 1770 signal(SIGWINCH, sigwinch_handler); 1771 signal(SIGALRM, sigalrm_handler); 1772 1773 itimerval.it_value.tv_sec = 0; 1774 itimerval.it_value.tv_usec = 250000; 1775 itimerval.it_interval.tv_sec = 0; 1776 itimerval.it_interval.tv_usec = 250000; 1777 setitimer(ITIMER_REAL, &itimerval, NULL); 1778 1779 pthread_cond_init(&progress_wait, NULL); 1780 1781 pthread_mutex_lock(&screen_mutex); 1782 while(1) { 1783 gettimeofday(&timeval, NULL); 1784 timespec.tv_sec = timeval.tv_sec; 1785 if(timeval.tv_usec + 250000 > 999999) 1786 timespec.tv_sec++; 1787 timespec.tv_nsec = ((timeval.tv_usec + 250000) % 1000000) * 1788 1000; 1789 pthread_cond_timedwait(&progress_wait, &screen_mutex, 1790 ×pec); 1791 if(progress_enabled) 1792 progress_bar(sym_count + dev_count + 1793 fifo_count + cur_blocks, total_inodes - 1794 total_files + total_blocks, columns); 1795 } 1796} 1797 1798 1799void initialise_threads(int fragment_buffer_size, int data_buffer_size) 1800{ 1801 int i; 1802 sigset_t sigmask, old_mask; 1803 int all_buffers_size = fragment_buffer_size + data_buffer_size; 1804 1805 sigemptyset(&sigmask); 1806 sigaddset(&sigmask, SIGINT); 1807 sigaddset(&sigmask, SIGQUIT); 1808 if(sigprocmask(SIG_BLOCK, &sigmask, &old_mask) == -1) 1809 EXIT_UNSQUASH("Failed to set signal mask in intialise_threads" 1810 "\n"); 1811 1812 if(processors == -1) { 1813#ifndef linux 1814 int mib[2]; 1815 size_t len = sizeof(processors); 1816 1817 mib[0] = CTL_HW; 1818#ifdef HW_AVAILCPU 1819 mib[1] = HW_AVAILCPU; 1820#else 1821 mib[1] = HW_NCPU; 1822#endif 1823 1824 if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) { 1825 ERROR("Failed to get number of available processors. " 1826 "Defaulting to 1\n"); 1827 processors = 1; 1828 } 1829#else 1830 processors = sysconf(_SC_NPROCESSORS_ONLN); 1831#endif 1832 } 1833 1834 thread = malloc((3 + processors) * sizeof(pthread_t)); 1835 if(thread == NULL) 1836 EXIT_UNSQUASH("Out of memory allocating thread descriptors\n"); 1837 deflator_thread = &thread[3]; 1838 1839 to_reader = queue_init(all_buffers_size); 1840 to_deflate = queue_init(all_buffers_size); 1841 to_writer = queue_init(1000); 1842 from_writer = queue_init(1); 1843 fragment_cache = cache_init(block_size, fragment_buffer_size); 1844 data_cache = cache_init(block_size, data_buffer_size); 1845 pthread_create(&thread[0], NULL, reader, NULL); 1846 pthread_create(&thread[1], NULL, writer, NULL); 1847 pthread_create(&thread[2], NULL, progress_thread, NULL); 1848 pthread_mutex_init(&fragment_mutex, NULL); 1849 1850 for(i = 0; i < processors; i++) { 1851 if(pthread_create(&deflator_thread[i], NULL, deflator, NULL) != 1852 0) 1853 EXIT_UNSQUASH("Failed to create thread\n"); 1854 } 1855 1856 printf("Parallel unsquashfs: Using %d processor%s\n", processors, 1857 processors == 1 ? "" : "s"); 1858 1859 if(sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) 1860 EXIT_UNSQUASH("Failed to set signal mask in intialise_threads" 1861 "\n"); 1862} 1863 1864 1865void enable_progress_bar() 1866{ 1867 pthread_mutex_lock(&screen_mutex); 1868 progress_enabled = TRUE; 1869 pthread_mutex_unlock(&screen_mutex); 1870} 1871 1872 1873void disable_progress_bar() 1874{ 1875 pthread_mutex_lock(&screen_mutex); 1876 progress_enabled = FALSE; 1877 pthread_mutex_unlock(&screen_mutex); 1878} 1879 1880 1881void update_progress_bar() 1882{ 1883 pthread_mutex_lock(&screen_mutex); 1884 pthread_cond_signal(&progress_wait); 1885 pthread_mutex_unlock(&screen_mutex); 1886} 1887 1888 1889void progress_bar(long long current, long long max, int columns) 1890{ 1891 char rotate_list[] = { '|', '/', '-', '\\' }; 1892 int max_digits, used, hashes, spaces; 1893 static int tty = -1; 1894 1895 if(max == 0) 1896 return; 1897 1898 max_digits = floor(log10(max)) + 1; 1899 used = max_digits * 2 + 11; 1900 hashes = (current * (columns - used)) / max; 1901 spaces = columns - used - hashes; 1902 1903 if((current > max) || (columns - used < 0)) 1904 return; 1905 1906 if(tty == -1) 1907 tty = isatty(STDOUT_FILENO); 1908 if(!tty) { 1909 static long long previous = -1; 1910 1911 /* 1912 * Updating much more frequently than this results in huge 1913 * log files. 1914 */ 1915 if((current % 100) != 0 && current != max) 1916 return; 1917 /* Don't update just to rotate the spinner. */ 1918 if(current == previous) 1919 return; 1920 previous = current; 1921 } 1922 1923 printf("\r["); 1924 1925 while (hashes --) 1926 putchar('='); 1927 1928 putchar(rotate_list[rotate]); 1929 1930 while(spaces --) 1931 putchar(' '); 1932 1933 printf("] %*lld/%*lld", max_digits, current, max_digits, max); 1934 printf(" %3lld%%", current * 100 / max); 1935 fflush(stdout); 1936} 1937 1938 1939#define VERSION() \ 1940 printf("unsquashfs version 4.2 (2011/02/28)\n");\ 1941 printf("copyright (C) 2011 Phillip Lougher "\ 1942 "<phillip@lougher.demon.co.uk>\n\n");\ 1943 printf("This program is free software; you can redistribute it and/or"\ 1944 "\n");\ 1945 printf("modify it under the terms of the GNU General Public License"\ 1946 "\n");\ 1947 printf("as published by the Free Software Foundation; either version "\ 1948 "2,\n");\ 1949 printf("or (at your option) any later version.\n\n");\ 1950 printf("This program is distributed in the hope that it will be "\ 1951 "useful,\n");\ 1952 printf("but WITHOUT ANY WARRANTY; without even the implied warranty of"\ 1953 "\n");\ 1954 printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"\ 1955 "\n");\ 1956 printf("GNU General Public License for more details.\n"); 1957int main(int argc, char *argv[]) 1958{ 1959 char *dest = "squashfs-root"; 1960 int i, stat_sys = FALSE, version = FALSE; 1961 int n; 1962 struct pathnames *paths = NULL; 1963 struct pathname *path = NULL; 1964 int fragment_buffer_size = FRAGMENT_BUFFER_DEFAULT; 1965 int data_buffer_size = DATA_BUFFER_DEFAULT; 1966 char *b; 1967 1968 pthread_mutex_init(&screen_mutex, NULL); 1969 root_process = geteuid() == 0; 1970 if(root_process) 1971 umask(0); 1972 1973 for(i = 1; i < argc; i++) { 1974 if(*argv[i] != '-') 1975 break; 1976 if(strcmp(argv[i], "-version") == 0 || 1977 strcmp(argv[i], "-v") == 0) { 1978 VERSION(); 1979 version = TRUE; 1980 } else if(strcmp(argv[i], "-info") == 0 || 1981 strcmp(argv[i], "-i") == 0) 1982 info = TRUE; 1983 else if(strcmp(argv[i], "-ls") == 0 || 1984 strcmp(argv[i], "-l") == 0) 1985 lsonly = TRUE; 1986 else if(strcmp(argv[i], "-no-progress") == 0 || 1987 strcmp(argv[i], "-n") == 0) 1988 progress = FALSE; 1989 else if(strcmp(argv[i], "-no-xattrs") == 0 || 1990 strcmp(argv[i], "-no") == 0) 1991 no_xattrs = TRUE; 1992 else if(strcmp(argv[i], "-xattrs") == 0 || 1993 strcmp(argv[i], "-x") == 0) 1994 no_xattrs = FALSE; 1995 else if(strcmp(argv[i], "-dest") == 0 || 1996 strcmp(argv[i], "-d") == 0) { 1997 if(++i == argc) { 1998 fprintf(stderr, "%s: -dest missing filename\n", 1999 argv[0]); 2000 exit(1); 2001 } 2002 dest = argv[i]; 2003 } else if(strcmp(argv[i], "-processors") == 0 || 2004 strcmp(argv[i], "-p") == 0) { 2005 if((++i == argc) || 2006 (processors = strtol(argv[i], &b, 10), 2007 *b != '\0')) { 2008 ERROR("%s: -processors missing or invalid " 2009 "processor number\n", argv[0]); 2010 exit(1); 2011 } 2012 if(processors < 1) { 2013 ERROR("%s: -processors should be 1 or larger\n", 2014 argv[0]); 2015 exit(1); 2016 } 2017 } else if(strcmp(argv[i], "-data-queue") == 0 || 2018 strcmp(argv[i], "-da") == 0) { 2019 if((++i == argc) || 2020 (data_buffer_size = strtol(argv[i], &b, 2021 10), *b != '\0')) { 2022 ERROR("%s: -data-queue missing or invalid " 2023 "queue size\n", argv[0]); 2024 exit(1); 2025 } 2026 if(data_buffer_size < 1) { 2027 ERROR("%s: -data-queue should be 1 Mbyte or " 2028 "larger\n", argv[0]); 2029 exit(1); 2030 } 2031 } else if(strcmp(argv[i], "-frag-queue") == 0 || 2032 strcmp(argv[i], "-fr") == 0) { 2033 if((++i == argc) || 2034 (fragment_buffer_size = strtol(argv[i], 2035 &b, 10), *b != '\0')) { 2036 ERROR("%s: -frag-queue missing or invalid " 2037 "queue size\n", argv[0]); 2038 exit(1); 2039 } 2040 if(fragment_buffer_size < 1) { 2041 ERROR("%s: -frag-queue should be 1 Mbyte or " 2042 "larger\n", argv[0]); 2043 exit(1); 2044 } 2045 } else if(strcmp(argv[i], "-force") == 0 || 2046 strcmp(argv[i], "-f") == 0) 2047 force = TRUE; 2048 else if(strcmp(argv[i], "-stat") == 0 || 2049 strcmp(argv[i], "-s") == 0) 2050 stat_sys = TRUE; 2051 else if(strcmp(argv[i], "-lls") == 0 || 2052 strcmp(argv[i], "-ll") == 0) { 2053 lsonly = TRUE; 2054 short_ls = FALSE; 2055 } else if(strcmp(argv[i], "-linfo") == 0 || 2056 strcmp(argv[i], "-li") == 0) { 2057 info = TRUE; 2058 short_ls = FALSE; 2059 } else if(strcmp(argv[i], "-ef") == 0 || 2060 strcmp(argv[i], "-e") == 0) { 2061 if(++i == argc) { 2062 fprintf(stderr, "%s: -ef missing filename\n", 2063 argv[0]); 2064 exit(1); 2065 } 2066 path = process_extract_files(path, argv[i]); 2067 } else if(strcmp(argv[i], "-regex") == 0 || 2068 strcmp(argv[i], "-r") == 0) 2069 use_regex = TRUE; 2070 else 2071 goto options; 2072 } 2073 2074 if(lsonly || info) 2075 progress = FALSE; 2076 2077#ifdef SQUASHFS_TRACE 2078 progress = FALSE; 2079#endif 2080 2081 if(i == argc) { 2082 if(!version) { 2083options: 2084 ERROR("SYNTAX: %s [options] filesystem [directories or " 2085 "files to extract]\n", argv[0]); 2086 ERROR("\t-v[ersion]\t\tprint version, licence and " 2087 "copyright information\n"); 2088 ERROR("\t-d[est] <pathname>\tunsquash to <pathname>, " 2089 "default \"squashfs-root\"\n"); 2090 ERROR("\t-n[o-progress]\t\tdon't display the progress " 2091 "bar\n"); 2092 ERROR("\t-no[-xattrs]\t\tdon't extract xattrs in file system" 2093 NOXOPT_STR"\n"); 2094 ERROR("\t-x[attrs]\t\textract xattrs in file system" 2095 XOPT_STR "\n"); 2096 ERROR("\t-p[rocessors] <number>\tuse <number> " 2097 "processors. By default will use\n"); 2098 ERROR("\t\t\t\tnumber of processors available\n"); 2099 ERROR("\t-i[nfo]\t\t\tprint files as they are " 2100 "unsquashed\n"); 2101 ERROR("\t-li[nfo]\t\tprint files as they are " 2102 "unsquashed with file\n"); 2103 ERROR("\t\t\t\tattributes (like ls -l output)\n"); 2104 ERROR("\t-l[s]\t\t\tlist filesystem, but don't unsquash" 2105 "\n"); 2106 ERROR("\t-ll[s]\t\t\tlist filesystem with file " 2107 "attributes (like\n"); 2108 ERROR("\t\t\t\tls -l output), but don't unsquash\n"); 2109 ERROR("\t-f[orce]\t\tif file already exists then " 2110 "overwrite\n"); 2111 ERROR("\t-s[tat]\t\t\tdisplay filesystem superblock " 2112 "information\n"); 2113 ERROR("\t-e[f] <extract file>\tlist of directories or " 2114 "files to extract.\n\t\t\t\tOne per line\n"); 2115 ERROR("\t-da[ta-queue] <size>\tSet data queue to " 2116 "<size> Mbytes. Default %d\n\t\t\t\tMbytes\n", 2117 DATA_BUFFER_DEFAULT); 2118 ERROR("\t-fr[ag-queue] <size>\tSet fragment queue to " 2119 "<size> Mbytes. Default\n\t\t\t\t%d Mbytes\n", 2120 FRAGMENT_BUFFER_DEFAULT); 2121 ERROR("\t-r[egex]\t\ttreat extract names as POSIX " 2122 "regular expressions\n"); 2123 ERROR("\t\t\t\trather than use the default shell " 2124 "wildcard\n\t\t\t\texpansion (globbing)\n"); 2125 ERROR("\nDecompressors available:\n"); 2126 display_compressors("", ""); 2127 } 2128 exit(1); 2129 } 2130 2131 for(n = i + 1; n < argc; n++) 2132 path = add_path(path, argv[n], argv[n]); 2133 2134 if((fd = open(argv[i], O_RDONLY)) == -1) { 2135 ERROR("Could not open %s, because %s\n", argv[i], 2136 strerror(errno)); 2137 exit(1); 2138 } 2139 2140 if(read_super(argv[i]) == FALSE) 2141 exit(1); 2142 2143 if(stat_sys) { 2144 squashfs_stat(argv[i]); 2145 exit(0); 2146 } 2147 2148 if(!comp->supported) { 2149 ERROR("Filesystem uses %s compression, this is " 2150 "unsupported by this version\n", comp->name); 2151 ERROR("Decompressors available:\n"); 2152 display_compressors("", ""); 2153 exit(1); 2154 } 2155 2156 block_size = sBlk.s.block_size; 2157 block_log = sBlk.s.block_log; 2158 2159 fragment_buffer_size <<= 20 - block_log; 2160 data_buffer_size <<= 20 - block_log; 2161 initialise_threads(fragment_buffer_size, data_buffer_size); 2162 2163 fragment_data = malloc(block_size); 2164 if(fragment_data == NULL) 2165 EXIT_UNSQUASH("failed to allocate fragment_data\n"); 2166 2167 file_data = malloc(block_size); 2168 if(file_data == NULL) 2169 EXIT_UNSQUASH("failed to allocate file_data"); 2170 2171 data = malloc(block_size); 2172 if(data == NULL) 2173 EXIT_UNSQUASH("failed to allocate data\n"); 2174 2175 created_inode = malloc(sBlk.s.inodes * sizeof(char *)); 2176 if(created_inode == NULL) 2177 EXIT_UNSQUASH("failed to allocate created_inode\n"); 2178 2179 memset(created_inode, 0, sBlk.s.inodes * sizeof(char *)); 2180 2181 if(s_ops.read_uids_guids() == FALSE) 2182 EXIT_UNSQUASH("failed to uid/gid table\n"); 2183 2184 if(s_ops.read_fragment_table() == FALSE) 2185 EXIT_UNSQUASH("failed to read fragment table\n"); 2186 2187 uncompress_inode_table(sBlk.s.inode_table_start, 2188 sBlk.s.directory_table_start); 2189 2190 uncompress_directory_table(sBlk.s.directory_table_start, 2191 sBlk.s.fragment_table_start); 2192 2193 if(no_xattrs) 2194 sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK; 2195 2196 if(read_xattrs_from_disk(fd, &sBlk.s) == 0) 2197 EXIT_UNSQUASH("failed to read the xattr table\n"); 2198 2199 if(path) { 2200 paths = init_subdir(); 2201 paths = add_subdir(paths, path); 2202 } 2203 2204 pre_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode), 2205 SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths); 2206 2207 memset(created_inode, 0, sBlk.s.inodes * sizeof(char *)); 2208 inode_number = 1; 2209 2210 printf("%d inodes (%d blocks) to write\n\n", total_inodes, 2211 total_inodes - total_files + total_blocks); 2212 2213 if(progress) 2214 enable_progress_bar(); 2215 2216 dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode), 2217 SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths); 2218 2219 queue_put(to_writer, NULL); 2220 queue_get(from_writer); 2221 2222 if(progress) { 2223 disable_progress_bar(); 2224 progress_bar(sym_count + dev_count + fifo_count + cur_blocks, 2225 total_inodes - total_files + total_blocks, columns); 2226 } 2227 2228 if(!lsonly) { 2229 printf("\n"); 2230 printf("created %d files\n", file_count); 2231 printf("created %d directories\n", dir_count); 2232 printf("created %d symlinks\n", sym_count); 2233 printf("created %d devices\n", dev_count); 2234 printf("created %d fifos\n", fifo_count); 2235 } 2236 2237 return 0; 2238} 2239