allow coexistance of N build and AC build.
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / scripts / squashfs / unsquashfs.c
blobdae8fd62e72f6c7f401b5d2cbd2dc694c7318906
1 /*
2 * Unsquash a squashfs filesystem. This is a highly compressed read only filesystem.
4 * Copyright (c) 2002, 2003, 2004, 2005, 2006
5 * Phillip Lougher <phillip@lougher.org.uk>
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.
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.
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.
21 * unsquash.c
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>
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
44 #include <squashfs_fs.h>
45 #include "read_fs.h"
46 #include "global.h"
48 #include <stdlib.h>
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
58 #define ERROR(s, args...) do { \
59 fprintf(stderr, s, ## args); \
60 } while(0)
62 #define EXIT_UNSQUASH(s, args...) do { \
63 fprintf(stderr, "FATAL ERROR aborting: "s, ## args); \
64 } while(0)
66 struct hash_table_entry {
67 int start;
68 int bytes;
69 struct hash_table_entry *next;
72 int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, dev_count = 0, fifo_count = 0;
73 char *inode_table = NULL, *directory_table = NULL;
74 struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536];
75 int fd;
76 squashfs_fragment_entry *fragment_table;
77 unsigned int *uid_table, *guid_table;
78 unsigned int cached_frag = SQUASHFS_INVALID_FRAG;
79 char *fragment_data;
80 char *file_data;
81 char *data;
82 unsigned int block_size;
83 int lsonly = FALSE, info = FALSE;
84 char **created_inode;
86 #define CALCULATE_HASH(start) (start & 0xffff)
88 int add_entry(struct hash_table_entry *hash_table[], int start, int bytes)
90 int hash = CALCULATE_HASH(start);
91 struct hash_table_entry *hash_table_entry;
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;
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;
103 return TRUE;
107 int lookup_entry(struct hash_table_entry *hash_table[], int start)
109 int hash = CALCULATE_HASH(start);
110 struct hash_table_entry *hash_table_entry;
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;
116 return -1;
120 int read_bytes(long long byte, int bytes, char *buff)
122 off_t off = byte;
124 TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, bytes);
126 if(lseek(fd, off, SEEK_SET) == -1) {
127 ERROR("Lseek failed because %s\b", strerror(errno));
128 return FALSE;
131 if(read(fd, buff, bytes) == -1) {
132 ERROR("Read on destination failed because %s\n", strerror(errno));
133 return FALSE;
136 return TRUE;
140 int read_block(long long start, long long *next, char *block, squashfs_super_block *sBlk)
142 unsigned short c_byte;
143 int offset = 2;
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;
154 TRACE("read_block: block @0x%llx, %d %s bytes\n", start, SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? "compressed" : "uncompressed");
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;
163 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
164 if(read_bytes(start + offset, c_byte, buffer) == FALSE)
165 goto failed;
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;
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;
188 failed:
189 return FALSE;
193 int read_data_block(long long start, unsigned int size, char *block)
195 int res;
196 unsigned long bytes = block_size;
197 int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
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");
201 if(SQUASHFS_COMPRESSED_BLOCK(size)) {
202 if(read_bytes(start, c_byte, data) == FALSE)
203 return 0;
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;
215 return bytes;
216 } else {
217 if(read_bytes(start, c_byte, block) == FALSE)
218 return 0;
220 return c_byte;
225 void uncompress_inode_table(long long start, long long end, squashfs_super_block *sBlk)
227 int size = 0, bytes = 0, res;
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");
239 bytes += res;
244 int set_attributes(char *pathname, unsigned int mode, unsigned int uid, unsigned int guid,
245 unsigned int mtime, unsigned int set_mode)
247 struct utimbuf times = { (time_t) mtime, (time_t) mtime };
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;
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;
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];
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;
269 return TRUE;
273 void read_uids_guids(squashfs_super_block *sBlk)
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");
278 guid_table = uid_table + sBlk->no_uids;
280 if(read_bytes(sBlk->uid_start, (sBlk->no_uids + sBlk->no_guids) * sizeof(unsigned int), (char *) uid_table) ==
281 FALSE)
282 EXIT_UNSQUASH("read_uids_guids: failed to read uid/gid table\n");
286 void read_fragment_table(squashfs_super_block *sBlk)
288 int i, indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
289 squashfs_fragment_index fragment_table_index[indexes];
291 TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%llx\n", sBlk->fragments, indexes, sBlk->fragment_table_start);
292 if(sBlk->fragments == 0)
293 return;
295 if((fragment_table = (squashfs_fragment_entry *) malloc(sBlk->fragments * sizeof(squashfs_fragment_entry))) == NULL)
296 EXIT_UNSQUASH("read_fragment_table: failed to allocate fragment table\n");
298 if(swap) {
299 squashfs_fragment_index sfragment_table_index[indexes];
301 read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) sfragment_table_index);
302 SQUASHFS_SWAP_FRAGMENT_INDEXES(fragment_table_index, sfragment_table_index, indexes);
303 } else
304 read_bytes(sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) fragment_table_index);
306 for(i = 0; i < indexes; i++) {
307 int length = read_block(fragment_table_index[i], NULL, ((char *) fragment_table) + (i * SQUASHFS_METADATA_SIZE), sBlk);
308 TRACE("Read fragment table block %d, from 0x%llx, length %d\n", i, fragment_table_index[i], length);
311 if(swap) {
312 squashfs_fragment_entry sfragment;
313 for(i = 0; i < sBlk->fragments; i++) {
314 SQUASHFS_SWAP_FRAGMENT_ENTRY((&sfragment), (&fragment_table[i]));
315 memcpy((char *) &fragment_table[i], (char *) &sfragment, sizeof(squashfs_fragment_entry));
321 char *read_fragment(unsigned int fragment)
323 TRACE("read_fragment: reading fragment %d\n", fragment);
325 if(cached_frag == SQUASHFS_INVALID_FRAG || fragment != cached_frag) {
326 squashfs_fragment_entry *fragment_entry = &fragment_table[fragment];
327 if(read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data) == 0) {
328 ERROR("read_fragment: failed to read fragment %d\n", fragment);
329 cached_frag = SQUASHFS_INVALID_FRAG;
330 return NULL;
332 cached_frag = fragment;
335 return fragment_data;
339 int write_file(char *pathname, unsigned int fragment, unsigned int frag_bytes, unsigned int offset,
340 unsigned int blocks, long long start, char *block_ptr, unsigned int mode)
342 unsigned int file_fd, bytes, i;
343 unsigned int *block_list;
345 TRACE("write_file: regular file, blocks %d\n", blocks);
347 if((block_list = malloc(blocks * sizeof(unsigned int))) == NULL) {
348 ERROR("write_file: unable to malloc block list\n");
349 return FALSE;
352 if(swap) {
353 unsigned int sblock_list[blocks];
354 memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int));
355 SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks);
356 } else
357 memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
359 if((file_fd = open(pathname, O_CREAT | O_WRONLY, (mode_t) mode)) == -1) {
360 ERROR("write_file: failed to create file %s, because %s\n", pathname,
361 strerror(errno));
362 free(block_list);
363 return FALSE;
366 for(i = 0; i < blocks; i++) {
367 if((bytes = read_data_block(start, block_list[i], file_data)) == 0) {
368 ERROR("write_file: failed to read data block 0x%llx\n", start);
369 goto failure;
372 if(write(file_fd, file_data, bytes) < bytes) {
373 ERROR("write_file: failed to write data block 0x%llx\n", start);
374 goto failure;
377 start += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
380 if(frag_bytes != 0) {
381 char *fragment_data = read_fragment(fragment);
383 if(fragment_data == NULL)
384 goto failure;
386 if(write(file_fd, fragment_data + offset, frag_bytes) < frag_bytes) {
387 ERROR("write_file: failed to write fragment %d\n", fragment);
388 goto failure;
392 close(file_fd);
393 return TRUE;
395 failure:
396 close(file_fd);
397 free(block_list);
398 return FALSE;
402 int create_inode(char *pathname, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk)
404 long long start = sBlk->inode_table_start + start_block;
405 squashfs_inode_header header;
406 char *block_ptr;
407 int bytes = lookup_entry(inode_table_hash, start), file_fd;
409 TRACE("create_inode: pathname %s, start 0x%llx, offset %d\n", pathname, start, offset);
411 if(bytes == -1) {
412 ERROR("create_inode: inode block 0x%llx out of range!\n", start);
413 return FALSE;
415 block_ptr = inode_table + bytes + offset;
417 if(swap) {
418 squashfs_base_inode_header sinode;
419 memcpy(&sinode, block_ptr, sizeof(header.base));
420 SQUASHFS_SWAP_BASE_INODE_HEADER(&header.base, &sinode, sizeof(squashfs_base_inode_header));
421 } else
422 memcpy(&header.base, block_ptr, sizeof(header.base));
424 if(created_inode[header.base.inode_number - 1]) {
425 TRACE("create_inode: hard link\n");
426 if(link(created_inode[header.base.inode_number - 1], pathname) == -1) {
427 ERROR("create_inode: failed to create hardlink, because %s\n", strerror(errno));
428 return FALSE;
431 return TRUE;
434 switch(header.base.inode_type) {
435 case SQUASHFS_FILE_TYPE: {
436 unsigned int frag_bytes;
437 unsigned int blocks;
438 unsigned int offset;
439 long long start;
440 squashfs_reg_inode_header *inode = &header.reg;
442 if(swap) {
443 squashfs_reg_inode_header sinode;
444 memcpy(&sinode, block_ptr, sizeof(sinode));
445 SQUASHFS_SWAP_REG_INODE_HEADER(inode, &sinode);
446 } else
447 memcpy(inode, block_ptr, sizeof(*inode));
449 frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size;
450 offset = inode->offset;
451 blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size
452 + sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >>
453 sBlk->block_log;
454 start = inode->start_block;
456 TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
458 if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start,
459 block_ptr + sizeof(*inode), inode->mode)) {
460 set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE);
461 file_count ++;
463 break;
465 case SQUASHFS_LREG_TYPE: {
466 unsigned int frag_bytes;
467 unsigned int blocks;
468 unsigned int offset;
469 long long start;
470 squashfs_lreg_inode_header *inode = &header.lreg;
472 if(swap) {
473 squashfs_lreg_inode_header sinode;
474 memcpy(&sinode, block_ptr, sizeof(sinode));
475 SQUASHFS_SWAP_LREG_INODE_HEADER(inode, &sinode);
476 } else
477 memcpy(inode, block_ptr, sizeof(*inode));
479 frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG ? 0 : inode->file_size % sBlk->block_size;
480 offset = inode->offset;
481 blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? (inode->file_size
482 + sBlk->block_size - 1) >> sBlk->block_log : inode->file_size >>
483 sBlk->block_log;
484 start = inode->start_block;
486 TRACE("create_inode: regular file, file_size %lld, blocks %d\n", inode->file_size, blocks);
488 if(write_file(pathname, inode->fragment, frag_bytes, offset, blocks, start,
489 block_ptr + sizeof(*inode), inode->mode)) {
490 set_attributes(pathname, inode->mode, inode->uid, inode->guid, inode->mtime, FALSE);
491 file_count ++;
493 break;
495 case SQUASHFS_SYMLINK_TYPE: {
496 squashfs_symlink_inode_header *inodep = &header.symlink;
497 char name[65536];
499 if(swap) {
500 squashfs_symlink_inode_header sinodep;
501 memcpy(&sinodep, block_ptr, sizeof(sinodep));
502 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, &sinodep);
503 } else
504 memcpy(inodep, block_ptr, sizeof(*inodep));
506 TRACE("create_inode: symlink, symlink_size %d\n", inodep->symlink_size);
508 strncpy(name, block_ptr + sizeof(squashfs_symlink_inode_header), inodep->symlink_size);
509 name[inodep->symlink_size] = '\0';
511 if(symlink(name, pathname) == -1) {
512 ERROR("create_inode: failed to create symlink %s, because %s\n", pathname,
513 strerror(errno));
514 break;
517 if(geteuid() == 0) {
518 uid_t uid_value = (uid_t) uid_table[inodep->uid];
519 uid_t guid_value = inodep->guid == SQUASHFS_GUIDS ? uid_value : (uid_t) guid_table[inodep->guid];
521 if(lchown(pathname, uid_value, guid_value) == -1)
522 ERROR("create_inode: failed to change uid and gids on %s, because %s\n", pathname, strerror(errno));
525 sym_count ++;
526 break;
528 case SQUASHFS_BLKDEV_TYPE:
529 case SQUASHFS_CHRDEV_TYPE: {
530 squashfs_dev_inode_header *inodep = &header.dev;
532 if(swap) {
533 squashfs_dev_inode_header sinodep;
534 memcpy(&sinodep, block_ptr, sizeof(sinodep));
535 SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, &sinodep);
536 } else
537 memcpy(inodep, block_ptr, sizeof(*inodep));
539 TRACE("create_inode: dev, rdev 0x%x\n", inodep->rdev);
541 if(geteuid() == 0) {
542 if(mknod(pathname, inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? S_IFCHR : S_IFBLK,
543 makedev((inodep->rdev >> 8) & 0xff, inodep->rdev & 0xff))
544 == -1) {
545 ERROR("create_inode: failed to create %s device %s, because %s\n",
546 inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
547 pathname, strerror(errno));
548 break;
550 set_attributes(pathname, inodep->mode, inodep->uid, inodep->guid, inodep->mtime, TRUE);
551 dev_count ++;
552 } else
553 ERROR("create_inode: could not create %s device %s, because you're not superuser!\n",
554 inodep->inode_type == SQUASHFS_CHRDEV_TYPE ? "character" : "block",
555 pathname, strerror(errno));
556 break;
558 case SQUASHFS_FIFO_TYPE:
559 TRACE("create_inode: fifo\n");
561 if(mknod(pathname, S_IFIFO, 0) == -1) {
562 ERROR("create_inode: failed to create fifo %s, because %s\n",
563 pathname, strerror(errno));
564 break;
566 set_attributes(pathname, header.base.mode, header.base.uid, header.base.guid,
567 header.base.mtime, TRUE);
568 fifo_count ++;
569 break;
570 case SQUASHFS_SOCKET_TYPE:
571 TRACE("create_inode: socket\n");
572 ERROR("create_inode: socket %s ignored\n", pathname);
573 break;
574 default:
575 ERROR("Unknown inode type %d in create_inode_table!\n", header.base.inode_type);
576 return FALSE;
579 created_inode[header.base.inode_number - 1] = strdup(pathname);
581 return TRUE;
585 void uncompress_directory_table(long long start, long long end, squashfs_super_block *sBlk)
587 int bytes = 0, size = 0, res;
589 while(start < end) {
590 if(size - bytes < SQUASHFS_METADATA_SIZE && (directory_table = realloc(directory_table, size += SQUASHFS_METADATA_SIZE)) == NULL)
591 EXIT_UNSQUASH("uncompress_directory_table: out of memory in realloc\n");
592 TRACE("uncompress_directory_table: reading block 0x%llx\n", start);
593 add_entry(directory_table_hash, start, bytes);
594 if((res = read_block(start, &start, directory_table + bytes, sBlk)) == 0)
595 EXIT_UNSQUASH("uncompress_directory_table: failed to read block\n");
596 bytes += res;
601 #define DIR_ENT_SIZE 16
603 struct dir_ent {
604 char name[SQUASHFS_NAME_LEN + 1];
605 unsigned int start_block;
606 unsigned int offset;
607 unsigned int type;
610 struct dir {
611 int dir_count;
612 int cur_entry;
613 unsigned int mode;
614 unsigned int uid;
615 unsigned int guid;
616 unsigned int mtime;
617 struct dir_ent *dirs;
621 struct dir *squashfs_openddir(unsigned int block_start, unsigned int offset, squashfs_super_block *sBlk)
623 squashfs_dir_header dirh;
624 char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
625 squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer;
626 long long start = sBlk->inode_table_start + block_start;
627 char *block_ptr;
628 int bytes = lookup_entry(inode_table_hash, start);
629 squashfs_inode_header header;
630 int dir_count, size;
631 struct dir_ent *new_dir;
632 struct dir *dir;
634 TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start, offset);
636 if(bytes == -1) {
637 ERROR("squashfs_opendir: inode block %d not found!\n", block_start);
638 return NULL;
640 block_ptr = inode_table + bytes + offset;
642 if(swap) {
643 squashfs_dir_inode_header sinode;
644 memcpy(&sinode, block_ptr, sizeof(header.dir));
645 SQUASHFS_SWAP_DIR_INODE_HEADER(&header.dir, &sinode);
646 } else
647 memcpy(&header.dir, block_ptr, sizeof(header.dir));
649 switch(header.dir.inode_type) {
650 case SQUASHFS_DIR_TYPE:
651 block_start = header.dir.start_block;
652 offset = header.dir.offset;
653 size = header.dir.file_size;
654 break;
655 case SQUASHFS_LDIR_TYPE:
656 if(swap) {
657 squashfs_ldir_inode_header sinode;
658 memcpy(&sinode, block_ptr, sizeof(header.ldir));
659 SQUASHFS_SWAP_LDIR_INODE_HEADER(&header.ldir, &sinode);
660 } else
661 memcpy(&header.ldir, block_ptr, sizeof(header.ldir));
662 block_start = header.ldir.start_block;
663 offset = header.ldir.offset;
664 size = header.ldir.file_size;
665 break;
666 default:
667 ERROR("squashfs_opendir: inode not a directory\n");
668 return NULL;
671 start = sBlk->directory_table_start + block_start;
672 bytes = lookup_entry(directory_table_hash, start);
674 if(bytes == -1) {
675 ERROR("squashfs_opendir: directory block %d not found!\n", block_start);
676 return NULL;
678 bytes += offset;
679 size += bytes - 3;
681 if((dir = malloc(sizeof(struct dir))) == NULL) {
682 ERROR("squashfs_opendir: malloc failed!\n");
683 return NULL;
686 dir->dir_count = 0;
687 dir->cur_entry = 0;
688 dir->mode = header.dir.mode;
689 dir->uid = header.dir.uid;
690 dir->guid = header.dir.guid;
691 dir->mtime = header.dir.mtime;
692 dir->dirs = NULL;
694 while(bytes < size) {
695 if(swap) {
696 squashfs_dir_header sdirh;
697 memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
698 SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
699 } else
700 memcpy(&dirh, directory_table + bytes, sizeof(dirh));
702 dir_count = dirh.count + 1;
703 TRACE("squashfs_opendir: Read directory header @ byte position %d, %d directory entries\n", bytes, dir_count);
704 bytes += sizeof(dirh);
706 while(dir_count--) {
707 if(swap) {
708 squashfs_dir_entry sdire;
709 memcpy(&sdire, directory_table + bytes, sizeof(sdire));
710 SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
711 } else
712 memcpy(dire, directory_table + bytes, sizeof(dire));
713 bytes += sizeof(*dire);
715 memcpy(dire->name, directory_table + bytes, dire->size + 1);
716 dire->name[dire->size + 1] = '\0';
717 TRACE("squashfs_opendir: directory entry %s, inode %d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type);
718 if((dir->dir_count % DIR_ENT_SIZE) == 0) {
719 if((new_dir = realloc(dir->dirs, (dir->dir_count + DIR_ENT_SIZE) * sizeof(struct dir_ent))) == NULL) {
720 ERROR("squashfs_opendir: realloc failed!\n");
721 free(dir->dirs);
722 free(dir);
723 return NULL;
725 dir->dirs = new_dir;
727 strcpy(dir->dirs[dir->dir_count].name, dire->name);
728 dir->dirs[dir->dir_count].start_block = dirh.start_block;
729 dir->dirs[dir->dir_count].offset = dire->offset;
730 dir->dirs[dir->dir_count].type = dire->type;
731 dir->dir_count ++;
732 bytes += dire->size + 1;
736 return dir;
740 int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, unsigned int *offset, unsigned int *type)
742 if(dir->cur_entry == dir->dir_count)
743 return FALSE;
745 *name = dir->dirs[dir->cur_entry].name;
746 *start_block = dir->dirs[dir->cur_entry].start_block;
747 *offset = dir->dirs[dir->cur_entry].offset;
748 *type = dir->dirs[dir->cur_entry].type;
749 dir->cur_entry ++;
751 return TRUE;
755 void squashfs_closedir(struct dir *dir)
757 free(dir->dirs);
758 free(dir);
762 int dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, squashfs_super_block *sBlk)
764 struct dir *dir = squashfs_openddir(start_block, offset, sBlk);
765 unsigned int type;
766 char *name, pathname[1024];
768 if(dir == NULL) {
769 ERROR("dir_scan: Failed to read directory %s (%x:%x)\n", parent_name, start_block, offset);
770 return FALSE;
773 if(!lsonly && mkdir(parent_name, (mode_t) dir->mode) == -1) {
774 ERROR("dir_scan: failed to open directory %s, because %s\n", parent_name, strerror(errno));
775 return FALSE;
778 while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) {
779 TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", name, start_block, offset, type);
781 strcat(strcat(strcpy(pathname, parent_name), "/"), name);
783 if(lsonly || info)
784 printf("%s\n", pathname);
786 if(type == SQUASHFS_DIR_TYPE)
787 dir_scan(pathname, start_block, offset, sBlk);
788 else
789 if(!lsonly)
790 create_inode(pathname, start_block, offset, sBlk);
793 !lsonly && set_attributes(parent_name, dir->mode, dir->uid, dir->guid, dir->mtime, TRUE);
795 squashfs_closedir(dir);
796 dir_count ++;
798 return TRUE;
802 int read_super(squashfs_super_block *sBlk, char *source)
804 read_bytes(SQUASHFS_START, sizeof(squashfs_super_block), (char *) sBlk);
806 /* Check it is a SQUASHFS superblock */
807 swap = 0;
808 if(sBlk->s_magic != SQUASHFS_MAGIC) {
809 if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) {
810 squashfs_super_block sblk;
811 ERROR("Reading a different endian SQUASHFS filesystem on %s\n", source);
812 SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk);
813 memcpy(sBlk, &sblk, sizeof(squashfs_super_block));
814 swap = 1;
815 } else {
816 ERROR("Can't find a SQUASHFS superblock on %s\n", source);
817 goto failed_mount;
821 /* Check the MAJOR & MINOR versions */
822 if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
823 ERROR("Major/Minor mismatch, filesystem on %s is (%d:%d)\n",
824 source, sBlk->s_major, sBlk->s_minor);
825 ERROR("I only support Squashfs 3.0 filesystems! Later releases will support older Squashfs filesystems\n");
826 goto failed_mount;
829 #if __BYTE_ORDER == __BIG_ENDIAN
830 TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "little" : "big", source);
831 #else
832 TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap ? "big" : "little", source);
833 #endif
835 TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
836 TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
837 TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
838 TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not");
839 TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not" : "");
840 TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not");
841 TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not");
842 TRACE("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n", sBlk->bytes_used / 1024.0, sBlk->bytes_used / (1024.0 * 1024.0));
843 TRACE("\tBlock size %d\n", sBlk->block_size);
844 TRACE("\tNumber of fragments %d\n", sBlk->fragments);
845 TRACE("\tNumber of inodes %d\n", sBlk->inodes);
846 TRACE("\tNumber of uids %d\n", sBlk->no_uids);
847 TRACE("\tNumber of gids %d\n", sBlk->no_guids);
848 TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start);
849 TRACE("sBlk->directory_table_start 0x%llx\n", sBlk->directory_table_start);
850 TRACE("sBlk->uid_start 0x%llx\n", sBlk->uid_start);
851 TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start);
852 TRACE("\n");
854 return TRUE;
856 failed_mount:
857 return FALSE;
861 #define VERSION() \
862 printf("unsquashfs version 1.0 (2006/03/15)\n");\
863 printf("copyright (C) 2006 Phillip Lougher <phillip@lougher.org.uk>\n\n"); \
864 printf("This program is free software; you can redistribute it and/or\n");\
865 printf("modify it under the terms of the GNU General Public License\n");\
866 printf("as published by the Free Software Foundation; either version 2,\n");\
867 printf("or (at your option) any later version.\n\n");\
868 printf("This program is distributed in the hope that it will be useful,\n");\
869 printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");\
870 printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");\
871 printf("GNU General Public License for more details.\n");
872 int main(int argc, char *argv[])
874 squashfs_super_block sBlk;
875 char *dest = "squashfs-root";
876 int i, version = FALSE;
878 for(i = 1; i < argc; i++) {
879 if(*argv[i] != '-')
880 break;
881 if(strcmp(argv[i], "-version") == 0) {
882 VERSION();
883 version = TRUE;
884 } else if(strcmp(argv[i], "-info") == 0)
885 info = TRUE;
886 else if(strcmp(argv[i], "-ls") == 0)
887 lsonly = TRUE;
888 else if(strcmp(argv[i], "-dest") == 0) {
889 if(++i == argc)
890 goto options;
891 dest = argv[i];
895 if(i == argc) {
896 if(!version) {
897 options:
898 ERROR("SYNTAX: %s [-ls | -dest] filesystem\n", argv[0]);
899 ERROR("\t-version\t\tprint version, licence and copyright information\n");
900 ERROR("\t-info\t\t\tprint files as they are unsquashed\n");
901 ERROR("\t-ls\t\t\tlist filesystem only\n");
902 ERROR("\t-dest <pathname>\tunsquash to <pathname>, default \"squashfs-root\"\n");
904 exit(1);
907 if((fd = open(argv[i], O_RDONLY)) == -1) {
908 ERROR("Could not open %s, because %s\n", argv[i], strerror(errno));
909 exit(1);
912 if(read_super(&sBlk, argv[i]) == FALSE)
913 exit(1);
915 block_size = sBlk.block_size;
916 if((fragment_data = malloc(block_size)) == NULL)
917 EXIT_UNSQUASH("failed to allocate fragment_data\n");
919 if((file_data = malloc(block_size)) == NULL)
920 EXIT_UNSQUASH("failed to allocate file_data");
922 if((data = malloc(block_size)) == NULL)
923 EXIT_UNSQUASH("failed to allocate datan\n");
925 if((created_inode = malloc(sBlk.inodes * sizeof(char *))) == NULL)
926 EXIT_UNSQUASH("failed to allocate created_inode\n");
928 memset(created_inode, 0, sBlk.inodes * sizeof(char *));
930 read_uids_guids(&sBlk);
931 read_fragment_table(&sBlk);
932 uncompress_inode_table(sBlk.inode_table_start, sBlk.directory_table_start, &sBlk);
933 uncompress_directory_table(sBlk.directory_table_start, sBlk.fragment_table_start, &sBlk);
935 dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.root_inode), SQUASHFS_INODE_OFFSET(sBlk.root_inode), &sBlk);
937 if(!lsonly) {
938 printf("\n");
939 printf("created %d files\n", file_count);
940 printf("created %d directories\n", dir_count);
941 printf("created %d symlinks\n", sym_count);
942 printf("created %d devices\n", dev_count);
943 printf("created %d fifos\n", fifo_count);