3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2004, 2005 Free Software Foundation, Inc.
6
* This program 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 2 of the License, or
9
* (at your option) any later version.
11
* This program 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 this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
#include <grub/file.h>
24
#include <grub/misc.h>
25
#include <grub/disk.h>
27
#include <grub/types.h>
29
#define GRUB_HFS_SBLOCK 2
30
#define GRUB_HFS_MAGIC 0x4244
32
#define GRUB_HFS_BLKS (data->blksz >> 9)
34
#define GRUB_HFS_NODE_LEAF 0xFF
36
/* The two supported filesystems a record can have. */
39
GRUB_HFS_FILETYPE_DIR = 1,
40
GRUB_HFS_FILETYPE_FILE = 2
43
/* A single extent. A file consists of suchs extents. */
44
struct grub_hfs_extent
46
/* The first physical block. */
47
grub_uint16_t first_block;
51
/* HFS stores extents in groups of 3. */
52
typedef struct grub_hfs_extent grub_hfs_datarecord_t[3];
54
/* The HFS superblock (The official name is `Master Directory
56
struct grub_hfs_sblock
59
grub_uint8_t unused[18];
61
grub_uint8_t unused2[4];
62
grub_uint16_t first_block;
63
grub_uint8_t unused4[6];
65
/* A pascal style string that holds the volumename. */
66
grub_uint8_t volname[28];
68
grub_uint8_t unused5[70];
69
grub_hfs_datarecord_t extent_recs;
70
grub_uint32_t catalog_size;
71
grub_hfs_datarecord_t catalog_recs;
72
} __attribute__ ((packed));
74
/* A node desciptor. This is the header of every node. */
83
} __attribute__ ((packed));
85
/* The head of the B*-Tree. */
86
struct grub_hfs_treeheader
88
grub_uint16_t tree_depth;
89
/* The number of the first node. */
90
grub_uint32_t root_node;
92
grub_uint32_t first_leaf;
93
grub_uint32_t last_leaf;
94
grub_uint16_t node_size;
95
grub_uint16_t key_size;
97
grub_uint32_t free_nodes;
98
grub_uint8_t unused[76];
99
} __attribute__ ((packed));
101
/* The state of a mounted HFS filesystem. */
104
struct grub_hfs_sblock sblock;
106
grub_hfs_datarecord_t extents;
118
/* The key as used on disk in a catalog tree. This is used to lookup
119
file/directory nodes by parent directory ID and filename. */
120
struct grub_hfs_catalog_key
123
grub_uint32_t parent_dir;
125
/* Filename length. */
129
grub_uint8_t str[31];
130
} __attribute__ ((packed));
132
/* The key as used on disk in a extent overflow tree. Using this key
133
the extents can be looked up using a fileid and logical start block
135
struct grub_hfs_extent_key
137
/* The kind of fork. This is used to store meta information like
138
icons, attributes, etc. We will only use the datafork, which is
140
grub_uint8_t forktype;
141
grub_uint32_t fileid;
142
grub_uint16_t first_block;
143
} __attribute__ ((packed));
145
/* A dirrect record. This is used to find out the directory ID. */
146
struct grub_hfs_dirrec
148
/* For a directory, type == 1. */
150
grub_uint8_t unused[5];
152
} __attribute__ ((packed));
154
/* Information about a file. */
155
struct grub_hfs_filerec
157
/* For a file, type == 2. */
159
grub_uint8_t unused[19];
160
grub_uint32_t fileid;
161
grub_uint8_t unused2[2];
163
grub_uint8_t unused3[44];
165
/* The first 3 extents of the file. The other extents can be found
166
in the extent overflow file. */
167
grub_hfs_datarecord_t extents;
168
} __attribute__ ((packed));
170
/* A record descriptor, both key and data, used to pass to call back
172
struct grub_hfs_record
181
static grub_dl_t my_mod;
184
static int grub_hfs_find_node (struct grub_hfs_data *, char *,
185
grub_uint32_t, int, char *, int);
187
/* Find block BLOCK of the file FILE in the mounted UFS filesystem
188
DATA. The first 3 extents are described by DAT. If cache is set,
189
using caching to improve non-random reads. */
191
grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat,
192
int file, int block, int cache)
194
grub_hfs_datarecord_t dr;
196
struct grub_hfs_extent_key key;
199
static int cache_file = 0;
200
static int cache_pos = 0;
201
static grub_hfs_datarecord_t cache_dr;
203
grub_memcpy (dr, dat, sizeof (dr));
206
key.fileid = grub_cpu_to_be32 (file);
208
if (cache && cache_file == file && block > cache_pos)
211
key.first_block = grub_cpu_to_be16 (pos);
212
grub_memcpy (dr, cache_dr, sizeof (cache_dr));
219
/* Try all 3 extents. */
220
for (i = 0; i < 3; i++)
222
/* Check if the block is stored in this extent. */
223
if (grub_be_to_cpu16 (dr[i].count) + pos > block)
225
int first = grub_be_to_cpu16 (dr[i].first_block);
227
/* If the cache is enabled, store the current position
233
grub_memcpy (cache_dr, dr, sizeof (cache_dr));
236
return (grub_be_to_cpu16 (data->sblock.first_block)
237
+ (first + block - pos) * GRUB_HFS_BLKS);
240
/* Try the next extent. */
241
pos += grub_be_to_cpu16 (dr[i].count);
244
/* Lookup the block in the extent overflow file. */
245
key.first_block = grub_cpu_to_be16 (pos);
247
grub_hfs_find_node (data, (char *) &key, data->ext_root,
248
1, (char *) &dr, sizeof (dr));
255
/* Read LEN bytes from the file described by DATA starting with byte
256
POS. Return the amount of read bytes in READ. */
258
grub_hfs_read_file (struct grub_hfs_data *data,
259
void (*read_hook) (unsigned long sector,
260
unsigned offset, unsigned length),
261
int pos, unsigned int len, char *buf)
266
/* Adjust len so it we can't read past the end of the file. */
267
if (len > grub_le_to_cpu32 (data->size))
268
len = grub_le_to_cpu32 (data->size);
270
blockcnt = ((len + pos)
271
+ data->blksz - 1) / data->blksz;
273
for (i = pos / data->blksz; i < blockcnt; i++)
276
int blockoff = pos % data->blksz;
277
int blockend = data->blksz;
281
blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
286
if (i == blockcnt - 1)
288
blockend = (len + pos) % data->blksz;
290
/* The last portion is exactly EXT2_BLOCK_SIZE (data). */
292
blockend = data->blksz;
296
if (i == pos / data->blksz)
298
skipfirst = blockoff;
299
blockend -= skipfirst;
302
/* If the block number is 0 this block is not stored on disk but
303
is zero filled instead. */
306
data->disk->read_hook = read_hook;
307
grub_disk_read (data->disk, blknr, skipfirst,
309
data->disk->read_hook = 0;
314
buf += data->blksz - skipfirst;
321
/* Mount the filesystem on the disk DISK. */
322
static struct grub_hfs_data *
323
grub_hfs_mount (grub_disk_t disk)
325
struct grub_hfs_data *data;
326
struct grub_hfs_catalog_key key;
327
struct grub_hfs_dirrec dir;
332
struct grub_hfs_node node;
333
struct grub_hfs_treeheader head;
336
data = grub_malloc (sizeof (struct grub_hfs_data));
340
/* Read the superblock. */
341
if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
342
sizeof (struct grub_hfs_sblock), (char *) &data->sblock))
345
/* Check if this is a HFS filesystem. */
346
if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC)
348
grub_error (GRUB_ERR_BAD_FS, "not a hfs filesystem");
352
data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
355
/* Lookup the root node of the extent overflow tree. */
356
first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
358
+ grub_be_to_cpu16 (data->sblock.first_block));
360
if (grub_disk_read (data->disk, first_block, 0,
361
sizeof (treehead), (char *) &treehead))
363
data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
364
data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
366
/* Lookup the root node of the catalog tree. */
367
first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
369
+ grub_be_to_cpu16 (data->sblock.first_block));
370
if (grub_disk_read (data->disk, first_block, 0,
371
sizeof (treehead), (char *) &treehead))
373
data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
374
data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
376
/* Lookup the root directory node in the catalog tree using the
378
key.parent_dir = grub_cpu_to_be32 (1);
379
key.strlen = data->sblock.volname[0];
380
grub_strcpy (key.str, 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, "can not 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");
405
/* Compare the K1 and K2 catalog file keys. */
407
grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
408
struct grub_hfs_catalog_key *k2)
410
int cmp = (grub_be_to_cpu32 (k1->parent_dir)
411
- grub_be_to_cpu32 (k2->parent_dir));
416
cmp = grub_strncasecmp (k1->str, k2->str, k1->strlen);
418
/* This is required because the compared strings are not of equal
420
if (cmp == 0 && k1->strlen < k2->strlen)
426
/* Compare the K1 and K2 extent overflow file keys. */
428
grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
429
struct grub_hfs_extent_key *k2)
431
int cmp = k1->forktype - k2->forktype;
433
cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
435
cmp = (grub_be_to_cpu16 (k1->first_block)
436
- grub_be_to_cpu16 (k2->first_block));
441
/* Iterate the records in the node with index IDX in the mounted HFS
442
filesystem DATA. This node holds data of the type TYPE (0 =
443
catalog node, 1 = extent overflow node). If this is set, continue
444
iterating to the next node. For every records, call NODE_HOOK. */
446
grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
447
int this, int (*node_hook) (struct grub_hfs_node *hnd,
448
struct grub_hfs_record *))
450
int nodesize = type == 0 ? data->cat_size : data->ext_size;
454
struct grub_hfs_node node;
455
char rawnode[nodesize];
456
grub_uint16_t offsets[nodesize / 2];
462
struct grub_hfs_extent *dat;
465
dat = (struct grub_hfs_extent *) (type == 0
466
? (&data->sblock.catalog_recs)
467
: (&data->sblock.extent_recs));
469
/* Read the node into memory. */
470
blk = grub_hfs_block (data, dat,
471
0, idx / (data->blksz / nodesize), 0);
472
blk += (idx % (data->blksz / nodesize));
476
if (grub_disk_read (data->disk, blk, 0,
477
sizeof (node), (char *) &node))
480
/* Iterate over all records in this node. */
481
for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
483
int pos = (nodesize >> 1) - 1 - i;
488
} __attribute__ ((packed)) *pnt;
489
pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
492
struct grub_hfs_record rec =
496
&pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
497
nodesize - grub_be_to_cpu16 (node.offsets[pos])
501
if (node_hook (&node.node, &rec))
505
if (idx % (data->blksz / nodesize) == 0)
506
idx = grub_be_to_cpu32 (node.node.next);
509
} while (idx && this);
515
/* Lookup a record in the mounted filesystem DATA using the key KEY.
516
The index of the node on top of the tree is IDX. The tree is of
517
the type TYPE (0 = catalog node, 1 = extent overflow node). Return
518
the data in DATAR with a maximum length of DATALEN. */
520
grub_hfs_find_node (struct grub_hfs_data *data, char *key,
521
grub_uint32_t idx, int type, char *datar, int datalen)
526
auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
528
int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
533
cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
535
cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
537
/* If the key is smaller or equal to the currect node, mark the
538
entry. In case of a non-leaf mode it will be used to lookup
539
the rest of the tree. */
542
grub_uint32_t *node = (grub_uint32_t *) rec->data;
543
found = grub_be_to_cpu32 (*node);
545
else /* The key can not be found in the tree. */
548
/* Check if this node is a leaf node. */
549
if (hnd->type == GRUB_HFS_NODE_LEAF)
556
grub_memcpy (datar, rec->data,
557
rec->datalen < datalen ? rec->datalen : datalen);
565
if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
574
return grub_hfs_find_node (data, key, found, type, datar, datalen);
578
/* Iterate over the directory with the id DIR. The tree is searched
579
starting with the node ROOT_IDX. For every entry in this directory
582
grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
583
unsigned int dir, int (*hook) (struct grub_hfs_record *))
589
/* The lowest key possible with DIR as root directory. */
590
struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
592
auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
593
auto int it_dir (struct grub_hfs_node * __attribute ((unused)),
594
struct grub_hfs_record *);
597
int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
599
struct grub_hfs_catalog_key *ckey = rec->key;
601
if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
602
found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
604
if (hnd->type == 0xFF && ckey->strlen > 0)
607
next = grub_be_to_cpu32 (hnd->next);
609
/* An entry was found. */
610
if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
617
int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
618
struct grub_hfs_record *rec)
620
struct grub_hfs_catalog_key *ckey = rec->key;
621
struct grub_hfs_catalog_key *origkey = &key;
623
/* Stop when the entries do not match anymore. */
624
if (grub_be_to_cpu32 (ckey->parent_dir)
625
!= grub_be_to_cpu32 ((origkey)->parent_dir))
631
if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
637
/* If there was a matching record in this leaf node, continue the
638
iteration until the last record was found. */
641
grub_hfs_iterate_records (data, 0, next, 1, it_dir);
645
return grub_hfs_iterate_dir (data, found, dir, hook);
649
/* Find a file or directory with the pathname PATH in the filesystem
650
DATA. Return the file record in RETDATA when it is non-zero.
651
Return the directory number in RETINODE when it is non-zero. */
653
grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
654
struct grub_hfs_filerec *retdata, int *retinode)
656
int inode = data->rootdir;
659
struct grub_hfs_filerec frec;
660
struct grub_hfs_dirrec *dir = (struct grub_hfs_dirrec *) &frec;
661
frec.type = GRUB_HFS_FILETYPE_DIR;
665
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
669
origpath = grub_strdup (path);
676
while (path && grub_strlen (path))
678
if (frec.type != GRUB_HFS_FILETYPE_DIR)
680
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
684
/* Isolate a part of the path. */
685
next = grub_strchr (path, '/');
692
struct grub_hfs_catalog_key key;
694
key.parent_dir = grub_cpu_to_be32 (inode);
695
key.strlen = grub_strlen (path);
696
grub_strcpy (key.str, path);
698
/* Lookup this node. */
699
if (!grub_hfs_find_node (data, (char *) &key, data->cat_root,
700
0, (char *) &frec, sizeof (frec)))
702
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
709
inode = grub_be_to_cpu32 (dir->dirid);
714
grub_memcpy (retdata, &frec, sizeof (frec));
720
grub_free (origpath);
727
grub_hfs_dir (grub_device_t device, const char *path,
728
int (*hook) (const char *filename, int dir))
732
auto int dir_hook (struct grub_hfs_record *rec);
734
int dir_hook (struct grub_hfs_record *rec)
736
char fname[32] = { 0 };
737
char *filetype = rec->data;
738
struct grub_hfs_catalog_key *ckey = rec->key;
740
grub_strncpy (fname, ckey->str, ckey->strlen);
742
if (*filetype == GRUB_HFS_FILETYPE_DIR)
743
return hook (fname, 1);
744
else if (*filetype == GRUB_HFS_FILETYPE_FILE)
745
return hook (fname, 0);
749
struct grub_hfs_data *data;
750
struct grub_hfs_filerec frec;
753
grub_dl_ref (my_mod);
756
data = grub_hfs_mount (device->disk);
760
/* First the directory ID for the directory. */
761
if (grub_hfs_find_dir (data, path, &frec, &inode))
764
if (frec.type != GRUB_HFS_FILETYPE_DIR)
766
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
770
grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
776
grub_dl_unref (my_mod);
783
/* Open a file named NAME and initialize FILE. */
785
grub_hfs_open (struct grub_file *file, const char *name)
787
struct grub_hfs_data *data;
788
struct grub_hfs_filerec frec;
791
grub_dl_ref (my_mod);
794
data = grub_hfs_mount (file->device->disk);
796
if (grub_hfs_find_dir (data, name, &frec, 0))
800
grub_dl_unref (my_mod);
805
if (frec.type != GRUB_HFS_FILETYPE_FILE)
808
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
810
grub_dl_unref (my_mod);
815
grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
816
file->size = grub_be_to_cpu32 (frec.size);
817
data->size = grub_be_to_cpu32 (frec.size);
818
data->fileid = grub_be_to_cpu32 (frec.fileid);
827
grub_hfs_read (grub_file_t file, char *buf, grub_ssize_t len)
829
struct grub_hfs_data *data =
830
(struct grub_hfs_data *) file->data;
832
return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
837
grub_hfs_close (grub_file_t file)
839
grub_free (file->data);
842
grub_dl_unref (my_mod);
850
grub_hfs_label (grub_device_t device, char **label)
852
struct grub_hfs_data *data;
854
data = grub_hfs_mount (device->disk);
857
*label = grub_strndup (data->sblock.volname + 1, *data->sblock.volname);
867
static struct grub_fs grub_hfs_fs =
871
.open = grub_hfs_open,
872
.read = grub_hfs_read,
873
.close = grub_hfs_close,
874
.label = grub_hfs_label,
880
grub_fs_register (&grub_hfs_fs);
888
grub_fs_unregister (&grub_hfs_fs);