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, &times) == -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			&timespec);
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