]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/fs/squash4.c
change password input field to type=password (#2427)
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / fs / squash4.c
1 /* squash4.c - SquashFS */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27 #include <grub/fshelp.h>
28 #include <grub/deflate.h>
29 #include <minilzo.h>
30 #include <zstd.h>
31
32 #include "xz.h"
33 #include "xz_stream.h"
34
35 GRUB_MOD_LICENSE ("GPLv3+");
36
37 /*
38 object format Pointed by
39 superblock RAW Fixed offset (0)
40 data RAW ? Fixed offset (60)
41 inode table Chunk superblock
42 dir table Chunk superblock
43 fragment table Chunk unk1
44 unk1 RAW, Chunk superblock
45 unk2 RAW superblock
46 UID/GID Chunk exttblptr
47 exttblptr RAW superblock
48
49 UID/GID table is the array ot uint32_t
50 unk1 contains pointer to fragment table followed by some chunk.
51 unk2 containts one uint64_t
52 */
53
54 struct grub_squash_super
55 {
56 grub_uint32_t magic;
57 #define SQUASH_MAGIC 0x73717368
58 grub_uint32_t dummy1;
59 grub_uint32_t creation_time;
60 grub_uint32_t block_size;
61 grub_uint32_t dummy2;
62 grub_uint16_t compression;
63 grub_uint16_t dummy3;
64 grub_uint64_t dummy4;
65 grub_uint16_t root_ino_offset;
66 grub_uint32_t root_ino_chunk;
67 grub_uint16_t dummy5;
68 grub_uint64_t total_size;
69 grub_uint64_t exttbloffset;
70 grub_uint64_t dummy6;
71 grub_uint64_t inodeoffset;
72 grub_uint64_t diroffset;
73 grub_uint64_t unk1offset;
74 grub_uint64_t unk2offset;
75 } GRUB_PACKED;
76
77 /* Chunk-based */
78 struct grub_squash_inode
79 {
80 /* Same values as direlem types. */
81 grub_uint16_t type;
82 grub_uint16_t dummy[3];
83 grub_uint32_t mtime;
84 grub_uint32_t dummy2;
85 union
86 {
87 struct {
88 grub_uint32_t chunk;
89 grub_uint32_t fragment;
90 grub_uint32_t offset;
91 grub_uint32_t size;
92 grub_uint32_t block_size[0];
93 } GRUB_PACKED file;
94 struct {
95 grub_uint64_t chunk;
96 grub_uint64_t size;
97 grub_uint32_t dummy1[3];
98 grub_uint32_t fragment;
99 grub_uint32_t offset;
100 grub_uint32_t dummy3;
101 grub_uint32_t block_size[0];
102 } GRUB_PACKED long_file;
103 struct {
104 grub_uint32_t chunk;
105 grub_uint32_t dummy;
106 grub_uint16_t size;
107 grub_uint16_t offset;
108 } GRUB_PACKED dir;
109 struct {
110 grub_uint32_t dummy1;
111 grub_uint32_t size;
112 grub_uint32_t chunk;
113 grub_uint32_t dummy2;
114 grub_uint16_t dummy3;
115 grub_uint16_t offset;
116 } GRUB_PACKED long_dir;
117 struct {
118 grub_uint32_t dummy;
119 grub_uint32_t namelen;
120 char name[0];
121 } GRUB_PACKED symlink;
122 } GRUB_PACKED;
123 } GRUB_PACKED;
124
125 struct grub_squash_cache_inode
126 {
127 struct grub_squash_inode ino;
128 grub_disk_addr_t ino_chunk;
129 grub_uint16_t ino_offset;
130 grub_uint32_t *block_sizes;
131 grub_disk_addr_t *cumulated_block_sizes;
132 };
133
134 /* Chunk-based. */
135 struct grub_squash_dirent_header
136 {
137 /* Actually the value is the number of elements - 1. */
138 grub_uint32_t nelems;
139 grub_uint32_t ino_chunk;
140 grub_uint32_t dummy;
141 } GRUB_PACKED;
142
143 struct grub_squash_dirent
144 {
145 grub_uint16_t ino_offset;
146 grub_uint16_t dummy;
147 grub_uint16_t type;
148 /* Actually the value is the length of name - 1. */
149 grub_uint16_t namelen;
150 char name[0];
151 } GRUB_PACKED;
152
153 enum
154 {
155 SQUASH_TYPE_DIR = 1,
156 SQUASH_TYPE_REGULAR = 2,
157 SQUASH_TYPE_SYMLINK = 3,
158 SQUASH_TYPE_LONG_DIR = 8,
159 SQUASH_TYPE_LONG_REGULAR = 9,
160 };
161
162
163 struct grub_squash_frag_desc
164 {
165 grub_uint64_t offset;
166 grub_uint32_t size;
167 grub_uint32_t dummy;
168 } GRUB_PACKED;
169
170 enum
171 {
172 SQUASH_CHUNK_FLAGS = 0x8000,
173 SQUASH_CHUNK_UNCOMPRESSED = 0x8000
174 };
175
176 enum
177 {
178 SQUASH_BLOCK_FLAGS = 0x1000000,
179 SQUASH_BLOCK_UNCOMPRESSED = 0x1000000
180 };
181
182 enum
183 {
184 COMPRESSION_ZLIB = 1,
185 COMPRESSION_LZO = 3,
186 COMPRESSION_XZ = 4,
187 COMPRESSION_LZ4 = 5,
188 COMPRESSION_ZSTD = 6,
189 };
190
191
192 #define SQUASH_CHUNK_SIZE 0x2000
193 #define XZBUFSIZ 0x2000
194
195 struct grub_squash_data
196 {
197 grub_disk_t disk;
198 struct grub_squash_super sb;
199 struct grub_squash_cache_inode ino;
200 grub_uint64_t fragments;
201 int log2_blksz;
202 grub_size_t blksz;
203 grub_ssize_t (*decompress) (char *inbuf, grub_size_t insize, grub_off_t off,
204 char *outbuf, grub_size_t outsize,
205 struct grub_squash_data *data);
206 struct xz_dec *xzdec;
207 char *xzbuf;
208 };
209
210 struct grub_fshelp_node
211 {
212 struct grub_squash_data *data;
213 struct grub_squash_inode ino;
214 grub_size_t stsize;
215 struct
216 {
217 grub_disk_addr_t ino_chunk;
218 grub_uint16_t ino_offset;
219 } stack[1];
220 };
221
222 static grub_err_t
223 read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len,
224 grub_uint64_t chunk_start, grub_off_t offset)
225 {
226 while (len > 0)
227 {
228 grub_uint64_t csize;
229 grub_uint16_t d;
230 grub_err_t err;
231 while (1)
232 {
233 err = grub_disk_read (data->disk,
234 chunk_start >> GRUB_DISK_SECTOR_BITS,
235 chunk_start & (GRUB_DISK_SECTOR_SIZE - 1),
236 sizeof (d), &d);
237 if (err)
238 return err;
239 if (offset < SQUASH_CHUNK_SIZE)
240 break;
241 offset -= SQUASH_CHUNK_SIZE;
242 chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS);
243 }
244
245 csize = SQUASH_CHUNK_SIZE - offset;
246 if (csize > len)
247 csize = len;
248
249 if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED)
250 {
251 grub_disk_addr_t a = chunk_start + 2 + offset;
252 err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
253 a & (GRUB_DISK_SECTOR_SIZE - 1),
254 csize, buf);
255 if (err)
256 return err;
257 }
258 else
259 {
260 char *tmp;
261 grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS;
262 grub_disk_addr_t a = chunk_start + 2;
263 tmp = grub_malloc (bsize);
264 if (!tmp)
265 return grub_errno;
266 /* FIXME: buffer uncompressed data. */
267 err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
268 a & (GRUB_DISK_SECTOR_SIZE - 1),
269 bsize, tmp);
270 if (err)
271 {
272 grub_free (tmp);
273 return err;
274 }
275
276 if (data->decompress (tmp, bsize, offset,
277 buf, csize, data) < 0)
278 {
279 grub_free (tmp);
280 return grub_errno;
281 }
282 grub_free (tmp);
283 }
284 len -= csize;
285 offset += csize;
286 buf = (char *) buf + csize;
287 }
288 return GRUB_ERR_NONE;
289 }
290
291 static grub_ssize_t
292 zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
293 char *outbuf, grub_size_t outsize,
294 struct grub_squash_data *data __attribute__ ((unused)))
295 {
296 return grub_zlib_decompress (inbuf, insize, off, outbuf, outsize);
297 }
298
299 static grub_ssize_t
300 lzo_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
301 char *outbuf, grub_size_t len, struct grub_squash_data *data)
302 {
303 lzo_uint usize = data->blksz;
304 grub_uint8_t *udata;
305
306 if (usize < 8192)
307 usize = 8192;
308
309 udata = grub_malloc (usize);
310 if (!udata)
311 return -1;
312
313 if (lzo1x_decompress_safe ((grub_uint8_t *) inbuf,
314 insize, udata, &usize, NULL) != LZO_E_OK)
315 {
316 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
317 grub_free (udata);
318 return -1;
319 }
320 grub_memcpy (outbuf, udata + off, len);
321 grub_free (udata);
322 return len;
323 }
324
325 static grub_ssize_t
326 xz_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
327 char *outbuf, grub_size_t len, struct grub_squash_data *data)
328 {
329 grub_size_t ret = 0;
330 grub_off_t pos = 0;
331 struct xz_buf buf;
332
333 xz_dec_reset (data->xzdec);
334 buf.in = (grub_uint8_t *) inbuf;
335 buf.in_pos = 0;
336 buf.in_size = insize;
337 buf.out = (grub_uint8_t *) data->xzbuf;
338 buf.out_pos = 0;
339 buf.out_size = XZBUFSIZ;
340
341 while (len)
342 {
343 enum xz_ret xzret;
344
345 buf.out_pos = 0;
346
347 xzret = xz_dec_run (data->xzdec, &buf);
348
349 if (xzret != XZ_OK && xzret != XZ_STREAM_END)
350 {
351 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "invalid xz chunk");
352 return -1;
353 }
354 if (pos + buf.out_pos >= off)
355 {
356 grub_ssize_t outoff = pos - off;
357 grub_size_t l;
358 if (outoff >= 0)
359 {
360 l = buf.out_pos;
361 if (l > len)
362 l = len;
363 grub_memcpy (outbuf + outoff, buf.out, l);
364 }
365 else
366 {
367 outoff = -outoff;
368 l = buf.out_pos - outoff;
369 if (l > len)
370 l = len;
371 grub_memcpy (outbuf, buf.out + outoff, l);
372 }
373 ret += l;
374 len -= l;
375 }
376 pos += buf.out_pos;
377 if (xzret == XZ_STREAM_END)
378 break;
379 }
380 return ret;
381 }
382
383 int LZ4_uncompress_unknownOutputSize(const char *source, char *dest, int isize, int maxOutputSize);
384 static grub_ssize_t lz4_decompress_wrap(char *inbuf, grub_size_t insize, grub_off_t off,
385 char *outbuf, grub_size_t len, struct grub_squash_data *data)
386 {
387 char *udata = NULL;
388 int usize = data->blksz;
389
390 if (usize < 8192)
391 usize = 8192;
392
393 udata = grub_malloc (usize);
394 if (!udata)
395 return -1;
396
397 LZ4_uncompress_unknownOutputSize(inbuf, udata, insize, usize);
398 grub_memcpy (outbuf, udata + off, len);
399 grub_free (udata);
400 return len;
401 }
402
403 static grub_ssize_t zstd_decompress_wrap(char *inbuf, grub_size_t insize, grub_off_t off,
404 char *outbuf, grub_size_t len, struct grub_squash_data *data)
405 {
406 char *udata = NULL;
407 int usize = data->blksz;
408 if (usize < 8192)
409 usize = 8192;
410
411 udata = grub_malloc (usize);
412 if (!udata)
413 return -1;
414
415 ZSTD_decompress(udata, usize, inbuf, insize);
416 grub_memcpy(outbuf, udata + off, len);
417 grub_free(udata);
418
419 return len;
420 }
421
422 static struct grub_squash_data *
423 squash_mount (grub_disk_t disk)
424 {
425 struct grub_squash_super sb;
426 grub_err_t err;
427 struct grub_squash_data *data;
428 grub_uint64_t frag;
429
430 err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb);
431 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
432 grub_error (GRUB_ERR_BAD_FS, "not a squash4");
433 if (err)
434 return NULL;
435 if (sb.magic != grub_cpu_to_le32_compile_time (SQUASH_MAGIC)
436 || sb.block_size == 0
437 || ((sb.block_size - 1) & sb.block_size))
438 {
439 grub_error (GRUB_ERR_BAD_FS, "not squash4");
440 return NULL;
441 }
442
443 err = grub_disk_read (disk,
444 grub_le_to_cpu64 (sb.unk1offset)
445 >> GRUB_DISK_SECTOR_BITS,
446 grub_le_to_cpu64 (sb.unk1offset)
447 & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag);
448 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
449 grub_error (GRUB_ERR_BAD_FS, "not a squash4");
450 if (err)
451 return NULL;
452
453 data = grub_zalloc (sizeof (*data));
454 if (!data)
455 return NULL;
456 data->sb = sb;
457 data->disk = disk;
458 data->fragments = grub_le_to_cpu64 (frag);
459
460 switch (sb.compression)
461 {
462 case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB):
463 data->decompress = zlib_decompress;
464 break;
465 case grub_cpu_to_le16_compile_time (COMPRESSION_LZO):
466 data->decompress = lzo_decompress;
467 break;
468 case grub_cpu_to_le16_compile_time (COMPRESSION_LZ4):
469 data->decompress = lz4_decompress_wrap;
470 break;
471 case grub_cpu_to_le16_compile_time (COMPRESSION_ZSTD):
472 data->decompress = zstd_decompress_wrap;
473 break;
474 case grub_cpu_to_le16_compile_time (COMPRESSION_XZ):
475 data->decompress = xz_decompress;
476 data->xzbuf = grub_malloc (XZBUFSIZ);
477 if (!data->xzbuf)
478 {
479 grub_free (data);
480 return NULL;
481 }
482 data->xzdec = xz_dec_init (1 << 16);
483 if (!data->xzdec)
484 {
485 grub_free (data->xzbuf);
486 grub_free (data);
487 return NULL;
488 }
489 break;
490 default:
491 grub_free (data);
492 grub_error (GRUB_ERR_BAD_FS, "unsupported compression %d",
493 grub_le_to_cpu16 (sb.compression));
494 return NULL;
495 }
496
497 data->blksz = grub_le_to_cpu32 (data->sb.block_size);
498 for (data->log2_blksz = 0;
499 (1U << data->log2_blksz) < data->blksz;
500 data->log2_blksz++);
501
502 return data;
503 }
504
505 static char *
506 grub_squash_read_symlink (grub_fshelp_node_t node)
507 {
508 char *ret;
509 grub_err_t err;
510 ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
511
512 err = read_chunk (node->data, ret,
513 grub_le_to_cpu32 (node->ino.symlink.namelen),
514 grub_le_to_cpu64 (node->data->sb.inodeoffset)
515 + node->stack[node->stsize - 1].ino_chunk,
516 node->stack[node->stsize - 1].ino_offset
517 + (node->ino.symlink.name - (char *) &node->ino));
518 if (err)
519 {
520 grub_free (ret);
521 return NULL;
522 }
523 ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0;
524 return ret;
525 }
526
527 static int
528 grub_squash_iterate_dir (grub_fshelp_node_t dir,
529 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
530 {
531 grub_uint32_t off;
532 grub_uint32_t endoff;
533 grub_uint64_t chunk;
534 unsigned i;
535
536 /* FIXME: why - 3 ? */
537 switch (dir->ino.type)
538 {
539 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR):
540 off = grub_le_to_cpu16 (dir->ino.dir.offset);
541 endoff = grub_le_to_cpu16 (dir->ino.dir.size) + off - 3;
542 chunk = grub_le_to_cpu32 (dir->ino.dir.chunk);
543 break;
544 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR):
545 off = grub_le_to_cpu16 (dir->ino.long_dir.offset);
546 endoff = grub_le_to_cpu32 (dir->ino.long_dir.size) + off - 3;
547 chunk = grub_le_to_cpu32 (dir->ino.long_dir.chunk);
548 break;
549 default:
550 grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x",
551 grub_le_to_cpu16 (dir->ino.type));
552 return 0;
553 }
554
555 {
556 grub_fshelp_node_t node;
557 node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
558 if (!node)
559 return 0;
560 grub_memcpy (node, dir,
561 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
562 if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
563 return 1;
564
565 if (dir->stsize != 1)
566 {
567 grub_err_t err;
568
569 node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
570 if (!node)
571 return 0;
572
573 grub_memcpy (node, dir,
574 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
575
576 node->stsize--;
577 err = read_chunk (dir->data, &node->ino, sizeof (node->ino),
578 grub_le_to_cpu64 (dir->data->sb.inodeoffset)
579 + node->stack[node->stsize - 1].ino_chunk,
580 node->stack[node->stsize - 1].ino_offset);
581 if (err)
582 return 0;
583
584 if (hook ("..", GRUB_FSHELP_DIR, node, hook_data))
585 return 1;
586 }
587 }
588
589 while (off < endoff)
590 {
591 struct grub_squash_dirent_header dh;
592 grub_err_t err;
593
594 err = read_chunk (dir->data, &dh, sizeof (dh),
595 grub_le_to_cpu64 (dir->data->sb.diroffset)
596 + chunk, off);
597 if (err)
598 return 0;
599 off += sizeof (dh);
600 for (i = 0; i < (unsigned) grub_le_to_cpu32 (dh.nelems) + 1; i++)
601 {
602 char *buf;
603 int r;
604 struct grub_fshelp_node *node;
605 enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
606 struct grub_squash_dirent di;
607 struct grub_squash_inode ino;
608
609 err = read_chunk (dir->data, &di, sizeof (di),
610 grub_le_to_cpu64 (dir->data->sb.diroffset)
611 + chunk, off);
612 if (err)
613 return 0;
614 off += sizeof (di);
615
616 err = read_chunk (dir->data, &ino, sizeof (ino),
617 grub_le_to_cpu64 (dir->data->sb.inodeoffset)
618 + grub_le_to_cpu32 (dh.ino_chunk),
619 grub_cpu_to_le16 (di.ino_offset));
620 if (err)
621 return 0;
622
623 buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
624 if (!buf)
625 return 0;
626 err = read_chunk (dir->data, buf,
627 grub_le_to_cpu16 (di.namelen) + 1,
628 grub_le_to_cpu64 (dir->data->sb.diroffset)
629 + chunk, off);
630 if (err)
631 return 0;
632
633 off += grub_le_to_cpu16 (di.namelen) + 1;
634 buf[grub_le_to_cpu16 (di.namelen) + 1] = 0;
635 if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR)
636 filetype = GRUB_FSHELP_DIR;
637 if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
638 filetype = GRUB_FSHELP_SYMLINK;
639
640 node = grub_malloc (sizeof (*node)
641 + (dir->stsize + 1) * sizeof (dir->stack[0]));
642 if (! node)
643 return 0;
644
645 grub_memcpy (node, dir,
646 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
647
648 node->ino = ino;
649 node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
650 node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset);
651 node->stsize++;
652 r = hook (buf, filetype, node, hook_data);
653
654 grub_free (buf);
655 if (r)
656 return r;
657 }
658 }
659 return 0;
660 }
661
662 static grub_err_t
663 make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root)
664 {
665 grub_memset (root, 0, sizeof (*root));
666 root->data = data;
667 root->stsize = 1;
668 root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk);
669 root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset);
670 return read_chunk (data, &root->ino, sizeof (root->ino),
671 grub_le_to_cpu64 (data->sb.inodeoffset)
672 + root->stack[0].ino_chunk,
673 root->stack[0].ino_offset);
674 }
675
676 static void
677 squash_unmount (struct grub_squash_data *data)
678 {
679 if (data->xzdec)
680 xz_dec_end (data->xzdec);
681 grub_free (data->xzbuf);
682 grub_free (data->ino.cumulated_block_sizes);
683 grub_free (data->ino.block_sizes);
684 grub_free (data);
685 }
686
687
688 /* Context for grub_squash_dir. */
689 struct grub_squash_dir_ctx
690 {
691 grub_fs_dir_hook_t hook;
692 void *hook_data;
693 };
694
695 /* Helper for grub_squash_dir. */
696 static int
697 grub_squash_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
698 grub_fshelp_node_t node, void *data)
699 {
700 struct grub_squash_dir_ctx *ctx = data;
701 struct grub_dirhook_info info;
702
703 grub_memset (&info, 0, sizeof (info));
704 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
705 info.mtimeset = 1;
706 info.mtime = grub_le_to_cpu32 (node->ino.mtime);
707 grub_free (node);
708 return ctx->hook (filename, &info, ctx->hook_data);
709 }
710
711 static grub_err_t
712 grub_squash_dir (grub_device_t device, const char *path,
713 grub_fs_dir_hook_t hook, void *hook_data)
714 {
715 struct grub_squash_dir_ctx ctx = { hook, hook_data };
716 struct grub_squash_data *data = 0;
717 struct grub_fshelp_node *fdiro = 0;
718 struct grub_fshelp_node root;
719 grub_err_t err;
720
721 data = squash_mount (device->disk);
722 if (! data)
723 return grub_errno;
724
725 err = make_root_node (data, &root);
726 if (err)
727 return err;
728
729 grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
730 grub_squash_read_symlink, GRUB_FSHELP_DIR);
731 if (!grub_errno)
732 grub_squash_iterate_dir (fdiro, grub_squash_dir_iter, &ctx);
733
734 squash_unmount (data);
735
736 return grub_errno;
737 }
738
739 static grub_err_t
740 grub_squash_open (struct grub_file *file, const char *name)
741 {
742 struct grub_squash_data *data = 0;
743 struct grub_fshelp_node *fdiro = 0;
744 struct grub_fshelp_node root;
745 grub_err_t err;
746
747 data = squash_mount (file->device->disk);
748 if (! data)
749 return grub_errno;
750
751 err = make_root_node (data, &root);
752 if (err)
753 return err;
754
755 grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir,
756 grub_squash_read_symlink, GRUB_FSHELP_REG);
757 if (grub_errno)
758 {
759 squash_unmount (data);
760 return grub_errno;
761 }
762
763 file->data = data;
764 data->ino.ino = fdiro->ino;
765 data->ino.block_sizes = NULL;
766 data->ino.cumulated_block_sizes = NULL;
767 data->ino.ino_chunk = fdiro->stack[fdiro->stsize - 1].ino_chunk;
768 data->ino.ino_offset = fdiro->stack[fdiro->stsize - 1].ino_offset;
769
770 switch (fdiro->ino.type)
771 {
772 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
773 file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
774 break;
775 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
776 file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
777 break;
778 default:
779 {
780 grub_uint16_t type = grub_le_to_cpu16 (fdiro->ino.type);
781 grub_free (fdiro);
782 squash_unmount (data);
783 return grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", type);
784 }
785 }
786
787 grub_free (fdiro);
788
789 return GRUB_ERR_NONE;
790 }
791
792 static grub_ssize_t
793 direct_read (struct grub_squash_data *data,
794 struct grub_squash_cache_inode *ino,
795 grub_off_t off, char *buf, grub_size_t len)
796 {
797 grub_err_t err;
798 grub_off_t cumulated_uncompressed_size = 0;
799 grub_uint64_t a = 0;
800 grub_size_t i;
801 grub_size_t origlen = len;
802
803 switch (ino->ino.type)
804 {
805 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
806 a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
807 break;
808 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
809 a = grub_le_to_cpu32 (ino->ino.file.chunk);
810 break;
811 }
812
813 if (!ino->block_sizes)
814 {
815 grub_off_t total_size = 0;
816 grub_size_t total_blocks;
817 grub_size_t block_offset = 0;
818 switch (ino->ino.type)
819 {
820 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
821 total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
822 block_offset = ((char *) &ino->ino.long_file.block_size
823 - (char *) &ino->ino);
824 break;
825 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
826 total_size = grub_le_to_cpu32 (ino->ino.file.size);
827 block_offset = ((char *) &ino->ino.file.block_size
828 - (char *) &ino->ino);
829 break;
830 }
831 total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz);
832 ino->block_sizes = grub_malloc (total_blocks
833 * sizeof (ino->block_sizes[0]));
834 ino->cumulated_block_sizes = grub_malloc (total_blocks
835 * sizeof (ino->cumulated_block_sizes[0]));
836 if (!ino->block_sizes || !ino->cumulated_block_sizes)
837 {
838 grub_free (ino->block_sizes);
839 grub_free (ino->cumulated_block_sizes);
840 ino->block_sizes = 0;
841 ino->cumulated_block_sizes = 0;
842 return -1;
843 }
844 err = read_chunk (data, ino->block_sizes,
845 total_blocks * sizeof (ino->block_sizes[0]),
846 grub_le_to_cpu64 (data->sb.inodeoffset)
847 + ino->ino_chunk,
848 ino->ino_offset + block_offset);
849 if (err)
850 {
851 grub_free (ino->block_sizes);
852 grub_free (ino->cumulated_block_sizes);
853 ino->block_sizes = 0;
854 ino->cumulated_block_sizes = 0;
855 return -1;
856 }
857 ino->cumulated_block_sizes[0] = 0;
858 for (i = 1; i < total_blocks; i++)
859 ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
860 + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
861 }
862
863 if (a == 0)
864 a = sizeof (struct grub_squash_super);
865 i = off >> data->log2_blksz;
866 cumulated_uncompressed_size = data->blksz * (grub_disk_addr_t) i;
867 while (cumulated_uncompressed_size < off + len)
868 {
869 grub_size_t boff, curread;
870 boff = off - cumulated_uncompressed_size;
871 curread = data->blksz - boff;
872 if (curread > len)
873 curread = len;
874 if (!ino->block_sizes[i])
875 {
876 /* Sparse block */
877 grub_memset (buf, '\0', curread);
878 }
879 else if (!(ino->block_sizes[i]
880 & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)))
881 {
882 char *block;
883 grub_size_t csize;
884 csize = grub_le_to_cpu32 (ino->block_sizes[i]) & ~SQUASH_BLOCK_FLAGS;
885 block = grub_malloc (csize);
886 if (!block)
887 return -1;
888 err = grub_disk_read (data->disk,
889 (ino->cumulated_block_sizes[i] + a)
890 >> GRUB_DISK_SECTOR_BITS,
891 (ino->cumulated_block_sizes[i] + a)
892 & (GRUB_DISK_SECTOR_SIZE - 1),
893 csize, block);
894 if (err)
895 {
896 grub_free (block);
897 return -1;
898 }
899 if (data->decompress (block, csize, boff, buf, curread, data)
900 != (grub_ssize_t) curread)
901 {
902 grub_free (block);
903 if (!grub_errno)
904 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
905 return -1;
906 }
907 grub_free (block);
908 }
909 else
910 err = grub_disk_read (data->disk,
911 (ino->cumulated_block_sizes[i] + a + boff)
912 >> GRUB_DISK_SECTOR_BITS,
913 (ino->cumulated_block_sizes[i] + a + boff)
914 & (GRUB_DISK_SECTOR_SIZE - 1),
915 curread, buf);
916 if (err)
917 return -1;
918 off += curread;
919 len -= curread;
920 buf += curread;
921 cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
922 i++;
923 }
924 return origlen;
925 }
926
927
928 static grub_ssize_t
929 grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
930 {
931 struct grub_squash_data *data = file->data;
932 struct grub_squash_cache_inode *ino = &data->ino;
933 grub_off_t off = file->offset;
934 grub_err_t err;
935 grub_uint64_t a, b;
936 grub_uint32_t fragment = 0;
937 int compressed = 0;
938 struct grub_squash_frag_desc frag;
939 grub_off_t direct_len;
940 grub_uint64_t mask = grub_le_to_cpu32 (data->sb.block_size) - 1;
941 grub_size_t orig_len = len;
942
943 switch (ino->ino.type)
944 {
945 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
946 fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
947 break;
948 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
949 fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
950 break;
951 }
952
953 /* Squash may pack file tail as fragment. So read initial part directly and
954 get tail from fragments */
955 direct_len = fragment == 0xffffffff ? file->size : file->size & ~mask;
956 if (off < direct_len)
957 {
958 grub_size_t read_len = direct_len - off;
959 grub_ssize_t res;
960
961 if (read_len > len)
962 read_len = len;
963 res = direct_read (data, ino, off, buf, read_len);
964 if ((grub_size_t) res != read_len)
965 return -1; /* FIXME: is short read possible here? */
966 len -= read_len;
967 if (!len)
968 return read_len;
969 buf += read_len;
970 off = 0;
971 }
972 else
973 off -= direct_len;
974
975 err = read_chunk (data, &frag, sizeof (frag),
976 data->fragments, sizeof (frag) * fragment);
977 if (err)
978 return -1;
979 a = grub_le_to_cpu64 (frag.offset);
980 compressed = !(frag.size & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED));
981 if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
982 b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off;
983 else
984 b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
985
986 /* FIXME: cache uncompressed chunks. */
987 if (compressed)
988 {
989 char *block;
990 block = grub_malloc (grub_le_to_cpu32 (frag.size));
991 if (!block)
992 return -1;
993 err = grub_disk_read (data->disk,
994 a >> GRUB_DISK_SECTOR_BITS,
995 a & (GRUB_DISK_SECTOR_SIZE - 1),
996 grub_le_to_cpu32 (frag.size), block);
997 if (err)
998 {
999 grub_free (block);
1000 return -1;
1001 }
1002 if (data->decompress (block, grub_le_to_cpu32 (frag.size),
1003 b, buf, len, data)
1004 != (grub_ssize_t) len)
1005 {
1006 grub_free (block);
1007 if (!grub_errno)
1008 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
1009 return -1;
1010 }
1011 grub_free (block);
1012 }
1013 else
1014 {
1015 err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
1016 (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
1017 if (err)
1018 return -1;
1019 }
1020 return orig_len;
1021 }
1022
1023 static grub_err_t
1024 grub_squash_close (grub_file_t file)
1025 {
1026 squash_unmount (file->data);
1027 return GRUB_ERR_NONE;
1028 }
1029
1030 static grub_err_t
1031 grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
1032 {
1033 struct grub_squash_data *data = 0;
1034
1035 data = squash_mount (dev->disk);
1036 if (! data)
1037 return grub_errno;
1038 *tm = grub_le_to_cpu32 (data->sb.creation_time);
1039 squash_unmount (data);
1040 return GRUB_ERR_NONE;
1041 }
1042
1043 static struct grub_fs grub_squash_fs =
1044 {
1045 .name = "squash4",
1046 .fs_dir = grub_squash_dir,
1047 .fs_open = grub_squash_open,
1048 .fs_read = grub_squash_read,
1049 .fs_close = grub_squash_close,
1050 .fs_mtime = grub_squash_mtime,
1051 #ifdef GRUB_UTIL
1052 .reserved_first_sector = 0,
1053 .blocklist_install = 0,
1054 #endif
1055 .next = 0
1056 };
1057
1058 GRUB_MOD_INIT(squash4)
1059 {
1060 grub_fs_register (&grub_squash_fs);
1061 }
1062
1063 GRUB_MOD_FINI(squash4)
1064 {
1065 grub_fs_unregister (&grub_squash_fs);
1066 }
1067