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