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>
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
blockcnt = ((len + pos)
247
+ data->blksz - 1) / data->blksz;
249
for (i = pos / data->blksz; i < blockcnt; i++)
252
int blockoff = pos % data->blksz;
253
int blockend = data->blksz;
257
blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
262
if (i == blockcnt - 1)
264
blockend = (len + pos) % data->blksz;
266
/* The last portion is exactly EXT2_BLOCK_SIZE (data). */
268
blockend = data->blksz;
272
if (i == pos / data->blksz)
274
skipfirst = blockoff;
275
blockend -= skipfirst;
278
/* If the block number is 0 this block is not stored on disk but
279
is zero filled instead. */
282
data->disk->read_hook = read_hook;
283
grub_disk_read (data->disk, blknr, skipfirst,
285
data->disk->read_hook = 0;
290
buf += data->blksz - skipfirst;
297
/* Mount the filesystem on the disk DISK. */
298
static struct grub_hfs_data *
299
grub_hfs_mount (grub_disk_t disk)
301
struct grub_hfs_data *data;
302
struct grub_hfs_catalog_key key;
303
struct grub_hfs_dirrec dir;
308
struct grub_hfs_node node;
309
struct grub_hfs_treeheader head;
312
data = grub_malloc (sizeof (struct grub_hfs_data));
316
/* Read the superblock. */
317
if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
318
sizeof (struct grub_hfs_sblock), &data->sblock))
321
/* Check if this is a HFS filesystem. */
322
if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC)
324
grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
328
/* Check if this is an embedded HFS+ filesystem. */
329
if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
331
grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
335
data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
338
/* Lookup the root node of the extent overflow tree. */
339
first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
341
+ grub_be_to_cpu16 (data->sblock.first_block));
343
if (grub_disk_read (data->disk, first_block, 0,
344
sizeof (treehead), &treehead))
346
data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
347
data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
349
/* Lookup the root node of the catalog tree. */
350
first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
352
+ grub_be_to_cpu16 (data->sblock.first_block));
353
if (grub_disk_read (data->disk, first_block, 0,
354
sizeof (treehead), &treehead))
356
data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
357
data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
359
/* Lookup the root directory node in the catalog tree using the
361
key.parent_dir = grub_cpu_to_be32 (1);
362
key.strlen = data->sblock.volname[0];
363
grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
365
if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
366
0, (char *) &dir, sizeof (dir)) == 0)
368
grub_error (GRUB_ERR_BAD_FS, "cannot find the HFS root directory");
375
data->rootdir = grub_be_to_cpu32 (dir.dirid);
381
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
382
grub_error (GRUB_ERR_BAD_FS, "not a HFS filesystem");
387
/* Compare the K1 and K2 catalog file keys using HFS character ordering. */
389
grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
390
struct grub_hfs_catalog_key *k2)
392
/* Taken from hfsutils 3.2.6 and converted to a readable form */
393
static const unsigned char hfs_charorder[256] = {
426
[' '] = 32, [0xCA] = 32,
465
['A'] = 71, ['a'] = 71,
466
[0x88] = 72, [0xCB] = 72,
467
[0x80] = 73, [0x8A] = 73,
468
[0x8B] = 74, [0xCC] = 74,
469
[0x81] = 75, [0x8C] = 75,
470
[0xAE] = 76, [0xBE] = 76,
475
['B'] = 81, ['b'] = 81,
476
['C'] = 82, ['c'] = 82,
477
[0x82] = 83, [0x8D] = 83,
478
['D'] = 84, ['d'] = 84,
479
['E'] = 85, ['e'] = 85,
480
[0x83] = 86, [0x8E] = 86,
484
['F'] = 90, ['f'] = 90,
485
['G'] = 91, ['g'] = 91,
486
['H'] = 92, ['h'] = 92,
487
['I'] = 93, ['i'] = 93,
492
['J'] = 98, ['j'] = 98,
493
['K'] = 99, ['k'] = 99,
494
['L'] = 100, ['l'] = 100,
495
['M'] = 101, ['m'] = 101,
496
['N'] = 102, ['n'] = 102,
497
[0x84] = 103, [0x96] = 103,
498
['O'] = 104, ['o'] = 104,
499
[0x85] = 105, [0x9A] = 105,
500
[0x9B] = 106, [0xCD] = 106,
501
[0xAF] = 107, [0xBF] = 107,
502
[0xCE] = 108, [0xCF] = 108,
507
['P'] = 113, ['p'] = 113,
508
['Q'] = 114, ['q'] = 114,
509
['R'] = 115, ['r'] = 115,
510
['S'] = 116, ['s'] = 116,
512
['T'] = 118, ['t'] = 118,
513
['U'] = 119, ['u'] = 119,
514
[0x86] = 120, [0x9F] = 120,
518
['V'] = 124, ['v'] = 124,
519
['W'] = 125, ['w'] = 125,
520
['X'] = 126, ['x'] = 126,
521
['Y'] = 127, ['y'] = 127,
523
['Z'] = 129, ['z'] = 129,
613
int minlen = (k1->strlen < k2->strlen) ? k1->strlen : k2->strlen;
615
cmp = (grub_be_to_cpu32 (k1->parent_dir) - grub_be_to_cpu32 (k2->parent_dir));
619
for (i = 0; i < minlen; i++)
621
cmp = (hfs_charorder[k1->str[i]] - hfs_charorder[k2->str[i]]);
626
/* Shorter strings precede long ones. */
627
return (k1->strlen - k2->strlen);
631
/* Compare the K1 and K2 extent overflow file keys. */
633
grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
634
struct grub_hfs_extent_key *k2)
636
int cmp = k1->forktype - k2->forktype;
638
cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
640
cmp = (grub_be_to_cpu16 (k1->first_block)
641
- grub_be_to_cpu16 (k2->first_block));
646
/* Iterate the records in the node with index IDX in the mounted HFS
647
filesystem DATA. This node holds data of the type TYPE (0 =
648
catalog node, 1 = extent overflow node). If this is set, continue
649
iterating to the next node. For every records, call NODE_HOOK. */
651
grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
652
int this, int (*node_hook) (struct grub_hfs_node *hnd,
653
struct grub_hfs_record *))
655
int nodesize = type == 0 ? data->cat_size : data->ext_size;
659
struct grub_hfs_node node;
660
char rawnode[nodesize];
661
grub_uint16_t offsets[nodesize / 2];
667
struct grub_hfs_extent *dat;
670
dat = (struct grub_hfs_extent *) (type == 0
671
? (&data->sblock.catalog_recs)
672
: (&data->sblock.extent_recs));
674
/* Read the node into memory. */
675
blk = grub_hfs_block (data, dat,
676
(type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT,
677
idx / (data->blksz / nodesize), 0);
678
blk += (idx % (data->blksz / nodesize));
682
if (grub_disk_read (data->disk, blk, 0,
683
sizeof (node), &node))
686
/* Iterate over all records in this node. */
687
for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
689
int pos = (nodesize >> 1) - 1 - i;
694
} __attribute__ ((packed)) *pnt;
695
pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
698
struct grub_hfs_record rec =
702
&pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
703
nodesize - grub_be_to_cpu16 (node.offsets[pos])
707
if (node_hook (&node.node, &rec))
711
idx = grub_be_to_cpu32 (node.node.next);
712
} while (idx && this);
718
/* Lookup a record in the mounted filesystem DATA using the key KEY.
719
The index of the node on top of the tree is IDX. The tree is of
720
the type TYPE (0 = catalog node, 1 = extent overflow node). Return
721
the data in DATAR with a maximum length of DATALEN. */
723
grub_hfs_find_node (struct grub_hfs_data *data, char *key,
724
grub_uint32_t idx, int type, char *datar, int datalen)
730
auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
732
int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
737
cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
739
cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
741
/* If the key is smaller or equal to the current node, mark the
742
entry. In case of a non-leaf mode it will be used to lookup
743
the rest of the tree. */
746
grub_uint32_t *node = (grub_uint32_t *) rec->data;
747
found = grub_be_to_cpu32 (*node);
749
else /* The key can not be found in the tree. */
752
/* Check if this node is a leaf node. */
753
if (hnd->type == GRUB_HFS_NODE_LEAF)
762
grub_memcpy (datar, rec->data,
763
rec->datalen < datalen ? rec->datalen : datalen);
775
if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
788
/* Iterate over the directory with the id DIR. The tree is searched
789
starting with the node ROOT_IDX. For every entry in this directory
792
grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
793
unsigned int dir, int (*hook) (struct grub_hfs_record *))
799
/* The lowest key possible with DIR as root directory. */
800
struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
802
auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
803
auto int it_dir (struct grub_hfs_node * __attribute ((unused)),
804
struct grub_hfs_record *);
807
int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
809
struct grub_hfs_catalog_key *ckey = rec->key;
811
if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
812
found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
814
if (hnd->type == 0xFF && ckey->strlen > 0)
817
next = grub_be_to_cpu32 (hnd->next);
819
/* An entry was found. */
820
if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
827
int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
828
struct grub_hfs_record *rec)
830
struct grub_hfs_catalog_key *ckey = rec->key;
831
struct grub_hfs_catalog_key *origkey = &key;
833
/* Stop when the entries do not match anymore. */
834
if (grub_be_to_cpu32 (ckey->parent_dir)
835
!= grub_be_to_cpu32 ((origkey)->parent_dir))
845
if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
854
/* If there was a matching record in this leaf node, continue the
855
iteration until the last record was found. */
856
grub_hfs_iterate_records (data, 0, next, 1, it_dir);
861
/* Find a file or directory with the pathname PATH in the filesystem
862
DATA. Return the file record in RETDATA when it is non-zero.
863
Return the directory number in RETINODE when it is non-zero. */
865
grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
866
struct grub_hfs_filerec *retdata, int *retinode)
868
int inode = data->rootdir;
872
struct grub_hfs_filerec frec;
873
struct grub_hfs_dirrec dir;
876
fdrec.frec.type = GRUB_HFS_FILETYPE_DIR;
880
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
884
origpath = grub_strdup (path);
892
while (path && grub_strlen (path))
894
if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR)
896
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
900
/* Isolate a part of the path. */
901
next = grub_strchr (path, '/');
908
struct grub_hfs_catalog_key key;
910
key.parent_dir = grub_cpu_to_be32 (inode);
911
key.strlen = grub_strlen (path);
912
grub_strcpy ((char *) (key.str), path);
914
/* Lookup this node. */
915
if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
916
0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
918
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
925
inode = grub_be_to_cpu32 (fdrec.dir.dirid);
930
grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec));
936
grub_free (origpath);
943
grub_hfs_dir (grub_device_t device, const char *path,
944
int (*hook) (const char *filename,
945
const struct grub_dirhook_info *info))
949
auto int dir_hook (struct grub_hfs_record *rec);
951
int dir_hook (struct grub_hfs_record *rec)
953
char fname[32] = { 0 };
954
char *filetype = rec->data;
955
struct grub_hfs_catalog_key *ckey = rec->key;
956
struct grub_dirhook_info info;
957
grub_memset (&info, 0, sizeof (info));
959
grub_strncpy (fname, (char *) (ckey->str), ckey->strlen);
961
if (*filetype == GRUB_HFS_FILETYPE_DIR
962
|| *filetype == GRUB_HFS_FILETYPE_FILE)
964
info.dir = (*filetype == GRUB_HFS_FILETYPE_DIR);
965
return hook (fname, &info);
970
struct grub_hfs_data *data;
971
struct grub_hfs_filerec frec;
973
grub_dl_ref (my_mod);
975
data = grub_hfs_mount (device->disk);
979
/* First the directory ID for the directory. */
980
if (grub_hfs_find_dir (data, path, &frec, &inode))
983
if (frec.type != GRUB_HFS_FILETYPE_DIR)
985
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
989
grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
994
grub_dl_unref (my_mod);
1000
/* Open a file named NAME and initialize FILE. */
1002
grub_hfs_open (struct grub_file *file, const char *name)
1004
struct grub_hfs_data *data;
1005
struct grub_hfs_filerec frec;
1007
grub_dl_ref (my_mod);
1009
data = grub_hfs_mount (file->device->disk);
1011
if (grub_hfs_find_dir (data, name, &frec, 0))
1014
grub_dl_unref (my_mod);
1018
if (frec.type != GRUB_HFS_FILETYPE_FILE)
1021
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
1022
grub_dl_unref (my_mod);
1026
grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
1027
file->size = grub_be_to_cpu32 (frec.size);
1028
data->size = grub_be_to_cpu32 (frec.size);
1029
data->fileid = grub_be_to_cpu32 (frec.fileid);
1038
grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
1040
struct grub_hfs_data *data =
1041
(struct grub_hfs_data *) file->data;
1043
return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
1048
grub_hfs_close (grub_file_t file)
1050
grub_free (file->data);
1052
grub_dl_unref (my_mod);
1059
grub_hfs_label (grub_device_t device, char **label)
1061
struct grub_hfs_data *data;
1063
data = grub_hfs_mount (device->disk);
1066
*label = grub_strndup ((char *) (data->sblock.volname + 1),
1067
*data->sblock.volname);
1076
grub_hfs_uuid (grub_device_t device, char **uuid)
1078
struct grub_hfs_data *data;
1080
grub_dl_ref (my_mod);
1082
data = grub_hfs_mount (device->disk);
1083
if (data && data->sblock.num_serial != 0)
1085
*uuid = grub_xasprintf ("%016llx",
1086
(unsigned long long)
1087
grub_be_to_cpu64 (data->sblock.num_serial));
1092
grub_dl_unref (my_mod);
1101
static struct grub_fs grub_hfs_fs =
1104
.dir = grub_hfs_dir,
1105
.open = grub_hfs_open,
1106
.read = grub_hfs_read,
1107
.close = grub_hfs_close,
1108
.label = grub_hfs_label,
1109
.uuid = grub_hfs_uuid,
1115
grub_fs_register (&grub_hfs_fs);
1121
grub_fs_unregister (&grub_hfs_fs);