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_uint8_t computed_hash
[GRUB_CRYPTO_MAX_MDLEN
];
171 if (lzopio
->ccheck_fun
->mdlen
> GRUB_CRYPTO_MAX_MDLEN
)
174 grub_crypto_hash (lzopio
->ccheck_fun
, computed_hash
,
176 lzopio
->block
.csize
);
179 (computed_hash
, &lzopio
->block
.ccheck
,
180 sizeof (lzopio
->block
.ccheck
)) != 0)
187 /* Read block data, uncompressed and also store it in memory. */
188 /* XXX Investigate possibility of in-place decompression to reduce memory
189 * footprint. Or try to uncompress directly to buf if possible. */
191 uncompress_block (struct grub_lzopio
*lzopio
)
193 lzo_uint usize
= lzopio
->block
.usize
;
195 if (read_block_data (lzopio
) < 0)
198 /* Incompressible data. */
199 if (lzopio
->block
.csize
== lzopio
->block
.usize
)
201 lzopio
->block
.udata
= lzopio
->block
.cdata
;
202 lzopio
->block
.cdata
= NULL
;
206 lzopio
->block
.udata
= grub_malloc (lzopio
->block
.usize
);
207 if (!lzopio
->block
.udata
)
210 if (lzo1x_decompress_safe (lzopio
->block
.cdata
, lzopio
->block
.csize
,
211 lzopio
->block
.udata
, &usize
, NULL
)
215 if (lzopio
->ucheck_fun
)
217 grub_uint8_t computed_hash
[GRUB_CRYPTO_MAX_MDLEN
];
219 if (lzopio
->ucheck_fun
->mdlen
> GRUB_CRYPTO_MAX_MDLEN
)
222 grub_crypto_hash (lzopio
->ucheck_fun
, computed_hash
,
224 lzopio
->block
.usize
);
227 (computed_hash
, &lzopio
->block
.ucheck
,
228 sizeof (lzopio
->block
.ucheck
)) != 0)
232 /* Compressed data can be free now. */
233 grub_free (lzopio
->block
.cdata
);
234 lzopio
->block
.cdata
= NULL
;
240 /* Jump to next block and read its header. */
242 jump_block (struct grub_lzopio
*lzopio
)
244 /* only jump if block was not decompressed (and read from disk) */
245 if (!lzopio
->block
.udata
)
247 grub_off_t off
= grub_file_tell (lzopio
->file
) + lzopio
->block
.csize
;
249 if (grub_file_seek (lzopio
->file
, off
) == ((grub_off_t
) - 1))
253 return read_block_header (lzopio
);
257 calculate_uncompressed_size (grub_file_t file
)
259 grub_lzopio_t lzopio
= file
->data
;
260 grub_off_t usize_total
= 0;
262 if (read_block_header (lzopio
) < 0)
265 /* FIXME: Don't do this for not easily seekable files. */
266 while (lzopio
->block
.usize
!= 0)
268 usize_total
+= lzopio
->block
.usize
;
270 if (jump_block (lzopio
) < 0)
274 file
->size
= usize_total
;
281 grub_uint8_t magic
[LZOP_MAGIC_SIZE
];
282 grub_uint16_t lzop_version
;
283 grub_uint16_t lib_version
;
284 grub_uint16_t lib_version_ext
;
288 /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */
290 grub_uint32_t mtime_lo
;
291 grub_uint32_t mtime_hi
;
292 grub_uint8_t name_len
;
296 test_header (grub_file_t file
)
298 grub_lzopio_t lzopio
= file
->data
;
299 struct lzop_header header
;
300 grub_uint32_t flags
, checksum
;
301 const gcry_md_spec_t
*hcheck
;
302 grub_uint8_t
*context
= NULL
;
303 grub_uint8_t
*name
= NULL
;
305 if (grub_file_read (lzopio
->file
, &header
, sizeof (header
)) != sizeof (header
))
308 if (grub_memcmp (header
.magic
, LZOP_MAGIC
, LZOP_MAGIC_SIZE
) != 0)
311 if (grub_be_to_cpu16(header
.lib_version
) < LZOP_NEW_LIB
)
314 /* Too new version, should upgrade minilzo? */
315 if (grub_be_to_cpu16 (header
.lib_version_ext
) > MINILZO_VERSION
)
318 flags
= grub_be_to_cpu32 (header
.flags
);
320 if (flags
& F_CRC32_D
)
322 lzopio
->has_ucheck
= 1;
323 lzopio
->ucheck_fun
= grub_crypto_lookup_md_by_name ("crc32");
325 else if (flags
& F_ADLER32_D
)
327 lzopio
->has_ucheck
= 1;
328 lzopio
->ucheck_fun
= grub_crypto_lookup_md_by_name ("adler32");
331 if (flags
& F_CRC32_C
)
333 lzopio
->has_ccheck
= 1;
334 lzopio
->ccheck_fun
= grub_crypto_lookup_md_by_name ("crc32");
336 else if (flags
& F_ADLER32_C
)
338 lzopio
->has_ccheck
= 1;
339 lzopio
->ccheck_fun
= grub_crypto_lookup_md_by_name ("adler32");
342 if (flags
& F_H_CRC32
)
343 hcheck
= grub_crypto_lookup_md_by_name ("crc32");
345 hcheck
= grub_crypto_lookup_md_by_name ("adler32");
348 context
= grub_malloc(hcheck
->contextsize
);
352 hcheck
->init(context
);
354 /* MAGIC is not included in check calculation. */
355 hcheck
->write(context
, &header
.lzop_version
, sizeof(header
)- LZOP_MAGIC_SIZE
);
358 if (header
.name_len
!= 0)
360 name
= grub_malloc (header
.name_len
);
367 if (grub_file_read (lzopio
->file
, name
, header
.name_len
) !=
375 hcheck
->write(context
, name
, header
.name_len
);
381 hcheck
->final(context
);
383 if (grub_file_read (lzopio
->file
, &checksum
, sizeof (checksum
)) !=
387 if (hcheck
&& 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;
410 grub_lzopio_open (grub_file_t io
,
411 const char *name
__attribute__ ((unused
)))
414 grub_lzopio_t lzopio
;
416 file
= (grub_file_t
) grub_zalloc (sizeof (*file
));
420 lzopio
= grub_zalloc (sizeof (*lzopio
));
429 file
->device
= io
->device
;
431 file
->fs
= &grub_lzopio_fs
;
432 file
->size
= GRUB_FILE_SIZE_UNKNOWN
;
433 file
->not_easily_seekable
= 1;
435 if (grub_file_tell (lzopio
->file
) != 0)
436 grub_file_seek (lzopio
->file
, 0);
438 if (!test_header (file
))
440 grub_errno
= GRUB_ERR_NONE
;
441 grub_file_seek (io
, 0);
452 grub_lzopio_read (grub_file_t file
, char *buf
, grub_size_t len
)
454 grub_lzopio_t lzopio
= file
->data
;
455 grub_ssize_t ret
= 0;
458 /* Backward seek before last read block. */
459 if (lzopio
->saved_off
> grub_file_tell (file
))
461 grub_file_seek (lzopio
->file
, lzopio
->start_block_off
);
463 if (read_block_header (lzopio
) < 0)
466 lzopio
->saved_off
= 0;
469 /* Forward to first block with requested data. */
470 while (lzopio
->saved_off
+ lzopio
->block
.usize
<= grub_file_tell (file
))
472 /* EOF, could be possible files with unknown size. */
473 if (lzopio
->block
.usize
== 0)
476 if (jump_block (lzopio
) < 0)
480 off
= grub_file_tell (file
) - lzopio
->saved_off
;
482 while (len
!= 0 && lzopio
->block
.usize
!= 0)
486 /* Block not decompressed yet. */
487 if (!lzopio
->block
.udata
&& uncompress_block (lzopio
) < 0)
490 /* Copy requested data into buffer. */
491 to_copy
= lzopio
->block
.usize
- off
;
494 grub_memcpy (buf
, lzopio
->block
.udata
+ off
, to_copy
);
501 /* Read next block if needed. */
502 if (len
> 0 && read_block_header (lzopio
) < 0)
509 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA
, N_("lzop file corrupted"));
513 /* Release everything, including the underlying file object. */
515 grub_lzopio_close (grub_file_t file
)
517 grub_lzopio_t lzopio
= file
->data
;
519 grub_file_close (lzopio
->file
);
520 grub_free (lzopio
->block
.cdata
);
521 grub_free (lzopio
->block
.udata
);
524 /* Device must not be closed twice. */
530 static struct grub_fs grub_lzopio_fs
= {
534 .read
= grub_lzopio_read
,
535 .close
= grub_lzopio_close
,
540 GRUB_MOD_INIT (lzopio
)
542 grub_file_filter_register (GRUB_FILE_FILTER_LZOPIO
, grub_lzopio_open
);
545 GRUB_MOD_FINI (lzopio
)
547 grub_file_filter_unregister (GRUB_FILE_FILTER_LZOPIO
);