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