2 * Unsquash a squashfs filesystem. This is a highly compressed read only
5 * Copyright (c) 2009, 2010, 2011, 2012, 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 unsigned int *uid_table
, *guid_table
;
29 static char *inode_table
, *directory_table
;
30 static squashfs_operations ops
;
32 static void read_block_list(unsigned int *block_list
, char *block_ptr
, int blocks
)
34 unsigned short block_size
;
37 TRACE("read_block_list: blocks %d\n", blocks
);
39 for(i
= 0; i
< blocks
; i
++, block_ptr
+= 2) {
41 unsigned short sblock_size
;
42 memcpy(&sblock_size
, block_ptr
, sizeof(unsigned short));
43 SQUASHFS_SWAP_SHORTS_3((&block_size
), &sblock_size
, 1);
45 memcpy(&block_size
, block_ptr
, sizeof(unsigned short));
46 block_list
[i
] = SQUASHFS_COMPRESSED_SIZE(block_size
) |
47 (SQUASHFS_COMPRESSED(block_size
) ? 0 :
48 SQUASHFS_COMPRESSED_BIT_BLOCK
);
53 static struct inode
*read_inode(unsigned int start_block
, unsigned int offset
)
55 static union squashfs_inode_header_1 header
;
56 long long start
= sBlk
.s
.inode_table_start
+ start_block
;
57 int bytes
= lookup_entry(inode_table_hash
, start
);
58 char *block_ptr
= inode_table
+ bytes
+ offset
;
59 static struct inode i
;
61 TRACE("read_inode: reading inode [%d:%d]\n", start_block
, offset
);
64 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
68 squashfs_base_inode_header_1 sinode
;
69 memcpy(&sinode
, block_ptr
, sizeof(header
.base
));
70 SQUASHFS_SWAP_BASE_INODE_HEADER_1(&header
.base
, &sinode
,
71 sizeof(squashfs_base_inode_header_1
));
73 memcpy(&header
.base
, block_ptr
, sizeof(header
.base
));
75 i
.uid
= (uid_t
) uid_table
[(header
.base
.inode_type
- 1) /
76 SQUASHFS_TYPES
* 16 + header
.base
.uid
];
77 if(header
.base
.inode_type
== SQUASHFS_IPC_TYPE
) {
78 squashfs_ipc_inode_header_1
*inodep
= &header
.ipc
;
81 squashfs_ipc_inode_header_1 sinodep
;
82 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
83 SQUASHFS_SWAP_IPC_INODE_HEADER_1(inodep
, &sinodep
);
85 memcpy(inodep
, block_ptr
, sizeof(*inodep
));
87 if(inodep
->type
== SQUASHFS_SOCKET_TYPE
) {
88 i
.mode
= S_IFSOCK
| header
.base
.mode
;
89 i
.type
= SQUASHFS_SOCKET_TYPE
;
91 i
.mode
= S_IFIFO
| header
.base
.mode
;
92 i
.type
= SQUASHFS_FIFO_TYPE
;
94 i
.uid
= (uid_t
) uid_table
[inodep
->offset
* 16 + inodep
->uid
];
96 i
.mode
= lookup_type
[(header
.base
.inode_type
- 1) %
97 SQUASHFS_TYPES
+ 1] | header
.base
.mode
;
98 i
.type
= (header
.base
.inode_type
- 1) % SQUASHFS_TYPES
+ 1;
101 i
.xattr
= SQUASHFS_INVALID_XATTR
;
102 i
.gid
= header
.base
.guid
== 15 ? i
.uid
:
103 (uid_t
) guid_table
[header
.base
.guid
];
104 i
.time
= sBlk
.s
.mkfs_time
;
105 i
.inode_number
= inode_number
++;
108 case SQUASHFS_DIR_TYPE
: {
109 squashfs_dir_inode_header_1
*inode
= &header
.dir
;
112 squashfs_dir_inode_header_1 sinode
;
113 memcpy(&sinode
, block_ptr
, sizeof(header
.dir
));
114 SQUASHFS_SWAP_DIR_INODE_HEADER_1(inode
,
117 memcpy(inode
, block_ptr
, sizeof(header
.dir
));
119 i
.data
= inode
->file_size
;
120 i
.offset
= inode
->offset
;
121 i
.start
= inode
->start_block
;
122 i
.time
= inode
->mtime
;
125 case SQUASHFS_FILE_TYPE
: {
126 squashfs_reg_inode_header_1
*inode
= &header
.reg
;
129 squashfs_reg_inode_header_1 sinode
;
130 memcpy(&sinode
, block_ptr
, sizeof(sinode
));
131 SQUASHFS_SWAP_REG_INODE_HEADER_1(inode
,
134 memcpy(inode
, block_ptr
, sizeof(*inode
));
136 i
.data
= inode
->file_size
;
137 i
.time
= inode
->mtime
;
138 i
.blocks
= (i
.data
+ sBlk
.s
.block_size
- 1) >>
140 i
.start
= inode
->start_block
;
141 i
.block_ptr
= block_ptr
+ sizeof(*inode
);
148 case SQUASHFS_SYMLINK_TYPE
: {
149 squashfs_symlink_inode_header_1
*inodep
=
153 squashfs_symlink_inode_header_1 sinodep
;
154 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
155 SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(inodep
,
158 memcpy(inodep
, block_ptr
, sizeof(*inodep
));
160 i
.symlink
= malloc(inodep
->symlink_size
+ 1);
161 if(i
.symlink
== NULL
)
162 EXIT_UNSQUASH("read_inode: failed to malloc "
164 strncpy(i
.symlink
, block_ptr
+
165 sizeof(squashfs_symlink_inode_header_1
),
166 inodep
->symlink_size
);
167 i
.symlink
[inodep
->symlink_size
] = '\0';
168 i
.data
= inodep
->symlink_size
;
171 case SQUASHFS_BLKDEV_TYPE
:
172 case SQUASHFS_CHRDEV_TYPE
: {
173 squashfs_dev_inode_header_1
*inodep
= &header
.dev
;
176 squashfs_dev_inode_header_1 sinodep
;
177 memcpy(&sinodep
, block_ptr
, sizeof(sinodep
));
178 SQUASHFS_SWAP_DEV_INODE_HEADER_1(inodep
,
181 memcpy(inodep
, block_ptr
, sizeof(*inodep
));
183 i
.data
= inodep
->rdev
;
186 case SQUASHFS_FIFO_TYPE
:
187 case SQUASHFS_SOCKET_TYPE
: {
192 EXIT_UNSQUASH("Unknown inode type %d in "
193 " read_inode_header_1!\n",
194 header
.base
.inode_type
);
200 static struct dir
*squashfs_opendir(unsigned int block_start
, unsigned int offset
,
203 squashfs_dir_header_2 dirh
;
204 char buffer
[sizeof(squashfs_dir_entry_2
) + SQUASHFS_NAME_LEN
+ 1]
205 __attribute__((aligned
));
206 squashfs_dir_entry_2
*dire
= (squashfs_dir_entry_2
*) buffer
;
210 struct dir_ent
*new_dir
;
213 TRACE("squashfs_opendir: inode start block %d, offset %d\n",
214 block_start
, offset
);
216 *i
= read_inode(block_start
, offset
);
218 dir
= malloc(sizeof(struct dir
));
220 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
224 dir
->mode
= (*i
)->mode
;
225 dir
->uid
= (*i
)->uid
;
226 dir
->guid
= (*i
)->gid
;
227 dir
->mtime
= (*i
)->time
;
228 dir
->xattr
= (*i
)->xattr
;
233 * if the directory is empty, skip the unnecessary
234 * lookup_entry, this fixes the corner case with
235 * completely empty filesystems where lookup_entry correctly
236 * returning -1 is incorrectly treated as an error
240 start
= sBlk
.s
.directory_table_start
+ (*i
)->start
;
241 bytes
= lookup_entry(directory_table_hash
, start
);
243 EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
244 "found!\n", block_start
);
246 bytes
+= (*i
)->offset
;
247 size
= (*i
)->data
+ bytes
;
249 while(bytes
< size
) {
251 squashfs_dir_header_2 sdirh
;
252 memcpy(&sdirh
, directory_table
+ bytes
, sizeof(sdirh
));
253 SQUASHFS_SWAP_DIR_HEADER_2(&dirh
, &sdirh
);
255 memcpy(&dirh
, directory_table
+ bytes
, sizeof(dirh
));
257 dir_count
= dirh
.count
+ 1;
258 TRACE("squashfs_opendir: Read directory header @ byte position "
259 "%d, %d directory entries\n", bytes
, dir_count
);
260 bytes
+= sizeof(dirh
);
262 /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
263 if(dir_count
> SQUASHFS_DIR_COUNT
) {
264 ERROR("File system corrupted: too many entries in directory\n");
270 squashfs_dir_entry_2 sdire
;
271 memcpy(&sdire
, directory_table
+ bytes
,
273 SQUASHFS_SWAP_DIR_ENTRY_2(dire
, &sdire
);
275 memcpy(dire
, directory_table
+ bytes
,
277 bytes
+= sizeof(*dire
);
279 /* size should never be SQUASHFS_NAME_LEN or larger */
280 if(dire
->size
>= SQUASHFS_NAME_LEN
) {
281 ERROR("File system corrupted: filename too long\n");
285 memcpy(dire
->name
, directory_table
+ bytes
,
287 dire
->name
[dire
->size
+ 1] = '\0';
288 TRACE("squashfs_opendir: directory entry %s, inode "
289 "%d:%d, type %d\n", dire
->name
,
290 dirh
.start_block
, dire
->offset
, dire
->type
);
291 if((dir
->dir_count
% DIR_ENT_SIZE
) == 0) {
292 new_dir
= realloc(dir
->dirs
, (dir
->dir_count
+
293 DIR_ENT_SIZE
) * sizeof(struct dir_ent
));
295 EXIT_UNSQUASH("squashfs_opendir: "
296 "realloc failed!\n");
299 strcpy(dir
->dirs
[dir
->dir_count
].name
, dire
->name
);
300 dir
->dirs
[dir
->dir_count
].start_block
=
302 dir
->dirs
[dir
->dir_count
].offset
= dire
->offset
;
303 dir
->dirs
[dir
->dir_count
].type
= dire
->type
;
305 bytes
+= dire
->size
+ 1;
318 squashfs_operations
*read_filesystem_tables_1()
320 long long table_start
;
322 /* Read uid and gid lookup tables */
324 /* Sanity check super block contents */
326 if(sBlk
.guid_start
>= sBlk
.s
.bytes_used
) {
327 ERROR("read_filesystem_tables: gid start too large in super block\n");
331 /* In 1.x filesystems, there should never be more than 15 gids */
332 if(sBlk
.no_guids
> 15) {
333 ERROR("read_filesystem_tables: gids too large in super block\n");
337 if(read_ids(sBlk
.no_guids
, sBlk
.guid_start
, sBlk
.s
.bytes_used
, &guid_table
) == FALSE
)
340 table_start
= sBlk
.guid_start
;
342 /* no guids, guid_start should be 0 */
343 if(sBlk
.guid_start
!= 0) {
344 ERROR("read_filesystem_tables: gid start too large in super block\n");
348 table_start
= sBlk
.s
.bytes_used
;
351 if(sBlk
.uid_start
>= table_start
) {
352 ERROR("read_filesystem_tables: uid start too large in super block\n");
356 /* There should be at least one uid */
357 if(sBlk
.no_uids
== 0) {
358 ERROR("read_filesystem_tables: uid count bad in super block\n");
362 /* In 1.x filesystems, there should never be more than 48 uids */
363 if(sBlk
.no_uids
> 48) {
364 ERROR("read_filesystem_tables: uids too large in super block\n");
368 if(read_ids(sBlk
.no_uids
, sBlk
.uid_start
, table_start
, &uid_table
) == FALSE
)
371 table_start
= sBlk
.uid_start
;
373 /* Read directory table */
375 /* Sanity check super block contents */
376 if(sBlk
.s
.directory_table_start
> table_start
) {
377 ERROR("read_filesystem_tables: directory table start too large in super block\n");
381 directory_table
= read_directory_table(sBlk
.s
.directory_table_start
,
383 if(directory_table
== NULL
)
386 /* Read inode table */
388 /* Sanity check super block contents */
389 if(sBlk
.s
.inode_table_start
>= sBlk
.s
.directory_table_start
) {
390 ERROR("read_filesystem_tables: inode table start too large in super block\n");
394 inode_table
= read_inode_table(sBlk
.s
.inode_table_start
,
395 sBlk
.s
.directory_table_start
);
396 if(inode_table
== NULL
)
402 ERROR("File system corruption detected\n");
407 static squashfs_operations ops
= {
408 .opendir
= squashfs_opendir
,
409 .read_block_list
= read_block_list
,
410 .read_inode
= read_inode