1/*
2 * Unsquash a squashfs filesystem.  This is a highly compressed read only filesystem.
3 *
4 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
5 *  Phillip Lougher <phillip@lougher.org.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 * unsquash.c
22 */
23
24#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
25
26#define TRUE 1
27#define FALSE 0
28#include <stdio.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <errno.h>
33#include <string.h>
34#include <zlib.h>
35#include <sys/mman.h>
36#include <utime.h>
37
38#ifndef linux
39#define __BYTE_ORDER BYTE_ORDER
40#define __BIG_ENDIAN BIG_ENDIAN
41#define __LITTLE_ENDIAN LITTLE_ENDIAN
42#else
43#include <endian.h>
44#endif
45
46#include <linux/squashfs_fs.h>
47#include "read_fs.h"
48#include "global.h"
49#include "sqlzma.h"
50#include "LzmaDec.h"
51
52#include <stdlib.h>
53
54#ifdef SQUASHFS_TRACE
55#define TRACE(s, args...)		do { \
56						printf("mksquashfs: "s, ## args); \
57					} while(0)
58#else
59#define TRACE(s, args...)
60#endif
61
62#define ERROR(s, args...)		do { \
63						fprintf(stderr, s, ## args); \
64					} while(0)
65
66#define EXIT_UNSQUASH(s, args...)	do { \
67						fprintf(stderr, "FATAL ERROR aborting: "s, ## args); \
68					} while(0)
69
70struct hash_table_entry {
71	int	start;
72	int	bytes;
73	struct hash_table_entry *next;
74};
75
76typedef struct squashfs_operations {
77	struct dir	*(*squashfs_opendir)(unsigned int block_start, unsigned int offset);
78	char 		*(*read_fragment)(unsigned int fragment);
79	void		(*read_fragment_table)();
80	int		(*create_inode)(char *pathname, unsigned int start_block, unsigned int offset);
81} squashfs_operations;
82
83squashfs_super_block sBlk;
84squashfs_operations s_ops;
85
86int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0,
87	dev_count = 0, fifo_count = 0;
88char *inode_table = NULL, *directory_table = NULL;
89struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536];
90int fd;
91squashfs_fragment_entry *fragment_table;
92squashfs_fragment_entry_2 *fragment_table_2;
93unsigned int *uid_table, *guid_table;
94unsigned int cached_frag = SQUASHFS_INVALID_FRAG;
95char *fragment_data;
96char *file_data;
97char *data;
98unsigned int block_size;
99int lsonly = FALSE, info = FALSE, force = FALSE;
100char **created_inode;
101int root_process;
102
103#define CALCULATE_HASH(start)	(start & 0xffff)
104
105int add_entry(struct hash_table_entry *hash_table[], int start, int bytes)
106{
107	int hash = CALCULATE_HASH(start);
108	struct hash_table_entry *hash_table_entry;
109
110	if((hash_table_entry = malloc(sizeof(struct hash_table_entry))) == NULL) {
111		ERROR("add_hash: out of memory in malloc\n");
112		return FALSE;
113	}
114
115	hash_table_entry->start = start;
116	hash_table_entry->bytes = bytes;
117	hash_table_entry->next = hash_table[hash];
118	hash_table[hash] = hash_table_entry;
119
120	return TRUE;
121}
122
123
124int lookup_entry(struct hash_table_entry *hash_table[], int start)
125{
126	int hash = CALCULATE_HASH(start);
127	struct hash_table_entry *hash_table_entry;
128
129	for(hash_table_entry = hash_table[hash]; hash_table_entry;
130				hash_table_entry = hash_table_entry->next)
131		if(hash_table_entry->start == start)
132			return hash_table_entry->bytes;
133
134	return -1;
135}
136
137
138int read_bytes(long long byte, int bytes, char *buff)
139{
140	off_t off = byte;
141
142	TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, bytes);
143
144	if(lseek(fd, off, SEEK_SET) == -1) {
145		ERROR("Lseek failed because %s\b", strerror(errno));
146		return FALSE;
147	}
148
149	if(read(fd, buff, bytes) == -1) {
150		ERROR("Read on destination failed because %s\n", strerror(errno));
151		return FALSE;
152	}
153
154	return TRUE;
155}
156
157
158int read_block(long long start, long long *next, char *block)
159{
160	unsigned short c_byte;
161	int offset = 2;
162
163	if(swap) {
164		if(read_bytes(start, 2, block) == FALSE)
165			goto failed;
166		((unsigned char *) &c_byte)[1] = block[0];
167		((unsigned char *) &c_byte)[0] = block[1];
168	} else
169		if(read_bytes(start, 2, (char *)&c_byte) == FALSE)
170			goto failed;
171
172	TRACE("read_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? "compressed" : "uncompressed");
173
174	if(SQUASHFS_CHECK_DATA(sBlk.flags))
175		offset = 3;
176	if(SQUASHFS_COMPRESSED(c_byte)) {
177		char buffer[SQUASHFS_METADATA_SIZE];
178		int res;
179		unsigned long bytes = SQUASHFS_METADATA_SIZE;
180
181		c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
182		if(read_bytes(start + offset, c_byte, buffer) == FALSE)
183			goto failed;
184		if (!is_lzma(buffer[0])) {
185			if((res = uncompress((unsigned char *) block, &bytes,
186			(const unsigned char *) buffer, c_byte)) != Z_OK) {
187				if(res == Z_MEM_ERROR)
188					ERROR("zlib::uncompress failed, not enough memory\n");
189				else if(res == Z_BUF_ERROR)
190					ERROR("zlib::uncompress failed, not enough room in output buffer\n");
191				else
192					ERROR("zlib::uncompress failed, unknown error %d\n", res);
193				goto failed;
194			}
195		}
196		else {
197			if((res = LzmaUncompress(block, &bytes, buffer, c_byte)) != SZ_OK)
198				ERROR("LzmaUncompress: error (%d)\n", res);
199		}
200
201		if(next)
202			*next = start + offset + c_byte;
203		return bytes;
204	} else {
205		c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
206		if(read_bytes(start + offset, c_byte, block) == FALSE)
207			goto failed;
208		if(next)
209			*next = start + offset + c_byte;
210		return c_byte;
211	}
212
213failed:
214	return FALSE;
215}
216
217
218int read_data_block(long long start, unsigned int size, char *block)
219{
220	int res;
221	unsigned long bytes = block_size;
222	int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
223
224	TRACE("read_data_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte), SQUASHFS_COMPRESSED_BLOCK(c_byte) ? "compressed" : "uncompressed");
225
226	if(SQUASHFS_COMPRESSED_BLOCK(size)) {
227		if(read_bytes(start, c_byte, data) == FALSE)
228			return 0;
229
230		if (!is_lzma(data[0])) {
231			if((res = uncompress((unsigned char *) block, &bytes,
232			(const unsigned char *) data, c_byte)) != Z_OK) {
233				if(res == Z_MEM_ERROR)
234					ERROR("zlib::uncompress failed, not enough memory\n");
235				else if(res == Z_BUF_ERROR)
236					ERROR("zlib::uncompress failed, not enough room in output buffer\n");
237				else
238					ERROR("zlib::uncompress failed, unknown error %d\n", res);
239				return 0;
240			}
241		}
242		/* LZMA */
243		else {
244			if((res = LzmaUncompress(block, &bytes, data, c_byte)) != SZ_OK)
245				ERROR("LzmaUncompress: error (%d)\n", res);
246		}
247
248		return bytes;
249	} else {
250		if(read_bytes(start, c_byte, block) == FALSE)
251			return 0;
252
253		return c_byte;
254	}
255}
256
257
258void uncompress_inode_table(long long start, long long end)
259{
260	int size = 0, bytes = 0, res;
261
262	while(start < end) {
263		if((size - bytes < SQUASHFS_METADATA_SIZE) &&
264				((inode_table = realloc(inode_table, size +=
265				SQUASHFS_METADATA_SIZE)) == NULL))
266			EXIT_UNSQUASH("uncompress_inode_table: out of memory in realloc\n");
267		TRACE("uncompress_inode_table: reading block 0x%llx\n", start);
268		add_entry(inode_table_hash, start, bytes);
269		if((res = read_block(start, &start, inode_table + bytes)) == 0) {
270			free(inode_table);
271			EXIT_UNSQUASH("uncompress_inode_table: failed to read block\n");
272		}
273		bytes += res;
274	}
275}
276
277
278int set_attributes(char *pathname, unsigned int mode, unsigned int uid,
279unsigned int guid, unsigned int mtime, unsigned int set_mode)
280{
281	struct utimbuf times = { (time_t) mtime, (time_t) mtime };
282
283	if(utime(pathname, &times) == -1) {
284		ERROR("set_attributes: failed to set time on %s, because %s\n", pathname, strerror(errno));
285		return FALSE;
286	}
287
288	if(root_process) {
289		uid_t uid_value = (uid_t) uid_table[uid];
290		uid_t guid_value = guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[guid];
291
292		if(chown(pathname, uid_value, guid_value) == -1) {
293			ERROR("set_attributes: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno));
294			return FALSE;
295		}
296	} else
297		mode &= ~07000;
298
299	if((set_mode || (mode & 07000)) && chmod(pathname, (mode_t) mode) == -1) {
300		ERROR("set_attributes: failed to change mode %s, because %s\n", pathname, strerror(errno));
301		return FALSE;
302	}
303
304	return TRUE;
305}
306
307
308void read_uids_guids()
309{
310	if((uid_table = malloc((sBlk.no_uids + sBlk.no_guids) * sizeof(unsigned int))) == NULL)
311		EXIT_UNSQUASH("read_uids_guids: failed to allocate uid/gid table\n");
312
313	guid_table = uid_table + sBlk.no_uids;
314
315	if(swap) {
316		unsigned int suid_table[sBlk.no_uids + sBlk.no_guids];
317
318		if(read_bytes(sBlk.uid_start, (sBlk.no_uids + sBlk.no_guids)
319				* sizeof(unsigned int), (char *) suid_table) ==
320				FALSE)
321			EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n");
322		SQUASHFS_SWAP_INTS(uid_table, suid_table, sBlk.no_uids + sBlk.no_guids);
323	} else
324		if(read_bytes(sBlk.uid_start, (sBlk.no_uids + sBlk.no_guids)
325				* sizeof(unsigned int), (char *) uid_table) ==
326				FALSE)
327			EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n");
328}
329
330
331void read_fragment_table()
332{
333	int i, indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.fragments);
334	squashfs_fragment_index fragment_table_index[indexes];
335
336	TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%llx\n", sBlk.fragments, indexes, sBlk.fragment_table_start);
337
338	if(sBlk.fragments == 0)
339		return;
340
341	if((fragment_table = (squashfs_fragment_entry *)
342			malloc(sBlk.fragments *
343			sizeof(squashfs_fragment_entry))) == NULL)
344		EXIT_UNSQUASH("read_fragment_table: failed to allocate fragment table\n");
345
346	if(swap) {
347		squashfs_fragment_index sfragment_table_index[indexes];
348
349		read_bytes(sBlk.fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk.fragments), (char *) sfragment_table_index);
350		SQUASHFS_SWAP_FRAGMENT_INDEXES(fragment_table_index, sfragment_table_index, indexes);
351	} else
352		read_bytes(sBlk.fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk.fragments), (char *) fragment_table_index);
353
354	for(i = 0; i < indexes; i++) {
355		int length = read_block(fragment_table_index[i], NULL,
356		((char *) fragment_table) + (i * SQUASHFS_METADATA_SIZE));
357		TRACE("Read fragment table block %d, from 0x%llx, length %d\n", i, fragment_table_index[i], length);
358	}
359
360	if(swap) {
361		squashfs_fragment_entry sfragment;
362		for(i = 0; i < sBlk.fragments; i++) {
363			SQUASHFS_SWAP_FRAGMENT_ENTRY((&sfragment), (&fragment_table[i]));
364			memcpy((char *) &fragment_table[i], (char *) &sfragment, sizeof(squashfs_fragment_entry));
365		}
366	}
367}
368
369
370void read_fragment_table_2()
371{
372	int i, indexes = SQUASHFS_FRAGMENT_INDEXES_2(sBlk.fragments);
373	unsigned int fragment_table_index[indexes];
374
375	TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%llx\n", sBlk.fragments, indexes, sBlk.fragment_table_start);
376
377	if(sBlk.fragments == 0)
378		return;
379
380	if((fragment_table_2 = (squashfs_fragment_entry_2 *)
381			malloc(sBlk.fragments *
382			sizeof(squashfs_fragment_entry))) == NULL)
383		EXIT_UNSQUASH("read_fragment_table: failed to allocate fragment table\n");
384
385	if(swap) {
386		 unsigned int sfragment_table_index[indexes];
387
388		read_bytes(sBlk.fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sBlk.fragments), (char *) sfragment_table_index);
389		SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index, sfragment_table_index, indexes);
390	} else
391		read_bytes(sBlk.fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sBlk.fragments), (char *) fragment_table_index);
392
393	for(i = 0; i < indexes; i++) {
394		int length = read_block(fragment_table_index[i], NULL,
395		((char *) fragment_table_2) + (i * SQUASHFS_METADATA_SIZE));
396		TRACE("Read fragment table block %d, from 0x%llx, length %d\n", i, fragment_table_index[i], length);
397	}
398
399	if(swap) {
400		squashfs_fragment_entry_2 sfragment;
401		for(i = 0; i < sBlk.fragments; i++) {
402			SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment), (&fragment_table_2[i]));
403			memcpy((char *) &fragment_table_2[i], (char *) &sfragment, sizeof(squashfs_fragment_entry_2));
404		}
405	}
406}
407
408
409char *read_fragment(unsigned int fragment)
410{
411	TRACE("read_fragment: reading fragment %d\n", fragment);
412
413	if(cached_frag == SQUASHFS_INVALID_FRAG || fragment != cached_frag) {
414		squashfs_fragment_entry *fragment_entry = &fragment_table[fragment];
415		if(read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data) == 0) {
416			ERROR("read_fragment: failed to read fragment %d\n", fragment);
417			cached_frag = SQUASHFS_INVALID_FRAG;
418			return NULL;
419		}
420		cached_frag = fragment;
421	}
422
423	return fragment_data;
424}
425
426
427char *read_fragment_2(unsigned int fragment)
428{
429	TRACE("read_fragment: reading fragment %d\n", fragment);
430
431	if(cached_frag == SQUASHFS_INVALID_FRAG || fragment != cached_frag) {
432		squashfs_fragment_entry_2 *fragment_entry = &fragment_table_2[fragment];
433		if(read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data) == 0) {
434			ERROR("read_fragment: failed to read fragment %d\n", fragment);
435			cached_frag = SQUASHFS_INVALID_FRAG;
436			return NULL;
437		}
438		cached_frag = fragment;
439	}
440
441	return fragment_data;
442}
443
444
445int write_file(char *pathname, unsigned int fragment, unsigned int frag_bytes,
446unsigned int offset, unsigned int blocks, long long start, char *block_ptr,
447unsigned int mode)
448{
449	unsigned int file_fd, bytes, i;
450	unsigned int *block_list;
451
452	TRACE("write_file: regular file, blocks %d\n", blocks);
453
454	if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) {
455		ERROR("write_file: unable to malloc block list\n");
456		return FALSE;
457	}
458
459	if(swap) {
460		unsigned int sblock_list[blocks];
461		memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int));
462		SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks);
463	} else
464		memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
465
466	if((file_fd = open(pathname, O_CREAT | O_WRONLY | (force ? O_TRUNC : 0), (mode_t) mode & 0777)) == -1) {
467		ERROR("write_file: failed to create file %s, because %s\n", pathname,
468			strerror(errno));
469		free(block_list);
470		return FALSE;
471	}
472
473	for(i = 0; i < blocks; i++) {
474		if((bytes = read_data_block(start, block_list[i], file_data)) == 0) {
475			ERROR("write_file: failed to read data block 0x%llx\n", start);
476			goto failure;
477		}
478
479		if(write(file_fd, file_data, bytes) < bytes) {
480			ERROR("write_file: failed to write data block 0x%llx\n", start);
481			goto failure;
482		}
483
484		start += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
485	}
486
487	if(frag_bytes != 0) {
488		char *fragment_data = s_ops.read_fragment(fragment);
489
490		if(fragment_data == NULL)
491			goto failure;
492
493		if(write(file_fd, fragment_data + offset, frag_bytes) < frag_bytes) {
494			ERROR("write_file: failed to write fragment %d\n", fragment);
495			goto failure;
496		}
497	}
498
499	close(file_fd);
500	return TRUE;
501
502failure:
503	close(file_fd);
504	free(block_list);
505	return FALSE;
506}
507
508
509int create_inode(char *pathname, unsigned int start_block, unsigned int offset)
510{
511	long long start = sBlk.inode_table_start + start_block;
512	squashfs_inode_header header;
513	char *block_ptr;
514	int bytes = lookup_entry(inode_table_hash, start), file_fd;
515
516	TRACE("create_inode: pathname %s, start 0x%llx, offset %d\n", pathname, start, offset);
517
518	if(bytes == -1) {
519		ERROR("create_inode: inode block 0x%llx out of range!\n", start);
520		return FALSE;
521	}
522	block_ptr = inode_table + bytes + offset;
523
524	if(swap) {
525		squashfs_base_inode_header sinode;
526		memcpy(&sinode, block_ptr, sizeof(header.base));
527		SQUASHFS_SWAP_BASE_INODE_HEADER(&header.base, &sinode, sizeof(squashfs_base_inode_header));
528	} else
529		memcpy(&header.base, block_ptr, sizeof(header.base));
530
531	if(created_inode[header.base.inode_number - 1]) {
532		TRACE("create_inode: hard link\n");
533		if(force)
534			unlink(pathname);
535
536		if(link(created_inode[header.base.inode_number - 1], pathname) == -1) {
537			ERROR("create_inode: failed to create hardlink, because %s\n", strerror(errno));
538			return FALSE;
539		}
540
541		return TRUE;
542	}
543
544	switch(header.base.inode_type) {
545		case SQUASHFS_FILE_TYPE: {
546			unsigned int frag_bytes;
547			unsigned int blocks;
548			unsigned int offset;
549			long long start;
550			squashfs_reg_inode_header *inode = &header.reg;
551
552			if(swap) {
553				squashfs_reg_inode_header sinode;
554				memcpy(&sinode, block_ptr, sizeof(sinode));
555				SQUASHFS_SWAP_REG_INODE_HEADER(inode, &sinode);
556			} else
557				memcpy(inode, block_ptr, sizeof(*inode));
558
559			frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ?
560				0 : inode->file_size % sBlk.block_size;
561			offset = inode->offset;
562			blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
563				(inode->file_size + sBlk.block_size - 1) >>
564				sBlk.block_log : inode->file_size >>
565				sBlk.block_log;
566			start = inode->start_block;
567
568			TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
569
570			if(write_file(pathname, inode->fragment, frag_bytes,
571					offset, blocks, start, block_ptr +
572					sizeof(*inode), inode->mode)) {
573				set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, force);
574				file_count ++;
575			}
576			break;
577		}
578		case SQUASHFS_LREG_TYPE: {
579			unsigned int frag_bytes;
580			unsigned int blocks;
581			unsigned int offset;
582			long long start;
583			squashfs_lreg_inode_header *inode = &header.lreg;
584
585			if(swap) {
586				squashfs_lreg_inode_header sinode;
587				memcpy(&sinode, block_ptr, sizeof(sinode));
588				SQUASHFS_SWAP_LREG_INODE_HEADER(inode, &sinode);
589			} else
590				memcpy(inode, block_ptr, sizeof(*inode));
591
592			frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ?
593				0 : inode->file_size % sBlk.block_size;
594			offset = inode->offset;
595			blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
596				(inode->file_size + sBlk.block_size - 1) >>
597				sBlk.block_log : inode->file_size >>
598				sBlk.block_log;
599			start = inode->start_block;
600
601			TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
602
603			if(write_file(pathname, inode->fragment, frag_bytes,
604					offset, blocks, start, block_ptr +
605					sizeof(*inode), inode->mode)) {
606				set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, force);
607				file_count ++;
608			}
609			break;
610		}
611		case SQUASHFS_SYMLINK_TYPE: {
612			squashfs_symlink_inode_header *inodep = &header.symlink;
613			char name[65536];
614
615			if(swap) {
616				squashfs_symlink_inode_header sinodep;
617				memcpy(&sinodep, block_ptr, sizeof(sinodep));
618				SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, &sinodep);
619			} else
620				memcpy(inodep, block_ptr, sizeof(*inodep));
621
622			TRACE("create_inode: symlink, symlink_size %d\n", inodep->symlink_size);
623
624			strncpy(name, block_ptr + sizeof(squashfs_symlink_inode_header), inodep->symlink_size);
625			name[inodep->symlink_size] = '\0';
626
627			if(force)
628				unlink(pathname);
629
630			if(symlink(name, pathname) == -1) {
631				ERROR("create_inode: failed to create symlink %s, because %s\n", pathname,
632					strerror(errno));
633				break;
634			}
635
636			if(root_process) {
637				uid_t uid_value = (uid_t) uid_table[inodep->uid];
638				uid_t guid_value = inodep->guid ==
639					SQUASHFS_GUIDS ? uid_value : (uid_t)
640					guid_table[inodep->guid];
641
642				if(lchown(pathname, uid_value, guid_value) == -1)
643					ERROR("create_inode: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno));
644			}
645
646			sym_count ++;
647			break;
648		}
649 		case SQUASHFS_BLKDEV_TYPE:
650	 	case SQUASHFS_CHRDEV_TYPE: {
651			squashfs_dev_inode_header *inodep = &header.dev;
652
653			if(swap) {
654				squashfs_dev_inode_header sinodep;
655				memcpy(&sinodep, block_ptr, sizeof(sinodep));
656				SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, &sinodep);
657			} else
658				memcpy(inodep, block_ptr, sizeof(*inodep));
659
660			TRACE("create_inode: dev, rdev 0x%x\n", inodep->rdev);
661
662			if(root_process) {
663				if(force)
664					unlink(pathname);
665
666				if(mknod(pathname, inodep->inode_type ==
667					SQUASHFS_CHRDEV_TYPE ?  S_IFCHR :
668					S_IFBLK, makedev((inodep->rdev >> 8)
669					& 0xff, inodep->rdev & 0xff)) == -1) {
670					ERROR("create_inode: failed to create %s device %s, because %s\n",
671						inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
672						pathname, strerror(errno));
673					break;
674				}
675				set_attributes(pathname, inodep->mode, inodep->uid, inodep->guid, inodep->mtime, TRUE);
676				dev_count ++;
677			} else
678				ERROR("create_inode: could not create %s device %s, because you're not superuser! %s\n",
679					inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
680					pathname, strerror(errno));
681			break;
682			}
683		case SQUASHFS_FIFO_TYPE:
684			TRACE("create_inode: fifo\n");
685
686			if(force)
687				unlink(pathname);
688
689			if(mknod(pathname, S_IFIFO, 0) == -1) {
690				ERROR("create_inode: failed to create fifo %s, because %s\n",
691					pathname, strerror(errno));
692				break;
693			}
694			set_attributes(pathname, header.base.mode, header.base.uid, header.base.guid,
695				header.base.mtime, TRUE);
696			fifo_count ++;
697			break;
698		case SQUASHFS_SOCKET_TYPE:
699			TRACE("create_inode: socket\n");
700			ERROR("create_inode: socket %s ignored\n", pathname);
701			break;
702		default:
703			ERROR("Unknown inode type %d in create_inode_table!\n", header.base.inode_type);
704			return FALSE;
705	}
706
707	created_inode[header.base.inode_number - 1] = strdup(pathname);
708
709	return TRUE;
710}
711
712
713int create_inode_2(char *pathname, unsigned int start_block, unsigned int offset)
714{
715	long long start = sBlk.inode_table_start + start_block;
716	squashfs_inode_header_2 header;
717	char *block_ptr;
718	int bytes = lookup_entry(inode_table_hash, start), file_fd;
719
720	TRACE("create_inode: pathname %s, start 0x%llx, offset %d\n", pathname, start, offset);
721
722	if(bytes == -1) {
723		ERROR("create_inode: inode block 0x%llx out of range!\n", start);
724		return FALSE;
725	}
726	block_ptr = inode_table + bytes + offset;
727
728	if(swap) {
729		squashfs_base_inode_header_2 sinode;
730		memcpy(&sinode, block_ptr, sizeof(header.base));
731		SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header.base, &sinode, sizeof(squashfs_base_inode_header_2));
732	} else
733		memcpy(&header.base, block_ptr, sizeof(header.base));
734
735	switch(header.base.inode_type) {
736		case SQUASHFS_FILE_TYPE: {
737			unsigned int frag_bytes;
738			unsigned int blocks;
739			unsigned int offset;
740			long long start;
741			squashfs_reg_inode_header_2 *inode = &header.reg;
742
743			if(swap) {
744				squashfs_reg_inode_header_2 sinode;
745				memcpy(&sinode, block_ptr, sizeof(sinode));
746				SQUASHFS_SWAP_REG_INODE_HEADER_2(inode, &sinode);
747			} else
748				memcpy(inode, block_ptr, sizeof(*inode));
749
750			frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ?
751				0 : inode->file_size % sBlk.block_size;
752			offset = inode->offset;
753			blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
754				(inode->file_size + sBlk.block_size - 1) >>
755				sBlk.block_log : inode->file_size >>
756				sBlk.block_log;
757			start = inode->start_block;
758
759			TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
760
761			if(write_file(pathname, inode->fragment, frag_bytes,
762					offset, blocks, start, block_ptr +
763					sizeof(*inode), inode->mode)) {
764				set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, force);
765				file_count ++;
766			}
767			break;
768		}
769		case SQUASHFS_SYMLINK_TYPE: {
770			squashfs_symlink_inode_header_2 *inodep = &header.symlink;
771			char name[65536];
772
773			if(swap) {
774				squashfs_symlink_inode_header_2 sinodep;
775				memcpy(&sinodep, block_ptr, sizeof(sinodep));
776				SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, &sinodep);
777			} else
778				memcpy(inodep, block_ptr, sizeof(*inodep));
779
780			TRACE("create_inode: symlink, symlink_size %d\n", inodep->symlink_size);
781
782			strncpy(name, block_ptr + sizeof(squashfs_symlink_inode_header_2), inodep->symlink_size);
783			name[inodep->symlink_size] = '\0';
784
785			if(force)
786				unlink(pathname);
787
788			if(symlink(name, pathname) == -1) {
789				ERROR("create_inode: failed to create symlink %s, because %s\n", pathname,
790					strerror(errno));
791				break;
792			}
793
794			if(root_process) {
795				uid_t uid_value = (uid_t) uid_table[inodep->uid];
796				uid_t guid_value = inodep->guid ==
797					SQUASHFS_GUIDS ? uid_value : (uid_t)
798					guid_table[inodep->guid];
799
800				if(lchown(pathname, uid_value, guid_value) == -1)
801					ERROR("create_inode: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno));
802			}
803
804			sym_count ++;
805			break;
806		}
807 		case SQUASHFS_BLKDEV_TYPE:
808	 	case SQUASHFS_CHRDEV_TYPE: {
809			squashfs_dev_inode_header_2 *inodep = &header.dev;
810
811			if(swap) {
812				squashfs_dev_inode_header_2 sinodep;
813				memcpy(&sinodep, block_ptr, sizeof(sinodep));
814				SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, &sinodep);
815			} else
816				memcpy(inodep, block_ptr, sizeof(*inodep));
817
818			TRACE("create_inode: dev, rdev 0x%x\n", inodep->rdev);
819
820			if(root_process) {
821				if(force)
822					unlink(pathname);
823
824				if(mknod(pathname, inodep->inode_type ==
825					SQUASHFS_CHRDEV_TYPE ?  S_IFCHR :
826					S_IFBLK, makedev((inodep->rdev >> 8)
827					& 0xff, inodep->rdev & 0xff)) == -1) {
828					ERROR("create_inode: failed to create %s device %s, because %s\n",
829						inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
830						pathname, strerror(errno));
831					break;
832				}
833				set_attributes(pathname, inodep->mode, inodep->uid, inodep->guid, sBlk.mkfs_time, TRUE);
834				dev_count ++;
835			} else
836				ERROR("create_inode: could not create %s device %s, because you're not superuser! %s\n",
837					inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
838					pathname, strerror(errno));
839			break;
840			}
841		case SQUASHFS_FIFO_TYPE:
842			TRACE("create_inode: fifo\n");
843
844			if(force)
845				unlink(pathname);
846
847			if(mknod(pathname, S_IFIFO, 0) == -1) {
848				ERROR("create_inode: failed to create fifo %s, because %s\n",
849					pathname, strerror(errno));
850				break;
851			}
852			set_attributes(pathname, header.base.mode, header.base.uid, header.base.guid,
853				sBlk.mkfs_time, TRUE);
854			fifo_count ++;
855			break;
856		case SQUASHFS_SOCKET_TYPE:
857			TRACE("create_inode: socket\n");
858			ERROR("create_inode: socket %s ignored\n", pathname);
859			break;
860		default:
861			ERROR("Unknown inode type %d in create_inode_table!\n", header.base.inode_type);
862			return FALSE;
863	}
864
865	return TRUE;
866}
867
868
869void uncompress_directory_table(long long start, long long end)
870{
871	int bytes = 0, size = 0, res;
872
873	while(start < end) {
874		if(size - bytes < SQUASHFS_METADATA_SIZE && (directory_table =
875				realloc(directory_table, size +=
876				SQUASHFS_METADATA_SIZE)) == NULL)
877			EXIT_UNSQUASH("uncompress_directory_table: out of memory in realloc\n");
878		TRACE("uncompress_directory_table: reading block 0x%llx\n", start);
879		add_entry(directory_table_hash, start, bytes);
880		if((res = read_block(start, &start, directory_table + bytes)) == 0)
881			EXIT_UNSQUASH("uncompress_directory_table: failed to read block\n");
882		bytes += res;
883	}
884}
885
886
887#define DIR_ENT_SIZE	16
888
889struct dir_ent	{
890	char		name[SQUASHFS_NAME_LEN + 1];
891	unsigned int	start_block;
892	unsigned int	offset;
893	unsigned int	type;
894};
895
896struct dir {
897	int		dir_count;
898	int 		cur_entry;
899	unsigned int	mode;
900	unsigned int	uid;
901	unsigned int	guid;
902	unsigned int	mtime;
903	struct dir_ent	*dirs;
904};
905
906
907struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset)
908{
909	squashfs_dir_header dirh;
910	char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
911	squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer;
912	long long start = sBlk.inode_table_start + block_start;
913	char *block_ptr;
914	int bytes = lookup_entry(inode_table_hash, start);
915	squashfs_inode_header header;
916	int dir_count, size;
917	struct dir_ent *new_dir;
918	struct dir *dir;
919
920	TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start, offset);
921
922	if(bytes == -1) {
923		ERROR("squashfs_opendir: inode block %d not found!\n", block_start);
924		return NULL;
925	}
926	block_ptr = inode_table + bytes + offset;
927
928	if(swap) {
929		squashfs_dir_inode_header sinode;
930		memcpy(&sinode, block_ptr, sizeof(header.dir));
931		SQUASHFS_SWAP_DIR_INODE_HEADER(&header.dir, &sinode);
932	} else
933		memcpy(&header.dir, block_ptr, sizeof(header.dir));
934
935	switch(header.dir.inode_type) {
936		case SQUASHFS_DIR_TYPE:
937			block_start = header.dir.start_block;
938			offset = header.dir.offset;
939			size = header.dir.file_size;
940			break;
941		case SQUASHFS_LDIR_TYPE:
942			if(swap) {
943				squashfs_ldir_inode_header sinode;
944				memcpy(&sinode, block_ptr, sizeof(header.ldir));
945				SQUASHFS_SWAP_LDIR_INODE_HEADER(&header.ldir, &sinode);
946			} else
947				memcpy(&header.ldir, block_ptr, sizeof(header.ldir));
948			block_start = header.ldir.start_block;
949			offset = header.ldir.offset;
950			size = header.ldir.file_size;
951			break;
952		default:
953			ERROR("squashfs_opendir: inode not a directory\n");
954			return NULL;
955	}
956
957	start = sBlk.directory_table_start + block_start;
958	bytes = lookup_entry(directory_table_hash, start);
959
960	if(bytes == -1) {
961		ERROR("squashfs_opendir: directory block %d not found!\n", block_start);
962		return NULL;
963	}
964	bytes += offset;
965	size += bytes - 3;
966
967	if((dir = malloc(sizeof(struct dir))) == NULL) {
968		ERROR("squashfs_opendir: malloc failed!\n");
969		return NULL;
970	}
971
972	dir->dir_count = 0;
973	dir->cur_entry = 0;
974	dir->mode = header.dir.mode;
975	dir->uid = header.dir.uid;
976	dir->guid = header.dir.guid;
977	dir->mtime = header.dir.mtime;
978	dir->dirs = NULL;
979
980	while(bytes < size) {
981		if(swap) {
982			squashfs_dir_header sdirh;
983			memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
984			SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
985		} else
986			memcpy(&dirh, directory_table + bytes, sizeof(dirh));
987
988		dir_count = dirh.count + 1;
989		TRACE("squashfs_opendir: Read directory header @ byte position %d, %d directory entries\n", bytes, dir_count);
990		bytes += sizeof(dirh);
991
992		while(dir_count--) {
993			if(swap) {
994				squashfs_dir_entry sdire;
995				memcpy(&sdire, directory_table + bytes, sizeof(sdire));
996				SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
997			} else
998				memcpy(dire, directory_table + bytes, sizeof(dire));
999			bytes += sizeof(*dire);
1000
1001			memcpy(dire->name, directory_table + bytes, dire->size + 1);
1002			dire->name[dire->size + 1] = '\0';
1003			TRACE("squashfs_opendir: directory entry %s, inode %d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type);
1004			if((dir->dir_count % DIR_ENT_SIZE) == 0) {
1005				if((new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent))) == NULL) {
1006					ERROR("squashfs_opendir: realloc failed!\n");
1007					free(dir->dirs);
1008					free(dir);
1009					return NULL;
1010				}
1011				dir->dirs = new_dir;
1012			}
1013			strcpy(dir->dirs[dir->dir_count].name, dire->name);
1014			dir->dirs[dir->dir_count].start_block = dirh.start_block;
1015			dir->dirs[dir->dir_count].offset = dire->offset;
1016			dir->dirs[dir->dir_count].type = dire->type;
1017			dir->dir_count ++;
1018			bytes += dire->size + 1;
1019		}
1020	}
1021
1022	return dir;
1023}
1024
1025
1026struct dir *squashfs_opendir_2(unsigned int block_start, unsigned int offset)
1027{
1028	squashfs_dir_header_2 dirh;
1029	char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
1030	squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
1031	long long start = sBlk.inode_table_start + block_start;
1032	char *block_ptr;
1033	int bytes = lookup_entry(inode_table_hash, start);
1034	squashfs_inode_header_2 header;
1035	int dir_count, size;
1036	struct dir_ent *new_dir;
1037	struct dir *dir;
1038
1039	TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start, offset);
1040
1041	if(bytes == -1) {
1042		ERROR("squashfs_opendir: inode block %d not found!\n", block_start);
1043		return NULL;
1044	}
1045	block_ptr = inode_table + bytes + offset;
1046
1047	if(swap) {
1048		squashfs_dir_inode_header_2 sinode;
1049		memcpy(&sinode, block_ptr, sizeof(header.dir));
1050		SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir, &sinode);
1051	} else
1052		memcpy(&header.dir, block_ptr, sizeof(header.dir));
1053
1054	switch(header.dir.inode_type) {
1055		case SQUASHFS_DIR_TYPE:
1056			block_start = header.dir.start_block;
1057			offset = header.dir.offset;
1058			size = header.dir.file_size;
1059			break;
1060		case SQUASHFS_LDIR_TYPE:
1061			if(swap) {
1062				squashfs_ldir_inode_header_2 sinode;
1063				memcpy(&sinode, block_ptr, sizeof(header.ldir));
1064				SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir, &sinode);
1065			} else
1066				memcpy(&header.ldir, block_ptr, sizeof(header.ldir));
1067			block_start = header.ldir.start_block;
1068			offset = header.ldir.offset;
1069			size = header.ldir.file_size;
1070			break;
1071		default:
1072			ERROR("squashfs_opendir: inode not a directory\n");
1073			return NULL;
1074	}
1075
1076	start = sBlk.directory_table_start + block_start;
1077	bytes = lookup_entry(directory_table_hash, start);
1078
1079	if(bytes == -1) {
1080		ERROR("squashfs_opendir: directory block %d not found!\n", block_start);
1081		return NULL;
1082	}
1083	bytes += offset;
1084	size += bytes;
1085
1086	if((dir = malloc(sizeof(struct dir))) == NULL) {
1087		ERROR("squashfs_opendir: malloc failed!\n");
1088		return NULL;
1089	}
1090
1091	dir->dir_count = 0;
1092	dir->cur_entry = 0;
1093	dir->mode = header.dir.mode;
1094	dir->uid = header.dir.uid;
1095	dir->guid = header.dir.guid;
1096	dir->mtime = header.dir.mtime;
1097	dir->dirs = NULL;
1098
1099	while(bytes < size) {
1100		if(swap) {
1101			squashfs_dir_header_2 sdirh;
1102			memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
1103			SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
1104		} else
1105			memcpy(&dirh, directory_table + bytes, sizeof(dirh));
1106
1107		dir_count = dirh.count + 1;
1108		TRACE("squashfs_opendir: Read directory header @ byte position %d, %d directory entries\n", bytes, dir_count);
1109		bytes += sizeof(dirh);
1110
1111		while(dir_count--) {
1112			if(swap) {
1113				squashfs_dir_entry_2 sdire;
1114				memcpy(&sdire, directory_table + bytes, sizeof(sdire));
1115				SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
1116			} else
1117				memcpy(dire, directory_table + bytes, sizeof(dire));
1118			bytes += sizeof(*dire);
1119
1120			memcpy(dire->name, directory_table + bytes, dire->size + 1);
1121			dire->name[dire->size + 1] = '\0';
1122			TRACE("squashfs_opendir: directory entry %s, inode %d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type);
1123			if((dir->dir_count % DIR_ENT_SIZE) == 0) {
1124				if((new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent))) == NULL) {
1125					ERROR("squashfs_opendir: realloc failed!\n");
1126					free(dir->dirs);
1127					free(dir);
1128					return NULL;
1129				}
1130				dir->dirs = new_dir;
1131			}
1132			strcpy(dir->dirs[dir->dir_count].name, dire->name);
1133			dir->dirs[dir->dir_count].start_block = dirh.start_block;
1134			dir->dirs[dir->dir_count].offset = dire->offset;
1135			dir->dirs[dir->dir_count].type = dire->type;
1136			dir->dir_count ++;
1137			bytes += dire->size + 1;
1138		}
1139	}
1140
1141	return dir;
1142}
1143
1144
1145int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block,
1146unsigned int *offset, unsigned int *type)
1147{
1148	if(dir->cur_entry == dir->dir_count)
1149		return FALSE;
1150
1151	*name = dir->dirs[dir->cur_entry].name;
1152	*start_block = dir->dirs[dir->cur_entry].start_block;
1153	*offset = dir->dirs[dir->cur_entry].offset;
1154	*type = dir->dirs[dir->cur_entry].type;
1155	dir->cur_entry ++;
1156
1157	return TRUE;
1158}
1159
1160
1161void squashfs_closedir(struct dir *dir)
1162{
1163	free(dir->dirs);
1164	free(dir);
1165}
1166
1167
1168char *get_component(char *target, char *targname)
1169{
1170	while(*target == '/')
1171		*target ++;
1172
1173	while(*target != '/' && *target!= '\0')
1174		*targname ++ = *target ++;
1175
1176	*targname = '\0';
1177
1178	return target;
1179}
1180
1181
1182int matches(char *targname, char *name)
1183{
1184	if(*targname == '\0' || strcmp(targname, name) == 0)
1185		return TRUE;
1186
1187	return FALSE;
1188}
1189
1190
1191int dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, char *target)
1192{
1193	struct dir *dir = s_ops.squashfs_opendir(start_block, offset);
1194	unsigned int type;
1195	char *name, pathname[1024];
1196	char targname[1024];
1197
1198	target = get_component(target, targname);
1199
1200	if(dir == NULL) {
1201		ERROR("dir_scan: Failed to read directory %s (%x:%x)\n", parent_name, start_block, offset);
1202		return FALSE;
1203	}
1204
1205	if(!lsonly && mkdir(parent_name, (mode_t) dir->mode) == -1 && (!force || errno != EEXIST)) {
1206		ERROR("dir_scan: failed to open directory %s, because %s\n", parent_name, strerror(errno));
1207		return FALSE;
1208	}
1209
1210	while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) {
1211		TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", name, start_block, offset, type);
1212
1213
1214		if(!matches(targname, name))
1215			continue;
1216
1217		strcat(strcat(strcpy(pathname, parent_name), "/"), name);
1218
1219		if(lsonly || info)
1220			printf("%s\n", pathname);
1221
1222		if(type == SQUASHFS_DIR_TYPE)
1223			dir_scan(pathname, start_block, offset, target);
1224		else
1225			if(!lsonly)
1226				s_ops.create_inode(pathname, start_block, offset);
1227	}
1228
1229	!lsonly && set_attributes(parent_name, dir->mode, dir->uid, dir->guid, dir->mtime, force);
1230
1231	squashfs_closedir(dir);
1232	dir_count ++;
1233
1234	return TRUE;
1235}
1236
1237
1238int read_super(char *source)
1239{
1240	squashfs_super_block sblk;
1241
1242	read_bytes(SQUASHFS_START, sizeof(squashfs_super_block), (char *) &sBlk);
1243
1244	/* Check it is a SQUASHFS superblock */
1245	swap = 0;
1246	switch (sBlk.s_magic) {
1247	case SQUASHFS_MAGIC:
1248	case SQUASHFS_MAGIC_LZMA:
1249		break;
1250	case SQUASHFS_MAGIC_SWAP:
1251	case SQUASHFS_MAGIC_LZMA_SWAP:
1252		ERROR("Reading a different endian SQUASHFS filesystem on %s\n", source);
1253		SQUASHFS_SWAP_SUPER_BLOCK(&sblk, &sBlk);
1254		memcpy(&sBlk, &sblk, sizeof(squashfs_super_block));
1255		swap = 1;
1256	default:
1257		ERROR("Can't find a SQUASHFS superblock on %s\n", source);
1258		goto failed_mount;
1259	}
1260
1261	/* Check the MAJOR & MINOR versions */
1262	if(sBlk.s_major == 2) {
1263		sBlk.bytes_used = sBlk.bytes_used_2;
1264		sBlk.uid_start = sBlk.uid_start_2;
1265		sBlk.guid_start = sBlk.guid_start_2;
1266		sBlk.inode_table_start = sBlk.inode_table_start_2;
1267		sBlk.directory_table_start = sBlk.directory_table_start_2;
1268		sBlk.fragment_table_start = sBlk.fragment_table_start_2;
1269
1270		s_ops.squashfs_opendir = squashfs_opendir_2;
1271		s_ops.read_fragment = read_fragment_2;
1272		s_ops.read_fragment_table = read_fragment_table_2;
1273		s_ops.create_inode = create_inode_2;
1274	} else if(sBlk.s_major == 3 && sBlk.s_minor == 0) {
1275		s_ops.squashfs_opendir = squashfs_opendir;
1276		s_ops.read_fragment = read_fragment;
1277		s_ops.read_fragment_table = read_fragment_table;
1278		s_ops.create_inode = create_inode;
1279	} else {
1280		ERROR("Major/Minor mismatch, filesystem on %s is (%d:%d)\n",
1281				source, sBlk.s_major, sBlk.s_minor);
1282		ERROR("I support Squashfs 2.x and 3.0 filesystems!\n");
1283		goto failed_mount;
1284	}
1285
1286#if __BYTE_ORDER == __BIG_ENDIAN
1287	TRACE("Found a valid %s endian SQUASHFS %d:%d superblock on %s.\n", swap ? "little" : "big", sBlk.s_major, sBlk.s_minor, source);
1288#else
1289	TRACE("Found a valid %s endian SQUASHFS %d:%d superblock on %s.\n", swap ? "big" : "little", sBlk.s_major, sBlk.s_minor, source);
1290#endif
1291
1292	TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk.flags) ? "un" : "");
1293	TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk.flags) ? "un" : "");
1294	TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.flags) ? "un" : "");
1295	TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk.flags) ? "" : "not");
1296	TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk.flags) ? "not" : "");
1297	TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk.flags) ? "" : "not");
1298	TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(sBlk.flags) ? "" : "not");
1299	TRACE("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n", sBlk.bytes_used / 1024.0, sBlk.bytes_used / (1024.0 * 1024.0));
1300	TRACE("\tBlock size %d\n", sBlk.block_size);
1301	TRACE("\tNumber of fragments %d\n", sBlk.fragments);
1302	TRACE("\tNumber of inodes %d\n", sBlk.inodes);
1303	TRACE("\tNumber of uids %d\n", sBlk.no_uids);
1304	TRACE("\tNumber of gids %d\n", sBlk.no_guids);
1305	TRACE("sBlk.inode_table_start 0x%llx\n", sBlk.inode_table_start);
1306	TRACE("sBlk.directory_table_start 0x%llx\n", sBlk.directory_table_start);
1307	TRACE("sBlk.uid_start 0x%llx\n", sBlk.uid_start);
1308	TRACE("sBlk.fragment_table_start 0x%llx\n\n", sBlk.fragment_table_start);
1309
1310	return TRUE;
1311
1312failed_mount:
1313	return FALSE;
1314}
1315
1316
1317#define VERSION() \
1318	printf("unsquashfs version 1.3 (2007/01/02)\n");\
1319	printf("copyright (C) 2007 Phillip Lougher <phillip@lougher.org.uk>\n\n"); \
1320    	printf("This program is free software; you can redistribute it and/or\n");\
1321	printf("modify it under the terms of the GNU General Public License\n");\
1322	printf("as published by the Free Software Foundation; either version 2,\n");\
1323	printf("or (at your option) any later version.\n\n");\
1324	printf("This program is distributed in the hope that it will be useful,\n");\
1325	printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");\
1326	printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");\
1327	printf("GNU General Public License for more details.\n");
1328int main(int argc, char *argv[])
1329{
1330	char *dest = "squashfs-root";
1331	int i, version = FALSE;
1332	char *target = "";
1333
1334	if((root_process = (geteuid() == 0)))
1335		umask(0);
1336
1337	for(i = 1; i < argc; i++) {
1338		if(*argv[i] != '-')
1339			break;
1340		if(strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "-v") == 0) {
1341			VERSION();
1342			version = TRUE;
1343		} else if(strcmp(argv[i], "-info") == 0 || strcmp(argv[i], "-i") == 0)
1344			info = TRUE;
1345		else if(strcmp(argv[i], "-ls") == 0 || strcmp(argv[i], "-l") == 0)
1346			lsonly = TRUE;
1347		else if(strcmp(argv[i], "-dest") == 0 || strcmp(argv[i], "-d") == 0) {
1348			if(++i == argc)
1349				goto options;
1350			dest = argv[i];
1351		} else if(strcmp(argv[i], "-force") == 0 || strcmp(argv[i], "-f") == 0)
1352			force = TRUE;
1353	}
1354
1355	if(i == argc) {
1356		if(!version) {
1357options:
1358			ERROR("SYNTAX: %s [options] filesystem [directory or file to extract]\n", argv[0]);
1359			ERROR("\t-v[ersion]\t\tprint version, licence and copyright information\n");
1360			ERROR("\t-i[nfo]\t\t\tprint files as they are unsquashed\n");
1361			ERROR("\t-l[s]\t\t\tlist filesystem only\n");
1362			ERROR("\t-d[est] <pathname>\tunsquash to <pathname>, default \"squashfs-root\"\n");
1363			ERROR("\t-f[orce]\t\tif file already exists then overwrite\n");
1364		}
1365		exit(1);
1366	}
1367
1368	if((i + 1) < argc)
1369		target = argv[i + 1];
1370
1371	if((fd = open(argv[i], O_RDONLY)) == -1) {
1372		ERROR("Could not open %s, because %s\n", argv[i], strerror(errno));
1373		exit(1);
1374	}
1375
1376	if(read_super(argv[i]) == FALSE)
1377		exit(1);
1378
1379	block_size = sBlk.block_size;
1380	if((fragment_data = malloc(block_size)) == NULL)
1381		EXIT_UNSQUASH("failed to allocate fragment_data\n");
1382
1383	if((file_data = malloc(block_size)) == NULL)
1384		EXIT_UNSQUASH("failed to allocate file_data");
1385
1386	if((data = malloc(block_size)) == NULL)
1387		EXIT_UNSQUASH("failed to allocate datan\n");
1388
1389	if((created_inode = malloc(sBlk.inodes * sizeof(char *))) == NULL)
1390		EXIT_UNSQUASH("failed to allocate created_inode\n");
1391
1392	memset(created_inode, 0, sBlk.inodes * sizeof(char *));
1393
1394	read_uids_guids();
1395	s_ops.read_fragment_table();
1396	uncompress_inode_table(sBlk.inode_table_start, sBlk.directory_table_start);
1397	uncompress_directory_table(sBlk.directory_table_start, sBlk.fragment_table_start);
1398
1399	dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.root_inode), SQUASHFS_INODE_OFFSET(sBlk.root_inode), target);
1400
1401	if(!lsonly) {
1402		printf("\n");
1403		printf("created %d files\n", file_count);
1404		printf("created %d directories\n", dir_count);
1405		printf("created %d symlinks\n", sym_count);
1406		printf("created %d devices\n", dev_count);
1407		printf("created %d fifos\n", fifo_count);
1408	}
1409	return 0;
1410}
1411