]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-2.c
1.1.07 release
[Ventoy.git] / SQUASHFS / squashfs-tools-4.4 / squashfs-tools / unsquash-2.c
1 /*
2 * Unsquash a squashfs filesystem. This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2009, 2010, 2013, 2019
6 * Phillip Lougher <phillip@squashfs.org.uk>
7 *
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.
12 *
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.
17 *
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.
21 *
22 * unsquash-2.c
23 */
24
25 #include "unsquashfs.h"
26 #include "squashfs_compat.h"
27
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;
32
33 static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
34 {
35 TRACE("read_block_list: blocks %d\n", blocks);
36
37 if(swap) {
38 SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
39 } else
40 memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
41 }
42
43
44 static int read_fragment_table(long long *table_start)
45 {
46 /*
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
52 */
53 int res, i;
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;
58
59 /*
60 * The size of the index table (length bytes) should match the
61 * table start and end points
62 */
63 if(length != (*table_start- sBlk.s.fragment_table_start)) {
64 ERROR("read_ids: Bad inode count in super block\n");
65 return FALSE;
66 }
67
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);
71
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");
76
77 fragment_table = malloc(bytes);
78 if(fragment_table == NULL)
79 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
80 "fragment table\n");
81
82 if(swap) {
83 unsigned int *sfragment_table_index = malloc(length);
84
85 if(sfragment_table_index == NULL)
86 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
87 "fragment table index\n");
88
89 res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
90 length, sfragment_table_index);
91 if(res == FALSE) {
92 ERROR("read_fragment_table: failed to read fragment "
93 "table index\n");
94 free(sfragment_table_index);
95 goto failed;
96 }
97 SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index,
98 sfragment_table_index, indexes);
99 free(sfragment_table_index);
100 } else {
101 res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
102 length, fragment_table_index);
103 if(res == FALSE) {
104 ERROR("read_fragment_table: failed to read fragment "
105 "table index\n");
106 goto failed;
107 }
108 }
109
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 "
120 "table block\n");
121 goto failed;
122 }
123 }
124
125 if(swap) {
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));
132 }
133 }
134
135 *table_start = fragment_table_index[0];
136 free(fragment_table_index);
137
138 return TRUE;
139
140 failed:
141 free(fragment_table_index);
142 return FALSE;
143 }
144
145
146 static void read_fragment(unsigned int fragment, long long *start_block, int *size)
147 {
148 TRACE("read_fragment: reading fragment %d\n", fragment);
149
150 squashfs_fragment_entry_2 *fragment_entry = &fragment_table[fragment];
151 *start_block = fragment_entry->start_block;
152 *size = fragment_entry->size;
153 }
154
155
156 static struct inode *read_inode(unsigned int start_block, unsigned int offset)
157 {
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;
163
164 TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
165
166 if(bytes == -1)
167 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
168 start);
169
170 if(swap) {
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));
175 } else
176 memcpy(&header.base, block_ptr, sizeof(header.base));
177
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++;
186
187 switch(header.base.inode_type) {
188 case SQUASHFS_DIR_TYPE: {
189 squashfs_dir_inode_header_2 *inode = &header.dir;
190
191 if(swap) {
192 squashfs_dir_inode_header_2 sinode;
193 memcpy(&sinode, block_ptr, sizeof(header.dir));
194 SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir,
195 &sinode);
196 } else
197 memcpy(&header.dir, block_ptr,
198 sizeof(header.dir));
199
200 i.data = inode->file_size;
201 i.offset = inode->offset;
202 i.start = inode->start_block;
203 i.time = inode->mtime;
204 break;
205 }
206 case SQUASHFS_LDIR_TYPE: {
207 squashfs_ldir_inode_header_2 *inode = &header.ldir;
208
209 if(swap) {
210 squashfs_ldir_inode_header_2 sinode;
211 memcpy(&sinode, block_ptr, sizeof(header.ldir));
212 SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir,
213 &sinode);
214 } else
215 memcpy(&header.ldir, block_ptr,
216 sizeof(header.ldir));
217
218 i.data = inode->file_size;
219 i.offset = inode->offset;
220 i.start = inode->start_block;
221 i.time = inode->mtime;
222 break;
223 }
224 case SQUASHFS_FILE_TYPE: {
225 squashfs_reg_inode_header_2 *inode = &header.reg;
226
227 if(swap) {
228 squashfs_reg_inode_header_2 sinode;
229 memcpy(&sinode, block_ptr, sizeof(sinode));
230 SQUASHFS_SWAP_REG_INODE_HEADER_2(inode,
231 &sinode);
232 } else
233 memcpy(inode, block_ptr, sizeof(*inode));
234
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 >>
244 sBlk.s.block_log;
245 i.start = inode->start_block;
246 i.sparse = 0;
247 i.block_ptr = block_ptr + sizeof(*inode);
248 break;
249 }
250 case SQUASHFS_SYMLINK_TYPE: {
251 squashfs_symlink_inode_header_2 *inodep =
252 &header.symlink;
253
254 if(swap) {
255 squashfs_symlink_inode_header_2 sinodep;
256 memcpy(&sinodep, block_ptr, sizeof(sinodep));
257 SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
258 &sinodep);
259 } else
260 memcpy(inodep, block_ptr, sizeof(*inodep));
261
262 i.symlink = malloc(inodep->symlink_size + 1);
263 if(i.symlink == NULL)
264 EXIT_UNSQUASH("read_inode: failed to malloc "
265 "symlink data\n");
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;
271 break;
272 }
273 case SQUASHFS_BLKDEV_TYPE:
274 case SQUASHFS_CHRDEV_TYPE: {
275 squashfs_dev_inode_header_2 *inodep = &header.dev;
276
277 if(swap) {
278 squashfs_dev_inode_header_2 sinodep;
279 memcpy(&sinodep, block_ptr, sizeof(sinodep));
280 SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep,
281 &sinodep);
282 } else
283 memcpy(inodep, block_ptr, sizeof(*inodep));
284
285 i.data = inodep->rdev;
286 break;
287 }
288 case SQUASHFS_FIFO_TYPE:
289 case SQUASHFS_SOCKET_TYPE:
290 i.data = 0;
291 break;
292 default:
293 EXIT_UNSQUASH("Unknown inode type %d in "
294 "read_inode_header_2!\n",
295 header.base.inode_type);
296 }
297 return &i;
298 }
299
300
301 static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
302 struct inode **i)
303 {
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;
308 long long start;
309 int bytes;
310 int dir_count, size;
311 struct dir_ent *new_dir;
312 struct dir *dir;
313
314 TRACE("squashfs_opendir: inode start block %d, offset %d\n",
315 block_start, offset);
316
317 *i = read_inode(block_start, offset);
318
319 dir = malloc(sizeof(struct dir));
320 if(dir == NULL)
321 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
322
323 dir->dir_count = 0;
324 dir->cur_entry = 0;
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;
330 dir->dirs = NULL;
331
332 if ((*i)->data == 0)
333 /*
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
338 */
339 return dir;
340
341 start = sBlk.s.directory_table_start + (*i)->start;
342 bytes = lookup_entry(directory_table_hash, start);
343 if(bytes == -1)
344 EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
345 "found!\n", block_start);
346
347 bytes += (*i)->offset;
348 size = (*i)->data + bytes;
349
350 while(bytes < size) {
351 if(swap) {
352 squashfs_dir_header_2 sdirh;
353 memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
354 SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
355 } else
356 memcpy(&dirh, directory_table + bytes, sizeof(dirh));
357
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);
362
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");
366 goto corrupted;
367 }
368
369 while(dir_count--) {
370 if(swap) {
371 squashfs_dir_entry_2 sdire;
372 memcpy(&sdire, directory_table + bytes,
373 sizeof(sdire));
374 SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
375 } else
376 memcpy(dire, directory_table + bytes,
377 sizeof(*dire));
378 bytes += sizeof(*dire);
379
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");
383 goto corrupted;
384 }
385
386 memcpy(dire->name, directory_table + bytes,
387 dire->size + 1);
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));
395 if(new_dir == NULL)
396 EXIT_UNSQUASH("squashfs_opendir: "
397 "realloc failed!\n");
398 dir->dirs = new_dir;
399 }
400 strcpy(dir->dirs[dir->dir_count].name, dire->name);
401 dir->dirs[dir->dir_count].start_block =
402 dirh.start_block;
403 dir->dirs[dir->dir_count].offset = dire->offset;
404 dir->dirs[dir->dir_count].type = dire->type;
405 dir->dir_count ++;
406 bytes += dire->size + 1;
407 }
408 }
409
410 return dir;
411
412 corrupted:
413 free(dir->dirs);
414 free(dir);
415 return NULL;
416 }
417
418
419 squashfs_operations *read_filesystem_tables_2()
420 {
421 long long table_start;
422
423 /* Read uid and gid lookup tables */
424
425 /* Sanity check super block contents */
426 if(sBlk.no_guids) {
427 if(sBlk.guid_start >= sBlk.s.bytes_used) {
428 ERROR("read_filesystem_tables: gid start too large in super block\n");
429 goto corrupted;
430 }
431
432 if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
433 goto corrupted;
434
435 table_start = sBlk.guid_start;
436 } else {
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");
440 goto corrupted;
441 }
442
443 table_start = sBlk.s.bytes_used;
444 }
445
446 if(sBlk.uid_start >= table_start) {
447 ERROR("read_filesystem_tables: uid start too large in super block\n");
448 goto corrupted;
449 }
450
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");
454 goto corrupted;
455 }
456
457 if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
458 goto corrupted;
459
460 table_start = sBlk.uid_start;
461
462 /* Read fragment table */
463 if(sBlk.s.fragments != 0) {
464
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");
468 goto corrupted;
469 }
470
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");
474 goto corrupted;
475 }
476
477 if(read_fragment_table(&table_start) == FALSE)
478 goto corrupted;
479 } else {
480 /*
481 * Sanity check super block contents - with 0 fragments,
482 * the fragment table should be empty
483 */
484 if(sBlk.s.fragment_table_start != table_start) {
485 ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
486 goto corrupted;
487 }
488 }
489
490 /* Read directory table */
491
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");
495 goto corrupted;
496 }
497
498 directory_table = read_directory_table(sBlk.s.directory_table_start,
499 table_start);
500 if(directory_table == NULL)
501 goto corrupted;
502
503 /* Read inode table */
504
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");
508 goto corrupted;
509 }
510
511 inode_table = read_inode_table(sBlk.s.inode_table_start,
512 sBlk.s.directory_table_start);
513 if(inode_table == NULL)
514 goto corrupted;
515
516 return &ops;
517
518 corrupted:
519 ERROR("File system corruption detected\n");
520 return NULL;
521 }
522
523
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
529 };