lzopio.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. /* lzopio.c - decompression support for lzop */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2011 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/file.h>
  22. #include <grub/fs.h>
  23. #include <grub/dl.h>
  24. #include <grub/crypto.h>
  25. #include <minilzo.h>
  26. GRUB_MOD_LICENSE ("GPLv3+");
  27. #define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
  28. #define LZOP_MAGIC_SIZE 9
  29. #define LZOP_CHECK_SIZE 4
  30. #define LZOP_NEW_LIB 0x0940
  31. /* Header flags - copied from conf.h of LZOP source code. */
  32. #define F_ADLER32_D 0x00000001L
  33. #define F_ADLER32_C 0x00000002L
  34. #define F_STDIN 0x00000004L
  35. #define F_STDOUT 0x00000008L
  36. #define F_NAME_DEFAULT 0x00000010L
  37. #define F_DOSISH 0x00000020L
  38. #define F_H_EXTRA_FIELD 0x00000040L
  39. #define F_H_GMTDIFF 0x00000080L
  40. #define F_CRC32_D 0x00000100L
  41. #define F_CRC32_C 0x00000200L
  42. #define F_MULTIPART 0x00000400L
  43. #define F_H_FILTER 0x00000800L
  44. #define F_H_CRC32 0x00001000L
  45. #define F_H_PATH 0x00002000L
  46. #define F_MASK 0x00003FFFL
  47. struct block_header
  48. {
  49. grub_uint32_t usize;
  50. grub_uint32_t csize;
  51. grub_uint32_t ucheck;
  52. grub_uint32_t ccheck;
  53. unsigned char *cdata;
  54. unsigned char *udata;
  55. };
  56. struct grub_lzopio
  57. {
  58. grub_file_t file;
  59. int has_ccheck;
  60. int has_ucheck;
  61. const gcry_md_spec_t *ucheck_fun;
  62. const gcry_md_spec_t *ccheck_fun;
  63. grub_off_t saved_off; /* Rounded down to block boundary. */
  64. grub_off_t start_block_off;
  65. struct block_header block;
  66. };
  67. typedef struct grub_lzopio *grub_lzopio_t;
  68. static struct grub_fs grub_lzopio_fs;
  69. /* Some helper functions. On errors memory allocated by those function is free
  70. * either on close() so no risk of leaks. This makes functions simpler. */
  71. /* Read block header from file, after successful exit file points to
  72. * beginning of block data. */
  73. static int
  74. read_block_header (struct grub_lzopio *lzopio)
  75. {
  76. lzopio->saved_off += lzopio->block.usize;
  77. /* Free cached block data if any. */
  78. grub_free (lzopio->block.udata);
  79. grub_free (lzopio->block.cdata);
  80. lzopio->block.udata = NULL;
  81. lzopio->block.cdata = NULL;
  82. if (grub_file_read (lzopio->file, &lzopio->block.usize,
  83. sizeof (lzopio->block.usize)) !=
  84. sizeof (lzopio->block.usize))
  85. return -1;
  86. lzopio->block.usize = grub_be_to_cpu32 (lzopio->block.usize);
  87. /* Last block has uncompressed data size == 0 and no other fields. */
  88. if (lzopio->block.usize == 0)
  89. {
  90. if (grub_file_tell (lzopio->file) == grub_file_size (lzopio->file))
  91. return 0;
  92. else
  93. return -1;
  94. }
  95. /* Read compressed data block size. */
  96. if (grub_file_read (lzopio->file, &lzopio->block.csize,
  97. sizeof (lzopio->block.csize)) !=
  98. sizeof (lzopio->block.csize))
  99. return -1;
  100. lzopio->block.csize = grub_be_to_cpu32 (lzopio->block.csize);
  101. /* Corrupted. */
  102. if (lzopio->block.csize > lzopio->block.usize)
  103. return -1;
  104. /* Read checksum of uncompressed data. */
  105. if (lzopio->has_ucheck)
  106. {
  107. if (grub_file_read (lzopio->file, &lzopio->block.ucheck,
  108. sizeof (lzopio->block.ucheck)) !=
  109. sizeof (lzopio->block.ucheck))
  110. return -1;
  111. }
  112. /* Read checksum of compressed data. */
  113. if (lzopio->has_ccheck)
  114. {
  115. /* Incompressible data block. */
  116. if (lzopio->block.csize == lzopio->block.usize)
  117. {
  118. lzopio->block.ccheck = lzopio->block.ucheck;
  119. }
  120. else
  121. {
  122. if (grub_file_read (lzopio->file, &lzopio->block.ccheck,
  123. sizeof (lzopio->block.ccheck)) !=
  124. sizeof (lzopio->block.ccheck))
  125. return -1;
  126. }
  127. }
  128. return 0;
  129. }
  130. /* Read block data into memory. File must be set to beginning of block data.
  131. * Can't be called on last block. */
  132. static int
  133. read_block_data (struct grub_lzopio *lzopio)
  134. {
  135. lzopio->block.cdata = grub_malloc (lzopio->block.csize);
  136. if (!lzopio->block.cdata)
  137. return -1;
  138. if (grub_file_read (lzopio->file, lzopio->block.cdata, lzopio->block.csize)
  139. != (grub_ssize_t) lzopio->block.csize)
  140. return -1;
  141. if (lzopio->ccheck_fun)
  142. {
  143. grub_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN];
  144. if (lzopio->ccheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  145. return -1;
  146. grub_crypto_hash (lzopio->ccheck_fun, computed_hash,
  147. lzopio->block.cdata,
  148. lzopio->block.csize);
  149. if (grub_memcmp
  150. (computed_hash, &lzopio->block.ccheck,
  151. sizeof (lzopio->block.ccheck)) != 0)
  152. return -1;
  153. }
  154. return 0;
  155. }
  156. /* Read block data, uncompressed and also store it in memory. */
  157. /* XXX Investigate possibility of in-place decompression to reduce memory
  158. * footprint. Or try to uncompress directly to buf if possible. */
  159. static int
  160. uncompress_block (struct grub_lzopio *lzopio)
  161. {
  162. lzo_uint usize = lzopio->block.usize;
  163. if (read_block_data (lzopio) < 0)
  164. return -1;
  165. /* Incompressible data. */
  166. if (lzopio->block.csize == lzopio->block.usize)
  167. {
  168. lzopio->block.udata = lzopio->block.cdata;
  169. lzopio->block.cdata = NULL;
  170. }
  171. else
  172. {
  173. lzopio->block.udata = grub_malloc (lzopio->block.usize);
  174. if (!lzopio->block.udata)
  175. return -1;
  176. if (lzo1x_decompress_safe (lzopio->block.cdata, lzopio->block.csize,
  177. lzopio->block.udata, &usize, NULL)
  178. != LZO_E_OK)
  179. return -1;
  180. if (lzopio->ucheck_fun)
  181. {
  182. grub_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN];
  183. if (lzopio->ucheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  184. return -1;
  185. grub_crypto_hash (lzopio->ucheck_fun, computed_hash,
  186. lzopio->block.udata,
  187. lzopio->block.usize);
  188. if (grub_memcmp
  189. (computed_hash, &lzopio->block.ucheck,
  190. sizeof (lzopio->block.ucheck)) != 0)
  191. return -1;
  192. }
  193. /* Compressed data can be free now. */
  194. grub_free (lzopio->block.cdata);
  195. lzopio->block.cdata = NULL;
  196. }
  197. return 0;
  198. }
  199. /* Jump to next block and read its header. */
  200. static int
  201. jump_block (struct grub_lzopio *lzopio)
  202. {
  203. /* only jump if block was not decompressed (and read from disk) */
  204. if (!lzopio->block.udata)
  205. {
  206. grub_off_t off = grub_file_tell (lzopio->file) + lzopio->block.csize;
  207. if (grub_file_seek (lzopio->file, off) == ((grub_off_t) - 1))
  208. return -1;
  209. }
  210. return read_block_header (lzopio);
  211. }
  212. static int
  213. calculate_uncompressed_size (grub_file_t file)
  214. {
  215. grub_lzopio_t lzopio = file->data;
  216. grub_off_t usize_total = 0;
  217. if (read_block_header (lzopio) < 0)
  218. return -1;
  219. /* FIXME: Don't do this for not easily seekable files. */
  220. while (lzopio->block.usize != 0)
  221. {
  222. usize_total += lzopio->block.usize;
  223. if (jump_block (lzopio) < 0)
  224. return -1;
  225. }
  226. file->size = usize_total;
  227. return 0;
  228. }
  229. struct lzop_header
  230. {
  231. grub_uint8_t magic[LZOP_MAGIC_SIZE];
  232. grub_uint16_t lzop_version;
  233. grub_uint16_t lib_version;
  234. grub_uint16_t lib_version_ext;
  235. grub_uint8_t method;
  236. grub_uint8_t level;
  237. grub_uint32_t flags;
  238. /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */
  239. grub_uint32_t mode;
  240. grub_uint32_t mtime_lo;
  241. grub_uint32_t mtime_hi;
  242. grub_uint8_t name_len;
  243. } GRUB_PACKED;
  244. static int
  245. test_header (grub_file_t file)
  246. {
  247. grub_lzopio_t lzopio = file->data;
  248. struct lzop_header header;
  249. grub_uint32_t flags, checksum;
  250. const gcry_md_spec_t *hcheck;
  251. grub_uint8_t *context = NULL;
  252. grub_uint8_t *name = NULL;
  253. if (grub_file_read (lzopio->file, &header, sizeof (header)) != sizeof (header))
  254. return 0;
  255. if (grub_memcmp (header.magic, LZOP_MAGIC, LZOP_MAGIC_SIZE) != 0)
  256. return 0;
  257. if (grub_be_to_cpu16(header.lib_version) < LZOP_NEW_LIB)
  258. return 0;
  259. /* Too new version, should upgrade minilzo? */
  260. if (grub_be_to_cpu16 (header.lib_version_ext) > MINILZO_VERSION)
  261. return 0;
  262. flags = grub_be_to_cpu32 (header.flags);
  263. if (flags & F_CRC32_D)
  264. {
  265. lzopio->has_ucheck = 1;
  266. lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("crc32");
  267. }
  268. else if (flags & F_ADLER32_D)
  269. {
  270. lzopio->has_ucheck = 1;
  271. lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("adler32");
  272. }
  273. if (flags & F_CRC32_C)
  274. {
  275. lzopio->has_ccheck = 1;
  276. lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("crc32");
  277. }
  278. else if (flags & F_ADLER32_C)
  279. {
  280. lzopio->has_ccheck = 1;
  281. lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("adler32");
  282. }
  283. if (flags & F_H_CRC32)
  284. hcheck = grub_crypto_lookup_md_by_name ("crc32");
  285. else
  286. hcheck = grub_crypto_lookup_md_by_name ("adler32");
  287. if (hcheck) {
  288. context = grub_malloc(hcheck->contextsize);
  289. if (! context)
  290. return 0;
  291. hcheck->init(context);
  292. /* MAGIC is not included in check calculation. */
  293. hcheck->write(context, &header.lzop_version, sizeof(header)- LZOP_MAGIC_SIZE);
  294. }
  295. if (header.name_len != 0)
  296. {
  297. name = grub_malloc (header.name_len);
  298. if (! name)
  299. {
  300. grub_free (context);
  301. return 0;
  302. }
  303. if (grub_file_read (lzopio->file, name, header.name_len) !=
  304. header.name_len)
  305. {
  306. grub_free(name);
  307. goto CORRUPTED;
  308. }
  309. if (hcheck)
  310. hcheck->write(context, name, header.name_len);
  311. grub_free(name);
  312. }
  313. if (hcheck)
  314. hcheck->final(context);
  315. if (grub_file_read (lzopio->file, &checksum, sizeof (checksum)) !=
  316. sizeof (checksum))
  317. goto CORRUPTED;
  318. if (hcheck && grub_memcmp (&checksum, hcheck->read(context), sizeof(checksum)) != 0)
  319. goto CORRUPTED;
  320. lzopio->start_block_off = grub_file_tell (lzopio->file);
  321. if (calculate_uncompressed_size (file) < 0)
  322. goto CORRUPTED;
  323. /* Get back to start block. */
  324. grub_file_seek (lzopio->file, lzopio->start_block_off);
  325. /* Read first block - grub_lzopio_read() expects valid block. */
  326. if (read_block_header (lzopio) < 0)
  327. goto CORRUPTED;
  328. lzopio->saved_off = 0;
  329. return 1;
  330. CORRUPTED:
  331. return 0;
  332. }
  333. static grub_file_t
  334. grub_lzopio_open (grub_file_t io, enum grub_file_type type)
  335. {
  336. grub_file_t file;
  337. grub_lzopio_t lzopio;
  338. if (type & GRUB_FILE_TYPE_NO_DECOMPRESS)
  339. return io;
  340. file = (grub_file_t) grub_zalloc (sizeof (*file));
  341. if (!file)
  342. return 0;
  343. lzopio = grub_zalloc (sizeof (*lzopio));
  344. if (!lzopio)
  345. {
  346. grub_free (file);
  347. return 0;
  348. }
  349. lzopio->file = io;
  350. file->device = io->device;
  351. file->data = lzopio;
  352. file->fs = &grub_lzopio_fs;
  353. file->size = GRUB_FILE_SIZE_UNKNOWN;
  354. file->not_easily_seekable = 1;
  355. if (grub_file_tell (lzopio->file) != 0)
  356. grub_file_seek (lzopio->file, 0);
  357. if (!test_header (file))
  358. {
  359. grub_errno = GRUB_ERR_NONE;
  360. grub_file_seek (io, 0);
  361. grub_free (lzopio);
  362. grub_free (file);
  363. return io;
  364. }
  365. return file;
  366. }
  367. static grub_ssize_t
  368. grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len)
  369. {
  370. grub_lzopio_t lzopio = file->data;
  371. grub_ssize_t ret = 0;
  372. grub_off_t off;
  373. /* Backward seek before last read block. */
  374. if (lzopio->saved_off > grub_file_tell (file))
  375. {
  376. grub_file_seek (lzopio->file, lzopio->start_block_off);
  377. if (read_block_header (lzopio) < 0)
  378. goto CORRUPTED;
  379. lzopio->saved_off = 0;
  380. }
  381. /* Forward to first block with requested data. */
  382. while (lzopio->saved_off + lzopio->block.usize <= grub_file_tell (file))
  383. {
  384. /* EOF, could be possible files with unknown size. */
  385. if (lzopio->block.usize == 0)
  386. return 0;
  387. if (jump_block (lzopio) < 0)
  388. goto CORRUPTED;
  389. }
  390. off = grub_file_tell (file) - lzopio->saved_off;
  391. while (len != 0 && lzopio->block.usize != 0)
  392. {
  393. grub_size_t to_copy;
  394. /* Block not decompressed yet. */
  395. if (!lzopio->block.udata && uncompress_block (lzopio) < 0)
  396. goto CORRUPTED;
  397. /* Copy requested data into buffer. */
  398. to_copy = lzopio->block.usize - off;
  399. if (to_copy > len)
  400. to_copy = len;
  401. grub_memcpy (buf, lzopio->block.udata + off, to_copy);
  402. len -= to_copy;
  403. buf += to_copy;
  404. ret += to_copy;
  405. off = 0;
  406. /* Read next block if needed. */
  407. if (len > 0 && read_block_header (lzopio) < 0)
  408. goto CORRUPTED;
  409. }
  410. return ret;
  411. CORRUPTED:
  412. grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("lzop file corrupted"));
  413. return -1;
  414. }
  415. /* Release everything, including the underlying file object. */
  416. static grub_err_t
  417. grub_lzopio_close (grub_file_t file)
  418. {
  419. grub_lzopio_t lzopio = file->data;
  420. grub_file_close (lzopio->file);
  421. grub_free (lzopio->block.cdata);
  422. grub_free (lzopio->block.udata);
  423. grub_free (lzopio);
  424. /* Device must not be closed twice. */
  425. file->device = 0;
  426. file->name = 0;
  427. return grub_errno;
  428. }
  429. static struct grub_fs grub_lzopio_fs = {
  430. .name = "lzopio",
  431. .fs_dir = 0,
  432. .fs_open = 0,
  433. .fs_read = grub_lzopio_read,
  434. .fs_close = grub_lzopio_close,
  435. .fs_label = 0,
  436. .next = 0
  437. };
  438. GRUB_MOD_INIT (lzopio)
  439. {
  440. grub_file_filter_register (GRUB_FILE_FILTER_LZOPIO, grub_lzopio_open);
  441. }
  442. GRUB_MOD_FINI (lzopio)
  443. {
  444. grub_file_filter_unregister (GRUB_FILE_FILTER_LZOPIO);
  445. }