Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / hfs.c
blob0a249ccb8cbfd21dfb68e64869e328d89a9b4917
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 } __attribute__ ((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 } __attribute__ ((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 } __attribute__ ((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 } __attribute__ ((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 } __attribute__ ((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 } __attribute__ ((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 int keylen;
165 void *data;
166 int 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 *, int);
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 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
247 unsigned offset, unsigned length),
248 grub_off_t pos, grub_size_t len, char *buf)
250 grub_off_t i;
251 grub_off_t blockcnt;
253 blockcnt = grub_divmod64 (((len + pos)
254 + data->blksz - 1), data->blksz, 0);
256 for (i = grub_divmod64 (pos, data->blksz, 0); 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 grub_divmod64 (pos, data->blksz, &blockoff);
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 grub_divmod64 ((len + pos), data->blksz, &blockend);
275 /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
276 if (! blockend)
277 blockend = data->blksz;
280 /* First block. */
281 if (i == grub_divmod64 (pos, data->blksz, 0))
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 grub_disk_read (data->disk, blknr, skipfirst,
293 blockend, buf);
294 data->disk->read_hook = 0;
295 if (grub_errno)
296 return -1;
299 buf += data->blksz - skipfirst;
302 return len;
306 /* Mount the filesystem on the disk DISK. */
307 static struct grub_hfs_data *
308 grub_hfs_mount (grub_disk_t disk)
310 struct grub_hfs_data *data;
311 struct grub_hfs_catalog_key key;
312 struct grub_hfs_dirrec dir;
313 int first_block;
315 struct
317 struct grub_hfs_node node;
318 struct grub_hfs_treeheader head;
319 } treehead;
321 data = grub_malloc (sizeof (struct grub_hfs_data));
322 if (!data)
323 return 0;
325 /* Read the superblock. */
326 if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
327 sizeof (struct grub_hfs_sblock), &data->sblock))
328 goto fail;
330 /* Check if this is a HFS filesystem. */
331 if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC
332 || (data->sblock.blksz & grub_cpu_to_be32_compile_time (0xc00001ff)))
334 grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
335 goto fail;
338 /* Check if this is an embedded HFS+ filesystem. */
339 if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
341 grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
342 goto fail;
345 data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
346 data->disk = disk;
348 /* Lookup the root node of the extent overflow tree. */
349 first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
350 * GRUB_HFS_BLKS)
351 + grub_be_to_cpu16 (data->sblock.first_block));
353 if (grub_disk_read (data->disk, first_block, 0,
354 sizeof (treehead), &treehead))
355 goto fail;
356 data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
357 data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
359 /* Lookup the root node of the catalog tree. */
360 first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
361 * GRUB_HFS_BLKS)
362 + grub_be_to_cpu16 (data->sblock.first_block));
363 if (grub_disk_read (data->disk, first_block, 0,
364 sizeof (treehead), &treehead))
365 goto fail;
366 data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
367 data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
369 /* Lookup the root directory node in the catalog tree using the
370 volume name. */
371 key.parent_dir = grub_cpu_to_be32 (1);
372 key.strlen = data->sblock.volname[0];
373 grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
375 if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
376 0, (char *) &dir, sizeof (dir)) == 0)
378 grub_error (GRUB_ERR_BAD_FS, "cannot find the HFS root directory");
379 goto fail;
382 if (grub_errno)
383 goto fail;
385 data->rootdir = grub_be_to_cpu32 (dir.dirid);
387 return data;
388 fail:
389 grub_free (data);
391 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
392 grub_error (GRUB_ERR_BAD_FS, "not a HFS filesystem");
394 return 0;
397 /* Compare the K1 and K2 catalog file keys using HFS character ordering. */
398 static int
399 grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
400 struct grub_hfs_catalog_key *k2)
402 /* Taken from hfsutils 3.2.6 and converted to a readable form */
403 static const unsigned char hfs_charorder[256] = {
404 [0x00] = 0,
405 [0x01] = 1,
406 [0x02] = 2,
407 [0x03] = 3,
408 [0x04] = 4,
409 [0x05] = 5,
410 [0x06] = 6,
411 [0x07] = 7,
412 [0x08] = 8,
413 [0x09] = 9,
414 [0x0A] = 10,
415 [0x0B] = 11,
416 [0x0C] = 12,
417 [0x0D] = 13,
418 [0x0E] = 14,
419 [0x0F] = 15,
420 [0x10] = 16,
421 [0x11] = 17,
422 [0x12] = 18,
423 [0x13] = 19,
424 [0x14] = 20,
425 [0x15] = 21,
426 [0x16] = 22,
427 [0x17] = 23,
428 [0x18] = 24,
429 [0x19] = 25,
430 [0x1A] = 26,
431 [0x1B] = 27,
432 [0x1C] = 28,
433 [0x1D] = 29,
434 [0x1E] = 30,
435 [0x1F] = 31,
436 [' '] = 32, [0xCA] = 32,
437 ['!'] = 33,
438 ['"'] = 34,
439 [0xD2] = 35,
440 [0xD3] = 36,
441 [0xC7] = 37,
442 [0xC8] = 38,
443 ['#'] = 39,
444 ['$'] = 40,
445 ['%'] = 41,
446 ['&'] = 42,
447 ['\''] = 43,
448 [0xD4] = 44,
449 [0xD5] = 45,
450 ['('] = 46,
451 [')'] = 47,
452 ['*'] = 48,
453 ['+'] = 49,
454 [','] = 50,
455 ['-'] = 51,
456 ['.'] = 52,
457 ['/'] = 53,
458 ['0'] = 54,
459 ['1'] = 55,
460 ['2'] = 56,
461 ['3'] = 57,
462 ['4'] = 58,
463 ['5'] = 59,
464 ['6'] = 60,
465 ['7'] = 61,
466 ['8'] = 62,
467 ['9'] = 63,
468 [':'] = 64,
469 [';'] = 65,
470 ['<'] = 66,
471 ['='] = 67,
472 ['>'] = 68,
473 ['?'] = 69,
474 ['@'] = 70,
475 ['A'] = 71, ['a'] = 71,
476 [0x88] = 72, [0xCB] = 72,
477 [0x80] = 73, [0x8A] = 73,
478 [0x8B] = 74, [0xCC] = 74,
479 [0x81] = 75, [0x8C] = 75,
480 [0xAE] = 76, [0xBE] = 76,
481 ['`'] = 77,
482 [0x87] = 78,
483 [0x89] = 79,
484 [0xBB] = 80,
485 ['B'] = 81, ['b'] = 81,
486 ['C'] = 82, ['c'] = 82,
487 [0x82] = 83, [0x8D] = 83,
488 ['D'] = 84, ['d'] = 84,
489 ['E'] = 85, ['e'] = 85,
490 [0x83] = 86, [0x8E] = 86,
491 [0x8F] = 87,
492 [0x90] = 88,
493 [0x91] = 89,
494 ['F'] = 90, ['f'] = 90,
495 ['G'] = 91, ['g'] = 91,
496 ['H'] = 92, ['h'] = 92,
497 ['I'] = 93, ['i'] = 93,
498 [0x92] = 94,
499 [0x93] = 95,
500 [0x94] = 96,
501 [0x95] = 97,
502 ['J'] = 98, ['j'] = 98,
503 ['K'] = 99, ['k'] = 99,
504 ['L'] = 100, ['l'] = 100,
505 ['M'] = 101, ['m'] = 101,
506 ['N'] = 102, ['n'] = 102,
507 [0x84] = 103, [0x96] = 103,
508 ['O'] = 104, ['o'] = 104,
509 [0x85] = 105, [0x9A] = 105,
510 [0x9B] = 106, [0xCD] = 106,
511 [0xAF] = 107, [0xBF] = 107,
512 [0xCE] = 108, [0xCF] = 108,
513 [0x97] = 109,
514 [0x98] = 110,
515 [0x99] = 111,
516 [0xBC] = 112,
517 ['P'] = 113, ['p'] = 113,
518 ['Q'] = 114, ['q'] = 114,
519 ['R'] = 115, ['r'] = 115,
520 ['S'] = 116, ['s'] = 116,
521 [0xA7] = 117,
522 ['T'] = 118, ['t'] = 118,
523 ['U'] = 119, ['u'] = 119,
524 [0x86] = 120, [0x9F] = 120,
525 [0x9C] = 121,
526 [0x9D] = 122,
527 [0x9E] = 123,
528 ['V'] = 124, ['v'] = 124,
529 ['W'] = 125, ['w'] = 125,
530 ['X'] = 126, ['x'] = 126,
531 ['Y'] = 127, ['y'] = 127,
532 [0xD8] = 128,
533 ['Z'] = 129, ['z'] = 129,
534 ['['] = 130,
535 ['\\'] = 131,
536 [']'] = 132,
537 ['^'] = 133,
538 ['_'] = 134,
539 ['{'] = 135,
540 ['|'] = 136,
541 ['}'] = 137,
542 ['~'] = 138,
543 [0x7F] = 139,
544 [0xA0] = 140,
545 [0xA1] = 141,
546 [0xA2] = 142,
547 [0xA3] = 143,
548 [0xA4] = 144,
549 [0xA5] = 145,
550 [0xA6] = 146,
551 [0xA8] = 147,
552 [0xA9] = 148,
553 [0xAA] = 149,
554 [0xAB] = 150,
555 [0xAC] = 151,
556 [0xAD] = 152,
557 [0xB0] = 153,
558 [0xB1] = 154,
559 [0xB2] = 155,
560 [0xB3] = 156,
561 [0xB4] = 157,
562 [0xB5] = 158,
563 [0xB6] = 159,
564 [0xB7] = 160,
565 [0xB8] = 161,
566 [0xB9] = 162,
567 [0xBA] = 163,
568 [0xBD] = 164,
569 [0xC0] = 165,
570 [0xC1] = 166,
571 [0xC2] = 167,
572 [0xC3] = 168,
573 [0xC4] = 169,
574 [0xC5] = 170,
575 [0xC6] = 171,
576 [0xC9] = 172,
577 [0xD0] = 173,
578 [0xD1] = 174,
579 [0xD6] = 175,
580 [0xD7] = 176,
581 [0xD9] = 177,
582 [0xDA] = 178,
583 [0xDB] = 179,
584 [0xDC] = 180,
585 [0xDD] = 181,
586 [0xDE] = 182,
587 [0xDF] = 183,
588 [0xE0] = 184,
589 [0xE1] = 185,
590 [0xE2] = 186,
591 [0xE3] = 187,
592 [0xE4] = 188,
593 [0xE5] = 189,
594 [0xE6] = 190,
595 [0xE7] = 191,
596 [0xE8] = 192,
597 [0xE9] = 193,
598 [0xEA] = 194,
599 [0xEB] = 195,
600 [0xEC] = 196,
601 [0xED] = 197,
602 [0xEE] = 198,
603 [0xEF] = 199,
604 [0xF0] = 200,
605 [0xF1] = 201,
606 [0xF2] = 202,
607 [0xF3] = 203,
608 [0xF4] = 204,
609 [0xF5] = 205,
610 [0xF6] = 206,
611 [0xF7] = 207,
612 [0xF8] = 208,
613 [0xF9] = 209,
614 [0xFA] = 210,
615 [0xFB] = 211,
616 [0xFC] = 212,
617 [0xFD] = 213,
618 [0xFE] = 214,
619 [0xFF] = 215,
621 int i;
622 int cmp;
623 int minlen = (k1->strlen < k2->strlen) ? k1->strlen : k2->strlen;
625 cmp = (grub_be_to_cpu32 (k1->parent_dir) - grub_be_to_cpu32 (k2->parent_dir));
626 if (cmp != 0)
627 return cmp;
629 for (i = 0; i < minlen; i++)
631 cmp = (hfs_charorder[k1->str[i]] - hfs_charorder[k2->str[i]]);
632 if (cmp != 0)
633 return cmp;
636 /* Shorter strings precede long ones. */
637 return (k1->strlen - k2->strlen);
641 /* Compare the K1 and K2 extent overflow file keys. */
642 static int
643 grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
644 struct grub_hfs_extent_key *k2)
646 int cmp = k1->forktype - k2->forktype;
647 if (cmp == 0)
648 cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
649 if (cmp == 0)
650 cmp = (grub_be_to_cpu16 (k1->first_block)
651 - grub_be_to_cpu16 (k2->first_block));
652 return cmp;
656 /* Iterate the records in the node with index IDX in the mounted HFS
657 filesystem DATA. This node holds data of the type TYPE (0 =
658 catalog node, 1 = extent overflow node). If this is set, continue
659 iterating to the next node. For every records, call NODE_HOOK. */
660 static grub_err_t
661 grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
662 int this, int (*node_hook) (struct grub_hfs_node *hnd,
663 struct grub_hfs_record *))
665 int nodesize = type == 0 ? data->cat_size : data->ext_size;
667 union
669 struct grub_hfs_node node;
670 char rawnode[nodesize];
671 grub_uint16_t offsets[nodesize / 2];
672 } node;
676 int i;
677 struct grub_hfs_extent *dat;
678 int blk;
680 dat = (struct grub_hfs_extent *) (type == 0
681 ? (&data->sblock.catalog_recs)
682 : (&data->sblock.extent_recs));
684 /* Read the node into memory. */
685 blk = grub_hfs_block (data, dat,
686 (type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT,
687 idx / (data->blksz / nodesize), 0);
688 blk += (idx % (data->blksz / nodesize));
689 if (grub_errno)
690 return grub_errno;
692 if (grub_disk_read (data->disk, blk, 0,
693 sizeof (node), &node))
694 return grub_errno;
696 /* Iterate over all records in this node. */
697 for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
699 int pos = (nodesize >> 1) - 1 - i;
700 struct pointer
702 grub_uint8_t keylen;
703 grub_uint8_t key;
704 } __attribute__ ((packed)) *pnt;
705 pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
706 + node.rawnode);
708 struct grub_hfs_record rec =
710 &pnt->key,
711 pnt->keylen,
712 &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
713 nodesize - grub_be_to_cpu16 (node.offsets[pos])
714 - pnt->keylen - 1
717 if (node_hook (&node.node, &rec))
718 return 0;
721 idx = grub_be_to_cpu32 (node.node.next);
722 } while (idx && this);
724 return 0;
728 /* Lookup a record in the mounted filesystem DATA using the key KEY.
729 The index of the node on top of the tree is IDX. The tree is of
730 the type TYPE (0 = catalog node, 1 = extent overflow node). Return
731 the data in DATAR with a maximum length of DATALEN. */
732 static int
733 grub_hfs_find_node (struct grub_hfs_data *data, char *key,
734 grub_uint32_t idx, int type, char *datar, int datalen)
736 int found = -1;
737 int isleaf = 0;
738 int done = 0;
740 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
742 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
744 int cmp = 1;
746 if (type == 0)
747 cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
748 else
749 cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
751 /* If the key is smaller or equal to the current node, mark the
752 entry. In case of a non-leaf mode it will be used to lookup
753 the rest of the tree. */
754 if (cmp <= 0)
755 found = grub_be_to_cpu32 (grub_get_unaligned32 (rec->data));
756 else /* The key can not be found in the tree. */
757 return 1;
759 /* Check if this node is a leaf node. */
760 if (hnd->type == GRUB_HFS_NODE_LEAF)
762 isleaf = 1;
764 /* Found it!!!! */
765 if (cmp == 0)
767 done = 1;
769 grub_memcpy (datar, rec->data,
770 rec->datalen < datalen ? rec->datalen : datalen);
771 return 1;
775 return 0;
780 found = -1;
782 if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
783 return 0;
785 if (found == -1)
786 return 0;
788 idx = found;
789 } while (! isleaf);
791 return done;
795 /* Iterate over the directory with the id DIR. The tree is searched
796 starting with the node ROOT_IDX. For every entry in this directory
797 call HOOK. */
798 static grub_err_t
799 grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
800 unsigned int dir, int (*hook) (struct grub_hfs_record *))
802 int found = -1;
803 int isleaf = 0;
804 int next = 0;
806 /* The lowest key possible with DIR as root directory. */
807 struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
809 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
810 auto int it_dir (struct grub_hfs_node * __attribute ((unused)),
811 struct grub_hfs_record *);
814 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
816 struct grub_hfs_catalog_key *ckey = rec->key;
818 if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
819 found = grub_be_to_cpu32 (grub_get_unaligned32 (rec->data));
821 if (hnd->type == 0xFF && ckey->strlen > 0)
823 isleaf = 1;
824 next = grub_be_to_cpu32 (hnd->next);
826 /* An entry was found. */
827 if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
828 return hook (rec);
831 return 0;
834 int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
835 struct grub_hfs_record *rec)
837 struct grub_hfs_catalog_key *ckey = rec->key;
838 struct grub_hfs_catalog_key *origkey = &key;
840 /* Stop when the entries do not match anymore. */
841 if (grub_be_to_cpu32 (ckey->parent_dir)
842 != grub_be_to_cpu32 ((origkey)->parent_dir))
843 return 1;
845 return hook (rec);
850 found = -1;
852 if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
853 return grub_errno;
855 if (found == -1)
856 return 0;
858 root_idx = found;
859 } while (! isleaf);
861 /* If there was a matching record in this leaf node, continue the
862 iteration until the last record was found. */
863 grub_hfs_iterate_records (data, 0, next, 1, it_dir);
864 return grub_errno;
867 #define MAX_UTF8_PER_MAC_ROMAN 3
869 static const char macroman[0x80][MAX_UTF8_PER_MAC_ROMAN + 1] =
871 /* 80 */ "\xc3\x84",
872 /* 81 */ "\xc3\x85",
873 /* 82 */ "\xc3\x87",
874 /* 83 */ "\xc3\x89",
875 /* 84 */ "\xc3\x91",
876 /* 85 */ "\xc3\x96",
877 /* 86 */ "\xc3\x9c",
878 /* 87 */ "\xc3\xa1",
879 /* 88 */ "\xc3\xa0",
880 /* 89 */ "\xc3\xa2",
881 /* 8A */ "\xc3\xa4",
882 /* 8B */ "\xc3\xa3",
883 /* 8C */ "\xc3\xa5",
884 /* 8D */ "\xc3\xa7",
885 /* 8E */ "\xc3\xa9",
886 /* 8F */ "\xc3\xa8",
887 /* 90 */ "\xc3\xaa",
888 /* 91 */ "\xc3\xab",
889 /* 92 */ "\xc3\xad",
890 /* 93 */ "\xc3\xac",
891 /* 94 */ "\xc3\xae",
892 /* 95 */ "\xc3\xaf",
893 /* 96 */ "\xc3\xb1",
894 /* 97 */ "\xc3\xb3",
895 /* 98 */ "\xc3\xb2",
896 /* 99 */ "\xc3\xb4",
897 /* 9A */ "\xc3\xb6",
898 /* 9B */ "\xc3\xb5",
899 /* 9C */ "\xc3\xba",
900 /* 9D */ "\xc3\xb9",
901 /* 9E */ "\xc3\xbb",
902 /* 9F */ "\xc3\xbc",
903 /* A0 */ "\xe2\x80\xa0",
904 /* A1 */ "\xc2\xb0",
905 /* A2 */ "\xc2\xa2",
906 /* A3 */ "\xc2\xa3",
907 /* A4 */ "\xc2\xa7",
908 /* A5 */ "\xe2\x80\xa2",
909 /* A6 */ "\xc2\xb6",
910 /* A7 */ "\xc3\x9f",
911 /* A8 */ "\xc2\xae",
912 /* A9 */ "\xc2\xa9",
913 /* AA */ "\xe2\x84\xa2",
914 /* AB */ "\xc2\xb4",
915 /* AC */ "\xc2\xa8",
916 /* AD */ "\xe2\x89\xa0",
917 /* AE */ "\xc3\x86",
918 /* AF */ "\xc3\x98",
919 /* B0 */ "\xe2\x88\x9e",
920 /* B1 */ "\xc2\xb1",
921 /* B2 */ "\xe2\x89\xa4",
922 /* B3 */ "\xe2\x89\xa5",
923 /* B4 */ "\xc2\xa5",
924 /* B5 */ "\xc2\xb5",
925 /* B6 */ "\xe2\x88\x82",
926 /* B7 */ "\xe2\x88\x91",
927 /* B8 */ "\xe2\x88\x8f",
928 /* B9 */ "\xcf\x80",
929 /* BA */ "\xe2\x88\xab",
930 /* BB */ "\xc2\xaa",
931 /* BC */ "\xc2\xba",
932 /* BD */ "\xce\xa9",
933 /* BE */ "\xc3\xa6",
934 /* BF */ "\xc3\xb8",
935 /* C0 */ "\xc2\xbf",
936 /* C1 */ "\xc2\xa1",
937 /* C2 */ "\xc2\xac",
938 /* C3 */ "\xe2\x88\x9a",
939 /* C4 */ "\xc6\x92",
940 /* C5 */ "\xe2\x89\x88",
941 /* C6 */ "\xe2\x88\x86",
942 /* C7 */ "\xc2\xab",
943 /* C8 */ "\xc2\xbb",
944 /* C9 */ "\xe2\x80\xa6",
945 /* CA */ "\xc2\xa0",
946 /* CB */ "\xc3\x80",
947 /* CC */ "\xc3\x83",
948 /* CD */ "\xc3\x95",
949 /* CE */ "\xc5\x92",
950 /* CF */ "\xc5\x93",
951 /* D0 */ "\xe2\x80\x93",
952 /* D1 */ "\xe2\x80\x94",
953 /* D2 */ "\xe2\x80\x9c",
954 /* D3 */ "\xe2\x80\x9d",
955 /* D4 */ "\xe2\x80\x98",
956 /* D5 */ "\xe2\x80\x99",
957 /* D6 */ "\xc3\xb7",
958 /* D7 */ "\xe2\x97\x8a",
959 /* D8 */ "\xc3\xbf",
960 /* D9 */ "\xc5\xb8",
961 /* DA */ "\xe2\x81\x84",
962 /* DB */ "\xe2\x82\xac",
963 /* DC */ "\xe2\x80\xb9",
964 /* DD */ "\xe2\x80\xba",
965 /* DE */ "\xef\xac\x81",
966 /* DF */ "\xef\xac\x82",
967 /* E0 */ "\xe2\x80\xa1",
968 /* E1 */ "\xc2\xb7",
969 /* E2 */ "\xe2\x80\x9a",
970 /* E3 */ "\xe2\x80\x9e",
971 /* E4 */ "\xe2\x80\xb0",
972 /* E5 */ "\xc3\x82",
973 /* E6 */ "\xc3\x8a",
974 /* E7 */ "\xc3\x81",
975 /* E8 */ "\xc3\x8b",
976 /* E9 */ "\xc3\x88",
977 /* EA */ "\xc3\x8d",
978 /* EB */ "\xc3\x8e",
979 /* EC */ "\xc3\x8f",
980 /* ED */ "\xc3\x8c",
981 /* EE */ "\xc3\x93",
982 /* EF */ "\xc3\x94",
983 /* F0 */ "\xef\xa3\xbf",
984 /* F1 */ "\xc3\x92",
985 /* F2 */ "\xc3\x9a",
986 /* F3 */ "\xc3\x9b",
987 /* F4 */ "\xc3\x99",
988 /* F5 */ "\xc4\xb1",
989 /* F6 */ "\xcb\x86",
990 /* F7 */ "\xcb\x9c",
991 /* F8 */ "\xc2\xaf",
992 /* F9 */ "\xcb\x98",
993 /* FA */ "\xcb\x99",
994 /* FB */ "\xcb\x9a",
995 /* FC */ "\xc2\xb8",
996 /* FD */ "\xcb\x9d",
997 /* FE */ "\xcb\x9b",
998 /* FF */ "\xcb\x87",
1001 static void
1002 macroman_to_utf8 (char *to, const grub_uint8_t *from, grub_size_t len,
1003 int translate_slash)
1005 char *optr = to;
1006 const grub_uint8_t *iptr;
1008 for (iptr = from; iptr < from + len && *iptr; iptr++)
1010 /* Translate '/' to ':' as per HFS spec. */
1011 if (*iptr == '/' && translate_slash)
1013 *optr++ = ':';
1014 continue;
1016 if (!(*iptr & 0x80))
1018 *optr++ = *iptr;
1019 continue;
1021 optr = grub_stpcpy (optr, macroman[*iptr & 0x7f]);
1023 *optr = 0;
1026 static grub_ssize_t
1027 utf8_to_macroman (grub_uint8_t *to, const char *from)
1029 grub_uint8_t *end = to + 31;
1030 grub_uint8_t *optr = to;
1031 const char *iptr = from;
1033 while (*iptr && optr < end)
1035 int i, clen;
1036 /* Translate ':' to '/' as per HFS spec. */
1037 if (*iptr == ':')
1039 *optr++ = '/';
1040 iptr++;
1041 continue;
1043 if (!(*iptr & 0x80))
1045 *optr++ = *iptr++;
1046 continue;
1048 clen = 2;
1049 if ((*iptr & 0xf0) == 0xe0)
1050 clen++;
1051 for (i = 0; i < 0x80; i++)
1052 if (grub_memcmp (macroman[i], iptr, clen) == 0)
1053 break;
1054 if (i == 0x80)
1055 break;
1056 *optr++ = i | 0x80;
1057 iptr += clen;
1059 /* Too long or not encodable. */
1060 if (*iptr)
1061 return -1;
1062 return optr - to;
1066 /* Find a file or directory with the pathname PATH in the filesystem
1067 DATA. Return the file record in RETDATA when it is non-zero.
1068 Return the directory number in RETINODE when it is non-zero. */
1069 static grub_err_t
1070 grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
1071 struct grub_hfs_filerec *retdata, int *retinode)
1073 int inode = data->rootdir;
1074 char *next;
1075 char *origpath;
1076 union {
1077 struct grub_hfs_filerec frec;
1078 struct grub_hfs_dirrec dir;
1079 } fdrec;
1081 fdrec.frec.type = GRUB_HFS_FILETYPE_DIR;
1083 if (path[0] != '/')
1085 grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path);
1086 return 0;
1089 origpath = grub_strdup (path);
1090 if (!origpath)
1091 return grub_errno;
1093 path = origpath;
1094 while (*path == '/')
1095 path++;
1097 while (path && grub_strlen (path))
1099 grub_ssize_t slen;
1100 if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR)
1102 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
1103 goto fail;
1106 /* Isolate a part of the path. */
1107 next = grub_strchr (path, '/');
1108 if (next)
1110 while (*next == '/')
1111 *(next++) = '\0';
1114 struct grub_hfs_catalog_key key;
1116 key.parent_dir = grub_cpu_to_be32 (inode);
1117 slen = utf8_to_macroman (key.str, path);
1118 if (slen < 0)
1120 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path);
1121 goto fail;
1123 key.strlen = slen;
1125 /* Lookup this node. */
1126 if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
1127 0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
1129 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1130 goto fail;
1133 if (grub_errno)
1134 goto fail;
1136 inode = grub_be_to_cpu32 (fdrec.dir.dirid);
1137 path = next;
1140 if (retdata)
1141 grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec));
1143 if (retinode)
1144 *retinode = inode;
1146 fail:
1147 grub_free (origpath);
1148 return grub_errno;
1153 static grub_err_t
1154 grub_hfs_dir (grub_device_t device, const char *path,
1155 int (*hook) (const char *filename,
1156 const struct grub_dirhook_info *info))
1158 int inode;
1160 auto int dir_hook (struct grub_hfs_record *rec);
1162 int dir_hook (struct grub_hfs_record *rec)
1164 struct grub_hfs_dirrec *drec = rec->data;
1165 struct grub_hfs_filerec *frec = rec->data;
1166 struct grub_hfs_catalog_key *ckey = rec->key;
1167 char fname[sizeof (ckey->str) * MAX_UTF8_PER_MAC_ROMAN + 1];
1168 struct grub_dirhook_info info;
1169 grub_size_t len;
1171 grub_memset (fname, 0, sizeof (fname));
1173 grub_memset (&info, 0, sizeof (info));
1175 len = ckey->strlen;
1176 if (len > sizeof (ckey->str))
1177 len = sizeof (ckey->str);
1178 macroman_to_utf8 (fname, ckey->str, len, 1);
1180 info.case_insensitive = 1;
1182 if (drec->type == GRUB_HFS_FILETYPE_DIR)
1184 info.dir = 1;
1185 info.mtimeset = 1;
1186 info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800;
1187 return hook (fname, &info);
1189 if (frec->type == GRUB_HFS_FILETYPE_FILE)
1191 info.dir = 0;
1192 info.mtimeset = 1;
1193 info.mtime = grub_be_to_cpu32 (frec->mtime) - 2082844800;
1194 return hook (fname, &info);
1197 return 0;
1200 struct grub_hfs_data *data;
1201 struct grub_hfs_filerec frec;
1203 grub_dl_ref (my_mod);
1205 data = grub_hfs_mount (device->disk);
1206 if (!data)
1207 goto fail;
1209 /* First the directory ID for the directory. */
1210 if (grub_hfs_find_dir (data, path, &frec, &inode))
1211 goto fail;
1213 if (frec.type != GRUB_HFS_FILETYPE_DIR)
1215 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
1216 goto fail;
1219 grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
1221 fail:
1222 grub_free (data);
1224 grub_dl_unref (my_mod);
1226 return grub_errno;
1230 /* Open a file named NAME and initialize FILE. */
1231 static grub_err_t
1232 grub_hfs_open (struct grub_file *file, const char *name)
1234 struct grub_hfs_data *data;
1235 struct grub_hfs_filerec frec;
1237 grub_dl_ref (my_mod);
1239 data = grub_hfs_mount (file->device->disk);
1241 if (grub_hfs_find_dir (data, name, &frec, 0))
1243 grub_free (data);
1244 grub_dl_unref (my_mod);
1245 return grub_errno;
1248 if (frec.type != GRUB_HFS_FILETYPE_FILE)
1250 grub_free (data);
1251 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
1252 grub_dl_unref (my_mod);
1253 return grub_errno;
1256 grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
1257 file->size = grub_be_to_cpu32 (frec.size);
1258 data->size = grub_be_to_cpu32 (frec.size);
1259 data->fileid = grub_be_to_cpu32 (frec.fileid);
1260 file->offset = 0;
1262 file->data = data;
1264 return 0;
1267 static grub_ssize_t
1268 grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
1270 struct grub_hfs_data *data =
1271 (struct grub_hfs_data *) file->data;
1273 return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
1277 static grub_err_t
1278 grub_hfs_close (grub_file_t file)
1280 grub_free (file->data);
1282 grub_dl_unref (my_mod);
1284 return 0;
1288 static grub_err_t
1289 grub_hfs_label (grub_device_t device, char **label)
1291 struct grub_hfs_data *data;
1293 data = grub_hfs_mount (device->disk);
1295 if (data)
1297 grub_size_t len = data->sblock.volname[0];
1298 if (len > sizeof (data->sblock.volname) - 1)
1299 len = sizeof (data->sblock.volname) - 1;
1300 *label = grub_malloc (len * MAX_UTF8_PER_MAC_ROMAN + 1);
1301 if (*label)
1302 macroman_to_utf8 (*label, data->sblock.volname + 1,
1303 len + 1, 0);
1305 else
1306 *label = 0;
1308 grub_free (data);
1309 return grub_errno;
1312 static grub_err_t
1313 grub_hfs_mtime (grub_device_t device, grub_int32_t *tm)
1315 struct grub_hfs_data *data;
1317 data = grub_hfs_mount (device->disk);
1319 if (data)
1320 *tm = grub_be_to_cpu32 (data->sblock.mtime) - 2082844800;
1321 else
1322 *tm = 0;
1324 grub_free (data);
1325 return grub_errno;
1328 static grub_err_t
1329 grub_hfs_uuid (grub_device_t device, char **uuid)
1331 struct grub_hfs_data *data;
1333 grub_dl_ref (my_mod);
1335 data = grub_hfs_mount (device->disk);
1336 if (data && data->sblock.num_serial != 0)
1338 *uuid = grub_xasprintf ("%016llx",
1339 (unsigned long long)
1340 grub_be_to_cpu64 (data->sblock.num_serial));
1342 else
1343 *uuid = NULL;
1345 grub_dl_unref (my_mod);
1347 grub_free (data);
1349 return grub_errno;
1354 static struct grub_fs grub_hfs_fs =
1356 .name = "hfs",
1357 .dir = grub_hfs_dir,
1358 .open = grub_hfs_open,
1359 .read = grub_hfs_read,
1360 .close = grub_hfs_close,
1361 .label = grub_hfs_label,
1362 .uuid = grub_hfs_uuid,
1363 .mtime = grub_hfs_mtime,
1364 #ifdef GRUB_UTIL
1365 .reserved_first_sector = 1,
1366 .blocklist_install = 1,
1367 #endif
1368 .next = 0
1371 GRUB_MOD_INIT(hfs)
1373 grub_fs_register (&grub_hfs_fs);
1374 my_mod = mod;
1377 GRUB_MOD_FINI(hfs)
1379 grub_fs_unregister (&grub_hfs_fs);