xzio.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /* xzio.c - decompression support for xz */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2010 Free Software Foundation, Inc.
  5. *
  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.
  10. *
  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.
  15. *
  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/>.
  18. */
  19. #include <grub/err.h>
  20. #include <grub/mm.h>
  21. #include <grub/misc.h>
  22. #include <grub/file.h>
  23. #include <grub/fs.h>
  24. #include <grub/dl.h>
  25. GRUB_MOD_LICENSE ("GPLv3+");
  26. #include "xz.h"
  27. #include "xz_stream.h"
  28. #define XZBUFSIZ 0x2000
  29. #define VLI_MAX_DIGITS 9
  30. #define XZ_STREAM_FOOTER_SIZE 12
  31. struct grub_xzio
  32. {
  33. grub_file_t file;
  34. struct xz_buf buf;
  35. struct xz_dec *dec;
  36. grub_uint8_t inbuf[XZBUFSIZ];
  37. grub_uint8_t outbuf[XZBUFSIZ];
  38. grub_off_t saved_offset;
  39. };
  40. typedef struct grub_xzio *grub_xzio_t;
  41. static struct grub_fs grub_xzio_fs;
  42. static grub_size_t
  43. decode_vli (const grub_uint8_t buf[], grub_size_t size_max,
  44. grub_uint64_t *num)
  45. {
  46. if (size_max == 0)
  47. return 0;
  48. if (size_max > VLI_MAX_DIGITS)
  49. size_max = VLI_MAX_DIGITS;
  50. *num = buf[0] & 0x7F;
  51. grub_size_t i = 0;
  52. while (buf[i++] & 0x80)
  53. {
  54. if (i >= size_max || buf[i] == 0x00)
  55. return 0;
  56. *num |= (uint64_t) (buf[i] & 0x7F) << (i * 7);
  57. }
  58. return i;
  59. }
  60. static grub_ssize_t
  61. read_vli (grub_file_t file, grub_uint64_t *num)
  62. {
  63. grub_uint8_t buf[VLI_MAX_DIGITS];
  64. grub_ssize_t read_bytes;
  65. grub_size_t dec;
  66. read_bytes = grub_file_read (file, buf, VLI_MAX_DIGITS);
  67. if (read_bytes < 0)
  68. return -1;
  69. dec = decode_vli (buf, read_bytes, num);
  70. grub_file_seek (file, file->offset - (read_bytes - dec));
  71. return dec;
  72. }
  73. /* Function xz_dec_run() should consume header and ask for more (XZ_OK)
  74. * else file is corrupted (or options not supported) or not xz. */
  75. static int
  76. test_header (grub_file_t file)
  77. {
  78. grub_xzio_t xzio = file->data;
  79. enum xz_ret ret;
  80. xzio->buf.in_size = grub_file_read (xzio->file, xzio->inbuf,
  81. STREAM_HEADER_SIZE);
  82. if (xzio->buf.in_size != STREAM_HEADER_SIZE)
  83. return 0;
  84. ret = xz_dec_run (xzio->dec, &xzio->buf);
  85. if (ret == XZ_FORMAT_ERROR)
  86. return 0;
  87. if (ret != XZ_OK)
  88. return 0;
  89. return 1;
  90. }
  91. /* Try to find out size of uncompressed data,
  92. * also do some footer sanity checks. */
  93. static int
  94. test_footer (grub_file_t file)
  95. {
  96. grub_xzio_t xzio = file->data;
  97. grub_uint8_t footer[FOOTER_MAGIC_SIZE];
  98. grub_uint32_t backsize;
  99. grub_uint8_t imarker;
  100. grub_uint64_t uncompressed_size_total = 0;
  101. grub_uint64_t uncompressed_size;
  102. grub_uint64_t records;
  103. grub_file_seek (xzio->file, xzio->file->size - FOOTER_MAGIC_SIZE);
  104. if (grub_file_read (xzio->file, footer, FOOTER_MAGIC_SIZE)
  105. != FOOTER_MAGIC_SIZE
  106. || grub_memcmp (footer, FOOTER_MAGIC, FOOTER_MAGIC_SIZE) != 0)
  107. goto ERROR;
  108. grub_file_seek (xzio->file, xzio->file->size - 8);
  109. if (grub_file_read (xzio->file, &backsize, sizeof (backsize))
  110. != sizeof (backsize))
  111. goto ERROR;
  112. /* Calculate real backward size. */
  113. backsize = (grub_le_to_cpu32 (backsize) + 1) * 4;
  114. /* Set file to the beginning of stream index. */
  115. grub_file_seek (xzio->file,
  116. xzio->file->size - XZ_STREAM_FOOTER_SIZE - backsize);
  117. /* Test index marker. */
  118. if (grub_file_read (xzio->file, &imarker, sizeof (imarker))
  119. != sizeof (imarker) && imarker != 0x00)
  120. goto ERROR;
  121. if (read_vli (xzio->file, &records) <= 0)
  122. goto ERROR;
  123. for (; records != 0; records--)
  124. {
  125. if (read_vli (xzio->file, &uncompressed_size) <= 0) /* Ignore unpadded. */
  126. goto ERROR;
  127. if (read_vli (xzio->file, &uncompressed_size) <= 0) /* Uncompressed. */
  128. goto ERROR;
  129. uncompressed_size_total += uncompressed_size;
  130. }
  131. file->size = uncompressed_size_total;
  132. grub_file_seek (xzio->file, STREAM_HEADER_SIZE);
  133. return 1;
  134. ERROR:
  135. return 0;
  136. }
  137. static grub_file_t
  138. grub_xzio_open (grub_file_t io, enum grub_file_type type)
  139. {
  140. grub_file_t file;
  141. grub_xzio_t xzio;
  142. if (type & GRUB_FILE_TYPE_NO_DECOMPRESS)
  143. return io;
  144. file = (grub_file_t) grub_zalloc (sizeof (*file));
  145. if (!file)
  146. return 0;
  147. xzio = grub_zalloc (sizeof (*xzio));
  148. if (!xzio)
  149. {
  150. grub_free (file);
  151. return 0;
  152. }
  153. xzio->file = io;
  154. file->device = io->device;
  155. file->data = xzio;
  156. file->fs = &grub_xzio_fs;
  157. file->size = GRUB_FILE_SIZE_UNKNOWN;
  158. file->not_easily_seekable = 1;
  159. if (grub_file_tell (xzio->file) != 0)
  160. grub_file_seek (xzio->file, 0);
  161. /* Allocated 64KiB for dictionary.
  162. * Decoder will relocate if bigger is needed. */
  163. xzio->dec = xz_dec_init (1 << 16);
  164. if (!xzio->dec)
  165. {
  166. grub_free (file);
  167. grub_free (xzio);
  168. return 0;
  169. }
  170. xzio->buf.in = xzio->inbuf;
  171. xzio->buf.out = xzio->outbuf;
  172. xzio->buf.out_size = XZBUFSIZ;
  173. /* FIXME: don't test footer on not easily seekable files. */
  174. if (!test_header (file) || !test_footer (file))
  175. {
  176. grub_errno = GRUB_ERR_NONE;
  177. grub_file_seek (io, 0);
  178. xz_dec_end (xzio->dec);
  179. grub_free (xzio);
  180. grub_free (file);
  181. return io;
  182. }
  183. return file;
  184. }
  185. static grub_ssize_t
  186. grub_xzio_read (grub_file_t file, char *buf, grub_size_t len)
  187. {
  188. grub_ssize_t ret = 0;
  189. grub_ssize_t readret;
  190. enum xz_ret xzret;
  191. grub_xzio_t xzio = file->data;
  192. grub_off_t current_offset;
  193. /* If seek backward need to reset decoder and start from beginning of file.
  194. TODO Possible improvement by jumping blocks. */
  195. if (file->offset < xzio->saved_offset)
  196. {
  197. xz_dec_reset (xzio->dec);
  198. xzio->saved_offset = 0;
  199. xzio->buf.out_pos = 0;
  200. xzio->buf.in_pos = 0;
  201. xzio->buf.in_size = 0;
  202. grub_file_seek (xzio->file, 0);
  203. }
  204. current_offset = xzio->saved_offset;
  205. while (len > 0)
  206. {
  207. xzio->buf.out_size = file->offset + ret + len - current_offset;
  208. if (xzio->buf.out_size > XZBUFSIZ)
  209. xzio->buf.out_size = XZBUFSIZ;
  210. /* Feed input. */
  211. if (xzio->buf.in_pos == xzio->buf.in_size)
  212. {
  213. readret = grub_file_read (xzio->file, xzio->inbuf, XZBUFSIZ);
  214. if (readret < 0)
  215. return -1;
  216. xzio->buf.in_size = readret;
  217. xzio->buf.in_pos = 0;
  218. }
  219. xzret = xz_dec_run (xzio->dec, &xzio->buf);
  220. switch (xzret)
  221. {
  222. case XZ_MEMLIMIT_ERROR:
  223. case XZ_FORMAT_ERROR:
  224. case XZ_OPTIONS_ERROR:
  225. case XZ_DATA_ERROR:
  226. case XZ_BUF_ERROR:
  227. grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
  228. N_("xz file corrupted or unsupported block options"));
  229. return -1;
  230. default:
  231. break;
  232. }
  233. {
  234. grub_off_t new_offset = current_offset + xzio->buf.out_pos;
  235. if (file->offset <= new_offset)
  236. /* Store first chunk of data in buffer. */
  237. {
  238. grub_size_t delta = new_offset - (file->offset + ret);
  239. grub_memmove (buf, xzio->buf.out + (xzio->buf.out_pos - delta),
  240. delta);
  241. len -= delta;
  242. buf += delta;
  243. ret += delta;
  244. }
  245. current_offset = new_offset;
  246. }
  247. xzio->buf.out_pos = 0;
  248. if (xzret == XZ_STREAM_END) /* Stream end, EOF. */
  249. break;
  250. }
  251. if (ret >= 0)
  252. xzio->saved_offset = file->offset + ret;
  253. return ret;
  254. }
  255. /* Release everything, including the underlying file object. */
  256. static grub_err_t
  257. grub_xzio_close (grub_file_t file)
  258. {
  259. grub_xzio_t xzio = file->data;
  260. xz_dec_end (xzio->dec);
  261. grub_file_close (xzio->file);
  262. grub_free (xzio);
  263. /* Device must not be closed twice. */
  264. file->device = 0;
  265. file->name = 0;
  266. return grub_errno;
  267. }
  268. static struct grub_fs grub_xzio_fs = {
  269. .name = "xzio",
  270. .fs_dir = 0,
  271. .fs_open = 0,
  272. .fs_read = grub_xzio_read,
  273. .fs_close = grub_xzio_close,
  274. .fs_label = 0,
  275. .next = 0
  276. };
  277. GRUB_MOD_INIT (xzio)
  278. {
  279. grub_file_filter_register (GRUB_FILE_FILTER_XZIO, grub_xzio_open);
  280. }
  281. GRUB_MOD_FINI (xzio)
  282. {
  283. grub_file_filter_unregister (GRUB_FILE_FILTER_XZIO);
  284. }