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.
27 #include <sys/types.h>
37 #define __BYTE_ORDER BYTE_ORDER
38 #define __BIG_ENDIAN BIG_ENDIAN
39 #define __LITTLE_ENDIAN LITTLE_ENDIAN
44 #include <squashfs_fs.h>
51 #define TRACE(s, args...) do { \
52 printf("mksquashfs: "s, ## args); \
55 #define TRACE(s, args...)
58 #define ERROR(s, args...) do { \
59 fprintf(stderr, s, ## args); \
62 #define EXIT_UNSQUASH(s, args...) do { \
63 fprintf(stderr, "FATAL ERROR aborting: "s, ## args); \
66 struct hash_table_entry
{
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];
76 squashfs_fragment_entry
*fragment_table
;
77 unsigned int *uid_table
, *guid_table
;
78 unsigned int cached_frag
= SQUASHFS_INVALID_FRAG
;
82 unsigned int block_size
;
83 int lsonly
= FALSE
, info
= FALSE
;
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");
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
;
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
;
120 int read_bytes(long long byte
, int bytes
, char *buff
)
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
));
131 if(read(fd
, buff
, bytes
) == -1) {
132 ERROR("Read on destination failed because %s\n", strerror(errno
));
140 int read_block(long long start
, long long *next
, char *block
, squashfs_super_block
*sBlk
)
142 unsigned short c_byte
;
146 if(read_bytes(start
, 2, block
) == FALSE
)
148 ((unsigned char *) &c_byte
)[1] = block
[0];
149 ((unsigned char *) &c_byte
)[0] = block
[1];
151 if(read_bytes(start
, 2, (char *)&c_byte
) == FALSE
)
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
))
158 if(SQUASHFS_COMPRESSED(c_byte
)) {
159 char buffer
[SQUASHFS_METADATA_SIZE
];
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
)
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");
173 ERROR("zlib::uncompress failed, unknown error %d\n", res
);
177 *next
= start
+ offset
+ c_byte
;
180 c_byte
= SQUASHFS_COMPRESSED_SIZE(c_byte
);
181 if(read_bytes(start
+ offset
, c_byte
, block
) == FALSE
)
184 *next
= start
+ offset
+ c_byte
;
193 int read_data_block(long long start
, unsigned int size
, char *block
)
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
)
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");
211 ERROR("zlib::uncompress failed, unknown error %d\n", res
);
217 if(read_bytes(start
, c_byte
, block
) == FALSE
)
225 void uncompress_inode_table(long long start
, long long end
, squashfs_super_block
*sBlk
)
227 int size
= 0, bytes
= 0, res
;
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) {
237 EXIT_UNSQUASH("uncompress_inode_table: failed to read block\n");
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
, ×
) == -1) {
250 ERROR("set_attributes: failed to set time on %s, because %s\n", pathname
, strerror(errno
));
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
));
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
));
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
) ==
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)
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");
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
);
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
);
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
;
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");
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
);
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
,
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
);
372 if(write(file_fd
, file_data
, bytes
) < bytes
) {
373 ERROR("write_file: failed to write data block 0x%llx\n", start
);
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
)
386 if(write(file_fd
, fragment_data
+ offset
, frag_bytes
) < frag_bytes
) {
387 ERROR("write_file: failed to write fragment %d\n", fragment
);
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
;
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
);
412 ERROR("create_inode: inode block 0x%llx out of range!\n", start
);
415 block_ptr
= inode_table
+ bytes
+ offset
;
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
));
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
));
434 switch(header
.base
.inode_type
) {
435 case SQUASHFS_FILE_TYPE
: {
436 unsigned int frag_bytes
;
440 squashfs_reg_inode_header
*inode
= &header
.reg
;
443 squashfs_reg_inode_header sinode
;
444 memcpy(&sinode
, block_ptr
, sizeof(sinode
));
445 SQUASHFS_SWAP_REG_INODE_HEADER(inode
, &sinode
);
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
>>
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
);
465 case SQUASHFS_LREG_TYPE
: {
466 unsigned int frag_bytes
;
470 squashfs_lreg_inode_header
*inode
= &header
.lreg
;
473 squashfs_lreg_inode_header sinode
;
474 memcpy(&sinode
, block_ptr
, sizeof(sinode
));
475 SQUASHFS_SWAP_LREG_INODE_HEADER(inode
, &sinode
);
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
>>
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
);
495 case SQUASHFS_SYMLINK_TYPE
: {
496 squashfs_symlink_inode_header
*inodep
= &header
.symlink
;
500 squashfs_symlink_inode_header sinodep
;
501 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
502 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep
, &sinodep
);
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
,
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
));
528 case SQUASHFS_BLKDEV_TYPE
:
529 case SQUASHFS_CHRDEV_TYPE
: {
530 squashfs_dev_inode_header
*inodep
= &header
.dev
;
533 squashfs_dev_inode_header sinodep
;
534 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
535 SQUASHFS_SWAP_DEV_INODE_HEADER(inodep
, &sinodep
);
537 memcpy(inodep
, block_ptr
, sizeof(*inodep
));
539 TRACE("create_inode: dev, rdev 0x%x\n", inodep
->rdev
);
542 if(mknod(pathname
, inodep
->inode_type
== SQUASHFS_CHRDEV_TYPE
? S_IFCHR
: S_IFBLK
,
543 makedev((inodep
->rdev
>> 8) & 0xff, inodep
->rdev
& 0xff))
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
));
550 set_attributes(pathname
, inodep
->mode
, inodep
->uid
, inodep
->guid
, inodep
->mtime
, TRUE
);
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
));
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
));
566 set_attributes(pathname
, header
.base
.mode
, header
.base
.uid
, header
.base
.guid
,
567 header
.base
.mtime
, TRUE
);
570 case SQUASHFS_SOCKET_TYPE
:
571 TRACE("create_inode: socket\n");
572 ERROR("create_inode: socket %s ignored\n", pathname
);
575 ERROR("Unknown inode type %d in create_inode_table!\n", header
.base
.inode_type
);
579 created_inode
[header
.base
.inode_number
- 1] = strdup(pathname
);
585 void uncompress_directory_table(long long start
, long long end
, squashfs_super_block
*sBlk
)
587 int bytes
= 0, size
= 0, res
;
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");
601 #define DIR_ENT_SIZE 16
604 char name
[SQUASHFS_NAME_LEN
+ 1];
605 unsigned int start_block
;
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
;
628 int bytes
= lookup_entry(inode_table_hash
, start
);
629 squashfs_inode_header header
;
631 struct dir_ent
*new_dir
;
634 TRACE("squashfs_opendir: inode start block %d, offset %d\n", block_start
, offset
);
637 ERROR("squashfs_opendir: inode block %d not found!\n", block_start
);
640 block_ptr
= inode_table
+ bytes
+ offset
;
643 squashfs_dir_inode_header sinode
;
644 memcpy(&sinode
, block_ptr
, sizeof(header
.dir
));
645 SQUASHFS_SWAP_DIR_INODE_HEADER(&header
.dir
, &sinode
);
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
;
655 case SQUASHFS_LDIR_TYPE
:
657 squashfs_ldir_inode_header sinode
;
658 memcpy(&sinode
, block_ptr
, sizeof(header
.ldir
));
659 SQUASHFS_SWAP_LDIR_INODE_HEADER(&header
.ldir
, &sinode
);
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
;
667 ERROR("squashfs_opendir: inode not a directory\n");
671 start
= sBlk
->directory_table_start
+ block_start
;
672 bytes
= lookup_entry(directory_table_hash
, start
);
675 ERROR("squashfs_opendir: directory block %d not found!\n", block_start
);
681 if((dir
= malloc(sizeof(struct dir
))) == NULL
) {
682 ERROR("squashfs_opendir: malloc failed!\n");
688 dir
->mode
= header
.dir
.mode
;
689 dir
->uid
= header
.dir
.uid
;
690 dir
->guid
= header
.dir
.guid
;
691 dir
->mtime
= header
.dir
.mtime
;
694 while(bytes
< size
) {
696 squashfs_dir_header sdirh
;
697 memcpy(&sdirh
, directory_table
+ bytes
, sizeof(sdirh
));
698 SQUASHFS_SWAP_DIR_HEADER(&dirh
, &sdirh
);
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
);
708 squashfs_dir_entry sdire
;
709 memcpy(&sdire
, directory_table
+ bytes
, sizeof(sdire
));
710 SQUASHFS_SWAP_DIR_ENTRY(dire
, &sdire
);
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");
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
;
732 bytes
+= dire
->size
+ 1;
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
)
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
;
755 void squashfs_closedir(struct dir
*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
);
766 char *name
, pathname
[1024];
769 ERROR("dir_scan: Failed to read directory %s (%x:%x)\n", parent_name
, start_block
, offset
);
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
));
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
);
784 printf("%s\n", pathname
);
786 if(type
== SQUASHFS_DIR_TYPE
)
787 dir_scan(pathname
, start_block
, offset
, sBlk
);
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
);
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 */
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
));
816 ERROR("Can't find a SQUASHFS superblock on %s\n", source
);
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");
829 #if __BYTE_ORDER == __BIG_ENDIAN
830 TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap
? "little" : "big", source
);
832 TRACE("Found a valid %s endian SQUASHFS superblock on %s.\n", swap
? "big" : "little", source
);
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
);
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
++) {
881 if(strcmp(argv
[i
], "-version") == 0) {
884 } else if(strcmp(argv
[i
], "-info") == 0)
886 else if(strcmp(argv
[i
], "-ls") == 0)
888 else if(strcmp(argv
[i
], "-dest") == 0) {
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");
907 if((fd
= open(argv
[i
], O_RDONLY
)) == -1) {
908 ERROR("Could not open %s, because %s\n", argv
[i
], strerror(errno
));
912 if(read_super(&sBlk
, argv
[i
]) == FALSE
)
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
);
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
);