Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / hfs.c
blob06cf0c7abbd1d4f4ab3489eb3586cf1990f143bb
1 /* hfs.c - HFS. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 /* HFS is documented at
21 http://developer.apple.com/documentation/mac/Files/Files-2.html */
23 #include <grub/err.h>
24 #include <grub/file.h>
25 #include <grub/mm.h>
26 #include <grub/misc.h>
27 #include <grub/disk.h>
28 #include <grub/dl.h>
29 #include <grub/types.h>
30 #include <grub/hfs.h>
31 #include <grub/i18n.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 #define GRUB_HFS_SBLOCK 2
36 #define GRUB_HFS_EMBED_HFSPLUS_SIG 0x482B
38 #define GRUB_HFS_BLKS (data->blksz >> 9)
40 #define GRUB_HFS_NODE_LEAF 0xFF
42 /* The two supported filesystems a record can have. */
43 enum
45 GRUB_HFS_FILETYPE_DIR = 1,
46 GRUB_HFS_FILETYPE_FILE = 2
49 /* Catalog node ID (CNID). */
50 enum grub_hfs_cnid_type
52 GRUB_HFS_CNID_ROOT_PARENT = 1,
53 GRUB_HFS_CNID_ROOT = 2,
54 GRUB_HFS_CNID_EXT = 3,
55 GRUB_HFS_CNID_CAT = 4,
56 GRUB_HFS_CNID_BAD = 5
59 /* A node descriptor. This is the header of every node. */
60 struct grub_hfs_node
62 grub_uint32_t next;
63 grub_uint32_t prev;
64 grub_uint8_t type;
65 grub_uint8_t level;
66 grub_uint16_t reccnt;
67 grub_uint16_t unused;
68 } GRUB_PACKED;
70 /* The head of the B*-Tree. */
71 struct grub_hfs_treeheader
73 grub_uint16_t tree_depth;
74 /* The number of the first node. */
75 grub_uint32_t root_node;
76 grub_uint32_t leaves;
77 grub_uint32_t first_leaf;
78 grub_uint32_t last_leaf;
79 grub_uint16_t node_size;
80 grub_uint16_t key_size;
81 grub_uint32_t nodes;
82 grub_uint32_t free_nodes;
83 grub_uint8_t unused[76];
84 } GRUB_PACKED;
86 /* The state of a mounted HFS filesystem. */
87 struct grub_hfs_data
89 struct grub_hfs_sblock sblock;
90 grub_disk_t disk;
91 grub_hfs_datarecord_t extents;
92 int fileid;
93 int size;
94 int ext_root;
95 int ext_size;
96 int cat_root;
97 int cat_size;
98 int blksz;
99 int log2_blksz;
100 int rootdir;
103 /* The key as used on disk in a catalog tree. This is used to lookup
104 file/directory nodes by parent directory ID and filename. */
105 struct grub_hfs_catalog_key
107 grub_uint8_t unused;
108 grub_uint32_t parent_dir;
110 /* Filename length. */
111 grub_uint8_t strlen;
113 /* Filename. */
114 grub_uint8_t str[31];
115 } GRUB_PACKED;
117 /* The key as used on disk in a extent overflow tree. Using this key
118 the extents can be looked up using a fileid and logical start block
119 as index. */
120 struct grub_hfs_extent_key
122 /* The kind of fork. This is used to store meta information like
123 icons, attributes, etc. We will only use the datafork, which is
124 0. */
125 grub_uint8_t forktype;
126 grub_uint32_t fileid;
127 grub_uint16_t first_block;
128 } GRUB_PACKED;
130 /* A directory record. This is used to find out the directory ID. */
131 struct grub_hfs_dirrec
133 /* For a directory, type == 1. */
134 grub_uint8_t type;
135 grub_uint8_t unused[5];
136 grub_uint32_t dirid;
137 grub_uint32_t ctime;
138 grub_uint32_t mtime;
139 } GRUB_PACKED;
141 /* Information about a file. */
142 struct grub_hfs_filerec
144 /* For a file, type == 2. */
145 grub_uint8_t type;
146 grub_uint8_t unused[19];
147 grub_uint32_t fileid;
148 grub_uint8_t unused2[2];
149 grub_uint32_t size;
150 grub_uint8_t unused3[18];
151 grub_uint32_t mtime;
152 grub_uint8_t unused4[22];
154 /* The first 3 extents of the file. The other extents can be found
155 in the extent overflow file. */
156 grub_hfs_datarecord_t extents;
157 } GRUB_PACKED;
159 /* A record descriptor, both key and data, used to pass to call back
160 functions. */
161 struct grub_hfs_record
163 void *key;
164 grub_size_t keylen;
165 void *data;
166 grub_size_t datalen;
169 static grub_dl_t my_mod;
171 static int grub_hfs_find_node (struct grub_hfs_data *, char *,
172 grub_uint32_t, int, char *, grub_size_t);
174 /* Find block BLOCK of the file FILE in the mounted UFS filesystem
175 DATA. The first 3 extents are described by DAT. If cache is set,
176 using caching to improve non-random reads. */
177 static unsigned int
178 grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat,
179 int file, int block, int cache)
181 grub_hfs_datarecord_t dr;
182 int pos = 0;
183 struct grub_hfs_extent_key key;
185 int tree = 0;
186 static int cache_file = 0;
187 static int cache_pos = 0;
188 static grub_hfs_datarecord_t cache_dr;
190 grub_memcpy (dr, dat, sizeof (dr));
192 key.forktype = 0;
193 key.fileid = grub_cpu_to_be32 (file);
195 if (cache && cache_file == file && block > cache_pos)
197 pos = cache_pos;
198 key.first_block = grub_cpu_to_be16 (pos);
199 grub_memcpy (dr, cache_dr, sizeof (cache_dr));
202 for (;;)
204 int i;
206 /* Try all 3 extents. */
207 for (i = 0; i < 3; i++)
209 /* Check if the block is stored in this extent. */
210 if (grub_be_to_cpu16 (dr[i].count) + pos > block)
212 int first = grub_be_to_cpu16 (dr[i].first_block);
214 /* If the cache is enabled, store the current position
215 in the tree. */
216 if (tree && cache)
218 cache_file = file;
219 cache_pos = pos;
220 grub_memcpy (cache_dr, dr, sizeof (cache_dr));
223 return (grub_be_to_cpu16 (data->sblock.first_block)
224 + (first + block - pos) * GRUB_HFS_BLKS);
227 /* Try the next extent. */
228 pos += grub_be_to_cpu16 (dr[i].count);
231 /* Lookup the block in the extent overflow file. */
232 key.first_block = grub_cpu_to_be16 (pos);
233 tree = 1;
234 grub_hfs_find_node (data, (char *) &key, data->ext_root,
235 1, (char *) &dr, sizeof (dr));
236 if (grub_errno)
237 return 0;
242 /* Read LEN bytes from the file described by DATA starting with byte
243 POS. Return the amount of read bytes in READ. */
244 static grub_ssize_t
245 grub_hfs_read_file (struct grub_hfs_data *data,
246 grub_disk_read_hook_t read_hook, void *read_hook_data,
247 grub_uint32_t pos, grub_size_t len, char *buf)
249 grub_off_t i;
250 grub_off_t blockcnt;
252 /* Files are at most 2G/4G - 1 bytes on hfs. Avoid 64-bit division.
253 Moreover len > 0 as checked in upper layer. */
254 blockcnt = (len + pos - 1) / data->blksz + 1;
256 for (i = pos / data->blksz; i < blockcnt; i++)
258 grub_disk_addr_t blknr;
259 grub_off_t blockoff;
260 grub_off_t blockend = data->blksz;
262 int skipfirst = 0;
264 blockoff = pos % data->blksz;
266 blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
267 if (grub_errno)
268 return -1;
270 /* Last block. */
271 if (i == blockcnt - 1)
273 blockend = (len + pos) % data->blksz;
275 /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
276 if (! blockend)
277 blockend = data->blksz;
280 /* First block. */
281 if (i == pos / data->blksz)
283 skipfirst = blockoff;
284 blockend -= skipfirst;
287 /* If the block number is 0 this block is not stored on disk but
288 is zero filled instead. */
289 if (blknr)
291 data->disk->read_hook = read_hook;
292 data->disk->read_hook_data = read_hook_data;
293 grub_disk_read (data->disk, blknr, skipfirst,
294 blockend, buf);
295 data->disk->read_hook = 0;
296 if (grub_errno)
297 return -1;
300 buf += data->blksz - skipfirst;
303 return len;
307 /* Mount the filesystem on the disk DISK. */
308 static struct grub_hfs_data *
309 grub_hfs_mount (grub_disk_t disk)
311 struct grub_hfs_data *data;
312 struct grub_hfs_catalog_key key;
313 struct grub_hfs_dirrec dir;
314 int first_block;
316 struct
318 struct grub_hfs_node node;
319 struct grub_hfs_treeheader head;
320 } treehead;
322 data = grub_malloc (sizeof (struct grub_hfs_data));
323 if (!data)
324 return 0;
326 /* Read the superblock. */
327 if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
328 sizeof (struct grub_hfs_sblock), &data->sblock))
329 goto fail;
331 /* Check if this is a HFS filesystem. */
332 if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC
333 || data->sblock.blksz == 0
334 || (data->sblock.blksz & grub_cpu_to_be32_compile_time (0xc00001ff)))
336 grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
337 goto fail;
340 /* Check if this is an embedded HFS+ filesystem. */
341 if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
343 grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
344 goto fail;
347 data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
348 data->disk = disk;
350 /* Lookup the root node of the extent overflow tree. */
351 first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
352 * GRUB_HFS_BLKS)
353 + grub_be_to_cpu16 (data->sblock.first_block));
355 if (grub_disk_read (data->disk, first_block, 0,
356 sizeof (treehead), &treehead))
357 goto fail;
358 data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
359 data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
361 /* Lookup the root node of the catalog tree. */
362 first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
363 * GRUB_HFS_BLKS)
364 + grub_be_to_cpu16 (data->sblock.first_block));
365 if (grub_disk_read (data->disk, first_block, 0,
366 sizeof (treehead), &treehead))
367 goto fail;
368 data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
369 data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
371 if (data->cat_size == 0
372 || data->blksz < data->cat_size
373 || data->blksz < data->ext_size)
374 goto fail;
376 /* Lookup the root directory node in the catalog tree using the
377 volume name. */
378 key.parent_dir = grub_cpu_to_be32_compile_time (1);
379 key.strlen = data->sblock.volname[0];
380 grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
382 if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
383 0, (char *) &dir, sizeof (dir)) == 0)
385 grub_error (GRUB_ERR_BAD_FS, "cannot find the HFS root directory");
386 goto fail;
389 if (grub_errno)
390 goto fail;
392 data->rootdir = grub_be_to_cpu32 (dir.dirid);
394 return data;
395 fail:
396 grub_free (data);
398 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
399 grub_error (GRUB_ERR_BAD_FS, "not a HFS filesystem");
401 return 0;
404 /* Compare the K1 and K2 catalog file keys using HFS character ordering. */
405 static int
406 grub_hfs_cmp_catkeys (const struct grub_hfs_catalog_key *k1,
407 const struct grub_hfs_catalog_key *k2)
409 /* Taken from hfsutils 3.2.6 and converted to a readable form */
410 static const unsigned char hfs_charorder[256] = {
411 [0x00] = 0,
412 [0x01] = 1,
413 [0x02] = 2,
414 [0x03] = 3,
415 [0x04] = 4,
416 [0x05] = 5,
417 [0x06] = 6,
418 [0x07] = 7,
419 [0x08] = 8,
420 [0x09] = 9,
421 [0x0A] = 10,
422 [0x0B] = 11,
423 [0x0C] = 12,
424 [0x0D] = 13,
425 [0x0E] = 14,
426 [0x0F] = 15,
427 [0x10] = 16,
428 [0x11] = 17,
429 [0x12] = 18,
430 [0x13] = 19,
431 [0x14] = 20,
432 [0x15] = 21,
433 [0x16] = 22,
434 [0x17] = 23,
435 [0x18] = 24,
436 [0x19] = 25,
437 [0x1A] = 26,
438 [0x1B] = 27,
439 [0x1C] = 28,
440 [0x1D] = 29,
441 [0x1E] = 30,
442 [0x1F] = 31,
443 [' '] = 32, [0xCA] = 32,
444 ['!'] = 33,
445 ['"'] = 34,
446 [0xD2] = 35,
447 [0xD3] = 36,
448 [0xC7] = 37,
449 [0xC8] = 38,
450 ['#'] = 39,
451 ['$'] = 40,
452 ['%'] = 41,
453 ['&'] = 42,
454 ['\''] = 43,
455 [0xD4] = 44,
456 [0xD5] = 45,
457 ['('] = 46,
458 [')'] = 47,
459 ['*'] = 48,
460 ['+'] = 49,
461 [','] = 50,
462 ['-'] = 51,
463 ['.'] = 52,
464 ['/'] = 53,
465 ['0'] = 54,
466 ['1'] = 55,
467 ['2'] = 56,
468 ['3'] = 57,
469 ['4'] = 58,
470 ['5'] = 59,
471 ['6'] = 60,
472 ['7'] = 61,
473 ['8'] = 62,
474 ['9'] = 63,
475 [':'] = 64,
476 [';'] = 65,
477 ['<'] = 66,
478 ['='] = 67,
479 ['>'] = 68,
480 ['?'] = 69,
481 ['@'] = 70,
482 ['A'] = 71, ['a'] = 71,
483 [0x88] = 72, [0xCB] = 72,
484 [0x80] = 73, [0x8A] = 73,
485 [0x8B] = 74, [0xCC] = 74,
486 [0x81] = 75, [0x8C] = 75,
487 [0xAE] = 76, [0xBE] = 76,
488 ['`'] = 77,
489 [0x87] = 78,
490 [0x89] = 79,
491 [0xBB] = 80,
492 ['B'] = 81, ['b'] = 81,
493 ['C'] = 82, ['c'] = 82,
494 [0x82] = 83, [0x8D] = 83,
495 ['D'] = 84, ['d'] = 84,
496 ['E'] = 85, ['e'] = 85,
497 [0x83] = 86, [0x8E] = 86,
498 [0x8F] = 87,
499 [0x90] = 88,
500 [0x91] = 89,
501 ['F'] = 90, ['f'] = 90,
502 ['G'] = 91, ['g'] = 91,
503 ['H'] = 92, ['h'] = 92,
504 ['I'] = 93, ['i'] = 93,
505 [0x92] = 94,
506 [0x93] = 95,
507 [0x94] = 96,
508 [0x95] = 97,
509 ['J'] = 98, ['j'] = 98,
510 ['K'] = 99, ['k'] = 99,
511 ['L'] = 100, ['l'] = 100,
512 ['M'] = 101, ['m'] = 101,
513 ['N'] = 102, ['n'] = 102,
514 [0x84] = 103, [0x96] = 103,
515 ['O'] = 104, ['o'] = 104,
516 [0x85] = 105, [0x9A] = 105,
517 [0x9B] = 106, [0xCD] = 106,
518 [0xAF] = 107, [0xBF] = 107,
519 [0xCE] = 108, [0xCF] = 108,
520 [0x97] = 109,
521 [0x98] = 110,
522 [0x99] = 111,
523 [0xBC] = 112,
524 ['P'] = 113, ['p'] = 113,
525 ['Q'] = 114, ['q'] = 114,
526 ['R'] = 115, ['r'] = 115,
527 ['S'] = 116, ['s'] = 116,
528 [0xA7] = 117,
529 ['T'] = 118, ['t'] = 118,
530 ['U'] = 119, ['u'] = 119,
531 [0x86] = 120, [0x9F] = 120,
532 [0x9C] = 121,
533 [0x9D] = 122,
534 [0x9E] = 123,
535 ['V'] = 124, ['v'] = 124,
536 ['W'] = 125, ['w'] = 125,
537 ['X'] = 126, ['x'] = 126,
538 ['Y'] = 127, ['y'] = 127,
539 [0xD8] = 128,
540 ['Z'] = 129, ['z'] = 129,
541 ['['] = 130,
542 ['\\'] = 131,
543 [']'] = 132,
544 ['^'] = 133,
545 ['_'] = 134,
546 ['{'] = 135,
547 ['|'] = 136,
548 ['}'] = 137,
549 ['~'] = 138,
550 [0x7F] = 139,
551 [0xA0] = 140,
552 [0xA1] = 141,
553 [0xA2] = 142,
554 [0xA3] = 143,
555 [0xA4] = 144,
556 [0xA5] = 145,
557 [0xA6] = 146,
558 [0xA8] = 147,
559 [0xA9] = 148,
560 [0xAA] = 149,
561 [0xAB] = 150,
562 [0xAC] = 151,
563 [0xAD] = 152,
564 [0xB0] = 153,
565 [0xB1] = 154,
566 [0xB2] = 155,
567 [0xB3] = 156,
568 [0xB4] = 157,
569 [0xB5] = 158,
570 [0xB6] = 159,
571 [0xB7] = 160,
572 [0xB8] = 161,
573 [0xB9] = 162,
574 [0xBA] = 163,
575 [0xBD] = 164,
576 [0xC0] = 165,
577 [0xC1] = 166,
578 [0xC2] = 167,
579 [0xC3] = 168,
580 [0xC4] = 169,
581 [0xC5] = 170,
582 [0xC6] = 171,
583 [0xC9] = 172,
584 [0xD0] = 173,
585 [0xD1] = 174,
586 [0xD6] = 175,
587 [0xD7] = 176,
588 [0xD9] = 177,
589 [0xDA] = 178,
590 [0xDB] = 179,
591 [0xDC] = 180,
592 [0xDD] = 181,
593 [0xDE] = 182,
594 [0xDF] = 183,
595 [0xE0] = 184,
596 [0xE1] = 185,
597 [0xE2] = 186,
598 [0xE3] = 187,
599 [0xE4] = 188,
600 [0xE5] = 189,
601 [0xE6] = 190,
602 [0xE7] = 191,
603 [0xE8] = 192,
604 [0xE9] = 193,
605 [0xEA] = 194,
606 [0xEB] = 195,
607 [0xEC] = 196,
608 [0xED] = 197,
609 [0xEE] = 198,
610 [0xEF] = 199,
611 [0xF0] = 200,
612 [0xF1] = 201,
613 [0xF2] = 202,
614 [0xF3] = 203,
615 [0xF4] = 204,
616 [0xF5] = 205,
617 [0xF6] = 206,
618 [0xF7] = 207,
619 [0xF8] = 208,
620 [0xF9] = 209,
621 [0xFA] = 210,
622 [0xFB] = 211,
623 [0xFC] = 212,
624 [0xFD] = 213,
625 [0xFE] = 214,
626 [0xFF] = 215,
628 int i;
629 int cmp;
630 int minlen = (k1->strlen < k2->strlen) ? k1->strlen : k2->strlen;
632 cmp = (grub_be_to_cpu32 (k1->parent_dir) - grub_be_to_cpu32 (k2->parent_dir));
633 if (cmp != 0)
634 return cmp;
636 for (i = 0; i < minlen; i++)
638 cmp = (hfs_charorder[k1->str[i]] - hfs_charorder[k2->str[i]]);
639 if (cmp != 0)
640 return cmp;
643 /* Shorter strings precede long ones. */
644 return (k1->strlen - k2->strlen);
648 /* Compare the K1 and K2 extent overflow file keys. */
649 static int
650 grub_hfs_cmp_extkeys (const struct grub_hfs_extent_key *k1,
651 const struct grub_hfs_extent_key *k2)
653 int cmp = k1->forktype - k2->forktype;
654 if (cmp == 0)
655 cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
656 if (cmp == 0)
657 cmp = (grub_be_to_cpu16 (k1->first_block)
658 - grub_be_to_cpu16 (k2->first_block));
659 return cmp;
663 /* Iterate the records in the node with index IDX in the mounted HFS
664 filesystem DATA. This node holds data of the type TYPE (0 =
665 catalog node, 1 = extent overflow node). If this is set, continue
666 iterating to the next node. For every records, call NODE_HOOK. */
667 static grub_err_t
668 grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
669 int this, int (*node_hook) (struct grub_hfs_node *hnd,
670 struct grub_hfs_record *,
671 void *hook_arg),
672 void *hook_arg)
674 grub_size_t nodesize = type == 0 ? data->cat_size : data->ext_size;
676 union node_union
678 struct grub_hfs_node node;
679 char rawnode[0];
680 grub_uint16_t offsets[0];
681 } *node;
683 if (nodesize < sizeof (struct grub_hfs_node))
684 nodesize = sizeof (struct grub_hfs_node);
686 node = grub_malloc (nodesize);
687 if (!node)
688 return grub_errno;
692 int i;
693 struct grub_hfs_extent *dat;
694 int blk;
695 grub_uint16_t reccnt;
697 dat = (struct grub_hfs_extent *) (type == 0
698 ? (&data->sblock.catalog_recs)
699 : (&data->sblock.extent_recs));
701 /* Read the node into memory. */
702 blk = grub_hfs_block (data, dat,
703 (type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT,
704 idx / (data->blksz / nodesize), 0);
705 blk += (idx % (data->blksz / nodesize));
707 if (grub_errno || grub_disk_read (data->disk, blk, 0,
708 nodesize, node))
710 grub_free (node);
711 return grub_errno;
714 reccnt = grub_be_to_cpu16 (node->node.reccnt);
715 if (reccnt > (nodesize >> 1))
716 reccnt = (nodesize >> 1);
718 /* Iterate over all records in this node. */
719 for (i = 0; i < reccnt; i++)
721 int pos = (nodesize >> 1) - 1 - i;
722 struct pointer
724 grub_uint8_t keylen;
725 grub_uint8_t key;
726 } GRUB_PACKED *pnt;
727 grub_uint16_t off = grub_be_to_cpu16 (node->offsets[pos]);
728 if (off > nodesize - sizeof(*pnt))
729 continue;
730 pnt = (struct pointer *) (off + node->rawnode);
731 if (nodesize < (grub_size_t) off + pnt->keylen + 1)
732 continue;
734 struct grub_hfs_record rec =
736 &pnt->key,
737 pnt->keylen,
738 &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
739 nodesize - off - pnt->keylen - 1
742 if (node_hook (&node->node, &rec, hook_arg))
744 grub_free (node);
745 return 0;
749 idx = grub_be_to_cpu32 (node->node.next);
750 } while (idx && this);
751 grub_free (node);
752 return 0;
755 struct grub_hfs_find_node_node_found_ctx
757 int found;
758 int isleaf;
759 int done;
760 int type;
761 const char *key;
762 char *datar;
763 grub_size_t datalen;
766 static int
767 grub_hfs_find_node_node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec,
768 void *hook_arg)
770 struct grub_hfs_find_node_node_found_ctx *ctx = hook_arg;
771 int cmp = 1;
773 if (ctx->type == 0)
774 cmp = grub_hfs_cmp_catkeys (rec->key, (const void *) ctx->key);
775 else
776 cmp = grub_hfs_cmp_extkeys (rec->key, (const void *) ctx->key);
778 /* If the key is smaller or equal to the current node, mark the
779 entry. In case of a non-leaf mode it will be used to lookup
780 the rest of the tree. */
781 if (cmp <= 0)
782 ctx->found = grub_be_to_cpu32 (grub_get_unaligned32 (rec->data));
783 else /* The key can not be found in the tree. */
784 return 1;
786 /* Check if this node is a leaf node. */
787 if (hnd->type == GRUB_HFS_NODE_LEAF)
789 ctx->isleaf = 1;
791 /* Found it!!!! */
792 if (cmp == 0)
794 ctx->done = 1;
796 grub_memcpy (ctx->datar, rec->data,
797 rec->datalen < ctx->datalen ? rec->datalen : ctx->datalen);
798 return 1;
802 return 0;
806 /* Lookup a record in the mounted filesystem DATA using the key KEY.
807 The index of the node on top of the tree is IDX. The tree is of
808 the type TYPE (0 = catalog node, 1 = extent overflow node). Return
809 the data in DATAR with a maximum length of DATALEN. */
810 static int
811 grub_hfs_find_node (struct grub_hfs_data *data, char *key,
812 grub_uint32_t idx, int type, char *datar, grub_size_t datalen)
814 struct grub_hfs_find_node_node_found_ctx ctx =
816 .found = -1,
817 .isleaf = 0,
818 .done = 0,
819 .type = type,
820 .key = key,
821 .datar = datar,
822 .datalen = datalen
827 ctx.found = -1;
829 if (grub_hfs_iterate_records (data, type, idx, 0, grub_hfs_find_node_node_found, &ctx))
830 return 0;
832 if (ctx.found == -1)
833 return 0;
835 idx = ctx.found;
836 } while (! ctx.isleaf);
838 return ctx.done;
841 struct grub_hfs_iterate_dir_node_found_ctx
843 grub_uint32_t dir_be;
844 int found;
845 int isleaf;
846 grub_uint32_t next;
847 int (*hook) (struct grub_hfs_record *, void *hook_arg);
848 void *hook_arg;
851 static int
852 grub_hfs_iterate_dir_node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec,
853 void *hook_arg)
855 struct grub_hfs_iterate_dir_node_found_ctx *ctx = hook_arg;
856 struct grub_hfs_catalog_key *ckey = rec->key;
858 /* The lowest key possible with DIR as root directory. */
859 const struct grub_hfs_catalog_key key = {0, ctx->dir_be, 0, ""};
861 if (grub_hfs_cmp_catkeys (rec->key, &key) <= 0)
862 ctx->found = grub_be_to_cpu32 (grub_get_unaligned32 (rec->data));
864 if (hnd->type == 0xFF && ckey->strlen > 0)
866 ctx->isleaf = 1;
867 ctx->next = grub_be_to_cpu32 (hnd->next);
869 /* An entry was found. */
870 if (ckey->parent_dir == ctx->dir_be)
871 return ctx->hook (rec, ctx->hook_arg);
874 return 0;
877 static int
878 grub_hfs_iterate_dir_it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
879 struct grub_hfs_record *rec,
880 void *hook_arg)
882 struct grub_hfs_catalog_key *ckey = rec->key;
883 struct grub_hfs_iterate_dir_node_found_ctx *ctx = hook_arg;
885 /* Stop when the entries do not match anymore. */
886 if (ckey->parent_dir != ctx->dir_be)
887 return 1;
889 return ctx->hook (rec, ctx->hook_arg);
893 /* Iterate over the directory with the id DIR. The tree is searched
894 starting with the node ROOT_IDX. For every entry in this directory
895 call HOOK. */
896 static grub_err_t
897 grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
898 grub_uint32_t dir, int (*hook) (struct grub_hfs_record *, void *hook_arg),
899 void *hook_arg)
901 struct grub_hfs_iterate_dir_node_found_ctx ctx =
903 .dir_be = grub_cpu_to_be32 (dir),
904 .found = -1,
905 .isleaf = 0,
906 .next = 0,
907 .hook = hook,
908 .hook_arg = hook_arg
913 ctx.found = -1;
915 if (grub_hfs_iterate_records (data, 0, root_idx, 0, grub_hfs_iterate_dir_node_found, &ctx))
916 return grub_errno;
918 if (ctx.found == -1)
919 return 0;
921 root_idx = ctx.found;
922 } while (! ctx.isleaf);
924 /* If there was a matching record in this leaf node, continue the
925 iteration until the last record was found. */
926 grub_hfs_iterate_records (data, 0, ctx.next, 1, grub_hfs_iterate_dir_it_dir, &ctx);
927 return grub_errno;
930 #define MAX_UTF8_PER_MAC_ROMAN 3
932 static const char macroman[0x80][MAX_UTF8_PER_MAC_ROMAN + 1] =
934 /* 80 */ "\xc3\x84",
935 /* 81 */ "\xc3\x85",
936 /* 82 */ "\xc3\x87",
937 /* 83 */ "\xc3\x89",
938 /* 84 */ "\xc3\x91",
939 /* 85 */ "\xc3\x96",
940 /* 86 */ "\xc3\x9c",
941 /* 87 */ "\xc3\xa1",
942 /* 88 */ "\xc3\xa0",
943 /* 89 */ "\xc3\xa2",
944 /* 8A */ "\xc3\xa4",
945 /* 8B */ "\xc3\xa3",
946 /* 8C */ "\xc3\xa5",
947 /* 8D */ "\xc3\xa7",
948 /* 8E */ "\xc3\xa9",
949 /* 8F */ "\xc3\xa8",
950 /* 90 */ "\xc3\xaa",
951 /* 91 */ "\xc3\xab",
952 /* 92 */ "\xc3\xad",
953 /* 93 */ "\xc3\xac",
954 /* 94 */ "\xc3\xae",
955 /* 95 */ "\xc3\xaf",
956 /* 96 */ "\xc3\xb1",
957 /* 97 */ "\xc3\xb3",
958 /* 98 */ "\xc3\xb2",
959 /* 99 */ "\xc3\xb4",
960 /* 9A */ "\xc3\xb6",
961 /* 9B */ "\xc3\xb5",
962 /* 9C */ "\xc3\xba",
963 /* 9D */ "\xc3\xb9",
964 /* 9E */ "\xc3\xbb",
965 /* 9F */ "\xc3\xbc",
966 /* A0 */ "\xe2\x80\xa0",
967 /* A1 */ "\xc2\xb0",
968 /* A2 */ "\xc2\xa2",
969 /* A3 */ "\xc2\xa3",
970 /* A4 */ "\xc2\xa7",
971 /* A5 */ "\xe2\x80\xa2",
972 /* A6 */ "\xc2\xb6",
973 /* A7 */ "\xc3\x9f",
974 /* A8 */ "\xc2\xae",
975 /* A9 */ "\xc2\xa9",
976 /* AA */ "\xe2\x84\xa2",
977 /* AB */ "\xc2\xb4",
978 /* AC */ "\xc2\xa8",
979 /* AD */ "\xe2\x89\xa0",
980 /* AE */ "\xc3\x86",
981 /* AF */ "\xc3\x98",
982 /* B0 */ "\xe2\x88\x9e",
983 /* B1 */ "\xc2\xb1",
984 /* B2 */ "\xe2\x89\xa4",
985 /* B3 */ "\xe2\x89\xa5",
986 /* B4 */ "\xc2\xa5",
987 /* B5 */ "\xc2\xb5",
988 /* B6 */ "\xe2\x88\x82",
989 /* B7 */ "\xe2\x88\x91",
990 /* B8 */ "\xe2\x88\x8f",
991 /* B9 */ "\xcf\x80",
992 /* BA */ "\xe2\x88\xab",
993 /* BB */ "\xc2\xaa",
994 /* BC */ "\xc2\xba",
995 /* BD */ "\xce\xa9",
996 /* BE */ "\xc3\xa6",
997 /* BF */ "\xc3\xb8",
998 /* C0 */ "\xc2\xbf",
999 /* C1 */ "\xc2\xa1",
1000 /* C2 */ "\xc2\xac",
1001 /* C3 */ "\xe2\x88\x9a",
1002 /* C4 */ "\xc6\x92",
1003 /* C5 */ "\xe2\x89\x88",
1004 /* C6 */ "\xe2\x88\x86",
1005 /* C7 */ "\xc2\xab",
1006 /* C8 */ "\xc2\xbb",
1007 /* C9 */ "\xe2\x80\xa6",
1008 /* CA */ "\xc2\xa0",
1009 /* CB */ "\xc3\x80",
1010 /* CC */ "\xc3\x83",
1011 /* CD */ "\xc3\x95",
1012 /* CE */ "\xc5\x92",
1013 /* CF */ "\xc5\x93",
1014 /* D0 */ "\xe2\x80\x93",
1015 /* D1 */ "\xe2\x80\x94",
1016 /* D2 */ "\xe2\x80\x9c",
1017 /* D3 */ "\xe2\x80\x9d",
1018 /* D4 */ "\xe2\x80\x98",
1019 /* D5 */ "\xe2\x80\x99",
1020 /* D6 */ "\xc3\xb7",
1021 /* D7 */ "\xe2\x97\x8a",
1022 /* D8 */ "\xc3\xbf",
1023 /* D9 */ "\xc5\xb8",
1024 /* DA */ "\xe2\x81\x84",
1025 /* DB */ "\xe2\x82\xac",
1026 /* DC */ "\xe2\x80\xb9",
1027 /* DD */ "\xe2\x80\xba",
1028 /* DE */ "\xef\xac\x81",
1029 /* DF */ "\xef\xac\x82",
1030 /* E0 */ "\xe2\x80\xa1",
1031 /* E1 */ "\xc2\xb7",
1032 /* E2 */ "\xe2\x80\x9a",
1033 /* E3 */ "\xe2\x80\x9e",
1034 /* E4 */ "\xe2\x80\xb0",
1035 /* E5 */ "\xc3\x82",
1036 /* E6 */ "\xc3\x8a",
1037 /* E7 */ "\xc3\x81",
1038 /* E8 */ "\xc3\x8b",
1039 /* E9 */ "\xc3\x88",
1040 /* EA */ "\xc3\x8d",
1041 /* EB */ "\xc3\x8e",
1042 /* EC */ "\xc3\x8f",
1043 /* ED */ "\xc3\x8c",
1044 /* EE */ "\xc3\x93",
1045 /* EF */ "\xc3\x94",
1046 /* F0 */ "\xef\xa3\xbf",
1047 /* F1 */ "\xc3\x92",
1048 /* F2 */ "\xc3\x9a",
1049 /* F3 */ "\xc3\x9b",
1050 /* F4 */ "\xc3\x99",
1051 /* F5 */ "\xc4\xb1",
1052 /* F6 */ "\xcb\x86",
1053 /* F7 */ "\xcb\x9c",
1054 /* F8 */ "\xc2\xaf",
1055 /* F9 */ "\xcb\x98",
1056 /* FA */ "\xcb\x99",
1057 /* FB */ "\xcb\x9a",
1058 /* FC */ "\xc2\xb8",
1059 /* FD */ "\xcb\x9d",
1060 /* FE */ "\xcb\x9b",
1061 /* FF */ "\xcb\x87",
1064 static void
1065 macroman_to_utf8 (char *to, const grub_uint8_t *from, grub_size_t len,
1066 int translate_slash)
1068 char *optr = to;
1069 const grub_uint8_t *iptr;
1071 for (iptr = from; iptr < from + len && *iptr; iptr++)
1073 /* Translate '/' to ':' as per HFS spec. */
1074 if (*iptr == '/' && translate_slash)
1076 *optr++ = ':';
1077 continue;
1079 if (!(*iptr & 0x80))
1081 *optr++ = *iptr;
1082 continue;
1084 optr = grub_stpcpy (optr, macroman[*iptr & 0x7f]);
1086 *optr = 0;
1089 static grub_ssize_t
1090 utf8_to_macroman (grub_uint8_t *to, const char *from)
1092 grub_uint8_t *end = to + 31;
1093 grub_uint8_t *optr = to;
1094 const char *iptr = from;
1096 while (*iptr && optr < end)
1098 int i, clen;
1099 /* Translate ':' to '/' as per HFS spec. */
1100 if (*iptr == ':')
1102 *optr++ = '/';
1103 iptr++;
1104 continue;
1106 if (!(*iptr & 0x80))
1108 *optr++ = *iptr++;
1109 continue;
1111 clen = 2;
1112 if ((*iptr & 0xf0) == 0xe0)
1113 clen++;
1114 for (i = 0; i < 0x80; i++)
1115 if (grub_memcmp (macroman[i], iptr, clen) == 0)
1116 break;
1117 if (i == 0x80)
1118 break;
1119 *optr++ = i | 0x80;
1120 iptr += clen;
1122 /* Too long or not encodable. */
1123 if (*iptr)
1124 return -1;
1125 return optr - to;
1129 /* Find a file or directory with the pathname PATH in the filesystem
1130 DATA. Return the file record in RETDATA when it is non-zero.
1131 Return the directory number in RETINODE when it is non-zero. */
1132 static grub_err_t
1133 grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
1134 struct grub_hfs_filerec *retdata, int *retinode)
1136 int inode = data->rootdir;
1137 char *next;
1138 char *origpath;
1139 union {
1140 struct grub_hfs_filerec frec;
1141 struct grub_hfs_dirrec dir;
1142 } fdrec;
1144 fdrec.frec.type = GRUB_HFS_FILETYPE_DIR;
1146 if (path[0] != '/')
1148 grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path);
1149 return 0;
1152 origpath = grub_strdup (path);
1153 if (!origpath)
1154 return grub_errno;
1156 path = origpath;
1157 while (*path == '/')
1158 path++;
1160 while (path && grub_strlen (path))
1162 grub_ssize_t slen;
1163 if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR)
1165 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
1166 goto fail;
1169 /* Isolate a part of the path. */
1170 next = grub_strchr (path, '/');
1171 if (next)
1173 while (*next == '/')
1174 *(next++) = '\0';
1177 struct grub_hfs_catalog_key key;
1179 key.parent_dir = grub_cpu_to_be32 (inode);
1180 slen = utf8_to_macroman (key.str, path);
1181 if (slen < 0)
1183 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path);
1184 goto fail;
1186 key.strlen = slen;
1188 /* Lookup this node. */
1189 if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
1190 0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
1192 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1193 goto fail;
1196 if (grub_errno)
1197 goto fail;
1199 inode = grub_be_to_cpu32 (fdrec.dir.dirid);
1200 path = next;
1203 if (retdata)
1204 grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec));
1206 if (retinode)
1207 *retinode = inode;
1209 fail:
1210 grub_free (origpath);
1211 return grub_errno;
1214 struct grub_hfs_dir_hook_ctx
1216 grub_fs_dir_hook_t hook;
1217 void *hook_data;
1220 static int
1221 grub_hfs_dir_hook (struct grub_hfs_record *rec, void *hook_arg)
1223 struct grub_hfs_dir_hook_ctx *ctx = hook_arg;
1224 struct grub_hfs_dirrec *drec = rec->data;
1225 struct grub_hfs_filerec *frec = rec->data;
1226 struct grub_hfs_catalog_key *ckey = rec->key;
1227 char fname[sizeof (ckey->str) * MAX_UTF8_PER_MAC_ROMAN + 1];
1228 struct grub_dirhook_info info;
1229 grub_size_t len;
1231 grub_memset (fname, 0, sizeof (fname));
1233 grub_memset (&info, 0, sizeof (info));
1235 len = ckey->strlen;
1236 if (len > sizeof (ckey->str))
1237 len = sizeof (ckey->str);
1238 macroman_to_utf8 (fname, ckey->str, len, 1);
1240 info.case_insensitive = 1;
1242 if (drec->type == GRUB_HFS_FILETYPE_DIR)
1244 info.dir = 1;
1245 info.mtimeset = 1;
1246 info.inodeset = 1;
1247 info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800;
1248 info.inode = grub_be_to_cpu32 (drec->dirid);
1249 return ctx->hook (fname, &info, ctx->hook_data);
1251 if (frec->type == GRUB_HFS_FILETYPE_FILE)
1253 info.dir = 0;
1254 info.mtimeset = 1;
1255 info.inodeset = 1;
1256 info.mtime = grub_be_to_cpu32 (frec->mtime) - 2082844800;
1257 info.inode = grub_be_to_cpu32 (frec->fileid);
1258 return ctx->hook (fname, &info, ctx->hook_data);
1261 return 0;
1265 static grub_err_t
1266 grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
1267 void *hook_data)
1269 int inode;
1271 struct grub_hfs_data *data;
1272 struct grub_hfs_filerec frec;
1273 struct grub_hfs_dir_hook_ctx ctx =
1275 .hook = hook,
1276 .hook_data = hook_data
1279 grub_dl_ref (my_mod);
1281 data = grub_hfs_mount (device->disk);
1282 if (!data)
1283 goto fail;
1285 /* First the directory ID for the directory. */
1286 if (grub_hfs_find_dir (data, path, &frec, &inode))
1287 goto fail;
1289 if (frec.type != GRUB_HFS_FILETYPE_DIR)
1291 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
1292 goto fail;
1295 grub_hfs_iterate_dir (data, data->cat_root, inode, grub_hfs_dir_hook, &ctx);
1297 fail:
1298 grub_free (data);
1300 grub_dl_unref (my_mod);
1302 return grub_errno;
1306 /* Open a file named NAME and initialize FILE. */
1307 static grub_err_t
1308 grub_hfs_open (struct grub_file *file, const char *name)
1310 struct grub_hfs_data *data;
1311 struct grub_hfs_filerec frec;
1313 grub_dl_ref (my_mod);
1315 data = grub_hfs_mount (file->device->disk);
1317 if (!data)
1319 grub_dl_unref (my_mod);
1320 return grub_errno;
1323 if (grub_hfs_find_dir (data, name, &frec, 0))
1325 grub_free (data);
1326 grub_dl_unref (my_mod);
1327 return grub_errno;
1330 if (frec.type != GRUB_HFS_FILETYPE_FILE)
1332 grub_free (data);
1333 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
1334 grub_dl_unref (my_mod);
1335 return grub_errno;
1338 grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
1339 file->size = grub_be_to_cpu32 (frec.size);
1340 data->size = grub_be_to_cpu32 (frec.size);
1341 data->fileid = grub_be_to_cpu32 (frec.fileid);
1342 file->offset = 0;
1344 file->data = data;
1346 return 0;
1349 static grub_ssize_t
1350 grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
1352 struct grub_hfs_data *data =
1353 (struct grub_hfs_data *) file->data;
1355 return grub_hfs_read_file (data, file->read_hook, file->read_hook_data,
1356 file->offset, len, buf);
1360 static grub_err_t
1361 grub_hfs_close (grub_file_t file)
1363 grub_free (file->data);
1365 grub_dl_unref (my_mod);
1367 return 0;
1371 static grub_err_t
1372 grub_hfs_label (grub_device_t device, char **label)
1374 struct grub_hfs_data *data;
1376 data = grub_hfs_mount (device->disk);
1378 if (data)
1380 grub_size_t len = data->sblock.volname[0];
1381 if (len > sizeof (data->sblock.volname) - 1)
1382 len = sizeof (data->sblock.volname) - 1;
1383 *label = grub_malloc (len * MAX_UTF8_PER_MAC_ROMAN + 1);
1384 if (*label)
1385 macroman_to_utf8 (*label, data->sblock.volname + 1,
1386 len + 1, 0);
1388 else
1389 *label = 0;
1391 grub_free (data);
1392 return grub_errno;
1395 static grub_err_t
1396 grub_hfs_mtime (grub_device_t device, grub_int32_t *tm)
1398 struct grub_hfs_data *data;
1400 data = grub_hfs_mount (device->disk);
1402 if (data)
1403 *tm = grub_be_to_cpu32 (data->sblock.mtime) - 2082844800;
1404 else
1405 *tm = 0;
1407 grub_free (data);
1408 return grub_errno;
1411 static grub_err_t
1412 grub_hfs_uuid (grub_device_t device, char **uuid)
1414 struct grub_hfs_data *data;
1416 grub_dl_ref (my_mod);
1418 data = grub_hfs_mount (device->disk);
1419 if (data && data->sblock.num_serial != 0)
1421 *uuid = grub_xasprintf ("%016llx",
1422 (unsigned long long)
1423 grub_be_to_cpu64 (data->sblock.num_serial));
1425 else
1426 *uuid = NULL;
1428 grub_dl_unref (my_mod);
1430 grub_free (data);
1432 return grub_errno;
1437 static struct grub_fs grub_hfs_fs =
1439 .name = "hfs",
1440 .dir = grub_hfs_dir,
1441 .open = grub_hfs_open,
1442 .read = grub_hfs_read,
1443 .close = grub_hfs_close,
1444 .label = grub_hfs_label,
1445 .uuid = grub_hfs_uuid,
1446 .mtime = grub_hfs_mtime,
1447 #ifdef GRUB_UTIL
1448 .reserved_first_sector = 1,
1449 .blocklist_install = 1,
1450 #endif
1451 .next = 0
1454 GRUB_MOD_INIT(hfs)
1456 grub_fs_register (&grub_hfs_fs);
1457 my_mod = mod;
1460 GRUB_MOD_FINI(hfs)
1462 grub_fs_unregister (&grub_hfs_fs);