squash4.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. /* squash4.c - SquashFS */
  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/file.h>
  21. #include <grub/mm.h>
  22. #include <grub/misc.h>
  23. #include <grub/disk.h>
  24. #include <grub/dl.h>
  25. #include <grub/types.h>
  26. #include <grub/fshelp.h>
  27. #include <grub/deflate.h>
  28. #include <minilzo.h>
  29. #include "xz.h"
  30. #include "xz_stream.h"
  31. GRUB_MOD_LICENSE ("GPLv3+");
  32. /*
  33. object format Pointed by
  34. superblock RAW Fixed offset (0)
  35. data RAW ? Fixed offset (60)
  36. inode table Chunk superblock
  37. dir table Chunk superblock
  38. fragment table Chunk unk1
  39. unk1 RAW, Chunk superblock
  40. unk2 RAW superblock
  41. UID/GID Chunk exttblptr
  42. exttblptr RAW superblock
  43. UID/GID table is the array ot uint32_t
  44. unk1 contains pointer to fragment table followed by some chunk.
  45. unk2 containts one uint64_t
  46. */
  47. struct grub_squash_super
  48. {
  49. grub_uint32_t magic;
  50. #define SQUASH_MAGIC 0x73717368
  51. grub_uint32_t dummy1;
  52. grub_uint32_t creation_time;
  53. grub_uint32_t block_size;
  54. grub_uint32_t dummy2;
  55. grub_uint16_t compression;
  56. grub_uint16_t dummy3;
  57. grub_uint64_t dummy4;
  58. grub_uint16_t root_ino_offset;
  59. grub_uint32_t root_ino_chunk;
  60. grub_uint16_t dummy5;
  61. grub_uint64_t total_size;
  62. grub_uint64_t exttbloffset;
  63. grub_uint64_t dummy6;
  64. grub_uint64_t inodeoffset;
  65. grub_uint64_t diroffset;
  66. grub_uint64_t unk1offset;
  67. grub_uint64_t unk2offset;
  68. } GRUB_PACKED;
  69. /* Chunk-based */
  70. struct grub_squash_inode
  71. {
  72. /* Same values as direlem types. */
  73. grub_uint16_t type;
  74. grub_uint16_t dummy[3];
  75. grub_uint32_t mtime;
  76. grub_uint32_t dummy2;
  77. union
  78. {
  79. struct {
  80. grub_uint32_t chunk;
  81. grub_uint32_t fragment;
  82. grub_uint32_t offset;
  83. grub_uint32_t size;
  84. grub_uint32_t block_size[0];
  85. } GRUB_PACKED file;
  86. struct {
  87. grub_uint64_t chunk;
  88. grub_uint64_t size;
  89. grub_uint32_t dummy1[3];
  90. grub_uint32_t fragment;
  91. grub_uint32_t offset;
  92. grub_uint32_t dummy3;
  93. grub_uint32_t block_size[0];
  94. } GRUB_PACKED long_file;
  95. struct {
  96. grub_uint32_t chunk;
  97. grub_uint32_t dummy;
  98. grub_uint16_t size;
  99. grub_uint16_t offset;
  100. } GRUB_PACKED dir;
  101. struct {
  102. grub_uint32_t dummy1;
  103. grub_uint32_t size;
  104. grub_uint32_t chunk;
  105. grub_uint32_t dummy2;
  106. grub_uint16_t dummy3;
  107. grub_uint16_t offset;
  108. } GRUB_PACKED long_dir;
  109. struct {
  110. grub_uint32_t dummy;
  111. grub_uint32_t namelen;
  112. char name[0];
  113. } GRUB_PACKED symlink;
  114. } GRUB_PACKED;
  115. } GRUB_PACKED;
  116. struct grub_squash_cache_inode
  117. {
  118. struct grub_squash_inode ino;
  119. grub_disk_addr_t ino_chunk;
  120. grub_uint16_t ino_offset;
  121. grub_uint32_t *block_sizes;
  122. grub_disk_addr_t *cumulated_block_sizes;
  123. };
  124. /* Chunk-based. */
  125. struct grub_squash_dirent_header
  126. {
  127. /* Actually the value is the number of elements - 1. */
  128. grub_uint32_t nelems;
  129. grub_uint32_t ino_chunk;
  130. grub_uint32_t dummy;
  131. } GRUB_PACKED;
  132. struct grub_squash_dirent
  133. {
  134. grub_uint16_t ino_offset;
  135. grub_uint16_t dummy;
  136. grub_uint16_t type;
  137. /* Actually the value is the length of name - 1. */
  138. grub_uint16_t namelen;
  139. char name[0];
  140. } GRUB_PACKED;
  141. enum
  142. {
  143. SQUASH_TYPE_DIR = 1,
  144. SQUASH_TYPE_REGULAR = 2,
  145. SQUASH_TYPE_SYMLINK = 3,
  146. SQUASH_TYPE_LONG_DIR = 8,
  147. SQUASH_TYPE_LONG_REGULAR = 9,
  148. };
  149. struct grub_squash_frag_desc
  150. {
  151. grub_uint64_t offset;
  152. grub_uint32_t size;
  153. grub_uint32_t dummy;
  154. } GRUB_PACKED;
  155. enum
  156. {
  157. SQUASH_CHUNK_FLAGS = 0x8000,
  158. SQUASH_CHUNK_UNCOMPRESSED = 0x8000
  159. };
  160. enum
  161. {
  162. SQUASH_BLOCK_FLAGS = 0x1000000,
  163. SQUASH_BLOCK_UNCOMPRESSED = 0x1000000
  164. };
  165. enum
  166. {
  167. COMPRESSION_ZLIB = 1,
  168. COMPRESSION_LZO = 3,
  169. COMPRESSION_XZ = 4,
  170. };
  171. #define SQUASH_CHUNK_SIZE 0x2000
  172. #define XZBUFSIZ 0x2000
  173. struct grub_squash_data
  174. {
  175. grub_disk_t disk;
  176. struct grub_squash_super sb;
  177. struct grub_squash_cache_inode ino;
  178. grub_uint64_t fragments;
  179. int log2_blksz;
  180. grub_size_t blksz;
  181. grub_ssize_t (*decompress) (char *inbuf, grub_size_t insize, grub_off_t off,
  182. char *outbuf, grub_size_t outsize,
  183. struct grub_squash_data *data);
  184. struct xz_dec *xzdec;
  185. char *xzbuf;
  186. };
  187. struct grub_fshelp_node
  188. {
  189. struct grub_squash_data *data;
  190. struct grub_squash_inode ino;
  191. grub_size_t stsize;
  192. struct
  193. {
  194. grub_disk_addr_t ino_chunk;
  195. grub_uint16_t ino_offset;
  196. } stack[1];
  197. };
  198. static grub_err_t
  199. read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len,
  200. grub_uint64_t chunk_start, grub_off_t offset)
  201. {
  202. while (len > 0)
  203. {
  204. grub_uint64_t csize;
  205. grub_uint16_t d;
  206. grub_err_t err;
  207. while (1)
  208. {
  209. err = grub_disk_read (data->disk,
  210. chunk_start >> GRUB_DISK_SECTOR_BITS,
  211. chunk_start & (GRUB_DISK_SECTOR_SIZE - 1),
  212. sizeof (d), &d);
  213. if (err)
  214. return err;
  215. if (offset < SQUASH_CHUNK_SIZE)
  216. break;
  217. offset -= SQUASH_CHUNK_SIZE;
  218. chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS);
  219. }
  220. csize = SQUASH_CHUNK_SIZE - offset;
  221. if (csize > len)
  222. csize = len;
  223. if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED)
  224. {
  225. grub_disk_addr_t a = chunk_start + 2 + offset;
  226. err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
  227. a & (GRUB_DISK_SECTOR_SIZE - 1),
  228. csize, buf);
  229. if (err)
  230. return err;
  231. }
  232. else
  233. {
  234. char *tmp;
  235. grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS;
  236. grub_disk_addr_t a = chunk_start + 2;
  237. tmp = grub_malloc (bsize);
  238. if (!tmp)
  239. return grub_errno;
  240. /* FIXME: buffer uncompressed data. */
  241. err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
  242. a & (GRUB_DISK_SECTOR_SIZE - 1),
  243. bsize, tmp);
  244. if (err)
  245. {
  246. grub_free (tmp);
  247. return err;
  248. }
  249. if (data->decompress (tmp, bsize, offset,
  250. buf, csize, data) < 0)
  251. {
  252. grub_free (tmp);
  253. return grub_errno;
  254. }
  255. grub_free (tmp);
  256. }
  257. len -= csize;
  258. offset += csize;
  259. buf = (char *) buf + csize;
  260. }
  261. return GRUB_ERR_NONE;
  262. }
  263. static grub_ssize_t
  264. zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
  265. char *outbuf, grub_size_t outsize,
  266. struct grub_squash_data *data __attribute__ ((unused)))
  267. {
  268. return grub_zlib_decompress (inbuf, insize, off, outbuf, outsize);
  269. }
  270. static grub_ssize_t
  271. lzo_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
  272. char *outbuf, grub_size_t len, struct grub_squash_data *data)
  273. {
  274. lzo_uint usize = data->blksz;
  275. grub_uint8_t *udata;
  276. if (usize < 8192)
  277. usize = 8192;
  278. udata = grub_malloc (usize);
  279. if (!udata)
  280. return -1;
  281. if (lzo1x_decompress_safe ((grub_uint8_t *) inbuf,
  282. insize, udata, &usize, NULL) != LZO_E_OK)
  283. {
  284. grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
  285. grub_free (udata);
  286. return -1;
  287. }
  288. grub_memcpy (outbuf, udata + off, len);
  289. grub_free (udata);
  290. return len;
  291. }
  292. static grub_ssize_t
  293. xz_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
  294. char *outbuf, grub_size_t len, struct grub_squash_data *data)
  295. {
  296. grub_size_t ret = 0;
  297. grub_off_t pos = 0;
  298. struct xz_buf buf;
  299. xz_dec_reset (data->xzdec);
  300. buf.in = (grub_uint8_t *) inbuf;
  301. buf.in_pos = 0;
  302. buf.in_size = insize;
  303. buf.out = (grub_uint8_t *) data->xzbuf;
  304. buf.out_pos = 0;
  305. buf.out_size = XZBUFSIZ;
  306. while (len)
  307. {
  308. enum xz_ret xzret;
  309. buf.out_pos = 0;
  310. xzret = xz_dec_run (data->xzdec, &buf);
  311. if (xzret != XZ_OK && xzret != XZ_STREAM_END)
  312. {
  313. grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "invalid xz chunk");
  314. return -1;
  315. }
  316. if (pos + buf.out_pos >= off)
  317. {
  318. grub_ssize_t outoff = pos - off;
  319. grub_size_t l;
  320. if (outoff >= 0)
  321. {
  322. l = buf.out_pos;
  323. if (l > len)
  324. l = len;
  325. grub_memcpy (outbuf + outoff, buf.out, l);
  326. }
  327. else
  328. {
  329. outoff = -outoff;
  330. l = buf.out_pos - outoff;
  331. if (l > len)
  332. l = len;
  333. grub_memcpy (outbuf, buf.out + outoff, l);
  334. }
  335. ret += l;
  336. len -= l;
  337. }
  338. pos += buf.out_pos;
  339. if (xzret == XZ_STREAM_END)
  340. break;
  341. }
  342. return ret;
  343. }
  344. static struct grub_squash_data *
  345. squash_mount (grub_disk_t disk)
  346. {
  347. struct grub_squash_super sb;
  348. grub_err_t err;
  349. struct grub_squash_data *data;
  350. grub_uint64_t frag;
  351. err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb);
  352. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  353. grub_error (GRUB_ERR_BAD_FS, "not a squash4");
  354. if (err)
  355. return NULL;
  356. if (sb.magic != grub_cpu_to_le32_compile_time (SQUASH_MAGIC)
  357. || sb.block_size == 0
  358. || ((sb.block_size - 1) & sb.block_size))
  359. {
  360. grub_error (GRUB_ERR_BAD_FS, "not squash4");
  361. return NULL;
  362. }
  363. err = grub_disk_read (disk,
  364. grub_le_to_cpu64 (sb.unk1offset)
  365. >> GRUB_DISK_SECTOR_BITS,
  366. grub_le_to_cpu64 (sb.unk1offset)
  367. & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag);
  368. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  369. grub_error (GRUB_ERR_BAD_FS, "not a squash4");
  370. if (err)
  371. return NULL;
  372. data = grub_zalloc (sizeof (*data));
  373. if (!data)
  374. return NULL;
  375. data->sb = sb;
  376. data->disk = disk;
  377. data->fragments = grub_le_to_cpu64 (frag);
  378. switch (sb.compression)
  379. {
  380. case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB):
  381. data->decompress = zlib_decompress;
  382. break;
  383. case grub_cpu_to_le16_compile_time (COMPRESSION_LZO):
  384. data->decompress = lzo_decompress;
  385. break;
  386. case grub_cpu_to_le16_compile_time (COMPRESSION_XZ):
  387. data->decompress = xz_decompress;
  388. data->xzbuf = grub_malloc (XZBUFSIZ);
  389. if (!data->xzbuf)
  390. {
  391. grub_free (data);
  392. return NULL;
  393. }
  394. data->xzdec = xz_dec_init (1 << 16);
  395. if (!data->xzdec)
  396. {
  397. grub_free (data->xzbuf);
  398. grub_free (data);
  399. return NULL;
  400. }
  401. break;
  402. default:
  403. grub_free (data);
  404. grub_error (GRUB_ERR_BAD_FS, "unsupported compression %d",
  405. grub_le_to_cpu16 (sb.compression));
  406. return NULL;
  407. }
  408. data->blksz = grub_le_to_cpu32 (data->sb.block_size);
  409. for (data->log2_blksz = 0;
  410. (1U << data->log2_blksz) < data->blksz;
  411. data->log2_blksz++);
  412. return data;
  413. }
  414. static char *
  415. grub_squash_read_symlink (grub_fshelp_node_t node)
  416. {
  417. char *ret;
  418. grub_err_t err;
  419. ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
  420. err = read_chunk (node->data, ret,
  421. grub_le_to_cpu32 (node->ino.symlink.namelen),
  422. grub_le_to_cpu64 (node->data->sb.inodeoffset)
  423. + node->stack[node->stsize - 1].ino_chunk,
  424. node->stack[node->stsize - 1].ino_offset
  425. + (node->ino.symlink.name - (char *) &node->ino));
  426. if (err)
  427. {
  428. grub_free (ret);
  429. return NULL;
  430. }
  431. ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0;
  432. return ret;
  433. }
  434. static int
  435. grub_squash_iterate_dir (grub_fshelp_node_t dir,
  436. grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
  437. {
  438. grub_uint32_t off;
  439. grub_uint32_t endoff;
  440. grub_uint64_t chunk;
  441. unsigned i;
  442. /* FIXME: why - 3 ? */
  443. switch (dir->ino.type)
  444. {
  445. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR):
  446. off = grub_le_to_cpu16 (dir->ino.dir.offset);
  447. endoff = grub_le_to_cpu16 (dir->ino.dir.size) + off - 3;
  448. chunk = grub_le_to_cpu32 (dir->ino.dir.chunk);
  449. break;
  450. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR):
  451. off = grub_le_to_cpu16 (dir->ino.long_dir.offset);
  452. endoff = grub_le_to_cpu16 (dir->ino.long_dir.size) + off - 3;
  453. chunk = grub_le_to_cpu32 (dir->ino.long_dir.chunk);
  454. break;
  455. default:
  456. grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x",
  457. grub_le_to_cpu16 (dir->ino.type));
  458. return 0;
  459. }
  460. {
  461. grub_fshelp_node_t node;
  462. node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
  463. if (!node)
  464. return 0;
  465. grub_memcpy (node, dir,
  466. sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
  467. if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
  468. return 1;
  469. if (dir->stsize != 1)
  470. {
  471. grub_err_t err;
  472. node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
  473. if (!node)
  474. return 0;
  475. grub_memcpy (node, dir,
  476. sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
  477. node->stsize--;
  478. err = read_chunk (dir->data, &node->ino, sizeof (node->ino),
  479. grub_le_to_cpu64 (dir->data->sb.inodeoffset)
  480. + node->stack[node->stsize - 1].ino_chunk,
  481. node->stack[node->stsize - 1].ino_offset);
  482. if (err)
  483. return 0;
  484. if (hook ("..", GRUB_FSHELP_DIR, node, hook_data))
  485. return 1;
  486. }
  487. }
  488. while (off < endoff)
  489. {
  490. struct grub_squash_dirent_header dh;
  491. grub_err_t err;
  492. err = read_chunk (dir->data, &dh, sizeof (dh),
  493. grub_le_to_cpu64 (dir->data->sb.diroffset)
  494. + chunk, off);
  495. if (err)
  496. return 0;
  497. off += sizeof (dh);
  498. for (i = 0; i < (unsigned) grub_le_to_cpu32 (dh.nelems) + 1; i++)
  499. {
  500. char *buf;
  501. int r;
  502. struct grub_fshelp_node *node;
  503. enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
  504. struct grub_squash_dirent di;
  505. struct grub_squash_inode ino;
  506. err = read_chunk (dir->data, &di, sizeof (di),
  507. grub_le_to_cpu64 (dir->data->sb.diroffset)
  508. + chunk, off);
  509. if (err)
  510. return 0;
  511. off += sizeof (di);
  512. err = read_chunk (dir->data, &ino, sizeof (ino),
  513. grub_le_to_cpu64 (dir->data->sb.inodeoffset)
  514. + grub_le_to_cpu32 (dh.ino_chunk),
  515. grub_cpu_to_le16 (di.ino_offset));
  516. if (err)
  517. return 0;
  518. buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
  519. if (!buf)
  520. return 0;
  521. err = read_chunk (dir->data, buf,
  522. grub_le_to_cpu16 (di.namelen) + 1,
  523. grub_le_to_cpu64 (dir->data->sb.diroffset)
  524. + chunk, off);
  525. if (err)
  526. return 0;
  527. off += grub_le_to_cpu16 (di.namelen) + 1;
  528. buf[grub_le_to_cpu16 (di.namelen) + 1] = 0;
  529. if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR)
  530. filetype = GRUB_FSHELP_DIR;
  531. if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
  532. filetype = GRUB_FSHELP_SYMLINK;
  533. node = grub_malloc (sizeof (*node)
  534. + (dir->stsize + 1) * sizeof (dir->stack[0]));
  535. if (! node)
  536. return 0;
  537. grub_memcpy (node, dir,
  538. sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
  539. node->ino = ino;
  540. node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
  541. node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset);
  542. node->stsize++;
  543. r = hook (buf, filetype, node, hook_data);
  544. grub_free (buf);
  545. if (r)
  546. return r;
  547. }
  548. }
  549. return 0;
  550. }
  551. static grub_err_t
  552. make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root)
  553. {
  554. grub_memset (root, 0, sizeof (*root));
  555. root->data = data;
  556. root->stsize = 1;
  557. root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk);
  558. root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset);
  559. return read_chunk (data, &root->ino, sizeof (root->ino),
  560. grub_le_to_cpu64 (data->sb.inodeoffset)
  561. + root->stack[0].ino_chunk,
  562. root->stack[0].ino_offset);
  563. }
  564. static void
  565. squash_unmount (struct grub_squash_data *data)
  566. {
  567. if (data->xzdec)
  568. xz_dec_end (data->xzdec);
  569. grub_free (data->xzbuf);
  570. grub_free (data->ino.cumulated_block_sizes);
  571. grub_free (data->ino.block_sizes);
  572. grub_free (data);
  573. }
  574. /* Context for grub_squash_dir. */
  575. struct grub_squash_dir_ctx
  576. {
  577. grub_fs_dir_hook_t hook;
  578. void *hook_data;
  579. };
  580. /* Helper for grub_squash_dir. */
  581. static int
  582. grub_squash_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
  583. grub_fshelp_node_t node, void *data)
  584. {
  585. struct grub_squash_dir_ctx *ctx = data;
  586. struct grub_dirhook_info info;
  587. grub_memset (&info, 0, sizeof (info));
  588. info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
  589. info.mtimeset = 1;
  590. info.mtime = grub_le_to_cpu32 (node->ino.mtime);
  591. grub_free (node);
  592. return ctx->hook (filename, &info, ctx->hook_data);
  593. }
  594. static grub_err_t
  595. grub_squash_dir (grub_device_t device, const char *path,
  596. grub_fs_dir_hook_t hook, void *hook_data)
  597. {
  598. struct grub_squash_dir_ctx ctx = { hook, hook_data };
  599. struct grub_squash_data *data = 0;
  600. struct grub_fshelp_node *fdiro = 0;
  601. struct grub_fshelp_node root;
  602. grub_err_t err;
  603. data = squash_mount (device->disk);
  604. if (! data)
  605. return grub_errno;
  606. err = make_root_node (data, &root);
  607. if (err)
  608. return err;
  609. grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
  610. grub_squash_read_symlink, GRUB_FSHELP_DIR);
  611. if (!grub_errno)
  612. grub_squash_iterate_dir (fdiro, grub_squash_dir_iter, &ctx);
  613. squash_unmount (data);
  614. return grub_errno;
  615. }
  616. static grub_err_t
  617. grub_squash_open (struct grub_file *file, const char *name)
  618. {
  619. struct grub_squash_data *data = 0;
  620. struct grub_fshelp_node *fdiro = 0;
  621. struct grub_fshelp_node root;
  622. grub_err_t err;
  623. data = squash_mount (file->device->disk);
  624. if (! data)
  625. return grub_errno;
  626. err = make_root_node (data, &root);
  627. if (err)
  628. return err;
  629. grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir,
  630. grub_squash_read_symlink, GRUB_FSHELP_REG);
  631. if (grub_errno)
  632. {
  633. squash_unmount (data);
  634. return grub_errno;
  635. }
  636. file->data = data;
  637. data->ino.ino = fdiro->ino;
  638. data->ino.block_sizes = NULL;
  639. data->ino.cumulated_block_sizes = NULL;
  640. data->ino.ino_chunk = fdiro->stack[fdiro->stsize - 1].ino_chunk;
  641. data->ino.ino_offset = fdiro->stack[fdiro->stsize - 1].ino_offset;
  642. switch (fdiro->ino.type)
  643. {
  644. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
  645. file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
  646. break;
  647. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
  648. file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
  649. break;
  650. default:
  651. {
  652. grub_uint16_t type = grub_le_to_cpu16 (fdiro->ino.type);
  653. grub_free (fdiro);
  654. squash_unmount (data);
  655. return grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", type);
  656. }
  657. }
  658. grub_free (fdiro);
  659. return GRUB_ERR_NONE;
  660. }
  661. static grub_ssize_t
  662. direct_read (struct grub_squash_data *data,
  663. struct grub_squash_cache_inode *ino,
  664. grub_off_t off, char *buf, grub_size_t len)
  665. {
  666. grub_err_t err;
  667. grub_off_t cumulated_uncompressed_size = 0;
  668. grub_uint64_t a = 0;
  669. grub_size_t i;
  670. grub_size_t origlen = len;
  671. switch (ino->ino.type)
  672. {
  673. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
  674. a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
  675. break;
  676. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
  677. a = grub_le_to_cpu32 (ino->ino.file.chunk);
  678. break;
  679. }
  680. if (!ino->block_sizes)
  681. {
  682. grub_off_t total_size = 0;
  683. grub_size_t total_blocks;
  684. grub_size_t block_offset = 0;
  685. switch (ino->ino.type)
  686. {
  687. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
  688. total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
  689. block_offset = ((char *) &ino->ino.long_file.block_size
  690. - (char *) &ino->ino);
  691. break;
  692. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
  693. total_size = grub_le_to_cpu32 (ino->ino.file.size);
  694. block_offset = ((char *) &ino->ino.file.block_size
  695. - (char *) &ino->ino);
  696. break;
  697. }
  698. total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz);
  699. ino->block_sizes = grub_malloc (total_blocks
  700. * sizeof (ino->block_sizes[0]));
  701. ino->cumulated_block_sizes = grub_malloc (total_blocks
  702. * sizeof (ino->cumulated_block_sizes[0]));
  703. if (!ino->block_sizes || !ino->cumulated_block_sizes)
  704. {
  705. grub_free (ino->block_sizes);
  706. grub_free (ino->cumulated_block_sizes);
  707. ino->block_sizes = 0;
  708. ino->cumulated_block_sizes = 0;
  709. return -1;
  710. }
  711. err = read_chunk (data, ino->block_sizes,
  712. total_blocks * sizeof (ino->block_sizes[0]),
  713. grub_le_to_cpu64 (data->sb.inodeoffset)
  714. + ino->ino_chunk,
  715. ino->ino_offset + block_offset);
  716. if (err)
  717. {
  718. grub_free (ino->block_sizes);
  719. grub_free (ino->cumulated_block_sizes);
  720. ino->block_sizes = 0;
  721. ino->cumulated_block_sizes = 0;
  722. return -1;
  723. }
  724. ino->cumulated_block_sizes[0] = 0;
  725. for (i = 1; i < total_blocks; i++)
  726. ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
  727. + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
  728. }
  729. if (a == 0)
  730. a = sizeof (struct grub_squash_super);
  731. i = off >> data->log2_blksz;
  732. cumulated_uncompressed_size = data->blksz * (grub_disk_addr_t) i;
  733. while (cumulated_uncompressed_size < off + len)
  734. {
  735. grub_size_t boff, curread;
  736. boff = off - cumulated_uncompressed_size;
  737. curread = data->blksz - boff;
  738. if (curread > len)
  739. curread = len;
  740. if (!ino->block_sizes[i])
  741. {
  742. /* Sparse block */
  743. grub_memset (buf, '\0', curread);
  744. }
  745. else if (!(ino->block_sizes[i]
  746. & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)))
  747. {
  748. char *block;
  749. grub_size_t csize;
  750. csize = grub_le_to_cpu32 (ino->block_sizes[i]) & ~SQUASH_BLOCK_FLAGS;
  751. block = grub_malloc (csize);
  752. if (!block)
  753. return -1;
  754. err = grub_disk_read (data->disk,
  755. (ino->cumulated_block_sizes[i] + a)
  756. >> GRUB_DISK_SECTOR_BITS,
  757. (ino->cumulated_block_sizes[i] + a)
  758. & (GRUB_DISK_SECTOR_SIZE - 1),
  759. csize, block);
  760. if (err)
  761. {
  762. grub_free (block);
  763. return -1;
  764. }
  765. if (data->decompress (block, csize, boff, buf, curread, data)
  766. != (grub_ssize_t) curread)
  767. {
  768. grub_free (block);
  769. if (!grub_errno)
  770. grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
  771. return -1;
  772. }
  773. grub_free (block);
  774. }
  775. else
  776. err = grub_disk_read (data->disk,
  777. (ino->cumulated_block_sizes[i] + a + boff)
  778. >> GRUB_DISK_SECTOR_BITS,
  779. (ino->cumulated_block_sizes[i] + a + boff)
  780. & (GRUB_DISK_SECTOR_SIZE - 1),
  781. curread, buf);
  782. if (err)
  783. return -1;
  784. off += curread;
  785. len -= curread;
  786. buf += curread;
  787. cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
  788. i++;
  789. }
  790. return origlen;
  791. }
  792. static grub_ssize_t
  793. grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
  794. {
  795. struct grub_squash_data *data = file->data;
  796. struct grub_squash_cache_inode *ino = &data->ino;
  797. grub_off_t off = file->offset;
  798. grub_err_t err;
  799. grub_uint64_t a, b;
  800. grub_uint32_t fragment = 0;
  801. int compressed = 0;
  802. struct grub_squash_frag_desc frag;
  803. grub_off_t direct_len;
  804. grub_uint64_t mask = grub_le_to_cpu32 (data->sb.block_size) - 1;
  805. grub_size_t orig_len = len;
  806. switch (ino->ino.type)
  807. {
  808. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
  809. fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
  810. break;
  811. case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
  812. fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
  813. break;
  814. }
  815. /* Squash may pack file tail as fragment. So read initial part directly and
  816. get tail from fragments */
  817. direct_len = fragment == 0xffffffff ? file->size : file->size & ~mask;
  818. if (off < direct_len)
  819. {
  820. grub_size_t read_len = direct_len - off;
  821. grub_ssize_t res;
  822. if (read_len > len)
  823. read_len = len;
  824. res = direct_read (data, ino, off, buf, read_len);
  825. if ((grub_size_t) res != read_len)
  826. return -1; /* FIXME: is short read possible here? */
  827. len -= read_len;
  828. if (!len)
  829. return read_len;
  830. buf += read_len;
  831. off = 0;
  832. }
  833. else
  834. off -= direct_len;
  835. err = read_chunk (data, &frag, sizeof (frag),
  836. data->fragments, sizeof (frag) * fragment);
  837. if (err)
  838. return -1;
  839. a = grub_le_to_cpu64 (frag.offset);
  840. compressed = !(frag.size & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED));
  841. if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
  842. b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off;
  843. else
  844. b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
  845. /* FIXME: cache uncompressed chunks. */
  846. if (compressed)
  847. {
  848. char *block;
  849. block = grub_malloc (grub_le_to_cpu32 (frag.size));
  850. if (!block)
  851. return -1;
  852. err = grub_disk_read (data->disk,
  853. a >> GRUB_DISK_SECTOR_BITS,
  854. a & (GRUB_DISK_SECTOR_SIZE - 1),
  855. grub_le_to_cpu32 (frag.size), block);
  856. if (err)
  857. {
  858. grub_free (block);
  859. return -1;
  860. }
  861. if (data->decompress (block, grub_le_to_cpu32 (frag.size),
  862. b, buf, len, data)
  863. != (grub_ssize_t) len)
  864. {
  865. grub_free (block);
  866. if (!grub_errno)
  867. grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
  868. return -1;
  869. }
  870. grub_free (block);
  871. }
  872. else
  873. {
  874. err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
  875. (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
  876. if (err)
  877. return -1;
  878. }
  879. return orig_len;
  880. }
  881. static grub_err_t
  882. grub_squash_close (grub_file_t file)
  883. {
  884. squash_unmount (file->data);
  885. return GRUB_ERR_NONE;
  886. }
  887. static grub_err_t
  888. grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
  889. {
  890. struct grub_squash_data *data = 0;
  891. data = squash_mount (dev->disk);
  892. if (! data)
  893. return grub_errno;
  894. *tm = grub_le_to_cpu32 (data->sb.creation_time);
  895. squash_unmount (data);
  896. return GRUB_ERR_NONE;
  897. }
  898. static struct grub_fs grub_squash_fs =
  899. {
  900. .name = "squash4",
  901. .fs_dir = grub_squash_dir,
  902. .fs_open = grub_squash_open,
  903. .fs_read = grub_squash_read,
  904. .fs_close = grub_squash_close,
  905. .fs_mtime = grub_squash_mtime,
  906. #ifdef GRUB_UTIL
  907. .reserved_first_sector = 0,
  908. .blocklist_install = 0,
  909. #endif
  910. .next = 0
  911. };
  912. GRUB_MOD_INIT(squash4)
  913. {
  914. grub_fs_register (&grub_squash_fs);
  915. }
  916. GRUB_MOD_FINI(squash4)
  917. {
  918. grub_fs_unregister (&grub_squash_fs);
  919. }