]> code.delx.au - refind/blob - filesystems/fsw_hfs.c
27195680ac79a24b4449d9a32b5c7c54ae9bd4c0
[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 s.type = FSW_STRING_TYPE_ISO88591;
302 s.size = s.len = kHFSMaxVolumeNameChars;
303 s.data = "HFS+ volume";
304 status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s);
305 CHECK(status);
306
307 /* Setup catalog dnode */
308 status = fsw_dnode_create_root(vol, kHFSCatalogFileID, &vol->catalog_tree.file);
309 CHECK(status);
310 fsw_memcpy (vol->catalog_tree.file->extents,
311 vol->primary_voldesc->catalogFile.extents,
312 sizeof vol->catalog_tree.file->extents);
313 vol->catalog_tree.file->g.size =
314 be64_to_cpu(vol->primary_voldesc->catalogFile.logicalSize);
315
316 /* Setup extents overflow file */
317 status = fsw_dnode_create_root(vol, kHFSExtentsFileID, &vol->extents_tree.file);
318 fsw_memcpy (vol->extents_tree.file->extents,
319 vol->primary_voldesc->extentsFile.extents,
320 sizeof vol->extents_tree.file->extents);
321 vol->extents_tree.file->g.size =
322 be64_to_cpu(vol->primary_voldesc->extentsFile.logicalSize);
323
324 /* Setup the root dnode */
325 status = fsw_dnode_create_root(vol, kHFSRootFolderID, &vol->g.root);
326 CHECK(status);
327
328 /*
329 * Read catalog file, we know that first record is in the first node, right after
330 * the node descriptor.
331 */
332 r = fsw_hfs_read_file(vol->catalog_tree.file,
333 sizeof (BTNodeDescriptor),
334 sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
335 if (r <= 0)
336 {
337 status = FSW_VOLUME_CORRUPTED;
338 break;
339 }
340 vol->case_sensitive =
341 (signature == kHFSXSigWord) &&
342 (tree_header.keyCompareType == kHFSBinaryCompare);
343 vol->catalog_tree.root_node = be32_to_cpu (tree_header.rootNode);
344 vol->catalog_tree.node_size = be16_to_cpu (tree_header.nodeSize);
345
346 // /* get volume name */
347 // s.type = FSW_STRING_TYPE_ISO88591;
348 // s.size = s.len = kHFSMaxVolumeNameChars;
349 // s.data = vol->catalog_tree.file->g.name.data;
350 // status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s);
351 // CHECK(status);
352
353 /* Read extents overflow file */
354 r = fsw_hfs_read_file(vol->extents_tree.file,
355 sizeof (BTNodeDescriptor),
356 sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
357 if (r <= 0)
358 {
359 status = FSW_VOLUME_CORRUPTED;
360 break;
361 }
362
363 vol->extents_tree.root_node = be32_to_cpu (tree_header.rootNode);
364 vol->extents_tree.node_size = be16_to_cpu (tree_header.nodeSize);
365
366 rv = FSW_SUCCESS;
367 } while (0);
368
369 #undef CHECK
370
371
372 if (buffer != NULL)
373 fsw_block_release(vol, blockno, buffer);
374
375 return rv;
376 }
377 //Here is a method to obtain Volume label from Apple
378 //how to implement it?
379 /*
380 UInt16 nodeSize;
381 UInt32 firstLeafNode;
382 long long dirIndex;
383 char *name;
384 long flags, time;
385 char *nodeBuf, *testKey, *entry;
386
387
388 if (HFSInitPartition(ih) == -1) { return; }
389
390 // Fill some crucial data structures by side effect.
391 dirIndex = 0;
392 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
393
394 // Now we can loook up the volume name node.
395 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
396 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
397
398 dirIndex = (long long) firstLeafNode * nodeSize;
399 index = (long) (*dirIndex % nodeSize); == 0
400 curNode = (long) (*dirIndex / nodeSize); == firstLeafNode
401
402 //GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
403 // Read the BTree node and get the record for index.
404 ReadExtent(extent, extentSize, kHFSCatalogFileID,
405 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
406 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
407
408 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
409 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
410 (u_int8_t *)gTempStr, 256, OSBigEndian);
411
412 *name = gTempStr;
413
414 strncpy(str, name, strMaxLen);
415 str[strMaxLen] = '\0';
416 */
417
418 /**
419 * Free the volume data structure. Called by the core after an unmount or after
420 * an unsuccessful mount to release the memory used by the file system type specific
421 * part of the volume structure.
422 */
423
424 static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol)
425 {
426 if (vol->primary_voldesc)
427 {
428 fsw_free(vol->primary_voldesc);
429 vol->primary_voldesc = NULL;
430 }
431 }
432
433 /**
434 * Get in-depth information on a volume.
435 */
436
437 static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb)
438 {
439 sb->total_bytes = be32_to_cpu(vol->primary_voldesc->totalBlocks) << vol->block_size_shift;
440 sb->free_bytes = be32_to_cpu(vol->primary_voldesc->freeBlocks) << vol->block_size_shift;
441 return FSW_SUCCESS;
442 }
443
444 /**
445 * Get full information on a dnode from disk. This function is called by the core
446 * whenever it needs to access fields in the dnode structure that may not
447 * be filled immediately upon creation of the dnode.
448 */
449
450 static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
451 {
452 return FSW_SUCCESS;
453 }
454
455 /**
456 * Free the dnode data structure. Called by the core when deallocating a dnode
457 * structure to release the memory used by the file system type specific part
458 * of the dnode structure.
459 */
460
461 static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
462 {
463 }
464
465 static fsw_u32 mac_to_posix(fsw_u32 mac_time)
466 {
467 /* Mac time is 1904 year based */
468 return mac_time ? mac_time - 2082844800 : 0;
469 }
470
471 /**
472 * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
473 * has been called on the dnode before this function is called. Note that some
474 * data is not directly stored into the structure, but passed to a host-specific
475 * callback that converts it to the host-specific format.
476 */
477
478 static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol,
479 struct fsw_hfs_dnode *dno,
480 struct fsw_dnode_stat *sb)
481 {
482 sb->used_bytes = dno->used_bytes;
483 sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, mac_to_posix(dno->ctime));
484 sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, mac_to_posix(dno->mtime));
485 sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, 0);
486 sb->store_attr_posix(sb, 0700);
487
488 return FSW_SUCCESS;
489 }
490
491 static int
492 fsw_hfs_find_block(HFSPlusExtentRecord * exts,
493 fsw_u32 * lbno,
494 fsw_u32 * pbno)
495 {
496 int i;
497 fsw_u32 cur_lbno = *lbno;
498
499 for (i = 0; i < 8; i++)
500 {
501 fsw_u32 start = be32_to_cpu ((*exts)[i].startBlock);
502 fsw_u32 count = be32_to_cpu ((*exts)[i].blockCount);
503
504 if (cur_lbno < count)
505 {
506 *pbno = start + cur_lbno;
507 return 1;
508 }
509
510 cur_lbno -= count;
511 }
512
513 *lbno = cur_lbno;
514
515 return 0;
516 }
517
518 /* Find record offset, numbering starts from the end */
519 static fsw_u32
520 fsw_hfs_btree_recoffset (struct fsw_hfs_btree * btree,
521 BTNodeDescriptor * node,
522 fsw_u32 index)
523 {
524 fsw_u8 *cnode = (fsw_u8 *) node;
525 fsw_u16 *recptr;
526 recptr = (fsw_u16 *) (cnode+btree->node_size - index * 2 - 2);
527 return be16_to_cpu(*recptr);
528 }
529
530 /* Pointer to the key inside node */
531 static BTreeKey *
532 fsw_hfs_btree_rec (struct fsw_hfs_btree * btree,
533 BTNodeDescriptor * node,
534 fsw_u32 index)
535 {
536 fsw_u8 *cnode = (fsw_u8 *) node;
537 fsw_u32 offset;
538 offset = fsw_hfs_btree_recoffset (btree, node, index);
539 return (BTreeKey *) (cnode + offset);
540 }
541
542
543 static fsw_status_t
544 fsw_hfs_btree_search (struct fsw_hfs_btree * btree,
545 BTreeKey * key,
546 int (*compare_keys) (BTreeKey* key1, BTreeKey* key2),
547 BTNodeDescriptor ** result,
548 fsw_u32 * key_offset)
549 {
550 BTNodeDescriptor* node;
551 fsw_u32 currnode;
552 fsw_u32 rec;
553 fsw_status_t status;
554 fsw_u8* buffer = NULL;
555
556 currnode = btree->root_node;
557 status = fsw_alloc(btree->node_size, &buffer);
558 if (status)
559 return status;
560 node = (BTNodeDescriptor*)buffer;
561
562 while (1)
563 {
564 int cmp = 0;
565 int match;
566 fsw_u32 count;
567
568 readnode:
569 match = 0;
570 /* Read a node. */
571 if (fsw_hfs_read_file (btree->file,
572 (fsw_u64)currnode * btree->node_size,
573 btree->node_size, buffer) <= 0)
574 {
575 status = FSW_VOLUME_CORRUPTED;
576 break;
577 }
578
579 if (be16_to_cpu(*(fsw_u16*)(buffer + btree->node_size - 2)) != sizeof(BTNodeDescriptor))
580 BP("corrupted node\n");
581
582 count = be16_to_cpu (node->numRecords);
583
584 #if 1
585 for (rec = 0; rec < count; rec++)
586 {
587 BTreeKey *currkey;
588
589 currkey = fsw_hfs_btree_rec (btree, node, rec);
590 cmp = compare_keys (currkey, key);
591 //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
592
593 /* Leaf node. */
594 if (node->kind == kBTLeafNode)
595 {
596 if (cmp == 0)
597 {
598 /* Found! */
599 *result = node;
600 *key_offset = rec;
601
602 status = FSW_SUCCESS;
603 goto done;
604 }
605 }
606 else if (node->kind == kBTIndexNode)
607 {
608 fsw_u32 *pointer;
609
610 if (cmp > 0)
611 break;
612
613 pointer = (fsw_u32 *) ((char *) currkey
614 + be16_to_cpu (currkey->length16)
615 + 2);
616 currnode = be32_to_cpu (*pointer);
617 match = 1;
618 }
619 }
620
621 if (node->kind == kBTLeafNode && cmp < 0 && node->fLink)
622 {
623 currnode = be32_to_cpu(node->fLink);
624 goto readnode;
625 }
626 else if (!match)
627 {
628 status = FSW_NOT_FOUND;
629 break;
630 }
631 #else
632 /* Perform binary search */
633 fsw_u32 lower = 0;
634 fsw_u32 upper = count - 1;
635 fsw_s32 cmp = -1;
636 BTreeKey *currkey = NULL;
637
638 if (count == 0)
639 {
640 status = FSW_NOT_FOUND;
641 goto done;
642 }
643
644 while (lower <= upper)
645 {
646 fsw_u32 index = (lower + upper) / 2;
647
648 currkey = fsw_hfs_btree_rec (btree, node, index);
649
650 cmp = compare_keys (currkey, key);
651 if (cmp < 0) upper = index - 1;
652 if (cmp > 0) lower = index + 1;
653 if (cmp == 0)
654 {
655 /* Found! */
656 *result = node;
657 *key_offset = rec;
658
659 status = FSW_SUCCESS;
660 goto done;
661 }
662 }
663
664 if (cmp < 0)
665 currkey = fsw_hfs_btree_rec (btree, node, upper);
666
667 if (node->kind == kBTIndexNode && currkey)
668 {
669 fsw_u32 *pointer;
670
671 pointer = (fsw_u32 *) ((char *) currkey
672 + be16_to_cpu (currkey->length16)
673 + 2);
674 currnode = be32_to_cpu (*pointer);
675 }
676 else
677 {
678 status = FSW_NOT_FOUND;
679 break;
680 }
681 #endif
682 }
683
684
685 done:
686 if (buffer != NULL && status != FSW_SUCCESS)
687 fsw_free(buffer);
688
689 return status;
690 }
691 typedef struct
692 {
693 fsw_u32 id;
694 fsw_u32 type;
695 struct fsw_string * name;
696 fsw_u64 size;
697 fsw_u64 used;
698 fsw_u32 ctime;
699 fsw_u32 mtime;
700 HFSPlusExtentRecord extents;
701 } file_info_t;
702
703 typedef struct
704 {
705 fsw_u32 cur_pos; /* current position */
706 fsw_u32 parent;
707 struct fsw_hfs_volume * vol;
708
709 struct fsw_shandle * shandle; /* this one track iterator's state */
710 file_info_t file_info;
711 } visitor_parameter_t;
712
713 static int
714 fsw_hfs_btree_visit_node(BTreeKey *record, void* param)
715 {
716 visitor_parameter_t* vp = (visitor_parameter_t*)param;
717 fsw_u8* base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2;
718 fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base);
719 struct HFSPlusCatalogKey* cat_key = (HFSPlusCatalogKey*)record;
720 fsw_u16 name_len;
721 fsw_u16 *name_ptr;
722 fsw_u32 i;
723 struct fsw_string * file_name;
724
725 if (be32_to_cpu(cat_key->parentID) != vp->parent)
726 return -1;
727
728 /* not smth we care about */
729 if (vp->shandle->pos != vp->cur_pos++)
730 return 0;
731
732 switch (rec_type)
733 {
734 case kHFSPlusFolderRecord:
735 {
736 HFSPlusCatalogFolder* folder_info = (HFSPlusCatalogFolder*)base;
737
738 vp->file_info.id = be32_to_cpu(folder_info->folderID);
739 vp->file_info.type = FSW_DNODE_TYPE_DIR;
740 vp->file_info.size = be32_to_cpu(folder_info->valence);
741 vp->file_info.used = be32_to_cpu(folder_info->valence);
742 vp->file_info.ctime = be32_to_cpu(folder_info->createDate);
743 vp->file_info.mtime = be32_to_cpu(folder_info->contentModDate);
744 break;
745 }
746 case kHFSPlusFileRecord:
747 {
748 HFSPlusCatalogFile* file_info = (HFSPlusCatalogFile*)base;
749
750 vp->file_info.id = be32_to_cpu(file_info->fileID);
751 vp->file_info.type = FSW_DNODE_TYPE_FILE;
752 vp->file_info.size = be64_to_cpu(file_info->dataFork.logicalSize);
753 vp->file_info.used = LShiftU64(be32_to_cpu(file_info->dataFork.totalBlocks),
754 vp->vol->block_size_shift);
755 vp->file_info.ctime = be32_to_cpu(file_info->createDate);
756 vp->file_info.mtime = be32_to_cpu(file_info->contentModDate);
757 fsw_memcpy(&vp->file_info.extents, &file_info->dataFork.extents,
758 sizeof vp->file_info.extents);
759 break;
760 }
761 case kHFSPlusFolderThreadRecord:
762 case kHFSPlusFileThreadRecord:
763 {
764 vp->shandle->pos++;
765 return 0;
766 }
767 default:
768 BP("unknown file type\n");
769 vp->file_info.type = FSW_DNODE_TYPE_UNKNOWN;
770 break;
771 }
772
773 name_len = be16_to_cpu(cat_key->nodeName.length);
774
775 file_name = vp->file_info.name;
776 file_name->len = name_len;
777 fsw_memdup(&file_name->data, &cat_key->nodeName.unicode[0], 2*name_len);
778 file_name->size = 2*name_len;
779 file_name->type = FSW_STRING_TYPE_UTF16;
780 name_ptr = (fsw_u16*)file_name->data;
781 for (i=0; i<name_len; i++)
782 {
783 name_ptr[i] = be16_to_cpu(name_ptr[i]);
784 }
785 vp->shandle->pos++;
786
787 return 1;
788 }
789
790 static fsw_status_t
791 fsw_hfs_btree_iterate_node (struct fsw_hfs_btree * btree,
792 BTNodeDescriptor * first_node,
793 fsw_u32 first_rec,
794 int (*callback) (BTreeKey *record, void* param),
795 void * param)
796 {
797 fsw_status_t status;
798 /* We modify node, so make a copy */
799 BTNodeDescriptor* node = first_node;
800 fsw_u8* buffer = NULL;
801
802 status = fsw_alloc(btree->node_size, &buffer);
803 if (status)
804 return status;
805
806 while (1)
807 {
808 fsw_u32 i;
809 fsw_u32 count = be16_to_cpu(node->numRecords);
810 fsw_u32 next_node;
811
812 /* Iterate over all records in this node. */
813 for (i = first_rec; i < count; i++)
814 {
815 int rv = callback(fsw_hfs_btree_rec (btree, node, i), param);
816
817 switch (rv)
818 {
819 case 1:
820 status = FSW_SUCCESS;
821 goto done;
822 case -1:
823 status = FSW_NOT_FOUND;
824 goto done;
825 }
826 /* if callback returned 0 - continue */
827 }
828
829 next_node = be32_to_cpu(node->fLink);
830
831 if (!next_node)
832 {
833 status = FSW_NOT_FOUND;
834 break;
835 }
836
837 if (fsw_hfs_read_file (btree->file,
838 next_node * btree->node_size,
839 btree->node_size, buffer) <= 0)
840 {
841 status = FSW_VOLUME_CORRUPTED;
842 return 1;
843 }
844
845 node = (BTNodeDescriptor*)buffer;
846 first_rec = 0;
847 }
848 done:
849 if (buffer)
850 fsw_free(buffer);
851
852 return status;
853 }
854
855 #if 0
856 void deb(fsw_u16* p, int len, int swap)
857 {
858 int i;
859 for (i=0; i<len; i++)
860 {
861 printf("%c", swap ? be16_to_cpu(p[i]) : p[i]);
862 }
863 printf("\n");
864 }
865 #endif
866
867 static int
868 fsw_hfs_cmp_extkey(BTreeKey* key1, BTreeKey* key2)
869 {
870 HFSPlusExtentKey* ekey1 = (HFSPlusExtentKey*)key1;
871 HFSPlusExtentKey* ekey2 = (HFSPlusExtentKey*)key2;
872 int result;
873
874 /* First key is read from the FS data, second is in-memory in CPU endianess */
875 result = be32_to_cpu(ekey1->fileID) - ekey2->fileID;
876
877 if (result)
878 return result;
879
880 result = ekey1->forkType - ekey2->forkType;
881
882 if (result)
883 return result;
884
885 result = be32_to_cpu(ekey1->startBlock) - ekey2->startBlock;
886 return result;
887 }
888
889 static int
890 fsw_hfs_cmp_catkey (BTreeKey *key1, BTreeKey *key2)
891 {
892 HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
893 HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
894
895 int apos, bpos, lc;
896 fsw_u16 ac, bc;
897 fsw_u32 parentId1;
898 int key1Len;
899 fsw_u16 *p1;
900 fsw_u16 *p2;
901
902 parentId1 = be32_to_cpu(ckey1->parentID);
903
904 if (parentId1 > ckey2->parentID)
905 return 1;
906 if (parentId1 < ckey2->parentID)
907 return -1;
908
909 p1 = &ckey1->nodeName.unicode[0];
910 p2 = &ckey2->nodeName.unicode[0];
911 key1Len = be16_to_cpu (ckey1->nodeName.length);
912 apos = bpos = 0;
913
914 while(1)
915 {
916 /* get next valid character from ckey1 */
917 for (lc = 0; lc == 0 && apos < key1Len; apos++) {
918 ac = be16_to_cpu(p1[apos]);
919 lc = ac;
920 };
921 ac = (fsw_u16)lc;
922
923 /* get next valid character from ckey2 */
924 for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
925 bc = p2[bpos];
926 lc = bc;
927 };
928 bc = (fsw_u16)lc;
929
930 if (ac != bc || (ac == 0 && bc == 0))
931 return ac - bc;
932 }
933 }
934
935 static int
936 fsw_hfs_cmpi_catkey (BTreeKey *key1, BTreeKey *key2)
937 {
938 HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
939 HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
940
941 int apos, bpos, lc;
942 fsw_u16 ac, bc;
943 fsw_u32 parentId1;
944 int key1Len;
945 fsw_u16 *p1;
946 fsw_u16 *p2;
947
948 parentId1 = be32_to_cpu(ckey1->parentID);
949
950 if (parentId1 > ckey2->parentID)
951 return 1;
952 if (parentId1 < ckey2->parentID)
953 return -1;
954
955 key1Len = be16_to_cpu (ckey1->nodeName.length);
956
957 if (key1Len == 0 && ckey2->nodeName.length == 0)
958 return 0;
959
960 p1 = &ckey1->nodeName.unicode[0];
961 p2 = &ckey2->nodeName.unicode[0];
962
963 apos = bpos = 0;
964
965 while(1)
966 {
967 /* get next valid character from ckey1 */
968 for (lc = 0; lc == 0 && apos < key1Len; apos++) {
969 ac = be16_to_cpu(p1[apos]);
970 lc = ac ? fsw_to_lower(ac) : 0;
971 };
972 ac = (fsw_u16)lc;
973
974 /* get next valid character from ckey2 */
975 for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
976 bc = p2[bpos];
977 lc = bc ? fsw_to_lower(bc) : 0;
978 };
979 bc = (fsw_u16)lc;
980
981 if (ac != bc || (ac == 0 && bc == 0))
982 return ac - bc;
983 }
984 }
985
986 /**
987 * Retrieve file data mapping information. This function is called by the core when
988 * fsw_shandle_read needs to know where on the disk the required piece of the file's
989 * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
990 * on the dnode before. Our task here is to get the physical disk block number for
991 * the requested logical block number.
992 */
993
994 static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume * vol,
995 struct fsw_hfs_dnode * dno,
996 struct fsw_extent * extent)
997 {
998 fsw_status_t status;
999 fsw_u32 lbno;
1000 HFSPlusExtentRecord *exts;
1001 BTNodeDescriptor *node = NULL;
1002
1003 extent->type = FSW_EXTENT_TYPE_PHYSBLOCK;
1004 extent->log_count = 1;
1005 lbno = extent->log_start;
1006
1007 /* we only care about data forks atm, do we? */
1008 exts = &dno->extents;
1009
1010 while (1)
1011 {
1012 struct HFSPlusExtentKey* key;
1013 struct HFSPlusExtentKey overflowkey;
1014 fsw_u32 ptr;
1015 fsw_u32 phys_bno;
1016
1017 if (fsw_hfs_find_block(exts, &lbno, &phys_bno))
1018 {
1019 extent->phys_start = phys_bno + vol->emb_block_off;
1020 status = FSW_SUCCESS;
1021 break;
1022 }
1023
1024
1025 /* Find appropriate overflow record */
1026 overflowkey.fileID = dno->g.dnode_id;
1027 overflowkey.startBlock = extent->log_start - lbno;
1028
1029 if (node != NULL)
1030 {
1031 fsw_free(node);
1032 node = NULL;
1033 }
1034
1035 status = fsw_hfs_btree_search (&vol->extents_tree,
1036 (BTreeKey*)&overflowkey,
1037 fsw_hfs_cmp_extkey,
1038 &node, &ptr);
1039 if (status)
1040 break;
1041
1042 key = (struct HFSPlusExtentKey *)
1043 fsw_hfs_btree_rec (&vol->extents_tree, node, ptr);
1044 exts = (HFSPlusExtentRecord*) (key + 1);
1045 }
1046
1047 if (node != NULL)
1048 fsw_free(node);
1049
1050 return status;
1051 }
1052
1053 static const fsw_u16* g_blacklist[] =
1054 {
1055 //L"AppleIntelCPUPowerManagement.kext",
1056 NULL
1057 };
1058
1059
1060 //#define HFS_FILE_INJECTION
1061
1062 #ifdef HFS_FILE_INJECTION
1063 static struct
1064 {
1065 const fsw_u16* path;
1066 const fsw_u16* name;
1067 } g_injectList[] =
1068 {
1069 {
1070 L"/System/Library/Extensions",
1071 L"ApplePS2Controller.kext"
1072 },
1073 {
1074 NULL,
1075 NULL
1076 }
1077 };
1078 #endif
1079
1080 static fsw_status_t
1081 create_hfs_dnode(struct fsw_hfs_dnode * dno,
1082 file_info_t * file_info,
1083 struct fsw_hfs_dnode ** child_dno_out)
1084 {
1085 fsw_status_t status;
1086 struct fsw_hfs_dnode * baby;
1087
1088 status = fsw_dnode_create(dno, file_info->id, file_info->type,
1089 file_info->name, &baby);
1090 if (status)
1091 return status;
1092
1093 baby->g.size = file_info->size;
1094 baby->used_bytes = file_info->used;
1095 baby->ctime = file_info->ctime;
1096 baby->mtime = file_info->mtime;
1097
1098
1099 /* Fill-in extents info */
1100 if (file_info->type == FSW_DNODE_TYPE_FILE)
1101 {
1102 fsw_memcpy(baby->extents, &file_info->extents, sizeof file_info->extents);
1103 }
1104
1105 *child_dno_out = baby;
1106
1107 return FSW_SUCCESS;
1108 }
1109
1110
1111 /**
1112 * Lookup a directory's child dnode by name. This function is called on a directory
1113 * to retrieve the directory entry with the given name. A dnode is constructed for
1114 * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1115 * and the dnode is actually a directory.
1116 */
1117
1118 static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume * vol,
1119 struct fsw_hfs_dnode * dno,
1120 struct fsw_string * lookup_name,
1121 struct fsw_hfs_dnode ** child_dno_out)
1122 {
1123 fsw_status_t status;
1124 struct HFSPlusCatalogKey catkey;
1125 fsw_u32 ptr;
1126 fsw_u16 rec_type;
1127 BTNodeDescriptor * node = NULL;
1128 struct fsw_string rec_name;
1129 int free_data = 0, i;
1130 HFSPlusCatalogKey* file_key;
1131 file_info_t file_info;
1132 fsw_u8* base;
1133
1134
1135 fsw_memzero(&file_info, sizeof file_info);
1136 file_info.name = &rec_name;
1137
1138 catkey.parentID = dno->g.dnode_id;
1139 catkey.nodeName.length = (fsw_u16)lookup_name->len;
1140
1141 /* no need to allocate anything */
1142 if (lookup_name->type == FSW_STRING_TYPE_UTF16)
1143 {
1144 fsw_memcpy(catkey.nodeName.unicode, lookup_name->data, lookup_name->size);
1145 rec_name = *lookup_name;
1146 } else
1147 {
1148 status = fsw_strdup_coerce(&rec_name, FSW_STRING_TYPE_UTF16, lookup_name);
1149 /* nothing allocated so far */
1150 if (status)
1151 goto done;
1152 free_data = 1;
1153 fsw_memcpy(catkey.nodeName.unicode, rec_name.data, rec_name.size);
1154 }
1155
1156 /* Dirty hack: blacklisting of certain files on FS driver level */
1157 for (i = 0; g_blacklist[i]; i++)
1158 {
1159 if (fsw_memeq(g_blacklist[i], catkey.nodeName.unicode, catkey.nodeName.length*2))
1160 {
1161 DPRINT2("Blacklisted %s\n", g_blacklist[i]);
1162 status = FSW_NOT_FOUND;
1163 goto done;
1164 }
1165 }
1166
1167 #ifdef HFS_FILE_INJECTION
1168 if (fsw_hfs_inject(vol,
1169 dno,
1170 catkey.nodeName.unicode,
1171 catkey.nodeName.length,
1172 &file_info))
1173 {
1174 status = FSW_SUCCESS;
1175 goto create;
1176 }
1177 #endif
1178
1179 catkey.keyLength = (fsw_u16)(5 + rec_name.size);
1180
1181 status = fsw_hfs_btree_search (&vol->catalog_tree,
1182 (BTreeKey*)&catkey,
1183 vol->case_sensitive ?
1184 fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
1185 &node, &ptr);
1186 if (status)
1187 goto done;
1188
1189 file_key = (HFSPlusCatalogKey *)fsw_hfs_btree_rec (&vol->catalog_tree, node, ptr);
1190 /* for plain HFS "-(keySize & 1)" would be needed */
1191 base = (fsw_u8*)file_key + be16_to_cpu(file_key->keyLength) + 2;
1192 rec_type = be16_to_cpu(*(fsw_u16*)base);
1193
1194 /** @todo: read additional info */
1195 switch (rec_type)
1196 {
1197 case kHFSPlusFolderRecord:
1198 {
1199 HFSPlusCatalogFolder* info = (HFSPlusCatalogFolder*)base;
1200
1201 file_info.id = be32_to_cpu(info->folderID);
1202 file_info.type = FSW_DNODE_TYPE_DIR;
1203 /* @todo: return number of elements, maybe use smth else */
1204 file_info.size = be32_to_cpu(info->valence);
1205 file_info.used = be32_to_cpu(info->valence);
1206 file_info.ctime = be32_to_cpu(info->createDate);
1207 file_info.mtime = be32_to_cpu(info->contentModDate);
1208 break;
1209 }
1210 case kHFSPlusFileRecord:
1211 {
1212 HFSPlusCatalogFile* info = (HFSPlusCatalogFile*)base;
1213
1214 file_info.id = be32_to_cpu(info->fileID);
1215 file_info.type = FSW_DNODE_TYPE_FILE;
1216 file_info.size = be64_to_cpu(info->dataFork.logicalSize);
1217 file_info.used = LShiftU64(be32_to_cpu(info->dataFork.totalBlocks), vol->block_size_shift);
1218 file_info.ctime = be32_to_cpu(info->createDate);
1219 file_info.mtime = be32_to_cpu(info->contentModDate);
1220 fsw_memcpy(&file_info.extents, &info->dataFork.extents,
1221 sizeof file_info.extents);
1222 break;
1223 }
1224 default:
1225 BP("unknown file type\n");
1226 file_info.type = FSW_DNODE_TYPE_UNKNOWN;
1227
1228 break;
1229 }
1230 #ifdef HFS_FILE_INJECTION
1231 create:
1232 #endif
1233 status = create_hfs_dnode(dno, &file_info, child_dno_out);
1234 if (status)
1235 goto done;
1236
1237 done:
1238
1239 if (node != NULL)
1240 fsw_free(node);
1241
1242 if (free_data)
1243 fsw_strfree(&rec_name);
1244
1245 return status;
1246 }
1247
1248 /**
1249 * Get the next directory entry when reading a directory. This function is called during
1250 * directory iteration to retrieve the next directory entry. A dnode is constructed for
1251 * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1252 * and the dnode is actually a directory. The shandle provided by the caller is used to
1253 * record the position in the directory between calls.
1254 */
1255
1256 static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol,
1257 struct fsw_hfs_dnode *dno,
1258 struct fsw_shandle *shand,
1259 struct fsw_hfs_dnode **child_dno_out)
1260 {
1261 fsw_status_t status;
1262 struct HFSPlusCatalogKey catkey;
1263 fsw_u32 ptr;
1264 BTNodeDescriptor * node = NULL;
1265
1266 visitor_parameter_t param;
1267 struct fsw_string rec_name;
1268
1269 catkey.parentID = dno->g.dnode_id;
1270 catkey.nodeName.length = 0;
1271
1272 fsw_memzero(&param, sizeof(param));
1273
1274 rec_name.type = FSW_STRING_TYPE_EMPTY;
1275 param.file_info.name = &rec_name;
1276
1277 status = fsw_hfs_btree_search (&vol->catalog_tree,
1278 (BTreeKey*)&catkey,
1279 vol->case_sensitive ?
1280 fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
1281 &node, &ptr);
1282 if (status)
1283 goto done;
1284
1285 /* Iterator updates shand state */
1286 param.vol = vol;
1287 param.shandle = shand;
1288 param.parent = dno->g.dnode_id;
1289 param.cur_pos = 0;
1290 status = fsw_hfs_btree_iterate_node (&vol->catalog_tree,
1291 node,
1292 ptr,
1293 fsw_hfs_btree_visit_node,
1294 &param);
1295 if (status)
1296 goto done;
1297
1298 status = create_hfs_dnode(dno, &param.file_info, child_dno_out);
1299
1300 if (status)
1301 goto done;
1302
1303 done:
1304 fsw_strfree(&rec_name);
1305
1306 return status;
1307 }
1308
1309 /**
1310 * Get the target path of a symbolic link. This function is called when a symbolic
1311 * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
1312 * called on the dnode and that it really is a symlink.
1313 *
1314 */
1315 static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
1316 struct fsw_string *link_target)
1317 {
1318 return FSW_UNSUPPORTED;
1319 }
1320
1321 // EOF