Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / io / lzopio.c
blob7559c6c9cab9a94d7052c819976dc21217a30ed8
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_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN];
171 if (lzopio->ccheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN)
172 return -1;
174 grub_crypto_hash (lzopio->ccheck_fun, computed_hash,
175 lzopio->block.cdata,
176 lzopio->block.csize);
178 if (grub_memcmp
179 (computed_hash, &lzopio->block.ccheck,
180 sizeof (lzopio->block.ccheck)) != 0)
181 return -1;
184 return 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. */
190 static int
191 uncompress_block (struct grub_lzopio *lzopio)
193 lzo_uint usize = lzopio->block.usize;
195 if (read_block_data (lzopio) < 0)
196 return -1;
198 /* Incompressible data. */
199 if (lzopio->block.csize == lzopio->block.usize)
201 lzopio->block.udata = lzopio->block.cdata;
202 lzopio->block.cdata = NULL;
204 else
206 lzopio->block.udata = grub_malloc (lzopio->block.usize);
207 if (!lzopio->block.udata)
208 return -1;
210 if (lzo1x_decompress_safe (lzopio->block.cdata, lzopio->block.csize,
211 lzopio->block.udata, &usize, NULL)
212 != LZO_E_OK)
213 return -1;
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)
220 return -1;
222 grub_crypto_hash (lzopio->ucheck_fun, computed_hash,
223 lzopio->block.udata,
224 lzopio->block.usize);
226 if (grub_memcmp
227 (computed_hash, &lzopio->block.ucheck,
228 sizeof (lzopio->block.ucheck)) != 0)
229 return -1;
232 /* Compressed data can be free now. */
233 grub_free (lzopio->block.cdata);
234 lzopio->block.cdata = NULL;
237 return 0;
240 /* Jump to next block and read its header. */
241 static int
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))
250 return -1;
253 return read_block_header (lzopio);
256 static int
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)
263 return -1;
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)
271 return -1;
274 file->size = usize_total;
276 return 0;
279 struct lzop_header
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;
285 grub_uint8_t method;
286 grub_uint8_t level;
287 grub_uint32_t flags;
288 /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */
289 grub_uint32_t mode;
290 grub_uint32_t mtime_lo;
291 grub_uint32_t mtime_hi;
292 grub_uint8_t name_len;
293 } GRUB_PACKED;
295 static int
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))
306 return 0;
308 if (grub_memcmp (header.magic, LZOP_MAGIC, LZOP_MAGIC_SIZE) != 0)
309 return 0;
311 if (grub_be_to_cpu16(header.lib_version) < LZOP_NEW_LIB)
312 return 0;
314 /* Too new version, should upgrade minilzo? */
315 if (grub_be_to_cpu16 (header.lib_version_ext) > MINILZO_VERSION)
316 return 0;
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");
344 else
345 hcheck = grub_crypto_lookup_md_by_name ("adler32");
347 if (hcheck) {
348 context = grub_malloc(hcheck->contextsize);
349 if (! context)
350 return 0;
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);
361 if (! name)
363 grub_free (context);
364 return 0;
367 if (grub_file_read (lzopio->file, name, header.name_len) !=
368 header.name_len)
370 grub_free(name);
371 goto CORRUPTED;
374 if (hcheck)
375 hcheck->write(context, name, header.name_len);
377 grub_free(name);
380 if (hcheck)
381 hcheck->final(context);
383 if (grub_file_read (lzopio->file, &checksum, sizeof (checksum)) !=
384 sizeof (checksum))
385 goto CORRUPTED;
387 if (hcheck && grub_memcmp (&checksum, hcheck->read(context), sizeof(checksum)) != 0)
388 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 return 0;
409 static grub_file_t
410 grub_lzopio_open (grub_file_t io,
411 const char *name __attribute__ ((unused)))
413 grub_file_t file;
414 grub_lzopio_t lzopio;
416 file = (grub_file_t) grub_zalloc (sizeof (*file));
417 if (!file)
418 return 0;
420 lzopio = grub_zalloc (sizeof (*lzopio));
421 if (!lzopio)
423 grub_free (file);
424 return 0;
427 lzopio->file = io;
429 file->device = io->device;
430 file->data = lzopio;
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);
442 grub_free (lzopio);
443 grub_free (file);
445 return io;
448 return file;
451 static grub_ssize_t
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;
456 grub_off_t off;
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)
464 goto CORRUPTED;
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)
474 return 0;
476 if (jump_block (lzopio) < 0)
477 goto CORRUPTED;
480 off = grub_file_tell (file) - lzopio->saved_off;
482 while (len != 0 && lzopio->block.usize != 0)
484 grub_size_t to_copy;
486 /* Block not decompressed yet. */
487 if (!lzopio->block.udata && uncompress_block (lzopio) < 0)
488 goto CORRUPTED;
490 /* Copy requested data into buffer. */
491 to_copy = lzopio->block.usize - off;
492 if (to_copy > len)
493 to_copy = len;
494 grub_memcpy (buf, lzopio->block.udata + off, to_copy);
496 len -= to_copy;
497 buf += to_copy;
498 ret += to_copy;
499 off = 0;
501 /* Read next block if needed. */
502 if (len > 0 && read_block_header (lzopio) < 0)
503 goto CORRUPTED;
506 return ret;
508 CORRUPTED:
509 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("lzop file corrupted"));
510 return -1;
513 /* Release everything, including the underlying file object. */
514 static grub_err_t
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);
522 grub_free (lzopio);
524 /* Device must not be closed twice. */
525 file->device = 0;
526 file->name = 0;
527 return grub_errno;
530 static struct grub_fs grub_lzopio_fs = {
531 .name = "lzopio",
532 .dir = 0,
533 .open = 0,
534 .read = grub_lzopio_read,
535 .close = grub_lzopio_close,
536 .label = 0,
537 .next = 0
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);