1 /* squash4.c - SquashFS */
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/>.
21 #include <grub/file.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
26 #include <grub/types.h>
27 #include <grub/fshelp.h>
28 #include <grub/deflate.h>
33 #include "xz_stream.h"
35 GRUB_MOD_LICENSE ("GPLv3+");
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
46 UID/GID Chunk exttblptr
47 exttblptr RAW superblock
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
54 struct grub_squash_super
57 #define SQUASH_MAGIC 0x73717368
59 grub_uint32_t creation_time
;
60 grub_uint32_t block_size
;
62 grub_uint16_t compression
;
65 grub_uint16_t root_ino_offset
;
66 grub_uint32_t root_ino_chunk
;
68 grub_uint64_t total_size
;
69 grub_uint64_t exttbloffset
;
71 grub_uint64_t inodeoffset
;
72 grub_uint64_t diroffset
;
73 grub_uint64_t unk1offset
;
74 grub_uint64_t unk2offset
;
78 struct grub_squash_inode
80 /* Same values as direlem types. */
82 grub_uint16_t dummy
[3];
89 grub_uint32_t fragment
;
92 grub_uint32_t block_size
[0];
97 grub_uint32_t dummy1
[3];
98 grub_uint32_t fragment
;
100 grub_uint32_t dummy3
;
101 grub_uint32_t block_size
[0];
102 } GRUB_PACKED long_file
;
107 grub_uint16_t offset
;
110 grub_uint32_t dummy1
;
113 grub_uint32_t dummy2
;
114 grub_uint16_t dummy3
;
115 grub_uint16_t offset
;
116 } GRUB_PACKED long_dir
;
119 grub_uint32_t namelen
;
121 } GRUB_PACKED symlink
;
125 struct grub_squash_cache_inode
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
;
135 struct grub_squash_dirent_header
137 /* Actually the value is the number of elements - 1. */
138 grub_uint32_t nelems
;
139 grub_uint32_t ino_chunk
;
143 struct grub_squash_dirent
145 grub_uint16_t ino_offset
;
148 /* Actually the value is the length of name - 1. */
149 grub_uint16_t namelen
;
156 SQUASH_TYPE_REGULAR
= 2,
157 SQUASH_TYPE_SYMLINK
= 3,
158 SQUASH_TYPE_LONG_DIR
= 8,
159 SQUASH_TYPE_LONG_REGULAR
= 9,
163 struct grub_squash_frag_desc
165 grub_uint64_t offset
;
172 SQUASH_CHUNK_FLAGS
= 0x8000,
173 SQUASH_CHUNK_UNCOMPRESSED
= 0x8000
178 SQUASH_BLOCK_FLAGS
= 0x1000000,
179 SQUASH_BLOCK_UNCOMPRESSED
= 0x1000000
184 COMPRESSION_ZLIB
= 1,
188 COMPRESSION_ZSTD
= 6,
192 #define SQUASH_CHUNK_SIZE 0x2000
193 #define XZBUFSIZ 0x2000
195 struct grub_squash_data
198 struct grub_squash_super sb
;
199 struct grub_squash_cache_inode ino
;
200 grub_uint64_t fragments
;
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
;
210 struct grub_fshelp_node
212 struct grub_squash_data
*data
;
213 struct grub_squash_inode ino
;
217 grub_disk_addr_t ino_chunk
;
218 grub_uint16_t ino_offset
;
223 read_chunk (struct grub_squash_data
*data
, void *buf
, grub_size_t len
,
224 grub_uint64_t chunk_start
, grub_off_t offset
)
233 err
= grub_disk_read (data
->disk
,
234 chunk_start
>> GRUB_DISK_SECTOR_BITS
,
235 chunk_start
& (GRUB_DISK_SECTOR_SIZE
- 1),
239 if (offset
< SQUASH_CHUNK_SIZE
)
241 offset
-= SQUASH_CHUNK_SIZE
;
242 chunk_start
+= 2 + (grub_le_to_cpu16 (d
) & ~SQUASH_CHUNK_FLAGS
);
245 csize
= SQUASH_CHUNK_SIZE
- offset
;
249 if (grub_le_to_cpu16 (d
) & SQUASH_CHUNK_UNCOMPRESSED
)
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),
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
);
266 /* FIXME: buffer uncompressed data. */
267 err
= grub_disk_read (data
->disk
, (a
>> GRUB_DISK_SECTOR_BITS
),
268 a
& (GRUB_DISK_SECTOR_SIZE
- 1),
276 if (data
->decompress (tmp
, bsize
, offset
,
277 buf
, csize
, data
) < 0)
286 buf
= (char *) buf
+ csize
;
288 return GRUB_ERR_NONE
;
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
)))
296 return grub_zlib_decompress (inbuf
, insize
, off
, outbuf
, outsize
);
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
)
303 lzo_uint usize
= data
->blksz
;
309 udata
= grub_malloc (usize
);
313 if (lzo1x_decompress_safe ((grub_uint8_t
*) inbuf
,
314 insize
, udata
, &usize
, NULL
) != LZO_E_OK
)
316 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
320 grub_memcpy (outbuf
, udata
+ off
, len
);
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
)
333 xz_dec_reset (data
->xzdec
);
334 buf
.in
= (grub_uint8_t
*) inbuf
;
336 buf
.in_size
= insize
;
337 buf
.out
= (grub_uint8_t
*) data
->xzbuf
;
339 buf
.out_size
= XZBUFSIZ
;
347 xzret
= xz_dec_run (data
->xzdec
, &buf
);
349 if (xzret
!= XZ_OK
&& xzret
!= XZ_STREAM_END
)
351 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA
, "invalid xz chunk");
354 if (pos
+ buf
.out_pos
>= off
)
356 grub_ssize_t outoff
= pos
- off
;
363 grub_memcpy (outbuf
+ outoff
, buf
.out
, l
);
368 l
= buf
.out_pos
- outoff
;
371 grub_memcpy (outbuf
, buf
.out
+ outoff
, l
);
377 if (xzret
== XZ_STREAM_END
)
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
)
388 int usize
= data
->blksz
;
393 udata
= grub_malloc (usize
);
397 LZ4_uncompress_unknownOutputSize(inbuf
, udata
, insize
, usize
);
398 grub_memcpy (outbuf
, udata
+ off
, len
);
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
)
407 int usize
= data
->blksz
;
411 udata
= grub_malloc (usize
);
415 ZSTD_decompress(udata
, usize
, inbuf
, insize
);
416 grub_memcpy(outbuf
, udata
+ off
, len
);
422 static struct grub_squash_data
*
423 squash_mount (grub_disk_t disk
)
425 struct grub_squash_super sb
;
427 struct grub_squash_data
*data
;
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");
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
))
439 grub_error (GRUB_ERR_BAD_FS
, "not squash4");
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");
453 data
= grub_zalloc (sizeof (*data
));
458 data
->fragments
= grub_le_to_cpu64 (frag
);
460 switch (sb
.compression
)
462 case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB
):
463 data
->decompress
= zlib_decompress
;
465 case grub_cpu_to_le16_compile_time (COMPRESSION_LZO
):
466 data
->decompress
= lzo_decompress
;
468 case grub_cpu_to_le16_compile_time (COMPRESSION_LZ4
):
469 data
->decompress
= lz4_decompress_wrap
;
471 case grub_cpu_to_le16_compile_time (COMPRESSION_ZSTD
):
472 data
->decompress
= zstd_decompress_wrap
;
474 case grub_cpu_to_le16_compile_time (COMPRESSION_XZ
):
475 data
->decompress
= xz_decompress
;
476 data
->xzbuf
= grub_malloc (XZBUFSIZ
);
482 data
->xzdec
= xz_dec_init (1 << 16);
485 grub_free (data
->xzbuf
);
492 grub_error (GRUB_ERR_BAD_FS
, "unsupported compression %d",
493 grub_le_to_cpu16 (sb
.compression
));
497 data
->blksz
= grub_le_to_cpu32 (data
->sb
.block_size
);
498 for (data
->log2_blksz
= 0;
499 (1U << data
->log2_blksz
) < data
->blksz
;
506 grub_squash_read_symlink (grub_fshelp_node_t node
)
510 ret
= grub_malloc (grub_le_to_cpu32 (node
->ino
.symlink
.namelen
) + 1);
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
));
523 ret
[grub_le_to_cpu32 (node
->ino
.symlink
.namelen
)] = 0;
528 grub_squash_iterate_dir (grub_fshelp_node_t dir
,
529 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
532 grub_uint32_t endoff
;
536 /* FIXME: why - 3 ? */
537 switch (dir
->ino
.type
)
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
);
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
);
550 grub_error (GRUB_ERR_BAD_FS
, "unexpected ino type 0x%x",
551 grub_le_to_cpu16 (dir
->ino
.type
));
556 grub_fshelp_node_t node
;
557 node
= grub_malloc (sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
560 grub_memcpy (node
, dir
,
561 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
562 if (hook (".", GRUB_FSHELP_DIR
, node
, hook_data
))
565 if (dir
->stsize
!= 1)
569 node
= grub_malloc (sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
573 grub_memcpy (node
, dir
,
574 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
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
);
584 if (hook ("..", GRUB_FSHELP_DIR
, node
, hook_data
))
591 struct grub_squash_dirent_header dh
;
594 err
= read_chunk (dir
->data
, &dh
, sizeof (dh
),
595 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
600 for (i
= 0; i
< (unsigned) grub_le_to_cpu32 (dh
.nelems
) + 1; i
++)
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
;
609 err
= read_chunk (dir
->data
, &di
, sizeof (di
),
610 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
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
));
623 buf
= grub_malloc (grub_le_to_cpu16 (di
.namelen
) + 2);
626 err
= read_chunk (dir
->data
, buf
,
627 grub_le_to_cpu16 (di
.namelen
) + 1,
628 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
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
;
640 node
= grub_malloc (sizeof (*node
)
641 + (dir
->stsize
+ 1) * sizeof (dir
->stack
[0]));
645 grub_memcpy (node
, dir
,
646 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
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
);
652 r
= hook (buf
, filetype
, node
, hook_data
);
663 make_root_node (struct grub_squash_data
*data
, struct grub_fshelp_node
*root
)
665 grub_memset (root
, 0, sizeof (*root
));
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
);
677 squash_unmount (struct grub_squash_data
*data
)
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
);
688 /* Context for grub_squash_dir. */
689 struct grub_squash_dir_ctx
691 grub_fs_dir_hook_t hook
;
695 /* Helper for grub_squash_dir. */
697 grub_squash_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
698 grub_fshelp_node_t node
, void *data
)
700 struct grub_squash_dir_ctx
*ctx
= data
;
701 struct grub_dirhook_info info
;
703 grub_memset (&info
, 0, sizeof (info
));
704 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
706 info
.mtime
= grub_le_to_cpu32 (node
->ino
.mtime
);
708 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
712 grub_squash_dir (grub_device_t device
, const char *path
,
713 grub_fs_dir_hook_t hook
, void *hook_data
)
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
;
721 data
= squash_mount (device
->disk
);
725 err
= make_root_node (data
, &root
);
729 grub_fshelp_find_file (path
, &root
, &fdiro
, grub_squash_iterate_dir
,
730 grub_squash_read_symlink
, GRUB_FSHELP_DIR
);
732 grub_squash_iterate_dir (fdiro
, grub_squash_dir_iter
, &ctx
);
734 squash_unmount (data
);
740 grub_squash_open (struct grub_file
*file
, const char *name
)
742 struct grub_squash_data
*data
= 0;
743 struct grub_fshelp_node
*fdiro
= 0;
744 struct grub_fshelp_node root
;
747 data
= squash_mount (file
->device
->disk
);
751 err
= make_root_node (data
, &root
);
755 grub_fshelp_find_file (name
, &root
, &fdiro
, grub_squash_iterate_dir
,
756 grub_squash_read_symlink
, GRUB_FSHELP_REG
);
759 squash_unmount (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
;
770 switch (fdiro
->ino
.type
)
772 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
773 file
->size
= grub_le_to_cpu64 (fdiro
->ino
.long_file
.size
);
775 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
776 file
->size
= grub_le_to_cpu32 (fdiro
->ino
.file
.size
);
780 grub_uint16_t type
= grub_le_to_cpu16 (fdiro
->ino
.type
);
782 squash_unmount (data
);
783 return grub_error (GRUB_ERR_BAD_FS
, "unexpected ino type 0x%x", type
);
789 return GRUB_ERR_NONE
;
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
)
798 grub_off_t cumulated_uncompressed_size
= 0;
801 grub_size_t origlen
= len
;
803 switch (ino
->ino
.type
)
805 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
806 a
= grub_le_to_cpu64 (ino
->ino
.long_file
.chunk
);
808 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
809 a
= grub_le_to_cpu32 (ino
->ino
.file
.chunk
);
813 if (!ino
->block_sizes
)
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
)
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
);
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
);
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
)
838 grub_free (ino
->block_sizes
);
839 grub_free (ino
->cumulated_block_sizes
);
840 ino
->block_sizes
= 0;
841 ino
->cumulated_block_sizes
= 0;
844 err
= read_chunk (data
, ino
->block_sizes
,
845 total_blocks
* sizeof (ino
->block_sizes
[0]),
846 grub_le_to_cpu64 (data
->sb
.inodeoffset
)
848 ino
->ino_offset
+ block_offset
);
851 grub_free (ino
->block_sizes
);
852 grub_free (ino
->cumulated_block_sizes
);
853 ino
->block_sizes
= 0;
854 ino
->cumulated_block_sizes
= 0;
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
);
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
)
869 grub_size_t boff
, curread
;
870 boff
= off
- cumulated_uncompressed_size
;
871 curread
= data
->blksz
- boff
;
874 if (!ino
->block_sizes
[i
])
877 grub_memset (buf
, '\0', curread
);
879 else if (!(ino
->block_sizes
[i
]
880 & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED
)))
884 csize
= grub_le_to_cpu32 (ino
->block_sizes
[i
]) & ~SQUASH_BLOCK_FLAGS
;
885 block
= grub_malloc (csize
);
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),
899 if (data
->decompress (block
, csize
, boff
, buf
, curread
, data
)
900 != (grub_ssize_t
) curread
)
904 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
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),
921 cumulated_uncompressed_size
+= grub_le_to_cpu32 (data
->sb
.block_size
);
929 grub_squash_read (grub_file_t file
, char *buf
, grub_size_t len
)
931 struct grub_squash_data
*data
= file
->data
;
932 struct grub_squash_cache_inode
*ino
= &data
->ino
;
933 grub_off_t off
= file
->offset
;
936 grub_uint32_t fragment
= 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
;
943 switch (ino
->ino
.type
)
945 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
946 fragment
= grub_le_to_cpu32 (ino
->ino
.long_file
.fragment
);
948 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
949 fragment
= grub_le_to_cpu32 (ino
->ino
.file
.fragment
);
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
)
958 grub_size_t read_len
= direct_len
- off
;
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? */
975 err
= read_chunk (data
, &frag
, sizeof (frag
),
976 data
->fragments
, sizeof (frag
) * fragment
);
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
;
984 b
= grub_le_to_cpu32 (ino
->ino
.file
.offset
) + off
;
986 /* FIXME: cache uncompressed chunks. */
990 block
= grub_malloc (grub_le_to_cpu32 (frag
.size
));
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
);
1002 if (data
->decompress (block
, grub_le_to_cpu32 (frag
.size
),
1004 != (grub_ssize_t
) len
)
1008 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
1015 err
= grub_disk_read (data
->disk
, (a
+ b
) >> GRUB_DISK_SECTOR_BITS
,
1016 (a
+ b
) & (GRUB_DISK_SECTOR_SIZE
- 1), len
, buf
);
1024 grub_squash_close (grub_file_t file
)
1026 squash_unmount (file
->data
);
1027 return GRUB_ERR_NONE
;
1031 grub_squash_mtime (grub_device_t dev
, grub_int32_t
*tm
)
1033 struct grub_squash_data
*data
= 0;
1035 data
= squash_mount (dev
->disk
);
1038 *tm
= grub_le_to_cpu32 (data
->sb
.creation_time
);
1039 squash_unmount (data
);
1040 return GRUB_ERR_NONE
;
1043 static struct grub_fs grub_squash_fs
=
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
,
1052 .reserved_first_sector
= 0,
1053 .blocklist_install
= 0,
1058 GRUB_MOD_INIT(squash4
)
1060 grub_fs_register (&grub_squash_fs
);
1063 GRUB_MOD_FINI(squash4
)
1065 grub_fs_unregister (&grub_squash_fs
);