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/charset.h>
33
#define GRUB_HFSPLUS_MAGIC 0x482B
34
#define GRUB_HFSPLUSX_MAGIC 0x4858
35
#define GRUB_HFSPLUS_SBLOCK 2
38
struct grub_hfsplus_extent
40
/* The first block of a file on disk. */
42
/* The amount of blocks described by this extent. */
44
} __attribute__ ((packed));
46
/* The descriptor of a fork. */
47
struct grub_hfsplus_forkdata
50
grub_uint32_t clumpsize;
52
struct grub_hfsplus_extent extents[8];
53
} __attribute__ ((packed));
55
/* The HFS+ Volume Header. */
56
struct grub_hfsplus_volheader
59
grub_uint16_t version;
60
grub_uint32_t attributes;
61
grub_uint8_t unused1[12];
63
grub_uint8_t unused2[16];
64
grub_uint32_t blksize;
65
grub_uint8_t unused3[60];
66
grub_uint64_t num_serial;
67
struct grub_hfsplus_forkdata allocations_file;
68
struct grub_hfsplus_forkdata extents_file;
69
struct grub_hfsplus_forkdata catalog_file;
70
struct grub_hfsplus_forkdata attrib_file;
71
struct grub_hfsplus_forkdata startup_file;
72
} __attribute__ ((packed));
74
/* The type of node. */
75
enum grub_hfsplus_btnode_type
77
GRUB_HFSPLUS_BTNODE_TYPE_LEAF = -1,
78
GRUB_HFSPLUS_BTNODE_TYPE_INDEX = 0,
79
GRUB_HFSPLUS_BTNODE_TYPE_HEADER = 1,
80
GRUB_HFSPLUS_BTNODE_TYPE_MAP = 2,
83
struct grub_hfsplus_btnode
91
} __attribute__ ((packed));
93
/* The header of a HFS+ B+ Tree. */
94
struct grub_hfsplus_btheader
98
grub_uint32_t leaf_records;
99
grub_uint32_t first_leaf_node;
100
grub_uint32_t last_leaf_node;
101
grub_uint16_t nodesize;
102
grub_uint16_t keysize;
103
grub_uint32_t total_nodes;
104
grub_uint32_t free_nodes;
105
grub_uint16_t reserved1;
106
grub_uint32_t clump_size; /* ignored */
107
grub_uint8_t btree_type;
108
grub_uint8_t key_compare;
109
grub_uint32_t attributes;
110
} __attribute__ ((packed));
112
/* The on disk layout of a catalog key. */
113
struct grub_hfsplus_catkey
115
grub_uint16_t keylen;
116
grub_uint32_t parent;
117
grub_uint16_t namelen;
118
grub_uint16_t name[30];
119
} __attribute__ ((packed));
121
/* The on disk layout of an extent overflow file key. */
122
struct grub_hfsplus_extkey
124
grub_uint16_t keylen;
127
grub_uint32_t fileid;
129
} __attribute__ ((packed));
131
struct grub_hfsplus_key
135
struct grub_hfsplus_extkey extkey;
136
struct grub_hfsplus_catkey catkey;
137
grub_uint16_t keylen;
139
} __attribute__ ((packed));
141
struct grub_hfsplus_catfile
145
grub_uint32_t reserved;
146
grub_uint32_t fileid;
147
grub_uint8_t unused1[4];
149
grub_uint8_t unused2[22];
151
grub_uint8_t unused3[44];
152
struct grub_hfsplus_forkdata data;
153
struct grub_hfsplus_forkdata resource;
154
} __attribute__ ((packed));
156
/* Filetype information as used in inodes. */
157
#define GRUB_HFSPLUS_FILEMODE_MASK 0170000
158
#define GRUB_HFSPLUS_FILEMODE_REG 0100000
159
#define GRUB_HFSPLUS_FILEMODE_DIRECTORY 0040000
160
#define GRUB_HFSPLUS_FILEMODE_SYMLINK 0120000
162
/* Some pre-defined file IDs. */
163
#define GRUB_HFSPLUS_FILEID_ROOTDIR 2
164
#define GRUB_HFSPLUS_FILEID_OVERFLOW 3
165
#define GRUB_HFSPLUS_FILEID_CATALOG 4
167
enum grub_hfsplus_filetype
169
GRUB_HFSPLUS_FILETYPE_DIR = 1,
170
GRUB_HFSPLUS_FILETYPE_REG = 2,
171
GRUB_HFSPLUS_FILETYPE_DIR_THREAD = 3,
172
GRUB_HFSPLUS_FILETYPE_REG_THREAD = 4
175
#define GRUB_HFSPLUSX_BINARYCOMPARE 0xBC
176
#define GRUB_HFSPLUSX_CASEFOLDING 0xCF
178
/* Internal representation of a catalog key. */
179
struct grub_hfsplus_catkey_internal
185
/* Internal representation of an extent overflow key. */
186
struct grub_hfsplus_extkey_internal
188
grub_uint32_t fileid;
192
struct grub_hfsplus_key_internal
196
struct grub_hfsplus_extkey_internal extkey;
197
struct grub_hfsplus_catkey_internal catkey;
203
struct grub_fshelp_node
205
struct grub_hfsplus_data *data;
206
struct grub_hfsplus_extent extents[8];
208
grub_uint32_t fileid;
212
struct grub_hfsplus_btree
217
/* Catalog file node. */
218
struct grub_fshelp_node file;
221
/* Information about a "mounted" HFS+ filesystem. */
222
struct grub_hfsplus_data
224
struct grub_hfsplus_volheader volheader;
227
unsigned int log2blksize;
229
struct grub_hfsplus_btree catalog_tree;
230
struct grub_hfsplus_btree extoverflow_tree;
232
struct grub_fshelp_node dirroot;
233
struct grub_fshelp_node opened_file;
235
/* This is the offset into the physical disk for an embedded HFS+
236
filesystem (one inside a plain HFS wrapper). */
241
static grub_dl_t my_mod;
244
/* Return the offset of the record with the index INDEX, in the node
245
NODE which is part of the B+ tree BTREE. */
246
static inline unsigned int
247
grub_hfsplus_btree_recoffset (struct grub_hfsplus_btree *btree,
248
struct grub_hfsplus_btnode *node, int index)
250
char *cnode = (char *) node;
251
grub_uint16_t *recptr;
252
recptr = (grub_uint16_t *) (&cnode[btree->nodesize
253
- index * sizeof (grub_uint16_t) - 2]);
254
return grub_be_to_cpu16 (*recptr);
257
/* Return a pointer to the record with the index INDEX, in the node
258
NODE which is part of the B+ tree BTREE. */
259
static inline struct grub_hfsplus_key *
260
grub_hfsplus_btree_recptr (struct grub_hfsplus_btree *btree,
261
struct grub_hfsplus_btnode *node, int index)
263
char *cnode = (char *) node;
265
offset = grub_hfsplus_btree_recoffset (btree, node, index);
266
return (struct grub_hfsplus_key *) &cnode[offset];
270
/* Find the extent that points to FILEBLOCK. If it is not in one of
271
the 8 extents described by EXTENT, return -1. In that case set
272
FILEBLOCK to the next block. */
274
grub_hfsplus_find_block (struct grub_hfsplus_extent *extent,
278
grub_size_t blksleft = *fileblock;
280
/* First lookup the file in the given extents. */
281
for (i = 0; i < 8; i++)
283
if (blksleft < grub_be_to_cpu32 (extent[i].count))
284
return grub_be_to_cpu32 (extent[i].start) + blksleft;
285
blksleft -= grub_be_to_cpu32 (extent[i].count);
288
*fileblock = blksleft;
293
grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
294
struct grub_hfsplus_key_internal *key,
295
int (*compare_keys) (struct grub_hfsplus_key *keya,
296
struct grub_hfsplus_key_internal *keyb),
297
struct grub_hfsplus_btnode **matchnode, int *keyoffset);
299
static int grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya,
300
struct grub_hfsplus_key_internal *keyb);
302
/* Search for the block FILEBLOCK inside the file NODE. Return the
303
blocknumber of this block on disk. */
304
static grub_disk_addr_t
305
grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
307
struct grub_hfsplus_btnode *nnode = 0;
308
int blksleft = fileblock;
309
struct grub_hfsplus_extent *extents = &node->extents[0];
313
struct grub_hfsplus_extkey *key;
314
struct grub_hfsplus_extkey_internal extoverflow;
318
/* Try to find this block in the current set of extents. */
319
blk = grub_hfsplus_find_block (extents, &blksleft);
321
/* The previous iteration of this loop allocated memory. The
322
code above used this memory, it can be freed now. */
328
+ (node->data->embedded_offset >> (node->data->log2blksize
329
- GRUB_DISK_SECTOR_BITS)));
331
/* For the extent overflow file, extra extents can't be found in
332
the extent overflow file. If this happens, you found a
334
if (node->fileid == GRUB_HFSPLUS_FILEID_OVERFLOW)
336
grub_error (GRUB_ERR_READ_ERROR,
337
"extra extents found in an extend overflow file");
341
/* Set up the key to look for in the extent overflow file. */
342
extoverflow.fileid = node->fileid;
343
extoverflow.start = fileblock - blksleft;
345
if (grub_hfsplus_btree_search (&node->data->extoverflow_tree,
346
(struct grub_hfsplus_key_internal *) &extoverflow,
347
grub_hfsplus_cmp_extkey, &nnode, &ptr))
349
grub_error (GRUB_ERR_READ_ERROR,
350
"no block found for the file id 0x%x and the block offset 0x%x",
351
node->fileid, fileblock);
355
/* The extent overflow file has 8 extents right after the key. */
356
key = (struct grub_hfsplus_extkey *)
357
grub_hfsplus_btree_recptr (&node->data->extoverflow_tree, nnode, ptr);
358
extents = (struct grub_hfsplus_extent *) (key + 1);
360
/* The block wasn't found. Perhaps the next iteration will find
361
it. The last block we found is stored in BLKSLEFT now. */
366
/* Too bad, you lose. */
371
/* Read LEN bytes from the file described by DATA starting with byte
372
POS. Return the amount of read bytes in READ. */
374
grub_hfsplus_read_file (grub_fshelp_node_t node,
375
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
376
unsigned offset, unsigned length),
377
int pos, grub_size_t len, char *buf)
379
return grub_fshelp_read_file (node->data->disk, node, read_hook,
380
pos, len, buf, grub_hfsplus_read_block,
382
node->data->log2blksize - GRUB_DISK_SECTOR_BITS);
385
static struct grub_hfsplus_data *
386
grub_hfsplus_mount (grub_disk_t disk)
388
struct grub_hfsplus_data *data;
389
struct grub_hfsplus_btheader header;
390
struct grub_hfsplus_btnode node;
393
struct grub_hfs_sblock hfs;
394
struct grub_hfsplus_volheader hfsplus;
397
data = grub_malloc (sizeof (*data));
403
/* Read the bootblock. */
404
grub_disk_read (disk, GRUB_HFSPLUS_SBLOCK, 0, sizeof (volheader),
409
data->embedded_offset = 0;
410
if (grub_be_to_cpu16 (volheader.hfs.magic) == GRUB_HFS_MAGIC)
416
/* See if there's an embedded HFS+ filesystem. */
417
if (grub_be_to_cpu16 (volheader.hfs.embed_sig) != GRUB_HFSPLUS_MAGIC)
419
grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem");
423
/* Calculate the offset needed to translate HFS+ sector numbers. */
424
extent_start = grub_be_to_cpu16 (volheader.hfs.embed_extent.first_block);
425
ablk_size = grub_be_to_cpu32 (volheader.hfs.blksz);
426
ablk_start = grub_be_to_cpu16 (volheader.hfs.first_block);
427
data->embedded_offset = (ablk_start
429
* (ablk_size >> GRUB_DISK_SECTOR_BITS));
431
grub_disk_read (disk, data->embedded_offset + GRUB_HFSPLUS_SBLOCK, 0,
432
sizeof (volheader), &volheader);
437
/* Make sure this is an HFS+ filesystem. XXX: Do we really support
439
magic = grub_be_to_cpu16 (volheader.hfsplus.magic);
440
if ((magic != GRUB_HFSPLUS_MAGIC) && (magic != GRUB_HFSPLUSX_MAGIC))
442
grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem");
446
grub_memcpy (&data->volheader, &volheader.hfsplus,
447
sizeof (volheader.hfsplus));
449
if (grub_fshelp_log2blksize (grub_be_to_cpu32 (data->volheader.blksize),
453
/* Make a new node for the catalog tree. */
454
data->catalog_tree.file.data = data;
455
data->catalog_tree.file.fileid = GRUB_HFSPLUS_FILEID_CATALOG;
456
grub_memcpy (&data->catalog_tree.file.extents,
457
data->volheader.catalog_file.extents,
458
sizeof data->volheader.catalog_file.extents);
459
data->catalog_tree.file.size =
460
grub_be_to_cpu64 (data->volheader.catalog_file.size);
462
/* Make a new node for the extent overflow file. */
463
data->extoverflow_tree.file.data = data;
464
data->extoverflow_tree.file.fileid = GRUB_HFSPLUS_FILEID_OVERFLOW;
465
grub_memcpy (&data->extoverflow_tree.file.extents,
466
data->volheader.extents_file.extents,
467
sizeof data->volheader.catalog_file.extents);
469
data->extoverflow_tree.file.size =
470
grub_be_to_cpu64 (data->volheader.extents_file.size);
472
/* Read the essential information about the trees. */
473
if (grub_hfsplus_read_file (&data->catalog_tree.file, 0,
474
sizeof (struct grub_hfsplus_btnode),
475
sizeof (header), (char *) &header) <= 0)
478
data->catalog_tree.root = grub_be_to_cpu32 (header.root);
479
data->catalog_tree.nodesize = grub_be_to_cpu16 (header.nodesize);
480
data->case_sensitive = ((magic == GRUB_HFSPLUSX_MAGIC) &&
481
(header.key_compare == GRUB_HFSPLUSX_BINARYCOMPARE));
483
if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0,
484
sizeof (struct grub_hfsplus_btnode),
485
sizeof (header), (char *) &header) <= 0)
488
data->extoverflow_tree.root = grub_be_to_cpu32 (header.root);
490
if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0, 0,
491
sizeof (node), (char *) &node) <= 0)
494
data->extoverflow_tree.root = grub_be_to_cpu32 (header.root);
495
data->extoverflow_tree.nodesize = grub_be_to_cpu16 (header.nodesize);
497
data->dirroot.data = data;
498
data->dirroot.fileid = GRUB_HFSPLUS_FILEID_ROOTDIR;
504
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
505
grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem");
511
/* Compare the on disk catalog key KEYA with the catalog key we are
512
looking for (KEYB). */
514
grub_hfsplus_cmp_catkey (struct grub_hfsplus_key *keya,
515
struct grub_hfsplus_key_internal *keyb)
517
struct grub_hfsplus_catkey *catkey_a = &keya->catkey;
518
struct grub_hfsplus_catkey_internal *catkey_b = &keyb->catkey;
523
diff = grub_be_to_cpu32 (catkey_a->parent) - catkey_b->parent;
527
/* Change the filename in keya so the endianness is correct. */
528
for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++)
529
catkey_a->name[i] = grub_be_to_cpu16 (catkey_a->name[i]);
531
filename = grub_malloc (grub_be_to_cpu16 (catkey_a->namelen) + 1);
533
if (! grub_utf16_to_utf8 ((grub_uint8_t *) filename, catkey_a->name,
534
grub_be_to_cpu16 (catkey_a->namelen)))
535
return -1; /* XXX: This error never occurs, but in case it happens
536
just skip this entry. */
538
diff = grub_strncmp (filename, catkey_b->name,
539
grub_be_to_cpu16 (catkey_a->namelen));
541
grub_free (filename);
543
/* The endianness was changed to host format, change it back to
545
for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++)
546
catkey_a->name[i] = grub_cpu_to_be16 (catkey_a->name[i]);
550
/* Compare the on disk extent overflow key KEYA with the extent
551
overflow key we are looking for (KEYB). */
553
grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya,
554
struct grub_hfsplus_key_internal *keyb)
556
struct grub_hfsplus_extkey *extkey_a = &keya->extkey;
557
struct grub_hfsplus_extkey_internal *extkey_b = &keyb->extkey;
560
diff = grub_be_to_cpu32 (extkey_a->fileid) - extkey_b->fileid;
565
diff = grub_be_to_cpu32 (extkey_a->start) - extkey_b->start;
570
grub_hfsplus_read_symlink (grub_fshelp_node_t node)
573
grub_ssize_t numread;
575
symlink = grub_malloc (node->size + 1);
579
numread = grub_hfsplus_read_file (node, 0, 0, node->size, symlink);
580
if (numread != (grub_ssize_t) node->size)
585
symlink[node->size] = '\0';
591
grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree *btree,
592
struct grub_hfsplus_btnode *first_node,
594
int (*hook) (void *record))
600
char *cnode = (char *) first_node;
602
/* Iterate over all records in this node. */
603
for (rec = first_rec; rec < grub_be_to_cpu16 (first_node->count); rec++)
605
if (hook (grub_hfsplus_btree_recptr (btree, first_node, rec)))
609
if (! first_node->next)
612
if (grub_hfsplus_read_file (&btree->file, 0,
613
(grub_be_to_cpu32 (first_node->next)
615
btree->nodesize, cnode) <= 0)
618
/* Don't skip any record in the next iteration. */
625
/* Lookup the node described by KEY in the B+ Tree BTREE. Compare
626
keys using the function COMPARE_KEYS. When a match is found,
627
return the node in MATCHNODE and a pointer to the data in this node
628
in KEYOFFSET. MATCHNODE should be freed by the caller. */
630
grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
631
struct grub_hfsplus_key_internal *key,
632
int (*compare_keys) (struct grub_hfsplus_key *keya,
633
struct grub_hfsplus_key_internal *keyb),
634
struct grub_hfsplus_btnode **matchnode, int *keyoffset)
636
grub_uint64_t currnode;
638
struct grub_hfsplus_btnode *nodedesc;
641
node = grub_malloc (btree->nodesize);
645
currnode = btree->root;
651
if (grub_hfsplus_read_file (&btree->file, 0,
652
(long)currnode * (long)btree->nodesize,
653
btree->nodesize, (char *) node) <= 0)
656
return grub_error (GRUB_ERR_BAD_FS, "couldn't read i-node");
659
nodedesc = (struct grub_hfsplus_btnode *) node;
661
/* Find the record in this tree. */
662
for (rec = 0; rec < grub_be_to_cpu16 (nodedesc->count); rec++)
664
struct grub_hfsplus_key *currkey;
665
currkey = grub_hfsplus_btree_recptr (btree, nodedesc, rec);
667
/* The action that has to be taken depend on the type of
669
if (nodedesc->type == GRUB_HFSPLUS_BTNODE_TYPE_LEAF
670
&& compare_keys (currkey, key) == 0)
672
/* An exact match was found! */
674
*matchnode = nodedesc;
679
else if (nodedesc->type == GRUB_HFSPLUS_BTNODE_TYPE_INDEX)
681
grub_uint32_t *pointer;
683
/* The place where the key could have been found didn't
684
contain the key. This means that the previous match
685
is the one that should be followed. */
686
if (compare_keys (currkey, key) > 0)
689
/* Mark the last key which is lower or equal to the key
690
that we are looking for. The last match that is
691
found will be used to locate the child which can
692
contain the record. */
693
pointer = (grub_uint32_t *) ((char *) currkey
694
+ grub_be_to_cpu16 (currkey->keylen)
696
currnode = grub_be_to_cpu32 (*pointer);
701
/* No match is found, no record with this key exists in the
713
grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
715
(*hook) (const char *filename,
716
enum grub_fshelp_filetype filetype,
717
grub_fshelp_node_t node))
721
auto int list_nodes (void *record);
722
int list_nodes (void *record)
724
struct grub_hfsplus_catkey *catkey;
727
struct grub_fshelp_node *node;
728
struct grub_hfsplus_catfile *fileinfo;
729
enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
731
catkey = (struct grub_hfsplus_catkey *) record;
734
(struct grub_hfsplus_catfile *) ((char *) record
735
+ grub_be_to_cpu16 (catkey->keylen)
736
+ 2 + (grub_be_to_cpu16(catkey->keylen)
739
/* Stop iterating when the last directory entry is found. */
740
if (grub_be_to_cpu32 (catkey->parent) != dir->fileid)
743
/* Determine the type of the node that is found. */
744
if (grub_be_to_cpu16 (fileinfo->type) == GRUB_HFSPLUS_FILETYPE_REG)
746
int mode = (grub_be_to_cpu16 (fileinfo->mode)
747
& GRUB_HFSPLUS_FILEMODE_MASK);
749
if (mode == GRUB_HFSPLUS_FILEMODE_REG)
750
type = GRUB_FSHELP_REG;
751
else if (mode == GRUB_HFSPLUS_FILEMODE_SYMLINK)
752
type = GRUB_FSHELP_SYMLINK;
754
type = GRUB_FSHELP_UNKNOWN;
756
else if (grub_be_to_cpu16 (fileinfo->type) == GRUB_HFSPLUS_FILETYPE_DIR)
757
type = GRUB_FSHELP_DIR;
759
if (type == GRUB_FSHELP_UNKNOWN)
762
/* Make sure the byte order of the UTF16 string is correct. */
763
for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++)
765
catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]);
767
/* If the name is obviously invalid, skip this node. */
768
if (catkey->name[i] == 0)
772
filename = grub_malloc (grub_be_to_cpu16 (catkey->namelen) + 1);
776
if (! grub_utf16_to_utf8 ((grub_uint8_t *) filename, catkey->name,
777
grub_be_to_cpu16 (catkey->namelen)))
779
grub_free (filename);
783
filename[grub_be_to_cpu16 (catkey->namelen)] = '\0';
785
/* Restore the byte order to what it was previously. */
786
for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++)
787
catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]);
789
/* hfs+ is case insensitive. */
790
if (! dir->data->case_sensitive)
791
type |= GRUB_FSHELP_CASE_INSENSITIVE;
793
/* Only accept valid nodes. */
794
if (grub_strlen (filename) == grub_be_to_cpu16 (catkey->namelen))
796
/* A valid node is found; setup the node and call the
797
callback function. */
798
node = grub_malloc (sizeof (*node));
799
node->data = dir->data;
801
grub_memcpy (node->extents, fileinfo->data.extents,
802
sizeof (node->extents));
803
node->mtime = grub_be_to_cpu32 (fileinfo->mtime) - 2082844800;
804
node->size = grub_be_to_cpu64 (fileinfo->data.size);
805
node->fileid = grub_be_to_cpu32 (fileinfo->fileid);
807
ret = hook (filename, type, node);
810
grub_free (filename);
815
struct grub_hfsplus_key_internal intern;
816
struct grub_hfsplus_btnode *node;
819
/* Create a key that points to the first entry in the directory. */
820
intern.catkey.parent = dir->fileid;
821
intern.catkey.name = "";
823
/* First lookup the first entry. */
824
if (grub_hfsplus_btree_search (&dir->data->catalog_tree, &intern,
825
grub_hfsplus_cmp_catkey, &node, &ptr))
828
/* Iterate over all entries in this directory. */
829
grub_hfsplus_btree_iterate_node (&dir->data->catalog_tree, node, ptr,
837
/* Open a file named NAME and initialize FILE. */
839
grub_hfsplus_open (struct grub_file *file, const char *name)
841
struct grub_hfsplus_data *data;
842
struct grub_fshelp_node *fdiro = 0;
844
grub_dl_ref (my_mod);
846
data = grub_hfsplus_mount (file->device->disk);
850
grub_fshelp_find_file (name, &data->dirroot, &fdiro,
851
grub_hfsplus_iterate_dir,
852
grub_hfsplus_read_symlink, GRUB_FSHELP_REG);
856
file->size = fdiro->size;
857
data->opened_file = *fdiro;
866
if (data && fdiro != &data->dirroot)
870
grub_dl_unref (my_mod);
877
grub_hfsplus_close (grub_file_t file)
879
grub_free (file->data);
881
grub_dl_unref (my_mod);
883
return GRUB_ERR_NONE;
887
/* Read LEN bytes data from FILE into BUF. */
889
grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len)
891
struct grub_hfsplus_data *data =
892
(struct grub_hfsplus_data *) file->data;
894
int size = grub_hfsplus_read_file (&data->opened_file, file->read_hook,
895
file->offset, len, buf);
902
grub_hfsplus_dir (grub_device_t device, const char *path,
903
int (*hook) (const char *filename,
904
const struct grub_dirhook_info *info))
906
struct grub_hfsplus_data *data = 0;
907
struct grub_fshelp_node *fdiro = 0;
909
auto int NESTED_FUNC_ATTR iterate (const char *filename,
910
enum grub_fshelp_filetype filetype,
911
grub_fshelp_node_t node);
913
int NESTED_FUNC_ATTR iterate (const char *filename,
914
enum grub_fshelp_filetype filetype,
915
grub_fshelp_node_t node)
917
struct grub_dirhook_info info;
918
grub_memset (&info, 0, sizeof (info));
919
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
921
info.mtime = node->mtime;
922
info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE);
924
return hook (filename, &info);
927
grub_dl_ref (my_mod);
929
data = grub_hfsplus_mount (device->disk);
933
/* Find the directory that should be opened. */
934
grub_fshelp_find_file (path, &data->dirroot, &fdiro,
935
grub_hfsplus_iterate_dir,
936
grub_hfsplus_read_symlink, GRUB_FSHELP_DIR);
940
/* Iterate over all entries in this directory. */
941
grub_hfsplus_iterate_dir (fdiro, iterate);
944
if (data && fdiro != &data->dirroot)
948
grub_dl_unref (my_mod);
955
grub_hfsplus_label (grub_device_t device __attribute__((unused))
956
, char **label __attribute__((unused)))
958
/* XXX: It's not documented how to read a label. */
959
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
960
"reading the label of a HFS+ "
961
"partition is not implemented");
966
grub_hfsplus_mtime (grub_device_t device, grub_int32_t *tm)
968
struct grub_hfsplus_data *data;
969
grub_disk_t disk = device->disk;
971
grub_dl_ref (my_mod);
973
data = grub_hfsplus_mount (disk);
977
*tm = grub_be_to_cpu32 (data->volheader.utime) - 2082844800;
979
grub_dl_unref (my_mod);
988
grub_hfsplus_uuid (grub_device_t device, char **uuid)
990
struct grub_hfsplus_data *data;
991
grub_disk_t disk = device->disk;
993
grub_dl_ref (my_mod);
995
data = grub_hfsplus_mount (disk);
998
*uuid = grub_xasprintf ("%016llx",
1000
grub_be_to_cpu64 (data->volheader.num_serial));
1005
grub_dl_unref (my_mod);
1014
static struct grub_fs grub_hfsplus_fs =
1017
.dir = grub_hfsplus_dir,
1018
.open = grub_hfsplus_open,
1019
.read = grub_hfsplus_read,
1020
.close = grub_hfsplus_close,
1021
.label = grub_hfsplus_label,
1022
.mtime = grub_hfsplus_mtime,
1023
.uuid = grub_hfsplus_uuid,
1025
.reserved_first_sector = 1,
1030
GRUB_MOD_INIT(hfsplus)
1032
grub_fs_register (&grub_hfsplus_fs);
1036
GRUB_MOD_FINI(hfsplus)
1038
grub_fs_unregister (&grub_hfsplus_fs);