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 ZSTD_decompress(outbuf
, len
, inbuf
, insize
);
418 udata
= grub_malloc (usize
);
422 ZSTD_decompress(udata
, usize
, inbuf
, insize
);
423 grub_memcpy(outbuf
, udata
+ off
, len
);
430 static struct grub_squash_data
*
431 squash_mount (grub_disk_t disk
)
433 struct grub_squash_super sb
;
435 struct grub_squash_data
*data
;
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");
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
))
447 grub_error (GRUB_ERR_BAD_FS
, "not squash4");
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");
461 data
= grub_zalloc (sizeof (*data
));
466 data
->fragments
= grub_le_to_cpu64 (frag
);
468 switch (sb
.compression
)
470 case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB
):
471 data
->decompress
= zlib_decompress
;
473 case grub_cpu_to_le16_compile_time (COMPRESSION_LZO
):
474 data
->decompress
= lzo_decompress
;
476 case grub_cpu_to_le16_compile_time (COMPRESSION_LZ4
):
477 data
->decompress
= lz4_decompress_wrap
;
479 case grub_cpu_to_le16_compile_time (COMPRESSION_ZSTD
):
480 data
->decompress
= zstd_decompress_wrap
;
482 case grub_cpu_to_le16_compile_time (COMPRESSION_XZ
):
483 data
->decompress
= xz_decompress
;
484 data
->xzbuf
= grub_malloc (XZBUFSIZ
);
490 data
->xzdec
= xz_dec_init (1 << 16);
493 grub_free (data
->xzbuf
);
500 grub_error (GRUB_ERR_BAD_FS
, "unsupported compression %d",
501 grub_le_to_cpu16 (sb
.compression
));
505 data
->blksz
= grub_le_to_cpu32 (data
->sb
.block_size
);
506 for (data
->log2_blksz
= 0;
507 (1U << data
->log2_blksz
) < data
->blksz
;
514 grub_squash_read_symlink (grub_fshelp_node_t node
)
518 ret
= grub_malloc (grub_le_to_cpu32 (node
->ino
.symlink
.namelen
) + 1);
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
));
531 ret
[grub_le_to_cpu32 (node
->ino
.symlink
.namelen
)] = 0;
536 grub_squash_iterate_dir (grub_fshelp_node_t dir
,
537 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
540 grub_uint32_t endoff
;
544 /* FIXME: why - 3 ? */
545 switch (dir
->ino
.type
)
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
);
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
);
558 grub_error (GRUB_ERR_BAD_FS
, "unexpected ino type 0x%x",
559 grub_le_to_cpu16 (dir
->ino
.type
));
564 grub_fshelp_node_t node
;
565 node
= grub_malloc (sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
568 grub_memcpy (node
, dir
,
569 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
570 if (hook (".", GRUB_FSHELP_DIR
, node
, hook_data
))
573 if (dir
->stsize
!= 1)
577 node
= grub_malloc (sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
581 grub_memcpy (node
, dir
,
582 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
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
);
592 if (hook ("..", GRUB_FSHELP_DIR
, node
, hook_data
))
599 struct grub_squash_dirent_header dh
;
602 err
= read_chunk (dir
->data
, &dh
, sizeof (dh
),
603 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
608 for (i
= 0; i
< (unsigned) grub_le_to_cpu32 (dh
.nelems
) + 1; i
++)
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
;
617 err
= read_chunk (dir
->data
, &di
, sizeof (di
),
618 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
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
));
631 buf
= grub_malloc (grub_le_to_cpu16 (di
.namelen
) + 2);
634 err
= read_chunk (dir
->data
, buf
,
635 grub_le_to_cpu16 (di
.namelen
) + 1,
636 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
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
;
648 node
= grub_malloc (sizeof (*node
)
649 + (dir
->stsize
+ 1) * sizeof (dir
->stack
[0]));
653 grub_memcpy (node
, dir
,
654 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
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
);
660 r
= hook (buf
, filetype
, node
, hook_data
);
671 make_root_node (struct grub_squash_data
*data
, struct grub_fshelp_node
*root
)
673 grub_memset (root
, 0, sizeof (*root
));
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
);
685 squash_unmount (struct grub_squash_data
*data
)
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
);
696 /* Context for grub_squash_dir. */
697 struct grub_squash_dir_ctx
699 grub_fs_dir_hook_t hook
;
703 /* Helper for grub_squash_dir. */
705 grub_squash_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
706 grub_fshelp_node_t node
, void *data
)
708 struct grub_squash_dir_ctx
*ctx
= data
;
709 struct grub_dirhook_info info
;
711 grub_memset (&info
, 0, sizeof (info
));
712 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
714 info
.mtime
= grub_le_to_cpu32 (node
->ino
.mtime
);
716 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
720 grub_squash_dir (grub_device_t device
, const char *path
,
721 grub_fs_dir_hook_t hook
, void *hook_data
)
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
;
729 data
= squash_mount (device
->disk
);
733 err
= make_root_node (data
, &root
);
737 grub_fshelp_find_file (path
, &root
, &fdiro
, grub_squash_iterate_dir
,
738 grub_squash_read_symlink
, GRUB_FSHELP_DIR
);
740 grub_squash_iterate_dir (fdiro
, grub_squash_dir_iter
, &ctx
);
742 squash_unmount (data
);
748 grub_squash_open (struct grub_file
*file
, const char *name
)
750 struct grub_squash_data
*data
= 0;
751 struct grub_fshelp_node
*fdiro
= 0;
752 struct grub_fshelp_node root
;
755 data
= squash_mount (file
->device
->disk
);
759 err
= make_root_node (data
, &root
);
763 grub_fshelp_find_file (name
, &root
, &fdiro
, grub_squash_iterate_dir
,
764 grub_squash_read_symlink
, GRUB_FSHELP_REG
);
767 squash_unmount (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
;
778 switch (fdiro
->ino
.type
)
780 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
781 file
->size
= grub_le_to_cpu64 (fdiro
->ino
.long_file
.size
);
783 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
784 file
->size
= grub_le_to_cpu32 (fdiro
->ino
.file
.size
);
788 grub_uint16_t type
= grub_le_to_cpu16 (fdiro
->ino
.type
);
790 squash_unmount (data
);
791 return grub_error (GRUB_ERR_BAD_FS
, "unexpected ino type 0x%x", type
);
797 return GRUB_ERR_NONE
;
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
)
806 grub_off_t cumulated_uncompressed_size
= 0;
809 grub_size_t origlen
= len
;
811 switch (ino
->ino
.type
)
813 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
814 a
= grub_le_to_cpu64 (ino
->ino
.long_file
.chunk
);
816 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
817 a
= grub_le_to_cpu32 (ino
->ino
.file
.chunk
);
821 if (!ino
->block_sizes
)
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
)
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
);
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
);
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
)
846 grub_free (ino
->block_sizes
);
847 grub_free (ino
->cumulated_block_sizes
);
848 ino
->block_sizes
= 0;
849 ino
->cumulated_block_sizes
= 0;
852 err
= read_chunk (data
, ino
->block_sizes
,
853 total_blocks
* sizeof (ino
->block_sizes
[0]),
854 grub_le_to_cpu64 (data
->sb
.inodeoffset
)
856 ino
->ino_offset
+ block_offset
);
859 grub_free (ino
->block_sizes
);
860 grub_free (ino
->cumulated_block_sizes
);
861 ino
->block_sizes
= 0;
862 ino
->cumulated_block_sizes
= 0;
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
);
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
)
877 grub_size_t boff
, curread
;
878 boff
= off
- cumulated_uncompressed_size
;
879 curread
= data
->blksz
- boff
;
882 if (!ino
->block_sizes
[i
])
885 grub_memset (buf
, '\0', curread
);
887 else if (!(ino
->block_sizes
[i
]
888 & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED
)))
892 csize
= grub_le_to_cpu32 (ino
->block_sizes
[i
]) & ~SQUASH_BLOCK_FLAGS
;
893 block
= grub_malloc (csize
);
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),
907 if (data
->decompress (block
, csize
, boff
, buf
, curread
, data
)
908 != (grub_ssize_t
) curread
)
912 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
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),
929 cumulated_uncompressed_size
+= grub_le_to_cpu32 (data
->sb
.block_size
);
937 grub_squash_read (grub_file_t file
, char *buf
, grub_size_t len
)
939 struct grub_squash_data
*data
= file
->data
;
940 struct grub_squash_cache_inode
*ino
= &data
->ino
;
941 grub_off_t off
= file
->offset
;
944 grub_uint32_t fragment
= 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
;
951 switch (ino
->ino
.type
)
953 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
954 fragment
= grub_le_to_cpu32 (ino
->ino
.long_file
.fragment
);
956 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
957 fragment
= grub_le_to_cpu32 (ino
->ino
.file
.fragment
);
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
)
966 grub_size_t read_len
= direct_len
- off
;
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? */
983 err
= read_chunk (data
, &frag
, sizeof (frag
),
984 data
->fragments
, sizeof (frag
) * fragment
);
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
;
992 b
= grub_le_to_cpu32 (ino
->ino
.file
.offset
) + off
;
994 /* FIXME: cache uncompressed chunks. */
998 block
= grub_malloc (grub_le_to_cpu32 (frag
.size
));
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
);
1010 if (data
->decompress (block
, grub_le_to_cpu32 (frag
.size
),
1012 != (grub_ssize_t
) len
)
1016 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
1023 err
= grub_disk_read (data
->disk
, (a
+ b
) >> GRUB_DISK_SECTOR_BITS
,
1024 (a
+ b
) & (GRUB_DISK_SECTOR_SIZE
- 1), len
, buf
);
1032 grub_squash_close (grub_file_t file
)
1034 squash_unmount (file
->data
);
1035 return GRUB_ERR_NONE
;
1039 grub_squash_mtime (grub_device_t dev
, grub_int32_t
*tm
)
1041 struct grub_squash_data
*data
= 0;
1043 data
= squash_mount (dev
->disk
);
1046 *tm
= grub_le_to_cpu32 (data
->sb
.creation_time
);
1047 squash_unmount (data
);
1048 return GRUB_ERR_NONE
;
1051 static struct grub_fs grub_squash_fs
=
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
,
1060 .reserved_first_sector
= 0,
1061 .blocklist_install
= 0,
1066 GRUB_MOD_INIT(squash4
)
1068 grub_fs_register (&grub_squash_fs
);
1071 GRUB_MOD_FINI(squash4
)
1073 grub_fs_unregister (&grub_squash_fs
);