Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / io / lzopio.c
blob77291d00188eabd9f579e9f914e7675dd0bc2347
1 /* lzopio.c - decompression support for lzop */
2 /*
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/>.
20 #include <grub/err.h>
21 #include <grub/mm.h>
22 #include <grub/file.h>
23 #include <grub/fs.h>
24 #include <grub/dl.h>
25 #include <grub/crypto.h>
26 #include <minilzo.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
52 struct block_header
54 grub_uint32_t usize;
55 grub_uint32_t csize;
56 grub_uint32_t ucheck;
57 grub_uint32_t ccheck;
58 unsigned char *cdata;
59 unsigned char *udata;
62 struct grub_lzopio
64 grub_file_t file;
65 int has_ccheck;
66 int has_ucheck;
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. */
82 static int
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))
96 return -1;
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))
104 return 0;
105 else
106 return -1;
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))
113 return -1;
115 lzopio->block.csize = grub_be_to_cpu32 (lzopio->block.csize);
117 /* Corrupted. */
118 if (lzopio->block.csize > lzopio->block.usize)
119 return -1;
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))
127 return -1;
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;
140 else
142 if (grub_file_read (lzopio->file, &lzopio->block.ccheck,
143 sizeof (lzopio->block.ccheck)) !=
144 sizeof (lzopio->block.ccheck))
145 return -1;
147 lzopio->block.ccheck = lzopio->block.ccheck;
151 return 0;
154 /* Read block data into memory. File must be set to beginning of block data.
155 * Can't be called on last block. */
156 static int
157 read_block_data (struct grub_lzopio *lzopio)
159 lzopio->block.cdata = grub_malloc (lzopio->block.csize);
160 if (!lzopio->block.cdata)
161 return -1;
163 if (grub_file_read (lzopio->file, lzopio->block.cdata, lzopio->block.csize)
164 != (grub_ssize_t) lzopio->block.csize)
165 return -1;
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);
176 if (grub_memcmp
177 (lzopio->ccheck_fun->read (context), &lzopio->block.ccheck,
178 sizeof (lzopio->block.ccheck)) != 0)
179 return -1;
182 return 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. */
188 static int
189 uncompress_block (struct grub_lzopio *lzopio)
191 lzo_uint usize = lzopio->block.usize;
193 if (read_block_data (lzopio) < 0)
194 return -1;
196 /* Incompressible data. */
197 if (lzopio->block.csize == lzopio->block.usize)
199 lzopio->block.udata = lzopio->block.cdata;
200 lzopio->block.cdata = NULL;
202 else
204 lzopio->block.udata = grub_malloc (lzopio->block.usize);
205 if (!lzopio->block.udata)
206 return -1;
208 if (lzo1x_decompress_safe (lzopio->block.cdata, lzopio->block.csize,
209 lzopio->block.udata, &usize, NULL)
210 != LZO_E_OK)
211 return -1;
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);
222 if (grub_memcmp
223 (lzopio->ucheck_fun->read (context), &lzopio->block.ucheck,
224 sizeof (lzopio->block.ucheck)) != 0)
225 return -1;
228 /* Compressed data can be free now. */
229 grub_free (lzopio->block.cdata);
230 lzopio->block.cdata = NULL;
233 return 0;
236 /* Jump to next block and read its header. */
237 static int
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))
246 return -1;
249 return read_block_header (lzopio);
252 static int
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)
259 return -1;
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)
267 return -1;
270 file->size = usize_total;
272 return 0;
275 struct lzop_header
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;
281 grub_uint8_t method;
282 grub_uint8_t level;
283 grub_uint32_t flags;
284 /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */
285 grub_uint32_t mode;
286 grub_uint32_t mtime_lo;
287 grub_uint32_t mtime_hi;
288 grub_uint8_t name_len;
289 } __attribute__ ((packed));
291 static int
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))
302 return 0;
304 if (grub_memcmp (header.magic, LZOP_MAGIC, LZOP_MAGIC_SIZE) != 0)
305 return 0;
307 if (grub_be_to_cpu16(header.lib_version) < LZOP_NEW_LIB)
308 return 0;
310 /* Too new version, should upgrade minilzo? */
311 if (grub_be_to_cpu16 (header.lib_version_ext) > MINILZO_VERSION)
312 return 0;
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");
340 else
341 hcheck = grub_crypto_lookup_md_by_name ("adler32");
343 if (hcheck) {
344 context = grub_malloc(hcheck->contextsize);
345 if (! context)
346 return 0;
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);
357 if (! name)
359 grub_free (context);
360 return 0;
363 if (grub_file_read (lzopio->file, name, header.name_len) !=
364 header.name_len)
366 grub_free(name);
367 goto CORRUPTED;
370 if (hcheck)
371 hcheck->write(context, name, header.name_len);
373 grub_free(name);
376 if (hcheck)
377 hcheck->final(context);
379 if (grub_file_read (lzopio->file, &checksum, sizeof (checksum)) !=
380 sizeof (checksum))
381 goto CORRUPTED;
383 if (hcheck)
385 checksum = checksum;
386 if (grub_memcmp (&checksum, hcheck->read(context), sizeof(checksum)) != 0)
387 goto CORRUPTED;
390 lzopio->start_block_off = grub_file_tell (lzopio->file);
392 if (calculate_uncompressed_size (file) < 0)
393 goto CORRUPTED;
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)
400 goto CORRUPTED;
402 lzopio->saved_off = 0;
403 return 1;
405 CORRUPTED:
406 grub_free(name);
408 return 0;
411 static grub_file_t
412 grub_lzopio_open (grub_file_t io)
414 grub_file_t file;
415 grub_lzopio_t lzopio;
417 file = (grub_file_t) grub_zalloc (sizeof (*file));
418 if (!file)
419 return 0;
421 lzopio = grub_zalloc (sizeof (*lzopio));
422 if (!lzopio)
424 grub_free (file);
425 return 0;
428 lzopio->file = io;
430 file->device = io->device;
431 file->offset = 0;
432 file->data = lzopio;
433 file->read_hook = 0;
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);
445 grub_free (lzopio);
446 grub_free (file);
448 return io;
451 return file;
454 static grub_ssize_t
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;
459 grub_off_t off;
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)
467 goto CORRUPTED;
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)
477 return 0;
479 if (jump_block (lzopio) < 0)
480 goto CORRUPTED;
483 off = grub_file_tell (file) - lzopio->saved_off;
485 while (len != 0 && lzopio->block.usize != 0)
487 grub_size_t to_copy;
489 /* Block not decompressed yet. */
490 if (!lzopio->block.udata && uncompress_block (lzopio) < 0)
491 goto CORRUPTED;
493 /* Copy requested data into buffer. */
494 to_copy = lzopio->block.usize - off;
495 if (to_copy > len)
496 to_copy = len;
497 grub_memcpy (buf, lzopio->block.udata + off, to_copy);
499 len -= to_copy;
500 buf += to_copy;
501 ret += to_copy;
502 off = 0;
504 /* Read next block if needed. */
505 if (len > 0 && read_block_header (lzopio) < 0)
506 goto CORRUPTED;
509 return ret;
511 CORRUPTED:
512 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("lzop file corrupted"));
513 return -1;
516 /* Release everything, including the underlying file object. */
517 static grub_err_t
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);
525 grub_free (lzopio);
527 /* Device must not be closed twice. */
528 file->device = 0;
529 return grub_errno;
532 static struct grub_fs grub_lzopio_fs = {
533 .name = "lzopio",
534 .dir = 0,
535 .open = 0,
536 .read = grub_lzopio_read,
537 .close = grub_lzopio_close,
538 .label = 0,
539 .next = 0
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);