unsquashfs.c revision b7bb000643cd21c615a0366a7365441aa9c433f2
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_bytes(long long byte, int bytes, char *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(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_bytes(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_bytes(start, 2, (char *)&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_bytes(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_bytes(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_bytes(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_bytes(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(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, &times) == -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(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_bytes(SQUASHFS_START, sizeof(squashfs_super_block),
1439		(char *) &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_bytes(SQUASHFS_START, sizeof(squashfs_super_block_3),
1472		(char *) &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_bytes(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			&timespec);
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 = floor(log10(max)) + 1;
1875	int used = max_digits * 2 + 11;
1876	int hashes = (current * (columns - used)) / max;
1877	int spaces = columns - used - hashes;
1878	static int tty = -1;
1879
1880	if((current > max) || (columns - used < 0))
1881		return;
1882
1883	if(tty == -1)
1884		tty = isatty(STDOUT_FILENO);
1885	if(!tty) {
1886		static long long previous = -1;
1887
1888		/* Updating much more frequently than this results in huge
1889		 * log files. */
1890		if((current % 100) != 0 && current != max)
1891			return;
1892		/* Don't update just to rotate the spinner. */
1893		if(current == previous)
1894			return;
1895		previous = current;
1896	}
1897
1898	printf("\r[");
1899
1900	while (hashes --)
1901		putchar('=');
1902
1903	putchar(rotate_list[rotate]);
1904
1905	while(spaces --)
1906		putchar(' ');
1907
1908	printf("] %*lld/%*lld", max_digits, current, max_digits, max);
1909	printf(" %3lld%%", current * 100 / max);
1910	fflush(stdout);
1911}
1912
1913
1914#define VERSION() \
1915	printf("unsquashfs version 4.1-CVS (2010/03/06)\n");\
1916	printf("copyright (C) 2009 Phillip Lougher <phillip@lougher.demon.co.uk>"\
1917		"\n\n");\
1918    	printf("This program is free software; you can redistribute it and/or\n");\
1919	printf("modify it under the terms of the GNU General Public License\n");\
1920	printf("as published by the Free Software Foundation; either version 2,"\
1921		"\n");\
1922	printf("or (at your option) any later version.\n\n");\
1923	printf("This program is distributed in the hope that it will be useful,"\
1924		"\n");\
1925	printf("but WITHOUT ANY WARRANTY; without even the implied warranty of"\
1926		"\n");\
1927	printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the"\
1928		"\n");\
1929	printf("GNU General Public License for more details.\n");
1930int main(int argc, char *argv[])
1931{
1932	char *dest = "squashfs-root";
1933	int i, stat_sys = FALSE, version = FALSE;
1934	int n;
1935	struct pathnames *paths = NULL;
1936	struct pathname *path = NULL;
1937	int fragment_buffer_size = FRAGMENT_BUFFER_DEFAULT;
1938	int data_buffer_size = DATA_BUFFER_DEFAULT;
1939	char *b;
1940
1941	pthread_mutex_init(&screen_mutex, NULL);
1942	root_process = geteuid() == 0;
1943	if(root_process)
1944		umask(0);
1945
1946	for(i = 1; i < argc; i++) {
1947		if(*argv[i] != '-')
1948			break;
1949		if(strcmp(argv[i], "-version") == 0 ||
1950				strcmp(argv[i], "-v") == 0) {
1951			VERSION();
1952			version = TRUE;
1953		} else if(strcmp(argv[i], "-info") == 0 ||
1954				strcmp(argv[i], "-i") == 0)
1955			info = TRUE;
1956		else if(strcmp(argv[i], "-ls") == 0 ||
1957				strcmp(argv[i], "-l") == 0)
1958			lsonly = TRUE;
1959		else if(strcmp(argv[i], "-no-progress") == 0 ||
1960				strcmp(argv[i], "-n") == 0)
1961			progress = FALSE;
1962		else if(strcmp(argv[i], "-dest") == 0 ||
1963				strcmp(argv[i], "-d") == 0) {
1964			if(++i == argc) {
1965				fprintf(stderr, "%s: -dest missing filename\n",
1966					argv[0]);
1967				exit(1);
1968			}
1969			dest = argv[i];
1970		} else if(strcmp(argv[i], "-processors") == 0 ||
1971				strcmp(argv[i], "-p") == 0) {
1972			if((++i == argc) ||
1973					(processors = strtol(argv[i], &b, 10),
1974					*b != '\0')) {
1975				ERROR("%s: -processors missing or invalid "
1976					"processor number\n", argv[0]);
1977				exit(1);
1978			}
1979			if(processors < 1) {
1980				ERROR("%s: -processors should be 1 or larger\n",
1981					argv[0]);
1982				exit(1);
1983			}
1984		} else if(strcmp(argv[i], "-data-queue") == 0 ||
1985					 strcmp(argv[i], "-da") == 0) {
1986			if((++i == argc) ||
1987					(data_buffer_size = strtol(argv[i], &b,
1988					 10), *b != '\0')) {
1989				ERROR("%s: -data-queue missing or invalid "
1990					"queue size\n", argv[0]);
1991				exit(1);
1992			}
1993			if(data_buffer_size < 1) {
1994				ERROR("%s: -data-queue should be 1 Mbyte or "
1995					"larger\n", argv[0]);
1996				exit(1);
1997			}
1998		} else if(strcmp(argv[i], "-frag-queue") == 0 ||
1999					strcmp(argv[i], "-fr") == 0) {
2000			if((++i == argc) ||
2001					(fragment_buffer_size = strtol(argv[i],
2002					 &b, 10), *b != '\0')) {
2003				ERROR("%s: -frag-queue missing or invalid "
2004					"queue size\n", argv[0]);
2005				exit(1);
2006			}
2007			if(fragment_buffer_size < 1) {
2008				ERROR("%s: -frag-queue should be 1 Mbyte or "
2009					"larger\n", argv[0]);
2010				exit(1);
2011			}
2012		} else if(strcmp(argv[i], "-force") == 0 ||
2013				strcmp(argv[i], "-f") == 0)
2014			force = TRUE;
2015		else if(strcmp(argv[i], "-stat") == 0 ||
2016				strcmp(argv[i], "-s") == 0)
2017			stat_sys = TRUE;
2018		else if(strcmp(argv[i], "-lls") == 0 ||
2019				strcmp(argv[i], "-ll") == 0) {
2020			lsonly = TRUE;
2021			short_ls = FALSE;
2022		} else if(strcmp(argv[i], "-linfo") == 0 ||
2023				strcmp(argv[i], "-li") == 0) {
2024			info = TRUE;
2025			short_ls = FALSE;
2026		} else if(strcmp(argv[i], "-ef") == 0 ||
2027				strcmp(argv[i], "-e") == 0) {
2028			if(++i == argc) {
2029				fprintf(stderr, "%s: -ef missing filename\n",
2030					argv[0]);
2031				exit(1);
2032			}
2033			path = process_extract_files(path, argv[i]);
2034		} else if(strcmp(argv[i], "-regex") == 0 ||
2035				strcmp(argv[i], "-r") == 0)
2036			use_regex = TRUE;
2037		else
2038			goto options;
2039	}
2040
2041	if(lsonly || info)
2042		progress = FALSE;
2043
2044#ifdef SQUASHFS_TRACE
2045	progress = FALSE;
2046#endif
2047
2048	if(i == argc) {
2049		if(!version) {
2050options:
2051			ERROR("SYNTAX: %s [options] filesystem [directories or "
2052				"files to extract]\n", argv[0]);
2053			ERROR("\t-v[ersion]\t\tprint version, licence and "
2054				"copyright information\n");
2055			ERROR("\t-d[est] <pathname>\tunsquash to <pathname>, "
2056				"default \"squashfs-root\"\n");
2057			ERROR("\t-n[o-progress]\t\tdon't display the progress "
2058				"bar\n");
2059			ERROR("\t-p[rocessors] <number>\tuse <number> "
2060				"processors.  By default will use\n");
2061			ERROR("\t\t\t\tnumber of processors available\n");
2062			ERROR("\t-i[nfo]\t\t\tprint files as they are "
2063				"unsquashed\n");
2064			ERROR("\t-li[nfo]\t\tprint files as they are "
2065				"unsquashed with file\n");
2066			ERROR("\t\t\t\tattributes (like ls -l output)\n");
2067			ERROR("\t-l[s]\t\t\tlist filesystem, but don't unsquash"
2068				"\n");
2069			ERROR("\t-ll[s]\t\t\tlist filesystem with file "
2070				"attributes (like\n");
2071			ERROR("\t\t\t\tls -l output), but don't unsquash\n");
2072			ERROR("\t-f[orce]\t\tif file already exists then "
2073				"overwrite\n");
2074			ERROR("\t-s[tat]\t\t\tdisplay filesystem superblock "
2075				"information\n");
2076			ERROR("\t-e[f] <extract file>\tlist of directories or "
2077				"files to extract.\n\t\t\t\tOne per line\n");
2078			ERROR("\t-da[ta-queue] <size>\tSet data queue to "
2079				"<size> Mbytes.  Default %d\n\t\t\t\tMbytes\n",
2080				DATA_BUFFER_DEFAULT);
2081			ERROR("\t-fr[ag-queue] <size>\tSet fagment queue to "
2082				"<size> Mbytes.  Default %d\n\t\t\t\t Mbytes\n",
2083				FRAGMENT_BUFFER_DEFAULT);
2084			ERROR("\t-r[egex]\t\ttreat extract names as POSIX "
2085				"regular expressions\n");
2086			ERROR("\t\t\t\trather than use the default shell "
2087				"wildcard\n\t\t\t\texpansion (globbing)\n");
2088			ERROR("\nDecompressors available:\n");
2089			display_compressors("", "");
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