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