]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-3.c
update languages.ini (#829 #834)
[Ventoy.git] / SQUASHFS / squashfs-tools-4.4 / squashfs-tools / unsquash-3.c
1 /*
2 * Unsquash a squashfs filesystem. This is a highly compressed read only
3 * filesystem.
4 *
5 * Copyright (c) 2009, 2010, 2011, 2012, 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-3.c
23 */
24
25 #include "unsquashfs.h"
26 #include "squashfs_compat.h"
27
28 static squashfs_fragment_entry_3 *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 long long *salloc_index_table(int indexes)
34 {
35 static long long *alloc_table = NULL;
36 static int alloc_size = 0;
37 int length = indexes * sizeof(long long);
38
39 if(alloc_size < length || length == 0) {
40 long long *table = realloc(alloc_table, length);
41
42 if(table == NULL && length !=0 )
43 EXIT_UNSQUASH("alloc_index_table: failed to allocate "
44 "index table\n");
45
46 alloc_table = table;
47 alloc_size = length;
48 }
49
50 return alloc_table;
51 }
52
53
54 static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
55 {
56 TRACE("read_block_list: blocks %d\n", blocks);
57
58 if(swap) {
59 SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
60 } else
61 memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
62 }
63
64
65 static int read_fragment_table(long long *table_start)
66 {
67 /*
68 * Note on overflow limits:
69 * Size of SBlk.s.fragments is 2^32 (unsigned int)
70 * Max size of bytes is 2^32*16 or 2^36
71 * Max indexes is (2^32*16)/8K or 2^23
72 * Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
73 */
74 int res, i;
75 long long bytes = SQUASHFS_FRAGMENT_BYTES_3((long long) sBlk.s.fragments);
76 int indexes = SQUASHFS_FRAGMENT_INDEXES_3((long long) sBlk.s.fragments);
77 int length = SQUASHFS_FRAGMENT_INDEX_BYTES_3((long long) sBlk.s.fragments);
78 long long *fragment_table_index;
79
80 /*
81 * The size of the index table (length bytes) should match the
82 * table start and end points
83 */
84 if(length != (*table_start - sBlk.s.fragment_table_start)) {
85 ERROR("read_fragment_table: Bad fragment count in super block\n");
86 return FALSE;
87 }
88
89 TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
90 "from 0x%llx\n", sBlk.s.fragments, indexes,
91 sBlk.s.fragment_table_start);
92
93 fragment_table_index = alloc_index_table(indexes);
94 fragment_table = malloc(bytes);
95 if(fragment_table == NULL)
96 EXIT_UNSQUASH("read_fragment_table: failed to allocate "
97 "fragment table\n");
98
99 if(swap) {
100 long long *sfragment_table_index = salloc_index_table(indexes);
101
102 res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
103 length, sfragment_table_index);
104 if(res == FALSE) {
105 ERROR("read_fragment_table: failed to read fragment "
106 "table index\n");
107 return FALSE;
108 }
109 SQUASHFS_SWAP_FRAGMENT_INDEXES_3(fragment_table_index,
110 sfragment_table_index, indexes);
111 } else {
112 res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
113 length, fragment_table_index);
114 if(res == FALSE) {
115 ERROR("read_fragment_table: failed to read fragment "
116 "table index\n");
117 return FALSE;
118 }
119 }
120
121 for(i = 0; i < indexes; i++) {
122 int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
123 bytes & (SQUASHFS_METADATA_SIZE - 1);
124 int length = read_block(fd, fragment_table_index[i], NULL,
125 expected, ((char *) fragment_table) + ((long long) i *
126 SQUASHFS_METADATA_SIZE));
127 TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
128 i, fragment_table_index[i], length);
129 if(length == FALSE) {
130 ERROR("read_fragment_table: failed to read fragment "
131 "table block\n");
132 return FALSE;
133 }
134 }
135
136 if(swap) {
137 squashfs_fragment_entry_3 sfragment;
138 for(i = 0; i < sBlk.s.fragments; i++) {
139 SQUASHFS_SWAP_FRAGMENT_ENTRY_3((&sfragment),
140 (&fragment_table[i]));
141 memcpy((char *) &fragment_table[i], (char *) &sfragment,
142 sizeof(squashfs_fragment_entry_3));
143 }
144 }
145
146 *table_start = fragment_table_index[0];
147 return TRUE;
148 }
149
150
151 static void read_fragment(unsigned int fragment, long long *start_block, int *size)
152 {
153 TRACE("read_fragment: reading fragment %d\n", fragment);
154
155 squashfs_fragment_entry_3 *fragment_entry = &fragment_table[fragment];
156 *start_block = fragment_entry->start_block;
157 *size = fragment_entry->size;
158 }
159
160
161 static struct inode *read_inode(unsigned int start_block, unsigned int offset)
162 {
163 static union squashfs_inode_header_3 header;
164 long long start = sBlk.s.inode_table_start + start_block;
165 int bytes = lookup_entry(inode_table_hash, start);
166 char *block_ptr = inode_table + bytes + offset;
167 static struct inode i;
168
169 TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
170
171 if(bytes == -1)
172 EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
173 start);
174
175 if(swap) {
176 squashfs_base_inode_header_3 sinode;
177 memcpy(&sinode, block_ptr, sizeof(header.base));
178 SQUASHFS_SWAP_BASE_INODE_HEADER_3(&header.base, &sinode,
179 sizeof(squashfs_base_inode_header_3));
180 } else
181 memcpy(&header.base, block_ptr, sizeof(header.base));
182
183 i.xattr = SQUASHFS_INVALID_XATTR;
184 i.uid = (uid_t) uid_table[header.base.uid];
185 i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
186 (uid_t) guid_table[header.base.guid];
187 i.mode = lookup_type[header.base.inode_type] | header.base.mode;
188 i.type = header.base.inode_type;
189 i.time = header.base.mtime;
190 i.inode_number = header.base.inode_number;
191
192 switch(header.base.inode_type) {
193 case SQUASHFS_DIR_TYPE: {
194 squashfs_dir_inode_header_3 *inode = &header.dir;
195
196 if(swap) {
197 squashfs_dir_inode_header_3 sinode;
198 memcpy(&sinode, block_ptr, sizeof(header.dir));
199 SQUASHFS_SWAP_DIR_INODE_HEADER_3(&header.dir,
200 &sinode);
201 } else
202 memcpy(&header.dir, block_ptr,
203 sizeof(header.dir));
204
205 i.data = inode->file_size;
206 i.offset = inode->offset;
207 i.start = inode->start_block;
208 break;
209 }
210 case SQUASHFS_LDIR_TYPE: {
211 squashfs_ldir_inode_header_3 *inode = &header.ldir;
212
213 if(swap) {
214 squashfs_ldir_inode_header_3 sinode;
215 memcpy(&sinode, block_ptr, sizeof(header.ldir));
216 SQUASHFS_SWAP_LDIR_INODE_HEADER_3(&header.ldir,
217 &sinode);
218 } else
219 memcpy(&header.ldir, block_ptr,
220 sizeof(header.ldir));
221
222 i.data = inode->file_size;
223 i.offset = inode->offset;
224 i.start = inode->start_block;
225 break;
226 }
227 case SQUASHFS_FILE_TYPE: {
228 squashfs_reg_inode_header_3 *inode = &header.reg;
229
230 if(swap) {
231 squashfs_reg_inode_header_3 sinode;
232 memcpy(&sinode, block_ptr, sizeof(sinode));
233 SQUASHFS_SWAP_REG_INODE_HEADER_3(inode,
234 &sinode);
235 } else
236 memcpy(inode, block_ptr, sizeof(*inode));
237
238 i.data = inode->file_size;
239 i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
240 ? 0 : inode->file_size % sBlk.s.block_size;
241 i.fragment = inode->fragment;
242 i.offset = inode->offset;
243 i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
244 (i.data + sBlk.s.block_size - 1) >>
245 sBlk.s.block_log :
246 i.data >> sBlk.s.block_log;
247 i.start = inode->start_block;
248 i.sparse = 1;
249 i.block_ptr = block_ptr + sizeof(*inode);
250 break;
251 }
252 case SQUASHFS_LREG_TYPE: {
253 squashfs_lreg_inode_header_3 *inode = &header.lreg;
254
255 if(swap) {
256 squashfs_lreg_inode_header_3 sinode;
257 memcpy(&sinode, block_ptr, sizeof(sinode));
258 SQUASHFS_SWAP_LREG_INODE_HEADER_3(inode,
259 &sinode);
260 } else
261 memcpy(inode, block_ptr, sizeof(*inode));
262
263 i.data = inode->file_size;
264 i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
265 ? 0 : inode->file_size % sBlk.s.block_size;
266 i.fragment = inode->fragment;
267 i.offset = inode->offset;
268 i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
269 (inode->file_size + sBlk.s.block_size - 1) >>
270 sBlk.s.block_log :
271 inode->file_size >> sBlk.s.block_log;
272 i.start = inode->start_block;
273 i.sparse = 1;
274 i.block_ptr = block_ptr + sizeof(*inode);
275 break;
276 }
277 case SQUASHFS_SYMLINK_TYPE: {
278 squashfs_symlink_inode_header_3 *inodep =
279 &header.symlink;
280
281 if(swap) {
282 squashfs_symlink_inode_header_3 sinodep;
283 memcpy(&sinodep, block_ptr, sizeof(sinodep));
284 SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(inodep,
285 &sinodep);
286 } else
287 memcpy(inodep, block_ptr, sizeof(*inodep));
288
289 i.symlink = malloc(inodep->symlink_size + 1);
290 if(i.symlink == NULL)
291 EXIT_UNSQUASH("read_inode: failed to malloc "
292 "symlink data\n");
293 strncpy(i.symlink, block_ptr +
294 sizeof(squashfs_symlink_inode_header_3),
295 inodep->symlink_size);
296 i.symlink[inodep->symlink_size] = '\0';
297 i.data = inodep->symlink_size;
298 break;
299 }
300 case SQUASHFS_BLKDEV_TYPE:
301 case SQUASHFS_CHRDEV_TYPE: {
302 squashfs_dev_inode_header_3 *inodep = &header.dev;
303
304 if(swap) {
305 squashfs_dev_inode_header_3 sinodep;
306 memcpy(&sinodep, block_ptr, sizeof(sinodep));
307 SQUASHFS_SWAP_DEV_INODE_HEADER_3(inodep,
308 &sinodep);
309 } else
310 memcpy(inodep, block_ptr, sizeof(*inodep));
311
312 i.data = inodep->rdev;
313 break;
314 }
315 case SQUASHFS_FIFO_TYPE:
316 case SQUASHFS_SOCKET_TYPE:
317 i.data = 0;
318 break;
319 default:
320 EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
321 header.base.inode_type);
322 }
323 return &i;
324 }
325
326
327 static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
328 struct inode **i)
329 {
330 squashfs_dir_header_3 dirh;
331 char buffer[sizeof(squashfs_dir_entry_3) + SQUASHFS_NAME_LEN + 1]
332 __attribute__((aligned));
333 squashfs_dir_entry_3 *dire = (squashfs_dir_entry_3 *) buffer;
334 long long start;
335 int bytes;
336 int dir_count, size;
337 struct dir_ent *new_dir;
338 struct dir *dir;
339
340 TRACE("squashfs_opendir: inode start block %d, offset %d\n",
341 block_start, offset);
342
343 *i = read_inode(block_start, offset);
344
345 dir = malloc(sizeof(struct dir));
346 if(dir == NULL)
347 EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
348
349 dir->dir_count = 0;
350 dir->cur_entry = 0;
351 dir->mode = (*i)->mode;
352 dir->uid = (*i)->uid;
353 dir->guid = (*i)->gid;
354 dir->mtime = (*i)->time;
355 dir->xattr = (*i)->xattr;
356 dir->dirs = NULL;
357
358 if ((*i)->data == 3)
359 /*
360 * if the directory is empty, skip the unnecessary
361 * lookup_entry, this fixes the corner case with
362 * completely empty filesystems where lookup_entry correctly
363 * returning -1 is incorrectly treated as an error
364 */
365 return dir;
366
367 start = sBlk.s.directory_table_start + (*i)->start;
368 bytes = lookup_entry(directory_table_hash, start);
369
370 if(bytes == -1)
371 EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
372 "found!\n", block_start);
373
374 bytes += (*i)->offset;
375 size = (*i)->data + bytes - 3;
376
377 while(bytes < size) {
378 if(swap) {
379 squashfs_dir_header_3 sdirh;
380 memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
381 SQUASHFS_SWAP_DIR_HEADER_3(&dirh, &sdirh);
382 } else
383 memcpy(&dirh, directory_table + bytes, sizeof(dirh));
384
385 dir_count = dirh.count + 1;
386 TRACE("squashfs_opendir: Read directory header @ byte position "
387 "%d, %d directory entries\n", bytes, dir_count);
388 bytes += sizeof(dirh);
389
390 /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
391 if(dir_count > SQUASHFS_DIR_COUNT) {
392 ERROR("File system corrupted: too many entries in directory\n");
393 goto corrupted;
394 }
395
396 while(dir_count--) {
397 if(swap) {
398 squashfs_dir_entry_3 sdire;
399 memcpy(&sdire, directory_table + bytes,
400 sizeof(sdire));
401 SQUASHFS_SWAP_DIR_ENTRY_3(dire, &sdire);
402 } else
403 memcpy(dire, directory_table + bytes,
404 sizeof(*dire));
405 bytes += sizeof(*dire);
406
407 /* size should never be SQUASHFS_NAME_LEN or larger */
408 if(dire->size >= SQUASHFS_NAME_LEN) {
409 ERROR("File system corrupted: filename too long\n");
410 goto corrupted;
411 }
412
413 memcpy(dire->name, directory_table + bytes,
414 dire->size + 1);
415 dire->name[dire->size + 1] = '\0';
416 TRACE("squashfs_opendir: directory entry %s, inode "
417 "%d:%d, type %d\n", dire->name,
418 dirh.start_block, dire->offset, dire->type);
419 if((dir->dir_count % DIR_ENT_SIZE) == 0) {
420 new_dir = realloc(dir->dirs, (dir->dir_count +
421 DIR_ENT_SIZE) * sizeof(struct dir_ent));
422 if(new_dir == NULL)
423 EXIT_UNSQUASH("squashfs_opendir: "
424 "realloc failed!\n");
425 dir->dirs = new_dir;
426 }
427 strcpy(dir->dirs[dir->dir_count].name, dire->name);
428 dir->dirs[dir->dir_count].start_block =
429 dirh.start_block;
430 dir->dirs[dir->dir_count].offset = dire->offset;
431 dir->dirs[dir->dir_count].type = dire->type;
432 dir->dir_count ++;
433 bytes += dire->size + 1;
434 }
435 }
436
437 return dir;
438
439 corrupted:
440 free(dir->dirs);
441 free(dir);
442 return NULL;
443 }
444
445
446 static int parse_exports_table(long long *table_start)
447 {
448 /*
449 * Note on overflow limits:
450 * Size of SBlk.s.inodes is 2^32 (unsigned int)
451 * Max indexes is (2^32*8)/8K or 2^22
452 * Max length is ((2^32*8)/8K)*8 or 2^25
453 */
454 int res;
455 int indexes = SQUASHFS_LOOKUP_BLOCKS_3((long long) sBlk.s.inodes);
456 int length = SQUASHFS_LOOKUP_BLOCK_BYTES_3((long long) sBlk.s.inodes);
457 long long *export_index_table;
458
459 /*
460 * The size of the index table (length bytes) should match the
461 * table start and end points
462 */
463 if(length != (*table_start - sBlk.s.lookup_table_start)) {
464 ERROR("parse_exports_table: Bad inode count in super block\n");
465 return FALSE;
466 }
467
468 export_index_table = alloc_index_table(indexes);
469
470 if(swap) {
471 long long *sexport_index_table = salloc_index_table(indexes);
472
473 res = read_fs_bytes(fd, sBlk.s.lookup_table_start,
474 length, sexport_index_table);
475 if(res == FALSE) {
476 ERROR("parse_exorts_table: failed to read export "
477 "index table\n");
478 return FALSE;
479 }
480 SQUASHFS_SWAP_LOOKUP_BLOCKS_3(export_index_table,
481 sexport_index_table, indexes);
482 } else {
483 res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
484 export_index_table);
485 if(res == FALSE) {
486 ERROR("parse_exorts_table: failed to read export "
487 "index table\n");
488 return FALSE;
489 }
490 }
491
492 /*
493 * export_index_table[0] stores the start of the compressed export blocks.
494 * This by definition is also the end of the previous filesystem
495 * table - the fragment table.
496 */
497 *table_start = export_index_table[0];
498
499 return TRUE;
500 }
501
502
503 squashfs_operations *read_filesystem_tables_3()
504 {
505 long long table_start;
506
507 /* Read uid and gid lookup tables */
508
509 /* Sanity check super block contents */
510 if(sBlk.no_guids) {
511 if(sBlk.guid_start >= sBlk.s.bytes_used) {
512 ERROR("read_filesystem_tables: gid start too large in super block\n");
513 goto corrupted;
514 }
515
516 if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
517 goto corrupted;
518
519 table_start = sBlk.guid_start;
520 } else {
521 /* no guids, guid_start should be 0 */
522 if(sBlk.guid_start != 0) {
523 ERROR("read_filesystem_tables: gid start too large in super block\n");
524 goto corrupted;
525 }
526
527 table_start = sBlk.s.bytes_used;
528 }
529
530 if(sBlk.uid_start >= table_start) {
531 ERROR("read_filesystem_tables: uid start too large in super block\n");
532 goto corrupted;
533 }
534
535 /* There should be at least one uid */
536 if(sBlk.no_uids == 0) {
537 ERROR("read_filesystem_tables: uid count bad in super block\n");
538 goto corrupted;
539 }
540
541 if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
542 goto corrupted;
543
544 table_start = sBlk.uid_start;
545
546 /* Read exports table */
547 if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
548
549 /* sanity check super block contents */
550 if(sBlk.s.lookup_table_start >= table_start) {
551 ERROR("read_filesystem_tables: lookup table start too large in super block\n");
552 goto corrupted;
553 }
554
555 if(parse_exports_table(&table_start) == FALSE)
556 goto corrupted;
557 }
558
559 /* Read fragment table */
560 if(sBlk.s.fragments != 0) {
561
562 /* Sanity check super block contents */
563 if(sBlk.s.fragment_table_start >= table_start) {
564 ERROR("read_filesystem_tables: fragment table start too large in super block\n");
565 goto corrupted;
566 }
567
568 /* The number of fragments should not exceed the number of inodes */
569 if(sBlk.s.fragments > sBlk.s.inodes) {
570 ERROR("read_filesystem_tables: Bad fragment count in super block\n");
571 goto corrupted;
572 }
573
574 if(read_fragment_table(&table_start) == FALSE)
575 goto corrupted;
576 } else {
577 /*
578 * Sanity check super block contents - with 0 fragments,
579 * the fragment table should be empty
580 */
581 if(sBlk.s.fragment_table_start != table_start) {
582 ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
583 goto corrupted;
584 }
585 }
586
587 /* Read directory table */
588
589 /* Sanity check super block contents */
590 if(sBlk.s.directory_table_start > table_start) {
591 ERROR("read_filesystem_tables: directory table start too large in super block\n");
592 goto corrupted;
593 }
594
595 directory_table = read_directory_table(sBlk.s.directory_table_start,
596 table_start);
597 if(directory_table == NULL)
598 goto corrupted;
599
600 /* Read inode table */
601
602 /* Sanity check super block contents */
603 if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
604 ERROR("read_filesystem_tables: inode table start too large in super block\n");
605 goto corrupted;
606 }
607
608 inode_table = read_inode_table(sBlk.s.inode_table_start,
609 sBlk.s.directory_table_start);
610 if(inode_table == NULL)
611 goto corrupted;
612
613 alloc_index_table(0);
614 salloc_index_table(0);
615
616 return &ops;
617
618 corrupted:
619 ERROR("File system corruption detected\n");
620 alloc_index_table(0);
621 salloc_index_table(0);
622
623 return NULL;
624 }
625
626
627 static squashfs_operations ops = {
628 .opendir = squashfs_opendir,
629 .read_fragment = read_fragment,
630 .read_block_list = read_block_list,
631 .read_inode = read_inode
632 };