Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / squash4.c
blob44af0f82a28e1064b04ed28c83c57c7fddfa4a14
1 /* squash4.c - SquashFS */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010 Free Software Foundation, Inc.
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.
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.
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/>.
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>
31 #include "xz.h"
32 #include "xz_stream.h"
34 GRUB_MOD_LICENSE ("GPLv3+");
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
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
53 struct grub_squash_super
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 } __attribute__ ((packed));
76 /* Chunk-based */
77 struct grub_squash_inode
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
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 } __attribute__ ((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 } __attribute__ ((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 } __attribute__ ((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 } __attribute__ ((packed)) long_dir;
116 struct {
117 grub_uint32_t dummy;
118 grub_uint32_t namelen;
119 char name[0];
120 } __attribute__ ((packed)) symlink;
121 } __attribute__ ((packed));
122 } __attribute__ ((packed));
124 struct grub_squash_cache_inode
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;
133 /* Chunk-based. */
134 struct grub_squash_dirent_header
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 } __attribute__ ((packed));
142 struct grub_squash_dirent
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 } __attribute__ ((packed));
152 enum
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,
162 struct grub_squash_frag_desc
164 grub_uint64_t offset;
165 grub_uint32_t size;
166 grub_uint32_t dummy;
167 } __attribute__ ((packed));
169 enum
171 SQUASH_CHUNK_FLAGS = 0x8000,
172 SQUASH_CHUNK_UNCOMPRESSED = 0x8000
175 enum
177 SQUASH_BLOCK_FLAGS = 0x1000000,
178 SQUASH_BLOCK_UNCOMPRESSED = 0x1000000
181 enum
183 COMPRESSION_ZLIB = 1,
184 COMPRESSION_LZO = 3,
185 COMPRESSION_XZ = 4,
189 #define SQUASH_CHUNK_SIZE 0x2000
190 #define XZBUFSIZ 0x2000
192 struct grub_squash_data
194 grub_disk_t disk;
195 struct grub_squash_super sb;
196 struct grub_squash_cache_inode ino;
197 grub_uint64_t fragments;
198 int log2_blksz;
199 grub_size_t blksz;
200 grub_ssize_t (*decompress) (char *inbuf, grub_size_t insize, grub_off_t off,
201 char *outbuf, grub_size_t outsize,
202 struct grub_squash_data *data);
203 struct xz_dec *xzdec;
204 char *xzbuf;
207 struct grub_fshelp_node
209 struct grub_squash_data *data;
210 struct grub_squash_inode ino;
211 grub_size_t stsize;
212 struct
214 grub_disk_addr_t ino_chunk;
215 grub_uint16_t ino_offset;
216 } stack[1];
219 static grub_err_t
220 read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len,
221 grub_uint64_t chunk_start, grub_off_t offset)
223 while (len > 0)
225 grub_uint64_t csize;
226 grub_uint16_t d;
227 grub_err_t err;
228 while (1)
230 err = grub_disk_read (data->disk,
231 chunk_start >> GRUB_DISK_SECTOR_BITS,
232 chunk_start & (GRUB_DISK_SECTOR_SIZE - 1),
233 sizeof (d), &d);
234 if (err)
235 return err;
236 if (offset < SQUASH_CHUNK_SIZE)
237 break;
238 offset -= SQUASH_CHUNK_SIZE;
239 chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS);
242 csize = SQUASH_CHUNK_SIZE - offset;
243 if (csize > len)
244 csize = len;
246 if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED)
248 grub_disk_addr_t a = chunk_start + 2 + offset;
249 err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
250 a & (GRUB_DISK_SECTOR_SIZE - 1),
251 csize, buf);
252 if (err)
253 return err;
255 else
257 char *tmp;
258 grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS;
259 grub_disk_addr_t a = chunk_start + 2;
260 tmp = grub_malloc (bsize);
261 if (!tmp)
262 return grub_errno;
263 /* FIXME: buffer uncompressed data. */
264 err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
265 a & (GRUB_DISK_SECTOR_SIZE - 1),
266 bsize, tmp);
267 if (err)
269 grub_free (tmp);
270 return err;
273 if (data->decompress (tmp, bsize, offset,
274 buf, csize, data) < 0)
276 grub_free (tmp);
277 return grub_errno;
279 grub_free (tmp);
281 len -= csize;
282 offset += csize;
283 buf = (char *) buf + csize;
285 return GRUB_ERR_NONE;
288 static grub_ssize_t
289 zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
290 char *outbuf, grub_size_t outsize,
291 struct grub_squash_data *data __attribute__ ((unused)))
293 return grub_zlib_decompress (inbuf, insize, off, outbuf, outsize);
296 static grub_ssize_t
297 lzo_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
298 char *outbuf, grub_size_t len, struct grub_squash_data *data)
300 lzo_uint usize = data->blksz;
301 grub_uint8_t *udata;
303 if (usize < 8192)
304 usize = 8192;
306 udata = grub_malloc (usize);
307 if (!udata)
308 return -1;
310 if (lzo1x_decompress_safe ((grub_uint8_t *) inbuf,
311 insize, udata, &usize, NULL) != LZO_E_OK)
313 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
314 grub_free (udata);
315 return -1;
317 grub_memcpy (outbuf, udata + off, len);
318 grub_free (udata);
319 return len;
322 static grub_ssize_t
323 xz_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
324 char *outbuf, grub_size_t len, struct grub_squash_data *data)
326 grub_size_t ret = 0;
327 grub_off_t pos = 0;
328 struct xz_buf buf;
330 xz_dec_reset (data->xzdec);
331 buf.in = (grub_uint8_t *) inbuf;
332 buf.in_pos = 0;
333 buf.in_size = insize;
334 buf.out = (grub_uint8_t *) data->xzbuf;
335 buf.out_pos = 0;
336 buf.out_size = XZBUFSIZ;
338 while (len)
340 enum xz_ret xzret;
342 buf.out_pos = 0;
344 xzret = xz_dec_run (data->xzdec, &buf);
346 if (xzret != XZ_OK && xzret != XZ_STREAM_END)
348 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "invalid xz chunk");
349 return -1;
351 if (pos + buf.out_pos >= off)
353 grub_ssize_t outoff = pos - off;
354 grub_size_t l;
355 if (outoff >= 0)
357 l = buf.out_pos;
358 if (l > len)
359 l = len;
360 grub_memcpy (outbuf + outoff, buf.out, l);
362 else
364 outoff = -outoff;
365 l = buf.out_pos - outoff;
366 if (l > len)
367 l = len;
368 grub_memcpy (outbuf, buf.out + outoff, l);
370 ret += l;
371 len -= l;
373 pos += buf.out_pos;
374 if (xzret == XZ_STREAM_END)
375 break;
377 return ret;
380 static struct grub_squash_data *
381 squash_mount (grub_disk_t disk)
383 struct grub_squash_super sb;
384 grub_err_t err;
385 struct grub_squash_data *data;
386 grub_uint64_t frag;
388 err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb);
389 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
390 grub_error (GRUB_ERR_BAD_FS, "not a squash4");
391 if (err)
392 return NULL;
393 if (sb.magic != grub_cpu_to_le32_compile_time (SQUASH_MAGIC)
394 || sb.block_size == 0
395 || ((sb.block_size - 1) & sb.block_size))
397 grub_error (GRUB_ERR_BAD_FS, "not squash4");
398 return NULL;
401 err = grub_disk_read (disk,
402 grub_le_to_cpu64 (sb.unk1offset)
403 >> GRUB_DISK_SECTOR_BITS,
404 grub_le_to_cpu64 (sb.unk1offset)
405 & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag);
406 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
407 grub_error (GRUB_ERR_BAD_FS, "not a squash4");
408 if (err)
409 return NULL;
411 data = grub_zalloc (sizeof (*data));
412 if (!data)
413 return NULL;
414 data->sb = sb;
415 data->disk = disk;
416 data->fragments = grub_le_to_cpu64 (frag);
418 switch (sb.compression)
420 case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB):
421 data->decompress = zlib_decompress;
422 break;
423 case grub_cpu_to_le16_compile_time (COMPRESSION_LZO):
424 data->decompress = lzo_decompress;
425 break;
426 case grub_cpu_to_le16_compile_time (COMPRESSION_XZ):
427 data->decompress = xz_decompress;
428 data->xzbuf = grub_malloc (XZBUFSIZ);
429 if (!data->xzbuf)
431 grub_free (data);
432 return NULL;
434 data->xzdec = xz_dec_init (1 << 16);
435 if (!data->xzdec)
437 grub_free (data->xzbuf);
438 grub_free (data);
439 return NULL;
441 break;
442 default:
443 grub_free (data);
444 grub_error (GRUB_ERR_BAD_FS, "unsupported compression %d",
445 grub_le_to_cpu16 (sb.compression));
446 return NULL;
449 data->blksz = grub_le_to_cpu32 (data->sb.block_size);
450 for (data->log2_blksz = 0;
451 (1U << data->log2_blksz) < data->blksz;
452 data->log2_blksz++);
454 return data;
457 static char *
458 grub_squash_read_symlink (grub_fshelp_node_t node)
460 char *ret;
461 grub_err_t err;
462 ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
464 err = read_chunk (node->data, ret,
465 grub_le_to_cpu32 (node->ino.symlink.namelen),
466 grub_le_to_cpu64 (node->data->sb.inodeoffset)
467 + node->stack[node->stsize - 1].ino_chunk,
468 node->stack[node->stsize - 1].ino_offset
469 + (node->ino.symlink.name - (char *) &node->ino));
470 if (err)
472 grub_free (ret);
473 return NULL;
475 ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0;
476 return ret;
479 static int
480 grub_squash_iterate_dir (grub_fshelp_node_t dir,
481 int NESTED_FUNC_ATTR
482 (*hook) (const char *filename,
483 enum grub_fshelp_filetype filetype,
484 grub_fshelp_node_t node))
486 grub_uint32_t off;
487 grub_uint32_t endoff;
488 grub_uint64_t chunk;
489 unsigned i;
491 /* FIXME: why - 3 ? */
492 switch (dir->ino.type)
494 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR):
495 off = grub_le_to_cpu16 (dir->ino.dir.offset);
496 endoff = grub_le_to_cpu16 (dir->ino.dir.size) + off - 3;
497 chunk = grub_le_to_cpu32 (dir->ino.dir.chunk);
498 break;
499 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR):
500 off = grub_le_to_cpu16 (dir->ino.long_dir.offset);
501 endoff = grub_le_to_cpu16 (dir->ino.long_dir.size) + off - 3;
502 chunk = grub_le_to_cpu32 (dir->ino.long_dir.chunk);
503 break;
504 default:
505 grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x",
506 grub_le_to_cpu16 (dir->ino.type));
507 return 0;
511 grub_fshelp_node_t node;
512 node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
513 if (!node)
514 return 0;
515 grub_memcpy (node, dir,
516 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
517 if (hook (".", GRUB_FSHELP_DIR, node))
518 return 1;
520 if (dir->stsize != 1)
522 grub_err_t err;
524 node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
525 if (!node)
526 return 0;
528 grub_memcpy (node, dir,
529 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
531 node->stsize--;
532 err = read_chunk (dir->data, &node->ino, sizeof (node->ino),
533 grub_le_to_cpu64 (dir->data->sb.inodeoffset)
534 + node->stack[node->stsize - 1].ino_chunk,
535 node->stack[node->stsize - 1].ino_offset);
536 if (err)
537 return 0;
539 if (hook ("..", GRUB_FSHELP_DIR, node))
540 return 1;
544 while (off < endoff)
546 struct grub_squash_dirent_header dh;
547 grub_err_t err;
549 err = read_chunk (dir->data, &dh, sizeof (dh),
550 grub_le_to_cpu64 (dir->data->sb.diroffset)
551 + chunk, off);
552 if (err)
553 return 0;
554 off += sizeof (dh);
555 for (i = 0; i < (unsigned) grub_le_to_cpu32 (dh.nelems) + 1; i++)
557 char *buf;
558 int r;
559 struct grub_fshelp_node *node;
560 enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
561 struct grub_squash_dirent di;
562 struct grub_squash_inode ino;
564 err = read_chunk (dir->data, &di, sizeof (di),
565 grub_le_to_cpu64 (dir->data->sb.diroffset)
566 + chunk, off);
567 if (err)
568 return 0;
569 off += sizeof (di);
571 err = read_chunk (dir->data, &ino, sizeof (ino),
572 grub_le_to_cpu64 (dir->data->sb.inodeoffset)
573 + grub_le_to_cpu32 (dh.ino_chunk),
574 grub_cpu_to_le16 (di.ino_offset));
575 if (err)
576 return 0;
578 buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
579 if (!buf)
580 return 0;
581 err = read_chunk (dir->data, buf,
582 grub_le_to_cpu16 (di.namelen) + 1,
583 grub_le_to_cpu64 (dir->data->sb.diroffset)
584 + chunk, off);
585 if (err)
586 return 0;
588 off += grub_le_to_cpu16 (di.namelen) + 1;
589 buf[grub_le_to_cpu16 (di.namelen) + 1] = 0;
590 if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR)
591 filetype = GRUB_FSHELP_DIR;
592 if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
593 filetype = GRUB_FSHELP_SYMLINK;
595 node = grub_malloc (sizeof (*node)
596 + (dir->stsize + 1) * sizeof (dir->stack[0]));
597 if (! node)
598 return 0;
600 grub_memcpy (node, dir,
601 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
603 node->ino = ino;
604 node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
605 node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset);
606 node->stsize++;
607 r = hook (buf, filetype, node);
609 grub_free (buf);
610 if (r)
611 return r;
614 return 0;
617 static grub_err_t
618 make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root)
620 grub_memset (root, 0, sizeof (*root));
621 root->data = data;
622 root->stsize = 1;
623 root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk);
624 root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset);
625 return read_chunk (data, &root->ino, sizeof (root->ino),
626 grub_le_to_cpu64 (data->sb.inodeoffset)
627 + root->stack[0].ino_chunk,
628 root->stack[0].ino_offset);
631 static void
632 squash_unmount (struct grub_squash_data *data)
634 if (data->xzdec)
635 xz_dec_end (data->xzdec);
636 grub_free (data->xzbuf);
637 grub_free (data->ino.cumulated_block_sizes);
638 grub_free (data->ino.block_sizes);
639 grub_free (data);
643 static grub_err_t
644 grub_squash_dir (grub_device_t device, const char *path,
645 int (*hook) (const char *filename,
646 const struct grub_dirhook_info *info))
648 auto int NESTED_FUNC_ATTR iterate (const char *filename,
649 enum grub_fshelp_filetype filetype,
650 grub_fshelp_node_t node);
652 int NESTED_FUNC_ATTR iterate (const char *filename,
653 enum grub_fshelp_filetype filetype,
654 grub_fshelp_node_t node)
656 struct grub_dirhook_info info;
657 grub_memset (&info, 0, sizeof (info));
658 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
659 info.mtimeset = 1;
660 info.mtime = grub_le_to_cpu32 (node->ino.mtime);
661 grub_free (node);
662 return hook (filename, &info);
665 struct grub_squash_data *data = 0;
666 struct grub_fshelp_node *fdiro = 0;
667 struct grub_fshelp_node root;
668 grub_err_t err;
670 data = squash_mount (device->disk);
671 if (! data)
672 return grub_errno;
674 err = make_root_node (data, &root);
675 if (err)
676 return err;
678 grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
679 grub_squash_read_symlink, GRUB_FSHELP_DIR);
680 if (!grub_errno)
681 grub_squash_iterate_dir (fdiro, iterate);
683 squash_unmount (data);
685 return grub_errno;
688 static grub_err_t
689 grub_squash_open (struct grub_file *file, const char *name)
691 struct grub_squash_data *data = 0;
692 struct grub_fshelp_node *fdiro = 0;
693 struct grub_fshelp_node root;
694 grub_err_t err;
696 data = squash_mount (file->device->disk);
697 if (! data)
698 return grub_errno;
700 err = make_root_node (data, &root);
701 if (err)
702 return err;
704 grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir,
705 grub_squash_read_symlink, GRUB_FSHELP_REG);
706 if (grub_errno)
708 squash_unmount (data);
709 return grub_errno;
712 file->data = data;
713 data->ino.ino = fdiro->ino;
714 data->ino.block_sizes = NULL;
715 data->ino.cumulated_block_sizes = NULL;
716 data->ino.ino_chunk = fdiro->stack[fdiro->stsize - 1].ino_chunk;
717 data->ino.ino_offset = fdiro->stack[fdiro->stsize - 1].ino_offset;
719 switch (fdiro->ino.type)
721 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
722 file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
723 break;
724 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
725 file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
726 break;
727 default:
729 grub_uint16_t type = grub_le_to_cpu16 (fdiro->ino.type);
730 grub_free (fdiro);
731 squash_unmount (data);
732 return grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", type);
736 grub_free (fdiro);
738 return GRUB_ERR_NONE;
741 static grub_ssize_t
742 direct_read (struct grub_squash_data *data,
743 struct grub_squash_cache_inode *ino,
744 grub_off_t off, char *buf, grub_size_t len)
746 grub_err_t err;
747 grub_off_t cumulated_uncompressed_size = 0;
748 grub_uint64_t a = 0;
749 grub_size_t i;
750 grub_size_t origlen = len;
752 switch (ino->ino.type)
754 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
755 a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
756 break;
757 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
758 a = grub_le_to_cpu32 (ino->ino.file.chunk);
759 break;
762 if (!ino->block_sizes)
764 grub_off_t total_size = 0;
765 grub_size_t total_blocks;
766 grub_size_t block_offset = 0;
767 switch (ino->ino.type)
769 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
770 total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
771 block_offset = ((char *) &ino->ino.long_file.block_size
772 - (char *) &ino->ino);
773 break;
774 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
775 total_size = grub_le_to_cpu32 (ino->ino.file.size);
776 block_offset = ((char *) &ino->ino.file.block_size
777 - (char *) &ino->ino);
778 break;
780 total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz);
781 ino->block_sizes = grub_malloc (total_blocks
782 * sizeof (ino->block_sizes[0]));
783 ino->cumulated_block_sizes = grub_malloc (total_blocks
784 * sizeof (ino->cumulated_block_sizes[0]));
785 if (!ino->block_sizes || !ino->cumulated_block_sizes)
787 grub_free (ino->block_sizes);
788 grub_free (ino->cumulated_block_sizes);
789 ino->block_sizes = 0;
790 ino->cumulated_block_sizes = 0;
791 return -1;
793 err = read_chunk (data, ino->block_sizes,
794 total_blocks * sizeof (ino->block_sizes[0]),
795 grub_le_to_cpu64 (data->sb.inodeoffset)
796 + ino->ino_chunk,
797 ino->ino_offset + block_offset);
798 if (err)
800 grub_free (ino->block_sizes);
801 grub_free (ino->cumulated_block_sizes);
802 ino->block_sizes = 0;
803 ino->cumulated_block_sizes = 0;
804 return -1;
806 ino->cumulated_block_sizes[0] = 0;
807 for (i = 1; i < total_blocks; i++)
808 ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
809 + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
812 if (a == 0)
813 a = sizeof (struct grub_squash_super);
814 i = off >> data->log2_blksz;
815 cumulated_uncompressed_size = data->blksz * (grub_disk_addr_t) i;
816 while (cumulated_uncompressed_size < off + len)
818 grub_size_t boff, curread;
819 boff = off - cumulated_uncompressed_size;
820 curread = data->blksz - boff;
821 if (curread > len)
822 curread = len;
823 if (!(ino->block_sizes[i]
824 & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)))
826 char *block;
827 grub_size_t csize;
828 csize = grub_le_to_cpu32 (ino->block_sizes[i]) & ~SQUASH_BLOCK_FLAGS;
829 block = grub_malloc (csize);
830 if (!block)
831 return -1;
832 err = grub_disk_read (data->disk,
833 (ino->cumulated_block_sizes[i] + a)
834 >> GRUB_DISK_SECTOR_BITS,
835 (ino->cumulated_block_sizes[i] + a)
836 & (GRUB_DISK_SECTOR_SIZE - 1),
837 csize, block);
838 if (err)
840 grub_free (block);
841 return -1;
843 if (data->decompress (block, csize, boff, buf, curread, data)
844 != (grub_ssize_t) curread)
846 grub_free (block);
847 if (!grub_errno)
848 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
849 return -1;
851 grub_free (block);
853 else
854 err = grub_disk_read (data->disk,
855 (ino->cumulated_block_sizes[i] + a + boff)
856 >> GRUB_DISK_SECTOR_BITS,
857 (ino->cumulated_block_sizes[i] + a + boff)
858 & (GRUB_DISK_SECTOR_SIZE - 1),
859 curread, buf);
860 if (err)
861 return -1;
862 off += curread;
863 len -= curread;
864 buf += curread;
865 cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
866 i++;
868 return origlen;
872 static grub_ssize_t
873 grub_squash_read_data (struct grub_squash_data *data,
874 struct grub_squash_cache_inode *ino,
875 grub_off_t off, char *buf, grub_size_t len)
877 grub_err_t err;
878 grub_uint64_t a = 0, b;
879 grub_uint32_t fragment = 0;
880 int compressed = 0;
881 struct grub_squash_frag_desc frag;
883 switch (ino->ino.type)
885 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
886 a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
887 fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
888 break;
889 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
890 a = grub_le_to_cpu32 (ino->ino.file.chunk);
891 fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
892 break;
895 if (fragment == 0xffffffff)
896 return direct_read (data, ino, off, buf, len);
898 err = read_chunk (data, &frag, sizeof (frag),
899 data->fragments, sizeof (frag) * fragment);
900 if (err)
901 return -1;
902 a += grub_le_to_cpu64 (frag.offset);
903 compressed = !(frag.size & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED));
904 if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
905 b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off;
906 else
907 b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
909 /* FIXME: cache uncompressed chunks. */
910 if (compressed)
912 char *block;
913 block = grub_malloc (grub_le_to_cpu32 (frag.size));
914 if (!block)
915 return -1;
916 err = grub_disk_read (data->disk,
917 a >> GRUB_DISK_SECTOR_BITS,
918 a & (GRUB_DISK_SECTOR_SIZE - 1),
919 grub_le_to_cpu32 (frag.size), block);
920 if (err)
922 grub_free (block);
923 return -1;
925 if (data->decompress (block, grub_le_to_cpu32 (frag.size),
926 b, buf, len, data)
927 != (grub_ssize_t) len)
929 grub_free (block);
930 if (!grub_errno)
931 grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
932 return -1;
934 grub_free (block);
936 else
938 err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
939 (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
940 if (err)
941 return -1;
943 return len;
946 static grub_ssize_t
947 grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
949 struct grub_squash_data *data = file->data;
951 return grub_squash_read_data (data, &data->ino,
952 file->offset, buf, len);
955 static grub_err_t
956 grub_squash_close (grub_file_t file)
958 squash_unmount (file->data);
959 return GRUB_ERR_NONE;
962 static grub_err_t
963 grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
965 struct grub_squash_data *data = 0;
967 data = squash_mount (dev->disk);
968 if (! data)
969 return grub_errno;
970 *tm = grub_le_to_cpu32 (data->sb.creation_time);
971 squash_unmount (data);
972 return GRUB_ERR_NONE;
975 static struct grub_fs grub_squash_fs =
977 .name = "squash4",
978 .dir = grub_squash_dir,
979 .open = grub_squash_open,
980 .read = grub_squash_read,
981 .close = grub_squash_close,
982 .mtime = grub_squash_mtime,
983 #ifdef GRUB_UTIL
984 .reserved_first_sector = 0,
985 .blocklist_install = 0,
986 #endif
987 .next = 0
990 GRUB_MOD_INIT(squash4)
992 grub_fs_register (&grub_squash_fs);
995 GRUB_MOD_FINI(squash4)
997 grub_fs_unregister (&grub_squash_fs);