1 /* lzopio.c - decompression support for lzop */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2011 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/>.
22 #include <grub/file.h>
25 #include <grub/crypto.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
30 #define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
31 #define LZOP_MAGIC_SIZE 9
32 #define LZOP_CHECK_SIZE 4
33 #define LZOP_NEW_LIB 0x0940
35 /* Header flags - copied from conf.h of LZOP source code. */
36 #define F_ADLER32_D 0x00000001L
37 #define F_ADLER32_C 0x00000002L
38 #define F_STDIN 0x00000004L
39 #define F_STDOUT 0x00000008L
40 #define F_NAME_DEFAULT 0x00000010L
41 #define F_DOSISH 0x00000020L
42 #define F_H_EXTRA_FIELD 0x00000040L
43 #define F_H_GMTDIFF 0x00000080L
44 #define F_CRC32_D 0x00000100L
45 #define F_CRC32_C 0x00000200L
46 #define F_MULTIPART 0x00000400L
47 #define F_H_FILTER 0x00000800L
48 #define F_H_CRC32 0x00001000L
49 #define F_H_PATH 0x00002000L
50 #define F_MASK 0x00003FFFL
67 const gcry_md_spec_t
*ucheck_fun
;
68 const gcry_md_spec_t
*ccheck_fun
;
69 grub_off_t saved_off
; /* Rounded down to block boundary. */
70 grub_off_t start_block_off
;
71 struct block_header block
;
74 typedef struct grub_lzopio
*grub_lzopio_t
;
75 static struct grub_fs grub_lzopio_fs
;
77 /* Some helper functions. On errors memory allocated by those function is free
78 * either on close() so no risk of leaks. This makes functions simpler. */
80 /* Read block header from file, after successful exit file points to
81 * beginning of block data. */
83 read_block_header (struct grub_lzopio
*lzopio
)
85 lzopio
->saved_off
+= lzopio
->block
.usize
;
87 /* Free cached block data if any. */
88 grub_free (lzopio
->block
.udata
);
89 grub_free (lzopio
->block
.cdata
);
90 lzopio
->block
.udata
= NULL
;
91 lzopio
->block
.cdata
= NULL
;
93 if (grub_file_read (lzopio
->file
, &lzopio
->block
.usize
,
94 sizeof (lzopio
->block
.usize
)) !=
95 sizeof (lzopio
->block
.usize
))
98 lzopio
->block
.usize
= grub_be_to_cpu32 (lzopio
->block
.usize
);
100 /* Last block has uncompressed data size == 0 and no other fields. */
101 if (lzopio
->block
.usize
== 0)
103 if (grub_file_tell (lzopio
->file
) == grub_file_size (lzopio
->file
))
109 /* Read compressed data block size. */
110 if (grub_file_read (lzopio
->file
, &lzopio
->block
.csize
,
111 sizeof (lzopio
->block
.csize
)) !=
112 sizeof (lzopio
->block
.csize
))
115 lzopio
->block
.csize
= grub_be_to_cpu32 (lzopio
->block
.csize
);
118 if (lzopio
->block
.csize
> lzopio
->block
.usize
)
121 /* Read checksum of uncompressed data. */
122 if (lzopio
->has_ucheck
)
124 if (grub_file_read (lzopio
->file
, &lzopio
->block
.ucheck
,
125 sizeof (lzopio
->block
.ucheck
)) !=
126 sizeof (lzopio
->block
.ucheck
))
129 lzopio
->block
.ucheck
= lzopio
->block
.ucheck
;
132 /* Read checksum of compressed data. */
133 if (lzopio
->has_ccheck
)
135 /* Incompressible data block. */
136 if (lzopio
->block
.csize
== lzopio
->block
.usize
)
138 lzopio
->block
.ccheck
= lzopio
->block
.ucheck
;
142 if (grub_file_read (lzopio
->file
, &lzopio
->block
.ccheck
,
143 sizeof (lzopio
->block
.ccheck
)) !=
144 sizeof (lzopio
->block
.ccheck
))
147 lzopio
->block
.ccheck
= lzopio
->block
.ccheck
;
154 /* Read block data into memory. File must be set to beginning of block data.
155 * Can't be called on last block. */
157 read_block_data (struct grub_lzopio
*lzopio
)
159 lzopio
->block
.cdata
= grub_malloc (lzopio
->block
.csize
);
160 if (!lzopio
->block
.cdata
)
163 if (grub_file_read (lzopio
->file
, lzopio
->block
.cdata
, lzopio
->block
.csize
)
164 != (grub_ssize_t
) lzopio
->block
.csize
)
167 if (lzopio
->ccheck_fun
)
169 grub_uint64_t context
[(lzopio
->ccheck_fun
->contextsize
+ 7) / 8];
171 lzopio
->ccheck_fun
->init (context
);
172 lzopio
->ccheck_fun
->write (context
, lzopio
->block
.cdata
,
173 lzopio
->block
.csize
);
174 lzopio
->ccheck_fun
->final (context
);
177 (lzopio
->ccheck_fun
->read (context
), &lzopio
->block
.ccheck
,
178 sizeof (lzopio
->block
.ccheck
)) != 0)
185 /* Read block data, uncompressed and also store it in memory. */
186 /* XXX Investigate possibility of in-place decompression to reduce memory
187 * footprint. Or try to uncompress directly to buf if possible. */
189 uncompress_block (struct grub_lzopio
*lzopio
)
191 lzo_uint usize
= lzopio
->block
.usize
;
193 if (read_block_data (lzopio
) < 0)
196 /* Incompressible data. */
197 if (lzopio
->block
.csize
== lzopio
->block
.usize
)
199 lzopio
->block
.udata
= lzopio
->block
.cdata
;
200 lzopio
->block
.cdata
= NULL
;
204 lzopio
->block
.udata
= grub_malloc (lzopio
->block
.usize
);
205 if (!lzopio
->block
.udata
)
208 if (lzo1x_decompress_safe (lzopio
->block
.cdata
, lzopio
->block
.csize
,
209 lzopio
->block
.udata
, &usize
, NULL
)
213 if (lzopio
->ucheck_fun
)
215 grub_uint64_t context
[(lzopio
->ucheck_fun
->contextsize
+ 7) / 8];
217 lzopio
->ucheck_fun
->init (context
);
218 lzopio
->ucheck_fun
->write (context
, lzopio
->block
.udata
,
219 lzopio
->block
.usize
);
220 lzopio
->ucheck_fun
->final (context
);
223 (lzopio
->ucheck_fun
->read (context
), &lzopio
->block
.ucheck
,
224 sizeof (lzopio
->block
.ucheck
)) != 0)
228 /* Compressed data can be free now. */
229 grub_free (lzopio
->block
.cdata
);
230 lzopio
->block
.cdata
= NULL
;
236 /* Jump to next block and read its header. */
238 jump_block (struct grub_lzopio
*lzopio
)
240 /* only jump if block was not decompressed (and read from disk) */
241 if (!lzopio
->block
.udata
)
243 grub_off_t off
= grub_file_tell (lzopio
->file
) + lzopio
->block
.csize
;
245 if (grub_file_seek (lzopio
->file
, off
) == ((grub_off_t
) - 1))
249 return read_block_header (lzopio
);
253 calculate_uncompressed_size (grub_file_t file
)
255 grub_lzopio_t lzopio
= file
->data
;
256 grub_off_t usize_total
= 0;
258 if (read_block_header (lzopio
) < 0)
261 /* FIXME: Don't do this for not easily seekable files. */
262 while (lzopio
->block
.usize
!= 0)
264 usize_total
+= lzopio
->block
.usize
;
266 if (jump_block (lzopio
) < 0)
270 file
->size
= usize_total
;
277 grub_uint8_t magic
[LZOP_MAGIC_SIZE
];
278 grub_uint16_t lzop_version
;
279 grub_uint16_t lib_version
;
280 grub_uint16_t lib_version_ext
;
284 /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */
286 grub_uint32_t mtime_lo
;
287 grub_uint32_t mtime_hi
;
288 grub_uint8_t name_len
;
289 } __attribute__ ((packed
));
292 test_header (grub_file_t file
)
294 grub_lzopio_t lzopio
= file
->data
;
295 struct lzop_header header
;
296 grub_uint32_t flags
, checksum
;
297 const gcry_md_spec_t
*hcheck
;
298 grub_uint8_t
*context
= NULL
;
299 grub_uint8_t
*name
= NULL
;
301 if (grub_file_read (lzopio
->file
, &header
, sizeof (header
)) != sizeof (header
))
304 if (grub_memcmp (header
.magic
, LZOP_MAGIC
, LZOP_MAGIC_SIZE
) != 0)
307 if (grub_be_to_cpu16(header
.lib_version
) < LZOP_NEW_LIB
)
310 /* Too new version, should upgrade minilzo? */
311 if (grub_be_to_cpu16 (header
.lib_version_ext
) > MINILZO_VERSION
)
314 flags
= grub_be_to_cpu32 (header
.flags
);
316 if (flags
& F_CRC32_D
)
318 lzopio
->has_ucheck
= 1;
319 lzopio
->ucheck_fun
= grub_crypto_lookup_md_by_name ("crc32");
321 else if (flags
& F_ADLER32_D
)
323 lzopio
->has_ucheck
= 1;
324 lzopio
->ucheck_fun
= grub_crypto_lookup_md_by_name ("adler32");
327 if (flags
& F_CRC32_C
)
329 lzopio
->has_ccheck
= 1;
330 lzopio
->ccheck_fun
= grub_crypto_lookup_md_by_name ("crc32");
332 else if (flags
& F_ADLER32_C
)
334 lzopio
->has_ccheck
= 1;
335 lzopio
->ccheck_fun
= grub_crypto_lookup_md_by_name ("adler32");
338 if (flags
& F_H_CRC32
)
339 hcheck
= grub_crypto_lookup_md_by_name ("crc32");
341 hcheck
= grub_crypto_lookup_md_by_name ("adler32");
344 context
= grub_malloc(hcheck
->contextsize
);
348 hcheck
->init(context
);
350 /* MAGIC is not included in check calculation. */
351 hcheck
->write(context
, &header
.lzop_version
, sizeof(header
)- LZOP_MAGIC_SIZE
);
354 if (header
.name_len
!= 0)
356 name
= grub_malloc (header
.name_len
);
363 if (grub_file_read (lzopio
->file
, name
, header
.name_len
) !=
371 hcheck
->write(context
, name
, header
.name_len
);
377 hcheck
->final(context
);
379 if (grub_file_read (lzopio
->file
, &checksum
, sizeof (checksum
)) !=
386 if (grub_memcmp (&checksum
, hcheck
->read(context
), sizeof(checksum
)) != 0)
390 lzopio
->start_block_off
= grub_file_tell (lzopio
->file
);
392 if (calculate_uncompressed_size (file
) < 0)
395 /* Get back to start block. */
396 grub_file_seek (lzopio
->file
, lzopio
->start_block_off
);
398 /* Read first block - grub_lzopio_read() expects valid block. */
399 if (read_block_header (lzopio
) < 0)
402 lzopio
->saved_off
= 0;
412 grub_lzopio_open (grub_file_t io
)
415 grub_lzopio_t lzopio
;
417 file
= (grub_file_t
) grub_zalloc (sizeof (*file
));
421 lzopio
= grub_zalloc (sizeof (*lzopio
));
430 file
->device
= io
->device
;
434 file
->fs
= &grub_lzopio_fs
;
435 file
->size
= GRUB_FILE_SIZE_UNKNOWN
;
436 file
->not_easily_seekable
= 1;
438 if (grub_file_tell (lzopio
->file
) != 0)
439 grub_file_seek (lzopio
->file
, 0);
441 if (!test_header (file
))
443 grub_errno
= GRUB_ERR_NONE
;
444 grub_file_seek (io
, 0);
455 grub_lzopio_read (grub_file_t file
, char *buf
, grub_size_t len
)
457 grub_lzopio_t lzopio
= file
->data
;
458 grub_ssize_t ret
= 0;
461 /* Backward seek before last read block. */
462 if (lzopio
->saved_off
> grub_file_tell (file
))
464 grub_file_seek (lzopio
->file
, lzopio
->start_block_off
);
466 if (read_block_header (lzopio
) < 0)
469 lzopio
->saved_off
= 0;
472 /* Forward to first block with requested data. */
473 while (lzopio
->saved_off
+ lzopio
->block
.usize
<= grub_file_tell (file
))
475 /* EOF, could be possible files with unknown size. */
476 if (lzopio
->block
.usize
== 0)
479 if (jump_block (lzopio
) < 0)
483 off
= grub_file_tell (file
) - lzopio
->saved_off
;
485 while (len
!= 0 && lzopio
->block
.usize
!= 0)
489 /* Block not decompressed yet. */
490 if (!lzopio
->block
.udata
&& uncompress_block (lzopio
) < 0)
493 /* Copy requested data into buffer. */
494 to_copy
= lzopio
->block
.usize
- off
;
497 grub_memcpy (buf
, lzopio
->block
.udata
+ off
, to_copy
);
504 /* Read next block if needed. */
505 if (len
> 0 && read_block_header (lzopio
) < 0)
512 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA
, N_("lzop file corrupted"));
516 /* Release everything, including the underlying file object. */
518 grub_lzopio_close (grub_file_t file
)
520 grub_lzopio_t lzopio
= file
->data
;
522 grub_file_close (lzopio
->file
);
523 grub_free (lzopio
->block
.cdata
);
524 grub_free (lzopio
->block
.udata
);
527 /* Device must not be closed twice. */
532 static struct grub_fs grub_lzopio_fs
= {
536 .read
= grub_lzopio_read
,
537 .close
= grub_lzopio_close
,
542 GRUB_MOD_INIT (lzopio
)
544 grub_file_filter_register (GRUB_FILE_FILTER_LZOPIO
, grub_lzopio_open
);
547 GRUB_MOD_FINI (lzopio
)
549 grub_file_filter_unregister (GRUB_FILE_FILTER_LZOPIO
);