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 for (i
= kHFSMaxVolumeNameChars
; i
> 0; i
--)
302 if (mdb
->drVN
[i
-1] != ' ')
305 s
.type
= FSW_STRING_TYPE_ISO88591
;
307 s
.data
= NULL
; //&mdb->drVN; //"HFS+ volume";
309 //fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src)
310 status
= fsw_strdup_coerce(&vol
->g
.label
, vol
->g
.host_string_type
, &s
);
313 /* Setup catalog dnode */
314 status
= fsw_dnode_create_root(vol
, kHFSCatalogFileID
, &vol
->catalog_tree
.file
);
316 fsw_memcpy (vol
->catalog_tree
.file
->extents
,
317 vol
->primary_voldesc
->catalogFile
.extents
,
318 sizeof vol
->catalog_tree
.file
->extents
);
319 vol
->catalog_tree
.file
->g
.size
=
320 be64_to_cpu(vol
->primary_voldesc
->catalogFile
.logicalSize
);
322 /* Setup extents overflow file */
323 status
= fsw_dnode_create_root(vol
, kHFSExtentsFileID
, &vol
->extents_tree
.file
);
324 fsw_memcpy (vol
->extents_tree
.file
->extents
,
325 vol
->primary_voldesc
->extentsFile
.extents
,
326 sizeof vol
->extents_tree
.file
->extents
);
327 vol
->extents_tree
.file
->g
.size
=
328 be64_to_cpu(vol
->primary_voldesc
->extentsFile
.logicalSize
);
330 /* Setup the root dnode */
331 status
= fsw_dnode_create_root(vol
, kHFSRootFolderID
, &vol
->g
.root
);
335 * Read catalog file, we know that first record is in the first node, right after
336 * the node descriptor.
338 r
= fsw_hfs_read_file(vol
->catalog_tree
.file
,
339 sizeof (BTNodeDescriptor
),
340 sizeof (BTHeaderRec
), (fsw_u8
*) &tree_header
);
343 status
= FSW_VOLUME_CORRUPTED
;
346 vol
->case_sensitive
=
347 (signature
== kHFSXSigWord
) &&
348 (tree_header
.keyCompareType
== kHFSBinaryCompare
);
349 vol
->catalog_tree
.root_node
= be32_to_cpu (tree_header
.rootNode
);
350 vol
->catalog_tree
.node_size
= be16_to_cpu (tree_header
.nodeSize
);
352 /* Read extents overflow file */
353 r
= fsw_hfs_read_file(vol
->extents_tree
.file
,
354 sizeof (BTNodeDescriptor
),
355 sizeof (BTHeaderRec
), (fsw_u8
*) &tree_header
);
358 status
= FSW_VOLUME_CORRUPTED
;
362 vol
->extents_tree
.root_node
= be32_to_cpu (tree_header
.rootNode
);
363 vol
->extents_tree
.node_size
= be16_to_cpu (tree_header
.nodeSize
);
372 fsw_block_release(vol
, blockno
, buffer
);
376 //Here is a method to obtain Volume label from Apple
377 //how to implement it?
380 UInt32 firstLeafNode;
384 char *nodeBuf, *testKey, *entry;
387 if (HFSInitPartition(ih) == -1) { return; }
389 // Fill some crucial data structures by side effect.
391 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
393 // Now we can loook up the volume name node.
394 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
395 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
397 dirIndex = (long long) firstLeafNode * nodeSize;
398 index = (long) (*dirIndex % nodeSize); == 0
399 curNode = (long) (*dirIndex / nodeSize); == firstLeafNode
401 //GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
402 // Read the BTree node and get the record for index.
403 ReadExtent(extent, extentSize, kHFSCatalogFileID,
404 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
405 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
407 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
408 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
409 (u_int8_t *)gTempStr, 256, OSBigEndian);
413 strncpy(str, name, strMaxLen);
414 str[strMaxLen] = '\0';
418 * Free the volume data structure. Called by the core after an unmount or after
419 * an unsuccessful mount to release the memory used by the file system type specific
420 * part of the volume structure.
423 static void fsw_hfs_volume_free(struct fsw_hfs_volume
*vol
)
425 if (vol
->primary_voldesc
)
427 fsw_free(vol
->primary_voldesc
);
428 vol
->primary_voldesc
= NULL
;
433 * Get in-depth information on a volume.
436 static fsw_status_t
fsw_hfs_volume_stat(struct fsw_hfs_volume
*vol
, struct fsw_volume_stat
*sb
)
438 sb
->total_bytes
= be32_to_cpu(vol
->primary_voldesc
->totalBlocks
) << vol
->block_size_shift
;
439 sb
->free_bytes
= be32_to_cpu(vol
->primary_voldesc
->freeBlocks
) << vol
->block_size_shift
;
444 * Get full information on a dnode from disk. This function is called by the core
445 * whenever it needs to access fields in the dnode structure that may not
446 * be filled immediately upon creation of the dnode.
449 static fsw_status_t
fsw_hfs_dnode_fill(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
455 * Free the dnode data structure. Called by the core when deallocating a dnode
456 * structure to release the memory used by the file system type specific part
457 * of the dnode structure.
460 static void fsw_hfs_dnode_free(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
464 static fsw_u32
mac_to_posix(fsw_u32 mac_time
)
466 /* Mac time is 1904 year based */
467 return mac_time
? mac_time
- 2082844800 : 0;
471 * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
472 * has been called on the dnode before this function is called. Note that some
473 * data is not directly stored into the structure, but passed to a host-specific
474 * callback that converts it to the host-specific format.
477 static fsw_status_t
fsw_hfs_dnode_stat(struct fsw_hfs_volume
*vol
,
478 struct fsw_hfs_dnode
*dno
,
479 struct fsw_dnode_stat
*sb
)
481 sb
->used_bytes
= dno
->used_bytes
;
482 sb
->store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, mac_to_posix(dno
->ctime
));
483 sb
->store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, mac_to_posix(dno
->mtime
));
484 sb
->store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, 0);
485 sb
->store_attr_posix(sb
, 0700);
491 fsw_hfs_find_block(HFSPlusExtentRecord
* exts
,
496 fsw_u32 cur_lbno
= *lbno
;
498 for (i
= 0; i
< 8; i
++)
500 fsw_u32 start
= be32_to_cpu ((*exts
)[i
].startBlock
);
501 fsw_u32 count
= be32_to_cpu ((*exts
)[i
].blockCount
);
503 if (cur_lbno
< count
)
505 *pbno
= start
+ cur_lbno
;
517 /* Find record offset, numbering starts from the end */
519 fsw_hfs_btree_recoffset (struct fsw_hfs_btree
* btree
,
520 BTNodeDescriptor
* node
,
523 fsw_u8
*cnode
= (fsw_u8
*) node
;
525 recptr
= (fsw_u16
*) (cnode
+btree
->node_size
- index
* 2 - 2);
526 return be16_to_cpu(*recptr
);
529 /* Pointer to the key inside node */
531 fsw_hfs_btree_rec (struct fsw_hfs_btree
* btree
,
532 BTNodeDescriptor
* node
,
535 fsw_u8
*cnode
= (fsw_u8
*) node
;
537 offset
= fsw_hfs_btree_recoffset (btree
, node
, index
);
538 return (BTreeKey
*) (cnode
+ offset
);
543 fsw_hfs_btree_search (struct fsw_hfs_btree
* btree
,
545 int (*compare_keys
) (BTreeKey
* key1
, BTreeKey
* key2
),
546 BTNodeDescriptor
** result
,
547 fsw_u32
* key_offset
)
549 BTNodeDescriptor
* node
;
553 fsw_u8
* buffer
= NULL
;
555 currnode
= btree
->root_node
;
556 status
= fsw_alloc(btree
->node_size
, &buffer
);
559 node
= (BTNodeDescriptor
*)buffer
;
570 if (fsw_hfs_read_file (btree
->file
,
571 (fsw_u64
)currnode
* btree
->node_size
,
572 btree
->node_size
, buffer
) <= 0)
574 status
= FSW_VOLUME_CORRUPTED
;
578 if (be16_to_cpu(*(fsw_u16
*)(buffer
+ btree
->node_size
- 2)) != sizeof(BTNodeDescriptor
))
579 BP("corrupted node\n");
581 count
= be16_to_cpu (node
->numRecords
);
584 for (rec
= 0; rec
< count
; rec
++)
588 currkey
= fsw_hfs_btree_rec (btree
, node
, rec
);
589 cmp
= compare_keys (currkey
, key
);
590 //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
593 if (node
->kind
== kBTLeafNode
)
601 status
= FSW_SUCCESS
;
605 else if (node
->kind
== kBTIndexNode
)
612 pointer
= (fsw_u32
*) ((char *) currkey
613 + be16_to_cpu (currkey
->length16
)
615 currnode
= be32_to_cpu (*pointer
);
620 if (node
->kind
== kBTLeafNode
&& cmp
< 0 && node
->fLink
)
622 currnode
= be32_to_cpu(node
->fLink
);
627 status
= FSW_NOT_FOUND
;
631 /* Perform binary search */
633 fsw_u32 upper
= count
- 1;
635 BTreeKey
*currkey
= NULL
;
639 status
= FSW_NOT_FOUND
;
643 while (lower
<= upper
)
645 fsw_u32 index
= (lower
+ upper
) / 2;
647 currkey
= fsw_hfs_btree_rec (btree
, node
, index
);
649 cmp
= compare_keys (currkey
, key
);
650 if (cmp
< 0) upper
= index
- 1;
651 if (cmp
> 0) lower
= index
+ 1;
658 status
= FSW_SUCCESS
;
664 currkey
= fsw_hfs_btree_rec (btree
, node
, upper
);
666 if (node
->kind
== kBTIndexNode
&& currkey
)
670 pointer
= (fsw_u32
*) ((char *) currkey
671 + be16_to_cpu (currkey
->length16
)
673 currnode
= be32_to_cpu (*pointer
);
677 status
= FSW_NOT_FOUND
;
685 if (buffer
!= NULL
&& status
!= FSW_SUCCESS
)
694 struct fsw_string
* name
;
699 HFSPlusExtentRecord extents
;
704 fsw_u32 cur_pos
; /* current position */
706 struct fsw_hfs_volume
* vol
;
708 struct fsw_shandle
* shandle
; /* this one track iterator's state */
709 file_info_t file_info
;
710 } visitor_parameter_t
;
713 fsw_hfs_btree_visit_node(BTreeKey
*record
, void* param
)
715 visitor_parameter_t
* vp
= (visitor_parameter_t
*)param
;
716 fsw_u8
* base
= (fsw_u8
*)record
->rawData
+ be16_to_cpu(record
->length16
) + 2;
717 fsw_u16 rec_type
= be16_to_cpu(*(fsw_u16
*)base
);
718 struct HFSPlusCatalogKey
* cat_key
= (HFSPlusCatalogKey
*)record
;
722 struct fsw_string
* file_name
;
724 if (be32_to_cpu(cat_key
->parentID
) != vp
->parent
)
727 /* not smth we care about */
728 if (vp
->shandle
->pos
!= vp
->cur_pos
++)
733 case kHFSPlusFolderRecord
:
735 HFSPlusCatalogFolder
* folder_info
= (HFSPlusCatalogFolder
*)base
;
737 vp
->file_info
.id
= be32_to_cpu(folder_info
->folderID
);
738 vp
->file_info
.type
= FSW_DNODE_TYPE_DIR
;
739 vp
->file_info
.size
= be32_to_cpu(folder_info
->valence
);
740 vp
->file_info
.used
= be32_to_cpu(folder_info
->valence
);
741 vp
->file_info
.ctime
= be32_to_cpu(folder_info
->createDate
);
742 vp
->file_info
.mtime
= be32_to_cpu(folder_info
->contentModDate
);
745 case kHFSPlusFileRecord
:
747 HFSPlusCatalogFile
* file_info
= (HFSPlusCatalogFile
*)base
;
749 vp
->file_info
.id
= be32_to_cpu(file_info
->fileID
);
750 vp
->file_info
.type
= FSW_DNODE_TYPE_FILE
;
751 vp
->file_info
.size
= be64_to_cpu(file_info
->dataFork
.logicalSize
);
752 vp
->file_info
.used
= LShiftU64(be32_to_cpu(file_info
->dataFork
.totalBlocks
),
753 vp
->vol
->block_size_shift
);
754 vp
->file_info
.ctime
= be32_to_cpu(file_info
->createDate
);
755 vp
->file_info
.mtime
= be32_to_cpu(file_info
->contentModDate
);
756 fsw_memcpy(&vp
->file_info
.extents
, &file_info
->dataFork
.extents
,
757 sizeof vp
->file_info
.extents
);
760 case kHFSPlusFolderThreadRecord
:
761 case kHFSPlusFileThreadRecord
:
767 BP("unknown file type\n");
768 vp
->file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
772 name_len
= be16_to_cpu(cat_key
->nodeName
.length
);
774 file_name
= vp
->file_info
.name
;
775 file_name
->len
= name_len
;
776 fsw_memdup(&file_name
->data
, &cat_key
->nodeName
.unicode
[0], 2*name_len
);
777 file_name
->size
= 2*name_len
;
778 file_name
->type
= FSW_STRING_TYPE_UTF16
;
779 name_ptr
= (fsw_u16
*)file_name
->data
;
780 for (i
=0; i
<name_len
; i
++)
782 name_ptr
[i
] = be16_to_cpu(name_ptr
[i
]);
790 fsw_hfs_btree_iterate_node (struct fsw_hfs_btree
* btree
,
791 BTNodeDescriptor
* first_node
,
793 int (*callback
) (BTreeKey
*record
, void* param
),
797 /* We modify node, so make a copy */
798 BTNodeDescriptor
* node
= first_node
;
799 fsw_u8
* buffer
= NULL
;
801 status
= fsw_alloc(btree
->node_size
, &buffer
);
808 fsw_u32 count
= be16_to_cpu(node
->numRecords
);
811 /* Iterate over all records in this node. */
812 for (i
= first_rec
; i
< count
; i
++)
814 int rv
= callback(fsw_hfs_btree_rec (btree
, node
, i
), param
);
819 status
= FSW_SUCCESS
;
822 status
= FSW_NOT_FOUND
;
825 /* if callback returned 0 - continue */
828 next_node
= be32_to_cpu(node
->fLink
);
832 status
= FSW_NOT_FOUND
;
836 if (fsw_hfs_read_file (btree
->file
,
837 next_node
* btree
->node_size
,
838 btree
->node_size
, buffer
) <= 0)
840 status
= FSW_VOLUME_CORRUPTED
;
844 node
= (BTNodeDescriptor
*)buffer
;
855 void deb(fsw_u16
* p
, int len
, int swap
)
858 for (i
=0; i
<len
; i
++)
860 printf("%c", swap
? be16_to_cpu(p
[i
]) : p
[i
]);
867 fsw_hfs_cmp_extkey(BTreeKey
* key1
, BTreeKey
* key2
)
869 HFSPlusExtentKey
* ekey1
= (HFSPlusExtentKey
*)key1
;
870 HFSPlusExtentKey
* ekey2
= (HFSPlusExtentKey
*)key2
;
873 /* First key is read from the FS data, second is in-memory in CPU endianess */
874 result
= be32_to_cpu(ekey1
->fileID
) - ekey2
->fileID
;
879 result
= ekey1
->forkType
- ekey2
->forkType
;
884 result
= be32_to_cpu(ekey1
->startBlock
) - ekey2
->startBlock
;
889 fsw_hfs_cmp_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
891 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
892 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
901 parentId1
= be32_to_cpu(ckey1
->parentID
);
903 if (parentId1
> ckey2
->parentID
)
905 if (parentId1
< ckey2
->parentID
)
908 p1
= &ckey1
->nodeName
.unicode
[0];
909 p2
= &ckey2
->nodeName
.unicode
[0];
910 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
915 /* get next valid character from ckey1 */
916 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
917 ac
= be16_to_cpu(p1
[apos
]);
922 /* get next valid character from ckey2 */
923 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
929 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
935 fsw_hfs_cmpi_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
937 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
938 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
947 parentId1
= be32_to_cpu(ckey1
->parentID
);
949 if (parentId1
> ckey2
->parentID
)
951 if (parentId1
< ckey2
->parentID
)
954 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
956 if (key1Len
== 0 && ckey2
->nodeName
.length
== 0)
959 p1
= &ckey1
->nodeName
.unicode
[0];
960 p2
= &ckey2
->nodeName
.unicode
[0];
966 /* get next valid character from ckey1 */
967 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
968 ac
= be16_to_cpu(p1
[apos
]);
969 lc
= ac
? fsw_to_lower(ac
) : 0;
973 /* get next valid character from ckey2 */
974 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
976 lc
= bc
? fsw_to_lower(bc
) : 0;
980 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
986 * Retrieve file data mapping information. This function is called by the core when
987 * fsw_shandle_read needs to know where on the disk the required piece of the file's
988 * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
989 * on the dnode before. Our task here is to get the physical disk block number for
990 * the requested logical block number.
993 static fsw_status_t
fsw_hfs_get_extent(struct fsw_hfs_volume
* vol
,
994 struct fsw_hfs_dnode
* dno
,
995 struct fsw_extent
* extent
)
999 HFSPlusExtentRecord
*exts
;
1000 BTNodeDescriptor
*node
= NULL
;
1002 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1003 extent
->log_count
= 1;
1004 lbno
= extent
->log_start
;
1006 /* we only care about data forks atm, do we? */
1007 exts
= &dno
->extents
;
1011 struct HFSPlusExtentKey
* key
;
1012 struct HFSPlusExtentKey overflowkey
;
1016 if (fsw_hfs_find_block(exts
, &lbno
, &phys_bno
))
1018 extent
->phys_start
= phys_bno
+ vol
->emb_block_off
;
1019 status
= FSW_SUCCESS
;
1024 /* Find appropriate overflow record */
1025 overflowkey
.fileID
= dno
->g
.dnode_id
;
1026 overflowkey
.startBlock
= extent
->log_start
- lbno
;
1034 status
= fsw_hfs_btree_search (&vol
->extents_tree
,
1035 (BTreeKey
*)&overflowkey
,
1041 key
= (struct HFSPlusExtentKey
*)
1042 fsw_hfs_btree_rec (&vol
->extents_tree
, node
, ptr
);
1043 exts
= (HFSPlusExtentRecord
*) (key
+ 1);
1052 static const fsw_u16
* g_blacklist
[] =
1054 //L"AppleIntelCPUPowerManagement.kext",
1059 //#define HFS_FILE_INJECTION
1061 #ifdef HFS_FILE_INJECTION
1064 const fsw_u16
* path
;
1065 const fsw_u16
* name
;
1069 L
"/System/Library/Extensions",
1070 L
"ApplePS2Controller.kext"
1080 create_hfs_dnode(struct fsw_hfs_dnode
* dno
,
1081 file_info_t
* file_info
,
1082 struct fsw_hfs_dnode
** child_dno_out
)
1084 fsw_status_t status
;
1085 struct fsw_hfs_dnode
* baby
;
1087 status
= fsw_dnode_create(dno
, file_info
->id
, file_info
->type
,
1088 file_info
->name
, &baby
);
1092 baby
->g
.size
= file_info
->size
;
1093 baby
->used_bytes
= file_info
->used
;
1094 baby
->ctime
= file_info
->ctime
;
1095 baby
->mtime
= file_info
->mtime
;
1098 /* Fill-in extents info */
1099 if (file_info
->type
== FSW_DNODE_TYPE_FILE
)
1101 fsw_memcpy(baby
->extents
, &file_info
->extents
, sizeof file_info
->extents
);
1104 *child_dno_out
= baby
;
1111 * Lookup a directory's child dnode by name. This function is called on a directory
1112 * to retrieve the directory entry with the given name. A dnode is constructed for
1113 * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1114 * and the dnode is actually a directory.
1117 static fsw_status_t
fsw_hfs_dir_lookup(struct fsw_hfs_volume
* vol
,
1118 struct fsw_hfs_dnode
* dno
,
1119 struct fsw_string
* lookup_name
,
1120 struct fsw_hfs_dnode
** child_dno_out
)
1122 fsw_status_t status
;
1123 struct HFSPlusCatalogKey catkey
;
1126 BTNodeDescriptor
* node
= NULL
;
1127 struct fsw_string rec_name
;
1128 int free_data
= 0, i
;
1129 HFSPlusCatalogKey
* file_key
;
1130 file_info_t file_info
;
1134 fsw_memzero(&file_info
, sizeof file_info
);
1135 file_info
.name
= &rec_name
;
1137 catkey
.parentID
= dno
->g
.dnode_id
;
1138 catkey
.nodeName
.length
= (fsw_u16
)lookup_name
->len
;
1140 /* no need to allocate anything */
1141 if (lookup_name
->type
== FSW_STRING_TYPE_UTF16
)
1143 fsw_memcpy(catkey
.nodeName
.unicode
, lookup_name
->data
, lookup_name
->size
);
1144 rec_name
= *lookup_name
;
1147 status
= fsw_strdup_coerce(&rec_name
, FSW_STRING_TYPE_UTF16
, lookup_name
);
1148 /* nothing allocated so far */
1152 fsw_memcpy(catkey
.nodeName
.unicode
, rec_name
.data
, rec_name
.size
);
1155 /* Dirty hack: blacklisting of certain files on FS driver level */
1156 for (i
= 0; g_blacklist
[i
]; i
++)
1158 if (fsw_memeq(g_blacklist
[i
], catkey
.nodeName
.unicode
, catkey
.nodeName
.length
*2))
1160 DPRINT2("Blacklisted %s\n", g_blacklist
[i
]);
1161 status
= FSW_NOT_FOUND
;
1166 #ifdef HFS_FILE_INJECTION
1167 if (fsw_hfs_inject(vol
,
1169 catkey
.nodeName
.unicode
,
1170 catkey
.nodeName
.length
,
1173 status
= FSW_SUCCESS
;
1178 catkey
.keyLength
= (fsw_u16
)(5 + rec_name
.size
);
1180 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1182 vol
->case_sensitive
?
1183 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1188 file_key
= (HFSPlusCatalogKey
*)fsw_hfs_btree_rec (&vol
->catalog_tree
, node
, ptr
);
1189 /* for plain HFS "-(keySize & 1)" would be needed */
1190 base
= (fsw_u8
*)file_key
+ be16_to_cpu(file_key
->keyLength
) + 2;
1191 rec_type
= be16_to_cpu(*(fsw_u16
*)base
);
1193 /** @todo: read additional info */
1196 case kHFSPlusFolderRecord
:
1198 HFSPlusCatalogFolder
* info
= (HFSPlusCatalogFolder
*)base
;
1200 file_info
.id
= be32_to_cpu(info
->folderID
);
1201 file_info
.type
= FSW_DNODE_TYPE_DIR
;
1202 /* @todo: return number of elements, maybe use smth else */
1203 file_info
.size
= be32_to_cpu(info
->valence
);
1204 file_info
.used
= be32_to_cpu(info
->valence
);
1205 file_info
.ctime
= be32_to_cpu(info
->createDate
);
1206 file_info
.mtime
= be32_to_cpu(info
->contentModDate
);
1209 case kHFSPlusFileRecord
:
1211 HFSPlusCatalogFile
* info
= (HFSPlusCatalogFile
*)base
;
1213 file_info
.id
= be32_to_cpu(info
->fileID
);
1214 file_info
.type
= FSW_DNODE_TYPE_FILE
;
1215 file_info
.size
= be64_to_cpu(info
->dataFork
.logicalSize
);
1216 file_info
.used
= LShiftU64(be32_to_cpu(info
->dataFork
.totalBlocks
), vol
->block_size_shift
);
1217 file_info
.ctime
= be32_to_cpu(info
->createDate
);
1218 file_info
.mtime
= be32_to_cpu(info
->contentModDate
);
1219 fsw_memcpy(&file_info
.extents
, &info
->dataFork
.extents
,
1220 sizeof file_info
.extents
);
1224 BP("unknown file type\n");
1225 file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
1229 #ifdef HFS_FILE_INJECTION
1232 status
= create_hfs_dnode(dno
, &file_info
, child_dno_out
);
1242 fsw_strfree(&rec_name
);
1248 * Get the next directory entry when reading a directory. This function is called during
1249 * directory iteration to retrieve the next directory entry. A dnode is constructed for
1250 * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1251 * and the dnode is actually a directory. The shandle provided by the caller is used to
1252 * record the position in the directory between calls.
1255 static fsw_status_t
fsw_hfs_dir_read(struct fsw_hfs_volume
*vol
,
1256 struct fsw_hfs_dnode
*dno
,
1257 struct fsw_shandle
*shand
,
1258 struct fsw_hfs_dnode
**child_dno_out
)
1260 fsw_status_t status
;
1261 struct HFSPlusCatalogKey catkey
;
1263 BTNodeDescriptor
* node
= NULL
;
1265 visitor_parameter_t param
;
1266 struct fsw_string rec_name
;
1268 catkey
.parentID
= dno
->g
.dnode_id
;
1269 catkey
.nodeName
.length
= 0;
1271 fsw_memzero(¶m
, sizeof(param
));
1273 rec_name
.type
= FSW_STRING_TYPE_EMPTY
;
1274 param
.file_info
.name
= &rec_name
;
1276 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1278 vol
->case_sensitive
?
1279 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1284 /* Iterator updates shand state */
1286 param
.shandle
= shand
;
1287 param
.parent
= dno
->g
.dnode_id
;
1289 status
= fsw_hfs_btree_iterate_node (&vol
->catalog_tree
,
1292 fsw_hfs_btree_visit_node
,
1297 status
= create_hfs_dnode(dno
, ¶m
.file_info
, child_dno_out
);
1303 fsw_strfree(&rec_name
);
1309 * Get the target path of a symbolic link. This function is called when a symbolic
1310 * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
1311 * called on the dnode and that it really is a symlink.
1314 static fsw_status_t
fsw_hfs_readlink(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
1315 struct fsw_string
*link_target
)
1317 return FSW_UNSUPPORTED
;