]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/fs/squash4.c
Correct EFI chainloader image device path (#2397)
[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
409 if (off == 0)
410 {
411 ZSTD_decompress(outbuf, len, inbuf, insize);
412 }
413 else
414 {
415 if (usize < 8192)
416 usize = 8192;
417
418 udata = grub_malloc (usize);
419 if (!udata)
420 return -1;
421
422 ZSTD_decompress(udata, usize, inbuf, insize);
423 grub_memcpy(outbuf, udata + off, len);
424 grub_free(udata);
425 }
426
427 return len;
428 }
429
430 static struct grub_squash_data *
431 squash_mount (grub_disk_t disk)
432 {
433 struct grub_squash_super sb;
434 grub_err_t err;
435 struct grub_squash_data *data;
436 grub_uint64_t frag;
437
438 err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb);
439 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
440 grub_error (GRUB_ERR_BAD_FS, "not a squash4");
441 if (err)
442 return NULL;
443 if (sb.magic != grub_cpu_to_le32_compile_time (SQUASH_MAGIC)
444 || sb.block_size == 0
445 || ((sb.block_size - 1) & sb.block_size))
446 {
447 grub_error (GRUB_ERR_BAD_FS, "not squash4");
448 return NULL;
449 }
450
451 err = grub_disk_read (disk,
452 grub_le_to_cpu64 (sb.unk1offset)
453 >> GRUB_DISK_SECTOR_BITS,
454 grub_le_to_cpu64 (sb.unk1offset)
455 & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag);
456 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
457 grub_error (GRUB_ERR_BAD_FS, "not a squash4");
458 if (err)
459 return NULL;
460
461 data = grub_zalloc (sizeof (*data));
462 if (!data)
463 return NULL;
464 data->sb = sb;
465 data->disk = disk;
466 data->fragments = grub_le_to_cpu64 (frag);
467
468 switch (sb.compression)
469 {
470 case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB):
471 data->decompress = zlib_decompress;
472 break;
473 case grub_cpu_to_le16_compile_time (COMPRESSION_LZO):
474 data->decompress = lzo_decompress;
475 break;
476 case grub_cpu_to_le16_compile_time (COMPRESSION_LZ4):
477 data->decompress = lz4_decompress_wrap;
478 break;
479 case grub_cpu_to_le16_compile_time (COMPRESSION_ZSTD):
480 data->decompress = zstd_decompress_wrap;
481 break;
482 case grub_cpu_to_le16_compile_time (COMPRESSION_XZ):
483 data->decompress = xz_decompress;
484 data->xzbuf = grub_malloc (XZBUFSIZ);
485 if (!data->xzbuf)
486 {
487 grub_free (data);
488 return NULL;
489 }
490 data->xzdec = xz_dec_init (1 << 16);
491 if (!data->xzdec)
492 {
493 grub_free (data->xzbuf);
494 grub_free (data);
495 return NULL;
496 }
497 break;
498 default:
499 grub_free (data);
500 grub_error (GRUB_ERR_BAD_FS, "unsupported compression %d",
501 grub_le_to_cpu16 (sb.compression));
502 return NULL;
503 }
504
505 data->blksz = grub_le_to_cpu32 (data->sb.block_size);
506 for (data->log2_blksz = 0;
507 (1U << data->log2_blksz) < data->blksz;
508 data->log2_blksz++);
509
510 return data;
511 }
512
513 static char *
514 grub_squash_read_symlink (grub_fshelp_node_t node)
515 {
516 char *ret;
517 grub_err_t err;
518 ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
519
520 err = read_chunk (node->data, ret,
521 grub_le_to_cpu32 (node->ino.symlink.namelen),
522 grub_le_to_cpu64 (node->data->sb.inodeoffset)
523 + node->stack[node->stsize - 1].ino_chunk,
524 node->stack[node->stsize - 1].ino_offset
525 + (node->ino.symlink.name - (char *) &node->ino));
526 if (err)
527 {
528 grub_free (ret);
529 return NULL;
530 }
531 ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0;
532 return ret;
533 }
534
535 static int
536 grub_squash_iterate_dir (grub_fshelp_node_t dir,
537 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
538 {
539 grub_uint32_t off;
540 grub_uint32_t endoff;
541 grub_uint64_t chunk;
542 unsigned i;
543
544 /* FIXME: why - 3 ? */
545 switch (dir->ino.type)
546 {
547 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR):
548 off = grub_le_to_cpu16 (dir->ino.dir.offset);
549 endoff = grub_le_to_cpu16 (dir->ino.dir.size) + off - 3;
550 chunk = grub_le_to_cpu32 (dir->ino.dir.chunk);
551 break;
552 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR):
553 off = grub_le_to_cpu16 (dir->ino.long_dir.offset);
554 endoff = grub_le_to_cpu16 (dir->ino.long_dir.size) + off - 3;
555 chunk = grub_le_to_cpu32 (dir->ino.long_dir.chunk);
556 break;
557 default:
558 grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x",
559 grub_le_to_cpu16 (dir->ino.type));
560 return 0;
561 }
562
563 {
564 grub_fshelp_node_t node;
565 node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
566 if (!node)
567 return 0;
568 grub_memcpy (node, dir,
569 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
570 if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
571 return 1;
572
573 if (dir->stsize != 1)
574 {
575 grub_err_t err;
576
577 node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
578 if (!node)
579 return 0;
580
581 grub_memcpy (node, dir,
582 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
583
584 node->stsize--;
585 err = read_chunk (dir->data, &node->ino, sizeof (node->ino),
586 grub_le_to_cpu64 (dir->data->sb.inodeoffset)
587 + node->stack[node->stsize - 1].ino_chunk,
588 node->stack[node->stsize - 1].ino_offset);
589 if (err)
590 return 0;
591
592 if (hook ("..", GRUB_FSHELP_DIR, node, hook_data))
593 return 1;
594 }
595 }
596
597 while (off < endoff)
598 {
599 struct grub_squash_dirent_header dh;
600 grub_err_t err;
601
602 err = read_chunk (dir->data, &dh, sizeof (dh),
603 grub_le_to_cpu64 (dir->data->sb.diroffset)
604 + chunk, off);
605 if (err)
606 return 0;
607 off += sizeof (dh);
608 for (i = 0; i < (unsigned) grub_le_to_cpu32 (dh.nelems) + 1; i++)
609 {
610 char *buf;
611 int r;
612 struct grub_fshelp_node *node;
613 enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
614 struct grub_squash_dirent di;
615 struct grub_squash_inode ino;
616
617 err = read_chunk (dir->data, &di, sizeof (di),
618 grub_le_to_cpu64 (dir->data->sb.diroffset)
619 + chunk, off);
620 if (err)
621 return 0;
622 off += sizeof (di);
623
624 err = read_chunk (dir->data, &ino, sizeof (ino),
625 grub_le_to_cpu64 (dir->data->sb.inodeoffset)
626 + grub_le_to_cpu32 (dh.ino_chunk),
627 grub_cpu_to_le16 (di.ino_offset));
628 if (err)
629 return 0;
630
631 buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
632 if (!buf)
633 return 0;
634 err = read_chunk (dir->data, buf,
635 grub_le_to_cpu16 (di.namelen) + 1,
636 grub_le_to_cpu64 (dir->data->sb.diroffset)
637 + chunk, off);
638 if (err)
639 return 0;
640
641 off += grub_le_to_cpu16 (di.namelen) + 1;
642 buf[grub_le_to_cpu16 (di.namelen) + 1] = 0;
643 if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR)
644 filetype = GRUB_FSHELP_DIR;
645 if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
646 filetype = GRUB_FSHELP_SYMLINK;
647
648 node = grub_malloc (sizeof (*node)
649 + (dir->stsize + 1) * sizeof (dir->stack[0]));
650 if (! node)
651 return 0;
652
653 grub_memcpy (node, dir,
654 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
655
656 node->ino = ino;
657 node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
658 node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset);
659 node->stsize++;
660 r = hook (buf, filetype, node, hook_data);
661
662 grub_free (buf);
663 if (r)
664 return r;
665 }
666 }
667 return 0;
668 }
669
670 static grub_err_t
671 make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root)
672 {
673 grub_memset (root, 0, sizeof (*root));
674 root->data = data;
675 root->stsize = 1;
676 root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk);
677 root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset);
678 return read_chunk (data, &root->ino, sizeof (root->ino),
679 grub_le_to_cpu64 (data->sb.inodeoffset)
680 + root->stack[0].ino_chunk,
681 root->stack[0].ino_offset);
682 }
683
684 static void
685 squash_unmount (struct grub_squash_data *data)
686 {
687 if (data->xzdec)
688 xz_dec_end (data->xzdec);
689 grub_free (data->xzbuf);
690 grub_free (data->ino.cumulated_block_sizes);
691 grub_free (data->ino.block_sizes);
692 grub_free (data);
693 }
694
695
696 /* Context for grub_squash_dir. */
697 struct grub_squash_dir_ctx
698 {
699 grub_fs_dir_hook_t hook;
700 void *hook_data;
701 };
702
703 /* Helper for grub_squash_dir. */
704 static int
705 grub_squash_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
706 grub_fshelp_node_t node, void *data)
707 {
708 struct grub_squash_dir_ctx *ctx = data;
709 struct grub_dirhook_info info;
710
711 grub_memset (&info, 0, sizeof (info));
712 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
713 info.mtimeset = 1;
714 info.mtime = grub_le_to_cpu32 (node->ino.mtime);
715 grub_free (node);
716 return ctx->hook (filename, &info, ctx->hook_data);
717 }
718
719 static grub_err_t
720 grub_squash_dir (grub_device_t device, const char *path,
721 grub_fs_dir_hook_t hook, void *hook_data)
722 {
723 struct grub_squash_dir_ctx ctx = { hook, hook_data };
724 struct grub_squash_data *data = 0;
725 struct grub_fshelp_node *fdiro = 0;
726 struct grub_fshelp_node root;
727 grub_err_t err;
728
729 data = squash_mount (device->disk);
730 if (! data)
731 return grub_errno;
732
733 err = make_root_node (data, &root);
734 if (err)
735 return err;
736
737 grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
738 grub_squash_read_symlink, GRUB_FSHELP_DIR);
739 if (!grub_errno)
740 grub_squash_iterate_dir (fdiro, grub_squash_dir_iter, &ctx);
741
742 squash_unmount (data);
743
744 return grub_errno;
745 }
746
747 static grub_err_t
748 grub_squash_open (struct grub_file *file, const char *name)
749 {
750 struct grub_squash_data *data = 0;
751 struct grub_fshelp_node *fdiro = 0;
752 struct grub_fshelp_node root;
753 grub_err_t err;
754
755 data = squash_mount (file->device->disk);
756 if (! data)
757 return grub_errno;
758
759 err = make_root_node (data, &root);
760 if (err)
761 return err;
762
763 grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir,
764 grub_squash_read_symlink, GRUB_FSHELP_REG);
765 if (grub_errno)
766 {
767 squash_unmount (data);
768 return grub_errno;
769 }
770
771 file->data = data;
772 data->ino.ino = fdiro->ino;
773 data->ino.block_sizes = NULL;
774 data->ino.cumulated_block_sizes = NULL;
775 data->ino.ino_chunk = fdiro->stack[fdiro->stsize - 1].ino_chunk;
776 data->ino.ino_offset = fdiro->stack[fdiro->stsize - 1].ino_offset;
777
778 switch (fdiro->ino.type)
779 {
780 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
781 file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
782 break;
783 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
784 file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
785 break;
786 default:
787 {
788 grub_uint16_t type = grub_le_to_cpu16 (fdiro->ino.type);
789 grub_free (fdiro);
790 squash_unmount (data);
791 return grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", type);
792 }
793 }
794
795 grub_free (fdiro);
796
797 return GRUB_ERR_NONE;
798 }
799
800 static grub_ssize_t
801 direct_read (struct grub_squash_data *data,
802 struct grub_squash_cache_inode *ino,
803 grub_off_t off, char *buf, grub_size_t len)
804 {
805 grub_err_t err;
806 grub_off_t cumulated_uncompressed_size = 0;
807 grub_uint64_t a = 0;
808 grub_size_t i;
809 grub_size_t origlen = len;
810
811 switch (ino->ino.type)
812 {
813 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
814 a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
815 break;
816 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
817 a = grub_le_to_cpu32 (ino->ino.file.chunk);
818 break;
819 }
820
821 if (!ino->block_sizes)
822 {
823 grub_off_t total_size = 0;
824 grub_size_t total_blocks;
825 grub_size_t block_offset = 0;
826 switch (ino->ino.type)
827 {
828 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
829 total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
830 block_offset = ((char *) &ino->ino.long_file.block_size
831 - (char *) &ino->ino);
832 break;
833 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
834 total_size = grub_le_to_cpu32 (ino->ino.file.size);
835 block_offset = ((char *) &ino->ino.file.block_size
836 - (char *) &ino->ino);
837 break;
838 }
839 total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz);
840 ino->block_sizes = grub_malloc (total_blocks
841 * sizeof (ino->block_sizes[0]));
842 ino->cumulated_block_sizes = grub_malloc (total_blocks
843 * sizeof (ino->cumulated_block_sizes[0]));
844 if (!ino->block_sizes || !ino->cumulated_block_sizes)
845 {
846 grub_free (ino->block_sizes);
847 grub_free (ino->cumulated_block_sizes);
848 ino->block_sizes = 0;
849 ino->cumulated_block_sizes = 0;
850 return -1;
851 }
852 err = read_chunk (data, ino->block_sizes,
853 total_blocks * sizeof (ino->block_sizes[0]),
854 grub_le_to_cpu64 (data->sb.inodeoffset)
855 + ino->ino_chunk,
856 ino->ino_offset + block_offset);
857 if (err)
858 {
859 grub_free (ino->block_sizes);
860 grub_free (ino->cumulated_block_sizes);
861 ino->block_sizes = 0;
862 ino->cumulated_block_sizes = 0;
863 return -1;
864 }
865 ino->cumulated_block_sizes[0] = 0;
866 for (i = 1; i < total_blocks; i++)
867 ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
868 + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
869 }
870
871 if (a == 0)
872 a = sizeof (struct grub_squash_super);
873 i = off >> data->log2_blksz;
874 cumulated_uncompressed_size = data->blksz * (grub_disk_addr_t) i;
875 while (cumulated_uncompressed_size < off + len)
876 {
877 grub_size_t boff, curread;
878 boff = off - cumulated_uncompressed_size;
879 curread = data->blksz - boff;
880 if (curread > len)
881 curread = len;
882 if (!ino->block_sizes[i])
883 {
884 /* Sparse block */
885 grub_memset (buf, '\0', curread);
886 }
887 else if (!(ino->block_sizes[i]
888 & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)))
889 {
890 char *block;
891 grub_size_t csize;
892 csize = grub_le_to_cpu32 (ino->block_sizes[i]) & ~SQUASH_BLOCK_FLAGS;
893 block = grub_malloc (csize);
894 if (!block)
895 return -1;
896 err = grub_disk_read (data->disk,
897 (ino->cumulated_block_sizes[i] + a)
898 >> GRUB_DISK_SECTOR_BITS,
899 (ino->cumulated_block_sizes[i] + a)
900 & (GRUB_DISK_SECTOR_SIZE - 1),
901 csize, block);
902 if (err)
903 {
904 grub_free (block);
905 return -1;
906 }
907 if (data->decompress (block, csize, boff, buf, curread, data)
908 != (grub_ssize_t) curread)
909 {
910 grub_free (block);
911 if (!grub_errno)
912 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
913 return -1;
914 }
915 grub_free (block);
916 }
917 else
918 err = grub_disk_read (data->disk,
919 (ino->cumulated_block_sizes[i] + a + boff)
920 >> GRUB_DISK_SECTOR_BITS,
921 (ino->cumulated_block_sizes[i] + a + boff)
922 & (GRUB_DISK_SECTOR_SIZE - 1),
923 curread, buf);
924 if (err)
925 return -1;
926 off += curread;
927 len -= curread;
928 buf += curread;
929 cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
930 i++;
931 }
932 return origlen;
933 }
934
935
936 static grub_ssize_t
937 grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
938 {
939 struct grub_squash_data *data = file->data;
940 struct grub_squash_cache_inode *ino = &data->ino;
941 grub_off_t off = file->offset;
942 grub_err_t err;
943 grub_uint64_t a, b;
944 grub_uint32_t fragment = 0;
945 int compressed = 0;
946 struct grub_squash_frag_desc frag;
947 grub_off_t direct_len;
948 grub_uint64_t mask = grub_le_to_cpu32 (data->sb.block_size) - 1;
949 grub_size_t orig_len = len;
950
951 switch (ino->ino.type)
952 {
953 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
954 fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
955 break;
956 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
957 fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
958 break;
959 }
960
961 /* Squash may pack file tail as fragment. So read initial part directly and
962 get tail from fragments */
963 direct_len = fragment == 0xffffffff ? file->size : file->size & ~mask;
964 if (off < direct_len)
965 {
966 grub_size_t read_len = direct_len - off;
967 grub_ssize_t res;
968
969 if (read_len > len)
970 read_len = len;
971 res = direct_read (data, ino, off, buf, read_len);
972 if ((grub_size_t) res != read_len)
973 return -1; /* FIXME: is short read possible here? */
974 len -= read_len;
975 if (!len)
976 return read_len;
977 buf += read_len;
978 off = 0;
979 }
980 else
981 off -= direct_len;
982
983 err = read_chunk (data, &frag, sizeof (frag),
984 data->fragments, sizeof (frag) * fragment);
985 if (err)
986 return -1;
987 a = grub_le_to_cpu64 (frag.offset);
988 compressed = !(frag.size & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED));
989 if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
990 b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off;
991 else
992 b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
993
994 /* FIXME: cache uncompressed chunks. */
995 if (compressed)
996 {
997 char *block;
998 block = grub_malloc (grub_le_to_cpu32 (frag.size));
999 if (!block)
1000 return -1;
1001 err = grub_disk_read (data->disk,
1002 a >> GRUB_DISK_SECTOR_BITS,
1003 a & (GRUB_DISK_SECTOR_SIZE - 1),
1004 grub_le_to_cpu32 (frag.size), block);
1005 if (err)
1006 {
1007 grub_free (block);
1008 return -1;
1009 }
1010 if (data->decompress (block, grub_le_to_cpu32 (frag.size),
1011 b, buf, len, data)
1012 != (grub_ssize_t) len)
1013 {
1014 grub_free (block);
1015 if (!grub_errno)
1016 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
1017 return -1;
1018 }
1019 grub_free (block);
1020 }
1021 else
1022 {
1023 err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
1024 (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
1025 if (err)
1026 return -1;
1027 }
1028 return orig_len;
1029 }
1030
1031 static grub_err_t
1032 grub_squash_close (grub_file_t file)
1033 {
1034 squash_unmount (file->data);
1035 return GRUB_ERR_NONE;
1036 }
1037
1038 static grub_err_t
1039 grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
1040 {
1041 struct grub_squash_data *data = 0;
1042
1043 data = squash_mount (dev->disk);
1044 if (! data)
1045 return grub_errno;
1046 *tm = grub_le_to_cpu32 (data->sb.creation_time);
1047 squash_unmount (data);
1048 return GRUB_ERR_NONE;
1049 }
1050
1051 static struct grub_fs grub_squash_fs =
1052 {
1053 .name = "squash4",
1054 .fs_dir = grub_squash_dir,
1055 .fs_open = grub_squash_open,
1056 .fs_read = grub_squash_read,
1057 .fs_close = grub_squash_close,
1058 .fs_mtime = grub_squash_mtime,
1059 #ifdef GRUB_UTIL
1060 .reserved_first_sector = 0,
1061 .blocklist_install = 0,
1062 #endif
1063 .next = 0
1064 };
1065
1066 GRUB_MOD_INIT(squash4)
1067 {
1068 grub_fs_register (&grub_squash_fs);
1069 }
1070
1071 GRUB_MOD_FINI(squash4)
1072 {
1073 grub_fs_unregister (&grub_squash_fs);
1074 }
1075