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 */
24 #include <grub/file.h>
26 #include <grub/misc.h>
27 #include <grub/disk.h>
29 #include <grub/types.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. */
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,
59 /* A node descriptor. This is the header of every node. */
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
;
77 grub_uint32_t first_leaf
;
78 grub_uint32_t last_leaf
;
79 grub_uint16_t node_size
;
80 grub_uint16_t key_size
;
82 grub_uint32_t free_nodes
;
83 grub_uint8_t unused
[76];
86 /* The state of a mounted HFS filesystem. */
89 struct grub_hfs_sblock sblock
;
91 grub_hfs_datarecord_t extents
;
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
108 grub_uint32_t parent_dir
;
110 /* Filename length. */
114 grub_uint8_t str
[31];
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
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
125 grub_uint8_t forktype
;
126 grub_uint32_t fileid
;
127 grub_uint16_t first_block
;
130 /* A directory record. This is used to find out the directory ID. */
131 struct grub_hfs_dirrec
133 /* For a directory, type == 1. */
135 grub_uint8_t unused
[5];
141 /* Information about a file. */
142 struct grub_hfs_filerec
144 /* For a file, type == 2. */
146 grub_uint8_t unused
[19];
147 grub_uint32_t fileid
;
148 grub_uint8_t unused2
[2];
150 grub_uint8_t unused3
[18];
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
;
159 /* A record descriptor, both key and data, used to pass to call back
161 struct grub_hfs_record
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. */
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
;
183 struct grub_hfs_extent_key key
;
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
));
193 key
.fileid
= grub_cpu_to_be32 (file
);
195 if (cache
&& cache_file
== file
&& block
> cache_pos
)
198 key
.first_block
= grub_cpu_to_be16 (pos
);
199 grub_memcpy (dr
, cache_dr
, sizeof (cache_dr
));
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
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
);
234 grub_hfs_find_node (data
, (char *) &key
, data
->ext_root
,
235 1, (char *) &dr
, sizeof (dr
));
242 /* Read LEN bytes from the file described by DATA starting with byte
243 POS. Return the amount of read bytes in READ. */
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
)
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
;
260 grub_off_t blockend
= data
->blksz
;
264 blockoff
= pos
% data
->blksz
;
266 blknr
= grub_hfs_block (data
, data
->extents
, data
->fileid
, i
, 1);
271 if (i
== blockcnt
- 1)
273 blockend
= (len
+ pos
) % data
->blksz
;
275 /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
277 blockend
= data
->blksz
;
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. */
291 data
->disk
->read_hook
= read_hook
;
292 data
->disk
->read_hook_data
= read_hook_data
;
293 grub_disk_read (data
->disk
, blknr
, skipfirst
,
295 data
->disk
->read_hook
= 0;
300 buf
+= data
->blksz
- skipfirst
;
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
;
318 struct grub_hfs_node node
;
319 struct grub_hfs_treeheader head
;
322 data
= grub_malloc (sizeof (struct grub_hfs_data
));
326 /* Read the superblock. */
327 if (grub_disk_read (disk
, GRUB_HFS_SBLOCK
, 0,
328 sizeof (struct grub_hfs_sblock
), &data
->sblock
))
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");
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");
347 data
->blksz
= grub_be_to_cpu32 (data
->sblock
.blksz
);
350 /* Lookup the root node of the extent overflow tree. */
351 first_block
= ((grub_be_to_cpu16 (data
->sblock
.extent_recs
[0].first_block
)
353 + grub_be_to_cpu16 (data
->sblock
.first_block
));
355 if (grub_disk_read (data
->disk
, first_block
, 0,
356 sizeof (treehead
), &treehead
))
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
)
364 + grub_be_to_cpu16 (data
->sblock
.first_block
));
365 if (grub_disk_read (data
->disk
, first_block
, 0,
366 sizeof (treehead
), &treehead
))
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
)
376 /* Lookup the root directory node in the catalog tree using the
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");
392 data
->rootdir
= grub_be_to_cpu32 (dir
.dirid
);
398 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
399 grub_error (GRUB_ERR_BAD_FS
, "not a HFS filesystem");
404 /* Compare the K1 and K2 catalog file keys using HFS character ordering. */
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] = {
443 [' '] = 32, [0xCA] = 32,
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,
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,
501 ['F'] = 90, ['f'] = 90,
502 ['G'] = 91, ['g'] = 91,
503 ['H'] = 92, ['h'] = 92,
504 ['I'] = 93, ['i'] = 93,
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,
524 ['P'] = 113, ['p'] = 113,
525 ['Q'] = 114, ['q'] = 114,
526 ['R'] = 115, ['r'] = 115,
527 ['S'] = 116, ['s'] = 116,
529 ['T'] = 118, ['t'] = 118,
530 ['U'] = 119, ['u'] = 119,
531 [0x86] = 120, [0x9F] = 120,
535 ['V'] = 124, ['v'] = 124,
536 ['W'] = 125, ['w'] = 125,
537 ['X'] = 126, ['x'] = 126,
538 ['Y'] = 127, ['y'] = 127,
540 ['Z'] = 129, ['z'] = 129,
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
));
636 for (i
= 0; i
< minlen
; i
++)
638 cmp
= (hfs_charorder
[k1
->str
[i
]] - hfs_charorder
[k2
->str
[i
]]);
643 /* Shorter strings precede long ones. */
644 return (k1
->strlen
- k2
->strlen
);
648 /* Compare the K1 and K2 extent overflow file keys. */
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
;
655 cmp
= grub_be_to_cpu32 (k1
->fileid
) - grub_be_to_cpu32 (k2
->fileid
);
657 cmp
= (grub_be_to_cpu16 (k1
->first_block
)
658 - grub_be_to_cpu16 (k2
->first_block
));
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. */
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
*,
674 grub_size_t nodesize
= type
== 0 ? data
->cat_size
: data
->ext_size
;
678 struct grub_hfs_node node
;
680 grub_uint16_t offsets
[0];
683 if (nodesize
< sizeof (struct grub_hfs_node
))
684 nodesize
= sizeof (struct grub_hfs_node
);
686 node
= grub_malloc (nodesize
);
693 struct grub_hfs_extent
*dat
;
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,
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
;
727 grub_uint16_t off
= grub_be_to_cpu16 (node
->offsets
[pos
]);
728 if (off
> nodesize
- sizeof(*pnt
))
730 pnt
= (struct pointer
*) (off
+ node
->rawnode
);
731 if (nodesize
< (grub_size_t
) off
+ pnt
->keylen
+ 1)
734 struct grub_hfs_record rec
=
738 &pnt
->key
+ pnt
->keylen
+(pnt
->keylen
+ 1) % 2,
739 nodesize
- off
- pnt
->keylen
- 1
742 if (node_hook (&node
->node
, &rec
, hook_arg
))
749 idx
= grub_be_to_cpu32 (node
->node
.next
);
750 } while (idx
&& this);
755 struct grub_hfs_find_node_node_found_ctx
767 grub_hfs_find_node_node_found (struct grub_hfs_node
*hnd
, struct grub_hfs_record
*rec
,
770 struct grub_hfs_find_node_node_found_ctx
*ctx
= hook_arg
;
774 cmp
= grub_hfs_cmp_catkeys (rec
->key
, (const void *) ctx
->key
);
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. */
782 ctx
->found
= grub_be_to_cpu32 (grub_get_unaligned32 (rec
->data
));
783 else /* The key can not be found in the tree. */
786 /* Check if this node is a leaf node. */
787 if (hnd
->type
== GRUB_HFS_NODE_LEAF
)
796 grub_memcpy (ctx
->datar
, rec
->data
,
797 rec
->datalen
< ctx
->datalen
? rec
->datalen
: ctx
->datalen
);
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. */
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
=
829 if (grub_hfs_iterate_records (data
, type
, idx
, 0, grub_hfs_find_node_node_found
, &ctx
))
836 } while (! ctx
.isleaf
);
841 struct grub_hfs_iterate_dir_node_found_ctx
843 grub_uint32_t dir_be
;
847 int (*hook
) (struct grub_hfs_record
*, void *hook_arg
);
852 grub_hfs_iterate_dir_node_found (struct grub_hfs_node
*hnd
, struct grub_hfs_record
*rec
,
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)
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
);
878 grub_hfs_iterate_dir_it_dir (struct grub_hfs_node
*hnd
__attribute ((unused
)),
879 struct grub_hfs_record
*rec
,
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
)
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
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
),
901 struct grub_hfs_iterate_dir_node_found_ctx ctx
=
903 .dir_be
= grub_cpu_to_be32 (dir
),
915 if (grub_hfs_iterate_records (data
, 0, root_idx
, 0, grub_hfs_iterate_dir_node_found
, &ctx
))
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
);
930 #define MAX_UTF8_PER_MAC_ROMAN 3
932 static const char macroman
[0x80][MAX_UTF8_PER_MAC_ROMAN
+ 1] =
966 /* A0 */ "\xe2\x80\xa0",
971 /* A5 */ "\xe2\x80\xa2",
976 /* AA */ "\xe2\x84\xa2",
979 /* AD */ "\xe2\x89\xa0",
982 /* B0 */ "\xe2\x88\x9e",
984 /* B2 */ "\xe2\x89\xa4",
985 /* B3 */ "\xe2\x89\xa5",
988 /* B6 */ "\xe2\x88\x82",
989 /* B7 */ "\xe2\x88\x91",
990 /* B8 */ "\xe2\x88\x8f",
992 /* BA */ "\xe2\x88\xab",
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",
1065 macroman_to_utf8 (char *to
, const grub_uint8_t
*from
, grub_size_t len
,
1066 int translate_slash
)
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
)
1079 if (!(*iptr
& 0x80))
1084 optr
= grub_stpcpy (optr
, macroman
[*iptr
& 0x7f]);
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
)
1099 /* Translate ':' to '/' as per HFS spec. */
1106 if (!(*iptr
& 0x80))
1112 if ((*iptr
& 0xf0) == 0xe0)
1114 for (i
= 0; i
< 0x80; i
++)
1115 if (grub_memcmp (macroman
[i
], iptr
, clen
) == 0)
1122 /* Too long or not encodable. */
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. */
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
;
1140 struct grub_hfs_filerec frec
;
1141 struct grub_hfs_dirrec dir
;
1144 fdrec
.frec
.type
= GRUB_HFS_FILETYPE_DIR
;
1148 grub_error (GRUB_ERR_BAD_FILENAME
, N_("invalid file name `%s'"), path
);
1152 origpath
= grub_strdup (path
);
1157 while (*path
== '/')
1160 while (path
&& grub_strlen (path
))
1163 if (fdrec
.frec
.type
!= GRUB_HFS_FILETYPE_DIR
)
1165 grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("not a directory"));
1169 /* Isolate a part of the path. */
1170 next
= grub_strchr (path
, '/');
1173 while (*next
== '/')
1177 struct grub_hfs_catalog_key key
;
1179 key
.parent_dir
= grub_cpu_to_be32 (inode
);
1180 slen
= utf8_to_macroman (key
.str
, path
);
1183 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("file `%s' not found"), path
);
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
);
1199 inode
= grub_be_to_cpu32 (fdrec
.dir
.dirid
);
1204 grub_memcpy (retdata
, &fdrec
.frec
, sizeof (fdrec
.frec
));
1210 grub_free (origpath
);
1214 struct grub_hfs_dir_hook_ctx
1216 grub_fs_dir_hook_t hook
;
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
;
1231 grub_memset (fname
, 0, sizeof (fname
));
1233 grub_memset (&info
, 0, sizeof (info
));
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
)
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
)
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
);
1266 grub_hfs_dir (grub_device_t device
, const char *path
, grub_fs_dir_hook_t hook
,
1271 struct grub_hfs_data
*data
;
1272 struct grub_hfs_filerec frec
;
1273 struct grub_hfs_dir_hook_ctx ctx
=
1276 .hook_data
= hook_data
1279 grub_dl_ref (my_mod
);
1281 data
= grub_hfs_mount (device
->disk
);
1285 /* First the directory ID for the directory. */
1286 if (grub_hfs_find_dir (data
, path
, &frec
, &inode
))
1289 if (frec
.type
!= GRUB_HFS_FILETYPE_DIR
)
1291 grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("not a directory"));
1295 grub_hfs_iterate_dir (data
, data
->cat_root
, inode
, grub_hfs_dir_hook
, &ctx
);
1300 grub_dl_unref (my_mod
);
1306 /* Open a file named NAME and initialize FILE. */
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
);
1319 grub_dl_unref (my_mod
);
1323 if (grub_hfs_find_dir (data
, name
, &frec
, 0))
1326 grub_dl_unref (my_mod
);
1330 if (frec
.type
!= GRUB_HFS_FILETYPE_FILE
)
1333 grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("not a regular file"));
1334 grub_dl_unref (my_mod
);
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
);
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
);
1361 grub_hfs_close (grub_file_t file
)
1363 grub_free (file
->data
);
1365 grub_dl_unref (my_mod
);
1372 grub_hfs_label (grub_device_t device
, char **label
)
1374 struct grub_hfs_data
*data
;
1376 data
= grub_hfs_mount (device
->disk
);
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);
1385 macroman_to_utf8 (*label
, data
->sblock
.volname
+ 1,
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
);
1403 *tm
= grub_be_to_cpu32 (data
->sblock
.mtime
) - 2082844800;
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
));
1428 grub_dl_unref (my_mod
);
1437 static struct grub_fs grub_hfs_fs
=
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
,
1448 .reserved_first_sector
= 1,
1449 .blocklist_install
= 1,
1456 grub_fs_register (&grub_hfs_fs
);
1462 grub_fs_unregister (&grub_hfs_fs
);