squash4.c 26 KB

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