malloc->zalloc
[grub2/phcoder.git] / fs / hfs.c
blob2f0702cbb8f3a48cd6a791836fd4ab7c14bd5880
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 pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
700 + node.rawnode);
702 struct grub_hfs_record rec =
704 &pnt->key,
705 pnt->keylen,
706 &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
707 nodesize - grub_be_to_cpu16 (node.offsets[pos])
708 - pnt->keylen - 1
711 if (node_hook (&node.node, &rec))
712 return 0;
715 idx = grub_be_to_cpu32 (node.node.next);
716 } while (idx && this);
718 return 0;
722 /* Lookup a record in the mounted filesystem DATA using the key KEY.
723 The index of the node on top of the tree is IDX. The tree is of
724 the type TYPE (0 = catalog node, 1 = extent overflow node). Return
725 the data in DATAR with a maximum length of DATALEN. */
726 static int
727 grub_hfs_find_node (struct grub_hfs_data *data, char *key,
728 grub_uint32_t idx, int type, char *datar, int datalen)
730 int found = -1;
731 int isleaf = 0;
732 int done = 0;
734 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
736 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
738 int cmp = 1;
740 if (type == 0)
741 cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
742 else
743 cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
745 /* If the key is smaller or equal to the current node, mark the
746 entry. In case of a non-leaf mode it will be used to lookup
747 the rest of the tree. */
748 if (cmp <= 0)
750 grub_uint32_t *node = (grub_uint32_t *) rec->data;
751 found = grub_be_to_cpu32 (*node);
753 else /* The key can not be found in the tree. */
754 return 1;
756 /* Check if this node is a leaf node. */
757 if (hnd->type == GRUB_HFS_NODE_LEAF)
759 isleaf = 1;
761 /* Found it!!!! */
762 if (cmp == 0)
764 done = 1;
766 grub_memcpy (datar, rec->data,
767 rec->datalen < datalen ? rec->datalen : datalen);
768 return 1;
772 return 0;
777 found = -1;
779 if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
780 return 0;
782 if (found == -1)
783 return 0;
785 idx = found;
786 } while (! isleaf);
788 return done;
792 /* Iterate over the directory with the id DIR. The tree is searched
793 starting with the node ROOT_IDX. For every entry in this directory
794 call HOOK. */
795 static grub_err_t
796 grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
797 unsigned int dir, int (*hook) (struct grub_hfs_record *))
799 int found = -1;
800 int isleaf = 0;
801 int next = 0;
803 /* The lowest key possible with DIR as root directory. */
804 struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
806 auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
807 auto int it_dir (struct grub_hfs_node * __attribute ((unused)),
808 struct grub_hfs_record *);
811 int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
813 struct grub_hfs_catalog_key *ckey = rec->key;
815 if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
816 found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
818 if (hnd->type == 0xFF && ckey->strlen > 0)
820 isleaf = 1;
821 next = grub_be_to_cpu32 (hnd->next);
823 /* An entry was found. */
824 if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
825 return hook (rec);
828 return 0;
831 int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
832 struct grub_hfs_record *rec)
834 struct grub_hfs_catalog_key *ckey = rec->key;
835 struct grub_hfs_catalog_key *origkey = &key;
837 /* Stop when the entries do not match anymore. */
838 if (grub_be_to_cpu32 (ckey->parent_dir)
839 != grub_be_to_cpu32 ((origkey)->parent_dir))
840 return 1;
842 return hook (rec);
847 found = -1;
849 if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
850 return grub_errno;
852 if (found == -1)
853 return 0;
855 root_idx = found;
856 } while (! isleaf);
858 /* If there was a matching record in this leaf node, continue the
859 iteration until the last record was found. */
860 grub_hfs_iterate_records (data, 0, next, 1, it_dir);
861 return grub_errno;
865 /* Find a file or directory with the pathname PATH in the filesystem
866 DATA. Return the file record in RETDATA when it is non-zero.
867 Return the directory number in RETINODE when it is non-zero. */
868 static grub_err_t
869 grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
870 struct grub_hfs_filerec *retdata, int *retinode)
872 int inode = data->rootdir;
873 char *next;
874 char *origpath;
875 union {
876 struct grub_hfs_filerec frec;
877 struct grub_hfs_dirrec dir;
878 } fdrec;
880 fdrec.frec.type = GRUB_HFS_FILETYPE_DIR;
882 if (path[0] != '/')
884 grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
885 return 0;
888 origpath = grub_strdup (path);
889 if (!origpath)
890 return grub_errno;
892 path = origpath;
893 while (*path == '/')
894 path++;
896 while (path && grub_strlen (path))
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 struct grub_hfs_catalog_key key;
914 key.parent_dir = grub_cpu_to_be32 (inode);
915 key.strlen = grub_strlen (path);
916 grub_strcpy ((char *) (key.str), path);
918 /* Lookup this node. */
919 if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
920 0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
922 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
923 goto fail;
926 if (grub_errno)
927 goto fail;
929 inode = grub_be_to_cpu32 (fdrec.dir.dirid);
930 path = next;
933 if (retdata)
934 grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec));
936 if (retinode)
937 *retinode = inode;
939 fail:
940 grub_free (origpath);
941 return grub_errno;
946 static grub_err_t
947 grub_hfs_dir (grub_device_t device, const char *path,
948 int (*hook) (const char *filename,
949 const struct grub_dirhook_info *info))
951 int inode;
953 auto int dir_hook (struct grub_hfs_record *rec);
955 int dir_hook (struct grub_hfs_record *rec)
957 char fname[32] = { 0 };
958 char *filetype = rec->data;
959 struct grub_hfs_catalog_key *ckey = rec->key;
960 struct grub_dirhook_info info;
961 grub_memset (&info, 0, sizeof (info));
963 grub_strncpy (fname, (char *) (ckey->str), ckey->strlen);
965 if (*filetype == GRUB_HFS_FILETYPE_DIR
966 || *filetype == GRUB_HFS_FILETYPE_FILE)
968 info.dir = (*filetype == GRUB_HFS_FILETYPE_DIR);
969 return hook (fname, &info);
971 return 0;
974 struct grub_hfs_data *data;
975 struct grub_hfs_filerec frec;
977 grub_dl_ref (my_mod);
979 data = grub_hfs_mount (device->disk);
980 if (!data)
981 goto fail;
983 /* First the directory ID for the directory. */
984 if (grub_hfs_find_dir (data, path, &frec, &inode))
985 goto fail;
987 if (frec.type != GRUB_HFS_FILETYPE_DIR)
989 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
990 goto fail;
993 grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
995 fail:
996 grub_free (data);
998 grub_dl_unref (my_mod);
1000 return grub_errno;
1004 /* Open a file named NAME and initialize FILE. */
1005 static grub_err_t
1006 grub_hfs_open (struct grub_file *file, const char *name)
1008 struct grub_hfs_data *data;
1009 struct grub_hfs_filerec frec;
1011 grub_dl_ref (my_mod);
1013 data = grub_hfs_mount (file->device->disk);
1015 if (grub_hfs_find_dir (data, name, &frec, 0))
1017 grub_free (data);
1018 grub_dl_unref (my_mod);
1019 return grub_errno;
1022 if (frec.type != GRUB_HFS_FILETYPE_FILE)
1024 grub_free (data);
1025 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
1026 grub_dl_unref (my_mod);
1027 return grub_errno;
1030 grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
1031 file->size = grub_be_to_cpu32 (frec.size);
1032 data->size = grub_be_to_cpu32 (frec.size);
1033 data->fileid = grub_be_to_cpu32 (frec.fileid);
1034 file->offset = 0;
1036 file->data = data;
1038 return 0;
1041 static grub_ssize_t
1042 grub_hfs_read (grub_file_t file, char *buf, grub_size_t len)
1044 struct grub_hfs_data *data =
1045 (struct grub_hfs_data *) file->data;
1047 return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
1051 static grub_err_t
1052 grub_hfs_close (grub_file_t file)
1054 grub_free (file->data);
1056 grub_dl_unref (my_mod);
1058 return 0;
1062 static grub_err_t
1063 grub_hfs_label (grub_device_t device, char **label)
1065 struct grub_hfs_data *data;
1067 data = grub_hfs_mount (device->disk);
1069 if (data)
1070 *label = grub_strndup ((char *) (data->sblock.volname + 1),
1071 *data->sblock.volname);
1072 else
1073 *label = 0;
1075 grub_free (data);
1076 return grub_errno;
1081 static struct grub_fs grub_hfs_fs =
1083 .name = "hfs",
1084 .dir = grub_hfs_dir,
1085 .open = grub_hfs_open,
1086 .read = grub_hfs_read,
1087 .close = grub_hfs_close,
1088 .label = grub_hfs_label,
1089 .next = 0
1092 GRUB_MOD_INIT(hfs)
1094 grub_fs_register (&grub_hfs_fs);
1095 my_mod = mod;
1098 GRUB_MOD_FINI(hfs)
1100 grub_fs_unregister (&grub_hfs_fs);