]> code.delx.au - refind/blob - filesystems/fsw_hfs.c
Added filesystem drivers.
[refind] / filesystems / fsw_hfs.c
1 /* $Id: fsw_hfs.c 33540 2010-10-28 09:27:05Z vboxsync $ */
2 /** @file
3 * fsw_hfs.c - HFS file system driver code, see
4 *
5 * http://developer.apple.com/technotes/tn/tn1150.html
6 *
7 * Current limitations:
8 * - Doesn't support permissions
9 * - Complete Unicode case-insensitiveness disabled (large tables)
10 * - No links
11 * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS)
12 */
13
14 /*
15 * Copyright (C) 2010 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 */
25
26 #include "fsw_hfs.h"
27
28 #ifdef HOST_POSIX
29 #define DPRINT(x) printf(x)
30 #define DPRINT2(x,y) printf(x,y)
31 #define BP(msg) do { printf("ERROR: %s", msg); asm("int3"); } while (0)
32 #else
33 #define CONCAT(x,y) x##y
34 #define DPRINT(x) Print(CONCAT(L,x))
35 #define DPRINT2(x,y) Print(CONCAT(L,x), y)
36 #define BP(msg) DPRINT(msg)
37 #endif
38
39 // functions
40 #if 0
41 void dump_str(fsw_u16* p, fsw_u32 len, int swap)
42 {
43 int i;
44
45 for (i=0; i<len; i++)
46 {
47 fprintf(stderr, "%c", swap ? be16_to_cpu(p[i]) : p[i]);
48 }
49 fprintf(stderr, "\n");
50 }
51 #endif
52
53 static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol);
54 static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol);
55 static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb);
56
57 static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno);
58 static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno);
59 static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
60 struct fsw_dnode_stat *sb);
61 static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
62 struct fsw_extent *extent);
63
64 static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
65 struct fsw_string *lookup_name, struct fsw_hfs_dnode **child_dno);
66 static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
67 struct fsw_shandle *shand, struct fsw_hfs_dnode **child_dno);
68 #if 0
69 static fsw_status_t fsw_hfs_read_dirrec(struct fsw_shandle *shand, struct hfs_dirrec_buffer *dirrec_buffer);
70 #endif
71
72 static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
73 struct fsw_string *link);
74
75 //
76 // Dispatch Table
77 //
78
79 struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs) = {
80 { FSW_STRING_TYPE_ISO88591, 4, 4, "hfs" },
81 sizeof(struct fsw_hfs_volume),
82 sizeof(struct fsw_hfs_dnode),
83
84 fsw_hfs_volume_mount, // volume open
85 fsw_hfs_volume_free, // volume close
86 fsw_hfs_volume_stat, // volume info: total_bytes, free_bytes
87 fsw_hfs_dnode_fill, //return FSW_SUCCESS;
88 fsw_hfs_dnode_free, // empty
89 fsw_hfs_dnode_stat, //size and times
90 fsw_hfs_get_extent, // get the physical disk block number for the requested logical block number
91 fsw_hfs_dir_lookup, //retrieve the directory entry with the given name
92 fsw_hfs_dir_read, // next directory entry when reading a directory
93 fsw_hfs_readlink, // return FSW_UNSUPPORTED;
94 };
95
96 static fsw_s32
97 fsw_hfs_read_block (struct fsw_hfs_dnode * dno,
98 fsw_u32 log_bno,
99 fsw_u32 off,
100 fsw_s32 len,
101 fsw_u8 * buf)
102 {
103 fsw_status_t status;
104 struct fsw_extent extent;
105 fsw_u32 phys_bno;
106 fsw_u8* buffer;
107
108 extent.log_start = log_bno;
109 status = fsw_hfs_get_extent(dno->g.vol, dno, &extent);
110 if (status)
111 return status;
112
113 phys_bno = extent.phys_start;
114 //Slice - increase cache level from 0 to 3
115 status = fsw_block_get(dno->g.vol, phys_bno, 3, (void **)&buffer);
116 if (status)
117 return status;
118
119 fsw_memcpy(buf, buffer + off, len);
120
121 fsw_block_release(dno->g.vol, phys_bno, buffer);
122
123 return FSW_SUCCESS;
124
125 }
126
127 /* Read data from HFS file. */
128 static fsw_s32
129 fsw_hfs_read_file (struct fsw_hfs_dnode * dno,
130 fsw_u64 pos,
131 fsw_s32 len,
132 fsw_u8 * buf)
133 {
134
135 fsw_status_t status;
136 fsw_u32 log_bno;
137 fsw_u32 block_size_bits = dno->g.vol->block_size_shift;
138 fsw_u32 block_size = (1 << block_size_bits);
139 fsw_u32 block_size_mask = block_size - 1;
140 fsw_s32 read = 0;
141
142 while (len > 0)
143 {
144 fsw_u32 off = (fsw_u32)(pos & block_size_mask);
145 fsw_s32 next_len = len;
146
147 log_bno = (fsw_u32)RShiftU64(pos, block_size_bits);
148
149 if ( next_len >= 0
150 && (fsw_u32)next_len > block_size)
151 next_len = block_size;
152 status = fsw_hfs_read_block(dno, log_bno, off, next_len, buf);
153 if (status)
154 return -1;
155 buf += next_len;
156 pos += next_len;
157 len -= next_len;
158 read += next_len;
159 }
160
161 return read;
162 }
163
164
165 static fsw_s32
166 fsw_hfs_compute_shift(fsw_u32 size)
167 {
168 fsw_s32 i;
169
170 for (i=0; i<32; i++)
171 {
172 if ((size >> i) == 0)
173 return i - 1;
174 }
175
176 // BP("BUG\n");
177 return 0;
178 }
179
180 /**
181 * Mount an HFS+ volume. Reads the superblock and constructs the
182 * root directory dnode.
183 */
184 //algo from Chameleon
185 /*
186 void
187 HFSGetDescription(CICell ih, char *str, long strMaxLen)
188 {
189
190 UInt16 nodeSize;
191 UInt32 firstLeafNode;
192 long long dirIndex;
193 char *name;
194 long flags, time;
195
196 if (HFSInitPartition(ih) == -1) { return; }
197
198 // Fill some crucial data structures by side effect.
199 dirIndex = 0;
200 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
201
202 // Now we can loook up the volume name node.
203 nodeSize = be16_to_cpu(gBTHeaders[kBTreeCatalog]->nodeSize);
204 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
205
206 dirIndex = (long long) firstLeafNode * nodeSize;
207
208 GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
209
210 strncpy(str, name, strMaxLen);
211 str[strMaxLen] = '\0';
212 }
213 */
214
215
216 static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol)
217 {
218 fsw_status_t status, rv;
219 void *buffer = NULL;
220 HFSPlusVolumeHeader *voldesc;
221 fsw_u32 blockno;
222 struct fsw_string s;
223 HFSMasterDirectoryBlock* mdb;
224 UINTN i;
225
226 rv = FSW_UNSUPPORTED;
227
228 vol->primary_voldesc = NULL;
229 fsw_set_blocksize(vol, HFS_BLOCKSIZE, HFS_BLOCKSIZE);
230 blockno = HFS_SUPERBLOCK_BLOCKNO;
231
232 #define CHECK(s) \
233 if (status) { \
234 rv = status; \
235 break; \
236 }
237
238 vol->emb_block_off = 0;
239 vol->hfs_kind = 0;
240 do {
241 fsw_u16 signature;
242 BTHeaderRec tree_header;
243 fsw_s32 r;
244 fsw_u32 block_size;
245
246 status = fsw_block_get(vol, blockno, 0, &buffer);
247 CHECK(status);
248 voldesc = (HFSPlusVolumeHeader *)buffer;
249 mdb = (HFSMasterDirectoryBlock*)buffer;
250 signature = be16_to_cpu(voldesc->signature);
251
252 if ((signature == kHFSPlusSigWord) || (signature == kHFSXSigWord)) //H+ or HX
253 {
254 if (vol->hfs_kind == 0)
255 {
256 // DPRINT("found HFS+\n");
257 vol->hfs_kind = FSW_HFS_PLUS;
258 }
259 }
260 else if (signature == kHFSSigWord) // 'BD'
261 {
262 // HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer;
263 //VolumeName = mdb->drVN 28bytes
264 if (be16_to_cpu(mdb->drEmbedSigWord) == kHFSPlusSigWord)
265 {
266 DPRINT("found HFS+ inside HFS, untested\n");
267 vol->hfs_kind = FSW_HFS_PLUS_EMB;
268 vol->emb_block_off = be32_to_cpu(mdb->drEmbedExtent.startBlock);
269 blockno += vol->emb_block_off;
270 /* retry */
271 continue;
272 }
273 else
274 {
275 DPRINT("found plain HFS, unsupported\n");
276 vol->hfs_kind = FSW_HFS_PLAIN;
277 }
278 rv = FSW_UNSUPPORTED;
279 break;
280 }
281 else
282 {
283 rv = FSW_UNSUPPORTED;
284 break;
285 }
286
287 status = fsw_memdup((void **)&vol->primary_voldesc, voldesc,
288 sizeof(*voldesc));
289 CHECK(status);
290
291
292 block_size = be32_to_cpu(voldesc->blockSize);
293 vol->block_size_shift = fsw_hfs_compute_shift(block_size);
294
295 fsw_block_release(vol, blockno, buffer);
296 buffer = NULL;
297 voldesc = NULL;
298 fsw_set_blocksize(vol, block_size, block_size);
299
300 /* get volume name */
301 for (i = kHFSMaxVolumeNameChars; i > 0; i--)
302 if (mdb->drVN[i-1] != ' ')
303 break;
304
305 s.type = FSW_STRING_TYPE_ISO88591;
306 s.size = s.len = 0;
307 s.data = NULL; //&mdb->drVN; //"HFS+ volume";
308
309 //fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src)
310 status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s);
311 CHECK(status);
312
313 /* Setup catalog dnode */
314 status = fsw_dnode_create_root(vol, kHFSCatalogFileID, &vol->catalog_tree.file);
315 CHECK(status);
316 fsw_memcpy (vol->catalog_tree.file->extents,
317 vol->primary_voldesc->catalogFile.extents,
318 sizeof vol->catalog_tree.file->extents);
319 vol->catalog_tree.file->g.size =
320 be64_to_cpu(vol->primary_voldesc->catalogFile.logicalSize);
321
322 /* Setup extents overflow file */
323 status = fsw_dnode_create_root(vol, kHFSExtentsFileID, &vol->extents_tree.file);
324 fsw_memcpy (vol->extents_tree.file->extents,
325 vol->primary_voldesc->extentsFile.extents,
326 sizeof vol->extents_tree.file->extents);
327 vol->extents_tree.file->g.size =
328 be64_to_cpu(vol->primary_voldesc->extentsFile.logicalSize);
329
330 /* Setup the root dnode */
331 status = fsw_dnode_create_root(vol, kHFSRootFolderID, &vol->g.root);
332 CHECK(status);
333
334 /*
335 * Read catalog file, we know that first record is in the first node, right after
336 * the node descriptor.
337 */
338 r = fsw_hfs_read_file(vol->catalog_tree.file,
339 sizeof (BTNodeDescriptor),
340 sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
341 if (r <= 0)
342 {
343 status = FSW_VOLUME_CORRUPTED;
344 break;
345 }
346 vol->case_sensitive =
347 (signature == kHFSXSigWord) &&
348 (tree_header.keyCompareType == kHFSBinaryCompare);
349 vol->catalog_tree.root_node = be32_to_cpu (tree_header.rootNode);
350 vol->catalog_tree.node_size = be16_to_cpu (tree_header.nodeSize);
351
352 /* Read extents overflow file */
353 r = fsw_hfs_read_file(vol->extents_tree.file,
354 sizeof (BTNodeDescriptor),
355 sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
356 if (r <= 0)
357 {
358 status = FSW_VOLUME_CORRUPTED;
359 break;
360 }
361
362 vol->extents_tree.root_node = be32_to_cpu (tree_header.rootNode);
363 vol->extents_tree.node_size = be16_to_cpu (tree_header.nodeSize);
364
365 rv = FSW_SUCCESS;
366 } while (0);
367
368 #undef CHECK
369
370
371 if (buffer != NULL)
372 fsw_block_release(vol, blockno, buffer);
373
374 return rv;
375 }
376 //Here is a method to obtain Volume label from Apple
377 //how to implement it?
378 /*
379 UInt16 nodeSize;
380 UInt32 firstLeafNode;
381 long long dirIndex;
382 char *name;
383 long flags, time;
384 char *nodeBuf, *testKey, *entry;
385
386
387 if (HFSInitPartition(ih) == -1) { return; }
388
389 // Fill some crucial data structures by side effect.
390 dirIndex = 0;
391 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
392
393 // Now we can loook up the volume name node.
394 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
395 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
396
397 dirIndex = (long long) firstLeafNode * nodeSize;
398 index = (long) (*dirIndex % nodeSize); == 0
399 curNode = (long) (*dirIndex / nodeSize); == firstLeafNode
400
401 //GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
402 // Read the BTree node and get the record for index.
403 ReadExtent(extent, extentSize, kHFSCatalogFileID,
404 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
405 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
406
407 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
408 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
409 (u_int8_t *)gTempStr, 256, OSBigEndian);
410
411 *name = gTempStr;
412
413 strncpy(str, name, strMaxLen);
414 str[strMaxLen] = '\0';
415 */
416
417 /**
418 * Free the volume data structure. Called by the core after an unmount or after
419 * an unsuccessful mount to release the memory used by the file system type specific
420 * part of the volume structure.
421 */
422
423 static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol)
424 {
425 if (vol->primary_voldesc)
426 {
427 fsw_free(vol->primary_voldesc);
428 vol->primary_voldesc = NULL;
429 }
430 }
431
432 /**
433 * Get in-depth information on a volume.
434 */
435
436 static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb)
437 {
438 sb->total_bytes = be32_to_cpu(vol->primary_voldesc->totalBlocks) << vol->block_size_shift;
439 sb->free_bytes = be32_to_cpu(vol->primary_voldesc->freeBlocks) << vol->block_size_shift;
440 return FSW_SUCCESS;
441 }
442
443 /**
444 * Get full information on a dnode from disk. This function is called by the core
445 * whenever it needs to access fields in the dnode structure that may not
446 * be filled immediately upon creation of the dnode.
447 */
448
449 static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
450 {
451 return FSW_SUCCESS;
452 }
453
454 /**
455 * Free the dnode data structure. Called by the core when deallocating a dnode
456 * structure to release the memory used by the file system type specific part
457 * of the dnode structure.
458 */
459
460 static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
461 {
462 }
463
464 static fsw_u32 mac_to_posix(fsw_u32 mac_time)
465 {
466 /* Mac time is 1904 year based */
467 return mac_time ? mac_time - 2082844800 : 0;
468 }
469
470 /**
471 * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
472 * has been called on the dnode before this function is called. Note that some
473 * data is not directly stored into the structure, but passed to a host-specific
474 * callback that converts it to the host-specific format.
475 */
476
477 static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol,
478 struct fsw_hfs_dnode *dno,
479 struct fsw_dnode_stat *sb)
480 {
481 sb->used_bytes = dno->used_bytes;
482 sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, mac_to_posix(dno->ctime));
483 sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, mac_to_posix(dno->mtime));
484 sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, 0);
485 sb->store_attr_posix(sb, 0700);
486
487 return FSW_SUCCESS;
488 }
489
490 static int
491 fsw_hfs_find_block(HFSPlusExtentRecord * exts,
492 fsw_u32 * lbno,
493 fsw_u32 * pbno)
494 {
495 int i;
496 fsw_u32 cur_lbno = *lbno;
497
498 for (i = 0; i < 8; i++)
499 {
500 fsw_u32 start = be32_to_cpu ((*exts)[i].startBlock);
501 fsw_u32 count = be32_to_cpu ((*exts)[i].blockCount);
502
503 if (cur_lbno < count)
504 {
505 *pbno = start + cur_lbno;
506 return 1;
507 }
508
509 cur_lbno -= count;
510 }
511
512 *lbno = cur_lbno;
513
514 return 0;
515 }
516
517 /* Find record offset, numbering starts from the end */
518 static fsw_u32
519 fsw_hfs_btree_recoffset (struct fsw_hfs_btree * btree,
520 BTNodeDescriptor * node,
521 fsw_u32 index)
522 {
523 fsw_u8 *cnode = (fsw_u8 *) node;
524 fsw_u16 *recptr;
525 recptr = (fsw_u16 *) (cnode+btree->node_size - index * 2 - 2);
526 return be16_to_cpu(*recptr);
527 }
528
529 /* Pointer to the key inside node */
530 static BTreeKey *
531 fsw_hfs_btree_rec (struct fsw_hfs_btree * btree,
532 BTNodeDescriptor * node,
533 fsw_u32 index)
534 {
535 fsw_u8 *cnode = (fsw_u8 *) node;
536 fsw_u32 offset;
537 offset = fsw_hfs_btree_recoffset (btree, node, index);
538 return (BTreeKey *) (cnode + offset);
539 }
540
541
542 static fsw_status_t
543 fsw_hfs_btree_search (struct fsw_hfs_btree * btree,
544 BTreeKey * key,
545 int (*compare_keys) (BTreeKey* key1, BTreeKey* key2),
546 BTNodeDescriptor ** result,
547 fsw_u32 * key_offset)
548 {
549 BTNodeDescriptor* node;
550 fsw_u32 currnode;
551 fsw_u32 rec;
552 fsw_status_t status;
553 fsw_u8* buffer = NULL;
554
555 currnode = btree->root_node;
556 status = fsw_alloc(btree->node_size, &buffer);
557 if (status)
558 return status;
559 node = (BTNodeDescriptor*)buffer;
560
561 while (1)
562 {
563 int cmp = 0;
564 int match;
565 fsw_u32 count;
566
567 readnode:
568 match = 0;
569 /* Read a node. */
570 if (fsw_hfs_read_file (btree->file,
571 (fsw_u64)currnode * btree->node_size,
572 btree->node_size, buffer) <= 0)
573 {
574 status = FSW_VOLUME_CORRUPTED;
575 break;
576 }
577
578 if (be16_to_cpu(*(fsw_u16*)(buffer + btree->node_size - 2)) != sizeof(BTNodeDescriptor))
579 BP("corrupted node\n");
580
581 count = be16_to_cpu (node->numRecords);
582
583 #if 1
584 for (rec = 0; rec < count; rec++)
585 {
586 BTreeKey *currkey;
587
588 currkey = fsw_hfs_btree_rec (btree, node, rec);
589 cmp = compare_keys (currkey, key);
590 //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
591
592 /* Leaf node. */
593 if (node->kind == kBTLeafNode)
594 {
595 if (cmp == 0)
596 {
597 /* Found! */
598 *result = node;
599 *key_offset = rec;
600
601 status = FSW_SUCCESS;
602 goto done;
603 }
604 }
605 else if (node->kind == kBTIndexNode)
606 {
607 fsw_u32 *pointer;
608
609 if (cmp > 0)
610 break;
611
612 pointer = (fsw_u32 *) ((char *) currkey
613 + be16_to_cpu (currkey->length16)
614 + 2);
615 currnode = be32_to_cpu (*pointer);
616 match = 1;
617 }
618 }
619
620 if (node->kind == kBTLeafNode && cmp < 0 && node->fLink)
621 {
622 currnode = be32_to_cpu(node->fLink);
623 goto readnode;
624 }
625 else if (!match)
626 {
627 status = FSW_NOT_FOUND;
628 break;
629 }
630 #else
631 /* Perform binary search */
632 fsw_u32 lower = 0;
633 fsw_u32 upper = count - 1;
634 fsw_s32 cmp = -1;
635 BTreeKey *currkey = NULL;
636
637 if (count == 0)
638 {
639 status = FSW_NOT_FOUND;
640 goto done;
641 }
642
643 while (lower <= upper)
644 {
645 fsw_u32 index = (lower + upper) / 2;
646
647 currkey = fsw_hfs_btree_rec (btree, node, index);
648
649 cmp = compare_keys (currkey, key);
650 if (cmp < 0) upper = index - 1;
651 if (cmp > 0) lower = index + 1;
652 if (cmp == 0)
653 {
654 /* Found! */
655 *result = node;
656 *key_offset = rec;
657
658 status = FSW_SUCCESS;
659 goto done;
660 }
661 }
662
663 if (cmp < 0)
664 currkey = fsw_hfs_btree_rec (btree, node, upper);
665
666 if (node->kind == kBTIndexNode && currkey)
667 {
668 fsw_u32 *pointer;
669
670 pointer = (fsw_u32 *) ((char *) currkey
671 + be16_to_cpu (currkey->length16)
672 + 2);
673 currnode = be32_to_cpu (*pointer);
674 }
675 else
676 {
677 status = FSW_NOT_FOUND;
678 break;
679 }
680 #endif
681 }
682
683
684 done:
685 if (buffer != NULL && status != FSW_SUCCESS)
686 fsw_free(buffer);
687
688 return status;
689 }
690 typedef struct
691 {
692 fsw_u32 id;
693 fsw_u32 type;
694 struct fsw_string * name;
695 fsw_u64 size;
696 fsw_u64 used;
697 fsw_u32 ctime;
698 fsw_u32 mtime;
699 HFSPlusExtentRecord extents;
700 } file_info_t;
701
702 typedef struct
703 {
704 fsw_u32 cur_pos; /* current position */
705 fsw_u32 parent;
706 struct fsw_hfs_volume * vol;
707
708 struct fsw_shandle * shandle; /* this one track iterator's state */
709 file_info_t file_info;
710 } visitor_parameter_t;
711
712 static int
713 fsw_hfs_btree_visit_node(BTreeKey *record, void* param)
714 {
715 visitor_parameter_t* vp = (visitor_parameter_t*)param;
716 fsw_u8* base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2;
717 fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base);
718 struct HFSPlusCatalogKey* cat_key = (HFSPlusCatalogKey*)record;
719 fsw_u16 name_len;
720 fsw_u16 *name_ptr;
721 fsw_u32 i;
722 struct fsw_string * file_name;
723
724 if (be32_to_cpu(cat_key->parentID) != vp->parent)
725 return -1;
726
727 /* not smth we care about */
728 if (vp->shandle->pos != vp->cur_pos++)
729 return 0;
730
731 switch (rec_type)
732 {
733 case kHFSPlusFolderRecord:
734 {
735 HFSPlusCatalogFolder* folder_info = (HFSPlusCatalogFolder*)base;
736
737 vp->file_info.id = be32_to_cpu(folder_info->folderID);
738 vp->file_info.type = FSW_DNODE_TYPE_DIR;
739 vp->file_info.size = be32_to_cpu(folder_info->valence);
740 vp->file_info.used = be32_to_cpu(folder_info->valence);
741 vp->file_info.ctime = be32_to_cpu(folder_info->createDate);
742 vp->file_info.mtime = be32_to_cpu(folder_info->contentModDate);
743 break;
744 }
745 case kHFSPlusFileRecord:
746 {
747 HFSPlusCatalogFile* file_info = (HFSPlusCatalogFile*)base;
748
749 vp->file_info.id = be32_to_cpu(file_info->fileID);
750 vp->file_info.type = FSW_DNODE_TYPE_FILE;
751 vp->file_info.size = be64_to_cpu(file_info->dataFork.logicalSize);
752 vp->file_info.used = LShiftU64(be32_to_cpu(file_info->dataFork.totalBlocks),
753 vp->vol->block_size_shift);
754 vp->file_info.ctime = be32_to_cpu(file_info->createDate);
755 vp->file_info.mtime = be32_to_cpu(file_info->contentModDate);
756 fsw_memcpy(&vp->file_info.extents, &file_info->dataFork.extents,
757 sizeof vp->file_info.extents);
758 break;
759 }
760 case kHFSPlusFolderThreadRecord:
761 case kHFSPlusFileThreadRecord:
762 {
763 vp->shandle->pos++;
764 return 0;
765 }
766 default:
767 BP("unknown file type\n");
768 vp->file_info.type = FSW_DNODE_TYPE_UNKNOWN;
769 break;
770 }
771
772 name_len = be16_to_cpu(cat_key->nodeName.length);
773
774 file_name = vp->file_info.name;
775 file_name->len = name_len;
776 fsw_memdup(&file_name->data, &cat_key->nodeName.unicode[0], 2*name_len);
777 file_name->size = 2*name_len;
778 file_name->type = FSW_STRING_TYPE_UTF16;
779 name_ptr = (fsw_u16*)file_name->data;
780 for (i=0; i<name_len; i++)
781 {
782 name_ptr[i] = be16_to_cpu(name_ptr[i]);
783 }
784 vp->shandle->pos++;
785
786 return 1;
787 }
788
789 static fsw_status_t
790 fsw_hfs_btree_iterate_node (struct fsw_hfs_btree * btree,
791 BTNodeDescriptor * first_node,
792 fsw_u32 first_rec,
793 int (*callback) (BTreeKey *record, void* param),
794 void * param)
795 {
796 fsw_status_t status;
797 /* We modify node, so make a copy */
798 BTNodeDescriptor* node = first_node;
799 fsw_u8* buffer = NULL;
800
801 status = fsw_alloc(btree->node_size, &buffer);
802 if (status)
803 return status;
804
805 while (1)
806 {
807 fsw_u32 i;
808 fsw_u32 count = be16_to_cpu(node->numRecords);
809 fsw_u32 next_node;
810
811 /* Iterate over all records in this node. */
812 for (i = first_rec; i < count; i++)
813 {
814 int rv = callback(fsw_hfs_btree_rec (btree, node, i), param);
815
816 switch (rv)
817 {
818 case 1:
819 status = FSW_SUCCESS;
820 goto done;
821 case -1:
822 status = FSW_NOT_FOUND;
823 goto done;
824 }
825 /* if callback returned 0 - continue */
826 }
827
828 next_node = be32_to_cpu(node->fLink);
829
830 if (!next_node)
831 {
832 status = FSW_NOT_FOUND;
833 break;
834 }
835
836 if (fsw_hfs_read_file (btree->file,
837 next_node * btree->node_size,
838 btree->node_size, buffer) <= 0)
839 {
840 status = FSW_VOLUME_CORRUPTED;
841 return 1;
842 }
843
844 node = (BTNodeDescriptor*)buffer;
845 first_rec = 0;
846 }
847 done:
848 if (buffer)
849 fsw_free(buffer);
850
851 return status;
852 }
853
854 #if 0
855 void deb(fsw_u16* p, int len, int swap)
856 {
857 int i;
858 for (i=0; i<len; i++)
859 {
860 printf("%c", swap ? be16_to_cpu(p[i]) : p[i]);
861 }
862 printf("\n");
863 }
864 #endif
865
866 static int
867 fsw_hfs_cmp_extkey(BTreeKey* key1, BTreeKey* key2)
868 {
869 HFSPlusExtentKey* ekey1 = (HFSPlusExtentKey*)key1;
870 HFSPlusExtentKey* ekey2 = (HFSPlusExtentKey*)key2;
871 int result;
872
873 /* First key is read from the FS data, second is in-memory in CPU endianess */
874 result = be32_to_cpu(ekey1->fileID) - ekey2->fileID;
875
876 if (result)
877 return result;
878
879 result = ekey1->forkType - ekey2->forkType;
880
881 if (result)
882 return result;
883
884 result = be32_to_cpu(ekey1->startBlock) - ekey2->startBlock;
885 return result;
886 }
887
888 static int
889 fsw_hfs_cmp_catkey (BTreeKey *key1, BTreeKey *key2)
890 {
891 HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
892 HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
893
894 int apos, bpos, lc;
895 fsw_u16 ac, bc;
896 fsw_u32 parentId1;
897 int key1Len;
898 fsw_u16 *p1;
899 fsw_u16 *p2;
900
901 parentId1 = be32_to_cpu(ckey1->parentID);
902
903 if (parentId1 > ckey2->parentID)
904 return 1;
905 if (parentId1 < ckey2->parentID)
906 return -1;
907
908 p1 = &ckey1->nodeName.unicode[0];
909 p2 = &ckey2->nodeName.unicode[0];
910 key1Len = be16_to_cpu (ckey1->nodeName.length);
911 apos = bpos = 0;
912
913 while(1)
914 {
915 /* get next valid character from ckey1 */
916 for (lc = 0; lc == 0 && apos < key1Len; apos++) {
917 ac = be16_to_cpu(p1[apos]);
918 lc = ac;
919 };
920 ac = (fsw_u16)lc;
921
922 /* get next valid character from ckey2 */
923 for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
924 bc = p2[bpos];
925 lc = bc;
926 };
927 bc = (fsw_u16)lc;
928
929 if (ac != bc || (ac == 0 && bc == 0))
930 return ac - bc;
931 }
932 }
933
934 static int
935 fsw_hfs_cmpi_catkey (BTreeKey *key1, BTreeKey *key2)
936 {
937 HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
938 HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
939
940 int apos, bpos, lc;
941 fsw_u16 ac, bc;
942 fsw_u32 parentId1;
943 int key1Len;
944 fsw_u16 *p1;
945 fsw_u16 *p2;
946
947 parentId1 = be32_to_cpu(ckey1->parentID);
948
949 if (parentId1 > ckey2->parentID)
950 return 1;
951 if (parentId1 < ckey2->parentID)
952 return -1;
953
954 key1Len = be16_to_cpu (ckey1->nodeName.length);
955
956 if (key1Len == 0 && ckey2->nodeName.length == 0)
957 return 0;
958
959 p1 = &ckey1->nodeName.unicode[0];
960 p2 = &ckey2->nodeName.unicode[0];
961
962 apos = bpos = 0;
963
964 while(1)
965 {
966 /* get next valid character from ckey1 */
967 for (lc = 0; lc == 0 && apos < key1Len; apos++) {
968 ac = be16_to_cpu(p1[apos]);
969 lc = ac ? fsw_to_lower(ac) : 0;
970 };
971 ac = (fsw_u16)lc;
972
973 /* get next valid character from ckey2 */
974 for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
975 bc = p2[bpos];
976 lc = bc ? fsw_to_lower(bc) : 0;
977 };
978 bc = (fsw_u16)lc;
979
980 if (ac != bc || (ac == 0 && bc == 0))
981 return ac - bc;
982 }
983 }
984
985 /**
986 * Retrieve file data mapping information. This function is called by the core when
987 * fsw_shandle_read needs to know where on the disk the required piece of the file's
988 * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
989 * on the dnode before. Our task here is to get the physical disk block number for
990 * the requested logical block number.
991 */
992
993 static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume * vol,
994 struct fsw_hfs_dnode * dno,
995 struct fsw_extent * extent)
996 {
997 fsw_status_t status;
998 fsw_u32 lbno;
999 HFSPlusExtentRecord *exts;
1000 BTNodeDescriptor *node = NULL;
1001
1002 extent->type = FSW_EXTENT_TYPE_PHYSBLOCK;
1003 extent->log_count = 1;
1004 lbno = extent->log_start;
1005
1006 /* we only care about data forks atm, do we? */
1007 exts = &dno->extents;
1008
1009 while (1)
1010 {
1011 struct HFSPlusExtentKey* key;
1012 struct HFSPlusExtentKey overflowkey;
1013 fsw_u32 ptr;
1014 fsw_u32 phys_bno;
1015
1016 if (fsw_hfs_find_block(exts, &lbno, &phys_bno))
1017 {
1018 extent->phys_start = phys_bno + vol->emb_block_off;
1019 status = FSW_SUCCESS;
1020 break;
1021 }
1022
1023
1024 /* Find appropriate overflow record */
1025 overflowkey.fileID = dno->g.dnode_id;
1026 overflowkey.startBlock = extent->log_start - lbno;
1027
1028 if (node != NULL)
1029 {
1030 fsw_free(node);
1031 node = NULL;
1032 }
1033
1034 status = fsw_hfs_btree_search (&vol->extents_tree,
1035 (BTreeKey*)&overflowkey,
1036 fsw_hfs_cmp_extkey,
1037 &node, &ptr);
1038 if (status)
1039 break;
1040
1041 key = (struct HFSPlusExtentKey *)
1042 fsw_hfs_btree_rec (&vol->extents_tree, node, ptr);
1043 exts = (HFSPlusExtentRecord*) (key + 1);
1044 }
1045
1046 if (node != NULL)
1047 fsw_free(node);
1048
1049 return status;
1050 }
1051
1052 static const fsw_u16* g_blacklist[] =
1053 {
1054 //L"AppleIntelCPUPowerManagement.kext",
1055 NULL
1056 };
1057
1058
1059 //#define HFS_FILE_INJECTION
1060
1061 #ifdef HFS_FILE_INJECTION
1062 static struct
1063 {
1064 const fsw_u16* path;
1065 const fsw_u16* name;
1066 } g_injectList[] =
1067 {
1068 {
1069 L"/System/Library/Extensions",
1070 L"ApplePS2Controller.kext"
1071 },
1072 {
1073 NULL,
1074 NULL
1075 }
1076 };
1077 #endif
1078
1079 static fsw_status_t
1080 create_hfs_dnode(struct fsw_hfs_dnode * dno,
1081 file_info_t * file_info,
1082 struct fsw_hfs_dnode ** child_dno_out)
1083 {
1084 fsw_status_t status;
1085 struct fsw_hfs_dnode * baby;
1086
1087 status = fsw_dnode_create(dno, file_info->id, file_info->type,
1088 file_info->name, &baby);
1089 if (status)
1090 return status;
1091
1092 baby->g.size = file_info->size;
1093 baby->used_bytes = file_info->used;
1094 baby->ctime = file_info->ctime;
1095 baby->mtime = file_info->mtime;
1096
1097
1098 /* Fill-in extents info */
1099 if (file_info->type == FSW_DNODE_TYPE_FILE)
1100 {
1101 fsw_memcpy(baby->extents, &file_info->extents, sizeof file_info->extents);
1102 }
1103
1104 *child_dno_out = baby;
1105
1106 return FSW_SUCCESS;
1107 }
1108
1109
1110 /**
1111 * Lookup a directory's child dnode by name. This function is called on a directory
1112 * to retrieve the directory entry with the given name. A dnode is constructed for
1113 * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1114 * and the dnode is actually a directory.
1115 */
1116
1117 static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume * vol,
1118 struct fsw_hfs_dnode * dno,
1119 struct fsw_string * lookup_name,
1120 struct fsw_hfs_dnode ** child_dno_out)
1121 {
1122 fsw_status_t status;
1123 struct HFSPlusCatalogKey catkey;
1124 fsw_u32 ptr;
1125 fsw_u16 rec_type;
1126 BTNodeDescriptor * node = NULL;
1127 struct fsw_string rec_name;
1128 int free_data = 0, i;
1129 HFSPlusCatalogKey* file_key;
1130 file_info_t file_info;
1131 fsw_u8* base;
1132
1133
1134 fsw_memzero(&file_info, sizeof file_info);
1135 file_info.name = &rec_name;
1136
1137 catkey.parentID = dno->g.dnode_id;
1138 catkey.nodeName.length = (fsw_u16)lookup_name->len;
1139
1140 /* no need to allocate anything */
1141 if (lookup_name->type == FSW_STRING_TYPE_UTF16)
1142 {
1143 fsw_memcpy(catkey.nodeName.unicode, lookup_name->data, lookup_name->size);
1144 rec_name = *lookup_name;
1145 } else
1146 {
1147 status = fsw_strdup_coerce(&rec_name, FSW_STRING_TYPE_UTF16, lookup_name);
1148 /* nothing allocated so far */
1149 if (status)
1150 goto done;
1151 free_data = 1;
1152 fsw_memcpy(catkey.nodeName.unicode, rec_name.data, rec_name.size);
1153 }
1154
1155 /* Dirty hack: blacklisting of certain files on FS driver level */
1156 for (i = 0; g_blacklist[i]; i++)
1157 {
1158 if (fsw_memeq(g_blacklist[i], catkey.nodeName.unicode, catkey.nodeName.length*2))
1159 {
1160 DPRINT2("Blacklisted %s\n", g_blacklist[i]);
1161 status = FSW_NOT_FOUND;
1162 goto done;
1163 }
1164 }
1165
1166 #ifdef HFS_FILE_INJECTION
1167 if (fsw_hfs_inject(vol,
1168 dno,
1169 catkey.nodeName.unicode,
1170 catkey.nodeName.length,
1171 &file_info))
1172 {
1173 status = FSW_SUCCESS;
1174 goto create;
1175 }
1176 #endif
1177
1178 catkey.keyLength = (fsw_u16)(5 + rec_name.size);
1179
1180 status = fsw_hfs_btree_search (&vol->catalog_tree,
1181 (BTreeKey*)&catkey,
1182 vol->case_sensitive ?
1183 fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
1184 &node, &ptr);
1185 if (status)
1186 goto done;
1187
1188 file_key = (HFSPlusCatalogKey *)fsw_hfs_btree_rec (&vol->catalog_tree, node, ptr);
1189 /* for plain HFS "-(keySize & 1)" would be needed */
1190 base = (fsw_u8*)file_key + be16_to_cpu(file_key->keyLength) + 2;
1191 rec_type = be16_to_cpu(*(fsw_u16*)base);
1192
1193 /** @todo: read additional info */
1194 switch (rec_type)
1195 {
1196 case kHFSPlusFolderRecord:
1197 {
1198 HFSPlusCatalogFolder* info = (HFSPlusCatalogFolder*)base;
1199
1200 file_info.id = be32_to_cpu(info->folderID);
1201 file_info.type = FSW_DNODE_TYPE_DIR;
1202 /* @todo: return number of elements, maybe use smth else */
1203 file_info.size = be32_to_cpu(info->valence);
1204 file_info.used = be32_to_cpu(info->valence);
1205 file_info.ctime = be32_to_cpu(info->createDate);
1206 file_info.mtime = be32_to_cpu(info->contentModDate);
1207 break;
1208 }
1209 case kHFSPlusFileRecord:
1210 {
1211 HFSPlusCatalogFile* info = (HFSPlusCatalogFile*)base;
1212
1213 file_info.id = be32_to_cpu(info->fileID);
1214 file_info.type = FSW_DNODE_TYPE_FILE;
1215 file_info.size = be64_to_cpu(info->dataFork.logicalSize);
1216 file_info.used = LShiftU64(be32_to_cpu(info->dataFork.totalBlocks), vol->block_size_shift);
1217 file_info.ctime = be32_to_cpu(info->createDate);
1218 file_info.mtime = be32_to_cpu(info->contentModDate);
1219 fsw_memcpy(&file_info.extents, &info->dataFork.extents,
1220 sizeof file_info.extents);
1221 break;
1222 }
1223 default:
1224 BP("unknown file type\n");
1225 file_info.type = FSW_DNODE_TYPE_UNKNOWN;
1226
1227 break;
1228 }
1229 #ifdef HFS_FILE_INJECTION
1230 create:
1231 #endif
1232 status = create_hfs_dnode(dno, &file_info, child_dno_out);
1233 if (status)
1234 goto done;
1235
1236 done:
1237
1238 if (node != NULL)
1239 fsw_free(node);
1240
1241 if (free_data)
1242 fsw_strfree(&rec_name);
1243
1244 return status;
1245 }
1246
1247 /**
1248 * Get the next directory entry when reading a directory. This function is called during
1249 * directory iteration to retrieve the next directory entry. A dnode is constructed for
1250 * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1251 * and the dnode is actually a directory. The shandle provided by the caller is used to
1252 * record the position in the directory between calls.
1253 */
1254
1255 static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol,
1256 struct fsw_hfs_dnode *dno,
1257 struct fsw_shandle *shand,
1258 struct fsw_hfs_dnode **child_dno_out)
1259 {
1260 fsw_status_t status;
1261 struct HFSPlusCatalogKey catkey;
1262 fsw_u32 ptr;
1263 BTNodeDescriptor * node = NULL;
1264
1265 visitor_parameter_t param;
1266 struct fsw_string rec_name;
1267
1268 catkey.parentID = dno->g.dnode_id;
1269 catkey.nodeName.length = 0;
1270
1271 fsw_memzero(&param, sizeof(param));
1272
1273 rec_name.type = FSW_STRING_TYPE_EMPTY;
1274 param.file_info.name = &rec_name;
1275
1276 status = fsw_hfs_btree_search (&vol->catalog_tree,
1277 (BTreeKey*)&catkey,
1278 vol->case_sensitive ?
1279 fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
1280 &node, &ptr);
1281 if (status)
1282 goto done;
1283
1284 /* Iterator updates shand state */
1285 param.vol = vol;
1286 param.shandle = shand;
1287 param.parent = dno->g.dnode_id;
1288 param.cur_pos = 0;
1289 status = fsw_hfs_btree_iterate_node (&vol->catalog_tree,
1290 node,
1291 ptr,
1292 fsw_hfs_btree_visit_node,
1293 &param);
1294 if (status)
1295 goto done;
1296
1297 status = create_hfs_dnode(dno, &param.file_info, child_dno_out);
1298
1299 if (status)
1300 goto done;
1301
1302 done:
1303 fsw_strfree(&rec_name);
1304
1305 return status;
1306 }
1307
1308 /**
1309 * Get the target path of a symbolic link. This function is called when a symbolic
1310 * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
1311 * called on the dnode and that it really is a symlink.
1312 *
1313 */
1314 static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
1315 struct fsw_string *link_target)
1316 {
1317 return FSW_UNSUPPORTED;
1318 }
1319
1320 // EOF