hfs.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. /* hfs.c - HFS. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2004,2005,2006,2007,2008,2009 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. /* HFS is documented at
  20. http://developer.apple.com/documentation/mac/Files/Files-2.html */
  21. #include <grub/err.h>
  22. #include <grub/file.h>
  23. #include <grub/mm.h>
  24. #include <grub/misc.h>
  25. #include <grub/disk.h>
  26. #include <grub/dl.h>
  27. #include <grub/types.h>
  28. #include <grub/hfs.h>
  29. #define GRUB_HFS_SBLOCK 2
  30. #define GRUB_HFS_EMBED_HFSPLUS_SIG 0x482B
  31. #define GRUB_HFS_BLKS (data->blksz >> 9)
  32. #define GRUB_HFS_NODE_LEAF 0xFF
  33. /* The two supported filesystems a record can have. */
  34. enum
  35. {
  36. GRUB_HFS_FILETYPE_DIR = 1,
  37. GRUB_HFS_FILETYPE_FILE = 2
  38. };
  39. /* Catalog node ID (CNID). */
  40. enum grub_hfs_cnid_type
  41. {
  42. GRUB_HFS_CNID_ROOT_PARENT = 1,
  43. GRUB_HFS_CNID_ROOT = 2,
  44. GRUB_HFS_CNID_EXT = 3,
  45. GRUB_HFS_CNID_CAT = 4,
  46. GRUB_HFS_CNID_BAD = 5
  47. };
  48. /* A node descriptor. This is the header of every node. */
  49. struct grub_hfs_node
  50. {
  51. grub_uint32_t next;
  52. grub_uint32_t prev;
  53. grub_uint8_t type;
  54. grub_uint8_t level;
  55. grub_uint16_t reccnt;
  56. grub_uint16_t unused;
  57. } __attribute__ ((packed));
  58. /* The head of the B*-Tree. */
  59. struct grub_hfs_treeheader
  60. {
  61. grub_uint16_t tree_depth;
  62. /* The number of the first node. */
  63. grub_uint32_t root_node;
  64. grub_uint32_t leaves;
  65. grub_uint32_t first_leaf;
  66. grub_uint32_t last_leaf;
  67. grub_uint16_t node_size;
  68. grub_uint16_t key_size;
  69. grub_uint32_t nodes;
  70. grub_uint32_t free_nodes;
  71. grub_uint8_t unused[76];
  72. } __attribute__ ((packed));
  73. /* The state of a mounted HFS filesystem. */
  74. struct grub_hfs_data
  75. {
  76. struct grub_hfs_sblock sblock;
  77. grub_disk_t disk;
  78. grub_hfs_datarecord_t extents;
  79. int fileid;
  80. int size;
  81. int ext_root;
  82. int ext_size;
  83. int cat_root;
  84. int cat_size;
  85. int blksz;
  86. int log2_blksz;
  87. int rootdir;
  88. };
  89. /* The key as used on disk in a catalog tree. This is used to lookup
  90. file/directory nodes by parent directory ID and filename. */
  91. struct grub_hfs_catalog_key
  92. {
  93. grub_uint8_t unused;
  94. grub_uint32_t parent_dir;
  95. /* Filename length. */
  96. grub_uint8_t strlen;
  97. /* Filename. */
  98. grub_uint8_t str[31];
  99. } __attribute__ ((packed));
  100. /* The key as used on disk in a extent overflow tree. Using this key
  101. the extents can be looked up using a fileid and logical start block
  102. as index. */
  103. struct grub_hfs_extent_key
  104. {
  105. /* The kind of fork. This is used to store meta information like
  106. icons, attributes, etc. We will only use the datafork, which is
  107. 0. */
  108. grub_uint8_t forktype;
  109. grub_uint32_t fileid;
  110. grub_uint16_t first_block;
  111. } __attribute__ ((packed));
  112. /* A directory record. This is used to find out the directory ID. */
  113. struct grub_hfs_dirrec
  114. {
  115. /* For a directory, type == 1. */
  116. grub_uint8_t type;
  117. grub_uint8_t unused[5];
  118. grub_uint32_t dirid;
  119. } __attribute__ ((packed));
  120. /* Information about a file. */
  121. struct grub_hfs_filerec
  122. {
  123. /* For a file, type == 2. */
  124. grub_uint8_t type;
  125. grub_uint8_t unused[19];
  126. grub_uint32_t fileid;
  127. grub_uint8_t unused2[2];
  128. grub_uint32_t size;
  129. grub_uint8_t unused3[44];
  130. /* The first 3 extents of the file. The other extents can be found
  131. in the extent overflow file. */
  132. grub_hfs_datarecord_t extents;
  133. } __attribute__ ((packed));
  134. /* A record descriptor, both key and data, used to pass to call back
  135. functions. */
  136. struct grub_hfs_record
  137. {
  138. void *key;
  139. int keylen;
  140. void *data;
  141. int datalen;
  142. };
  143. static grub_dl_t my_mod;
  144. static int grub_hfs_find_node (struct grub_hfs_data *, char *,
  145. grub_uint32_t, int, char *, int);
  146. /* Find block BLOCK of the file FILE in the mounted UFS filesystem
  147. DATA. The first 3 extents are described by DAT. If cache is set,
  148. using caching to improve non-random reads. */
  149. static unsigned int
  150. grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat,
  151. int file, int block, int cache)
  152. {
  153. grub_hfs_datarecord_t dr;
  154. int pos = 0;
  155. struct grub_hfs_extent_key key;
  156. int tree = 0;
  157. static int cache_file = 0;
  158. static int cache_pos = 0;
  159. static grub_hfs_datarecord_t cache_dr;
  160. grub_memcpy (dr, dat, sizeof (dr));
  161. key.forktype = 0;
  162. key.fileid = grub_cpu_to_be32 (file);
  163. if (cache && cache_file == file && block > cache_pos)
  164. {
  165. pos = cache_pos;
  166. key.first_block = grub_cpu_to_be16 (pos);
  167. grub_memcpy (dr, cache_dr, sizeof (cache_dr));
  168. }
  169. for (;;)
  170. {
  171. int i;
  172. /* Try all 3 extents. */
  173. for (i = 0; i < 3; i++)
  174. {
  175. /* Check if the block is stored in this extent. */
  176. if (grub_be_to_cpu16 (dr[i].count) + pos > block)
  177. {
  178. int first = grub_be_to_cpu16 (dr[i].first_block);
  179. /* If the cache is enabled, store the current position
  180. in the tree. */
  181. if (tree && cache)
  182. {
  183. cache_file = file;
  184. cache_pos = pos;
  185. grub_memcpy (cache_dr, dr, sizeof (cache_dr));
  186. }
  187. return (grub_be_to_cpu16 (data->sblock.first_block)
  188. + (first + block - pos) * GRUB_HFS_BLKS);
  189. }
  190. /* Try the next extent. */
  191. pos += grub_be_to_cpu16 (dr[i].count);
  192. }
  193. /* Lookup the block in the extent overflow file. */
  194. key.first_block = grub_cpu_to_be16 (pos);
  195. tree = 1;
  196. grub_hfs_find_node (data, (char *) &key, data->ext_root,
  197. 1, (char *) &dr, sizeof (dr));
  198. if (grub_errno)
  199. return 0;
  200. }
  201. }
  202. /* Read LEN bytes from the file described by DATA starting with byte
  203. POS. Return the amount of read bytes in READ. */
  204. static grub_ssize_t
  205. grub_hfs_read_file (struct grub_hfs_data *data,
  206. void (*read_hook) (grub_disk_addr_t sector,
  207. unsigned offset, unsigned length,
  208. void *closure),
  209. void *closure,
  210. int pos, grub_size_t len, char *buf)
  211. {
  212. int i;
  213. int blockcnt;
  214. blockcnt = ((len + pos)
  215. + data->blksz - 1) / data->blksz;
  216. for (i = pos / data->blksz; i < blockcnt; i++)
  217. {
  218. int blknr;
  219. int blockoff = pos % data->blksz;
  220. int blockend = data->blksz;
  221. int skipfirst = 0;
  222. blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
  223. if (grub_errno)
  224. return -1;
  225. /* Last block. */
  226. if (i == blockcnt - 1)
  227. {
  228. blockend = (len + pos) % data->blksz;
  229. /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
  230. if (! blockend)
  231. blockend = data->blksz;
  232. }
  233. /* First block. */
  234. if (i == pos / data->blksz)
  235. {
  236. skipfirst = blockoff;
  237. blockend -= skipfirst;
  238. }
  239. /* If the block number is 0 this block is not stored on disk but
  240. is zero filled instead. */
  241. if (blknr)
  242. {
  243. data->disk->read_hook = read_hook;
  244. data->disk->closure = closure;
  245. grub_disk_read (data->disk, blknr, skipfirst,
  246. blockend, buf);
  247. data->disk->read_hook = 0;
  248. if (grub_errno)
  249. return -1;
  250. }
  251. buf += data->blksz - skipfirst;
  252. }
  253. return len;
  254. }
  255. /* Mount the filesystem on the disk DISK. */
  256. static struct grub_hfs_data *
  257. grub_hfs_mount (grub_disk_t disk)
  258. {
  259. struct grub_hfs_data *data;
  260. struct grub_hfs_catalog_key key;
  261. struct grub_hfs_dirrec dir;
  262. int first_block;
  263. struct
  264. {
  265. struct grub_hfs_node node;
  266. struct grub_hfs_treeheader head;
  267. } treehead;
  268. data = grub_malloc (sizeof (struct grub_hfs_data));
  269. if (!data)
  270. return 0;
  271. /* Read the superblock. */
  272. if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
  273. sizeof (struct grub_hfs_sblock), &data->sblock))
  274. goto fail;
  275. /* Check if this is a HFS filesystem. */
  276. if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC)
  277. {
  278. grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
  279. goto fail;
  280. }
  281. /* Check if this is an embedded HFS+ filesystem. */
  282. if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
  283. {
  284. grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
  285. goto fail;
  286. }
  287. data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
  288. data->disk = disk;
  289. /* Lookup the root node of the extent overflow tree. */
  290. first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
  291. * GRUB_HFS_BLKS)
  292. + grub_be_to_cpu16 (data->sblock.first_block));
  293. if (grub_disk_read (data->disk, first_block, 0,
  294. sizeof (treehead), &treehead))
  295. goto fail;
  296. data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
  297. data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
  298. /* Lookup the root node of the catalog tree. */
  299. first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
  300. * GRUB_HFS_BLKS)
  301. + grub_be_to_cpu16 (data->sblock.first_block));
  302. if (grub_disk_read (data->disk, first_block, 0,
  303. sizeof (treehead), &treehead))
  304. goto fail;
  305. data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
  306. data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
  307. /* Lookup the root directory node in the catalog tree using the
  308. volume name. */
  309. key.parent_dir = grub_cpu_to_be32 (1);
  310. key.strlen = data->sblock.volname[0];
  311. grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
  312. if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
  313. 0, (char *) &dir, sizeof (dir)) == 0)
  314. {
  315. grub_error (GRUB_ERR_BAD_FS, "cannot find the HFS root directory");
  316. goto fail;
  317. }
  318. if (grub_errno)
  319. goto fail;
  320. data->rootdir = grub_be_to_cpu32 (dir.dirid);
  321. return data;
  322. fail:
  323. grub_free (data);
  324. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  325. grub_error (GRUB_ERR_BAD_FS, "not a HFS filesystem");
  326. return 0;
  327. }
  328. /* Compare the K1 and K2 catalog file keys using HFS character ordering. */
  329. static int
  330. grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
  331. struct grub_hfs_catalog_key *k2)
  332. {
  333. /* Taken from hfsutils 3.2.6 and converted to a readable form */
  334. static const unsigned char hfs_charorder[256] = {
  335. [0x00] = 0,
  336. [0x01] = 1,
  337. [0x02] = 2,
  338. [0x03] = 3,
  339. [0x04] = 4,
  340. [0x05] = 5,
  341. [0x06] = 6,
  342. [0x07] = 7,
  343. [0x08] = 8,
  344. [0x09] = 9,
  345. [0x0A] = 10,
  346. [0x0B] = 11,
  347. [0x0C] = 12,
  348. [0x0D] = 13,
  349. [0x0E] = 14,
  350. [0x0F] = 15,
  351. [0x10] = 16,
  352. [0x11] = 17,
  353. [0x12] = 18,
  354. [0x13] = 19,
  355. [0x14] = 20,
  356. [0x15] = 21,
  357. [0x16] = 22,
  358. [0x17] = 23,
  359. [0x18] = 24,
  360. [0x19] = 25,
  361. [0x1A] = 26,
  362. [0x1B] = 27,
  363. [0x1C] = 28,
  364. [0x1D] = 29,
  365. [0x1E] = 30,
  366. [0x1F] = 31,
  367. [' '] = 32, [0xCA] = 32,
  368. ['!'] = 33,
  369. ['"'] = 34,
  370. [0xD2] = 35,
  371. [0xD3] = 36,
  372. [0xC7] = 37,
  373. [0xC8] = 38,
  374. ['#'] = 39,
  375. ['$'] = 40,
  376. ['%'] = 41,
  377. ['&'] = 42,
  378. ['\''] = 43,
  379. [0xD4] = 44,
  380. [0xD5] = 45,
  381. ['('] = 46,
  382. [')'] = 47,
  383. ['*'] = 48,
  384. ['+'] = 49,
  385. [','] = 50,
  386. ['-'] = 51,
  387. ['.'] = 52,
  388. ['/'] = 53,
  389. ['0'] = 54,
  390. ['1'] = 55,
  391. ['2'] = 56,
  392. ['3'] = 57,
  393. ['4'] = 58,
  394. ['5'] = 59,
  395. ['6'] = 60,
  396. ['7'] = 61,
  397. ['8'] = 62,
  398. ['9'] = 63,
  399. [':'] = 64,
  400. [';'] = 65,
  401. ['<'] = 66,
  402. ['='] = 67,
  403. ['>'] = 68,
  404. ['?'] = 69,
  405. ['@'] = 70,
  406. ['A'] = 71, ['a'] = 71,
  407. [0x88] = 72, [0xCB] = 72,
  408. [0x80] = 73, [0x8A] = 73,
  409. [0x8B] = 74, [0xCC] = 74,
  410. [0x81] = 75, [0x8C] = 75,
  411. [0xAE] = 76, [0xBE] = 76,
  412. ['`'] = 77,
  413. [0x87] = 78,
  414. [0x89] = 79,
  415. [0xBB] = 80,
  416. ['B'] = 81, ['b'] = 81,
  417. ['C'] = 82, ['c'] = 82,
  418. [0x82] = 83, [0x8D] = 83,
  419. ['D'] = 84, ['d'] = 84,
  420. ['E'] = 85, ['e'] = 85,
  421. [0x83] = 86, [0x8E] = 86,
  422. [0x8F] = 87,
  423. [0x90] = 88,
  424. [0x91] = 89,
  425. ['F'] = 90, ['f'] = 90,
  426. ['G'] = 91, ['g'] = 91,
  427. ['H'] = 92, ['h'] = 92,
  428. ['I'] = 93, ['i'] = 93,
  429. [0x92] = 94,
  430. [0x93] = 95,
  431. [0x94] = 96,
  432. [0x95] = 97,
  433. ['J'] = 98, ['j'] = 98,
  434. ['K'] = 99, ['k'] = 99,
  435. ['L'] = 100, ['l'] = 100,
  436. ['M'] = 101, ['m'] = 101,
  437. ['N'] = 102, ['n'] = 102,
  438. [0x84] = 103, [0x96] = 103,
  439. ['O'] = 104, ['o'] = 104,
  440. [0x85] = 105, [0x9A] = 105,
  441. [0x9B] = 106, [0xCD] = 106,
  442. [0xAF] = 107, [0xBF] = 107,
  443. [0xCE] = 108, [0xCF] = 108,
  444. [0x97] = 109,
  445. [0x98] = 110,
  446. [0x99] = 111,
  447. [0xBC] = 112,
  448. ['P'] = 113, ['p'] = 113,
  449. ['Q'] = 114, ['q'] = 114,
  450. ['R'] = 115, ['r'] = 115,
  451. ['S'] = 116, ['s'] = 116,
  452. [0xA7] = 117,
  453. ['T'] = 118, ['t'] = 118,
  454. ['U'] = 119, ['u'] = 119,
  455. [0x86] = 120, [0x9F] = 120,
  456. [0x9C] = 121,
  457. [0x9D] = 122,
  458. [0x9E] = 123,
  459. ['V'] = 124, ['v'] = 124,
  460. ['W'] = 125, ['w'] = 125,
  461. ['X'] = 126, ['x'] = 126,
  462. ['Y'] = 127, ['y'] = 127,
  463. [0xD8] = 128,
  464. ['Z'] = 129, ['z'] = 129,
  465. ['['] = 130,
  466. ['\\'] = 131,
  467. [']'] = 132,
  468. ['^'] = 133,
  469. ['_'] = 134,
  470. ['{'] = 135,
  471. ['|'] = 136,
  472. ['}'] = 137,
  473. ['~'] = 138,
  474. [0x7F] = 139,
  475. [0xA0] = 140,
  476. [0xA1] = 141,
  477. [0xA2] = 142,
  478. [0xA3] = 143,
  479. [0xA4] = 144,
  480. [0xA5] = 145,
  481. [0xA6] = 146,
  482. [0xA8] = 147,
  483. [0xA9] = 148,
  484. [0xAA] = 149,
  485. [0xAB] = 150,
  486. [0xAC] = 151,
  487. [0xAD] = 152,
  488. [0xB0] = 153,
  489. [0xB1] = 154,
  490. [0xB2] = 155,
  491. [0xB3] = 156,
  492. [0xB4] = 157,
  493. [0xB5] = 158,
  494. [0xB6] = 159,
  495. [0xB7] = 160,
  496. [0xB8] = 161,
  497. [0xB9] = 162,
  498. [0xBA] = 163,
  499. [0xBD] = 164,
  500. [0xC0] = 165,
  501. [0xC1] = 166,
  502. [0xC2] = 167,
  503. [0xC3] = 168,
  504. [0xC4] = 169,
  505. [0xC5] = 170,
  506. [0xC6] = 171,
  507. [0xC9] = 172,
  508. [0xD0] = 173,
  509. [0xD1] = 174,
  510. [0xD6] = 175,
  511. [0xD7] = 176,
  512. [0xD9] = 177,
  513. [0xDA] = 178,
  514. [0xDB] = 179,
  515. [0xDC] = 180,
  516. [0xDD] = 181,
  517. [0xDE] = 182,
  518. [0xDF] = 183,
  519. [0xE0] = 184,
  520. [0xE1] = 185,
  521. [0xE2] = 186,
  522. [0xE3] = 187,
  523. [0xE4] = 188,
  524. [0xE5] = 189,
  525. [0xE6] = 190,
  526. [0xE7] = 191,
  527. [0xE8] = 192,
  528. [0xE9] = 193,
  529. [0xEA] = 194,
  530. [0xEB] = 195,
  531. [0xEC] = 196,
  532. [0xED] = 197,
  533. [0xEE] = 198,
  534. [0xEF] = 199,
  535. [0xF0] = 200,
  536. [0xF1] = 201,
  537. [0xF2] = 202,
  538. [0xF3] = 203,
  539. [0xF4] = 204,
  540. [0xF5] = 205,
  541. [0xF6] = 206,
  542. [0xF7] = 207,
  543. [0xF8] = 208,
  544. [0xF9] = 209,
  545. [0xFA] = 210,
  546. [0xFB] = 211,
  547. [0xFC] = 212,
  548. [0xFD] = 213,
  549. [0xFE] = 214,
  550. [0xFF] = 215,
  551. };
  552. int i;
  553. int cmp;
  554. int minlen = (k1->strlen < k2->strlen) ? k1->strlen : k2->strlen;
  555. cmp = (grub_be_to_cpu32 (k1->parent_dir) - grub_be_to_cpu32 (k2->parent_dir));
  556. if (cmp != 0)
  557. return cmp;
  558. for (i = 0; i < minlen; i++)
  559. {
  560. cmp = (hfs_charorder[k1->str[i]] - hfs_charorder[k2->str[i]]);
  561. if (cmp != 0)
  562. return cmp;
  563. }
  564. /* Shorter strings precede long ones. */
  565. return (k1->strlen - k2->strlen);
  566. }
  567. /* Compare the K1 and K2 extent overflow file keys. */
  568. static int
  569. grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
  570. struct grub_hfs_extent_key *k2)
  571. {
  572. int cmp = k1->forktype - k2->forktype;
  573. if (cmp == 0)
  574. cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
  575. if (cmp == 0)
  576. cmp = (grub_be_to_cpu16 (k1->first_block)
  577. - grub_be_to_cpu16 (k2->first_block));
  578. return cmp;
  579. }
  580. /* Iterate the records in the node with index IDX in the mounted HFS
  581. filesystem DATA. This node holds data of the type TYPE (0 =
  582. catalog node, 1 = extent overflow node). If this is set, continue
  583. iterating to the next node. For every records, call NODE_HOOK. */
  584. static grub_err_t
  585. grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx, int this,
  586. int (*node_hook) (struct grub_hfs_node *hnd,
  587. struct grub_hfs_record *,
  588. void *closure),
  589. void *closure)
  590. {
  591. int nodesize = type == 0 ? data->cat_size : data->ext_size;
  592. union
  593. {
  594. struct grub_hfs_node node;
  595. char rawnode[nodesize];
  596. grub_uint16_t offsets[nodesize / 2];
  597. } node;
  598. do
  599. {
  600. int i;
  601. struct grub_hfs_extent *dat;
  602. int blk;
  603. dat = (struct grub_hfs_extent *) (type == 0
  604. ? (&data->sblock.catalog_recs)
  605. : (&data->sblock.extent_recs));
  606. /* Read the node into memory. */
  607. blk = grub_hfs_block (data, dat,
  608. (type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT,
  609. idx / (data->blksz / nodesize), 0);
  610. blk += (idx % (data->blksz / nodesize));
  611. if (grub_errno)
  612. return grub_errno;
  613. if (grub_disk_read (data->disk, blk, 0,
  614. sizeof (node), &node))
  615. return grub_errno;
  616. /* Iterate over all records in this node. */
  617. for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
  618. {
  619. int pos = (nodesize >> 1) - 1 - i;
  620. struct pointer
  621. {
  622. grub_uint8_t keylen;
  623. grub_uint8_t key;
  624. } __attribute__ ((packed)) *pnt;
  625. pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
  626. + node.rawnode);
  627. struct grub_hfs_record rec =
  628. {
  629. &pnt->key,
  630. pnt->keylen,
  631. &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
  632. nodesize - grub_be_to_cpu16 (node.offsets[pos])
  633. - pnt->keylen - 1
  634. };
  635. if (node_hook (&node.node, &rec, closure))
  636. return 0;
  637. }
  638. idx = grub_be_to_cpu32 (node.node.next);
  639. } while (idx && this);
  640. return 0;
  641. }
  642. struct grub_hfs_find_node_closure
  643. {
  644. char *key;
  645. int type;
  646. char *datar;
  647. int datalen;
  648. int found;
  649. int isleaf;
  650. int done;
  651. };
  652. static int
  653. grub_hfs_find_node_node_found (struct grub_hfs_node *hnd,
  654. struct grub_hfs_record *rec,
  655. void *closure)
  656. {
  657. struct grub_hfs_find_node_closure *c = closure;
  658. int cmp = 1;
  659. if (c->type == 0)
  660. cmp = grub_hfs_cmp_catkeys (rec->key, (void *) c->key);
  661. else
  662. cmp = grub_hfs_cmp_extkeys (rec->key, (void *) c->key);
  663. /* If the key is smaller or equal to the current node, mark the
  664. entry. In case of a non-leaf mode it will be used to lookup
  665. the rest of the tree. */
  666. if (cmp <= 0)
  667. {
  668. grub_uint32_t *node = (grub_uint32_t *) rec->data;
  669. c->found = grub_be_to_cpu32 (*node);
  670. }
  671. else /* The key can not be found in the tree. */
  672. return 1;
  673. /* Check if this node is a leaf node. */
  674. if (hnd->type == GRUB_HFS_NODE_LEAF)
  675. {
  676. c->isleaf = 1;
  677. /* Found it!!!! */
  678. if (cmp == 0)
  679. {
  680. c->done = 1;
  681. grub_memcpy (c->datar, rec->data,
  682. rec->datalen < c->datalen ? rec->datalen : c->datalen);
  683. return 1;
  684. }
  685. }
  686. return 0;
  687. }
  688. /* Lookup a record in the mounted filesystem DATA using the key KEY.
  689. The index of the node on top of the tree is IDX. The tree is of
  690. the type TYPE (0 = catalog node, 1 = extent overflow node). Return
  691. the data in DATAR with a maximum length of DATALEN. */
  692. static int
  693. grub_hfs_find_node (struct grub_hfs_data *data, char *key,
  694. grub_uint32_t idx, int type, char *datar, int datalen)
  695. {
  696. struct grub_hfs_find_node_closure c;
  697. c.key = key;
  698. c.type = type;
  699. c.datar = datar;
  700. c.datalen = datalen;
  701. c.isleaf = 0;
  702. c.done = 0;
  703. do
  704. {
  705. c.found = -1;
  706. if (grub_hfs_iterate_records (data, type, idx, 0,
  707. grub_hfs_find_node_node_found, &c))
  708. return 0;
  709. if (c.found == -1)
  710. return 0;
  711. idx = c.found;
  712. } while (! c.isleaf);
  713. return c.done;
  714. }
  715. struct grub_hfs_iterate_dir_closure
  716. {
  717. unsigned int dir;
  718. int (*hook) (struct grub_hfs_record *, void *closure);
  719. void *closure;
  720. struct grub_hfs_catalog_key *key;
  721. int found;
  722. int isleaf;
  723. int next;
  724. };
  725. static int
  726. grub_hfs_iterate_dir_node_found (struct grub_hfs_node *hnd,
  727. struct grub_hfs_record *rec,
  728. void *closure)
  729. {
  730. struct grub_hfs_iterate_dir_closure *c = closure;
  731. struct grub_hfs_catalog_key *ckey = rec->key;
  732. if (grub_hfs_cmp_catkeys (rec->key, (void *) c->key) <= 0)
  733. c->found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
  734. if (hnd->type == 0xFF && ckey->strlen > 0)
  735. {
  736. c->isleaf = 1;
  737. c->next = grub_be_to_cpu32 (hnd->next);
  738. /* An entry was found. */
  739. if (grub_be_to_cpu32 (ckey->parent_dir) == c->dir)
  740. return c->hook (rec, c->closure);
  741. }
  742. return 0;
  743. }
  744. static int
  745. grub_hfs_iterate_dir_it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
  746. struct grub_hfs_record *rec,
  747. void *closure)
  748. {
  749. struct grub_hfs_iterate_dir_closure *c = closure;
  750. struct grub_hfs_catalog_key *ckey = rec->key;
  751. struct grub_hfs_catalog_key *origkey = c->key;
  752. /* Stop when the entries do not match anymore. */
  753. if (grub_be_to_cpu32 (ckey->parent_dir)
  754. != grub_be_to_cpu32 ((origkey)->parent_dir))
  755. return 1;
  756. return c->hook (rec, c->closure);
  757. }
  758. /* Iterate over the directory with the id DIR. The tree is searched
  759. starting with the node ROOT_IDX. For every entry in this directory
  760. call HOOK. */
  761. static grub_err_t
  762. grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
  763. unsigned int dir,
  764. int (*hook) (struct grub_hfs_record *, void *closure),
  765. void *closure)
  766. {
  767. /* The lowest key possible with DIR as root directory. */
  768. struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
  769. struct grub_hfs_iterate_dir_closure c;
  770. c.key = &key;
  771. c.dir = dir;
  772. c.hook = hook;
  773. c.closure = closure;
  774. c.isleaf = 0;
  775. c.next = 0;
  776. do
  777. {
  778. c.found = -1;
  779. if (grub_hfs_iterate_records (data, 0, root_idx, 0,
  780. grub_hfs_iterate_dir_node_found, &c))
  781. return grub_errno;
  782. if (c.found == -1)
  783. return 0;
  784. root_idx = c.found;
  785. } while (! c.isleaf);
  786. /* If there was a matching record in this leaf node, continue the
  787. iteration until the last record was found. */
  788. grub_hfs_iterate_records (data, 0, c.next, 1, grub_hfs_iterate_dir_it_dir, &c);
  789. return grub_errno;
  790. }
  791. /* Find a file or directory with the pathname PATH in the filesystem
  792. DATA. Return the file record in RETDATA when it is non-zero.
  793. Return the directory number in RETINODE when it is non-zero. */
  794. static grub_err_t
  795. grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
  796. struct grub_hfs_filerec *retdata, int *retinode)
  797. {
  798. int inode = data->rootdir;
  799. char *next;
  800. char *origpath;
  801. union {
  802. struct grub_hfs_filerec frec;
  803. struct grub_hfs_dirrec dir;
  804. } fdrec;
  805. fdrec.frec.type = GRUB_HFS_FILETYPE_DIR;
  806. if (path[0] != '/')
  807. {
  808. grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
  809. return 0;
  810. }
  811. origpath = grub_strdup (path);
  812. if (!origpath)
  813. return grub_errno;
  814. path = origpath;
  815. while (*path == '/')
  816. path++;
  817. while (path && grub_strlen (path))
  818. {
  819. if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR)
  820. {
  821. grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
  822. goto fail;
  823. }
  824. /* Isolate a part of the path. */
  825. next = grub_strchr (path, '/');
  826. if (next)
  827. {
  828. while (*next == '/')
  829. *(next++) = '\0';
  830. }
  831. struct grub_hfs_catalog_key key;
  832. key.parent_dir = grub_cpu_to_be32 (inode);
  833. key.strlen = grub_strlen (path);
  834. grub_strcpy ((char *) (key.str), path);
  835. /* Lookup this node. */
  836. if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
  837. 0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
  838. {
  839. grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
  840. goto fail;
  841. }
  842. if (grub_errno)
  843. goto fail;
  844. inode = grub_be_to_cpu32 (fdrec.dir.dirid);
  845. path = next;
  846. }
  847. if (retdata)
  848. grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec));
  849. if (retinode)
  850. *retinode = inode;
  851. fail:
  852. grub_free (origpath);
  853. return grub_errno;
  854. }
  855. struct grub_hfs_dir_closure
  856. {
  857. int (*hook) (const char *filename,
  858. const struct grub_dirhook_info *info, void *closure);
  859. void *closure;
  860. };
  861. static int
  862. grub_hfs_dir_hook (struct grub_hfs_record *rec, void *closure)
  863. {
  864. struct grub_hfs_dir_closure *c = closure;
  865. char fname[32] = { 0 };
  866. char *filetype = rec->data;
  867. struct grub_hfs_catalog_key *ckey = rec->key;
  868. struct grub_dirhook_info info;
  869. grub_memset (&info, 0, sizeof (info));
  870. grub_strncpy (fname, (char *) (ckey->str), ckey->strlen);
  871. if (*filetype == GRUB_HFS_FILETYPE_DIR
  872. || *filetype == GRUB_HFS_FILETYPE_FILE)
  873. {
  874. info.dir = (*filetype == GRUB_HFS_FILETYPE_DIR);
  875. return c->hook (fname, &info, c->closure);
  876. }
  877. return 0;
  878. }
  879. static grub_err_t
  880. grub_hfs_dir (grub_device_t device, const char *path,
  881. int (*hook) (const char *filename,
  882. const struct grub_dirhook_info *info, void *closure),
  883. void *closure)
  884. {
  885. int inode;
  886. struct grub_hfs_data *data;
  887. struct grub_hfs_filerec frec;
  888. struct grub_hfs_dir_closure c;
  889. grub_dl_ref (my_mod);
  890. data = grub_hfs_mount (device->disk);
  891. if (!data)
  892. goto fail;
  893. /* First the directory ID for the directory. */
  894. if (grub_hfs_find_dir (data, path, &frec, &inode))
  895. goto fail;
  896. if (frec.type != GRUB_HFS_FILETYPE_DIR)
  897. {
  898. grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
  899. goto fail;
  900. }
  901. c.hook = hook;
  902. c.closure = closure;
  903. grub_hfs_iterate_dir (data, data->cat_root, inode, grub_hfs_dir_hook, &c);
  904. fail:
  905. grub_free (data);
  906. grub_dl_unref (my_mod);
  907. return grub_errno;
  908. }
  909. /* Open a file named NAME and initialize FILE. */
  910. static grub_err_t
  911. grub_hfs_open (struct grub_file *file, const char *name)
  912. {
  913. struct grub_hfs_data *data;
  914. struct grub_hfs_filerec frec;
  915. grub_dl_ref (my_mod);
  916. data = grub_hfs_mount (file->device->disk);
  917. if (grub_hfs_find_dir (data, name, &frec, 0))
  918. {
  919. grub_free (data);
  920. grub_dl_unref (my_mod);
  921. return grub_errno;
  922. }
  923. if (frec.type != GRUB_HFS_FILETYPE_FILE)
  924. {
  925. grub_free (data);
  926. grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
  927. grub_dl_unref (my_mod);
  928. return grub_errno;
  929. }
  930. grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
  931. file->size = grub_be_to_cpu32 (frec.size);
  932. data->size = grub_be_to_cpu32 (frec.size);
  933. data->fileid = grub_be_to_cpu32 (frec.fileid);
  934. file->offset = 0;
  935. file->data = data;
  936. return 0;
  937. }
  938. static grub_ssize_t
  939. grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
  940. {
  941. struct grub_hfs_data *data =
  942. (struct grub_hfs_data *) file->data;
  943. return grub_hfs_read_file (data, file->read_hook, file->closure,
  944. file->offset, len, buf);
  945. }
  946. static grub_err_t
  947. grub_hfs_close (grub_file_t file)
  948. {
  949. grub_free (file->data);
  950. grub_dl_unref (my_mod);
  951. return 0;
  952. }
  953. static grub_err_t
  954. grub_hfs_label (grub_device_t device, char **label)
  955. {
  956. struct grub_hfs_data *data;
  957. data = grub_hfs_mount (device->disk);
  958. if (data)
  959. *label = grub_strndup ((char *) (data->sblock.volname + 1),
  960. *data->sblock.volname);
  961. else
  962. *label = 0;
  963. grub_free (data);
  964. return grub_errno;
  965. }
  966. static grub_err_t
  967. grub_hfs_uuid (grub_device_t device, char **uuid)
  968. {
  969. struct grub_hfs_data *data;
  970. grub_dl_ref (my_mod);
  971. data = grub_hfs_mount (device->disk);
  972. if (data && data->sblock.num_serial != 0)
  973. {
  974. *uuid = grub_xasprintf ("%016llx",
  975. (unsigned long long)
  976. grub_be_to_cpu64 (data->sblock.num_serial));
  977. }
  978. else
  979. *uuid = NULL;
  980. grub_dl_unref (my_mod);
  981. grub_free (data);
  982. return grub_errno;
  983. }
  984. static struct grub_fs grub_hfs_fs =
  985. {
  986. .name = "hfs",
  987. .dir = grub_hfs_dir,
  988. .open = grub_hfs_open,
  989. .read = grub_hfs_read,
  990. .close = grub_hfs_close,
  991. .label = grub_hfs_label,
  992. .uuid = grub_hfs_uuid,
  993. .next = 0
  994. };
  995. GRUB_MOD_INIT(hfs)
  996. {
  997. grub_fs_register (&grub_hfs_fs);
  998. my_mod = mod;
  999. }
  1000. GRUB_MOD_FINI(hfs)
  1001. {
  1002. grub_fs_unregister (&grub_hfs_fs);
  1003. }