]> git.proxmox.com Git - grub2.git/blob - fs/hfs.c
2009-01-10 Robert Millan <rmh@aybabtu.com>
[grub2.git] / fs / hfs.c
1 /* hfs.c - HFS. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20 /* HFS is documented at
21 http://developer.apple.com/documentation/mac/Files/Files-2.html */
22
23 #include <grub/err.h>
24 #include <grub/file.h>
25 #include <grub/mm.h>
26 #include <grub/misc.h>
27 #include <grub/disk.h>
28 #include <grub/dl.h>
29 #include <grub/types.h>
30 #include <grub/hfs.h>
31
32 #define GRUB_HFS_SBLOCK 2
33 #define GRUB_HFS_EMBED_HFSPLUS_SIG 0x482B
34
35 #define GRUB_HFS_BLKS (data->blksz >> 9)
36
37 #define GRUB_HFS_NODE_LEAF 0xFF
38
39 /* The two supported filesystems a record can have. */
40 enum
41 {
42 GRUB_HFS_FILETYPE_DIR = 1,
43 GRUB_HFS_FILETYPE_FILE = 2
44 };
45
46 /* Catalog node ID (CNID). */
47 enum grub_hfs_cnid_type
48 {
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,
53 GRUB_HFS_CNID_BAD = 5
54 };
55
56 /* A node descriptor. This is the header of every node. */
57 struct grub_hfs_node
58 {
59 grub_uint32_t next;
60 grub_uint32_t prev;
61 grub_uint8_t type;
62 grub_uint8_t level;
63 grub_uint16_t reccnt;
64 grub_uint16_t unused;
65 } __attribute__ ((packed));
66
67 /* The head of the B*-Tree. */
68 struct grub_hfs_treeheader
69 {
70 grub_uint16_t tree_depth;
71 /* The number of the first node. */
72 grub_uint32_t root_node;
73 grub_uint32_t leaves;
74 grub_uint32_t first_leaf;
75 grub_uint32_t last_leaf;
76 grub_uint16_t node_size;
77 grub_uint16_t key_size;
78 grub_uint32_t nodes;
79 grub_uint32_t free_nodes;
80 grub_uint8_t unused[76];
81 } __attribute__ ((packed));
82
83 /* The state of a mounted HFS filesystem. */
84 struct grub_hfs_data
85 {
86 struct grub_hfs_sblock sblock;
87 grub_disk_t disk;
88 grub_hfs_datarecord_t extents;
89 int fileid;
90 int size;
91 int ext_root;
92 int ext_size;
93 int cat_root;
94 int cat_size;
95 int blksz;
96 int log2_blksz;
97 int rootdir;
98 };
99
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
103 {
104 grub_uint8_t unused;
105 grub_uint32_t parent_dir;
106
107 /* Filename length. */
108 grub_uint8_t strlen;
109
110 /* Filename. */
111 grub_uint8_t str[31];
112 } __attribute__ ((packed));
113
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
116 as index. */
117 struct grub_hfs_extent_key
118 {
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
121 0. */
122 grub_uint8_t forktype;
123 grub_uint32_t fileid;
124 grub_uint16_t first_block;
125 } __attribute__ ((packed));
126
127 /* A dirrect record. This is used to find out the directory ID. */
128 struct grub_hfs_dirrec
129 {
130 /* For a directory, type == 1. */
131 grub_uint8_t type;
132 grub_uint8_t unused[5];
133 grub_uint32_t dirid;
134 } __attribute__ ((packed));
135
136 /* Information about a file. */
137 struct grub_hfs_filerec
138 {
139 /* For a file, type == 2. */
140 grub_uint8_t type;
141 grub_uint8_t unused[19];
142 grub_uint32_t fileid;
143 grub_uint8_t unused2[2];
144 grub_uint32_t size;
145 grub_uint8_t unused3[44];
146
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));
151
152 /* A record descriptor, both key and data, used to pass to call back
153 functions. */
154 struct grub_hfs_record
155 {
156 void *key;
157 int keylen;
158 void *data;
159 int datalen;
160 };
161
162 #ifndef GRUB_UTIL
163 static grub_dl_t my_mod;
164 #endif
165 \f
166 static int grub_hfs_find_node (struct grub_hfs_data *, char *,
167 grub_uint32_t, int, char *, int);
168
169 /* Find block BLOCK of the file FILE in the mounted UFS filesystem
170 DATA. The first 3 extents are described by DAT. If cache is set,
171 using caching to improve non-random reads. */
172 static unsigned int
173 grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat,
174 int file, int block, int cache)
175 {
176 grub_hfs_datarecord_t dr;
177 int pos = 0;
178 struct grub_hfs_extent_key key;
179
180 int tree = 0;
181 static int cache_file = 0;
182 static int cache_pos = 0;
183 static grub_hfs_datarecord_t cache_dr;
184
185 grub_memcpy (dr, dat, sizeof (dr));
186
187 key.forktype = 0;
188 key.fileid = grub_cpu_to_be32 (file);
189
190 if (cache && cache_file == file && block > cache_pos)
191 {
192 pos = cache_pos;
193 key.first_block = grub_cpu_to_be16 (pos);
194 grub_memcpy (dr, cache_dr, sizeof (cache_dr));
195 }
196
197 for (;;)
198 {
199 int i;
200
201 /* Try all 3 extents. */
202 for (i = 0; i < 3; i++)
203 {
204 /* Check if the block is stored in this extent. */
205 if (grub_be_to_cpu16 (dr[i].count) + pos > block)
206 {
207 int first = grub_be_to_cpu16 (dr[i].first_block);
208
209 /* If the cache is enabled, store the current position
210 in the tree. */
211 if (tree && cache)
212 {
213 cache_file = file;
214 cache_pos = pos;
215 grub_memcpy (cache_dr, dr, sizeof (cache_dr));
216 }
217
218 return (grub_be_to_cpu16 (data->sblock.first_block)
219 + (first + block - pos) * GRUB_HFS_BLKS);
220 }
221
222 /* Try the next extent. */
223 pos += grub_be_to_cpu16 (dr[i].count);
224 }
225
226 /* Lookup the block in the extent overflow file. */
227 key.first_block = grub_cpu_to_be16 (pos);
228 tree = 1;
229 grub_hfs_find_node (data, (char *) &key, data->ext_root,
230 1, (char *) &dr, sizeof (dr));
231 if (grub_errno)
232 return 0;
233 }
234 }
235
236
237 /* Read LEN bytes from the file described by DATA starting with byte
238 POS. Return the amount of read bytes in READ. */
239 static grub_ssize_t
240 grub_hfs_read_file (struct grub_hfs_data *data,
241 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
242 unsigned offset, unsigned length),
243 int pos, grub_size_t len, char *buf)
244 {
245 int i;
246 int blockcnt;
247
248 /* Adjust len so it we can't read past the end of the file. */
249 if (len > grub_le_to_cpu32 (data->size))
250 len = grub_le_to_cpu32 (data->size);
251
252 blockcnt = ((len + pos)
253 + data->blksz - 1) / data->blksz;
254
255 for (i = pos / data->blksz; i < blockcnt; i++)
256 {
257 int blknr;
258 int blockoff = pos % data->blksz;
259 int blockend = data->blksz;
260
261 int skipfirst = 0;
262
263 blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
264 if (grub_errno)
265 return -1;
266
267 /* Last block. */
268 if (i == blockcnt - 1)
269 {
270 blockend = (len + pos) % data->blksz;
271
272 /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
273 if (! blockend)
274 blockend = data->blksz;
275 }
276
277 /* First block. */
278 if (i == pos / data->blksz)
279 {
280 skipfirst = blockoff;
281 blockend -= skipfirst;
282 }
283
284 /* If the block number is 0 this block is not stored on disk but
285 is zero filled instead. */
286 if (blknr)
287 {
288 data->disk->read_hook = read_hook;
289 grub_disk_read (data->disk, blknr, skipfirst,
290 blockend, buf);
291 data->disk->read_hook = 0;
292 if (grub_errno)
293 return -1;
294 }
295
296 buf += data->blksz - skipfirst;
297 }
298
299 return len;
300 }
301
302
303 /* Mount the filesystem on the disk DISK. */
304 static struct grub_hfs_data *
305 grub_hfs_mount (grub_disk_t disk)
306 {
307 struct grub_hfs_data *data;
308 struct grub_hfs_catalog_key key;
309 struct grub_hfs_dirrec dir;
310 int first_block;
311
312 struct
313 {
314 struct grub_hfs_node node;
315 struct grub_hfs_treeheader head;
316 } treehead;
317
318 data = grub_malloc (sizeof (struct grub_hfs_data));
319 if (!data)
320 return 0;
321
322 /* Read the superblock. */
323 if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
324 sizeof (struct grub_hfs_sblock), (char *) &data->sblock))
325 goto fail;
326
327 /* Check if this is a HFS filesystem. */
328 if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC)
329 {
330 grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
331 goto fail;
332 }
333
334 /* Check if this is an embedded HFS+ filesystem. */
335 if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
336 {
337 grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
338 goto fail;
339 }
340
341 data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
342 data->disk = disk;
343
344 /* Lookup the root node of the extent overflow tree. */
345 first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
346 * GRUB_HFS_BLKS)
347 + grub_be_to_cpu16 (data->sblock.first_block));
348
349 if (grub_disk_read (data->disk, first_block, 0,
350 sizeof (treehead), (char *) &treehead))
351 goto fail;
352 data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
353 data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
354
355 /* Lookup the root node of the catalog tree. */
356 first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
357 * GRUB_HFS_BLKS)
358 + grub_be_to_cpu16 (data->sblock.first_block));
359 if (grub_disk_read (data->disk, first_block, 0,
360 sizeof (treehead), (char *) &treehead))
361 goto fail;
362 data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
363 data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
364
365 /* Lookup the root directory node in the catalog tree using the
366 volume name. */
367 key.parent_dir = grub_cpu_to_be32 (1);
368 key.strlen = data->sblock.volname[0];
369 grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
370
371 if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
372 0, (char *) &dir, sizeof (dir)) == 0)
373 {
374 grub_error (GRUB_ERR_BAD_FS, "can not find the hfs root directory");
375 goto fail;
376 }
377
378 if (grub_errno)
379 goto fail;
380
381 data->rootdir = grub_be_to_cpu32 (dir.dirid);
382
383 return data;
384 fail:
385 grub_free (data);
386
387 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
388 grub_error (GRUB_ERR_BAD_FS, "not a hfs filesystem");
389
390 return 0;
391 }
392
393
394 /* Compare the K1 and K2 catalog file keys. */
395 static int
396 grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
397 struct grub_hfs_catalog_key *k2)
398 {
399 int cmp = (grub_be_to_cpu32 (k1->parent_dir)
400 - grub_be_to_cpu32 (k2->parent_dir));
401
402 if (cmp != 0)
403 return cmp;
404
405 cmp = grub_strncasecmp ((char *) (k1->str), (char *) (k2->str), k1->strlen);
406
407 /* This is required because the compared strings are not of equal
408 length. */
409 if (cmp == 0 && k1->strlen < k2->strlen)
410 return -1;
411 return cmp;
412 }
413
414
415 /* Compare the K1 and K2 extent overflow file keys. */
416 static int
417 grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
418 struct grub_hfs_extent_key *k2)
419 {
420 int cmp = k1->forktype - k2->forktype;
421 if (cmp == 0)
422 cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
423 if (cmp == 0)
424 cmp = (grub_be_to_cpu16 (k1->first_block)
425 - grub_be_to_cpu16 (k2->first_block));
426 return cmp;
427 }
428
429
430 /* Iterate the records in the node with index IDX in the mounted HFS
431 filesystem DATA. This node holds data of the type TYPE (0 =
432 catalog node, 1 = extent overflow node). If this is set, continue
433 iterating to the next node. For every records, call NODE_HOOK. */
434 static grub_err_t
435 grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
436 int this, int (*node_hook) (struct grub_hfs_node *hnd,
437 struct grub_hfs_record *))
438 {
439 int nodesize = type == 0 ? data->cat_size : data->ext_size;
440
441 union
442 {
443 struct grub_hfs_node node;
444 char rawnode[nodesize];
445 grub_uint16_t offsets[nodesize / 2];
446 } node;
447
448 do
449 {
450 int i;
451 struct grub_hfs_extent *dat;
452 int blk;
453
454 dat = (struct grub_hfs_extent *) (type == 0
455 ? (&data->sblock.catalog_recs)
456 : (&data->sblock.extent_recs));
457
458 /* Read the node into memory. */
459 blk = grub_hfs_block (data, dat,
460 (type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT,
461 idx / (data->blksz / nodesize), 0);
462 blk += (idx % (data->blksz / nodesize));
463 if (grub_errno)
464 return grub_errno;
465
466 if (grub_disk_read (data->disk, blk, 0,
467 sizeof (node), (char *) &node))
468 return grub_errno;
469
470 /* Iterate over all records in this node. */
471 for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
472 {
473 int pos = (nodesize >> 1) - 1 - i;
474 struct pointer
475 {
476 grub_uint8_t keylen;
477 grub_uint8_t key;
478 } __attribute__ ((packed)) *pnt;
479 pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
480 + node.rawnode);
481
482 struct grub_hfs_record rec =
483 {
484 &pnt->key,
485 pnt->keylen,
486 &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
487 nodesize - grub_be_to_cpu16 (node.offsets[pos])
488 - pnt->keylen - 1
489 };
490
491 if (node_hook (&node.node, &rec))
492 return 0;
493 }
494
495 idx = grub_be_to_cpu32 (node.node.next);
496 } while (idx && this);
497
498 return 0;
499 }
500
501
502 /* Lookup a record in the mounted filesystem DATA using the key KEY.
503 The index of the node on top of the tree is IDX. The tree is of
504 the type TYPE (0 = catalog node, 1 = extent overflow node). Return
505 the data in DATAR with a maximum length of DATALEN. */
506 static int
507 grub_hfs_find_node (struct grub_hfs_data *data, char *key,
508 grub_uint32_t idx, int type, char *datar, int datalen)
509 {
510 int found = -1;
511 int isleaf = 0;
512 int done = 0;
513
514 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
515
516 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
517 {
518 int cmp = 1;
519
520 if (type == 0)
521 cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
522 else
523 cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
524
525 /* If the key is smaller or equal to the currect node, mark the
526 entry. In case of a non-leaf mode it will be used to lookup
527 the rest of the tree. */
528 if (cmp <= 0)
529 {
530 grub_uint32_t *node = (grub_uint32_t *) rec->data;
531 found = grub_be_to_cpu32 (*node);
532 }
533 else /* The key can not be found in the tree. */
534 return 1;
535
536 /* Check if this node is a leaf node. */
537 if (hnd->type == GRUB_HFS_NODE_LEAF)
538 {
539 isleaf = 1;
540
541 /* Found it!!!! */
542 if (cmp == 0)
543 {
544 done = 1;
545
546 grub_memcpy (datar, rec->data,
547 rec->datalen < datalen ? rec->datalen : datalen);
548 return 1;
549 }
550 }
551
552 return 0;
553 }
554
555 do
556 {
557 found = -1;
558
559 if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
560 return 0;
561
562 if (found == -1)
563 return 0;
564
565 idx = found;
566 } while (! isleaf);
567
568 return done;
569 }
570
571
572 /* Iterate over the directory with the id DIR. The tree is searched
573 starting with the node ROOT_IDX. For every entry in this directory
574 call HOOK. */
575 static grub_err_t
576 grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
577 unsigned int dir, int (*hook) (struct grub_hfs_record *))
578 {
579 int found = -1;
580 int isleaf = 0;
581 int next = 0;
582
583 /* The lowest key possible with DIR as root directory. */
584 struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
585
586 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
587 auto int it_dir (struct grub_hfs_node * __attribute ((unused)),
588 struct grub_hfs_record *);
589
590
591 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
592 {
593 struct grub_hfs_catalog_key *ckey = rec->key;
594
595 if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
596 found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
597
598 if (hnd->type == 0xFF && ckey->strlen > 0)
599 {
600 isleaf = 1;
601 next = grub_be_to_cpu32 (hnd->next);
602
603 /* An entry was found. */
604 if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
605 return hook (rec);
606 }
607
608 return 0;
609 }
610
611 int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
612 struct grub_hfs_record *rec)
613 {
614 struct grub_hfs_catalog_key *ckey = rec->key;
615 struct grub_hfs_catalog_key *origkey = &key;
616
617 /* Stop when the entries do not match anymore. */
618 if (grub_be_to_cpu32 (ckey->parent_dir)
619 != grub_be_to_cpu32 ((origkey)->parent_dir))
620 return 1;
621
622 return hook (rec);
623 }
624
625 do
626 {
627 found = -1;
628
629 if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
630 return grub_errno;
631
632 if (found == -1)
633 return 0;
634
635 root_idx = found;
636 } while (! isleaf);
637
638 /* If there was a matching record in this leaf node, continue the
639 iteration until the last record was found. */
640 grub_hfs_iterate_records (data, 0, next, 1, it_dir);
641 return grub_errno;
642 }
643
644
645 /* Find a file or directory with the pathname PATH in the filesystem
646 DATA. Return the file record in RETDATA when it is non-zero.
647 Return the directory number in RETINODE when it is non-zero. */
648 static grub_err_t
649 grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
650 struct grub_hfs_filerec *retdata, int *retinode)
651 {
652 int inode = data->rootdir;
653 char *next;
654 char *origpath;
655 struct grub_hfs_filerec frec;
656 struct grub_hfs_dirrec *dir = (struct grub_hfs_dirrec *) &frec;
657 frec.type = GRUB_HFS_FILETYPE_DIR;
658
659 if (path[0] != '/')
660 {
661 grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
662 return 0;
663 }
664
665 origpath = grub_strdup (path);
666 if (!origpath)
667 return grub_errno;
668
669 path = origpath;
670 path++;
671
672 while (path && grub_strlen (path))
673 {
674 if (frec.type != GRUB_HFS_FILETYPE_DIR)
675 {
676 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
677 goto fail;
678 }
679
680 /* Isolate a part of the path. */
681 next = grub_strchr (path, '/');
682 if (next)
683 {
684 next[0] = '\0';
685 next++;
686 }
687
688 struct grub_hfs_catalog_key key;
689
690 key.parent_dir = grub_cpu_to_be32 (inode);
691 key.strlen = grub_strlen (path);
692 grub_strcpy ((char *) (key.str), path);
693
694 /* Lookup this node. */
695 if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
696 0, (char *) &frec, sizeof (frec)))
697 {
698 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
699 goto fail;
700 }
701
702 if (grub_errno)
703 goto fail;
704
705 inode = grub_be_to_cpu32 (dir->dirid);
706 path = next;
707 }
708
709 if (retdata)
710 grub_memcpy (retdata, &frec, sizeof (frec));
711
712 if (retinode)
713 *retinode = inode;
714
715 fail:
716 grub_free (origpath);
717 return grub_errno;
718 }
719
720
721 \f
722 static grub_err_t
723 grub_hfs_dir (grub_device_t device, const char *path,
724 int (*hook) (const char *filename, int dir))
725 {
726 int inode;
727
728 auto int dir_hook (struct grub_hfs_record *rec);
729
730 int dir_hook (struct grub_hfs_record *rec)
731 {
732 char fname[32] = { 0 };
733 char *filetype = rec->data;
734 struct grub_hfs_catalog_key *ckey = rec->key;
735
736 grub_strncpy (fname, (char *) (ckey->str), ckey->strlen);
737
738 if (*filetype == GRUB_HFS_FILETYPE_DIR)
739 return hook (fname, 1);
740 else if (*filetype == GRUB_HFS_FILETYPE_FILE)
741 return hook (fname, 0);
742 return 0;
743 }
744
745 struct grub_hfs_data *data;
746 struct grub_hfs_filerec frec;
747
748 #ifndef GRUB_UTIL
749 grub_dl_ref (my_mod);
750 #endif
751
752 data = grub_hfs_mount (device->disk);
753 if (!data)
754 goto fail;
755
756 /* First the directory ID for the directory. */
757 if (grub_hfs_find_dir (data, path, &frec, &inode))
758 goto fail;
759
760 if (frec.type != GRUB_HFS_FILETYPE_DIR)
761 {
762 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
763 goto fail;
764 }
765
766 grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
767
768 fail:
769 grub_free (data);
770
771 #ifndef GRUB_UTIL
772 grub_dl_unref (my_mod);
773 #endif
774
775 return grub_errno;
776 }
777
778
779 /* Open a file named NAME and initialize FILE. */
780 static grub_err_t
781 grub_hfs_open (struct grub_file *file, const char *name)
782 {
783 struct grub_hfs_data *data;
784 struct grub_hfs_filerec frec;
785
786 #ifndef GRUB_UTIL
787 grub_dl_ref (my_mod);
788 #endif
789
790 data = grub_hfs_mount (file->device->disk);
791
792 if (grub_hfs_find_dir (data, name, &frec, 0))
793 {
794 grub_free (data);
795 #ifndef GRUB_UTIL
796 grub_dl_unref (my_mod);
797 #endif
798 return grub_errno;
799 }
800
801 if (frec.type != GRUB_HFS_FILETYPE_FILE)
802 {
803 grub_free (data);
804 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
805 #ifndef GRUB_UTIL
806 grub_dl_unref (my_mod);
807 #endif
808 return grub_errno;
809 }
810
811 grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
812 file->size = grub_be_to_cpu32 (frec.size);
813 data->size = grub_be_to_cpu32 (frec.size);
814 data->fileid = grub_be_to_cpu32 (frec.fileid);
815 file->offset = 0;
816
817 file->data = data;
818
819 return 0;
820 }
821
822 static grub_ssize_t
823 grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
824 {
825 struct grub_hfs_data *data =
826 (struct grub_hfs_data *) file->data;
827
828 return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
829 }
830
831
832 static grub_err_t
833 grub_hfs_close (grub_file_t file)
834 {
835 grub_free (file->data);
836
837 #ifndef GRUB_UTIL
838 grub_dl_unref (my_mod);
839 #endif
840
841 return 0;
842 }
843
844
845 static grub_err_t
846 grub_hfs_label (grub_device_t device, char **label)
847 {
848 struct grub_hfs_data *data;
849
850 data = grub_hfs_mount (device->disk);
851
852 if (data)
853 *label = grub_strndup ((char *) (data->sblock.volname + 1),
854 *data->sblock.volname);
855 else
856 *label = 0;
857
858 grub_free (data);
859 return grub_errno;
860 }
861
862
863 \f
864 static struct grub_fs grub_hfs_fs =
865 {
866 .name = "hfs",
867 .dir = grub_hfs_dir,
868 .open = grub_hfs_open,
869 .read = grub_hfs_read,
870 .close = grub_hfs_close,
871 .label = grub_hfs_label,
872 .next = 0
873 };
874
875 GRUB_MOD_INIT(hfs)
876 {
877 grub_fs_register (&grub_hfs_fs);
878 #ifndef GRUB_UTIL
879 my_mod = mod;
880 #endif
881 }
882
883 GRUB_MOD_FINI(hfs)
884 {
885 grub_fs_unregister (&grub_hfs_fs);
886 }