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