2 * Unsquash a squashfs filesystem. This is a highly compressed read only
5 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2019
6 * Phillip Lougher <phillip@squashfs.org.uk>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2,
11 * or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include "unsquashfs.h"
26 #include "squashfs_swap.h"
29 static struct squashfs_fragment_entry
*fragment_table
;
30 static unsigned int *id_table
;
31 static char *inode_table
, *directory_table
;
32 static squashfs_operations ops
;
34 static void read_block_list(unsigned int *block_list
, char *block_ptr
, int blocks
)
36 TRACE("read_block_list: blocks %d\n", blocks
);
38 memcpy(block_list
, block_ptr
, blocks
* sizeof(unsigned int));
39 SQUASHFS_INSWAP_INTS(block_list
, blocks
);
43 static int read_fragment_table(long long *table_start
)
46 * Note on overflow limits:
47 * Size of SBlk.s.fragments is 2^32 (unsigned int)
48 * Max size of bytes is 2^32*16 or 2^36
49 * Max indexes is (2^32*16)/8K or 2^23
50 * Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
53 long long bytes
= SQUASHFS_FRAGMENT_BYTES((long long) sBlk
.s
.fragments
);
54 int indexes
= SQUASHFS_FRAGMENT_INDEXES((long long) sBlk
.s
.fragments
);
55 int length
= SQUASHFS_FRAGMENT_INDEX_BYTES((long long) sBlk
.s
.fragments
);
56 long long *fragment_table_index
;
59 * The size of the index table (length bytes) should match the
60 * table start and end points
62 if(length
!= (*table_start
- sBlk
.s
.fragment_table_start
)) {
63 ERROR("read_fragment_table: Bad fragment count in super block\n");
67 TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
68 "from 0x%llx\n", sBlk
.s
.fragments
, indexes
,
69 sBlk
.s
.fragment_table_start
);
71 fragment_table_index
= alloc_index_table(indexes
);
72 fragment_table
= malloc(bytes
);
73 if(fragment_table
== NULL
)
74 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
77 res
= read_fs_bytes(fd
, sBlk
.s
.fragment_table_start
, length
,
78 fragment_table_index
);
80 ERROR("read_fragment_table: failed to read fragment table "
84 SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index
, indexes
);
86 for(i
= 0; i
< indexes
; i
++) {
87 int expected
= (i
+ 1) != indexes
? SQUASHFS_METADATA_SIZE
:
88 bytes
& (SQUASHFS_METADATA_SIZE
- 1);
89 int length
= read_block(fd
, fragment_table_index
[i
], NULL
,
90 expected
, ((char *) fragment_table
) + (i
*
91 SQUASHFS_METADATA_SIZE
));
92 TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
93 i
, fragment_table_index
[i
], length
);
95 ERROR("read_fragment_table: failed to read fragment "
101 for(i
= 0; i
< sBlk
.s
.fragments
; i
++)
102 SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table
[i
]);
104 *table_start
= fragment_table_index
[0];
109 static void read_fragment(unsigned int fragment
, long long *start_block
, int *size
)
111 TRACE("read_fragment: reading fragment %d\n", fragment
);
113 struct squashfs_fragment_entry
*fragment_entry
;
115 fragment_entry
= &fragment_table
[fragment
];
116 *start_block
= fragment_entry
->start_block
;
117 *size
= fragment_entry
->size
;
121 static struct inode
*read_inode(unsigned int start_block
, unsigned int offset
)
123 static union squashfs_inode_header header
;
124 long long start
= sBlk
.s
.inode_table_start
+ start_block
;
125 long long bytes
= lookup_entry(inode_table_hash
, start
);
126 char *block_ptr
= inode_table
+ bytes
+ offset
;
127 static struct inode i
;
129 TRACE("read_inode: reading inode [%d:%d]\n", start_block
, offset
);
132 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
135 SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr
, &header
.base
);
137 i
.uid
= (uid_t
) id_table
[header
.base
.uid
];
138 i
.gid
= (uid_t
) id_table
[header
.base
.guid
];
139 i
.mode
= lookup_type
[header
.base
.inode_type
] | header
.base
.mode
;
140 i
.type
= header
.base
.inode_type
;
141 i
.time
= header
.base
.mtime
;
142 i
.inode_number
= header
.base
.inode_number
;
144 switch(header
.base
.inode_type
) {
145 case SQUASHFS_DIR_TYPE
: {
146 struct squashfs_dir_inode_header
*inode
= &header
.dir
;
148 SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr
, inode
);
150 i
.data
= inode
->file_size
;
151 i
.offset
= inode
->offset
;
152 i
.start
= inode
->start_block
;
153 i
.xattr
= SQUASHFS_INVALID_XATTR
;
156 case SQUASHFS_LDIR_TYPE
: {
157 struct squashfs_ldir_inode_header
*inode
= &header
.ldir
;
159 SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr
, inode
);
161 i
.data
= inode
->file_size
;
162 i
.offset
= inode
->offset
;
163 i
.start
= inode
->start_block
;
164 i
.xattr
= inode
->xattr
;
167 case SQUASHFS_FILE_TYPE
: {
168 struct squashfs_reg_inode_header
*inode
= &header
.reg
;
170 SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr
, inode
);
172 i
.data
= inode
->file_size
;
173 i
.frag_bytes
= inode
->fragment
== SQUASHFS_INVALID_FRAG
174 ? 0 : inode
->file_size
% sBlk
.s
.block_size
;
175 i
.fragment
= inode
->fragment
;
176 i
.offset
= inode
->offset
;
177 i
.blocks
= inode
->fragment
== SQUASHFS_INVALID_FRAG
?
178 (i
.data
+ sBlk
.s
.block_size
- 1) >>
180 i
.data
>> sBlk
.s
.block_log
;
181 i
.start
= inode
->start_block
;
183 i
.block_ptr
= block_ptr
+ sizeof(*inode
);
184 i
.xattr
= SQUASHFS_INVALID_XATTR
;
187 case SQUASHFS_LREG_TYPE
: {
188 struct squashfs_lreg_inode_header
*inode
= &header
.lreg
;
190 SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr
, inode
);
192 i
.data
= inode
->file_size
;
193 i
.frag_bytes
= inode
->fragment
== SQUASHFS_INVALID_FRAG
194 ? 0 : inode
->file_size
% sBlk
.s
.block_size
;
195 i
.fragment
= inode
->fragment
;
196 i
.offset
= inode
->offset
;
197 i
.blocks
= inode
->fragment
== SQUASHFS_INVALID_FRAG
?
198 (inode
->file_size
+ sBlk
.s
.block_size
- 1) >>
200 inode
->file_size
>> sBlk
.s
.block_log
;
201 i
.start
= inode
->start_block
;
202 i
.sparse
= inode
->sparse
!= 0;
203 i
.block_ptr
= block_ptr
+ sizeof(*inode
);
204 i
.xattr
= inode
->xattr
;
207 case SQUASHFS_SYMLINK_TYPE
:
208 case SQUASHFS_LSYMLINK_TYPE
: {
209 struct squashfs_symlink_inode_header
*inode
= &header
.symlink
;
211 SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr
, inode
);
213 i
.symlink
= malloc(inode
->symlink_size
+ 1);
214 if(i
.symlink
== NULL
)
215 EXIT_UNSQUASH("read_inode: failed to malloc "
217 strncpy(i
.symlink
, block_ptr
+
218 sizeof(struct squashfs_symlink_inode_header
),
219 inode
->symlink_size
);
220 i
.symlink
[inode
->symlink_size
] = '\0';
221 i
.data
= inode
->symlink_size
;
223 if(header
.base
.inode_type
== SQUASHFS_LSYMLINK_TYPE
)
224 SQUASHFS_SWAP_INTS(block_ptr
+
225 sizeof(struct squashfs_symlink_inode_header
) +
226 inode
->symlink_size
, &i
.xattr
, 1);
228 i
.xattr
= SQUASHFS_INVALID_XATTR
;
231 case SQUASHFS_BLKDEV_TYPE
:
232 case SQUASHFS_CHRDEV_TYPE
: {
233 struct squashfs_dev_inode_header
*inode
= &header
.dev
;
235 SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr
, inode
);
237 i
.data
= inode
->rdev
;
238 i
.xattr
= SQUASHFS_INVALID_XATTR
;
241 case SQUASHFS_LBLKDEV_TYPE
:
242 case SQUASHFS_LCHRDEV_TYPE
: {
243 struct squashfs_ldev_inode_header
*inode
= &header
.ldev
;
245 SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr
, inode
);
247 i
.data
= inode
->rdev
;
248 i
.xattr
= inode
->xattr
;
251 case SQUASHFS_FIFO_TYPE
:
252 case SQUASHFS_SOCKET_TYPE
:
254 i
.xattr
= SQUASHFS_INVALID_XATTR
;
256 case SQUASHFS_LFIFO_TYPE
:
257 case SQUASHFS_LSOCKET_TYPE
: {
258 struct squashfs_lipc_inode_header
*inode
= &header
.lipc
;
260 SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr
, inode
);
263 i
.xattr
= inode
->xattr
;
267 EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
268 header
.base
.inode_type
);
274 static struct dir
*squashfs_opendir(unsigned int block_start
, unsigned int offset
,
277 struct squashfs_dir_header dirh
;
278 char buffer
[sizeof(struct squashfs_dir_entry
) + SQUASHFS_NAME_LEN
+ 1]
279 __attribute__((aligned
));
280 struct squashfs_dir_entry
*dire
= (struct squashfs_dir_entry
*) buffer
;
284 struct dir_ent
*new_dir
;
287 TRACE("squashfs_opendir: inode start block %d, offset %d\n",
288 block_start
, offset
);
290 *i
= read_inode(block_start
, offset
);
292 dir
= malloc(sizeof(struct dir
));
294 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
298 dir
->mode
= (*i
)->mode
;
299 dir
->uid
= (*i
)->uid
;
300 dir
->guid
= (*i
)->gid
;
301 dir
->mtime
= (*i
)->time
;
302 dir
->xattr
= (*i
)->xattr
;
307 * if the directory is empty, skip the unnecessary
308 * lookup_entry, this fixes the corner case with
309 * completely empty filesystems where lookup_entry correctly
310 * returning -1 is incorrectly treated as an error
314 start
= sBlk
.s
.directory_table_start
+ (*i
)->start
;
315 bytes
= lookup_entry(directory_table_hash
, start
);
318 EXIT_UNSQUASH("squashfs_opendir: directory block %lld not "
321 bytes
+= (*i
)->offset
;
322 size
= (*i
)->data
+ bytes
- 3;
324 while(bytes
< size
) {
325 SQUASHFS_SWAP_DIR_HEADER(directory_table
+ bytes
, &dirh
);
327 dir_count
= dirh
.count
+ 1;
328 TRACE("squashfs_opendir: Read directory header @ byte position "
329 "%d, %d directory entries\n", bytes
, dir_count
);
330 bytes
+= sizeof(dirh
);
332 /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
333 if(dir_count
> SQUASHFS_DIR_COUNT
) {
334 ERROR("File system corrupted: too many entries in directory\n");
339 SQUASHFS_SWAP_DIR_ENTRY(directory_table
+ bytes
, dire
);
341 bytes
+= sizeof(*dire
);
343 /* size should never be SQUASHFS_NAME_LEN or larger */
344 if(dire
->size
>= SQUASHFS_NAME_LEN
) {
345 ERROR("File system corrupted: filename too long\n");
349 memcpy(dire
->name
, directory_table
+ bytes
,
351 dire
->name
[dire
->size
+ 1] = '\0';
352 TRACE("squashfs_opendir: directory entry %s, inode "
353 "%d:%d, type %d\n", dire
->name
,
354 dirh
.start_block
, dire
->offset
, dire
->type
);
355 if((dir
->dir_count
% DIR_ENT_SIZE
) == 0) {
356 new_dir
= realloc(dir
->dirs
, (dir
->dir_count
+
357 DIR_ENT_SIZE
) * sizeof(struct dir_ent
));
359 EXIT_UNSQUASH("squashfs_opendir: "
360 "realloc failed!\n");
363 strcpy(dir
->dirs
[dir
->dir_count
].name
, dire
->name
);
364 dir
->dirs
[dir
->dir_count
].start_block
=
366 dir
->dirs
[dir
->dir_count
].offset
= dire
->offset
;
367 dir
->dirs
[dir
->dir_count
].type
= dire
->type
;
369 bytes
+= dire
->size
+ 1;
382 static int read_id_table(long long *table_start
)
385 * Note on overflow limits:
386 * Size of SBlk.s.no_ids is 2^16 (unsigned short)
387 * Max size of bytes is 2^16*4 or 256K
388 * Max indexes is (2^16*4)/8K or 32
389 * Max length is ((2^16*4)/8K)*8 or 256
392 int bytes
= SQUASHFS_ID_BYTES(sBlk
.s
.no_ids
);
393 int indexes
= SQUASHFS_ID_BLOCKS(sBlk
.s
.no_ids
);
394 int length
= SQUASHFS_ID_BLOCK_BYTES(sBlk
.s
.no_ids
);
395 long long *id_index_table
;
398 * The size of the index table (length bytes) should match the
399 * table start and end points
401 if(length
!= (*table_start
- sBlk
.s
.id_table_start
)) {
402 ERROR("read_id_table: Bad id count in super block\n");
406 TRACE("read_id_table: no_ids %d\n", sBlk
.s
.no_ids
);
408 id_index_table
= alloc_index_table(indexes
);
409 id_table
= malloc(bytes
);
410 if(id_table
== NULL
) {
411 ERROR("read_id_table: failed to allocate id table\n");
415 res
= read_fs_bytes(fd
, sBlk
.s
.id_table_start
, length
, id_index_table
);
417 ERROR("read_id_table: failed to read id index table\n");
420 SQUASHFS_INSWAP_ID_BLOCKS(id_index_table
, indexes
);
423 * id_index_table[0] stores the start of the compressed id blocks.
424 * This by definition is also the end of the previous filesystem
425 * table - this may be the exports table if it is present, or the
426 * fragments table if it isn't.
428 *table_start
= id_index_table
[0];
430 for(i
= 0; i
< indexes
; i
++) {
431 int expected
= (i
+ 1) != indexes
? SQUASHFS_METADATA_SIZE
:
432 bytes
& (SQUASHFS_METADATA_SIZE
- 1);
433 res
= read_block(fd
, id_index_table
[i
], NULL
, expected
,
434 ((char *) id_table
) + i
* SQUASHFS_METADATA_SIZE
);
436 ERROR("read_id_table: failed to read id table block"
442 SQUASHFS_INSWAP_INTS(id_table
, sBlk
.s
.no_ids
);
448 static int parse_exports_table(long long *table_start
)
451 * Note on overflow limits:
452 * Size of SBlk.s.inodes is 2^32 (unsigned int)
453 * Max indexes is (2^32*8)/8K or 2^22
454 * Max length is ((2^32*8)/8K)*8 or 2^25
457 int indexes
= SQUASHFS_LOOKUP_BLOCKS((long long) sBlk
.s
.inodes
);
458 int length
= SQUASHFS_LOOKUP_BLOCK_BYTES((long long) sBlk
.s
.inodes
);
459 long long *export_index_table
;
462 * The size of the index table (length bytes) should match the
463 * table start and end points
465 if(length
!= (*table_start
- sBlk
.s
.lookup_table_start
)) {
466 ERROR("parse_exports_table: Bad inode count in super block\n");
470 export_index_table
= alloc_index_table(indexes
);
472 res
= read_fs_bytes(fd
, sBlk
.s
.lookup_table_start
, length
,
475 ERROR("parse_exports_table: failed to read export index table\n");
478 SQUASHFS_INSWAP_LOOKUP_BLOCKS(export_index_table
, indexes
);
481 * export_index_table[0] stores the start of the compressed export blocks.
482 * This by definition is also the end of the previous filesystem
483 * table - the fragment table.
485 *table_start
= export_index_table
[0];
491 squashfs_operations
*read_filesystem_tables_4()
493 long long table_start
;
496 if(sBlk
.s
.xattr_id_table_start
!= SQUASHFS_INVALID_BLK
) {
497 /* sanity check super block contents */
498 if(sBlk
.s
.xattr_id_table_start
>= sBlk
.s
.bytes_used
) {
499 ERROR("read_filesystem_tables: xattr id table start too large in super block\n");
503 if(read_xattrs_from_disk(fd
, &sBlk
.s
, no_xattrs
, &table_start
) == 0)
506 table_start
= sBlk
.s
.bytes_used
;
508 /* Read id lookup table */
510 /* Sanity check super block contents */
511 if(sBlk
.s
.id_table_start
>= table_start
) {
512 ERROR("read_filesystem_tables: id table start too large in super block\n");
516 /* there should always be at least one id */
517 if(sBlk
.s
.no_ids
== 0) {
518 ERROR("read_filesystem_tables: Bad id count in super block\n");
523 * the number of ids can never be more than double the number of inodes
524 * (the maximum is a unique uid and gid for each inode).
526 if(sBlk
.s
.no_ids
> (sBlk
.s
.inodes
* 2L)) {
527 ERROR("read_filesystem_tables: Bad id count in super block\n");
531 if(read_id_table(&table_start
) == FALSE
)
534 /* Read exports table */
535 if(sBlk
.s
.lookup_table_start
!= SQUASHFS_INVALID_BLK
) {
537 /* sanity check super block contents */
538 if(sBlk
.s
.lookup_table_start
>= table_start
) {
539 ERROR("read_filesystem_tables: lookup table start too large in super block\n");
543 if(parse_exports_table(&table_start
) == FALSE
)
547 /* Read fragment table */
548 if(sBlk
.s
.fragments
!= 0) {
550 /* Sanity check super block contents */
551 if(sBlk
.s
.fragment_table_start
>= table_start
) {
552 ERROR("read_filesystem_tables: fragment table start too large in super block\n");
556 /* The number of fragments should not exceed the number of inodes */
557 if(sBlk
.s
.fragments
> sBlk
.s
.inodes
) {
558 ERROR("read_filesystem_tables: Bad fragment count in super block\n");
562 if(read_fragment_table(&table_start
) == FALSE
)
566 * Sanity check super block contents - with 0 fragments,
567 * the fragment table should be empty
569 if(sBlk
.s
.fragment_table_start
!= table_start
) {
570 ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
575 /* Read directory table */
577 /* Sanity check super block contents */
578 if(sBlk
.s
.directory_table_start
> table_start
) {
579 ERROR("read_filesystem_tables: directory table start too large in super block\n");
583 directory_table
= read_directory_table(sBlk
.s
.directory_table_start
,
585 if(directory_table
== NULL
)
588 /* Read inode table */
590 /* Sanity check super block contents */
591 if(sBlk
.s
.inode_table_start
>= sBlk
.s
.directory_table_start
) {
592 ERROR("read_filesystem_tables: inode table start too large in super block\n");
596 inode_table
= read_inode_table(sBlk
.s
.inode_table_start
,
597 sBlk
.s
.directory_table_start
);
598 if(inode_table
== NULL
)
602 sBlk
.s
.xattr_id_table_start
= SQUASHFS_INVALID_BLK
;
604 alloc_index_table(0);
609 ERROR("File system corruption detected\n");
610 alloc_index_table(0);
616 static squashfs_operations ops
= {
617 .opendir
= squashfs_opendir
,
618 .read_fragment
= read_fragment
,
619 .read_block_list
= read_block_list
,
620 .read_inode
= read_inode