unsquashfs.c revision b54566f5c433764830c29c83151691d0034de094
1/*
2 * Unsquash a squashfs filesystem.  This is a highly compressed read only filesystem.
3 *
4 * Copyright (c) 2002, 2003, 2004, 2005, 2006
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 TRUE 1
25#define FALSE 0
26#include <stdio.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <string.h>
32#include <zlib.h>
33#include <sys/mman.h>
34#include <utime.h>
35
36#ifndef linux
37#define __BYTE_ORDER BYTE_ORDER
38#define __BIG_ENDIAN BIG_ENDIAN
39#define __LITTLE_ENDIAN LITTLE_ENDIAN
40#else
41#include <endian.h>
42#endif
43
44#include <squashfs_fs.h>
45#include "read_fs.h"
46#include "global.h"
47
48#include <stdlib.h>
49
50#ifdef SQUASHFS_TRACE
51#define TRACE(s, args...)		do { \
52						printf("mksquashfs: "s, ## args); \
53					} while(0)
54#else
55#define TRACE(s, args...)
56#endif
57
58#define ERROR(s, args...)		do { \
59						fprintf(stderr, s, ## args); \
60					} while(0)
61
62#define EXIT_UNSQUASH(s, args...)	do { \
63						fprintf(stderr, "FATAL ERROR aborting: "s, ## args); \
64					} while(0)
65
66struct hash_table_entry {
67	int	start;
68	int	bytes;
69	struct hash_table_entry *next;
70};
71
72int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, dev_count = 0, fifo_count = 0;
73char *inode_table = NULL, *directory_table = NULL;
74struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536];
75int fd;
76squashfs_fragment_entry *fragment_table;
77unsigned int *uid_table, *guid_table;
78unsigned int cached_frag = SQUASHFS_INVALID_FRAG;
79char *fragment_data;
80char *file_data;
81char *data;
82unsigned int block_size;
83int lsonly = FALSE, info = FALSE;
84char **created_inode;
85
86#define CALCULATE_HASH(start)	(start & 0xffff)
87
88int add_entry(struct hash_table_entry *hash_table[], int start, int bytes)
89{
90	int hash = CALCULATE_HASH(start);
91	struct hash_table_entry *hash_table_entry;
92
93	if((hash_table_entry = malloc(sizeof(struct hash_table_entry))) == NULL) {
94		ERROR("add_hash: out of memory in malloc\n");
95		return FALSE;
96	}
97
98	hash_table_entry->start = start;
99	hash_table_entry->bytes = bytes;
100	hash_table_entry->next = hash_table[hash];
101	hash_table[hash] = hash_table_entry;
102
103	return TRUE;
104}
105
106
107int lookup_entry(struct hash_table_entry *hash_table[], int start)
108{
109	int hash = CALCULATE_HASH(start);
110	struct hash_table_entry *hash_table_entry;
111
112	for(hash_table_entry = hash_table[hash]; hash_table_entry; hash_table_entry = hash_table_entry->next)
113		if(hash_table_entry->start == start)
114			return hash_table_entry->bytes;
115
116	return -1;
117}
118
119
120int read_bytes(long long byte, int bytes, char *buff)
121{
122	off_t off = byte;
123
124	TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, bytes);
125
126	if(lseek(fd, off, SEEK_SET) == -1) {
127		ERROR("Lseek failed because %s\b", strerror(errno));
128		return FALSE;
129	}
130
131	if(read(fd, buff, bytes) == -1) {
132		ERROR("Read on destination failed because %s\n", strerror(errno));
133		return FALSE;
134	}
135
136	return TRUE;
137}
138
139
140int read_block(long long start, long long *next, char *block, squashfs_super_block *sBlk)
141{
142	unsigned short c_byte;
143	int offset = 2;
144
145	if(swap) {
146		if(read_bytes(start, 2, block) == FALSE)
147			goto failed;
148		((unsigned char *) &c_byte)[1] = block[0];
149		((unsigned char *) &c_byte)[0] = block[1];
150	} else
151		if(read_bytes(start, 2, (char *)&c_byte) == FALSE)
152			goto failed;
153
154	TRACE("read_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? "compressed" : "uncompressed");
155
156	if(SQUASHFS_CHECK_DATA(sBlk->flags))
157		offset = 3;
158	if(SQUASHFS_COMPRESSED(c_byte)) {
159		char buffer[SQUASHFS_METADATA_SIZE];
160		int res;
161		unsigned long bytes = SQUASHFS_METADATA_SIZE;
162
163		c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
164		if(read_bytes(start + offset, c_byte, buffer) == FALSE)
165			goto failed;
166
167		if((res = uncompress((unsigned char *) block, &bytes, (const unsigned char *) buffer, c_byte)) != Z_OK) {
168			if(res == Z_MEM_ERROR)
169				ERROR("zlib::uncompress failed, not enough memory\n");
170			else if(res == Z_BUF_ERROR)
171				ERROR("zlib::uncompress failed, not enough room in output buffer\n");
172			else
173				ERROR("zlib::uncompress failed, unknown error %d\n", res);
174			goto failed;
175		}
176		if(next)
177			*next = start + offset + c_byte;
178		return bytes;
179	} else {
180		c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
181		if(read_bytes(start + offset, c_byte, block) == FALSE)
182			goto failed;
183		if(next)
184			*next = start + offset + c_byte;
185		return c_byte;
186	}
187
188failed:
189	return FALSE;
190}
191
192
193int read_data_block(long long start, unsigned int size, char *block)
194{
195	int res;
196	unsigned long bytes = block_size;
197	int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
198
199	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");
200
201	if(SQUASHFS_COMPRESSED_BLOCK(size)) {
202		if(read_bytes(start, c_byte, data) == FALSE)
203			return 0;
204
205		if((res = uncompress((unsigned char *) block, &bytes, (const unsigned char *) data, c_byte)) != Z_OK) {
206			if(res == Z_MEM_ERROR)
207				ERROR("zlib::uncompress failed, not enough memory\n");
208			else if(res == Z_BUF_ERROR)
209				ERROR("zlib::uncompress failed, not enough room in output buffer\n");
210			else
211				ERROR("zlib::uncompress failed, unknown error %d\n", res);
212			return 0;
213		}
214
215		return bytes;
216	} else {
217		if(read_bytes(start, c_byte, block) == FALSE)
218			return 0;
219
220		return c_byte;
221	}
222}
223
224
225void uncompress_inode_table(long long start, long long end, squashfs_super_block *sBlk)
226{
227	int size = 0, bytes = 0, res;
228
229	while(start < end) {
230		if((size - bytes < SQUASHFS_METADATA_SIZE) &&
231				((inode_table = realloc(inode_table, size += SQUASHFS_METADATA_SIZE)) == NULL))
232			EXIT_UNSQUASH("uncompress_inode_table: out of memory in realloc\n");
233		TRACE("uncompress_inode_table: reading block 0x%llx\n", start);
234		add_entry(inode_table_hash, start, bytes);
235		if((res = read_block(start, &start, inode_table + bytes, sBlk)) == 0) {
236			free(inode_table);
237			EXIT_UNSQUASH("uncompress_inode_table: failed to read block\n");
238		}
239		bytes += res;
240	}
241}
242
243
244int set_attributes(char *pathname, unsigned int mode, unsigned int uid, unsigned int guid,
245unsigned int mtime, unsigned int set_mode)
246{
247	struct utimbuf times = { (time_t) mtime, (time_t) mtime };
248
249	if(utime(pathname, &times) == -1) {
250		ERROR("set_attributes: failed to set time on %s, because %s\n", pathname, strerror(errno));
251		return FALSE;
252	}
253
254	if(set_mode && chmod(pathname, (mode_t) mode) == -1) {
255		ERROR("set_attributes: failed to change mode %s, because %s\n", pathname, strerror(errno));
256		return FALSE;
257	}
258
259	if(geteuid() == 0) {
260		uid_t uid_value = (uid_t) uid_table[uid];
261		uid_t guid_value = guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[guid];
262
263		if(chown(pathname, uid_value, guid_value) == -1) {
264			ERROR("set_attributes: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno));
265			return FALSE;
266		}
267	}
268
269	return TRUE;
270}
271
272
273void read_uids_guids(squashfs_super_block *sBlk)
274{
275	if((uid_table = malloc((sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int))) == NULL)
276		EXIT_UNSQUASH("read_uids_guids: failed to allocate uid/gid table\n");
277
278	guid_table = uid_table + sBlk->no_uids;
279
280	if(swap) {
281		unsigned int suid_table[sBlk->no_uids + sBlk->no_guids];
282
283		if(read_bytes(sBlk->uid_start, (sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int), (char *) suid_table) ==
284			FALSE)
285			EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n");
286		SQUASHFS_SWAP_INTS(uid_table, suid_table, sBlk->no_uids + sBlk->no_guids);
287	} else
288		if(read_bytes(sBlk->uid_start, (sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int), (char *) uid_table) ==
289			FALSE)
290			EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n");
291}
292
293
294void read_fragment_table(squashfs_super_block *sBlk)
295{
296	int i, indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
297	squashfs_fragment_index fragment_table_index[indexes];
298
299	TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%llx\n", sBlk->fragments, indexes, sBlk->fragment_table_start);
300	if(sBlk->fragments == 0)
301		return;
302
303	if((fragment_table = (squashfs_fragment_entry *) malloc(sBlk->fragments * sizeof(squashfs_fragment_entry))) == NULL)
304		EXIT_UNSQUASH("read_fragment_table: failed to allocate fragment table\n");
305
306	if(swap) {
307		squashfs_fragment_index sfragment_table_index[indexes];
308
309		read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) sfragment_table_index);
310		SQUASHFS_SWAP_FRAGMENT_INDEXES(fragment_table_index, sfragment_table_index, indexes);
311	} else
312		read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) fragment_table_index);
313
314	for(i = 0; i < indexes; i++) {
315		int length = read_block(fragment_table_index[i], NULL, ((char *) fragment_table) + (i * SQUASHFS_METADATA_SIZE), sBlk);
316		TRACE("Read fragment table block %d, from 0x%llx, length %d\n", i, fragment_table_index[i], length);
317	}
318
319	if(swap) {
320		squashfs_fragment_entry sfragment;
321		for(i = 0; i < sBlk->fragments; i++) {
322			SQUASHFS_SWAP_FRAGMENT_ENTRY((&sfragment), (&fragment_table[i]));
323			memcpy((char *) &fragment_table[i], (char *) &sfragment, sizeof(squashfs_fragment_entry));
324		}
325	}
326}
327
328
329char *read_fragment(unsigned int fragment)
330{
331	TRACE("read_fragment: reading fragment %d\n", fragment);
332
333	if(cached_frag == SQUASHFS_INVALID_FRAG || fragment != cached_frag) {
334		squashfs_fragment_entry *fragment_entry = &fragment_table[fragment];
335		if(read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data) == 0) {
336			ERROR("read_fragment: failed to read fragment %d\n", fragment);
337			cached_frag = SQUASHFS_INVALID_FRAG;
338			return NULL;
339		}
340		cached_frag = fragment;
341	}
342
343	return fragment_data;
344}
345
346
347int write_file(char *pathname, unsigned int fragment, unsigned int frag_bytes, unsigned int offset,
348unsigned int blocks, long long start, char *block_ptr, unsigned int mode)
349{
350	unsigned int file_fd, bytes, i;
351	unsigned int *block_list;
352
353	TRACE("write_file: regular file, blocks %d\n", blocks);
354
355	if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) {
356		ERROR("write_file: unable to malloc block list\n");
357		return FALSE;
358	}
359
360	if(swap) {
361		unsigned int sblock_list[blocks];
362		memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int));
363		SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks);
364	} else
365		memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
366
367	if((file_fd = open(pathname, O_CREAT | O_WRONLY, (mode_t) mode)) == -1) {
368		ERROR("write_file: failed to create file %s, because %s\n", pathname,
369			strerror(errno));
370		free(block_list);
371		return FALSE;
372	}
373
374	for(i = 0; i < blocks; i++) {
375		if((bytes = read_data_block(start, block_list[i], file_data)) == 0) {
376			ERROR("write_file: failed to read data block 0x%llx\n", start);
377			goto failure;
378		}
379
380		if(write(file_fd, file_data, bytes) < bytes) {
381			ERROR("write_file: failed to write data block 0x%llx\n", start);
382			goto failure;
383		}
384
385		start += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
386	}
387
388	if(frag_bytes != 0) {
389		char *fragment_data = read_fragment(fragment);
390
391		if(fragment_data == NULL)
392			goto failure;
393
394		if(write(file_fd, fragment_data + offset, frag_bytes) < frag_bytes) {
395			ERROR("write_file: failed to write fragment %d\n", fragment);
396			goto failure;
397		}
398	}
399
400	close(file_fd);
401	return TRUE;
402
403failure:
404	close(file_fd);
405	free(block_list);
406	return FALSE;
407}
408
409
410int create_inode(char *pathname, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk)
411{
412	long long start = sBlk->inode_table_start + start_block;
413	squashfs_inode_header header;
414	char *block_ptr;
415	int bytes = lookup_entry(inode_table_hash, start), file_fd;
416
417	TRACE("create_inode: pathname %s, start 0x%llx, offset %d\n", pathname, start, offset);
418
419	if(bytes == -1) {
420		ERROR("create_inode: inode block 0x%llx out of range!\n", start);
421		return FALSE;
422	}
423	block_ptr = inode_table + bytes + offset;
424
425	if(swap) {
426		squashfs_base_inode_header sinode;
427		memcpy(&sinode, block_ptr, sizeof(header.base));
428		SQUASHFS_SWAP_BASE_INODE_HEADER(&header.base, &sinode, sizeof(squashfs_base_inode_header));
429	} else
430		memcpy(&header.base, block_ptr, sizeof(header.base));
431
432	if(created_inode[header.base.inode_number - 1]) {
433		TRACE("create_inode: hard link\n");
434		if(link(created_inode[header.base.inode_number - 1], pathname) == -1) {
435			ERROR("create_inode: failed to create hardlink, because %s\n", strerror(errno));
436			return FALSE;
437		}
438
439		return TRUE;
440	}
441
442	switch(header.base.inode_type) {
443		case SQUASHFS_FILE_TYPE: {
444			unsigned int frag_bytes;
445			unsigned int blocks;
446			unsigned int offset;
447			long long start;
448			squashfs_reg_inode_header *inode = &header.reg;
449
450			if(swap) {
451				squashfs_reg_inode_header sinode;
452				memcpy(&sinode, block_ptr, sizeof(sinode));
453				SQUASHFS_SWAP_REG_INODE_HEADER(inode, &sinode);
454			} else
455				memcpy(inode, block_ptr, sizeof(*inode));
456
457			frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size;
458			offset = inode->offset;
459			blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size
460				+ sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >>
461				sBlk->block_log;
462			start = inode->start_block;
463
464			TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
465
466			if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start,
467					block_ptr + sizeof(*inode), inode->mode)) {
468				set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE);
469				file_count ++;
470			}
471			break;
472		}
473		case SQUASHFS_LREG_TYPE: {
474			unsigned int frag_bytes;
475			unsigned int blocks;
476			unsigned int offset;
477			long long start;
478			squashfs_lreg_inode_header *inode = &header.lreg;
479
480			if(swap) {
481				squashfs_lreg_inode_header sinode;
482				memcpy(&sinode, block_ptr, sizeof(sinode));
483				SQUASHFS_SWAP_LREG_INODE_HEADER(inode, &sinode);
484			} else
485				memcpy(inode, block_ptr, sizeof(*inode));
486
487			frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size;
488			offset = inode->offset;
489			blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size
490				+ sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >>
491				sBlk->block_log;
492			start = inode->start_block;
493
494			TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
495
496			if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start,
497					block_ptr + sizeof(*inode), inode->mode)) {
498				set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE);
499				file_count ++;
500			}
501			break;
502		}
503		case SQUASHFS_SYMLINK_TYPE: {
504			squashfs_symlink_inode_header *inodep = &header.symlink;
505			char name[65536];
506
507			if(swap) {
508				squashfs_symlink_inode_header sinodep;
509				memcpy(&sinodep, block_ptr, sizeof(sinodep));
510				SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, &sinodep);
511			} else
512				memcpy(inodep, block_ptr, sizeof(*inodep));
513
514			TRACE("create_inode: symlink, symlink_size %d\n", inodep->symlink_size);
515
516			strncpy(name, block_ptr + sizeof(squashfs_symlink_inode_header), inodep->symlink_size);
517			name[inodep->symlink_size] = '\0';
518
519			if(symlink(name, pathname) == -1) {
520				ERROR("create_inode: failed to create symlink %s, because %s\n", pathname,
521					strerror(errno));
522				break;
523			}
524
525			if(geteuid() == 0) {
526				uid_t uid_value = (uid_t) uid_table[inodep->uid];
527				uid_t guid_value = inodep->guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[inodep->guid];
528
529				if(lchown(pathname, uid_value, guid_value) == -1)
530					ERROR("create_inode: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno));
531			}
532
533			sym_count ++;
534			break;
535		}
536 		case SQUASHFS_BLKDEV_TYPE:
537	 	case SQUASHFS_CHRDEV_TYPE: {
538			squashfs_dev_inode_header *inodep = &header.dev;
539
540			if(swap) {
541				squashfs_dev_inode_header sinodep;
542				memcpy(&sinodep, block_ptr, sizeof(sinodep));
543				SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, &sinodep);
544			} else
545				memcpy(inodep, block_ptr, sizeof(*inodep));
546
547			TRACE("create_inode: dev, rdev 0x%x\n", inodep->rdev);
548
549			if(geteuid() == 0) {
550				if(mknod(pathname, inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? S_IFCHR : S_IFBLK,
551							makedev((inodep->rdev >> 8) & 0xff, inodep->rdev & 0xff))
552							== -1) {
553					ERROR("create_inode: failed to create %s device %s, because %s\n",
554						inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
555						pathname, strerror(errno));
556					break;
557				}
558				set_attributes(pathname, inodep->mode, inodep->uid, inodep->guid, inodep->mtime, TRUE);
559				dev_count ++;
560			} else
561				ERROR("create_inode: could not create %s device %s, because you're not superuser!\n",
562					inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
563					pathname, strerror(errno));
564			break;
565			}
566		case SQUASHFS_FIFO_TYPE:
567			TRACE("create_inode: fifo\n");
568
569			if(mknod(pathname, S_IFIFO, 0) == -1) {
570				ERROR("create_inode: failed to create fifo %s, because %s\n",
571					pathname, strerror(errno));
572				break;
573			}
574			set_attributes(pathname, header.base.mode, header.base.uid, header.base.guid,
575				header.base.mtime, TRUE);
576			fifo_count ++;
577			break;
578		case SQUASHFS_SOCKET_TYPE:
579			TRACE("create_inode: socket\n");
580			ERROR("create_inode: socket %s ignored\n", pathname);
581			break;
582		default:
583			ERROR("Unknown inode type %d in create_inode_table!\n", header.base.inode_type);
584			return FALSE;
585	}
586
587	created_inode[header.base.inode_number - 1] = strdup(pathname);
588
589	return TRUE;
590}
591
592
593void uncompress_directory_table(long long start, long long end, squashfs_super_block *sBlk)
594{
595	int bytes = 0, size = 0, res;
596
597	while(start < end) {
598		if(size - bytes < SQUASHFS_METADATA_SIZE && (directory_table = realloc(directory_table, size += SQUASHFS_METADATA_SIZE)) == NULL)
599			EXIT_UNSQUASH("uncompress_directory_table: out of memory in realloc\n");
600		TRACE("uncompress_directory_table: reading block 0x%llx\n", start);
601		add_entry(directory_table_hash, start, bytes);
602		if((res = read_block(start, &start, directory_table + bytes, sBlk)) == 0)
603			EXIT_UNSQUASH("uncompress_directory_table: failed to read block\n");
604		bytes += res;
605	}
606}
607
608
609#define DIR_ENT_SIZE	16
610
611struct dir_ent	{
612	char		name[SQUASHFS_NAME_LEN + 1];
613	unsigned int	start_block;
614	unsigned int	offset;
615	unsigned int	type;
616};
617
618struct dir {
619	int		dir_count;
620	int 		cur_entry;
621	unsigned int	mode;
622	unsigned int	uid;
623	unsigned int	guid;
624	unsigned int	mtime;
625	struct dir_ent	*dirs;
626};
627
628
629struct dir *squashfs_openddir(unsigned int block_start, unsigned int offset, squashfs_super_block *sBlk)
630{
631	squashfs_dir_header dirh;
632	char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
633	squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer;
634	long long start = sBlk->inode_table_start + block_start;
635	char *block_ptr;
636	int bytes = lookup_entry(inode_table_hash, start);
637	squashfs_inode_header header;
638	int dir_count, size;
639	struct dir_ent *new_dir;
640	struct dir *dir;
641
642	TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start, offset);
643
644	if(bytes == -1) {
645		ERROR("squashfs_opendir: inode block %d not found!\n", block_start);
646		return NULL;
647	}
648	block_ptr = inode_table + bytes + offset;
649
650	if(swap) {
651		squashfs_dir_inode_header sinode;
652		memcpy(&sinode, block_ptr, sizeof(header.dir));
653		SQUASHFS_SWAP_DIR_INODE_HEADER(&header.dir, &sinode);
654	} else
655		memcpy(&header.dir, block_ptr, sizeof(header.dir));
656
657	switch(header.dir.inode_type) {
658		case SQUASHFS_DIR_TYPE:
659			block_start = header.dir.start_block;
660			offset = header.dir.offset;
661			size = header.dir.file_size;
662			break;
663		case SQUASHFS_LDIR_TYPE:
664			if(swap) {
665				squashfs_ldir_inode_header sinode;
666				memcpy(&sinode, block_ptr, sizeof(header.ldir));
667				SQUASHFS_SWAP_LDIR_INODE_HEADER(&header.ldir, &sinode);
668			} else
669				memcpy(&header.ldir, block_ptr, sizeof(header.ldir));
670			block_start = header.ldir.start_block;
671			offset = header.ldir.offset;
672			size = header.ldir.file_size;
673			break;
674		default:
675			ERROR("squashfs_opendir: inode not a directory\n");
676			return NULL;
677	}
678
679	start = sBlk->directory_table_start + block_start;
680	bytes = lookup_entry(directory_table_hash, start);
681
682	if(bytes == -1) {
683		ERROR("squashfs_opendir: directory block %d not found!\n", block_start);
684		return NULL;
685	}
686	bytes += offset;
687	size += bytes - 3;
688
689	if((dir = malloc(sizeof(struct dir))) == NULL) {
690		ERROR("squashfs_opendir: malloc failed!\n");
691		return NULL;
692	}
693
694	dir->dir_count = 0;
695	dir->cur_entry = 0;
696	dir->mode = header.dir.mode;
697	dir->uid = header.dir.uid;
698	dir->guid = header.dir.guid;
699	dir->mtime = header.dir.mtime;
700	dir->dirs = NULL;
701
702	while(bytes < size) {
703		if(swap) {
704			squashfs_dir_header sdirh;
705			memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
706			SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
707		} else
708			memcpy(&dirh, directory_table + bytes, sizeof(dirh));
709
710		dir_count = dirh.count + 1;
711		TRACE("squashfs_opendir: Read directory header @ byte position %d, %d directory entries\n", bytes, dir_count);
712		bytes += sizeof(dirh);
713
714		while(dir_count--) {
715			if(swap) {
716				squashfs_dir_entry sdire;
717				memcpy(&sdire, directory_table + bytes, sizeof(sdire));
718				SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
719			} else
720				memcpy(dire, directory_table + bytes, sizeof(dire));
721			bytes += sizeof(*dire);
722
723			memcpy(dire->name, directory_table + bytes, dire->size + 1);
724			dire->name[dire->size + 1] = '\0';
725			TRACE("squashfs_opendir: directory entry %s, inode %d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type);
726			if((dir->dir_count % DIR_ENT_SIZE) == 0) {
727				if((new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent))) == NULL) {
728					ERROR("squashfs_opendir: realloc failed!\n");
729					free(dir->dirs);
730					free(dir);
731					return NULL;
732				}
733				dir->dirs = new_dir;
734			}
735			strcpy(dir->dirs[dir->dir_count].name, dire->name);
736			dir->dirs[dir->dir_count].start_block = dirh.start_block;
737			dir->dirs[dir->dir_count].offset = dire->offset;
738			dir->dirs[dir->dir_count].type = dire->type;
739			dir->dir_count ++;
740			bytes += dire->size + 1;
741		}
742	}
743
744	return dir;
745}
746
747
748int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, unsigned int *offset, unsigned int *type)
749{
750	if(dir->cur_entry == dir->dir_count)
751		return FALSE;
752
753	*name = dir->dirs[dir->cur_entry].name;
754	*start_block = dir->dirs[dir->cur_entry].start_block;
755	*offset = dir->dirs[dir->cur_entry].offset;
756	*type = dir->dirs[dir->cur_entry].type;
757	dir->cur_entry ++;
758
759	return TRUE;
760}
761
762
763void squashfs_closedir(struct dir *dir)
764{
765	free(dir->dirs);
766	free(dir);
767}
768
769
770char *get_component(char *target, char *targname)
771{
772	while(*target == '/')
773		*target ++;
774
775	while(*target != '/' && *target!= '\0')
776		*targname ++ = *target ++;
777
778	*targname = '\0';
779
780	return target;
781}
782
783
784int matches(char *targname, char *name)
785{
786	if(*targname == '\0' || strcmp(targname, name) == 0)
787		return TRUE;
788
789	return FALSE;
790}
791
792
793int dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk, char *target)
794{
795	struct dir *dir = squashfs_openddir(start_block, offset, sBlk);
796	unsigned int type;
797	char *name, pathname[1024];
798	char targname[1024];
799
800	target = get_component(target, targname);
801
802	if(dir == NULL) {
803		ERROR("dir_scan: Failed to read directory %s (%x:%x)\n", parent_name, start_block, offset);
804		return FALSE;
805	}
806
807	if(!lsonly && mkdir(parent_name, (mode_t) dir->mode) == -1) {
808		ERROR("dir_scan: failed to open directory %s, because %s\n", parent_name, strerror(errno));
809		return FALSE;
810	}
811
812	while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) {
813		TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", name, start_block, offset, type);
814
815
816		if(!matches(targname, name))
817			continue;
818
819		strcat(strcat(strcpy(pathname, parent_name), "/"), name);
820
821		if(lsonly || info)
822			printf("%s\n", pathname);
823
824		if(type == SQUASHFS_DIR_TYPE)
825			dir_scan(pathname, start_block, offset, sBlk, target);
826		else
827			if(!lsonly)
828				create_inode(pathname, start_block, offset, sBlk);
829	}
830
831	!lsonly && set_attributes(parent_name, dir->mode, dir->uid, dir->guid, dir->mtime, TRUE);
832
833	squashfs_closedir(dir);
834	dir_count ++;
835
836	return TRUE;
837}
838
839
840int read_super(squashfs_super_block *sBlk, char *source)
841{
842	read_bytes(SQUASHFS_START, sizeof(squashfs_super_block), (char *) sBlk);
843
844	/* Check it is a SQUASHFS superblock */
845	swap = 0;
846	if(sBlk->s_magic != SQUASHFS_MAGIC) {
847		if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) {
848			squashfs_super_block sblk;
849			ERROR("Reading a different endian SQUASHFS filesystem on %s\n", source);
850			SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk);
851			memcpy(sBlk, &sblk, sizeof(squashfs_super_block));
852			swap = 1;
853		} else  {
854			ERROR("Can't find a SQUASHFS superblock on %s\n", source);
855			goto failed_mount;
856		}
857	}
858
859	/* Check the MAJOR & MINOR versions */
860	if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
861		ERROR("Major/Minor mismatch, filesystem on %s is (%d:%d)\n",
862				source, sBlk->s_major, sBlk->s_minor);
863		ERROR("I only support Squashfs 3.0 filesystems!  Later releases will support older Squashfs filesystems\n");
864		goto failed_mount;
865	}
866
867#if __BYTE_ORDER == __BIG_ENDIAN
868	TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "little" : "big", source);
869#else
870	TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "big" : "little", source);
871#endif
872
873	TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
874	TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
875	TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
876	TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not");
877	TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not" : "");
878	TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not");
879	TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not");
880	TRACE("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n", sBlk->bytes_used / 1024.0, sBlk->bytes_used / (1024.0 * 1024.0));
881	TRACE("\tBlock size %d\n", sBlk->block_size);
882	TRACE("\tNumber of fragments %d\n", sBlk->fragments);
883	TRACE("\tNumber of inodes %d\n", sBlk->inodes);
884	TRACE("\tNumber of uids %d\n", sBlk->no_uids);
885	TRACE("\tNumber of gids %d\n", sBlk->no_guids);
886	TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start);
887	TRACE("sBlk->directory_table_start 0x%llx\n", sBlk->directory_table_start);
888	TRACE("sBlk->uid_start 0x%llx\n", sBlk->uid_start);
889	TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start);
890	TRACE("\n");
891
892	return TRUE;
893
894failed_mount:
895	return FALSE;
896}
897
898
899#define VERSION() \
900	printf("unsquashfs version 1.1 (2006/07/09)\n");\
901	printf("copyright (C) 2006 Phillip Lougher <phillip@lougher.org.uk>\n\n"); \
902    	printf("This program is free software; you can redistribute it and/or\n");\
903	printf("modify it under the terms of the GNU General Public License\n");\
904	printf("as published by the Free Software Foundation; either version 2,\n");\
905	printf("or (at your option) any later version.\n\n");\
906	printf("This program is distributed in the hope that it will be useful,\n");\
907	printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");\
908	printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");\
909	printf("GNU General Public License for more details.\n");
910int main(int argc, char *argv[])
911{
912	squashfs_super_block sBlk;
913	char *dest = "squashfs-root";
914	int i, version = FALSE;
915	char *target = "";
916
917	for(i = 1; i < argc; i++) {
918		if(*argv[i] != '-')
919			break;
920		if(strcmp(argv[i], "-version") == 0) {
921			VERSION();
922			version = TRUE;
923		} else if(strcmp(argv[i], "-info") == 0)
924			info = TRUE;
925		else if(strcmp(argv[i], "-ls") == 0)
926			lsonly = TRUE;
927		else if(strcmp(argv[i], "-dest") == 0) {
928			if(++i == argc)
929				goto options;
930			dest = argv[i];
931		}
932	}
933
934	if(i == argc) {
935		if(!version) {
936options:
937			ERROR("SYNTAX: %s [-ls | -dest] filesystem [directory or filename to be extracted]\n", argv[0]);
938			ERROR("\t-version\t\tprint version, licence and copyright information\n");
939			ERROR("\t-info\t\t\tprint files as they are unsquashed\n");
940			ERROR("\t-ls\t\t\tlist filesystem only\n");
941			ERROR("\t-dest <pathname>\tunsquash to <pathname>, default \"squashfs-root\"\n");
942		}
943		exit(1);
944	}
945
946	if((i + 1) < argc)
947		target = argv[i + 1];
948
949	if((fd = open(argv[i], O_RDONLY)) == -1) {
950		ERROR("Could not open %s, because %s\n", argv[i], strerror(errno));
951		exit(1);
952	}
953
954	if(read_super(&sBlk, argv[i]) == FALSE)
955		exit(1);
956
957	block_size = sBlk.block_size;
958	if((fragment_data = malloc(block_size)) == NULL)
959		EXIT_UNSQUASH("failed to allocate fragment_data\n");
960
961	if((file_data = malloc(block_size)) == NULL)
962		EXIT_UNSQUASH("failed to allocate file_data");
963
964	if((data = malloc(block_size)) == NULL)
965		EXIT_UNSQUASH("failed to allocate datan\n");
966
967	if((created_inode = malloc(sBlk.inodes * sizeof(char *))) == NULL)
968		EXIT_UNSQUASH("failed to allocate created_inode\n");
969
970	memset(created_inode, 0, sBlk.inodes * sizeof(char *));
971
972	read_uids_guids(&sBlk);
973	read_fragment_table(&sBlk);
974	uncompress_inode_table(sBlk.inode_table_start, sBlk.directory_table_start, &sBlk);
975	uncompress_directory_table(sBlk.directory_table_start, sBlk.fragment_table_start, &sBlk);
976
977	dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.root_inode), SQUASHFS_INODE_OFFSET(sBlk.root_inode), &sBlk, target);
978
979	if(!lsonly) {
980		printf("\n");
981		printf("created %d files\n", file_count);
982		printf("created %d directories\n", dir_count);
983		printf("created %d symlinks\n", sym_count);
984		printf("created %d devices\n", dev_count);
985		printf("created %d fifos\n", fifo_count);
986	}
987
988}
989