• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Read a squashfs filesystem.  This is a highly compressed read only
3  * filesystem.
4  *
5  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
6  * 2012, 2013, 2014
7  * Phillip Lougher <phillip@squashfs.org.uk>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2,
12  * or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * read_fs.c
24  */
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 <sys/mman.h>
35 #include <limits.h>
36 #include <dirent.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 <stdlib.h>
47 
48 #include "squashfs_fs.h"
49 #include "squashfs_swap.h"
50 #include "compressor.h"
51 #include "xattr.h"
52 #include "error.h"
53 #include "mksquashfs.h"
54 
read_block(int fd,long long start,long long * next,int expected,void * block)55 int read_block(int fd, long long start, long long *next, int expected,
56 								void *block)
57 {
58 	unsigned short c_byte;
59 	int res, compressed;
60 	int outlen = expected ? expected : SQUASHFS_METADATA_SIZE;
61 
62 	/* Read block size */
63 	res = read_fs_bytes(fd, start, 2, &c_byte);
64 	if(res == 0)
65 		return 0;
66 
67 	SQUASHFS_INSWAP_SHORTS(&c_byte, 1);
68 	compressed = SQUASHFS_COMPRESSED(c_byte);
69 	c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
70 
71 	/*
72 	 * The block size should not be larger than
73 	 * the uncompressed size (or max uncompressed size if
74 	 * expected is 0)
75 	 */
76 	if (c_byte > outlen)
77 		return 0;
78 
79 	if(compressed) {
80 		char buffer[c_byte];
81 		int error;
82 
83 		res = read_fs_bytes(fd, start + 2, c_byte, buffer);
84 		if(res == 0)
85 			return 0;
86 
87 		res = compressor_uncompress(comp, block, buffer, c_byte,
88 			outlen, &error);
89 		if(res == -1) {
90 			ERROR("%s uncompress failed with error code %d\n",
91 				comp->name, error);
92 			return 0;
93 		}
94 	} else {
95 		res = read_fs_bytes(fd, start + 2, c_byte, block);
96 		if(res == 0)
97 			return 0;
98 		res = c_byte;
99 	}
100 
101 	if(next)
102 		*next = start + 2 + c_byte;
103 
104 	/*
105 	 * if expected, then check the (uncompressed) return data
106 	 * is of the expected size
107 	 */
108 	if(expected && expected != res)
109 		return 0;
110 	else
111 		return res;
112 }
113 
114 
115 #define NO_BYTES(SIZE) \
116 	(bytes - (cur_ptr - *inode_table) < (SIZE))
117 
118 #define NO_INODE_BYTES(INODE) NO_BYTES(sizeof(struct INODE))
119 
scan_inode_table(int fd,long long start,long long end,long long root_inode_start,int root_inode_offset,struct squashfs_super_block * sBlk,union squashfs_inode_header * dir_inode,unsigned char ** inode_table,unsigned int * root_inode_block,unsigned int * root_inode_size,long long * uncompressed_file,unsigned int * uncompressed_directory,int * file_count,int * sym_count,int * dev_count,int * dir_count,int * fifo_count,int * sock_count,unsigned int * id_table)120 int scan_inode_table(int fd, long long start, long long end,
121 	long long root_inode_start, int root_inode_offset,
122 	struct squashfs_super_block *sBlk, union squashfs_inode_header
123 	*dir_inode, unsigned char **inode_table, unsigned int *root_inode_block,
124 	unsigned int *root_inode_size, long long *uncompressed_file,
125 	unsigned int *uncompressed_directory, int *file_count, int *sym_count,
126 	int *dev_count, int *dir_count, int *fifo_count, int *sock_count,
127 	unsigned int *id_table)
128 {
129 	unsigned char *cur_ptr;
130 	int byte, files = 0;
131 	unsigned int directory_start_block, bytes = 0, size = 0;
132 	struct squashfs_base_inode_header base;
133 
134 	TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start "
135 		"0x%llx\n", start, end, root_inode_start);
136 
137 	*root_inode_block = UINT_MAX;
138 	while(start < end) {
139 		if(start == root_inode_start) {
140 			TRACE("scan_inode_table: read compressed block 0x%llx "
141 				"containing root inode\n", start);
142 			*root_inode_block = bytes;
143 		}
144 		if(size - bytes < SQUASHFS_METADATA_SIZE) {
145 			*inode_table = realloc(*inode_table, size
146 				+= SQUASHFS_METADATA_SIZE);
147 			if(*inode_table == NULL)
148 				MEM_ERROR();
149 		}
150 		TRACE("scan_inode_table: reading block 0x%llx\n", start);
151 		byte = read_block(fd, start, &start, 0, *inode_table + bytes);
152 		if(byte == 0)
153 			goto corrupted;
154 
155 		bytes += byte;
156 
157 		/* If this is not the last metadata block in the inode table
158 		 * then it should be SQUASHFS_METADATA_SIZE in size.
159 		 * Note, we can't use expected in read_block() above for this
160 		 * because we don't know if this is the last block until
161 		 * after reading.
162 		 */
163 		if(start != end && byte != SQUASHFS_METADATA_SIZE)
164 			goto corrupted;
165 	}
166 
167 	/*
168 	 * We expect to have found the metadata block containing the
169 	 * root inode in the above inode_table metadata block scan.  If it
170 	 * hasn't been found then the filesystem is corrupted
171 	 */
172 	if(*root_inode_block == UINT_MAX)
173 		goto corrupted;
174 
175 	/*
176 	 * The number of bytes available after the root inode medata block
177 	 * should be at least the root inode offset + the size of a
178 	 * regular directory inode, if not the filesystem is corrupted
179 	 *
180 	 *	+-----------------------+-----------------------+
181 	 *	| 			|        directory	|
182 	 *	|			|          inode	|
183 	 *	+-----------------------+-----------------------+
184 	 *	^			^			^
185 	 *	*root_inode_block	root_inode_offset	bytes
186 	 */
187 	if((bytes - *root_inode_block) < (root_inode_offset +
188 			sizeof(struct squashfs_dir_inode_header)))
189 		goto corrupted;
190 
191 	/*
192 	 * Read last inode entry which is the root directory inode, and obtain
193 	 * the last directory start block index.  This is used when calculating
194 	 * the total uncompressed directory size.  The directory bytes in the
195 	 * last * block will be counted as normal.
196 	 *
197 	 * Note, the previous check ensures the following calculation won't
198 	 * underflow, and we won't access beyond the buffer
199 	 */
200 	*root_inode_size = bytes - (*root_inode_block + root_inode_offset);
201 	bytes = *root_inode_block + root_inode_offset;
202 	SQUASHFS_SWAP_DIR_INODE_HEADER(*inode_table + bytes, &dir_inode->dir);
203 
204 	if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE)
205 		directory_start_block = dir_inode->dir.start_block;
206 	else if(dir_inode->base.inode_type == SQUASHFS_LDIR_TYPE) {
207 		if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header))
208 			/* corrupted filesystem */
209 			goto corrupted;
210 		SQUASHFS_SWAP_LDIR_INODE_HEADER(*inode_table + bytes,
211 			&dir_inode->ldir);
212 		directory_start_block = dir_inode->ldir.start_block;
213 	} else
214 		/* bad type, corrupted filesystem */
215 		goto corrupted;
216 
217 	get_uid(id_table[dir_inode->base.uid]);
218 	get_guid(id_table[dir_inode->base.guid]);
219 
220 	/* allocate fragment to file mapping table */
221 	file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *));
222 	if(file_mapping == NULL)
223 		MEM_ERROR();
224 
225 	for(cur_ptr = *inode_table; cur_ptr < *inode_table + bytes; files ++) {
226 		if(NO_INODE_BYTES(squashfs_base_inode_header))
227 			/* corrupted filesystem */
228 			goto corrupted;
229 
230 		SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base);
231 
232 		TRACE("scan_inode_table: processing inode @ byte position "
233 			"0x%x, type 0x%x\n",
234 			(unsigned int) (cur_ptr - *inode_table),
235 			base.inode_type);
236 
237 		get_uid(id_table[base.uid]);
238 		get_guid(id_table[base.guid]);
239 
240 		switch(base.inode_type) {
241 		case SQUASHFS_FILE_TYPE: {
242 			struct squashfs_reg_inode_header inode;
243 			int frag_bytes, blocks, i;
244 			long long start, file_bytes = 0;
245 			unsigned int *block_list;
246 
247 			if(NO_INODE_BYTES(squashfs_reg_inode_header))
248 				/* corrupted filesystem */
249 				goto corrupted;
250 
251 			SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode);
252 
253 			frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
254 				0 : inode.file_size % sBlk->block_size;
255 			blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
256 				(inode.file_size + sBlk->block_size - 1) >>
257 				sBlk->block_log : inode.file_size >>
258 				sBlk->block_log;
259 			start = inode.start_block;
260 
261 			TRACE("scan_inode_table: regular file, file_size %d, "
262 				"blocks %d\n", inode.file_size, blocks);
263 
264 			if(NO_BYTES(blocks * sizeof(unsigned int)))
265 				/* corrupted filesystem */
266 				goto corrupted;
267 
268 			block_list = malloc(blocks * sizeof(unsigned int));
269 			if(block_list == NULL)
270 				MEM_ERROR();
271 
272 			cur_ptr += sizeof(inode);
273 			SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
274 
275 			*uncompressed_file += inode.file_size;
276 			(*file_count) ++;
277 
278 			for(i = 0; i < blocks; i++)
279 				file_bytes +=
280 					SQUASHFS_COMPRESSED_SIZE_BLOCK
281 								(block_list[i]);
282 
283 			if(inode.fragment != SQUASHFS_INVALID_FRAG &&
284 					inode.fragment >= sBlk->fragments) {
285 				free(block_list);
286 				goto corrupted;
287 			}
288 
289 			add_file(start, inode.file_size, file_bytes,
290 				block_list, blocks, inode.fragment,
291 				inode.offset, frag_bytes);
292 
293 			cur_ptr += blocks * sizeof(unsigned int);
294 			break;
295 		}
296 		case SQUASHFS_LREG_TYPE: {
297 			struct squashfs_lreg_inode_header inode;
298 			int frag_bytes, blocks, i;
299 			long long start, file_bytes = 0;
300 			unsigned int *block_list;
301 
302 			if(NO_INODE_BYTES(squashfs_lreg_inode_header))
303 				/* corrupted filesystem */
304 				goto corrupted;
305 
306 			SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode);
307 
308 			frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
309 				0 : inode.file_size % sBlk->block_size;
310 			blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
311 				(inode.file_size + sBlk->block_size - 1) >>
312 				sBlk->block_log : inode.file_size >>
313 				sBlk->block_log;
314 			start = inode.start_block;
315 
316 			TRACE("scan_inode_table: extended regular "
317 				"file, file_size %lld, blocks %d\n",
318 				inode.file_size, blocks);
319 
320 			if(NO_BYTES(blocks * sizeof(unsigned int)))
321 				/* corrupted filesystem */
322 				goto corrupted;
323 
324 			block_list = malloc(blocks * sizeof(unsigned int));
325 			if(block_list == NULL)
326 				MEM_ERROR();
327 
328 			cur_ptr += sizeof(inode);
329 			SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
330 
331 			*uncompressed_file += inode.file_size;
332 			(*file_count) ++;
333 
334 			for(i = 0; i < blocks; i++)
335 				file_bytes +=
336 					SQUASHFS_COMPRESSED_SIZE_BLOCK
337 								(block_list[i]);
338 
339 			if(inode.fragment != SQUASHFS_INVALID_FRAG &&
340 					inode.fragment >= sBlk->fragments) {
341 				free(block_list);
342 				goto corrupted;
343 			}
344 
345 			add_file(start, inode.file_size, file_bytes,
346 				block_list, blocks, inode.fragment,
347 				inode.offset, frag_bytes);
348 
349 			cur_ptr += blocks * sizeof(unsigned int);
350 			break;
351 		}
352 		case SQUASHFS_SYMLINK_TYPE:
353 		case SQUASHFS_LSYMLINK_TYPE: {
354 			struct squashfs_symlink_inode_header inode;
355 
356 			if(NO_INODE_BYTES(squashfs_symlink_inode_header))
357 				/* corrupted filesystem */
358 				goto corrupted;
359 
360 			SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode);
361 
362 			(*sym_count) ++;
363 
364 			if (inode.inode_type == SQUASHFS_LSYMLINK_TYPE) {
365 				if(NO_BYTES(inode.symlink_size +
366 							sizeof(unsigned int)))
367 					/* corrupted filesystem */
368 					goto corrupted;
369 				cur_ptr += sizeof(inode) + inode.symlink_size +
370 							sizeof(unsigned int);
371 			} else {
372 				if(NO_BYTES(inode.symlink_size))
373 					/* corrupted filesystem */
374 					goto corrupted;
375 				cur_ptr += sizeof(inode) + inode.symlink_size;
376 			}
377 			break;
378 		}
379 		case SQUASHFS_DIR_TYPE: {
380 			struct squashfs_dir_inode_header dir_inode;
381 
382 			if(NO_INODE_BYTES(squashfs_dir_inode_header))
383 				/* corrupted filesystem */
384 				goto corrupted;
385 
386 			SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode);
387 
388 			if(dir_inode.start_block < directory_start_block)
389 				*uncompressed_directory += dir_inode.file_size;
390 
391 			(*dir_count) ++;
392 			cur_ptr += sizeof(struct squashfs_dir_inode_header);
393 			break;
394 		}
395 		case SQUASHFS_LDIR_TYPE: {
396 			struct squashfs_ldir_inode_header dir_inode;
397 			int i;
398 
399 			if(NO_INODE_BYTES(squashfs_ldir_inode_header))
400 				/* corrupted filesystem */
401 				goto corrupted;
402 
403 			SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode);
404 
405 			if(dir_inode.start_block < directory_start_block)
406 				*uncompressed_directory += dir_inode.file_size;
407 
408 			(*dir_count) ++;
409 			cur_ptr += sizeof(struct squashfs_ldir_inode_header);
410 
411 			for(i = 0; i < dir_inode.i_count; i++) {
412 				struct squashfs_dir_index index;
413 
414 				if(NO_BYTES(sizeof(index)))
415 					/* corrupted filesystem */
416 					goto corrupted;
417 
418 				SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index);
419 
420 				if(NO_BYTES(index.size + 1))
421 					/* corrupted filesystem */
422 					goto corrupted;
423 
424 				cur_ptr += sizeof(index) + index.size + 1;
425 			}
426 			break;
427 		}
428 	 	case SQUASHFS_BLKDEV_TYPE:
429 	 	case SQUASHFS_CHRDEV_TYPE:
430 			if(NO_INODE_BYTES(squashfs_dev_inode_header))
431 				/* corrupted filesystem */
432 				goto corrupted;
433 
434 			(*dev_count) ++;
435 			cur_ptr += sizeof(struct squashfs_dev_inode_header);
436 			break;
437 	 	case SQUASHFS_LBLKDEV_TYPE:
438 	 	case SQUASHFS_LCHRDEV_TYPE:
439 			if(NO_INODE_BYTES(squashfs_ldev_inode_header))
440 				/* corrupted filesystem */
441 				goto corrupted;
442 
443 			(*dev_count) ++;
444 			cur_ptr += sizeof(struct squashfs_ldev_inode_header);
445 			break;
446 		case SQUASHFS_FIFO_TYPE:
447 			if(NO_INODE_BYTES(squashfs_ipc_inode_header))
448 				/* corrupted filesystem */
449 				goto corrupted;
450 
451 			(*fifo_count) ++;
452 			cur_ptr += sizeof(struct squashfs_ipc_inode_header);
453 			break;
454 		case SQUASHFS_LFIFO_TYPE:
455 			if(NO_INODE_BYTES(squashfs_lipc_inode_header))
456 				/* corrupted filesystem */
457 				goto corrupted;
458 
459 			(*fifo_count) ++;
460 			cur_ptr += sizeof(struct squashfs_lipc_inode_header);
461 			break;
462 		case SQUASHFS_SOCKET_TYPE:
463 			if(NO_INODE_BYTES(squashfs_ipc_inode_header))
464 				/* corrupted filesystem */
465 				goto corrupted;
466 
467 			(*sock_count) ++;
468 			cur_ptr += sizeof(struct squashfs_ipc_inode_header);
469 			break;
470 		case SQUASHFS_LSOCKET_TYPE:
471 			if(NO_INODE_BYTES(squashfs_lipc_inode_header))
472 				/* corrupted filesystem */
473 				goto corrupted;
474 
475 			(*sock_count) ++;
476 			cur_ptr += sizeof(struct squashfs_lipc_inode_header);
477 			break;
478 	 	default:
479 			ERROR("Unknown inode type %d in scan_inode_table!\n",
480 					base.inode_type);
481 			goto corrupted;
482 		}
483 	}
484 
485 	printf("Read existing filesystem, %d inodes scanned\n", files);
486 	return TRUE;
487 
488 corrupted:
489 	ERROR("scan_inode_table: filesystem corruption detected in "
490 		"scanning metadata\n");
491 	free(*inode_table);
492 	return FALSE;
493 }
494 
495 
read_super(int fd,struct squashfs_super_block * sBlk,char * source)496 struct compressor *read_super(int fd, struct squashfs_super_block *sBlk, char *source)
497 {
498 	int res, bytes = 0;
499 	char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned));
500 
501 	res = read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
502 		sBlk);
503 	if(res == 0) {
504 		ERROR("Can't find a SQUASHFS superblock on %s\n",
505 				source);
506 		ERROR("Wrong filesystem or filesystem is corrupted!\n");
507 		goto failed_mount;
508 	}
509 
510 	SQUASHFS_INSWAP_SUPER_BLOCK(sBlk);
511 
512 	if(sBlk->s_magic != SQUASHFS_MAGIC) {
513 		if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP)
514 			ERROR("Pre 4.0 big-endian filesystem on %s, appending"
515 				" to this is unsupported\n", source);
516 		else {
517 			ERROR("Can't find a SQUASHFS superblock on %s\n",
518 				source);
519 			ERROR("Wrong filesystem or filesystem is corrupted!\n");
520 		}
521 		goto failed_mount;
522 	}
523 
524 	/* Check the MAJOR & MINOR versions */
525 	if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
526 		if(sBlk->s_major < 4)
527 			ERROR("Filesystem on %s is a SQUASHFS %d.%d filesystem."
528 				"  Appending\nto SQUASHFS %d.%d filesystems is "
529 				"not supported.  Please convert it to a "
530 				"SQUASHFS 4 filesystem\n", source,
531 				sBlk->s_major,
532 				sBlk->s_minor, sBlk->s_major, sBlk->s_minor);
533 		else
534 			ERROR("Filesystem on %s is %d.%d, which is a later "
535 				"filesystem version than I support\n",
536 				source, sBlk->s_major, sBlk->s_minor);
537 		goto failed_mount;
538 	}
539 
540 	/* Check the compression type */
541 	comp = lookup_compressor_id(sBlk->compression);
542 	if(!comp->supported) {
543 		ERROR("Filesystem on %s uses %s compression, this is "
544 			"unsupported by this version\n", source, comp->name);
545 		ERROR("Compressors available:\n");
546 		display_compressors("", "");
547 		goto failed_mount;
548 	}
549 
550 	/*
551 	 * Read extended superblock information from disk.
552 	 *
553 	 * Read compressor specific options from disk if present, and pass
554 	 * to compressor to set compressor options.
555 	 *
556 	 * Note, if there's no compressor options present, the compressor
557 	 * is still called to set the default options (the defaults may have
558 	 * been changed by the user specifying options on the command
559 	 * line which need to be over-ridden).
560 	 *
561 	 * Compressor_extract_options is also used to ensure that
562 	 * we know how decompress a filesystem compressed with these
563 	 * compression options.
564 	 */
565 	if(SQUASHFS_COMP_OPTS(sBlk->flags)) {
566 		bytes = read_block(fd, sizeof(*sBlk), NULL, 0, buffer);
567 
568 		if(bytes == 0) {
569 			ERROR("Failed to read compressor options from append "
570 				"filesystem\n");
571 			ERROR("Filesystem corrupted?\n");
572 			goto failed_mount;
573 		}
574 	}
575 
576 	res = compressor_extract_options(comp, sBlk->block_size, buffer, bytes);
577 	if(res == -1) {
578 		ERROR("Compressor failed to set compressor options\n");
579 		goto failed_mount;
580 	}
581 
582 	printf("Found a valid %sSQUASHFS superblock on %s.\n",
583 		SQUASHFS_EXPORTABLE(sBlk->flags) ? "exportable " : "", source);
584 	printf("\tCompression used %s\n", comp->name);
585 	printf("\tInodes are %scompressed\n",
586 		SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
587 	printf("\tData is %scompressed\n",
588 		SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
589 	printf("\tFragments are %scompressed\n",
590 		SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
591 	printf("\tXattrs are %scompressed\n",
592 		SQUASHFS_UNCOMPRESSED_XATTRS(sBlk->flags) ? "un" : "");
593 	printf("\tFragments are %spresent in the filesystem\n",
594 		SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not " : "");
595 	printf("\tAlways-use-fragments option is %sspecified\n",
596 		SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not ");
597 	printf("\tDuplicates are %sremoved\n",
598 		SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not ");
599 	printf("\tXattrs are %sstored\n",
600 		SQUASHFS_NO_XATTRS(sBlk->flags) ? "not " : "");
601 	printf("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n",
602 		sBlk->bytes_used / 1024.0, sBlk->bytes_used
603 		/ (1024.0 * 1024.0));
604 	printf("\tBlock size %d\n", sBlk->block_size);
605 	printf("\tNumber of fragments %d\n", sBlk->fragments);
606 	printf("\tNumber of inodes %d\n", sBlk->inodes);
607 	printf("\tNumber of ids %d\n", sBlk->no_ids);
608 	TRACE("sBlk->inode_table_start %llx\n", sBlk->inode_table_start);
609 	TRACE("sBlk->directory_table_start %llx\n",
610 		sBlk->directory_table_start);
611 	TRACE("sBlk->id_table_start %llx\n", sBlk->id_table_start);
612 	TRACE("sBlk->fragment_table_start %llx\n", sBlk->fragment_table_start);
613 	TRACE("sBlk->lookup_table_start %llx\n", sBlk->lookup_table_start);
614 	TRACE("sBlk->xattr_id_table_start %llx\n", sBlk->xattr_id_table_start);
615 	printf("\n");
616 
617 	return comp;
618 
619 failed_mount:
620 	return NULL;
621 }
622 
623 
squashfs_readdir(int fd,int root_entries,unsigned int directory_start_block,int offset,int size,unsigned int * last_directory_block,struct squashfs_super_block * sBlk,void (push_directory_entry)(char *,squashfs_inode,int,int))624 unsigned char *squashfs_readdir(int fd, int root_entries,
625 	unsigned int directory_start_block, int offset, int size,
626 	unsigned int *last_directory_block, struct squashfs_super_block *sBlk,
627 	void (push_directory_entry)(char *, squashfs_inode, int, int))
628 {
629 	struct squashfs_dir_header dirh;
630 	char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
631 		__attribute__ ((aligned));
632 	struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
633 	unsigned char *directory_table = NULL;
634 	int byte, bytes = 0, dir_count;
635 	long long start = sBlk->directory_table_start + directory_start_block,
636 		last_start_block = start;
637 
638 	size += offset;
639 	directory_table = malloc((size + SQUASHFS_METADATA_SIZE * 2 - 1) &
640 		~(SQUASHFS_METADATA_SIZE - 1));
641 	if(directory_table == NULL)
642 		MEM_ERROR();
643 
644 	while(bytes < size) {
645 		int expected = (size - bytes) >= SQUASHFS_METADATA_SIZE ?
646 			SQUASHFS_METADATA_SIZE : 0;
647 
648 		TRACE("squashfs_readdir: reading block 0x%llx, bytes read so "
649 			"far %d\n", start, bytes);
650 
651 		last_start_block = start;
652 		byte = read_block(fd, start, &start, expected, directory_table + bytes);
653 		if(byte == 0) {
654 			ERROR("Failed to read directory\n");
655 			ERROR("Filesystem corrupted?\n");
656 			free(directory_table);
657 			return NULL;
658 		}
659 		bytes += byte;
660 	}
661 
662 	if(!root_entries)
663 		goto all_done;
664 
665 	bytes = offset;
666  	while(bytes < size) {
667 		SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
668 
669 		dir_count = dirh.count + 1;
670 		TRACE("squashfs_readdir: Read directory header @ byte position "
671 			"0x%x, 0x%x directory entries\n", bytes, dir_count);
672 		bytes += sizeof(dirh);
673 
674 		while(dir_count--) {
675 			SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
676 			bytes += sizeof(*dire);
677 
678 			memcpy(dire->name, directory_table + bytes,
679 				dire->size + 1);
680 			dire->name[dire->size + 1] = '\0';
681 			TRACE("squashfs_readdir: pushing directory entry %s, "
682 				"inode %x:%x, type 0x%x\n", dire->name,
683 				dirh.start_block, dire->offset, dire->type);
684 			push_directory_entry(dire->name,
685 				SQUASHFS_MKINODE(dirh.start_block,
686 				dire->offset), dirh.inode_number +
687 				dire->inode_number, dire->type);
688 			bytes += dire->size + 1;
689 		}
690 	}
691 
692 all_done:
693 	*last_directory_block = (unsigned int) last_start_block -
694 		sBlk->directory_table_start;
695 	return directory_table;
696 }
697 
698 
read_id_table(int fd,struct squashfs_super_block * sBlk)699 unsigned int *read_id_table(int fd, struct squashfs_super_block *sBlk)
700 {
701 	int indexes = SQUASHFS_ID_BLOCKS(sBlk->no_ids);
702 	long long index[indexes];
703 	int bytes = SQUASHFS_ID_BYTES(sBlk->no_ids);
704 	unsigned int *id_table;
705 	int res, i;
706 
707 	id_table = malloc(bytes);
708 	if(id_table == NULL)
709 		MEM_ERROR();
710 
711 	res = read_fs_bytes(fd, sBlk->id_table_start,
712 		SQUASHFS_ID_BLOCK_BYTES(sBlk->no_ids), index);
713 	if(res == 0) {
714 		ERROR("Failed to read id table index\n");
715 		ERROR("Filesystem corrupted?\n");
716 		free(id_table);
717 		return NULL;
718 	}
719 
720 	SQUASHFS_INSWAP_ID_BLOCKS(index, indexes);
721 
722 	for(i = 0; i < indexes; i++) {
723 		int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
724 					bytes & (SQUASHFS_METADATA_SIZE - 1);
725 		int length = read_block(fd, index[i], NULL, expected,
726 			((unsigned char *) id_table) +
727 			(i * SQUASHFS_METADATA_SIZE));
728 		TRACE("Read id table block %d, from 0x%llx, length %d\n", i,
729 			index[i], length);
730 		if(length == 0) {
731 			ERROR("Failed to read id table block %d, from 0x%llx, "
732 				"length %d\n", i, index[i], length);
733 			ERROR("Filesystem corrupted?\n");
734 			free(id_table);
735 			return NULL;
736 		}
737 	}
738 
739 	SQUASHFS_INSWAP_INTS(id_table, sBlk->no_ids);
740 
741 	for(i = 0; i < sBlk->no_ids; i++) {
742 		TRACE("Adding id %d to id tables\n", id_table[i]);
743 		create_id(id_table[i]);
744 	}
745 
746 	return id_table;
747 }
748 
749 
read_fragment_table(int fd,struct squashfs_super_block * sBlk,struct squashfs_fragment_entry ** fragment_table)750 int read_fragment_table(int fd, struct squashfs_super_block *sBlk,
751 	struct squashfs_fragment_entry **fragment_table)
752 {
753 	int res, i;
754 	int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk->fragments);
755 	int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
756 	long long fragment_table_index[indexes];
757 
758 	TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
759 		"from 0x%llx\n", sBlk->fragments, indexes,
760 		sBlk->fragment_table_start);
761 
762 	if(sBlk->fragments == 0)
763 		return 1;
764 
765 	*fragment_table = malloc(bytes);
766 	if(*fragment_table == NULL)
767 		MEM_ERROR();
768 
769 	res = read_fs_bytes(fd, sBlk->fragment_table_start,
770 		SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments),
771 		fragment_table_index);
772 	if(res == 0) {
773 		ERROR("Failed to read fragment table index\n");
774 		ERROR("Filesystem corrupted?\n");
775 		free(*fragment_table);
776 		return 0;
777 	}
778 
779 	SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
780 
781 	for(i = 0; i < indexes; i++) {
782 		int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
783 					bytes & (SQUASHFS_METADATA_SIZE - 1);
784 		int length = read_block(fd, fragment_table_index[i], NULL,
785 			expected, ((unsigned char *) *fragment_table) +
786 			(i * SQUASHFS_METADATA_SIZE));
787 		TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
788 			i, fragment_table_index[i], length);
789 		if(length == 0) {
790 			ERROR("Failed to read fragment table block %d, from "
791 				"0x%llx, length %d\n", i,
792 				fragment_table_index[i], length);
793 			ERROR("Filesystem corrupted?\n");
794 			free(*fragment_table);
795 			return 0;
796 		}
797 	}
798 
799 	for(i = 0; i < sBlk->fragments; i++)
800 		SQUASHFS_INSWAP_FRAGMENT_ENTRY(&(*fragment_table)[i]);
801 
802 	return 1;
803 }
804 
805 
read_inode_lookup_table(int fd,struct squashfs_super_block * sBlk,squashfs_inode ** inode_lookup_table)806 int read_inode_lookup_table(int fd, struct squashfs_super_block *sBlk,
807 	squashfs_inode **inode_lookup_table)
808 {
809 	int lookup_bytes = SQUASHFS_LOOKUP_BYTES(sBlk->inodes);
810 	int indexes = SQUASHFS_LOOKUP_BLOCKS(sBlk->inodes);
811 	long long index[indexes];
812 	int res, i;
813 
814 	if(sBlk->lookup_table_start == SQUASHFS_INVALID_BLK)
815 		return 1;
816 
817 	*inode_lookup_table = malloc(lookup_bytes);
818 	if(*inode_lookup_table == NULL)
819 		MEM_ERROR();
820 
821 	res = read_fs_bytes(fd, sBlk->lookup_table_start,
822 		SQUASHFS_LOOKUP_BLOCK_BYTES(sBlk->inodes), index);
823 	if(res == 0) {
824 		ERROR("Failed to read inode lookup table index\n");
825 		ERROR("Filesystem corrupted?\n");
826 		free(*inode_lookup_table);
827 		return 0;
828 	}
829 
830 	SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
831 
832 	for(i = 0; i <  indexes; i++) {
833 		int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
834 				lookup_bytes & (SQUASHFS_METADATA_SIZE - 1);
835 		int length = read_block(fd, index[i], NULL, expected,
836 			((unsigned char *) *inode_lookup_table) +
837 			(i * SQUASHFS_METADATA_SIZE));
838 		TRACE("Read inode lookup table block %d, from 0x%llx, length "
839 			"%d\n", i, index[i], length);
840 		if(length == 0) {
841 			ERROR("Failed to read inode lookup table block %d, "
842 				"from 0x%llx, length %d\n", i, index[i],
843 				length);
844 			ERROR("Filesystem corrupted?\n");
845 			free(*inode_lookup_table);
846 			return 0;
847 		}
848 	}
849 
850 	SQUASHFS_INSWAP_LONG_LONGS(*inode_lookup_table, sBlk->inodes);
851 
852 	return 1;
853 }
854 
855 
read_filesystem(char * root_name,int fd,struct squashfs_super_block * sBlk,char ** cinode_table,char ** data_cache,char ** cdirectory_table,char ** directory_data_cache,unsigned int * last_directory_block,unsigned int * inode_dir_offset,unsigned int * inode_dir_file_size,unsigned int * root_inode_size,unsigned int * inode_dir_start_block,int * file_count,int * sym_count,int * dev_count,int * dir_count,int * fifo_count,int * sock_count,long long * uncompressed_file,unsigned int * uncompressed_inode,unsigned int * uncompressed_directory,unsigned int * inode_dir_inode_number,unsigned int * inode_dir_parent_inode,void (push_directory_entry)(char *,squashfs_inode,int,int),struct squashfs_fragment_entry ** fragment_table,squashfs_inode ** inode_lookup_table)856 long long read_filesystem(char *root_name, int fd, struct squashfs_super_block *sBlk,
857 	char **cinode_table, char **data_cache, char **cdirectory_table,
858 	char **directory_data_cache, unsigned int *last_directory_block,
859 	unsigned int *inode_dir_offset, unsigned int *inode_dir_file_size,
860 	unsigned int *root_inode_size, unsigned int *inode_dir_start_block,
861 	int *file_count, int *sym_count, int *dev_count, int *dir_count,
862 	int *fifo_count, int *sock_count, long long *uncompressed_file,
863 	unsigned int *uncompressed_inode, unsigned int *uncompressed_directory,
864 	unsigned int *inode_dir_inode_number,
865 	unsigned int *inode_dir_parent_inode,
866 	void (push_directory_entry)(char *, squashfs_inode, int, int),
867 	struct squashfs_fragment_entry **fragment_table,
868 	squashfs_inode **inode_lookup_table)
869 {
870 	unsigned char *inode_table = NULL, *directory_table = NULL;
871 	long long start = sBlk->inode_table_start;
872 	long long end = sBlk->directory_table_start;
873 	long long root_inode_start = start +
874 		SQUASHFS_INODE_BLK(sBlk->root_inode);
875 	unsigned int root_inode_offset =
876 		SQUASHFS_INODE_OFFSET(sBlk->root_inode);
877 	unsigned int root_inode_block;
878 	union squashfs_inode_header inode;
879 	unsigned int *id_table = NULL;
880 	int res;
881 
882 	printf("Scanning existing filesystem...\n");
883 
884 	if(get_xattrs(fd, sBlk) == 0)
885 		goto error;
886 
887 	if(read_fragment_table(fd, sBlk, fragment_table) == 0)
888 		goto error;
889 
890 	if(read_inode_lookup_table(fd, sBlk, inode_lookup_table) == 0)
891 		goto error;
892 
893 	id_table = read_id_table(fd, sBlk);
894 	if(id_table == NULL)
895 		goto error;
896 
897 	res = scan_inode_table(fd, start, end, root_inode_start,
898 		root_inode_offset, sBlk, &inode, &inode_table,
899 		&root_inode_block, root_inode_size, uncompressed_file,
900 		uncompressed_directory, file_count, sym_count, dev_count,
901 		dir_count, fifo_count, sock_count, id_table);
902 	if(res == 0)
903 		goto error;
904 
905 	*uncompressed_inode = root_inode_block;
906 
907 	if(inode.base.inode_type == SQUASHFS_DIR_TYPE ||
908 			inode.base.inode_type == SQUASHFS_LDIR_TYPE) {
909 		if(inode.base.inode_type == SQUASHFS_DIR_TYPE) {
910 			*inode_dir_start_block = inode.dir.start_block;
911 			*inode_dir_offset = inode.dir.offset;
912 			*inode_dir_file_size = inode.dir.file_size - 3;
913 			*inode_dir_inode_number = inode.dir.inode_number;
914 			*inode_dir_parent_inode = inode.dir.parent_inode;
915 		} else {
916 			*inode_dir_start_block = inode.ldir.start_block;
917 			*inode_dir_offset = inode.ldir.offset;
918 			*inode_dir_file_size = inode.ldir.file_size - 3;
919 			*inode_dir_inode_number = inode.ldir.inode_number;
920 			*inode_dir_parent_inode = inode.ldir.parent_inode;
921 		}
922 
923 		directory_table = squashfs_readdir(fd, !root_name,
924 			*inode_dir_start_block, *inode_dir_offset,
925 			*inode_dir_file_size, last_directory_block, sBlk,
926 			push_directory_entry);
927 		if(directory_table == NULL)
928 			goto error;
929 
930 		root_inode_start -= start;
931 		*cinode_table = malloc(root_inode_start);
932 		if(*cinode_table == NULL)
933 			MEM_ERROR();
934 
935 	       	res = read_fs_bytes(fd, start, root_inode_start, *cinode_table);
936 		if(res == 0) {
937 			ERROR("Failed to read inode table\n");
938 			ERROR("Filesystem corrupted?\n");
939 			goto error;
940 		}
941 
942 		*cdirectory_table = malloc(*last_directory_block);
943 		if(*cdirectory_table == NULL)
944 			MEM_ERROR();
945 
946 		res = read_fs_bytes(fd, sBlk->directory_table_start,
947 			*last_directory_block, *cdirectory_table);
948 		if(res == 0) {
949 			ERROR("Failed to read directory table\n");
950 			ERROR("Filesystem corrupted?\n");
951 			goto error;
952 		}
953 
954 		*data_cache = malloc(root_inode_offset + *root_inode_size);
955 		if(*data_cache == NULL)
956 			MEM_ERROR();
957 
958 		memcpy(*data_cache, inode_table + root_inode_block,
959 			root_inode_offset + *root_inode_size);
960 
961 		*directory_data_cache = malloc(*inode_dir_offset +
962 			*inode_dir_file_size);
963 		if(*directory_data_cache == NULL)
964 			MEM_ERROR();
965 
966 		memcpy(*directory_data_cache, directory_table,
967 			*inode_dir_offset + *inode_dir_file_size);
968 
969 		free(id_table);
970 		free(inode_table);
971 		free(directory_table);
972 		return sBlk->inode_table_start;
973 	}
974 
975 error:
976 	free(id_table);
977 	free(inode_table);
978 	free(directory_table);
979 	return 0;
980 }
981