be nearer to original
[grub2/phcoder.git] / fs / hfs.c
blobe8e838f990204181ee63d9df714faec6c89b347e
1 /* hfs.c - HFS. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008 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 */
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>
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. */
40 enum
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,
53 GRUB_HFS_CNID_BAD = 5
56 /* A node descriptor. This is the header of every node. */
57 struct grub_hfs_node
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));
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;
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));
83 /* The state of a mounted HFS filesystem. */
84 struct grub_hfs_data
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;
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
104 grub_uint8_t unused;
105 grub_uint32_t parent_dir;
107 /* Filename length. */
108 grub_uint8_t strlen;
110 /* Filename. */
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
116 as index. */
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
121 0. */
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. */
131 grub_uint8_t type;
132 grub_uint8_t unused[5];
133 grub_uint32_t dirid;
134 } __attribute__ ((packed));
136 /* Information about a file. */
137 struct grub_hfs_filerec
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];
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
153 functions. */
154 struct grub_hfs_record
156 void *key;
157 int keylen;
158 void *data;
159 int datalen;
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. */
170 static unsigned int
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;
175 int pos = 0;
176 struct grub_hfs_extent_key key;
178 int tree = 0;
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));
185 key.forktype = 0;
186 key.fileid = grub_cpu_to_be32 (file);
188 if (cache && cache_file == file && block > cache_pos)
190 pos = cache_pos;
191 key.first_block = grub_cpu_to_be16 (pos);
192 grub_memcpy (dr, cache_dr, sizeof (cache_dr));
195 for (;;)
197 int i;
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
208 in the tree. */
209 if (tree && cache)
211 cache_file = file;
212 cache_pos = pos;
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);
226 tree = 1;
227 grub_hfs_find_node (data, (char *) &key, data->ext_root,
228 1, (char *) &dr, sizeof (dr));
229 if (grub_errno)
230 return 0;
235 /* Read LEN bytes from the file described by DATA starting with byte
236 POS. Return the amount of read bytes in READ. */
237 static grub_ssize_t
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)
243 int i;
244 int blockcnt;
246 /* Adjust len so it we can't read past the end of the file. */
247 if (len > grub_le_to_cpu32 (data->size))
248 len = grub_le_to_cpu32 (data->size);
250 blockcnt = ((len + pos)
251 + data->blksz - 1) / data->blksz;
253 for (i = pos / data->blksz; i < blockcnt; i++)
255 int blknr;
256 int blockoff = pos % data->blksz;
257 int blockend = data->blksz;
259 int skipfirst = 0;
261 blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
262 if (grub_errno)
263 return -1;
265 /* Last block. */
266 if (i == blockcnt - 1)
268 blockend = (len + pos) % data->blksz;
270 /* The last portion is exactly EXT2_BLOCK_SIZE (data). */
271 if (! blockend)
272 blockend = data->blksz;
275 /* First block. */
276 if (i == pos / data->blksz)
278 skipfirst = blockoff;
279 blockend -= skipfirst;
282 /* If the block number is 0 this block is not stored on disk but
283 is zero filled instead. */
284 if (blknr)
286 data->disk->read_hook = read_hook;
287 grub_disk_read (data->disk, blknr, skipfirst,
288 blockend, buf);
289 data->disk->read_hook = 0;
290 if (grub_errno)
291 return -1;
294 buf += data->blksz - skipfirst;
297 return len;
301 /* Mount the filesystem on the disk DISK. */
302 static struct grub_hfs_data *
303 grub_hfs_mount (grub_disk_t disk)
305 struct grub_hfs_data *data;
306 struct grub_hfs_catalog_key key;
307 struct grub_hfs_dirrec dir;
308 int first_block;
310 struct
312 struct grub_hfs_node node;
313 struct grub_hfs_treeheader head;
314 } treehead;
316 data = grub_malloc (sizeof (struct grub_hfs_data));
317 if (!data)
318 return 0;
320 /* Read the superblock. */
321 if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
322 sizeof (struct grub_hfs_sblock), &data->sblock))
323 goto fail;
325 /* Check if this is a HFS filesystem. */
326 if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC)
328 grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
329 goto fail;
332 /* Check if this is an embedded HFS+ filesystem. */
333 if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
335 grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
336 goto fail;
339 data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
340 data->disk = disk;
342 /* Lookup the root node of the extent overflow tree. */
343 first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
344 * GRUB_HFS_BLKS)
345 + grub_be_to_cpu16 (data->sblock.first_block));
347 if (grub_disk_read (data->disk, first_block, 0,
348 sizeof (treehead), &treehead))
349 goto fail;
350 data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
351 data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
353 /* Lookup the root node of the catalog tree. */
354 first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
355 * GRUB_HFS_BLKS)
356 + grub_be_to_cpu16 (data->sblock.first_block));
357 if (grub_disk_read (data->disk, first_block, 0,
358 sizeof (treehead), &treehead))
359 goto fail;
360 data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
361 data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
363 /* Lookup the root directory node in the catalog tree using the
364 volume name. */
365 key.parent_dir = grub_cpu_to_be32 (1);
366 key.strlen = data->sblock.volname[0];
367 grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
369 if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
370 0, (char *) &dir, sizeof (dir)) == 0)
372 grub_error (GRUB_ERR_BAD_FS, "can not find the hfs root directory");
373 goto fail;
376 if (grub_errno)
377 goto fail;
379 data->rootdir = grub_be_to_cpu32 (dir.dirid);
381 return data;
382 fail:
383 grub_free (data);
385 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
386 grub_error (GRUB_ERR_BAD_FS, "not a hfs filesystem");
388 return 0;
391 /* Compare the K1 and K2 catalog file keys using HFS character ordering. */
392 static int
393 grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
394 struct grub_hfs_catalog_key *k2)
396 /* Taken from hfsutils 3.2.6 and converted to a readable form */
397 static const unsigned char hfs_charorder[256] = {
398 [0x00] = 0,
399 [0x01] = 1,
400 [0x02] = 2,
401 [0x03] = 3,
402 [0x04] = 4,
403 [0x05] = 5,
404 [0x06] = 6,
405 [0x07] = 7,
406 [0x08] = 8,
407 [0x09] = 9,
408 [0x0A] = 10,
409 [0x0B] = 11,
410 [0x0C] = 12,
411 [0x0D] = 13,
412 [0x0E] = 14,
413 [0x0F] = 15,
414 [0x10] = 16,
415 [0x11] = 17,
416 [0x12] = 18,
417 [0x13] = 19,
418 [0x14] = 20,
419 [0x15] = 21,
420 [0x16] = 22,
421 [0x17] = 23,
422 [0x18] = 24,
423 [0x19] = 25,
424 [0x1A] = 26,
425 [0x1B] = 27,
426 [0x1C] = 28,
427 [0x1D] = 29,
428 [0x1E] = 30,
429 [0x1F] = 31,
430 [' '] = 32, [0xCA] = 32,
431 ['!'] = 33,
432 ['"'] = 34,
433 [0xD2] = 35,
434 [0xD3] = 36,
435 [0xC7] = 37,
436 [0xC8] = 38,
437 ['#'] = 39,
438 ['$'] = 40,
439 ['%'] = 41,
440 ['&'] = 42,
441 ['\''] = 43,
442 [0xD4] = 44,
443 [0xD5] = 45,
444 ['('] = 46,
445 [')'] = 47,
446 ['*'] = 48,
447 ['+'] = 49,
448 [','] = 50,
449 ['-'] = 51,
450 ['.'] = 52,
451 ['/'] = 53,
452 ['0'] = 54,
453 ['1'] = 55,
454 ['2'] = 56,
455 ['3'] = 57,
456 ['4'] = 58,
457 ['5'] = 59,
458 ['6'] = 60,
459 ['7'] = 61,
460 ['8'] = 62,
461 ['9'] = 63,
462 [':'] = 64,
463 [';'] = 65,
464 ['<'] = 66,
465 ['='] = 67,
466 ['>'] = 68,
467 ['?'] = 69,
468 ['@'] = 70,
469 ['A'] = 71, ['a'] = 71,
470 [0x88] = 72, [0xCB] = 72,
471 [0x80] = 73, [0x8A] = 73,
472 [0x8B] = 74, [0xCC] = 74,
473 [0x81] = 75, [0x8C] = 75,
474 [0xAE] = 76, [0xBE] = 76,
475 ['`'] = 77,
476 [0x87] = 78,
477 [0x89] = 79,
478 [0xBB] = 80,
479 ['B'] = 81, ['b'] = 81,
480 ['C'] = 82, ['c'] = 82,
481 [0x82] = 83, [0x8D] = 83,
482 ['D'] = 84, ['d'] = 84,
483 ['E'] = 85, ['e'] = 85,
484 [0x83] = 86, [0x8E] = 86,
485 [0x8F] = 87,
486 [0x90] = 88,
487 [0x91] = 89,
488 ['F'] = 90, ['f'] = 90,
489 ['G'] = 91, ['g'] = 91,
490 ['H'] = 92, ['h'] = 92,
491 ['I'] = 93, ['i'] = 93,
492 [0x92] = 94,
493 [0x93] = 95,
494 [0x94] = 96,
495 [0x95] = 97,
496 ['J'] = 98, ['j'] = 98,
497 ['K'] = 99, ['k'] = 99,
498 ['L'] = 100, ['l'] = 100,
499 ['M'] = 101, ['m'] = 101,
500 ['N'] = 102, ['n'] = 102,
501 [0x84] = 103, [0x96] = 103,
502 ['O'] = 104, ['o'] = 104,
503 [0x85] = 105, [0x9A] = 105,
504 [0x9B] = 106, [0xCD] = 106,
505 [0xAF] = 107, [0xBF] = 107,
506 [0xCE] = 108, [0xCF] = 108,
507 [0x97] = 109,
508 [0x98] = 110,
509 [0x99] = 111,
510 [0xBC] = 112,
511 ['P'] = 113, ['p'] = 113,
512 ['Q'] = 114, ['q'] = 114,
513 ['R'] = 115, ['r'] = 115,
514 ['S'] = 116, ['s'] = 116,
515 [0xA7] = 117,
516 ['T'] = 118, ['t'] = 118,
517 ['U'] = 119, ['u'] = 119,
518 [0x86] = 120, [0x9F] = 120,
519 [0x9C] = 121,
520 [0x9D] = 122,
521 [0x9E] = 123,
522 ['V'] = 124, ['v'] = 124,
523 ['W'] = 125, ['w'] = 125,
524 ['X'] = 126, ['x'] = 126,
525 ['Y'] = 127, ['y'] = 127,
526 [0xD8] = 128,
527 ['Z'] = 129, ['z'] = 129,
528 ['['] = 130,
529 ['\\'] = 131,
530 [']'] = 132,
531 ['^'] = 133,
532 ['_'] = 134,
533 ['{'] = 135,
534 ['|'] = 136,
535 ['}'] = 137,
536 ['~'] = 138,
537 [0x7F] = 139,
538 [0xA0] = 140,
539 [0xA1] = 141,
540 [0xA2] = 142,
541 [0xA3] = 143,
542 [0xA4] = 144,
543 [0xA5] = 145,
544 [0xA6] = 146,
545 [0xA8] = 147,
546 [0xA9] = 148,
547 [0xAA] = 149,
548 [0xAB] = 150,
549 [0xAC] = 151,
550 [0xAD] = 152,
551 [0xB0] = 153,
552 [0xB1] = 154,
553 [0xB2] = 155,
554 [0xB3] = 156,
555 [0xB4] = 157,
556 [0xB5] = 158,
557 [0xB6] = 159,
558 [0xB7] = 160,
559 [0xB8] = 161,
560 [0xB9] = 162,
561 [0xBA] = 163,
562 [0xBD] = 164,
563 [0xC0] = 165,
564 [0xC1] = 166,
565 [0xC2] = 167,
566 [0xC3] = 168,
567 [0xC4] = 169,
568 [0xC5] = 170,
569 [0xC6] = 171,
570 [0xC9] = 172,
571 [0xD0] = 173,
572 [0xD1] = 174,
573 [0xD6] = 175,
574 [0xD7] = 176,
575 [0xD9] = 177,
576 [0xDA] = 178,
577 [0xDB] = 179,
578 [0xDC] = 180,
579 [0xDD] = 181,
580 [0xDE] = 182,
581 [0xDF] = 183,
582 [0xE0] = 184,
583 [0xE1] = 185,
584 [0xE2] = 186,
585 [0xE3] = 187,
586 [0xE4] = 188,
587 [0xE5] = 189,
588 [0xE6] = 190,
589 [0xE7] = 191,
590 [0xE8] = 192,
591 [0xE9] = 193,
592 [0xEA] = 194,
593 [0xEB] = 195,
594 [0xEC] = 196,
595 [0xED] = 197,
596 [0xEE] = 198,
597 [0xEF] = 199,
598 [0xF0] = 200,
599 [0xF1] = 201,
600 [0xF2] = 202,
601 [0xF3] = 203,
602 [0xF4] = 204,
603 [0xF5] = 205,
604 [0xF6] = 206,
605 [0xF7] = 207,
606 [0xF8] = 208,
607 [0xF9] = 209,
608 [0xFA] = 210,
609 [0xFB] = 211,
610 [0xFC] = 212,
611 [0xFD] = 213,
612 [0xFE] = 214,
613 [0xFF] = 215,
615 int i;
616 int cmp;
617 int minlen = (k1->strlen < k2->strlen) ? k1->strlen : k2->strlen;
619 cmp = (grub_be_to_cpu32 (k1->parent_dir) - grub_be_to_cpu32 (k2->parent_dir));
620 if (cmp != 0)
621 return cmp;
623 for (i = 0; i < minlen; i++)
625 cmp = (hfs_charorder[k1->str[i]] - hfs_charorder[k2->str[i]]);
626 if (cmp != 0)
627 return cmp;
630 /* Shorter strings precede long ones. */
631 return (k1->strlen - k2->strlen);
635 /* Compare the K1 and K2 extent overflow file keys. */
636 static int
637 grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
638 struct grub_hfs_extent_key *k2)
640 int cmp = k1->forktype - k2->forktype;
641 if (cmp == 0)
642 cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
643 if (cmp == 0)
644 cmp = (grub_be_to_cpu16 (k1->first_block)
645 - grub_be_to_cpu16 (k2->first_block));
646 return cmp;
650 /* Iterate the records in the node with index IDX in the mounted HFS
651 filesystem DATA. This node holds data of the type TYPE (0 =
652 catalog node, 1 = extent overflow node). If this is set, continue
653 iterating to the next node. For every records, call NODE_HOOK. */
654 static grub_err_t
655 grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
656 int this, int (*node_hook) (struct grub_hfs_node *hnd,
657 struct grub_hfs_record *))
659 int nodesize = type == 0 ? data->cat_size : data->ext_size;
661 union
663 struct grub_hfs_node node;
664 char rawnode[nodesize];
665 grub_uint16_t offsets[nodesize / 2];
666 } node;
670 int i;
671 struct grub_hfs_extent *dat;
672 int blk;
674 dat = (struct grub_hfs_extent *) (type == 0
675 ? (&data->sblock.catalog_recs)
676 : (&data->sblock.extent_recs));
678 /* Read the node into memory. */
679 blk = grub_hfs_block (data, dat,
680 (type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT,
681 idx / (data->blksz / nodesize), 0);
682 blk += (idx % (data->blksz / nodesize));
683 if (grub_errno)
684 return grub_errno;
686 if (grub_disk_read (data->disk, blk, 0,
687 sizeof (node), &node))
688 return grub_errno;
690 /* Iterate over all records in this node. */
691 for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
693 int pos = (nodesize >> 1) - 1 - i;
694 struct pointer
696 grub_uint8_t keylen;
697 grub_uint8_t key;
698 } __attribute__ ((packed)) *pnt;
699 struct grub_hfs_record rec;
701 pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
702 + node.rawnode);
703 rec.key = &pnt->key;
704 rec.keylen = pnt->keylen;
705 rec.data = &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2;
706 rec.datalen = nodesize - grub_be_to_cpu16 (node.offsets[pos])
707 - pnt->keylen - 1;
709 if (node_hook (&node.node, &rec))
710 return 0;
713 idx = grub_be_to_cpu32 (node.node.next);
714 } while (idx && this);
716 return 0;
720 /* Lookup a record in the mounted filesystem DATA using the key KEY.
721 The index of the node on top of the tree is IDX. The tree is of
722 the type TYPE (0 = catalog node, 1 = extent overflow node). Return
723 the data in DATAR with a maximum length of DATALEN. */
724 static int
725 grub_hfs_find_node (struct grub_hfs_data *data, char *key,
726 grub_uint32_t idx, int type, char *datar, int datalen)
728 int found = -1;
729 int isleaf = 0;
730 int done = 0;
732 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
734 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
736 int cmp = 1;
738 if (type == 0)
739 cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
740 else
741 cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
743 /* If the key is smaller or equal to the current node, mark the
744 entry. In case of a non-leaf mode it will be used to lookup
745 the rest of the tree. */
746 if (cmp <= 0)
748 grub_uint32_t *node = (grub_uint32_t *) rec->data;
749 found = grub_be_to_cpu32 (*node);
751 else /* The key can not be found in the tree. */
752 return 1;
754 /* Check if this node is a leaf node. */
755 if (hnd->type == GRUB_HFS_NODE_LEAF)
757 isleaf = 1;
759 /* Found it!!!! */
760 if (cmp == 0)
762 done = 1;
764 grub_memcpy (datar, rec->data,
765 rec->datalen < datalen ? rec->datalen : datalen);
766 return 1;
770 return 0;
775 found = -1;
777 if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
778 return 0;
780 if (found == -1)
781 return 0;
783 idx = found;
784 } while (! isleaf);
786 return done;
790 /* Iterate over the directory with the id DIR. The tree is searched
791 starting with the node ROOT_IDX. For every entry in this directory
792 call HOOK. */
793 static grub_err_t
794 grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
795 unsigned int dir, int (*hook) (struct grub_hfs_record *))
797 int found = -1;
798 int isleaf = 0;
799 int next = 0;
801 /* The lowest key possible with DIR as root directory. */
802 struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
804 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
805 auto int it_dir (struct grub_hfs_node * __attribute ((unused)) node,
806 struct grub_hfs_record *);
809 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
811 struct grub_hfs_catalog_key *ckey = rec->key;
813 if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
814 found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
816 if (hnd->type == 0xFF && ckey->strlen > 0)
818 isleaf = 1;
819 next = grub_be_to_cpu32 (hnd->next);
821 /* An entry was found. */
822 if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
823 return hook (rec);
826 return 0;
829 int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
830 struct grub_hfs_record *rec)
832 struct grub_hfs_catalog_key *ckey = rec->key;
833 struct grub_hfs_catalog_key *origkey = &key;
835 /* Stop when the entries do not match anymore. */
836 if (grub_be_to_cpu32 (ckey->parent_dir)
837 != grub_be_to_cpu32 ((origkey)->parent_dir))
838 return 1;
840 return hook (rec);
845 found = -1;
847 if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
848 return grub_errno;
850 if (found == -1)
851 return 0;
853 root_idx = found;
854 } while (! isleaf);
856 /* If there was a matching record in this leaf node, continue the
857 iteration until the last record was found. */
858 grub_hfs_iterate_records (data, 0, next, 1, it_dir);
859 return grub_errno;
863 /* Find a file or directory with the pathname PATH in the filesystem
864 DATA. Return the file record in RETDATA when it is non-zero.
865 Return the directory number in RETINODE when it is non-zero. */
866 static grub_err_t
867 grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
868 struct grub_hfs_filerec *retdata, int *retinode)
870 int inode = data->rootdir;
871 char *next;
872 char *origpath;
873 union {
874 struct grub_hfs_filerec frec;
875 struct grub_hfs_dirrec dir;
876 } fdrec;
878 fdrec.frec.type = GRUB_HFS_FILETYPE_DIR;
880 if (path[0] != '/')
882 grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
883 return 0;
886 origpath = grub_strdup (path);
887 if (!origpath)
888 return grub_errno;
890 path = origpath;
891 while (*path == '/')
892 path++;
894 while (path && grub_strlen (path))
896 struct grub_hfs_catalog_key key;
898 if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR)
900 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
901 goto fail;
904 /* Isolate a part of the path. */
905 next = grub_strchr (path, '/');
906 if (next)
908 while (*next == '/')
909 *(next++) = '\0';
912 key.parent_dir = grub_cpu_to_be32 (inode);
913 key.strlen = grub_strlen (path);
914 grub_strcpy ((char *) (key.str), path);
916 /* Lookup this node. */
917 if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
918 0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
920 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
921 goto fail;
924 if (grub_errno)
925 goto fail;
927 inode = grub_be_to_cpu32 (fdrec.dir.dirid);
928 path = next;
931 if (retdata)
932 grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec));
934 if (retinode)
935 *retinode = inode;
937 fail:
938 grub_free (origpath);
939 return grub_errno;
944 static grub_err_t
945 grub_hfs_dir (grub_device_t device, const char *path,
946 int (*hook) (const char *filename,
947 const struct grub_dirhook_info *info))
949 int inode;
951 auto int dir_hook (struct grub_hfs_record *rec);
953 int dir_hook (struct grub_hfs_record *rec)
955 char fname[32] = { 0 };
956 char *filetype = rec->data;
957 struct grub_hfs_catalog_key *ckey = rec->key;
958 struct grub_dirhook_info info;
959 grub_memset (&info, 0, sizeof (info));
961 grub_strncpy (fname, (char *) (ckey->str), ckey->strlen);
963 if (*filetype == GRUB_HFS_FILETYPE_DIR
964 || *filetype == GRUB_HFS_FILETYPE_FILE)
966 info.dir = (*filetype == GRUB_HFS_FILETYPE_DIR);
967 return hook (fname, &info);
969 return 0;
972 struct grub_hfs_data *data;
973 struct grub_hfs_filerec frec;
975 grub_dl_ref (my_mod);
977 data = grub_hfs_mount (device->disk);
978 if (!data)
979 goto fail;
981 /* First the directory ID for the directory. */
982 if (grub_hfs_find_dir (data, path, &frec, &inode))
983 goto fail;
985 if (frec.type != GRUB_HFS_FILETYPE_DIR)
987 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
988 goto fail;
991 grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
993 fail:
994 grub_free (data);
996 grub_dl_unref (my_mod);
998 return grub_errno;
1002 /* Open a file named NAME and initialize FILE. */
1003 static grub_err_t
1004 grub_hfs_open (struct grub_file *file, const char *name)
1006 struct grub_hfs_data *data;
1007 struct grub_hfs_filerec frec;
1009 grub_dl_ref (my_mod);
1011 data = grub_hfs_mount (file->device->disk);
1013 if (grub_hfs_find_dir (data, name, &frec, 0))
1015 grub_free (data);
1016 grub_dl_unref (my_mod);
1017 return grub_errno;
1020 if (frec.type != GRUB_HFS_FILETYPE_FILE)
1022 grub_free (data);
1023 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
1024 grub_dl_unref (my_mod);
1025 return grub_errno;
1028 grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
1029 file->size = grub_be_to_cpu32 (frec.size);
1030 data->size = grub_be_to_cpu32 (frec.size);
1031 data->fileid = grub_be_to_cpu32 (frec.fileid);
1032 file->offset = 0;
1034 file->data = data;
1036 return 0;
1039 static grub_ssize_t
1040 grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
1042 struct grub_hfs_data *data =
1043 (struct grub_hfs_data *) file->data;
1045 return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
1049 static grub_err_t
1050 grub_hfs_close (grub_file_t file)
1052 grub_free (file->data);
1054 grub_dl_unref (my_mod);
1056 return 0;
1060 static grub_err_t
1061 grub_hfs_label (grub_device_t device, char **label)
1063 struct grub_hfs_data *data;
1065 data = grub_hfs_mount (device->disk);
1067 if (data)
1068 *label = grub_strndup ((char *) (data->sblock.volname + 1),
1069 *data->sblock.volname);
1070 else
1071 *label = 0;
1073 grub_free (data);
1074 return grub_errno;
1079 static struct grub_fs grub_hfs_fs =
1081 .name = "hfs",
1082 .dir = grub_hfs_dir,
1083 .open = grub_hfs_open,
1084 .read = grub_hfs_read,
1085 .close = grub_hfs_close,
1086 .label = grub_hfs_label,
1087 .next = 0
1090 GRUB_MOD_INIT(hfs)
1092 grub_fs_register (&grub_hfs_fs);
1093 my_mod = mod;
1096 GRUB_MOD_FINI(hfs)
1098 grub_fs_unregister (&grub_hfs_fs);