1 /* hfsplus.c - HFS+ Filesystem. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 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 http://developer.apple.com/technotes/tn/tn1150.html */
23 #include <grub/file.h>
25 #include <grub/misc.h>
26 #include <grub/disk.h>
28 #include <grub/types.h>
29 #include <grub/fshelp.h>
31 #include <grub/hfsplus.h>
33 /* Internal representation of a catalog key. */
34 struct grub_hfsplus_catkey_internal
40 /* Internal representation of an extent overflow key. */
41 struct grub_hfsplus_extkey_internal
47 struct grub_hfsplus_key_internal
51 struct grub_hfsplus_extkey_internal extkey
;
52 struct grub_hfsplus_catkey_internal catkey
;
58 struct grub_fshelp_node
60 struct grub_hfsplus_data
*data
;
61 struct grub_hfsplus_extent extents
[8];
67 struct grub_hfsplus_btree
72 /* Catalog file node. */
73 struct grub_fshelp_node file
;
76 /* Information about a "mounted" HFS+ filesystem. */
77 struct grub_hfsplus_data
79 struct grub_hfsplus_volheader volheader
;
82 unsigned int log2blksize
;
84 struct grub_hfsplus_btree catalog_tree
;
85 struct grub_hfsplus_btree extoverflow_tree
;
87 struct grub_fshelp_node dirroot
;
88 struct grub_fshelp_node opened_file
;
90 /* This is the offset into the physical disk for an embedded HFS+
91 filesystem (one inside a plain HFS wrapper). */
96 static grub_dl_t my_mod
;
99 /* Return the offset of the record with the index INDEX, in the node
100 NODE which is part of the B+ tree BTREE. */
101 static inline unsigned int
102 grub_hfsplus_btree_recoffset (struct grub_hfsplus_btree
*btree
,
103 struct grub_hfsplus_btnode
*node
, int index
)
105 char *cnode
= (char *) node
;
106 grub_uint16_t
*recptr
;
107 recptr
= (grub_uint16_t
*) (&cnode
[btree
->nodesize
108 - index
* sizeof (grub_uint16_t
) - 2]);
109 return grub_be_to_cpu16 (*recptr
);
112 /* Return a pointer to the record with the index INDEX, in the node
113 NODE which is part of the B+ tree BTREE. */
114 static inline struct grub_hfsplus_key
*
115 grub_hfsplus_btree_recptr (struct grub_hfsplus_btree
*btree
,
116 struct grub_hfsplus_btnode
*node
, int index
)
118 char *cnode
= (char *) node
;
120 offset
= grub_hfsplus_btree_recoffset (btree
, node
, index
);
121 return (struct grub_hfsplus_key
*) &cnode
[offset
];
125 /* Find the extent that points to FILEBLOCK. If it is not in one of
126 the 8 extents described by EXTENT, return -1. In that case set
127 FILEBLOCK to the next block. */
129 grub_hfsplus_find_block (struct grub_hfsplus_extent
*extent
,
133 grub_size_t blksleft
= *fileblock
;
135 /* First lookup the file in the given extents. */
136 for (i
= 0; i
< 8; i
++)
138 if (blksleft
< grub_be_to_cpu32 (extent
[i
].count
))
139 return grub_be_to_cpu32 (extent
[i
].start
) + blksleft
;
140 blksleft
-= grub_be_to_cpu32 (extent
[i
].count
);
143 *fileblock
= blksleft
;
148 grub_hfsplus_btree_search (struct grub_hfsplus_btree
*btree
,
149 struct grub_hfsplus_key_internal
*key
,
150 int (*compare_keys
) (struct grub_hfsplus_key
*keya
,
151 struct grub_hfsplus_key_internal
*keyb
),
152 struct grub_hfsplus_btnode
**matchnode
, int *keyoffset
);
154 static int grub_hfsplus_cmp_extkey (struct grub_hfsplus_key
*keya
,
155 struct grub_hfsplus_key_internal
*keyb
);
157 /* Search for the block FILEBLOCK inside the file NODE. Return the
158 blocknumber of this block on disk. */
159 static grub_disk_addr_t
160 grub_hfsplus_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
162 struct grub_hfsplus_btnode
*nnode
= 0;
163 int blksleft
= fileblock
;
164 struct grub_hfsplus_extent
*extents
= &node
->extents
[0];
168 struct grub_hfsplus_extkey
*key
;
169 struct grub_hfsplus_extkey_internal extoverflow
;
173 /* Try to find this block in the current set of extents. */
174 blk
= grub_hfsplus_find_block (extents
, &blksleft
);
176 /* The previous iteration of this loop allocated memory. The
177 code above used this memory, it can be freed now. */
183 + (node
->data
->embedded_offset
>> (node
->data
->log2blksize
184 - GRUB_DISK_SECTOR_BITS
)));
186 /* For the extent overflow file, extra extents can't be found in
187 the extent overflow file. If this happens, you found a
189 if (node
->fileid
== GRUB_HFSPLUS_FILEID_OVERFLOW
)
191 grub_error (GRUB_ERR_READ_ERROR
,
192 "extra extents found in an extend overflow file");
196 /* Set up the key to look for in the extent overflow file. */
197 extoverflow
.fileid
= node
->fileid
;
198 extoverflow
.start
= fileblock
- blksleft
;
200 if (grub_hfsplus_btree_search (&node
->data
->extoverflow_tree
,
201 (struct grub_hfsplus_key_internal
*) &extoverflow
,
202 grub_hfsplus_cmp_extkey
, &nnode
, &ptr
))
204 grub_error (GRUB_ERR_READ_ERROR
,
205 "no block found for the file id 0x%x and the block offset 0x%x",
206 node
->fileid
, fileblock
);
210 /* The extent overflow file has 8 extents right after the key. */
211 key
= (struct grub_hfsplus_extkey
*)
212 grub_hfsplus_btree_recptr (&node
->data
->extoverflow_tree
, nnode
, ptr
);
213 extents
= (struct grub_hfsplus_extent
*) (key
+ 1);
215 /* The block wasn't found. Perhaps the next iteration will find
216 it. The last block we found is stored in BLKSLEFT now. */
221 /* Too bad, you lose. */
226 /* Read LEN bytes from the file described by DATA starting with byte
227 POS. Return the amount of read bytes in READ. */
229 grub_hfsplus_read_file (grub_fshelp_node_t node
,
230 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
231 unsigned offset
, unsigned length
),
232 int pos
, grub_size_t len
, char *buf
)
234 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
235 pos
, len
, buf
, grub_hfsplus_read_block
,
237 node
->data
->log2blksize
- GRUB_DISK_SECTOR_BITS
);
240 static struct grub_hfsplus_data
*
241 grub_hfsplus_mount (grub_disk_t disk
)
243 struct grub_hfsplus_data
*data
;
244 struct grub_hfsplus_btheader header
;
245 struct grub_hfsplus_btnode node
;
248 struct grub_hfs_sblock hfs
;
249 struct grub_hfsplus_volheader hfsplus
;
252 data
= grub_malloc (sizeof (*data
));
258 /* Read the bootblock. */
259 grub_disk_read (disk
, GRUB_HFSPLUS_SBLOCK
, 0, sizeof (volheader
),
264 data
->embedded_offset
= 0;
265 if (grub_be_to_cpu16 (volheader
.hfs
.magic
) == GRUB_HFS_MAGIC
)
271 /* See if there's an embedded HFS+ filesystem. */
272 if (grub_be_to_cpu16 (volheader
.hfs
.embed_sig
) != GRUB_HFSPLUS_MAGIC
)
274 grub_error (GRUB_ERR_BAD_FS
, "not a HFS+ filesystem");
278 /* Calculate the offset needed to translate HFS+ sector numbers. */
279 extent_start
= grub_be_to_cpu16 (volheader
.hfs
.embed_extent
.first_block
);
280 ablk_size
= grub_be_to_cpu32 (volheader
.hfs
.blksz
);
281 ablk_start
= grub_be_to_cpu16 (volheader
.hfs
.first_block
);
282 data
->embedded_offset
= (ablk_start
284 * (ablk_size
>> GRUB_DISK_SECTOR_BITS
));
286 grub_disk_read (disk
, data
->embedded_offset
+ GRUB_HFSPLUS_SBLOCK
, 0,
287 sizeof (volheader
), &volheader
);
292 /* Make sure this is an HFS+ filesystem. XXX: Do we really support
294 magic
= grub_be_to_cpu16 (volheader
.hfsplus
.magic
);
295 if ((magic
!= GRUB_HFSPLUS_MAGIC
) && (magic
!= GRUB_HFSPLUSX_MAGIC
))
297 grub_error (GRUB_ERR_BAD_FS
, "not a HFS+ filesystem");
301 grub_memcpy (&data
->volheader
, &volheader
.hfsplus
,
302 sizeof (volheader
.hfsplus
));
304 if (grub_fshelp_log2blksize (grub_be_to_cpu32 (data
->volheader
.blksize
),
308 /* Make a new node for the catalog tree. */
309 data
->catalog_tree
.file
.data
= data
;
310 data
->catalog_tree
.file
.fileid
= GRUB_HFSPLUS_FILEID_CATALOG
;
311 grub_memcpy (&data
->catalog_tree
.file
.extents
,
312 data
->volheader
.catalog_file
.extents
,
313 sizeof data
->volheader
.catalog_file
.extents
);
314 data
->catalog_tree
.file
.size
=
315 grub_be_to_cpu64 (data
->volheader
.catalog_file
.size
);
317 /* Make a new node for the extent overflow file. */
318 data
->extoverflow_tree
.file
.data
= data
;
319 data
->extoverflow_tree
.file
.fileid
= GRUB_HFSPLUS_FILEID_OVERFLOW
;
320 grub_memcpy (&data
->extoverflow_tree
.file
.extents
,
321 data
->volheader
.extents_file
.extents
,
322 sizeof data
->volheader
.catalog_file
.extents
);
324 data
->extoverflow_tree
.file
.size
=
325 grub_be_to_cpu64 (data
->volheader
.extents_file
.size
);
327 /* Read the essential information about the trees. */
328 if (! grub_hfsplus_read_file (&data
->catalog_tree
.file
, 0,
329 sizeof (struct grub_hfsplus_btnode
),
330 sizeof (header
), (char *) &header
))
333 data
->catalog_tree
.root
= grub_be_to_cpu32 (header
.root
);
334 data
->catalog_tree
.nodesize
= grub_be_to_cpu16 (header
.nodesize
);
335 data
->case_sensitive
= ((magic
== GRUB_HFSPLUSX_MAGIC
) &&
336 (header
.key_compare
== GRUB_HFSPLUSX_BINARYCOMPARE
));
338 if (! grub_hfsplus_read_file (&data
->extoverflow_tree
.file
, 0,
339 sizeof (struct grub_hfsplus_btnode
),
340 sizeof (header
), (char *) &header
))
343 data
->extoverflow_tree
.root
= grub_be_to_cpu32 (header
.root
);
345 if (! grub_hfsplus_read_file (&data
->extoverflow_tree
.file
, 0, 0,
346 sizeof (node
), (char *) &node
))
349 data
->extoverflow_tree
.root
= grub_be_to_cpu32 (header
.root
);
350 data
->extoverflow_tree
.nodesize
= grub_be_to_cpu16 (header
.nodesize
);
352 data
->dirroot
.data
= data
;
353 data
->dirroot
.fileid
= GRUB_HFSPLUS_FILEID_ROOTDIR
;
359 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
360 grub_error (GRUB_ERR_BAD_FS
, "not a hfsplus filesystem");
366 /* Compare the on disk catalog key KEYA with the catalog key we are
367 looking for (KEYB). */
369 grub_hfsplus_cmp_catkey (struct grub_hfsplus_key
*keya
,
370 struct grub_hfsplus_key_internal
*keyb
)
372 struct grub_hfsplus_catkey
*catkey_a
= &keya
->catkey
;
373 struct grub_hfsplus_catkey_internal
*catkey_b
= &keyb
->catkey
;
378 diff
= grub_be_to_cpu32 (catkey_a
->parent
) - catkey_b
->parent
;
382 /* Change the filename in keya so the endianness is correct. */
383 for (i
= 0; i
< grub_be_to_cpu16 (catkey_a
->namelen
); i
++)
384 catkey_a
->name
[i
] = grub_be_to_cpu16 (catkey_a
->name
[i
]);
386 filename
= grub_malloc (grub_be_to_cpu16 (catkey_a
->namelen
) + 1);
388 if (! grub_utf16_to_utf8 ((grub_uint8_t
*) filename
, catkey_a
->name
,
389 grub_be_to_cpu16 (catkey_a
->namelen
)))
390 return -1; /* XXX: This error never occurs, but in case it happens
391 just skip this entry. */
393 diff
= grub_strncmp (filename
, catkey_b
->name
,
394 grub_be_to_cpu16 (catkey_a
->namelen
));
396 grub_free (filename
);
398 /* The endianness was changed to host format, change it back to
400 for (i
= 0; i
< grub_be_to_cpu16 (catkey_a
->namelen
); i
++)
401 catkey_a
->name
[i
] = grub_cpu_to_be16 (catkey_a
->name
[i
]);
405 /* Compare the on disk extent overflow key KEYA with the extent
406 overflow key we are looking for (KEYB). */
408 grub_hfsplus_cmp_extkey (struct grub_hfsplus_key
*keya
,
409 struct grub_hfsplus_key_internal
*keyb
)
411 struct grub_hfsplus_extkey
*extkey_a
= &keya
->extkey
;
412 struct grub_hfsplus_extkey_internal
*extkey_b
= &keyb
->extkey
;
415 diff
= grub_be_to_cpu32 (extkey_a
->fileid
) - extkey_b
->fileid
;
420 diff
= grub_be_to_cpu32 (extkey_a
->start
) - extkey_b
->start
;
425 grub_hfsplus_read_symlink (grub_fshelp_node_t node
)
428 grub_ssize_t numread
;
430 symlink
= grub_malloc (node
->size
+ 1);
434 numread
= grub_hfsplus_read_file (node
, 0, 0, node
->size
, symlink
);
435 if (numread
!= (grub_ssize_t
) node
->size
)
440 symlink
[node
->size
] = '\0';
446 grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree
*btree
,
447 struct grub_hfsplus_btnode
*first_node
,
449 int (*hook
) (void *record
))
455 char *cnode
= (char *) first_node
;
457 /* Iterate over all records in this node. */
458 for (rec
= first_rec
; rec
< grub_be_to_cpu16 (first_node
->count
); rec
++)
460 if (hook (grub_hfsplus_btree_recptr (btree
, first_node
, rec
)))
464 if (! first_node
->next
)
467 if (! grub_hfsplus_read_file (&btree
->file
, 0,
468 (grub_be_to_cpu32 (first_node
->next
)
470 btree
->nodesize
, cnode
))
473 /* Don't skip any record in the next iteration. */
480 /* Lookup the node described by KEY in the B+ Tree BTREE. Compare
481 keys using the function COMPARE_KEYS. When a match is found,
482 return the node in MATCHNODE and a pointer to the data in this node
483 in KEYOFFSET. MATCHNODE should be freed by the caller. */
485 grub_hfsplus_btree_search (struct grub_hfsplus_btree
*btree
,
486 struct grub_hfsplus_key_internal
*key
,
487 int (*compare_keys
) (struct grub_hfsplus_key
*keya
,
488 struct grub_hfsplus_key_internal
*keyb
),
489 struct grub_hfsplus_btnode
**matchnode
, int *keyoffset
)
491 grub_uint64_t currnode
;
493 struct grub_hfsplus_btnode
*nodedesc
;
496 node
= grub_malloc (btree
->nodesize
);
500 currnode
= btree
->root
;
506 if (! grub_hfsplus_read_file (&btree
->file
, 0,
507 (long)currnode
* (long)btree
->nodesize
,
508 btree
->nodesize
, (char *) node
))
514 nodedesc
= (struct grub_hfsplus_btnode
*) node
;
516 /* Find the record in this tree. */
517 for (rec
= 0; rec
< grub_be_to_cpu16 (nodedesc
->count
); rec
++)
519 struct grub_hfsplus_key
*currkey
;
520 currkey
= grub_hfsplus_btree_recptr (btree
, nodedesc
, rec
);
522 /* The action that has to be taken depend on the type of
524 if (nodedesc
->type
== GRUB_HFSPLUS_BTNODE_TYPE_LEAF
525 && compare_keys (currkey
, key
) == 0)
527 /* An exact match was found! */
529 *matchnode
= nodedesc
;
534 else if (nodedesc
->type
== GRUB_HFSPLUS_BTNODE_TYPE_INDEX
)
536 grub_uint32_t
*pointer
;
538 /* The place where the key could have been found didn't
539 contain the key. This means that the previous match
540 is the one that should be followed. */
541 if (compare_keys (currkey
, key
) > 0)
544 /* Mark the last key which is lower or equal to the key
545 that we are looking for. The last match that is
546 found will be used to locate the child which can
547 contain the record. */
548 pointer
= (grub_uint32_t
*) ((char *) currkey
549 + grub_be_to_cpu16 (currkey
->keylen
)
551 currnode
= grub_be_to_cpu32 (*pointer
);
556 /* No match is found, no record with this key exists in the
568 grub_hfsplus_iterate_dir (grub_fshelp_node_t dir
,
570 (*hook
) (const char *filename
,
571 enum grub_fshelp_filetype filetype
,
572 grub_fshelp_node_t node
))
576 auto int list_nodes (void *record
);
577 int list_nodes (void *record
)
579 struct grub_hfsplus_catkey
*catkey
;
582 struct grub_fshelp_node
*node
;
583 struct grub_hfsplus_catfile
*fileinfo
;
584 enum grub_fshelp_filetype type
= GRUB_FSHELP_UNKNOWN
;
586 catkey
= (struct grub_hfsplus_catkey
*) record
;
589 (struct grub_hfsplus_catfile
*) ((char *) record
590 + grub_be_to_cpu16 (catkey
->keylen
)
591 + 2 + (grub_be_to_cpu16(catkey
->keylen
)
594 /* Stop iterating when the last directory entry is found. */
595 if (grub_be_to_cpu32 (catkey
->parent
) != dir
->fileid
)
598 /* Determine the type of the node that is found. */
599 if (grub_be_to_cpu16 (fileinfo
->type
) == GRUB_HFSPLUS_FILETYPE_REG
)
601 int mode
= (grub_be_to_cpu16 (fileinfo
->mode
)
602 & GRUB_HFSPLUS_FILEMODE_MASK
);
604 if (mode
== GRUB_HFSPLUS_FILEMODE_REG
)
605 type
= GRUB_FSHELP_REG
;
606 else if (mode
== GRUB_HFSPLUS_FILEMODE_SYMLINK
)
607 type
= GRUB_FSHELP_SYMLINK
;
609 type
= GRUB_FSHELP_UNKNOWN
;
611 else if (grub_be_to_cpu16 (fileinfo
->type
) == GRUB_HFSPLUS_FILETYPE_DIR
)
612 type
= GRUB_FSHELP_DIR
;
614 if (type
== GRUB_FSHELP_UNKNOWN
)
617 /* Make sure the byte order of the UTF16 string is correct. */
618 for (i
= 0; i
< grub_be_to_cpu16 (catkey
->namelen
); i
++)
620 catkey
->name
[i
] = grub_be_to_cpu16 (catkey
->name
[i
]);
622 /* If the name is obviously invalid, skip this node. */
623 if (catkey
->name
[i
] == 0)
627 filename
= grub_malloc (grub_be_to_cpu16 (catkey
->namelen
) + 1);
631 if (! grub_utf16_to_utf8 ((grub_uint8_t
*) filename
, catkey
->name
,
632 grub_be_to_cpu16 (catkey
->namelen
)))
634 grub_free (filename
);
638 filename
[grub_be_to_cpu16 (catkey
->namelen
)] = '\0';
640 /* Restore the byte order to what it was previously. */
641 for (i
= 0; i
< grub_be_to_cpu16 (catkey
->namelen
); i
++)
642 catkey
->name
[i
] = grub_be_to_cpu16 (catkey
->name
[i
]);
644 /* hfs+ is case insensitive. */
645 if (! dir
->data
->case_sensitive
)
646 type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
648 /* Only accept valid nodes. */
649 if (grub_strlen (filename
) == grub_be_to_cpu16 (catkey
->namelen
))
651 /* A valid node is found; setup the node and call the
652 callback function. */
653 node
= grub_malloc (sizeof (*node
));
654 node
->data
= dir
->data
;
656 grub_memcpy (node
->extents
, fileinfo
->data
.extents
,
657 sizeof (node
->extents
));
658 node
->mtime
= grub_be_to_cpu32 (fileinfo
->mtime
) - 2082844800;
659 node
->size
= grub_be_to_cpu64 (fileinfo
->data
.size
);
660 node
->fileid
= grub_be_to_cpu32 (fileinfo
->fileid
);
662 ret
= hook (filename
, type
, node
);
665 grub_free (filename
);
670 struct grub_hfsplus_key_internal intern
;
671 struct grub_hfsplus_btnode
*node
;
674 /* Create a key that points to the first entry in the directory. */
675 intern
.catkey
.parent
= dir
->fileid
;
676 intern
.catkey
.name
= "";
678 /* First lookup the first entry. */
679 if (grub_hfsplus_btree_search (&dir
->data
->catalog_tree
, &intern
,
680 grub_hfsplus_cmp_catkey
, &node
, &ptr
))
683 /* Iterate over all entries in this directory. */
684 grub_hfsplus_btree_iterate_node (&dir
->data
->catalog_tree
, node
, ptr
,
692 /* Open a file named NAME and initialize FILE. */
694 grub_hfsplus_open (struct grub_file
*file
, const char *name
)
696 struct grub_hfsplus_data
*data
;
697 struct grub_fshelp_node
*fdiro
= 0;
699 grub_dl_ref (my_mod
);
701 data
= grub_hfsplus_mount (file
->device
->disk
);
705 grub_fshelp_find_file (name
, &data
->dirroot
, &fdiro
,
706 grub_hfsplus_iterate_dir
,
707 grub_hfsplus_read_symlink
, GRUB_FSHELP_REG
);
711 file
->size
= fdiro
->size
;
712 data
->opened_file
= *fdiro
;
721 if (data
&& fdiro
!= &data
->dirroot
)
725 grub_dl_unref (my_mod
);
732 grub_hfsplus_close (grub_file_t file
)
734 grub_free (file
->data
);
736 grub_dl_unref (my_mod
);
738 return GRUB_ERR_NONE
;
742 /* Read LEN bytes data from FILE into BUF. */
744 grub_hfsplus_read (grub_file_t file
, char *buf
, grub_size_t len
)
746 struct grub_hfsplus_data
*data
=
747 (struct grub_hfsplus_data
*) file
->data
;
749 int size
= grub_hfsplus_read_file (&data
->opened_file
, file
->read_hook
,
750 file
->offset
, len
, buf
);
757 grub_hfsplus_dir (grub_device_t device
, const char *path
,
758 int (*hook
) (const char *filename
,
759 const struct grub_dirhook_info
*info
))
761 struct grub_hfsplus_data
*data
= 0;
762 struct grub_fshelp_node
*fdiro
= 0;
764 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
765 enum grub_fshelp_filetype filetype
,
766 grub_fshelp_node_t node
);
768 int NESTED_FUNC_ATTR
iterate (const char *filename
,
769 enum grub_fshelp_filetype filetype
,
770 grub_fshelp_node_t node
)
772 struct grub_dirhook_info info
;
773 grub_memset (&info
, 0, sizeof (info
));
774 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
776 info
.mtime
= node
->mtime
;
778 info
.inode
= node
->fileid
;
779 info
.case_insensitive
= !! (filetype
& GRUB_FSHELP_CASE_INSENSITIVE
);
781 return hook (filename
, &info
);
784 grub_dl_ref (my_mod
);
786 data
= grub_hfsplus_mount (device
->disk
);
790 /* Find the directory that should be opened. */
791 grub_fshelp_find_file (path
, &data
->dirroot
, &fdiro
,
792 grub_hfsplus_iterate_dir
,
793 grub_hfsplus_read_symlink
, GRUB_FSHELP_DIR
);
797 /* Iterate over all entries in this directory. */
798 grub_hfsplus_iterate_dir (fdiro
, iterate
);
801 if (data
&& fdiro
!= &data
->dirroot
)
805 grub_dl_unref (my_mod
);
812 grub_hfsplus_label (grub_device_t device
__attribute__((unused
))
813 , char **label
__attribute__((unused
)))
815 /* XXX: It's not documented how to read a label. */
816 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
817 "reading the label of a HFS+ "
818 "partition is not implemented");
823 grub_hfsplus_mtime (grub_device_t device
, grub_int32_t
*tm
)
825 struct grub_hfsplus_data
*data
;
826 grub_disk_t disk
= device
->disk
;
828 grub_dl_ref (my_mod
);
830 data
= grub_hfsplus_mount (disk
);
834 *tm
= grub_be_to_cpu32 (data
->volheader
.utime
) - 2082844800;
836 grub_dl_unref (my_mod
);
845 grub_hfsplus_uuid (grub_device_t device
, char **uuid
)
847 struct grub_hfsplus_data
*data
;
848 grub_disk_t disk
= device
->disk
;
850 grub_dl_ref (my_mod
);
852 data
= grub_hfsplus_mount (disk
);
855 *uuid
= grub_malloc (16 + sizeof ('\0'));
856 grub_sprintf (*uuid
, "%016llx",
858 grub_be_to_cpu64 (data
->volheader
.num_serial
));
863 grub_dl_unref (my_mod
);
872 static struct grub_fs grub_hfsplus_fs
=
875 .dir
= grub_hfsplus_dir
,
876 .open
= grub_hfsplus_open
,
877 .read
= grub_hfsplus_read
,
878 .close
= grub_hfsplus_close
,
879 .label
= grub_hfsplus_label
,
880 .mtime
= grub_hfsplus_mtime
,
881 .uuid
= grub_hfsplus_uuid
,
885 GRUB_MOD_INIT(hfsplus
)
887 grub_fs_register (&grub_hfsplus_fs
);
891 GRUB_MOD_FINI(hfsplus
)
893 grub_fs_unregister (&grub_hfsplus_fs
);