1
/* xzio.c - decompression support for xz */
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/>.
22
#include <grub/misc.h>
23
#include <grub/file.h>
28
#include "xz_stream.h"
30
#define XZBUFSIZ 0x2000
31
#define VLI_MAX_DIGITS 9
32
#define XZ_STREAM_FOOTER_SIZE 12
39
grub_uint8_t inbuf[XZBUFSIZ];
40
grub_uint8_t outbuf[XZBUFSIZ];
41
grub_off_t saved_offset;
44
typedef struct grub_xzio *grub_xzio_t;
45
static struct grub_fs grub_xzio_fs;
48
decode_vli (const grub_uint8_t buf[], grub_size_t size_max,
54
if (size_max > VLI_MAX_DIGITS)
55
size_max = VLI_MAX_DIGITS;
60
while (buf[i++] & 0x80)
62
if (i >= size_max || buf[i] == 0x00)
65
*num |= (uint64_t) (buf[i] & 0x7F) << (i * 7);
72
read_vli (grub_file_t file, grub_uint64_t * num)
74
grub_uint8_t buf[VLI_MAX_DIGITS];
78
read = grub_file_read (file, buf, VLI_MAX_DIGITS);
82
dec = decode_vli (buf, read, num);
83
grub_file_seek (file, file->offset - (read - dec));
87
/* Function xz_dec_run() should consume header and ask for more (XZ_OK)
88
* else file is corrupted (or options not supported) or not xz. */
90
test_header (grub_file_t file)
92
grub_xzio_t xzio = file->data;
93
xzio->buf.in_size = grub_file_read (xzio->file, xzio->inbuf,
96
if (xzio->buf.in_size != STREAM_HEADER_SIZE)
98
grub_error (GRUB_ERR_BAD_FILE_TYPE, "no xz magic found");
102
enum xz_ret ret = xz_dec_run (xzio->dec, &xzio->buf);
104
if (ret == XZ_FORMAT_ERROR)
106
grub_error (GRUB_ERR_BAD_FILE_TYPE, "no xz magic found");
112
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "not supported xz options");
119
/* Try to find out size of uncompressed data,
120
* also do some footer sanity checks. */
122
test_footer (grub_file_t file)
124
grub_xzio_t xzio = file->data;
125
grub_uint8_t footer[FOOTER_MAGIC_SIZE];
126
grub_uint32_t backsize;
127
grub_uint8_t imarker;
128
grub_uint64_t uncompressed_size_total = 0;
129
grub_uint64_t uncompressed_size;
130
grub_uint64_t records;
132
grub_file_seek (xzio->file, xzio->file->size - FOOTER_MAGIC_SIZE);
133
if (grub_file_read (xzio->file, footer, FOOTER_MAGIC_SIZE) !=
135
|| grub_memcmp (footer, FOOTER_MAGIC, FOOTER_MAGIC_SIZE) != 0)
138
grub_file_seek (xzio->file, xzio->file->size - 8);
139
if (grub_file_read (xzio->file, &backsize, sizeof (backsize))
140
!= sizeof (backsize))
143
/* Calculate real backward size. */
144
backsize = (grub_le_to_cpu32 (backsize) + 1) * 4;
146
/* Set file to the beginning of stream index. */
147
grub_file_seek (xzio->file,
148
xzio->file->size - XZ_STREAM_FOOTER_SIZE - backsize);
150
/* Test index marker. */
151
if (grub_file_read (xzio->file, &imarker, sizeof (imarker)) !=
152
sizeof (imarker) && imarker != 0x00)
155
if (read_vli (xzio->file, &records) <= 0)
158
for (; records != 0; records--)
160
if (read_vli (xzio->file, &uncompressed_size) <= 0) /* Ignore unpadded. */
162
if (read_vli (xzio->file, &uncompressed_size) <= 0) /* Uncompressed. */
165
uncompressed_size_total += uncompressed_size;
168
file->size = uncompressed_size_total;
169
grub_file_seek (xzio->file, STREAM_HEADER_SIZE);
173
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "bad footer magic");
178
grub_xzio_open (grub_file_t io)
183
file = (grub_file_t) grub_zalloc (sizeof (*file));
187
xzio = grub_zalloc (sizeof (*xzio));
195
xzio->saved_offset = 0;
197
file->device = io->device;
201
file->fs = &grub_xzio_fs;
202
file->size = GRUB_FILE_SIZE_UNKNOWN;
203
file->not_easly_seekable = 1;
205
if (grub_file_tell (xzio->file) != 0)
206
grub_file_seek (xzio->file, 0);
208
/* Allocated 64KiB for dictionary.
209
* Decoder will relocate if bigger is needed. */
210
xzio->dec = xz_dec_init (1 << 16);
218
xzio->buf.in = xzio->inbuf;
219
xzio->buf.in_pos = 0;
220
xzio->buf.in_size = 0;
221
xzio->buf.out = xzio->outbuf;
222
xzio->buf.out_pos = 0;
223
xzio->buf.out_size = XZBUFSIZ;
225
if (!test_header (file) || !(grub_file_seekable (io) && test_footer (file)))
227
grub_errno = GRUB_ERR_NONE;
228
grub_file_seek (io, 0);
229
xz_dec_end (xzio->dec);
240
grub_xzio_read (grub_file_t file, char *buf, grub_size_t len)
242
grub_ssize_t ret = 0;
243
grub_ssize_t readret;
245
grub_xzio_t xzio = file->data;
246
grub_off_t current_offset;
248
/* If seek backward need to reset decoder and start from beginning of file.
249
TODO Possible improvement by jumping blocks. */
250
if (file->offset < xzio->saved_offset)
252
xz_dec_reset (xzio->dec);
253
xzio->saved_offset = 0;
254
xzio->buf.out_pos = 0;
255
xzio->buf.in_pos = 0;
256
xzio->buf.in_size = 0;
257
grub_file_seek (xzio->file, 0);
260
current_offset = xzio->saved_offset;
264
xzio->buf.out_size = grub_min (file->offset + ret + len - current_offset,
268
if (xzio->buf.in_pos == xzio->buf.in_size)
270
readret = grub_file_read (xzio->file, xzio->inbuf, XZBUFSIZ);
273
xzio->buf.in_size = readret;
274
xzio->buf.in_pos = 0;
277
xzret = xz_dec_run (xzio->dec, &xzio->buf);
280
case XZ_MEMLIMIT_ERROR:
281
case XZ_FORMAT_ERROR:
282
case XZ_OPTIONS_ERROR:
285
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
286
"file corrupted or unsupported block options");
293
grub_off_t new_offset = current_offset + xzio->buf.out_pos;
295
if (file->offset <= new_offset)
296
/* Store first chunk of data in buffer. */
298
grub_size_t delta = new_offset - (file->offset + ret);
299
grub_memmove (buf, xzio->buf.out + (xzio->buf.out_pos - delta),
305
current_offset = new_offset;
307
xzio->buf.out_pos = 0;
309
if (xzret == XZ_STREAM_END) /* Stream end, EOF. */
314
xzio->saved_offset = file->offset + ret;
319
/* Release everything, including the underlying file object. */
321
grub_xzio_close (grub_file_t file)
323
grub_xzio_t xzio = file->data;
325
xz_dec_end (xzio->dec);
327
grub_file_close (xzio->file);
330
/* Device must not be closed twice. */
335
static struct grub_fs grub_xzio_fs = {
339
.read = grub_xzio_read,
340
.close = grub_xzio_close,
347
grub_file_filter_register (GRUB_FILE_FILTER_XZIO, grub_xzio_open);
352
grub_file_filter_unregister (GRUB_FILE_FILTER_XZIO);