1 /* $Id: fsw_hfs.c 33540 2010-10-28 09:27:05Z vboxsync $ */
3 * fsw_hfs.c - HFS file system driver code, see
5 * http://developer.apple.com/technotes/tn/tn1150.html
8 * - Doesn't support permissions
9 * - Complete Unicode case-insensitiveness disabled (large tables)
11 * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS)
15 * Copyright (C) 2010 Oracle Corporation
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.
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)
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)
41 void dump_str(fsw_u16
* p
, fsw_u32 len
, int swap
)
47 fprintf(stderr
, "%c", swap
? be16_to_cpu(p
[i
]) : p
[i
]);
49 fprintf(stderr
, "\n");
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
);
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
);
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
);
69 static fsw_status_t
fsw_hfs_read_dirrec(struct fsw_shandle
*shand
, struct hfs_dirrec_buffer
*dirrec_buffer
);
72 static fsw_status_t
fsw_hfs_readlink(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
73 struct fsw_string
*link
);
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
),
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;
97 fsw_hfs_read_block (struct fsw_hfs_dnode
* dno
,
104 struct fsw_extent extent
;
108 extent
.log_start
= log_bno
;
109 status
= fsw_hfs_get_extent(dno
->g
.vol
, dno
, &extent
);
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
);
119 fsw_memcpy(buf
, buffer
+ off
, len
);
121 fsw_block_release(dno
->g
.vol
, phys_bno
, buffer
);
127 /* Read data from HFS file. */
129 fsw_hfs_read_file (struct fsw_hfs_dnode
* dno
,
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;
144 fsw_u32 off
= (fsw_u32
)(pos
& block_size_mask
);
145 fsw_s32 next_len
= len
;
147 log_bno
= (fsw_u32
)RShiftU64(pos
, block_size_bits
);
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
);
166 fsw_hfs_compute_shift(fsw_u32 size
)
172 if ((size
>> i
) == 0)
181 * Mount an HFS+ volume. Reads the superblock and constructs the
182 * root directory dnode.
184 //algo from Chameleon
187 HFSGetDescription(CICell ih, char *str, long strMaxLen)
191 UInt32 firstLeafNode;
196 if (HFSInitPartition(ih) == -1) { return; }
198 // Fill some crucial data structures by side effect.
200 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
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);
206 dirIndex = (long long) firstLeafNode * nodeSize;
208 GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
210 strncpy(str, name, strMaxLen);
211 str[strMaxLen] = '\0';
216 static fsw_status_t
fsw_hfs_volume_mount(struct fsw_hfs_volume
*vol
)
218 fsw_status_t status
, rv
;
220 HFSPlusVolumeHeader
*voldesc
;
223 HFSMasterDirectoryBlock
* mdb
;
224 fsw_u32 firstLeafNum
;
226 fsw_u8 cbuff
[sizeof (BTNodeDescriptor
) + sizeof (HFSPlusCatalogKey
)];
228 rv
= FSW_UNSUPPORTED
;
230 vol
->primary_voldesc
= NULL
;
231 fsw_set_blocksize(vol
, HFS_BLOCKSIZE
, HFS_BLOCKSIZE
);
232 blockno
= HFS_SUPERBLOCK_BLOCKNO
;
240 vol
->emb_block_off
= 0;
244 BTHeaderRec tree_header
;
248 status
= fsw_block_get(vol
, blockno
, 0, &buffer
);
250 voldesc
= (HFSPlusVolumeHeader
*)buffer
;
251 mdb
= (HFSMasterDirectoryBlock
*)buffer
;
252 signature
= be16_to_cpu(voldesc
->signature
);
254 if ((signature
== kHFSPlusSigWord
) || (signature
== kHFSXSigWord
)) //H+ or HX
256 if (vol
->hfs_kind
== 0)
258 // DPRINT("found HFS+\n");
259 vol
->hfs_kind
= FSW_HFS_PLUS
;
262 else if (signature
== kHFSSigWord
) // 'BD'
264 if (be16_to_cpu(mdb
->drEmbedSigWord
) == kHFSPlusSigWord
)
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
;
275 DPRINT("found plain HFS, unsupported\n");
276 vol
->hfs_kind
= FSW_HFS_PLAIN
;
278 rv
= FSW_UNSUPPORTED
;
283 rv
= FSW_UNSUPPORTED
;
287 status
= fsw_memdup((void **)&vol
->primary_voldesc
, voldesc
,
292 block_size
= be32_to_cpu(voldesc
->blockSize
);
293 vol
->block_size_shift
= fsw_hfs_compute_shift(block_size
);
295 fsw_block_release(vol
, blockno
, buffer
);
298 fsw_set_blocksize(vol
, block_size
, block_size
);
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
);
307 /* Setup catalog dnode */
308 status
= fsw_dnode_create_root(vol
, kHFSCatalogFileID
, &vol
->catalog_tree
.file
);
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
);
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
);
324 /* Setup the root dnode */
325 status
= fsw_dnode_create_root(vol
, kHFSRootFolderID
, &vol
->g
.root
);
329 * Read catalog file, we know that first record is in the first node, right after
330 * the node descriptor.
332 r
= fsw_hfs_read_file(vol
->catalog_tree
.file
,
333 sizeof (BTNodeDescriptor
),
334 sizeof (BTHeaderRec
), (fsw_u8
*) &tree_header
);
337 status
= FSW_VOLUME_CORRUPTED
;
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
);
347 /* Take Volume Name before tree_header overwritten */
348 firstLeafNum
= be32_to_cpu(tree_header
.firstLeafNode
);
349 catfOffset
= firstLeafNum
* vol
->catalog_tree
.node_size
;
351 r
= fsw_hfs_read_file(vol
->catalog_tree
.file
, catfOffset
, sizeof (cbuff
), cbuff
);
353 if (r
== sizeof (cbuff
))
355 BTNodeDescriptor
* btnd
;
356 HFSPlusCatalogKey
* ck
;
358 btnd
= (BTNodeDescriptor
*) cbuff
;
359 ck
= (HFSPlusCatalogKey
*) (cbuff
+ sizeof(BTNodeDescriptor
));
360 if (btnd
->kind
== kBTLeafNode
&& be32_to_cpu (ck
->parentID
) == kHFSRootParentID
)
362 struct fsw_string vn
;
364 vn
.type
= FSW_STRING_TYPE_UTF16_BE
;
365 vn
.len
= be16_to_cpu (ck
->nodeName
.length
);
366 vn
.size
= vn
.len
* sizeof (fsw_u16
);
367 vn
.data
= ck
->nodeName
.unicode
;
368 fsw_strfree (&vol
->g
.label
);
369 status
= fsw_strdup_coerce(&vol
->g
.label
, vol
->g
.host_string_type
, &vn
);
374 /* Read extents overflow file */
375 r
= fsw_hfs_read_file(vol
->extents_tree
.file
,
376 sizeof (BTNodeDescriptor
),
377 sizeof (BTHeaderRec
), (fsw_u8
*) &tree_header
);
380 status
= FSW_VOLUME_CORRUPTED
;
384 vol
->extents_tree
.root_node
= be32_to_cpu (tree_header
.rootNode
);
385 vol
->extents_tree
.node_size
= be16_to_cpu (tree_header
.nodeSize
);
394 fsw_block_release(vol
, blockno
, buffer
);
398 //Here is a method to obtain Volume label from Apple
399 //how to implement it?
402 UInt32 firstLeafNode;
406 char *nodeBuf, *testKey, *entry;
409 if (HFSInitPartition(ih) == -1) { return; }
411 // Fill some crucial data structures by side effect.
413 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
415 // Now we can loook up the volume name node.
416 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
417 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
419 dirIndex = (long long) firstLeafNode * nodeSize;
420 index = (long) (*dirIndex % nodeSize); == 0
421 curNode = (long) (*dirIndex / nodeSize); == firstLeafNode
423 //GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
424 // Read the BTree node and get the record for index.
425 ReadExtent(extent, extentSize, kHFSCatalogFileID,
426 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
427 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
429 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
430 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
431 (u_int8_t *)gTempStr, 256, OSBigEndian);
435 strncpy(str, name, strMaxLen);
436 str[strMaxLen] = '\0';
440 * Free the volume data structure. Called by the core after an unmount or after
441 * an unsuccessful mount to release the memory used by the file system type specific
442 * part of the volume structure.
445 static void fsw_hfs_volume_free(struct fsw_hfs_volume
*vol
)
447 if (vol
->primary_voldesc
)
449 fsw_free(vol
->primary_voldesc
);
450 vol
->primary_voldesc
= NULL
;
455 * Get in-depth information on a volume.
458 static fsw_status_t
fsw_hfs_volume_stat(struct fsw_hfs_volume
*vol
, struct fsw_volume_stat
*sb
)
460 sb
->total_bytes
= be32_to_cpu(vol
->primary_voldesc
->totalBlocks
) << vol
->block_size_shift
;
461 sb
->free_bytes
= be32_to_cpu(vol
->primary_voldesc
->freeBlocks
) << vol
->block_size_shift
;
466 * Get full information on a dnode from disk. This function is called by the core
467 * whenever it needs to access fields in the dnode structure that may not
468 * be filled immediately upon creation of the dnode.
471 static fsw_status_t
fsw_hfs_dnode_fill(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
477 * Free the dnode data structure. Called by the core when deallocating a dnode
478 * structure to release the memory used by the file system type specific part
479 * of the dnode structure.
482 static void fsw_hfs_dnode_free(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
486 static fsw_u32
mac_to_posix(fsw_u32 mac_time
)
488 /* Mac time is 1904 year based */
489 return mac_time
? mac_time
- 2082844800 : 0;
493 * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
494 * has been called on the dnode before this function is called. Note that some
495 * data is not directly stored into the structure, but passed to a host-specific
496 * callback that converts it to the host-specific format.
499 static fsw_status_t
fsw_hfs_dnode_stat(struct fsw_hfs_volume
*vol
,
500 struct fsw_hfs_dnode
*dno
,
501 struct fsw_dnode_stat
*sb
)
503 sb
->used_bytes
= dno
->used_bytes
;
504 sb
->store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, mac_to_posix(dno
->ctime
));
505 sb
->store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, mac_to_posix(dno
->mtime
));
506 sb
->store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, 0);
507 sb
->store_attr_posix(sb
, 0700);
513 fsw_hfs_find_block(HFSPlusExtentRecord
* exts
,
518 fsw_u32 cur_lbno
= *lbno
;
520 for (i
= 0; i
< 8; i
++)
522 fsw_u32 start
= be32_to_cpu ((*exts
)[i
].startBlock
);
523 fsw_u32 count
= be32_to_cpu ((*exts
)[i
].blockCount
);
525 if (cur_lbno
< count
)
527 *pbno
= start
+ cur_lbno
;
539 /* Find record offset, numbering starts from the end */
541 fsw_hfs_btree_recoffset (struct fsw_hfs_btree
* btree
,
542 BTNodeDescriptor
* node
,
545 fsw_u8
*cnode
= (fsw_u8
*) node
;
547 recptr
= (fsw_u16
*) (cnode
+btree
->node_size
- index
* 2 - 2);
548 return be16_to_cpu(*recptr
);
551 /* Pointer to the key inside node */
553 fsw_hfs_btree_rec (struct fsw_hfs_btree
* btree
,
554 BTNodeDescriptor
* node
,
557 fsw_u8
*cnode
= (fsw_u8
*) node
;
559 offset
= fsw_hfs_btree_recoffset (btree
, node
, index
);
560 return (BTreeKey
*) (cnode
+ offset
);
565 fsw_hfs_btree_search (struct fsw_hfs_btree
* btree
,
567 int (*compare_keys
) (BTreeKey
* key1
, BTreeKey
* key2
),
568 BTNodeDescriptor
** result
,
569 fsw_u32
* key_offset
)
571 BTNodeDescriptor
* node
;
575 fsw_u8
* buffer
= NULL
;
577 currnode
= btree
->root_node
;
578 status
= fsw_alloc(btree
->node_size
, &buffer
);
581 node
= (BTNodeDescriptor
*)buffer
;
592 if (fsw_hfs_read_file (btree
->file
,
593 (fsw_u64
)currnode
* btree
->node_size
,
594 btree
->node_size
, buffer
) <= 0)
596 status
= FSW_VOLUME_CORRUPTED
;
600 if (be16_to_cpu(*(fsw_u16
*)(buffer
+ btree
->node_size
- 2)) != sizeof(BTNodeDescriptor
))
601 BP("corrupted node\n");
603 count
= be16_to_cpu (node
->numRecords
);
606 for (rec
= 0; rec
< count
; rec
++)
610 currkey
= fsw_hfs_btree_rec (btree
, node
, rec
);
611 cmp
= compare_keys (currkey
, key
);
612 //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
615 if (node
->kind
== kBTLeafNode
)
623 status
= FSW_SUCCESS
;
627 else if (node
->kind
== kBTIndexNode
)
634 pointer
= (fsw_u32
*) ((char *) currkey
635 + be16_to_cpu (currkey
->length16
)
637 currnode
= be32_to_cpu (*pointer
);
642 if (node
->kind
== kBTLeafNode
&& cmp
< 0 && node
->fLink
)
644 currnode
= be32_to_cpu(node
->fLink
);
649 status
= FSW_NOT_FOUND
;
653 /* Perform binary search */
655 fsw_u32 upper
= count
- 1;
657 BTreeKey
*currkey
= NULL
;
661 status
= FSW_NOT_FOUND
;
665 while (lower
<= upper
)
667 fsw_u32 index
= (lower
+ upper
) / 2;
669 currkey
= fsw_hfs_btree_rec (btree
, node
, index
);
671 cmp
= compare_keys (currkey
, key
);
672 if (cmp
< 0) upper
= index
- 1;
673 if (cmp
> 0) lower
= index
+ 1;
680 status
= FSW_SUCCESS
;
686 currkey
= fsw_hfs_btree_rec (btree
, node
, upper
);
688 if (node
->kind
== kBTIndexNode
&& currkey
)
692 pointer
= (fsw_u32
*) ((char *) currkey
693 + be16_to_cpu (currkey
->length16
)
695 currnode
= be32_to_cpu (*pointer
);
699 status
= FSW_NOT_FOUND
;
707 if (buffer
!= NULL
&& status
!= FSW_SUCCESS
)
716 struct fsw_string
* name
;
721 HFSPlusExtentRecord extents
;
726 fsw_u32 cur_pos
; /* current position */
728 struct fsw_hfs_volume
* vol
;
730 struct fsw_shandle
* shandle
; /* this one track iterator's state */
731 file_info_t file_info
;
732 } visitor_parameter_t
;
735 fsw_hfs_btree_visit_node(BTreeKey
*record
, void* param
)
737 visitor_parameter_t
* vp
= (visitor_parameter_t
*)param
;
738 fsw_u8
* base
= (fsw_u8
*)record
->rawData
+ be16_to_cpu(record
->length16
) + 2;
739 fsw_u16 rec_type
= be16_to_cpu(*(fsw_u16
*)base
);
740 struct HFSPlusCatalogKey
* cat_key
= (HFSPlusCatalogKey
*)record
;
744 struct fsw_string
* file_name
;
746 if (be32_to_cpu(cat_key
->parentID
) != vp
->parent
)
749 /* not smth we care about */
750 if (vp
->shandle
->pos
!= vp
->cur_pos
++)
755 case kHFSPlusFolderRecord
:
757 HFSPlusCatalogFolder
* folder_info
= (HFSPlusCatalogFolder
*)base
;
759 vp
->file_info
.id
= be32_to_cpu(folder_info
->folderID
);
760 vp
->file_info
.type
= FSW_DNODE_TYPE_DIR
;
761 vp
->file_info
.size
= be32_to_cpu(folder_info
->valence
);
762 vp
->file_info
.used
= be32_to_cpu(folder_info
->valence
);
763 vp
->file_info
.ctime
= be32_to_cpu(folder_info
->createDate
);
764 vp
->file_info
.mtime
= be32_to_cpu(folder_info
->contentModDate
);
767 case kHFSPlusFileRecord
:
769 HFSPlusCatalogFile
* file_info
= (HFSPlusCatalogFile
*)base
;
771 vp
->file_info
.id
= be32_to_cpu(file_info
->fileID
);
772 vp
->file_info
.type
= FSW_DNODE_TYPE_FILE
;
773 vp
->file_info
.size
= be64_to_cpu(file_info
->dataFork
.logicalSize
);
774 vp
->file_info
.used
= LShiftU64(be32_to_cpu(file_info
->dataFork
.totalBlocks
),
775 vp
->vol
->block_size_shift
);
776 vp
->file_info
.ctime
= be32_to_cpu(file_info
->createDate
);
777 vp
->file_info
.mtime
= be32_to_cpu(file_info
->contentModDate
);
778 fsw_memcpy(&vp
->file_info
.extents
, &file_info
->dataFork
.extents
,
779 sizeof vp
->file_info
.extents
);
782 case kHFSPlusFolderThreadRecord
:
783 case kHFSPlusFileThreadRecord
:
789 BP("unknown file type\n");
790 vp
->file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
794 name_len
= be16_to_cpu(cat_key
->nodeName
.length
);
796 file_name
= vp
->file_info
.name
;
797 file_name
->len
= name_len
;
798 fsw_memdup(&file_name
->data
, &cat_key
->nodeName
.unicode
[0], 2*name_len
);
799 file_name
->size
= 2*name_len
;
800 file_name
->type
= FSW_STRING_TYPE_UTF16
;
801 name_ptr
= (fsw_u16
*)file_name
->data
;
802 for (i
=0; i
<name_len
; i
++)
804 name_ptr
[i
] = be16_to_cpu(name_ptr
[i
]);
812 fsw_hfs_btree_iterate_node (struct fsw_hfs_btree
* btree
,
813 BTNodeDescriptor
* first_node
,
815 int (*callback
) (BTreeKey
*record
, void* param
),
819 /* We modify node, so make a copy */
820 BTNodeDescriptor
* node
= first_node
;
821 fsw_u8
* buffer
= NULL
;
823 status
= fsw_alloc(btree
->node_size
, &buffer
);
830 fsw_u32 count
= be16_to_cpu(node
->numRecords
);
833 /* Iterate over all records in this node. */
834 for (i
= first_rec
; i
< count
; i
++)
836 int rv
= callback(fsw_hfs_btree_rec (btree
, node
, i
), param
);
841 status
= FSW_SUCCESS
;
844 status
= FSW_NOT_FOUND
;
847 /* if callback returned 0 - continue */
850 next_node
= be32_to_cpu(node
->fLink
);
854 status
= FSW_NOT_FOUND
;
858 if (fsw_hfs_read_file (btree
->file
,
859 next_node
* btree
->node_size
,
860 btree
->node_size
, buffer
) <= 0)
862 status
= FSW_VOLUME_CORRUPTED
;
866 node
= (BTNodeDescriptor
*)buffer
;
877 void deb(fsw_u16
* p
, int len
, int swap
)
880 for (i
=0; i
<len
; i
++)
882 printf("%c", swap
? be16_to_cpu(p
[i
]) : p
[i
]);
889 fsw_hfs_cmp_extkey(BTreeKey
* key1
, BTreeKey
* key2
)
891 HFSPlusExtentKey
* ekey1
= (HFSPlusExtentKey
*)key1
;
892 HFSPlusExtentKey
* ekey2
= (HFSPlusExtentKey
*)key2
;
895 /* First key is read from the FS data, second is in-memory in CPU endianess */
896 result
= be32_to_cpu(ekey1
->fileID
) - ekey2
->fileID
;
901 result
= ekey1
->forkType
- ekey2
->forkType
;
906 result
= be32_to_cpu(ekey1
->startBlock
) - ekey2
->startBlock
;
911 fsw_hfs_cmp_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
913 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
914 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
923 parentId1
= be32_to_cpu(ckey1
->parentID
);
925 if (parentId1
> ckey2
->parentID
)
927 if (parentId1
< ckey2
->parentID
)
930 p1
= &ckey1
->nodeName
.unicode
[0];
931 p2
= &ckey2
->nodeName
.unicode
[0];
932 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
937 /* get next valid character from ckey1 */
938 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
939 ac
= be16_to_cpu(p1
[apos
]);
944 /* get next valid character from ckey2 */
945 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
951 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
957 fsw_hfs_cmpi_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
959 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
960 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
969 parentId1
= be32_to_cpu(ckey1
->parentID
);
971 if (parentId1
> ckey2
->parentID
)
973 if (parentId1
< ckey2
->parentID
)
976 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
978 if (key1Len
== 0 && ckey2
->nodeName
.length
== 0)
981 p1
= &ckey1
->nodeName
.unicode
[0];
982 p2
= &ckey2
->nodeName
.unicode
[0];
988 /* get next valid character from ckey1 */
989 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
990 ac
= be16_to_cpu(p1
[apos
]);
991 lc
= ac
? fsw_to_lower(ac
) : 0;
995 /* get next valid character from ckey2 */
996 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
998 lc
= bc
? fsw_to_lower(bc
) : 0;
1002 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
1008 * Retrieve file data mapping information. This function is called by the core when
1009 * fsw_shandle_read needs to know where on the disk the required piece of the file's
1010 * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
1011 * on the dnode before. Our task here is to get the physical disk block number for
1012 * the requested logical block number.
1015 static fsw_status_t
fsw_hfs_get_extent(struct fsw_hfs_volume
* vol
,
1016 struct fsw_hfs_dnode
* dno
,
1017 struct fsw_extent
* extent
)
1019 fsw_status_t status
;
1021 HFSPlusExtentRecord
*exts
;
1022 BTNodeDescriptor
*node
= NULL
;
1024 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1025 extent
->log_count
= 1;
1026 lbno
= extent
->log_start
;
1028 /* we only care about data forks atm, do we? */
1029 exts
= &dno
->extents
;
1033 struct HFSPlusExtentKey
* key
;
1034 struct HFSPlusExtentKey overflowkey
;
1038 if (fsw_hfs_find_block(exts
, &lbno
, &phys_bno
))
1040 extent
->phys_start
= phys_bno
+ vol
->emb_block_off
;
1041 status
= FSW_SUCCESS
;
1046 /* Find appropriate overflow record */
1047 overflowkey
.fileID
= dno
->g
.dnode_id
;
1048 overflowkey
.startBlock
= extent
->log_start
- lbno
;
1056 status
= fsw_hfs_btree_search (&vol
->extents_tree
,
1057 (BTreeKey
*)&overflowkey
,
1063 key
= (struct HFSPlusExtentKey
*)
1064 fsw_hfs_btree_rec (&vol
->extents_tree
, node
, ptr
);
1065 exts
= (HFSPlusExtentRecord
*) (key
+ 1);
1074 static const fsw_u16
* g_blacklist
[] =
1076 //L"AppleIntelCPUPowerManagement.kext",
1081 //#define HFS_FILE_INJECTION
1083 #ifdef HFS_FILE_INJECTION
1086 const fsw_u16
* path
;
1087 const fsw_u16
* name
;
1091 L
"/System/Library/Extensions",
1092 L
"ApplePS2Controller.kext"
1102 create_hfs_dnode(struct fsw_hfs_dnode
* dno
,
1103 file_info_t
* file_info
,
1104 struct fsw_hfs_dnode
** child_dno_out
)
1106 fsw_status_t status
;
1107 struct fsw_hfs_dnode
* baby
;
1109 status
= fsw_dnode_create(dno
, file_info
->id
, file_info
->type
,
1110 file_info
->name
, &baby
);
1114 baby
->g
.size
= file_info
->size
;
1115 baby
->used_bytes
= file_info
->used
;
1116 baby
->ctime
= file_info
->ctime
;
1117 baby
->mtime
= file_info
->mtime
;
1120 /* Fill-in extents info */
1121 if (file_info
->type
== FSW_DNODE_TYPE_FILE
)
1123 fsw_memcpy(baby
->extents
, &file_info
->extents
, sizeof file_info
->extents
);
1126 *child_dno_out
= baby
;
1133 * Lookup a directory's child dnode by name. This function is called on a directory
1134 * to retrieve the directory entry with the given name. A dnode is constructed for
1135 * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1136 * and the dnode is actually a directory.
1139 static fsw_status_t
fsw_hfs_dir_lookup(struct fsw_hfs_volume
* vol
,
1140 struct fsw_hfs_dnode
* dno
,
1141 struct fsw_string
* lookup_name
,
1142 struct fsw_hfs_dnode
** child_dno_out
)
1144 fsw_status_t status
;
1145 struct HFSPlusCatalogKey catkey
;
1148 BTNodeDescriptor
* node
= NULL
;
1149 struct fsw_string rec_name
;
1150 int free_data
= 0, i
;
1151 HFSPlusCatalogKey
* file_key
;
1152 file_info_t file_info
;
1156 fsw_memzero(&file_info
, sizeof file_info
);
1157 file_info
.name
= &rec_name
;
1159 catkey
.parentID
= dno
->g
.dnode_id
;
1160 catkey
.nodeName
.length
= (fsw_u16
)lookup_name
->len
;
1162 /* no need to allocate anything */
1163 if (lookup_name
->type
== FSW_STRING_TYPE_UTF16
)
1165 fsw_memcpy(catkey
.nodeName
.unicode
, lookup_name
->data
, lookup_name
->size
);
1166 rec_name
= *lookup_name
;
1169 status
= fsw_strdup_coerce(&rec_name
, FSW_STRING_TYPE_UTF16
, lookup_name
);
1170 /* nothing allocated so far */
1174 fsw_memcpy(catkey
.nodeName
.unicode
, rec_name
.data
, rec_name
.size
);
1177 /* Dirty hack: blacklisting of certain files on FS driver level */
1178 for (i
= 0; g_blacklist
[i
]; i
++)
1180 if (fsw_memeq(g_blacklist
[i
], catkey
.nodeName
.unicode
, catkey
.nodeName
.length
*2))
1182 DPRINT2("Blacklisted %s\n", g_blacklist
[i
]);
1183 status
= FSW_NOT_FOUND
;
1188 #ifdef HFS_FILE_INJECTION
1189 if (fsw_hfs_inject(vol
,
1191 catkey
.nodeName
.unicode
,
1192 catkey
.nodeName
.length
,
1195 status
= FSW_SUCCESS
;
1200 catkey
.keyLength
= (fsw_u16
)(5 + rec_name
.size
);
1202 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1204 vol
->case_sensitive
?
1205 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1210 file_key
= (HFSPlusCatalogKey
*)fsw_hfs_btree_rec (&vol
->catalog_tree
, node
, ptr
);
1211 /* for plain HFS "-(keySize & 1)" would be needed */
1212 base
= (fsw_u8
*)file_key
+ be16_to_cpu(file_key
->keyLength
) + 2;
1213 rec_type
= be16_to_cpu(*(fsw_u16
*)base
);
1215 /** @todo: read additional info */
1218 case kHFSPlusFolderRecord
:
1220 HFSPlusCatalogFolder
* info
= (HFSPlusCatalogFolder
*)base
;
1222 file_info
.id
= be32_to_cpu(info
->folderID
);
1223 file_info
.type
= FSW_DNODE_TYPE_DIR
;
1224 /* @todo: return number of elements, maybe use smth else */
1225 file_info
.size
= be32_to_cpu(info
->valence
);
1226 file_info
.used
= be32_to_cpu(info
->valence
);
1227 file_info
.ctime
= be32_to_cpu(info
->createDate
);
1228 file_info
.mtime
= be32_to_cpu(info
->contentModDate
);
1231 case kHFSPlusFileRecord
:
1233 HFSPlusCatalogFile
* info
= (HFSPlusCatalogFile
*)base
;
1235 file_info
.id
= be32_to_cpu(info
->fileID
);
1236 file_info
.type
= FSW_DNODE_TYPE_FILE
;
1237 file_info
.size
= be64_to_cpu(info
->dataFork
.logicalSize
);
1238 file_info
.used
= LShiftU64(be32_to_cpu(info
->dataFork
.totalBlocks
), vol
->block_size_shift
);
1239 file_info
.ctime
= be32_to_cpu(info
->createDate
);
1240 file_info
.mtime
= be32_to_cpu(info
->contentModDate
);
1241 fsw_memcpy(&file_info
.extents
, &info
->dataFork
.extents
,
1242 sizeof file_info
.extents
);
1246 BP("unknown file type\n");
1247 file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
1251 #ifdef HFS_FILE_INJECTION
1254 status
= create_hfs_dnode(dno
, &file_info
, child_dno_out
);
1264 fsw_strfree(&rec_name
);
1270 * Get the next directory entry when reading a directory. This function is called during
1271 * directory iteration to retrieve the next directory entry. A dnode is constructed for
1272 * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1273 * and the dnode is actually a directory. The shandle provided by the caller is used to
1274 * record the position in the directory between calls.
1277 static fsw_status_t
fsw_hfs_dir_read(struct fsw_hfs_volume
*vol
,
1278 struct fsw_hfs_dnode
*dno
,
1279 struct fsw_shandle
*shand
,
1280 struct fsw_hfs_dnode
**child_dno_out
)
1282 fsw_status_t status
;
1283 struct HFSPlusCatalogKey catkey
;
1285 BTNodeDescriptor
* node
= NULL
;
1287 visitor_parameter_t param
;
1288 struct fsw_string rec_name
;
1290 catkey
.parentID
= dno
->g
.dnode_id
;
1291 catkey
.nodeName
.length
= 0;
1293 fsw_memzero(¶m
, sizeof(param
));
1295 rec_name
.type
= FSW_STRING_TYPE_EMPTY
;
1296 param
.file_info
.name
= &rec_name
;
1298 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1300 vol
->case_sensitive
?
1301 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1306 /* Iterator updates shand state */
1308 param
.shandle
= shand
;
1309 param
.parent
= dno
->g
.dnode_id
;
1311 status
= fsw_hfs_btree_iterate_node (&vol
->catalog_tree
,
1314 fsw_hfs_btree_visit_node
,
1319 status
= create_hfs_dnode(dno
, ¶m
.file_info
, child_dno_out
);
1325 fsw_strfree(&rec_name
);
1331 * Get the target path of a symbolic link. This function is called when a symbolic
1332 * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
1333 * called on the dnode and that it really is a symlink.
1336 static fsw_status_t
fsw_hfs_readlink(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
1337 struct fsw_string
*link_target
)
1339 return FSW_UNSUPPORTED
;