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
;
226 rv
= FSW_UNSUPPORTED
;
228 vol
->primary_voldesc
= NULL
;
229 fsw_set_blocksize(vol
, HFS_BLOCKSIZE
, HFS_BLOCKSIZE
);
230 blockno
= HFS_SUPERBLOCK_BLOCKNO
;
238 vol
->emb_block_off
= 0;
242 BTHeaderRec tree_header
;
246 status
= fsw_block_get(vol
, blockno
, 0, &buffer
);
248 voldesc
= (HFSPlusVolumeHeader
*)buffer
;
249 mdb
= (HFSMasterDirectoryBlock
*)buffer
;
250 signature
= be16_to_cpu(voldesc
->signature
);
252 if ((signature
== kHFSPlusSigWord
) || (signature
== kHFSXSigWord
)) //H+ or HX
254 if (vol
->hfs_kind
== 0)
256 // DPRINT("found HFS+\n");
257 vol
->hfs_kind
= FSW_HFS_PLUS
;
260 else if (signature
== kHFSSigWord
) // 'BD'
262 // HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer;
263 //VolumeName = mdb->drVN 28bytes
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
);
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);
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
);
359 status
= FSW_VOLUME_CORRUPTED
;
363 vol
->extents_tree
.root_node
= be32_to_cpu (tree_header
.rootNode
);
364 vol
->extents_tree
.node_size
= be16_to_cpu (tree_header
.nodeSize
);
373 fsw_block_release(vol
, blockno
, buffer
);
377 //Here is a method to obtain Volume label from Apple
378 //how to implement it?
381 UInt32 firstLeafNode;
385 char *nodeBuf, *testKey, *entry;
388 if (HFSInitPartition(ih) == -1) { return; }
390 // Fill some crucial data structures by side effect.
392 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
394 // Now we can loook up the volume name node.
395 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
396 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
398 dirIndex = (long long) firstLeafNode * nodeSize;
399 index = (long) (*dirIndex % nodeSize); == 0
400 curNode = (long) (*dirIndex / nodeSize); == firstLeafNode
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);
408 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
409 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
410 (u_int8_t *)gTempStr, 256, OSBigEndian);
414 strncpy(str, name, strMaxLen);
415 str[strMaxLen] = '\0';
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.
424 static void fsw_hfs_volume_free(struct fsw_hfs_volume
*vol
)
426 if (vol
->primary_voldesc
)
428 fsw_free(vol
->primary_voldesc
);
429 vol
->primary_voldesc
= NULL
;
434 * Get in-depth information on a volume.
437 static fsw_status_t
fsw_hfs_volume_stat(struct fsw_hfs_volume
*vol
, struct fsw_volume_stat
*sb
)
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
;
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.
450 static fsw_status_t
fsw_hfs_dnode_fill(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
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.
461 static void fsw_hfs_dnode_free(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
465 static fsw_u32
mac_to_posix(fsw_u32 mac_time
)
467 /* Mac time is 1904 year based */
468 return mac_time
? mac_time
- 2082844800 : 0;
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.
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
)
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);
492 fsw_hfs_find_block(HFSPlusExtentRecord
* exts
,
497 fsw_u32 cur_lbno
= *lbno
;
499 for (i
= 0; i
< 8; i
++)
501 fsw_u32 start
= be32_to_cpu ((*exts
)[i
].startBlock
);
502 fsw_u32 count
= be32_to_cpu ((*exts
)[i
].blockCount
);
504 if (cur_lbno
< count
)
506 *pbno
= start
+ cur_lbno
;
518 /* Find record offset, numbering starts from the end */
520 fsw_hfs_btree_recoffset (struct fsw_hfs_btree
* btree
,
521 BTNodeDescriptor
* node
,
524 fsw_u8
*cnode
= (fsw_u8
*) node
;
526 recptr
= (fsw_u16
*) (cnode
+btree
->node_size
- index
* 2 - 2);
527 return be16_to_cpu(*recptr
);
530 /* Pointer to the key inside node */
532 fsw_hfs_btree_rec (struct fsw_hfs_btree
* btree
,
533 BTNodeDescriptor
* node
,
536 fsw_u8
*cnode
= (fsw_u8
*) node
;
538 offset
= fsw_hfs_btree_recoffset (btree
, node
, index
);
539 return (BTreeKey
*) (cnode
+ offset
);
544 fsw_hfs_btree_search (struct fsw_hfs_btree
* btree
,
546 int (*compare_keys
) (BTreeKey
* key1
, BTreeKey
* key2
),
547 BTNodeDescriptor
** result
,
548 fsw_u32
* key_offset
)
550 BTNodeDescriptor
* node
;
554 fsw_u8
* buffer
= NULL
;
556 currnode
= btree
->root_node
;
557 status
= fsw_alloc(btree
->node_size
, &buffer
);
560 node
= (BTNodeDescriptor
*)buffer
;
571 if (fsw_hfs_read_file (btree
->file
,
572 (fsw_u64
)currnode
* btree
->node_size
,
573 btree
->node_size
, buffer
) <= 0)
575 status
= FSW_VOLUME_CORRUPTED
;
579 if (be16_to_cpu(*(fsw_u16
*)(buffer
+ btree
->node_size
- 2)) != sizeof(BTNodeDescriptor
))
580 BP("corrupted node\n");
582 count
= be16_to_cpu (node
->numRecords
);
585 for (rec
= 0; rec
< count
; rec
++)
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);
594 if (node
->kind
== kBTLeafNode
)
602 status
= FSW_SUCCESS
;
606 else if (node
->kind
== kBTIndexNode
)
613 pointer
= (fsw_u32
*) ((char *) currkey
614 + be16_to_cpu (currkey
->length16
)
616 currnode
= be32_to_cpu (*pointer
);
621 if (node
->kind
== kBTLeafNode
&& cmp
< 0 && node
->fLink
)
623 currnode
= be32_to_cpu(node
->fLink
);
628 status
= FSW_NOT_FOUND
;
632 /* Perform binary search */
634 fsw_u32 upper
= count
- 1;
636 BTreeKey
*currkey
= NULL
;
640 status
= FSW_NOT_FOUND
;
644 while (lower
<= upper
)
646 fsw_u32 index
= (lower
+ upper
) / 2;
648 currkey
= fsw_hfs_btree_rec (btree
, node
, index
);
650 cmp
= compare_keys (currkey
, key
);
651 if (cmp
< 0) upper
= index
- 1;
652 if (cmp
> 0) lower
= index
+ 1;
659 status
= FSW_SUCCESS
;
665 currkey
= fsw_hfs_btree_rec (btree
, node
, upper
);
667 if (node
->kind
== kBTIndexNode
&& currkey
)
671 pointer
= (fsw_u32
*) ((char *) currkey
672 + be16_to_cpu (currkey
->length16
)
674 currnode
= be32_to_cpu (*pointer
);
678 status
= FSW_NOT_FOUND
;
686 if (buffer
!= NULL
&& status
!= FSW_SUCCESS
)
695 struct fsw_string
* name
;
700 HFSPlusExtentRecord extents
;
705 fsw_u32 cur_pos
; /* current position */
707 struct fsw_hfs_volume
* vol
;
709 struct fsw_shandle
* shandle
; /* this one track iterator's state */
710 file_info_t file_info
;
711 } visitor_parameter_t
;
714 fsw_hfs_btree_visit_node(BTreeKey
*record
, void* param
)
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
;
723 struct fsw_string
* file_name
;
725 if (be32_to_cpu(cat_key
->parentID
) != vp
->parent
)
728 /* not smth we care about */
729 if (vp
->shandle
->pos
!= vp
->cur_pos
++)
734 case kHFSPlusFolderRecord
:
736 HFSPlusCatalogFolder
* folder_info
= (HFSPlusCatalogFolder
*)base
;
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
);
746 case kHFSPlusFileRecord
:
748 HFSPlusCatalogFile
* file_info
= (HFSPlusCatalogFile
*)base
;
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
);
761 case kHFSPlusFolderThreadRecord
:
762 case kHFSPlusFileThreadRecord
:
768 BP("unknown file type\n");
769 vp
->file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
773 name_len
= be16_to_cpu(cat_key
->nodeName
.length
);
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
++)
783 name_ptr
[i
] = be16_to_cpu(name_ptr
[i
]);
791 fsw_hfs_btree_iterate_node (struct fsw_hfs_btree
* btree
,
792 BTNodeDescriptor
* first_node
,
794 int (*callback
) (BTreeKey
*record
, void* param
),
798 /* We modify node, so make a copy */
799 BTNodeDescriptor
* node
= first_node
;
800 fsw_u8
* buffer
= NULL
;
802 status
= fsw_alloc(btree
->node_size
, &buffer
);
809 fsw_u32 count
= be16_to_cpu(node
->numRecords
);
812 /* Iterate over all records in this node. */
813 for (i
= first_rec
; i
< count
; i
++)
815 int rv
= callback(fsw_hfs_btree_rec (btree
, node
, i
), param
);
820 status
= FSW_SUCCESS
;
823 status
= FSW_NOT_FOUND
;
826 /* if callback returned 0 - continue */
829 next_node
= be32_to_cpu(node
->fLink
);
833 status
= FSW_NOT_FOUND
;
837 if (fsw_hfs_read_file (btree
->file
,
838 next_node
* btree
->node_size
,
839 btree
->node_size
, buffer
) <= 0)
841 status
= FSW_VOLUME_CORRUPTED
;
845 node
= (BTNodeDescriptor
*)buffer
;
856 void deb(fsw_u16
* p
, int len
, int swap
)
859 for (i
=0; i
<len
; i
++)
861 printf("%c", swap
? be16_to_cpu(p
[i
]) : p
[i
]);
868 fsw_hfs_cmp_extkey(BTreeKey
* key1
, BTreeKey
* key2
)
870 HFSPlusExtentKey
* ekey1
= (HFSPlusExtentKey
*)key1
;
871 HFSPlusExtentKey
* ekey2
= (HFSPlusExtentKey
*)key2
;
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
;
880 result
= ekey1
->forkType
- ekey2
->forkType
;
885 result
= be32_to_cpu(ekey1
->startBlock
) - ekey2
->startBlock
;
890 fsw_hfs_cmp_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
892 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
893 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
902 parentId1
= be32_to_cpu(ckey1
->parentID
);
904 if (parentId1
> ckey2
->parentID
)
906 if (parentId1
< ckey2
->parentID
)
909 p1
= &ckey1
->nodeName
.unicode
[0];
910 p2
= &ckey2
->nodeName
.unicode
[0];
911 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
916 /* get next valid character from ckey1 */
917 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
918 ac
= be16_to_cpu(p1
[apos
]);
923 /* get next valid character from ckey2 */
924 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
930 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
936 fsw_hfs_cmpi_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
938 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
939 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
948 parentId1
= be32_to_cpu(ckey1
->parentID
);
950 if (parentId1
> ckey2
->parentID
)
952 if (parentId1
< ckey2
->parentID
)
955 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
957 if (key1Len
== 0 && ckey2
->nodeName
.length
== 0)
960 p1
= &ckey1
->nodeName
.unicode
[0];
961 p2
= &ckey2
->nodeName
.unicode
[0];
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;
974 /* get next valid character from ckey2 */
975 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
977 lc
= bc
? fsw_to_lower(bc
) : 0;
981 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
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.
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
)
1000 HFSPlusExtentRecord
*exts
;
1001 BTNodeDescriptor
*node
= NULL
;
1003 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1004 extent
->log_count
= 1;
1005 lbno
= extent
->log_start
;
1007 /* we only care about data forks atm, do we? */
1008 exts
= &dno
->extents
;
1012 struct HFSPlusExtentKey
* key
;
1013 struct HFSPlusExtentKey overflowkey
;
1017 if (fsw_hfs_find_block(exts
, &lbno
, &phys_bno
))
1019 extent
->phys_start
= phys_bno
+ vol
->emb_block_off
;
1020 status
= FSW_SUCCESS
;
1025 /* Find appropriate overflow record */
1026 overflowkey
.fileID
= dno
->g
.dnode_id
;
1027 overflowkey
.startBlock
= extent
->log_start
- lbno
;
1035 status
= fsw_hfs_btree_search (&vol
->extents_tree
,
1036 (BTreeKey
*)&overflowkey
,
1042 key
= (struct HFSPlusExtentKey
*)
1043 fsw_hfs_btree_rec (&vol
->extents_tree
, node
, ptr
);
1044 exts
= (HFSPlusExtentRecord
*) (key
+ 1);
1053 static const fsw_u16
* g_blacklist
[] =
1055 //L"AppleIntelCPUPowerManagement.kext",
1060 //#define HFS_FILE_INJECTION
1062 #ifdef HFS_FILE_INJECTION
1065 const fsw_u16
* path
;
1066 const fsw_u16
* name
;
1070 L
"/System/Library/Extensions",
1071 L
"ApplePS2Controller.kext"
1081 create_hfs_dnode(struct fsw_hfs_dnode
* dno
,
1082 file_info_t
* file_info
,
1083 struct fsw_hfs_dnode
** child_dno_out
)
1085 fsw_status_t status
;
1086 struct fsw_hfs_dnode
* baby
;
1088 status
= fsw_dnode_create(dno
, file_info
->id
, file_info
->type
,
1089 file_info
->name
, &baby
);
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
;
1099 /* Fill-in extents info */
1100 if (file_info
->type
== FSW_DNODE_TYPE_FILE
)
1102 fsw_memcpy(baby
->extents
, &file_info
->extents
, sizeof file_info
->extents
);
1105 *child_dno_out
= baby
;
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.
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
)
1123 fsw_status_t status
;
1124 struct HFSPlusCatalogKey catkey
;
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
;
1135 fsw_memzero(&file_info
, sizeof file_info
);
1136 file_info
.name
= &rec_name
;
1138 catkey
.parentID
= dno
->g
.dnode_id
;
1139 catkey
.nodeName
.length
= (fsw_u16
)lookup_name
->len
;
1141 /* no need to allocate anything */
1142 if (lookup_name
->type
== FSW_STRING_TYPE_UTF16
)
1144 fsw_memcpy(catkey
.nodeName
.unicode
, lookup_name
->data
, lookup_name
->size
);
1145 rec_name
= *lookup_name
;
1148 status
= fsw_strdup_coerce(&rec_name
, FSW_STRING_TYPE_UTF16
, lookup_name
);
1149 /* nothing allocated so far */
1153 fsw_memcpy(catkey
.nodeName
.unicode
, rec_name
.data
, rec_name
.size
);
1156 /* Dirty hack: blacklisting of certain files on FS driver level */
1157 for (i
= 0; g_blacklist
[i
]; i
++)
1159 if (fsw_memeq(g_blacklist
[i
], catkey
.nodeName
.unicode
, catkey
.nodeName
.length
*2))
1161 DPRINT2("Blacklisted %s\n", g_blacklist
[i
]);
1162 status
= FSW_NOT_FOUND
;
1167 #ifdef HFS_FILE_INJECTION
1168 if (fsw_hfs_inject(vol
,
1170 catkey
.nodeName
.unicode
,
1171 catkey
.nodeName
.length
,
1174 status
= FSW_SUCCESS
;
1179 catkey
.keyLength
= (fsw_u16
)(5 + rec_name
.size
);
1181 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1183 vol
->case_sensitive
?
1184 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
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
);
1194 /** @todo: read additional info */
1197 case kHFSPlusFolderRecord
:
1199 HFSPlusCatalogFolder
* info
= (HFSPlusCatalogFolder
*)base
;
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
);
1210 case kHFSPlusFileRecord
:
1212 HFSPlusCatalogFile
* info
= (HFSPlusCatalogFile
*)base
;
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
);
1225 BP("unknown file type\n");
1226 file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
1230 #ifdef HFS_FILE_INJECTION
1233 status
= create_hfs_dnode(dno
, &file_info
, child_dno_out
);
1243 fsw_strfree(&rec_name
);
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.
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
)
1261 fsw_status_t status
;
1262 struct HFSPlusCatalogKey catkey
;
1264 BTNodeDescriptor
* node
= NULL
;
1266 visitor_parameter_t param
;
1267 struct fsw_string rec_name
;
1269 catkey
.parentID
= dno
->g
.dnode_id
;
1270 catkey
.nodeName
.length
= 0;
1272 fsw_memzero(¶m
, sizeof(param
));
1274 rec_name
.type
= FSW_STRING_TYPE_EMPTY
;
1275 param
.file_info
.name
= &rec_name
;
1277 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1279 vol
->case_sensitive
?
1280 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1285 /* Iterator updates shand state */
1287 param
.shandle
= shand
;
1288 param
.parent
= dno
->g
.dnode_id
;
1290 status
= fsw_hfs_btree_iterate_node (&vol
->catalog_tree
,
1293 fsw_hfs_btree_visit_node
,
1298 status
= create_hfs_dnode(dno
, ¶m
.file_info
, child_dno_out
);
1304 fsw_strfree(&rec_name
);
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.
1315 static fsw_status_t
fsw_hfs_readlink(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
1316 struct fsw_string
*link_target
)
1318 return FSW_UNSUPPORTED
;