lzopio.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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. lzopio->block.ucheck = lzopio->block.ucheck;
  112. }
  113. /* Read checksum of compressed data. */
  114. if (lzopio->has_ccheck)
  115. {
  116. /* Incompressible data block. */
  117. if (lzopio->block.csize == lzopio->block.usize)
  118. {
  119. lzopio->block.ccheck = lzopio->block.ucheck;
  120. }
  121. else
  122. {
  123. if (grub_file_read (lzopio->file, &lzopio->block.ccheck,
  124. sizeof (lzopio->block.ccheck)) !=
  125. sizeof (lzopio->block.ccheck))
  126. return -1;
  127. lzopio->block.ccheck = lzopio->block.ccheck;
  128. }
  129. }
  130. return 0;
  131. }
  132. /* Read block data into memory. File must be set to beginning of block data.
  133. * Can't be called on last block. */
  134. static int
  135. read_block_data (struct grub_lzopio *lzopio)
  136. {
  137. lzopio->block.cdata = grub_malloc (lzopio->block.csize);
  138. if (!lzopio->block.cdata)
  139. return -1;
  140. if (grub_file_read (lzopio->file, lzopio->block.cdata, lzopio->block.csize)
  141. != (grub_ssize_t) lzopio->block.csize)
  142. return -1;
  143. if (lzopio->ccheck_fun)
  144. {
  145. grub_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN];
  146. if (lzopio->ccheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  147. return -1;
  148. grub_crypto_hash (lzopio->ccheck_fun, computed_hash,
  149. lzopio->block.cdata,
  150. lzopio->block.csize);
  151. if (grub_memcmp
  152. (computed_hash, &lzopio->block.ccheck,
  153. sizeof (lzopio->block.ccheck)) != 0)
  154. return -1;
  155. }
  156. return 0;
  157. }
  158. /* Read block data, uncompressed and also store it in memory. */
  159. /* XXX Investigate possibility of in-place decompression to reduce memory
  160. * footprint. Or try to uncompress directly to buf if possible. */
  161. static int
  162. uncompress_block (struct grub_lzopio *lzopio)
  163. {
  164. lzo_uint usize = lzopio->block.usize;
  165. if (read_block_data (lzopio) < 0)
  166. return -1;
  167. /* Incompressible data. */
  168. if (lzopio->block.csize == lzopio->block.usize)
  169. {
  170. lzopio->block.udata = lzopio->block.cdata;
  171. lzopio->block.cdata = NULL;
  172. }
  173. else
  174. {
  175. lzopio->block.udata = grub_malloc (lzopio->block.usize);
  176. if (!lzopio->block.udata)
  177. return -1;
  178. if (lzo1x_decompress_safe (lzopio->block.cdata, lzopio->block.csize,
  179. lzopio->block.udata, &usize, NULL)
  180. != LZO_E_OK)
  181. return -1;
  182. if (lzopio->ucheck_fun)
  183. {
  184. grub_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN];
  185. if (lzopio->ucheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN)
  186. return -1;
  187. grub_crypto_hash (lzopio->ucheck_fun, computed_hash,
  188. lzopio->block.udata,
  189. lzopio->block.usize);
  190. if (grub_memcmp
  191. (computed_hash, &lzopio->block.ucheck,
  192. sizeof (lzopio->block.ucheck)) != 0)
  193. return -1;
  194. }
  195. /* Compressed data can be free now. */
  196. grub_free (lzopio->block.cdata);
  197. lzopio->block.cdata = NULL;
  198. }
  199. return 0;
  200. }
  201. /* Jump to next block and read its header. */
  202. static int
  203. jump_block (struct grub_lzopio *lzopio)
  204. {
  205. /* only jump if block was not decompressed (and read from disk) */
  206. if (!lzopio->block.udata)
  207. {
  208. grub_off_t off = grub_file_tell (lzopio->file) + lzopio->block.csize;
  209. if (grub_file_seek (lzopio->file, off) == ((grub_off_t) - 1))
  210. return -1;
  211. }
  212. return read_block_header (lzopio);
  213. }
  214. static int
  215. calculate_uncompressed_size (grub_file_t file)
  216. {
  217. grub_lzopio_t lzopio = file->data;
  218. grub_off_t usize_total = 0;
  219. if (read_block_header (lzopio) < 0)
  220. return -1;
  221. /* FIXME: Don't do this for not easily seekable files. */
  222. while (lzopio->block.usize != 0)
  223. {
  224. usize_total += lzopio->block.usize;
  225. if (jump_block (lzopio) < 0)
  226. return -1;
  227. }
  228. file->size = usize_total;
  229. return 0;
  230. }
  231. struct lzop_header
  232. {
  233. grub_uint8_t magic[LZOP_MAGIC_SIZE];
  234. grub_uint16_t lzop_version;
  235. grub_uint16_t lib_version;
  236. grub_uint16_t lib_version_ext;
  237. grub_uint8_t method;
  238. grub_uint8_t level;
  239. grub_uint32_t flags;
  240. /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */
  241. grub_uint32_t mode;
  242. grub_uint32_t mtime_lo;
  243. grub_uint32_t mtime_hi;
  244. grub_uint8_t name_len;
  245. } GRUB_PACKED;
  246. static int
  247. test_header (grub_file_t file)
  248. {
  249. grub_lzopio_t lzopio = file->data;
  250. struct lzop_header header;
  251. grub_uint32_t flags, checksum;
  252. const gcry_md_spec_t *hcheck;
  253. grub_uint8_t *context = NULL;
  254. grub_uint8_t *name = NULL;
  255. if (grub_file_read (lzopio->file, &header, sizeof (header)) != sizeof (header))
  256. return 0;
  257. if (grub_memcmp (header.magic, LZOP_MAGIC, LZOP_MAGIC_SIZE) != 0)
  258. return 0;
  259. if (grub_be_to_cpu16(header.lib_version) < LZOP_NEW_LIB)
  260. return 0;
  261. /* Too new version, should upgrade minilzo? */
  262. if (grub_be_to_cpu16 (header.lib_version_ext) > MINILZO_VERSION)
  263. return 0;
  264. flags = grub_be_to_cpu32 (header.flags);
  265. if (flags & F_CRC32_D)
  266. {
  267. lzopio->has_ucheck = 1;
  268. lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("crc32");
  269. }
  270. else if (flags & F_ADLER32_D)
  271. {
  272. lzopio->has_ucheck = 1;
  273. lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("adler32");
  274. }
  275. if (flags & F_CRC32_C)
  276. {
  277. lzopio->has_ccheck = 1;
  278. lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("crc32");
  279. }
  280. else if (flags & F_ADLER32_C)
  281. {
  282. lzopio->has_ccheck = 1;
  283. lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("adler32");
  284. }
  285. if (flags & F_H_CRC32)
  286. hcheck = grub_crypto_lookup_md_by_name ("crc32");
  287. else
  288. hcheck = grub_crypto_lookup_md_by_name ("adler32");
  289. if (hcheck) {
  290. context = grub_malloc(hcheck->contextsize);
  291. if (! context)
  292. return 0;
  293. hcheck->init(context);
  294. /* MAGIC is not included in check calculation. */
  295. hcheck->write(context, &header.lzop_version, sizeof(header)- LZOP_MAGIC_SIZE);
  296. }
  297. if (header.name_len != 0)
  298. {
  299. name = grub_malloc (header.name_len);
  300. if (! name)
  301. {
  302. grub_free (context);
  303. return 0;
  304. }
  305. if (grub_file_read (lzopio->file, name, header.name_len) !=
  306. header.name_len)
  307. {
  308. grub_free(name);
  309. goto CORRUPTED;
  310. }
  311. if (hcheck)
  312. hcheck->write(context, name, header.name_len);
  313. grub_free(name);
  314. }
  315. if (hcheck)
  316. hcheck->final(context);
  317. if (grub_file_read (lzopio->file, &checksum, sizeof (checksum)) !=
  318. sizeof (checksum))
  319. goto CORRUPTED;
  320. if (hcheck && grub_memcmp (&checksum, hcheck->read(context), sizeof(checksum)) != 0)
  321. goto CORRUPTED;
  322. lzopio->start_block_off = grub_file_tell (lzopio->file);
  323. if (calculate_uncompressed_size (file) < 0)
  324. goto CORRUPTED;
  325. /* Get back to start block. */
  326. grub_file_seek (lzopio->file, lzopio->start_block_off);
  327. /* Read first block - grub_lzopio_read() expects valid block. */
  328. if (read_block_header (lzopio) < 0)
  329. goto CORRUPTED;
  330. lzopio->saved_off = 0;
  331. return 1;
  332. CORRUPTED:
  333. return 0;
  334. }
  335. static grub_file_t
  336. grub_lzopio_open (grub_file_t io, enum grub_file_type type)
  337. {
  338. grub_file_t file;
  339. grub_lzopio_t lzopio;
  340. if (type & GRUB_FILE_TYPE_NO_DECOMPRESS)
  341. return io;
  342. file = (grub_file_t) grub_zalloc (sizeof (*file));
  343. if (!file)
  344. return 0;
  345. lzopio = grub_zalloc (sizeof (*lzopio));
  346. if (!lzopio)
  347. {
  348. grub_free (file);
  349. return 0;
  350. }
  351. lzopio->file = io;
  352. file->device = io->device;
  353. file->data = lzopio;
  354. file->fs = &grub_lzopio_fs;
  355. file->size = GRUB_FILE_SIZE_UNKNOWN;
  356. file->not_easily_seekable = 1;
  357. if (grub_file_tell (lzopio->file) != 0)
  358. grub_file_seek (lzopio->file, 0);
  359. if (!test_header (file))
  360. {
  361. grub_errno = GRUB_ERR_NONE;
  362. grub_file_seek (io, 0);
  363. grub_free (lzopio);
  364. grub_free (file);
  365. return io;
  366. }
  367. return file;
  368. }
  369. static grub_ssize_t
  370. grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len)
  371. {
  372. grub_lzopio_t lzopio = file->data;
  373. grub_ssize_t ret = 0;
  374. grub_off_t off;
  375. /* Backward seek before last read block. */
  376. if (lzopio->saved_off > grub_file_tell (file))
  377. {
  378. grub_file_seek (lzopio->file, lzopio->start_block_off);
  379. if (read_block_header (lzopio) < 0)
  380. goto CORRUPTED;
  381. lzopio->saved_off = 0;
  382. }
  383. /* Forward to first block with requested data. */
  384. while (lzopio->saved_off + lzopio->block.usize <= grub_file_tell (file))
  385. {
  386. /* EOF, could be possible files with unknown size. */
  387. if (lzopio->block.usize == 0)
  388. return 0;
  389. if (jump_block (lzopio) < 0)
  390. goto CORRUPTED;
  391. }
  392. off = grub_file_tell (file) - lzopio->saved_off;
  393. while (len != 0 && lzopio->block.usize != 0)
  394. {
  395. grub_size_t to_copy;
  396. /* Block not decompressed yet. */
  397. if (!lzopio->block.udata && uncompress_block (lzopio) < 0)
  398. goto CORRUPTED;
  399. /* Copy requested data into buffer. */
  400. to_copy = lzopio->block.usize - off;
  401. if (to_copy > len)
  402. to_copy = len;
  403. grub_memcpy (buf, lzopio->block.udata + off, to_copy);
  404. len -= to_copy;
  405. buf += to_copy;
  406. ret += to_copy;
  407. off = 0;
  408. /* Read next block if needed. */
  409. if (len > 0 && read_block_header (lzopio) < 0)
  410. goto CORRUPTED;
  411. }
  412. return ret;
  413. CORRUPTED:
  414. grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("lzop file corrupted"));
  415. return -1;
  416. }
  417. /* Release everything, including the underlying file object. */
  418. static grub_err_t
  419. grub_lzopio_close (grub_file_t file)
  420. {
  421. grub_lzopio_t lzopio = file->data;
  422. grub_file_close (lzopio->file);
  423. grub_free (lzopio->block.cdata);
  424. grub_free (lzopio->block.udata);
  425. grub_free (lzopio);
  426. /* Device must not be closed twice. */
  427. file->device = 0;
  428. file->name = 0;
  429. return grub_errno;
  430. }
  431. static struct grub_fs grub_lzopio_fs = {
  432. .name = "lzopio",
  433. .fs_dir = 0,
  434. .fs_open = 0,
  435. .fs_read = grub_lzopio_read,
  436. .fs_close = grub_lzopio_close,
  437. .fs_label = 0,
  438. .next = 0
  439. };
  440. GRUB_MOD_INIT (lzopio)
  441. {
  442. grub_file_filter_register (GRUB_FILE_FILTER_LZOPIO, grub_lzopio_open);
  443. }
  444. GRUB_MOD_FINI (lzopio)
  445. {
  446. grub_file_filter_unregister (GRUB_FILE_FILTER_LZOPIO);
  447. }