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>
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
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
56 #define SQUASH_MAGIC 0x73717368
58 grub_uint32_t creation_time
;
59 grub_uint32_t block_size
;
61 grub_uint16_t compression
;
64 grub_uint16_t root_ino_offset
;
65 grub_uint32_t root_ino_chunk
;
67 grub_uint64_t total_size
;
68 grub_uint64_t exttbloffset
;
70 grub_uint64_t inodeoffset
;
71 grub_uint64_t diroffset
;
72 grub_uint64_t unk1offset
;
73 grub_uint64_t unk2offset
;
77 struct grub_squash_inode
79 /* Same values as direlem types. */
81 grub_uint16_t dummy
[3];
88 grub_uint32_t fragment
;
91 grub_uint32_t block_size
[0];
96 grub_uint32_t dummy1
[3];
97 grub_uint32_t fragment
;
100 grub_uint32_t block_size
[0];
101 } GRUB_PACKED long_file
;
106 grub_uint16_t offset
;
109 grub_uint32_t dummy1
;
112 grub_uint32_t dummy2
;
113 grub_uint16_t dummy3
;
114 grub_uint16_t offset
;
115 } GRUB_PACKED long_dir
;
118 grub_uint32_t namelen
;
120 } GRUB_PACKED symlink
;
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
;
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
;
142 struct grub_squash_dirent
144 grub_uint16_t ino_offset
;
147 /* Actually the value is the length of name - 1. */
148 grub_uint16_t namelen
;
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
;
171 SQUASH_CHUNK_FLAGS
= 0x8000,
172 SQUASH_CHUNK_UNCOMPRESSED
= 0x8000
177 SQUASH_BLOCK_FLAGS
= 0x1000000,
178 SQUASH_BLOCK_UNCOMPRESSED
= 0x1000000
183 COMPRESSION_ZLIB
= 1,
189 #define SQUASH_CHUNK_SIZE 0x2000
190 #define XZBUFSIZ 0x2000
192 struct grub_squash_data
195 struct grub_squash_super sb
;
196 struct grub_squash_cache_inode ino
;
197 grub_uint64_t fragments
;
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
;
207 struct grub_fshelp_node
209 struct grub_squash_data
*data
;
210 struct grub_squash_inode ino
;
214 grub_disk_addr_t ino_chunk
;
215 grub_uint16_t ino_offset
;
220 read_chunk (struct grub_squash_data
*data
, void *buf
, grub_size_t len
,
221 grub_uint64_t chunk_start
, grub_off_t offset
)
230 err
= grub_disk_read (data
->disk
,
231 chunk_start
>> GRUB_DISK_SECTOR_BITS
,
232 chunk_start
& (GRUB_DISK_SECTOR_SIZE
- 1),
236 if (offset
< SQUASH_CHUNK_SIZE
)
238 offset
-= SQUASH_CHUNK_SIZE
;
239 chunk_start
+= 2 + (grub_le_to_cpu16 (d
) & ~SQUASH_CHUNK_FLAGS
);
242 csize
= SQUASH_CHUNK_SIZE
- offset
;
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),
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
);
263 /* FIXME: buffer uncompressed data. */
264 err
= grub_disk_read (data
->disk
, (a
>> GRUB_DISK_SECTOR_BITS
),
265 a
& (GRUB_DISK_SECTOR_SIZE
- 1),
273 if (data
->decompress (tmp
, bsize
, offset
,
274 buf
, csize
, data
) < 0)
283 buf
= (char *) buf
+ csize
;
285 return GRUB_ERR_NONE
;
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
);
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
;
306 udata
= grub_malloc (usize
);
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");
317 grub_memcpy (outbuf
, udata
+ off
, len
);
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
)
330 xz_dec_reset (data
->xzdec
);
331 buf
.in
= (grub_uint8_t
*) inbuf
;
333 buf
.in_size
= insize
;
334 buf
.out
= (grub_uint8_t
*) data
->xzbuf
;
336 buf
.out_size
= XZBUFSIZ
;
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");
351 if (pos
+ buf
.out_pos
>= off
)
353 grub_ssize_t outoff
= pos
- off
;
360 grub_memcpy (outbuf
+ outoff
, buf
.out
, l
);
365 l
= buf
.out_pos
- outoff
;
368 grub_memcpy (outbuf
, buf
.out
+ outoff
, l
);
374 if (xzret
== XZ_STREAM_END
)
380 static struct grub_squash_data
*
381 squash_mount (grub_disk_t disk
)
383 struct grub_squash_super sb
;
385 struct grub_squash_data
*data
;
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");
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");
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");
411 data
= grub_zalloc (sizeof (*data
));
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
;
423 case grub_cpu_to_le16_compile_time (COMPRESSION_LZO
):
424 data
->decompress
= lzo_decompress
;
426 case grub_cpu_to_le16_compile_time (COMPRESSION_XZ
):
427 data
->decompress
= xz_decompress
;
428 data
->xzbuf
= grub_malloc (XZBUFSIZ
);
434 data
->xzdec
= xz_dec_init (1 << 16);
437 grub_free (data
->xzbuf
);
444 grub_error (GRUB_ERR_BAD_FS
, "unsupported compression %d",
445 grub_le_to_cpu16 (sb
.compression
));
449 data
->blksz
= grub_le_to_cpu32 (data
->sb
.block_size
);
450 for (data
->log2_blksz
= 0;
451 (1U << data
->log2_blksz
) < data
->blksz
;
458 grub_squash_read_symlink (grub_fshelp_node_t node
)
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
));
475 ret
[grub_le_to_cpu32 (node
->ino
.symlink
.namelen
)] = 0;
480 grub_squash_iterate_dir (grub_fshelp_node_t dir
,
481 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
484 grub_uint32_t endoff
;
488 /* FIXME: why - 3 ? */
489 switch (dir
->ino
.type
)
491 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR
):
492 off
= grub_le_to_cpu16 (dir
->ino
.dir
.offset
);
493 endoff
= grub_le_to_cpu16 (dir
->ino
.dir
.size
) + off
- 3;
494 chunk
= grub_le_to_cpu32 (dir
->ino
.dir
.chunk
);
496 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR
):
497 off
= grub_le_to_cpu16 (dir
->ino
.long_dir
.offset
);
498 endoff
= grub_le_to_cpu16 (dir
->ino
.long_dir
.size
) + off
- 3;
499 chunk
= grub_le_to_cpu32 (dir
->ino
.long_dir
.chunk
);
502 grub_error (GRUB_ERR_BAD_FS
, "unexpected ino type 0x%x",
503 grub_le_to_cpu16 (dir
->ino
.type
));
508 grub_fshelp_node_t node
;
509 node
= grub_malloc (sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
512 grub_memcpy (node
, dir
,
513 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
514 if (hook (".", GRUB_FSHELP_DIR
, node
, hook_data
))
517 if (dir
->stsize
!= 1)
521 node
= grub_malloc (sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
525 grub_memcpy (node
, dir
,
526 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
529 err
= read_chunk (dir
->data
, &node
->ino
, sizeof (node
->ino
),
530 grub_le_to_cpu64 (dir
->data
->sb
.inodeoffset
)
531 + node
->stack
[node
->stsize
- 1].ino_chunk
,
532 node
->stack
[node
->stsize
- 1].ino_offset
);
536 if (hook ("..", GRUB_FSHELP_DIR
, node
, hook_data
))
543 struct grub_squash_dirent_header dh
;
546 err
= read_chunk (dir
->data
, &dh
, sizeof (dh
),
547 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
552 for (i
= 0; i
< (unsigned) grub_le_to_cpu32 (dh
.nelems
) + 1; i
++)
556 struct grub_fshelp_node
*node
;
557 enum grub_fshelp_filetype filetype
= GRUB_FSHELP_REG
;
558 struct grub_squash_dirent di
;
559 struct grub_squash_inode ino
;
561 err
= read_chunk (dir
->data
, &di
, sizeof (di
),
562 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
568 err
= read_chunk (dir
->data
, &ino
, sizeof (ino
),
569 grub_le_to_cpu64 (dir
->data
->sb
.inodeoffset
)
570 + grub_le_to_cpu32 (dh
.ino_chunk
),
571 grub_cpu_to_le16 (di
.ino_offset
));
575 buf
= grub_malloc (grub_le_to_cpu16 (di
.namelen
) + 2);
578 err
= read_chunk (dir
->data
, buf
,
579 grub_le_to_cpu16 (di
.namelen
) + 1,
580 grub_le_to_cpu64 (dir
->data
->sb
.diroffset
)
585 off
+= grub_le_to_cpu16 (di
.namelen
) + 1;
586 buf
[grub_le_to_cpu16 (di
.namelen
) + 1] = 0;
587 if (grub_le_to_cpu16 (di
.type
) == SQUASH_TYPE_DIR
)
588 filetype
= GRUB_FSHELP_DIR
;
589 if (grub_le_to_cpu16 (di
.type
) == SQUASH_TYPE_SYMLINK
)
590 filetype
= GRUB_FSHELP_SYMLINK
;
592 node
= grub_malloc (sizeof (*node
)
593 + (dir
->stsize
+ 1) * sizeof (dir
->stack
[0]));
597 grub_memcpy (node
, dir
,
598 sizeof (*node
) + dir
->stsize
* sizeof (dir
->stack
[0]));
601 node
->stack
[node
->stsize
].ino_chunk
= grub_le_to_cpu32 (dh
.ino_chunk
);
602 node
->stack
[node
->stsize
].ino_offset
= grub_le_to_cpu16 (di
.ino_offset
);
604 r
= hook (buf
, filetype
, node
, hook_data
);
615 make_root_node (struct grub_squash_data
*data
, struct grub_fshelp_node
*root
)
617 grub_memset (root
, 0, sizeof (*root
));
620 root
->stack
[0].ino_chunk
= grub_le_to_cpu32 (data
->sb
.root_ino_chunk
);
621 root
->stack
[0].ino_offset
= grub_cpu_to_le16 (data
->sb
.root_ino_offset
);
622 return read_chunk (data
, &root
->ino
, sizeof (root
->ino
),
623 grub_le_to_cpu64 (data
->sb
.inodeoffset
)
624 + root
->stack
[0].ino_chunk
,
625 root
->stack
[0].ino_offset
);
629 squash_unmount (struct grub_squash_data
*data
)
632 xz_dec_end (data
->xzdec
);
633 grub_free (data
->xzbuf
);
634 grub_free (data
->ino
.cumulated_block_sizes
);
635 grub_free (data
->ino
.block_sizes
);
640 /* Context for grub_squash_dir. */
641 struct grub_squash_dir_ctx
643 grub_fs_dir_hook_t hook
;
647 /* Helper for grub_squash_dir. */
649 grub_squash_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
650 grub_fshelp_node_t node
, void *data
)
652 struct grub_squash_dir_ctx
*ctx
= data
;
653 struct grub_dirhook_info info
;
655 grub_memset (&info
, 0, sizeof (info
));
656 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
658 info
.mtime
= grub_le_to_cpu32 (node
->ino
.mtime
);
660 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
664 grub_squash_dir (grub_device_t device
, const char *path
,
665 grub_fs_dir_hook_t hook
, void *hook_data
)
667 struct grub_squash_dir_ctx ctx
= { hook
, hook_data
};
668 struct grub_squash_data
*data
= 0;
669 struct grub_fshelp_node
*fdiro
= 0;
670 struct grub_fshelp_node root
;
673 data
= squash_mount (device
->disk
);
677 err
= make_root_node (data
, &root
);
681 grub_fshelp_find_file (path
, &root
, &fdiro
, grub_squash_iterate_dir
,
682 grub_squash_read_symlink
, GRUB_FSHELP_DIR
);
684 grub_squash_iterate_dir (fdiro
, grub_squash_dir_iter
, &ctx
);
686 squash_unmount (data
);
692 grub_squash_open (struct grub_file
*file
, const char *name
)
694 struct grub_squash_data
*data
= 0;
695 struct grub_fshelp_node
*fdiro
= 0;
696 struct grub_fshelp_node root
;
699 data
= squash_mount (file
->device
->disk
);
703 err
= make_root_node (data
, &root
);
707 grub_fshelp_find_file (name
, &root
, &fdiro
, grub_squash_iterate_dir
,
708 grub_squash_read_symlink
, GRUB_FSHELP_REG
);
711 squash_unmount (data
);
716 data
->ino
.ino
= fdiro
->ino
;
717 data
->ino
.block_sizes
= NULL
;
718 data
->ino
.cumulated_block_sizes
= NULL
;
719 data
->ino
.ino_chunk
= fdiro
->stack
[fdiro
->stsize
- 1].ino_chunk
;
720 data
->ino
.ino_offset
= fdiro
->stack
[fdiro
->stsize
- 1].ino_offset
;
722 switch (fdiro
->ino
.type
)
724 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
725 file
->size
= grub_le_to_cpu64 (fdiro
->ino
.long_file
.size
);
727 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
728 file
->size
= grub_le_to_cpu32 (fdiro
->ino
.file
.size
);
732 grub_uint16_t type
= grub_le_to_cpu16 (fdiro
->ino
.type
);
734 squash_unmount (data
);
735 return grub_error (GRUB_ERR_BAD_FS
, "unexpected ino type 0x%x", type
);
741 return GRUB_ERR_NONE
;
745 direct_read (struct grub_squash_data
*data
,
746 struct grub_squash_cache_inode
*ino
,
747 grub_off_t off
, char *buf
, grub_size_t len
)
750 grub_off_t cumulated_uncompressed_size
= 0;
753 grub_size_t origlen
= len
;
755 switch (ino
->ino
.type
)
757 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
758 a
= grub_le_to_cpu64 (ino
->ino
.long_file
.chunk
);
760 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
761 a
= grub_le_to_cpu32 (ino
->ino
.file
.chunk
);
765 if (!ino
->block_sizes
)
767 grub_off_t total_size
= 0;
768 grub_size_t total_blocks
;
769 grub_size_t block_offset
= 0;
770 switch (ino
->ino
.type
)
772 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
773 total_size
= grub_le_to_cpu64 (ino
->ino
.long_file
.size
);
774 block_offset
= ((char *) &ino
->ino
.long_file
.block_size
775 - (char *) &ino
->ino
);
777 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
778 total_size
= grub_le_to_cpu32 (ino
->ino
.file
.size
);
779 block_offset
= ((char *) &ino
->ino
.file
.block_size
780 - (char *) &ino
->ino
);
783 total_blocks
= ((total_size
+ data
->blksz
- 1) >> data
->log2_blksz
);
784 ino
->block_sizes
= grub_malloc (total_blocks
785 * sizeof (ino
->block_sizes
[0]));
786 ino
->cumulated_block_sizes
= grub_malloc (total_blocks
787 * sizeof (ino
->cumulated_block_sizes
[0]));
788 if (!ino
->block_sizes
|| !ino
->cumulated_block_sizes
)
790 grub_free (ino
->block_sizes
);
791 grub_free (ino
->cumulated_block_sizes
);
792 ino
->block_sizes
= 0;
793 ino
->cumulated_block_sizes
= 0;
796 err
= read_chunk (data
, ino
->block_sizes
,
797 total_blocks
* sizeof (ino
->block_sizes
[0]),
798 grub_le_to_cpu64 (data
->sb
.inodeoffset
)
800 ino
->ino_offset
+ block_offset
);
803 grub_free (ino
->block_sizes
);
804 grub_free (ino
->cumulated_block_sizes
);
805 ino
->block_sizes
= 0;
806 ino
->cumulated_block_sizes
= 0;
809 ino
->cumulated_block_sizes
[0] = 0;
810 for (i
= 1; i
< total_blocks
; i
++)
811 ino
->cumulated_block_sizes
[i
] = ino
->cumulated_block_sizes
[i
- 1]
812 + (grub_le_to_cpu32 (ino
->block_sizes
[i
- 1]) & ~SQUASH_BLOCK_FLAGS
);
816 a
= sizeof (struct grub_squash_super
);
817 i
= off
>> data
->log2_blksz
;
818 cumulated_uncompressed_size
= data
->blksz
* (grub_disk_addr_t
) i
;
819 while (cumulated_uncompressed_size
< off
+ len
)
821 grub_size_t boff
, curread
;
822 boff
= off
- cumulated_uncompressed_size
;
823 curread
= data
->blksz
- boff
;
826 if (!(ino
->block_sizes
[i
]
827 & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED
)))
831 csize
= grub_le_to_cpu32 (ino
->block_sizes
[i
]) & ~SQUASH_BLOCK_FLAGS
;
832 block
= grub_malloc (csize
);
835 err
= grub_disk_read (data
->disk
,
836 (ino
->cumulated_block_sizes
[i
] + a
)
837 >> GRUB_DISK_SECTOR_BITS
,
838 (ino
->cumulated_block_sizes
[i
] + a
)
839 & (GRUB_DISK_SECTOR_SIZE
- 1),
846 if (data
->decompress (block
, csize
, boff
, buf
, curread
, data
)
847 != (grub_ssize_t
) curread
)
851 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
857 err
= grub_disk_read (data
->disk
,
858 (ino
->cumulated_block_sizes
[i
] + a
+ boff
)
859 >> GRUB_DISK_SECTOR_BITS
,
860 (ino
->cumulated_block_sizes
[i
] + a
+ boff
)
861 & (GRUB_DISK_SECTOR_SIZE
- 1),
868 cumulated_uncompressed_size
+= grub_le_to_cpu32 (data
->sb
.block_size
);
876 grub_squash_read_data (struct grub_squash_data
*data
,
877 struct grub_squash_cache_inode
*ino
,
878 grub_off_t off
, char *buf
, grub_size_t len
)
881 grub_uint64_t a
= 0, b
;
882 grub_uint32_t fragment
= 0;
884 struct grub_squash_frag_desc frag
;
886 switch (ino
->ino
.type
)
888 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
):
889 a
= grub_le_to_cpu64 (ino
->ino
.long_file
.chunk
);
890 fragment
= grub_le_to_cpu32 (ino
->ino
.long_file
.fragment
);
892 case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR
):
893 a
= grub_le_to_cpu32 (ino
->ino
.file
.chunk
);
894 fragment
= grub_le_to_cpu32 (ino
->ino
.file
.fragment
);
898 if (fragment
== 0xffffffff)
899 return direct_read (data
, ino
, off
, buf
, len
);
901 err
= read_chunk (data
, &frag
, sizeof (frag
),
902 data
->fragments
, sizeof (frag
) * fragment
);
905 a
+= grub_le_to_cpu64 (frag
.offset
);
906 compressed
= !(frag
.size
& grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED
));
907 if (ino
->ino
.type
== grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR
))
908 b
= grub_le_to_cpu32 (ino
->ino
.long_file
.offset
) + off
;
910 b
= grub_le_to_cpu32 (ino
->ino
.file
.offset
) + off
;
912 /* FIXME: cache uncompressed chunks. */
916 block
= grub_malloc (grub_le_to_cpu32 (frag
.size
));
919 err
= grub_disk_read (data
->disk
,
920 a
>> GRUB_DISK_SECTOR_BITS
,
921 a
& (GRUB_DISK_SECTOR_SIZE
- 1),
922 grub_le_to_cpu32 (frag
.size
), block
);
928 if (data
->decompress (block
, grub_le_to_cpu32 (frag
.size
),
930 != (grub_ssize_t
) len
)
934 grub_error (GRUB_ERR_BAD_FS
, "incorrect compressed chunk");
941 err
= grub_disk_read (data
->disk
, (a
+ b
) >> GRUB_DISK_SECTOR_BITS
,
942 (a
+ b
) & (GRUB_DISK_SECTOR_SIZE
- 1), len
, buf
);
950 grub_squash_read (grub_file_t file
, char *buf
, grub_size_t len
)
952 struct grub_squash_data
*data
= file
->data
;
954 return grub_squash_read_data (data
, &data
->ino
,
955 file
->offset
, buf
, len
);
959 grub_squash_close (grub_file_t file
)
961 squash_unmount (file
->data
);
962 return GRUB_ERR_NONE
;
966 grub_squash_mtime (grub_device_t dev
, grub_int32_t
*tm
)
968 struct grub_squash_data
*data
= 0;
970 data
= squash_mount (dev
->disk
);
973 *tm
= grub_le_to_cpu32 (data
->sb
.creation_time
);
974 squash_unmount (data
);
975 return GRUB_ERR_NONE
;
978 static struct grub_fs grub_squash_fs
=
981 .dir
= grub_squash_dir
,
982 .open
= grub_squash_open
,
983 .read
= grub_squash_read
,
984 .close
= grub_squash_close
,
985 .mtime
= grub_squash_mtime
,
987 .reserved_first_sector
= 0,
988 .blocklist_install
= 0,
993 GRUB_MOD_INIT(squash4
)
995 grub_fs_register (&grub_squash_fs
);
998 GRUB_MOD_FINI(squash4
)
1000 grub_fs_unregister (&grub_squash_fs
);