3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008 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>
32 #define GRUB_HFS_SBLOCK 2
33 #define GRUB_HFS_EMBED_HFSPLUS_SIG 0x482B
35 #define GRUB_HFS_BLKS (data->blksz >> 9)
37 #define GRUB_HFS_NODE_LEAF 0xFF
39 /* The two supported filesystems a record can have. */
42 GRUB_HFS_FILETYPE_DIR
= 1,
43 GRUB_HFS_FILETYPE_FILE
= 2
46 /* Catalog node ID (CNID). */
47 enum grub_hfs_cnid_type
49 GRUB_HFS_CNID_ROOT_PARENT
= 1,
50 GRUB_HFS_CNID_ROOT
= 2,
51 GRUB_HFS_CNID_EXT
= 3,
52 GRUB_HFS_CNID_CAT
= 4,
56 /* A node descriptor. This is the header of every node. */
65 } __attribute__ ((packed
));
67 /* The head of the B*-Tree. */
68 struct grub_hfs_treeheader
70 grub_uint16_t tree_depth
;
71 /* The number of the first node. */
72 grub_uint32_t root_node
;
74 grub_uint32_t first_leaf
;
75 grub_uint32_t last_leaf
;
76 grub_uint16_t node_size
;
77 grub_uint16_t key_size
;
79 grub_uint32_t free_nodes
;
80 grub_uint8_t unused
[76];
81 } __attribute__ ((packed
));
83 /* The state of a mounted HFS filesystem. */
86 struct grub_hfs_sblock sblock
;
88 grub_hfs_datarecord_t extents
;
100 /* The key as used on disk in a catalog tree. This is used to lookup
101 file/directory nodes by parent directory ID and filename. */
102 struct grub_hfs_catalog_key
105 grub_uint32_t parent_dir
;
107 /* Filename length. */
111 grub_uint8_t str
[31];
112 } __attribute__ ((packed
));
114 /* The key as used on disk in a extent overflow tree. Using this key
115 the extents can be looked up using a fileid and logical start block
117 struct grub_hfs_extent_key
119 /* The kind of fork. This is used to store meta information like
120 icons, attributes, etc. We will only use the datafork, which is
122 grub_uint8_t forktype
;
123 grub_uint32_t fileid
;
124 grub_uint16_t first_block
;
125 } __attribute__ ((packed
));
127 /* A directory record. This is used to find out the directory ID. */
128 struct grub_hfs_dirrec
130 /* For a directory, type == 1. */
132 grub_uint8_t unused
[5];
134 } __attribute__ ((packed
));
136 /* Information about a file. */
137 struct grub_hfs_filerec
139 /* For a file, type == 2. */
141 grub_uint8_t unused
[19];
142 grub_uint32_t fileid
;
143 grub_uint8_t unused2
[2];
145 grub_uint8_t unused3
[44];
147 /* The first 3 extents of the file. The other extents can be found
148 in the extent overflow file. */
149 grub_hfs_datarecord_t extents
;
150 } __attribute__ ((packed
));
152 /* A record descriptor, both key and data, used to pass to call back
154 struct grub_hfs_record
162 static grub_dl_t my_mod
;
164 static int grub_hfs_find_node (struct grub_hfs_data
*, char *,
165 grub_uint32_t
, int, char *, int);
167 /* Find block BLOCK of the file FILE in the mounted UFS filesystem
168 DATA. The first 3 extents are described by DAT. If cache is set,
169 using caching to improve non-random reads. */
171 grub_hfs_block (struct grub_hfs_data
*data
, grub_hfs_datarecord_t dat
,
172 int file
, int block
, int cache
)
174 grub_hfs_datarecord_t dr
;
176 struct grub_hfs_extent_key key
;
179 static int cache_file
= 0;
180 static int cache_pos
= 0;
181 static grub_hfs_datarecord_t cache_dr
;
183 grub_memcpy (dr
, dat
, sizeof (dr
));
186 key
.fileid
= grub_cpu_to_be32 (file
);
188 if (cache
&& cache_file
== file
&& block
> cache_pos
)
191 key
.first_block
= grub_cpu_to_be16 (pos
);
192 grub_memcpy (dr
, cache_dr
, sizeof (cache_dr
));
199 /* Try all 3 extents. */
200 for (i
= 0; i
< 3; i
++)
202 /* Check if the block is stored in this extent. */
203 if (grub_be_to_cpu16 (dr
[i
].count
) + pos
> block
)
205 int first
= grub_be_to_cpu16 (dr
[i
].first_block
);
207 /* If the cache is enabled, store the current position
213 grub_memcpy (cache_dr
, dr
, sizeof (cache_dr
));
216 return (grub_be_to_cpu16 (data
->sblock
.first_block
)
217 + (first
+ block
- pos
) * GRUB_HFS_BLKS
);
220 /* Try the next extent. */
221 pos
+= grub_be_to_cpu16 (dr
[i
].count
);
224 /* Lookup the block in the extent overflow file. */
225 key
.first_block
= grub_cpu_to_be16 (pos
);
227 grub_hfs_find_node (data
, (char *) &key
, data
->ext_root
,
228 1, (char *) &dr
, sizeof (dr
));
235 /* Read LEN bytes from the file described by DATA starting with byte
236 POS. Return the amount of read bytes in READ. */
238 grub_hfs_read_file (struct grub_hfs_data
*data
,
239 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
240 unsigned offset
, unsigned length
),
241 int pos
, grub_size_t len
, char *buf
)
246 /* Adjust len so it we can't read past the end of the file. */
247 if (len
> grub_le_to_cpu32 (data
->size
))
248 len
= grub_le_to_cpu32 (data
->size
);
250 blockcnt
= ((len
+ pos
)
251 + data
->blksz
- 1) / data
->blksz
;
253 for (i
= pos
/ data
->blksz
; i
< blockcnt
; i
++)
256 int blockoff
= pos
% data
->blksz
;
257 int blockend
= data
->blksz
;
261 blknr
= grub_hfs_block (data
, data
->extents
, data
->fileid
, i
, 1);
266 if (i
== blockcnt
- 1)
268 blockend
= (len
+ pos
) % data
->blksz
;
270 /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
272 blockend
= data
->blksz
;
276 if (i
== pos
/ data
->blksz
)
278 skipfirst
= blockoff
;
279 blockend
-= skipfirst
;
282 /* If the block number is 0 this block is not stored on disk but
283 is zero filled instead. */
286 data
->disk
->read_hook
= read_hook
;
287 grub_disk_read (data
->disk
, blknr
, skipfirst
,
289 data
->disk
->read_hook
= 0;
294 buf
+= data
->blksz
- skipfirst
;
301 /* Mount the filesystem on the disk DISK. */
302 static struct grub_hfs_data
*
303 grub_hfs_mount (grub_disk_t disk
)
305 struct grub_hfs_data
*data
;
306 struct grub_hfs_catalog_key key
;
307 struct grub_hfs_dirrec dir
;
312 struct grub_hfs_node node
;
313 struct grub_hfs_treeheader head
;
316 data
= grub_malloc (sizeof (struct grub_hfs_data
));
320 /* Read the superblock. */
321 if (grub_disk_read (disk
, GRUB_HFS_SBLOCK
, 0,
322 sizeof (struct grub_hfs_sblock
), &data
->sblock
))
325 /* Check if this is a HFS filesystem. */
326 if (grub_be_to_cpu16 (data
->sblock
.magic
) != GRUB_HFS_MAGIC
)
328 grub_error (GRUB_ERR_BAD_FS
, "not an HFS filesystem");
332 /* Check if this is an embedded HFS+ filesystem. */
333 if (grub_be_to_cpu16 (data
->sblock
.embed_sig
) == GRUB_HFS_EMBED_HFSPLUS_SIG
)
335 grub_error (GRUB_ERR_BAD_FS
, "embedded HFS+ filesystem");
339 data
->blksz
= grub_be_to_cpu32 (data
->sblock
.blksz
);
342 /* Lookup the root node of the extent overflow tree. */
343 first_block
= ((grub_be_to_cpu16 (data
->sblock
.extent_recs
[0].first_block
)
345 + grub_be_to_cpu16 (data
->sblock
.first_block
));
347 if (grub_disk_read (data
->disk
, first_block
, 0,
348 sizeof (treehead
), &treehead
))
350 data
->ext_root
= grub_be_to_cpu32 (treehead
.head
.root_node
);
351 data
->ext_size
= grub_be_to_cpu16 (treehead
.head
.node_size
);
353 /* Lookup the root node of the catalog tree. */
354 first_block
= ((grub_be_to_cpu16 (data
->sblock
.catalog_recs
[0].first_block
)
356 + grub_be_to_cpu16 (data
->sblock
.first_block
));
357 if (grub_disk_read (data
->disk
, first_block
, 0,
358 sizeof (treehead
), &treehead
))
360 data
->cat_root
= grub_be_to_cpu32 (treehead
.head
.root_node
);
361 data
->cat_size
= grub_be_to_cpu16 (treehead
.head
.node_size
);
363 /* Lookup the root directory node in the catalog tree using the
365 key
.parent_dir
= grub_cpu_to_be32 (1);
366 key
.strlen
= data
->sblock
.volname
[0];
367 grub_strcpy ((char *) key
.str
, (char *) (data
->sblock
.volname
+ 1));
369 if (grub_hfs_find_node (data
, (char *) &key
, data
->cat_root
,
370 0, (char *) &dir
, sizeof (dir
)) == 0)
372 grub_error (GRUB_ERR_BAD_FS
, "can not find the hfs root directory");
379 data
->rootdir
= grub_be_to_cpu32 (dir
.dirid
);
385 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
386 grub_error (GRUB_ERR_BAD_FS
, "not a hfs filesystem");
391 /* Compare the K1 and K2 catalog file keys using HFS character ordering. */
393 grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key
*k1
,
394 struct grub_hfs_catalog_key
*k2
)
396 /* Taken from hfsutils 3.2.6 and converted to a readable form */
397 static const unsigned char hfs_charorder
[256] = {
430 [' '] = 32, [0xCA] = 32,
469 ['A'] = 71, ['a'] = 71,
470 [0x88] = 72, [0xCB] = 72,
471 [0x80] = 73, [0x8A] = 73,
472 [0x8B] = 74, [0xCC] = 74,
473 [0x81] = 75, [0x8C] = 75,
474 [0xAE] = 76, [0xBE] = 76,
479 ['B'] = 81, ['b'] = 81,
480 ['C'] = 82, ['c'] = 82,
481 [0x82] = 83, [0x8D] = 83,
482 ['D'] = 84, ['d'] = 84,
483 ['E'] = 85, ['e'] = 85,
484 [0x83] = 86, [0x8E] = 86,
488 ['F'] = 90, ['f'] = 90,
489 ['G'] = 91, ['g'] = 91,
490 ['H'] = 92, ['h'] = 92,
491 ['I'] = 93, ['i'] = 93,
496 ['J'] = 98, ['j'] = 98,
497 ['K'] = 99, ['k'] = 99,
498 ['L'] = 100, ['l'] = 100,
499 ['M'] = 101, ['m'] = 101,
500 ['N'] = 102, ['n'] = 102,
501 [0x84] = 103, [0x96] = 103,
502 ['O'] = 104, ['o'] = 104,
503 [0x85] = 105, [0x9A] = 105,
504 [0x9B] = 106, [0xCD] = 106,
505 [0xAF] = 107, [0xBF] = 107,
506 [0xCE] = 108, [0xCF] = 108,
511 ['P'] = 113, ['p'] = 113,
512 ['Q'] = 114, ['q'] = 114,
513 ['R'] = 115, ['r'] = 115,
514 ['S'] = 116, ['s'] = 116,
516 ['T'] = 118, ['t'] = 118,
517 ['U'] = 119, ['u'] = 119,
518 [0x86] = 120, [0x9F] = 120,
522 ['V'] = 124, ['v'] = 124,
523 ['W'] = 125, ['w'] = 125,
524 ['X'] = 126, ['x'] = 126,
525 ['Y'] = 127, ['y'] = 127,
527 ['Z'] = 129, ['z'] = 129,
617 int minlen
= (k1
->strlen
< k2
->strlen
) ? k1
->strlen
: k2
->strlen
;
619 cmp
= (grub_be_to_cpu32 (k1
->parent_dir
) - grub_be_to_cpu32 (k2
->parent_dir
));
623 for (i
= 0; i
< minlen
; i
++)
625 cmp
= (hfs_charorder
[k1
->str
[i
]] - hfs_charorder
[k2
->str
[i
]]);
630 /* Shorter strings precede long ones. */
631 return (k1
->strlen
- k2
->strlen
);
635 /* Compare the K1 and K2 extent overflow file keys. */
637 grub_hfs_cmp_extkeys (struct grub_hfs_extent_key
*k1
,
638 struct grub_hfs_extent_key
*k2
)
640 int cmp
= k1
->forktype
- k2
->forktype
;
642 cmp
= grub_be_to_cpu32 (k1
->fileid
) - grub_be_to_cpu32 (k2
->fileid
);
644 cmp
= (grub_be_to_cpu16 (k1
->first_block
)
645 - grub_be_to_cpu16 (k2
->first_block
));
650 /* Iterate the records in the node with index IDX in the mounted HFS
651 filesystem DATA. This node holds data of the type TYPE (0 =
652 catalog node, 1 = extent overflow node). If this is set, continue
653 iterating to the next node. For every records, call NODE_HOOK. */
655 grub_hfs_iterate_records (struct grub_hfs_data
*data
, int type
, int idx
,
656 int this, int (*node_hook
) (struct grub_hfs_node
*hnd
,
657 struct grub_hfs_record
*))
659 int nodesize
= type
== 0 ? data
->cat_size
: data
->ext_size
;
663 struct grub_hfs_node node
;
664 char rawnode
[nodesize
];
665 grub_uint16_t offsets
[nodesize
/ 2];
671 struct grub_hfs_extent
*dat
;
674 dat
= (struct grub_hfs_extent
*) (type
== 0
675 ? (&data
->sblock
.catalog_recs
)
676 : (&data
->sblock
.extent_recs
));
678 /* Read the node into memory. */
679 blk
= grub_hfs_block (data
, dat
,
680 (type
== 0) ? GRUB_HFS_CNID_CAT
: GRUB_HFS_CNID_EXT
,
681 idx
/ (data
->blksz
/ nodesize
), 0);
682 blk
+= (idx
% (data
->blksz
/ nodesize
));
686 if (grub_disk_read (data
->disk
, blk
, 0,
687 sizeof (node
), &node
))
690 /* Iterate over all records in this node. */
691 for (i
= 0; i
< grub_be_to_cpu16 (node
.node
.reccnt
); i
++)
693 int pos
= (nodesize
>> 1) - 1 - i
;
698 } __attribute__ ((packed
)) *pnt
;
699 pnt
= (struct pointer
*) (grub_be_to_cpu16 (node
.offsets
[pos
])
702 struct grub_hfs_record rec
=
706 &pnt
->key
+ pnt
->keylen
+(pnt
->keylen
+ 1) % 2,
707 nodesize
- grub_be_to_cpu16 (node
.offsets
[pos
])
711 if (node_hook (&node
.node
, &rec
))
715 idx
= grub_be_to_cpu32 (node
.node
.next
);
716 } while (idx
&& this);
722 /* Lookup a record in the mounted filesystem DATA using the key KEY.
723 The index of the node on top of the tree is IDX. The tree is of
724 the type TYPE (0 = catalog node, 1 = extent overflow node). Return
725 the data in DATAR with a maximum length of DATALEN. */
727 grub_hfs_find_node (struct grub_hfs_data
*data
, char *key
,
728 grub_uint32_t idx
, int type
, char *datar
, int datalen
)
734 auto int node_found (struct grub_hfs_node
*, struct grub_hfs_record
*);
736 int node_found (struct grub_hfs_node
*hnd
, struct grub_hfs_record
*rec
)
741 cmp
= grub_hfs_cmp_catkeys (rec
->key
, (void *) key
);
743 cmp
= grub_hfs_cmp_extkeys (rec
->key
, (void *) key
);
745 /* If the key is smaller or equal to the current node, mark the
746 entry. In case of a non-leaf mode it will be used to lookup
747 the rest of the tree. */
750 grub_uint32_t
*node
= (grub_uint32_t
*) rec
->data
;
751 found
= grub_be_to_cpu32 (*node
);
753 else /* The key can not be found in the tree. */
756 /* Check if this node is a leaf node. */
757 if (hnd
->type
== GRUB_HFS_NODE_LEAF
)
766 grub_memcpy (datar
, rec
->data
,
767 rec
->datalen
< datalen
? rec
->datalen
: datalen
);
779 if (grub_hfs_iterate_records (data
, type
, idx
, 0, node_found
))
792 /* Iterate over the directory with the id DIR. The tree is searched
793 starting with the node ROOT_IDX. For every entry in this directory
796 grub_hfs_iterate_dir (struct grub_hfs_data
*data
, grub_uint32_t root_idx
,
797 unsigned int dir
, int (*hook
) (struct grub_hfs_record
*))
803 /* The lowest key possible with DIR as root directory. */
804 struct grub_hfs_catalog_key key
= {0, grub_cpu_to_be32 (dir
), 0, ""};
806 auto int node_found (struct grub_hfs_node
*, struct grub_hfs_record
*);
807 auto int it_dir (struct grub_hfs_node
* __attribute ((unused
)),
808 struct grub_hfs_record
*);
811 int node_found (struct grub_hfs_node
*hnd
, struct grub_hfs_record
*rec
)
813 struct grub_hfs_catalog_key
*ckey
= rec
->key
;
815 if (grub_hfs_cmp_catkeys (rec
->key
, (void *) &key
) <= 0)
816 found
= grub_be_to_cpu32 (*(grub_uint32_t
*) rec
->data
);
818 if (hnd
->type
== 0xFF && ckey
->strlen
> 0)
821 next
= grub_be_to_cpu32 (hnd
->next
);
823 /* An entry was found. */
824 if (grub_be_to_cpu32 (ckey
->parent_dir
) == dir
)
831 int it_dir (struct grub_hfs_node
*hnd
__attribute ((unused
)),
832 struct grub_hfs_record
*rec
)
834 struct grub_hfs_catalog_key
*ckey
= rec
->key
;
835 struct grub_hfs_catalog_key
*origkey
= &key
;
837 /* Stop when the entries do not match anymore. */
838 if (grub_be_to_cpu32 (ckey
->parent_dir
)
839 != grub_be_to_cpu32 ((origkey
)->parent_dir
))
849 if (grub_hfs_iterate_records (data
, 0, root_idx
, 0, node_found
))
858 /* If there was a matching record in this leaf node, continue the
859 iteration until the last record was found. */
860 grub_hfs_iterate_records (data
, 0, next
, 1, it_dir
);
865 /* Find a file or directory with the pathname PATH in the filesystem
866 DATA. Return the file record in RETDATA when it is non-zero.
867 Return the directory number in RETINODE when it is non-zero. */
869 grub_hfs_find_dir (struct grub_hfs_data
*data
, const char *path
,
870 struct grub_hfs_filerec
*retdata
, int *retinode
)
872 int inode
= data
->rootdir
;
875 struct grub_hfs_filerec frec
;
876 struct grub_hfs_dirrec
*dir
= (struct grub_hfs_dirrec
*) &frec
;
877 frec
.type
= GRUB_HFS_FILETYPE_DIR
;
881 grub_error (GRUB_ERR_BAD_FILENAME
, "bad filename");
885 origpath
= grub_strdup (path
);
893 while (path
&& grub_strlen (path
))
895 if (frec
.type
!= GRUB_HFS_FILETYPE_DIR
)
897 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
901 /* Isolate a part of the path. */
902 next
= grub_strchr (path
, '/');
909 struct grub_hfs_catalog_key key
;
911 key
.parent_dir
= grub_cpu_to_be32 (inode
);
912 key
.strlen
= grub_strlen (path
);
913 grub_strcpy ((char *) (key
.str
), path
);
915 /* Lookup this node. */
916 if (! grub_hfs_find_node (data
, (char *) &key
, data
->cat_root
,
917 0, (char *) &frec
, sizeof (frec
)))
919 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
926 inode
= grub_be_to_cpu32 (dir
->dirid
);
931 grub_memcpy (retdata
, &frec
, sizeof (frec
));
937 grub_free (origpath
);
944 grub_hfs_dir (grub_device_t device
, const char *path
,
945 int (*hook
) (const char *filename
,
946 const struct grub_dirhook_info
*info
))
950 auto int dir_hook (struct grub_hfs_record
*rec
);
952 int dir_hook (struct grub_hfs_record
*rec
)
954 char fname
[32] = { 0 };
955 char *filetype
= rec
->data
;
956 struct grub_hfs_catalog_key
*ckey
= rec
->key
;
957 struct grub_dirhook_info info
;
958 grub_memset (&info
, 0, sizeof (info
));
960 grub_strncpy (fname
, (char *) (ckey
->str
), ckey
->strlen
);
962 if (*filetype
== GRUB_HFS_FILETYPE_DIR
963 || *filetype
== GRUB_HFS_FILETYPE_FILE
)
965 info
.dir
= (*filetype
== GRUB_HFS_FILETYPE_DIR
);
966 return hook (fname
, &info
);
971 struct grub_hfs_data
*data
;
972 struct grub_hfs_filerec frec
;
974 grub_dl_ref (my_mod
);
976 data
= grub_hfs_mount (device
->disk
);
980 /* First the directory ID for the directory. */
981 if (grub_hfs_find_dir (data
, path
, &frec
, &inode
))
984 if (frec
.type
!= GRUB_HFS_FILETYPE_DIR
)
986 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
990 grub_hfs_iterate_dir (data
, data
->cat_root
, inode
, dir_hook
);
995 grub_dl_unref (my_mod
);
1001 /* Open a file named NAME and initialize FILE. */
1003 grub_hfs_open (struct grub_file
*file
, const char *name
)
1005 struct grub_hfs_data
*data
;
1006 struct grub_hfs_filerec frec
;
1008 grub_dl_ref (my_mod
);
1010 data
= grub_hfs_mount (file
->device
->disk
);
1012 if (grub_hfs_find_dir (data
, name
, &frec
, 0))
1015 grub_dl_unref (my_mod
);
1019 if (frec
.type
!= GRUB_HFS_FILETYPE_FILE
)
1022 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a file");
1023 grub_dl_unref (my_mod
);
1027 grub_memcpy (data
->extents
, frec
.extents
, sizeof (grub_hfs_datarecord_t
));
1028 file
->size
= grub_be_to_cpu32 (frec
.size
);
1029 data
->size
= grub_be_to_cpu32 (frec
.size
);
1030 data
->fileid
= grub_be_to_cpu32 (frec
.fileid
);
1039 grub_hfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1041 struct grub_hfs_data
*data
=
1042 (struct grub_hfs_data
*) file
->data
;
1044 return grub_hfs_read_file (data
, file
->read_hook
, file
->offset
, len
, buf
);
1049 grub_hfs_close (grub_file_t file
)
1051 grub_free (file
->data
);
1053 grub_dl_unref (my_mod
);
1060 grub_hfs_label (grub_device_t device
, char **label
)
1062 struct grub_hfs_data
*data
;
1064 data
= grub_hfs_mount (device
->disk
);
1067 *label
= grub_strndup ((char *) (data
->sblock
.volname
+ 1),
1068 *data
->sblock
.volname
);
1078 static struct grub_fs grub_hfs_fs
=
1081 .dir
= grub_hfs_dir
,
1082 .open
= grub_hfs_open
,
1083 .read
= grub_hfs_read
,
1084 .close
= grub_hfs_close
,
1085 .label
= grub_hfs_label
,
1091 grub_fs_register (&grub_hfs_fs
);
1097 grub_fs_unregister (&grub_hfs_fs
);