2 * Unsquash a squashfs filesystem. This is a highly compressed read only
5 * Copyright (c) 2009, 2010, 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_compat.h"
28 static squashfs_fragment_entry_2
*fragment_table
;
29 static unsigned int *uid_table
, *guid_table
;
30 static char *inode_table
, *directory_table
;
31 static squashfs_operations ops
;
33 static void read_block_list(unsigned int *block_list
, char *block_ptr
, int blocks
)
35 TRACE("read_block_list: blocks %d\n", blocks
);
38 SQUASHFS_SWAP_INTS_3(block_list
, block_ptr
, blocks
);
40 memcpy(block_list
, block_ptr
, blocks
* sizeof(unsigned int));
44 static int read_fragment_table(long long *table_start
)
47 * Note on overflow limits:
48 * Size of SBlk.s.fragments is 2^32 (unsigned int)
49 * Max size of bytes is 2^32*8 or 2^35
50 * Max indexes is (2^32*8)/8K or 2^22
51 * Max length is ((2^32*8)/8K)*4 or 2^24 or 16M
54 long long bytes
= SQUASHFS_FRAGMENT_BYTES_2((long long) sBlk
.s
.fragments
);
55 int indexes
= SQUASHFS_FRAGMENT_INDEXES_2((long long) sBlk
.s
.fragments
);
56 int length
= SQUASHFS_FRAGMENT_INDEX_BYTES_2((long long) sBlk
.s
.fragments
);
57 unsigned int *fragment_table_index
;
60 * The size of the index table (length bytes) should match the
61 * table start and end points
63 if(length
!= (*table_start
- sBlk
.s
.fragment_table_start
)) {
64 ERROR("read_ids: Bad inode count in super block\n");
68 TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
69 "from 0x%llx\n", sBlk
.s
.fragments
, indexes
,
70 sBlk
.s
.fragment_table_start
);
72 fragment_table_index
= malloc(length
);
73 if(fragment_table_index
== NULL
)
74 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
75 "fragment table index\n");
77 fragment_table
= malloc(bytes
);
78 if(fragment_table
== NULL
)
79 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
83 unsigned int *sfragment_table_index
= malloc(length
);
85 if(sfragment_table_index
== NULL
)
86 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
87 "fragment table index\n");
89 res
= read_fs_bytes(fd
, sBlk
.s
.fragment_table_start
,
90 length
, sfragment_table_index
);
92 ERROR("read_fragment_table: failed to read fragment "
94 free(sfragment_table_index
);
97 SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index
,
98 sfragment_table_index
, indexes
);
99 free(sfragment_table_index
);
101 res
= read_fs_bytes(fd
, sBlk
.s
.fragment_table_start
,
102 length
, fragment_table_index
);
104 ERROR("read_fragment_table: failed to read fragment "
110 for(i
= 0; i
< indexes
; i
++) {
111 int expected
= (i
+ 1) != indexes
? SQUASHFS_METADATA_SIZE
:
112 bytes
& (SQUASHFS_METADATA_SIZE
- 1);
113 int length
= read_block(fd
, fragment_table_index
[i
], NULL
,
114 expected
, ((char *) fragment_table
) + ((long long) i
*
115 SQUASHFS_METADATA_SIZE
));
116 TRACE("Read fragment table block %d, from 0x%x, length %d\n", i
,
117 fragment_table_index
[i
], length
);
118 if(length
== FALSE
) {
119 ERROR("read_fragment_table: failed to read fragment "
126 squashfs_fragment_entry_2 sfragment
;
127 for(i
= 0; i
< sBlk
.s
.fragments
; i
++) {
128 SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment
),
129 (&fragment_table
[i
]));
130 memcpy((char *) &fragment_table
[i
], (char *) &sfragment
,
131 sizeof(squashfs_fragment_entry_2
));
135 *table_start
= fragment_table_index
[0];
136 free(fragment_table_index
);
141 free(fragment_table_index
);
146 static void read_fragment(unsigned int fragment
, long long *start_block
, int *size
)
148 TRACE("read_fragment: reading fragment %d\n", fragment
);
150 squashfs_fragment_entry_2
*fragment_entry
= &fragment_table
[fragment
];
151 *start_block
= fragment_entry
->start_block
;
152 *size
= fragment_entry
->size
;
156 static struct inode
*read_inode(unsigned int start_block
, unsigned int offset
)
158 static union squashfs_inode_header_2 header
;
159 long long start
= sBlk
.s
.inode_table_start
+ start_block
;
160 int bytes
= lookup_entry(inode_table_hash
, start
);
161 char *block_ptr
= inode_table
+ bytes
+ offset
;
162 static struct inode i
;
164 TRACE("read_inode: reading inode [%d:%d]\n", start_block
, offset
);
167 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
171 squashfs_base_inode_header_2 sinode
;
172 memcpy(&sinode
, block_ptr
, sizeof(header
.base
));
173 SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header
.base
, &sinode
,
174 sizeof(squashfs_base_inode_header_2
));
176 memcpy(&header
.base
, block_ptr
, sizeof(header
.base
));
178 i
.xattr
= SQUASHFS_INVALID_XATTR
;
179 i
.uid
= (uid_t
) uid_table
[header
.base
.uid
];
180 i
.gid
= header
.base
.guid
== SQUASHFS_GUIDS
? i
.uid
:
181 (uid_t
) guid_table
[header
.base
.guid
];
182 i
.mode
= lookup_type
[header
.base
.inode_type
] | header
.base
.mode
;
183 i
.type
= header
.base
.inode_type
;
184 i
.time
= sBlk
.s
.mkfs_time
;
185 i
.inode_number
= inode_number
++;
187 switch(header
.base
.inode_type
) {
188 case SQUASHFS_DIR_TYPE
: {
189 squashfs_dir_inode_header_2
*inode
= &header
.dir
;
192 squashfs_dir_inode_header_2 sinode
;
193 memcpy(&sinode
, block_ptr
, sizeof(header
.dir
));
194 SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header
.dir
,
197 memcpy(&header
.dir
, block_ptr
,
200 i
.data
= inode
->file_size
;
201 i
.offset
= inode
->offset
;
202 i
.start
= inode
->start_block
;
203 i
.time
= inode
->mtime
;
206 case SQUASHFS_LDIR_TYPE
: {
207 squashfs_ldir_inode_header_2
*inode
= &header
.ldir
;
210 squashfs_ldir_inode_header_2 sinode
;
211 memcpy(&sinode
, block_ptr
, sizeof(header
.ldir
));
212 SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header
.ldir
,
215 memcpy(&header
.ldir
, block_ptr
,
216 sizeof(header
.ldir
));
218 i
.data
= inode
->file_size
;
219 i
.offset
= inode
->offset
;
220 i
.start
= inode
->start_block
;
221 i
.time
= inode
->mtime
;
224 case SQUASHFS_FILE_TYPE
: {
225 squashfs_reg_inode_header_2
*inode
= &header
.reg
;
228 squashfs_reg_inode_header_2 sinode
;
229 memcpy(&sinode
, block_ptr
, sizeof(sinode
));
230 SQUASHFS_SWAP_REG_INODE_HEADER_2(inode
,
233 memcpy(inode
, block_ptr
, sizeof(*inode
));
235 i
.data
= inode
->file_size
;
236 i
.time
= inode
->mtime
;
237 i
.frag_bytes
= inode
->fragment
== SQUASHFS_INVALID_FRAG
238 ? 0 : inode
->file_size
% sBlk
.s
.block_size
;
239 i
.fragment
= inode
->fragment
;
240 i
.offset
= inode
->offset
;
241 i
.blocks
= inode
->fragment
== SQUASHFS_INVALID_FRAG
?
242 (i
.data
+ sBlk
.s
.block_size
- 1) >>
243 sBlk
.s
.block_log
: i
.data
>>
245 i
.start
= inode
->start_block
;
247 i
.block_ptr
= block_ptr
+ sizeof(*inode
);
250 case SQUASHFS_SYMLINK_TYPE
: {
251 squashfs_symlink_inode_header_2
*inodep
=
255 squashfs_symlink_inode_header_2 sinodep
;
256 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
257 SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep
,
260 memcpy(inodep
, block_ptr
, sizeof(*inodep
));
262 i
.symlink
= malloc(inodep
->symlink_size
+ 1);
263 if(i
.symlink
== NULL
)
264 EXIT_UNSQUASH("read_inode: failed to malloc "
266 strncpy(i
.symlink
, block_ptr
+
267 sizeof(squashfs_symlink_inode_header_2
),
268 inodep
->symlink_size
);
269 i
.symlink
[inodep
->symlink_size
] = '\0';
270 i
.data
= inodep
->symlink_size
;
273 case SQUASHFS_BLKDEV_TYPE
:
274 case SQUASHFS_CHRDEV_TYPE
: {
275 squashfs_dev_inode_header_2
*inodep
= &header
.dev
;
278 squashfs_dev_inode_header_2 sinodep
;
279 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
280 SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep
,
283 memcpy(inodep
, block_ptr
, sizeof(*inodep
));
285 i
.data
= inodep
->rdev
;
288 case SQUASHFS_FIFO_TYPE
:
289 case SQUASHFS_SOCKET_TYPE
:
293 EXIT_UNSQUASH("Unknown inode type %d in "
294 "read_inode_header_2!\n",
295 header
.base
.inode_type
);
301 static struct dir
*squashfs_opendir(unsigned int block_start
, unsigned int offset
,
304 squashfs_dir_header_2 dirh
;
305 char buffer
[sizeof(squashfs_dir_entry_2
) + SQUASHFS_NAME_LEN
+ 1]
306 __attribute__((aligned
));
307 squashfs_dir_entry_2
*dire
= (squashfs_dir_entry_2
*) buffer
;
311 struct dir_ent
*new_dir
;
314 TRACE("squashfs_opendir: inode start block %d, offset %d\n",
315 block_start
, offset
);
317 *i
= read_inode(block_start
, offset
);
319 dir
= malloc(sizeof(struct dir
));
321 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
325 dir
->mode
= (*i
)->mode
;
326 dir
->uid
= (*i
)->uid
;
327 dir
->guid
= (*i
)->gid
;
328 dir
->mtime
= (*i
)->time
;
329 dir
->xattr
= (*i
)->xattr
;
334 * if the directory is empty, skip the unnecessary
335 * lookup_entry, this fixes the corner case with
336 * completely empty filesystems where lookup_entry correctly
337 * returning -1 is incorrectly treated as an error
341 start
= sBlk
.s
.directory_table_start
+ (*i
)->start
;
342 bytes
= lookup_entry(directory_table_hash
, start
);
344 EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
345 "found!\n", block_start
);
347 bytes
+= (*i
)->offset
;
348 size
= (*i
)->data
+ bytes
;
350 while(bytes
< size
) {
352 squashfs_dir_header_2 sdirh
;
353 memcpy(&sdirh
, directory_table
+ bytes
, sizeof(sdirh
));
354 SQUASHFS_SWAP_DIR_HEADER_2(&dirh
, &sdirh
);
356 memcpy(&dirh
, directory_table
+ bytes
, sizeof(dirh
));
358 dir_count
= dirh
.count
+ 1;
359 TRACE("squashfs_opendir: Read directory header @ byte position "
360 "%d, %d directory entries\n", bytes
, dir_count
);
361 bytes
+= sizeof(dirh
);
363 /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
364 if(dir_count
> SQUASHFS_DIR_COUNT
) {
365 ERROR("File system corrupted: too many entries in directory\n");
371 squashfs_dir_entry_2 sdire
;
372 memcpy(&sdire
, directory_table
+ bytes
,
374 SQUASHFS_SWAP_DIR_ENTRY_2(dire
, &sdire
);
376 memcpy(dire
, directory_table
+ bytes
,
378 bytes
+= sizeof(*dire
);
380 /* size should never be SQUASHFS_NAME_LEN or larger */
381 if(dire
->size
>= SQUASHFS_NAME_LEN
) {
382 ERROR("File system corrupted: filename too long\n");
386 memcpy(dire
->name
, directory_table
+ bytes
,
388 dire
->name
[dire
->size
+ 1] = '\0';
389 TRACE("squashfs_opendir: directory entry %s, inode "
390 "%d:%d, type %d\n", dire
->name
,
391 dirh
.start_block
, dire
->offset
, dire
->type
);
392 if((dir
->dir_count
% DIR_ENT_SIZE
) == 0) {
393 new_dir
= realloc(dir
->dirs
, (dir
->dir_count
+
394 DIR_ENT_SIZE
) * sizeof(struct dir_ent
));
396 EXIT_UNSQUASH("squashfs_opendir: "
397 "realloc failed!\n");
400 strcpy(dir
->dirs
[dir
->dir_count
].name
, dire
->name
);
401 dir
->dirs
[dir
->dir_count
].start_block
=
403 dir
->dirs
[dir
->dir_count
].offset
= dire
->offset
;
404 dir
->dirs
[dir
->dir_count
].type
= dire
->type
;
406 bytes
+= dire
->size
+ 1;
419 squashfs_operations
*read_filesystem_tables_2()
421 long long table_start
;
423 /* Read uid and gid lookup tables */
425 /* Sanity check super block contents */
427 if(sBlk
.guid_start
>= sBlk
.s
.bytes_used
) {
428 ERROR("read_filesystem_tables: gid start too large in super block\n");
432 if(read_ids(sBlk
.no_guids
, sBlk
.guid_start
, sBlk
.s
.bytes_used
, &guid_table
) == FALSE
)
435 table_start
= sBlk
.guid_start
;
437 /* no guids, guid_start should be 0 */
438 if(sBlk
.guid_start
!= 0) {
439 ERROR("read_filesystem_tables: gid start too large in super block\n");
443 table_start
= sBlk
.s
.bytes_used
;
446 if(sBlk
.uid_start
>= table_start
) {
447 ERROR("read_filesystem_tables: uid start too large in super block\n");
451 /* There should be at least one uid */
452 if(sBlk
.no_uids
== 0) {
453 ERROR("read_filesystem_tables: uid count bad in super block\n");
457 if(read_ids(sBlk
.no_uids
, sBlk
.uid_start
, table_start
, &uid_table
) == FALSE
)
460 table_start
= sBlk
.uid_start
;
462 /* Read fragment table */
463 if(sBlk
.s
.fragments
!= 0) {
465 /* Sanity check super block contents */
466 if(sBlk
.s
.fragment_table_start
>= table_start
) {
467 ERROR("read_filesystem_tables: fragment table start too large in super block\n");
471 /* The number of fragments should not exceed the number of inodes */
472 if(sBlk
.s
.fragments
> sBlk
.s
.inodes
) {
473 ERROR("read_filesystem_tables: Bad fragment count in super block\n");
477 if(read_fragment_table(&table_start
) == FALSE
)
481 * Sanity check super block contents - with 0 fragments,
482 * the fragment table should be empty
484 if(sBlk
.s
.fragment_table_start
!= table_start
) {
485 ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
490 /* Read directory table */
492 /* Sanity check super block contents */
493 if(sBlk
.s
.directory_table_start
> table_start
) {
494 ERROR("read_filesystem_tables: directory table start too large in super block\n");
498 directory_table
= read_directory_table(sBlk
.s
.directory_table_start
,
500 if(directory_table
== NULL
)
503 /* Read inode table */
505 /* Sanity check super block contents */
506 if(sBlk
.s
.inode_table_start
>= sBlk
.s
.directory_table_start
) {
507 ERROR("read_filesystem_tables: inode table start too large in super block\n");
511 inode_table
= read_inode_table(sBlk
.s
.inode_table_start
,
512 sBlk
.s
.directory_table_start
);
513 if(inode_table
== NULL
)
519 ERROR("File system corruption detected\n");
524 static squashfs_operations ops
= {
525 .opendir
= squashfs_opendir
,
526 .read_fragment
= read_fragment
,
527 .read_block_list
= read_block_list
,
528 .read_inode
= read_inode