2019-09-03 11:58:42 +02:00
/* $Id: fsw_hfs.c 33540 2010-10-28 09:27:05Z vboxsync $ */
/** @file
* fsw_hfs . c - HFS file system driver code , see
*
* http : //developer.apple.com/technotes/tn/tn1150.html
*
* Current limitations :
* - Doesn ' t support permissions
* - Complete Unicode case - insensitiveness disabled ( large tables )
* - links - > now supported symlinks and hardlinks . No aliasis .
* - Only supports pure HFS + ( i . e . no HFS , or HFS + embedded to HFS )
*/
/*
* Copyright ( C ) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition ( OSE ) , as
* available from http : //www.virtualbox.org. This file is free software;
* you can redistribute it and / or modify it under the terms of the GNU
* General Public License ( GPL ) as published by the Free Software
* Foundation , in version 2 as it comes in the " COPYING " file of the
* VirtualBox OSE distribution . VirtualBox OSE is distributed in the
* hope that it will be useful , but WITHOUT ANY WARRANTY of any kind .
*/
// improved by nms42: VolumeName, links. 2014
// Cleanup, tested and corrected by Slice
# include "fsw_hfs.h"
# include <Library/MemLogLib.h>
# include <Library/PrintLib.h>
# define VBOXHFS_BTREE_BINSEARCH 0
# define DEBUG_HFS 0
# if DEBUG_HFS==2
# define DBG(...) AsciiPrint(__VA_ARGS__)
# elif DEBUG_HFS==1
# define DBG(...) MemLog(TRUE, 1, __VA_ARGS__)
# else
# define DBG(...)
# endif
# if DEBUG_HFS == 2
# define CONCAT(x,y) x##y
# define DPRINT(x) Print(CONCAT(L,x))
# define DPRINT2(x,y) Print(CONCAT(L,x), y)
# define BP(msg) DPRINT(msg)
# else
# define CONCAT(x,y) x##y
# define DPRINT(x)
# define DPRINT2(x,y)
# define BP(msg)
# endif
2019-09-12 08:49:40 +02:00
# if defined(__GNUC__) && __GNUC__ >= 9
# pragma GCC diagnostic ignored "-Waddress-of-packed-member"
# endif
2019-09-03 11:58:42 +02:00
static int hardlink = 0 ;
static fsw_status_t fsw_hfs_volume_mount ( struct fsw_hfs_volume * vol ) ;
static void fsw_hfs_volume_free ( struct fsw_hfs_volume * vol ) ;
static fsw_status_t fsw_hfs_volume_stat ( struct fsw_hfs_volume * vol , struct fsw_volume_stat * sb ) ;
static fsw_status_t fsw_hfs_dnode_fill ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno ) ;
static void fsw_hfs_dnode_free ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno ) ;
static fsw_status_t fsw_hfs_dnode_stat ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno ,
struct fsw_dnode_stat_str * sb ) ;
static fsw_status_t fsw_hfs_get_extent ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno ,
struct fsw_extent * extent ) ;
static fsw_status_t fsw_hfs_dir_lookup ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno ,
struct fsw_string * lookup_name , struct fsw_hfs_dnode * * child_dno ) ;
static fsw_status_t fsw_hfs_dir_lookup_id ( struct fsw_hfs_volume * vol ,
fsw_u32 lookup_id ,
file_info_t * file_info ) ;
static fsw_status_t fsw_hfs_dir_read ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno ,
struct fsw_shandle * shand , struct fsw_hfs_dnode * * child_dno ) ;
#if 0
static fsw_status_t fsw_hfs_read_dirrec ( struct fsw_shandle * shand , struct hfs_dirrec_buffer * dirrec_buffer ) ;
# endif
static fsw_status_t fsw_hfs_readlink ( struct fsw_hfs_volume * vol ,
struct fsw_hfs_dnode * dno ,
struct fsw_string * link ) ;
//
// Dispatch Table
//
struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME ( hfs ) = {
{ FSW_STRING_TYPE_ISO88591 , 4 , 4 , " hfs " } ,
sizeof ( struct fsw_hfs_volume ) ,
sizeof ( struct fsw_hfs_dnode ) ,
fsw_hfs_volume_mount , // volume open
fsw_hfs_volume_free , // volume close
fsw_hfs_volume_stat , // volume info: total_bytes, free_bytes
fsw_hfs_dnode_fill , //return FSW_SUCCESS;
fsw_hfs_dnode_free , // empty
fsw_hfs_dnode_stat , //size and times
fsw_hfs_get_extent , // get the physical disk block number for the requested logical block number
fsw_hfs_dir_lookup , //retrieve the directory entry with the given name
fsw_hfs_dir_read , // next directory entry when reading a directory
fsw_hfs_readlink , // ;
} ;
static fsw_s32
fsw_hfs_read_block ( struct fsw_hfs_dnode * dno ,
fsw_u32 log_bno ,
fsw_u32 off ,
fsw_s32 len ,
fsw_u8 * buf )
{
fsw_status_t status ;
struct fsw_extent extent ;
fsw_u32 phys_bno ;
fsw_u8 * buffer ;
extent . log_start = log_bno ;
status = fsw_hfs_get_extent ( dno - > g . vol , dno , & extent ) ;
if ( status )
return status ;
phys_bno = extent . phys_start ;
//Slice - increase cache level from 0 to 3
status = fsw_block_get ( dno - > g . vol , phys_bno , 3 , ( void * * ) & buffer ) ;
if ( status )
return status ;
fsw_memcpy ( buf , buffer + off , len ) ;
fsw_block_release ( dno - > g . vol , phys_bno , buffer ) ;
return FSW_SUCCESS ;
}
/* Read data from HFS file. */
static fsw_s32
fsw_hfs_read_file ( struct fsw_hfs_dnode * dno ,
fsw_u64 pos ,
fsw_s32 len ,
fsw_u8 * buf )
{
fsw_status_t status ;
fsw_u32 log_bno ;
fsw_u32 block_size_bits = dno - > g . vol - > block_size_shift ;
fsw_u32 block_size = ( 1 < < block_size_bits ) ;
fsw_u32 block_size_mask = block_size - 1 ;
fsw_s32 read = 0 ;
while ( len > 0 ) {
fsw_u32 off = ( fsw_u32 ) ( pos & block_size_mask ) ;
fsw_s32 next_len = len ;
log_bno = ( fsw_u32 ) RShiftU64 ( pos , block_size_bits ) ;
if ( ( next_len > = 0 ) & & ( ( fsw_u32 ) next_len > block_size ) ) {
next_len = block_size ;
}
status = fsw_hfs_read_block ( dno , log_bno , off , next_len , buf ) ;
if ( status )
return - 1 ;
buf + = next_len ;
pos + = next_len ;
len - = next_len ;
read + = next_len ;
}
hardlink = 0 ; //this is static value that me must initialize sometimes
return read ;
}
static fsw_s32
fsw_hfs_compute_shift ( fsw_u32 size )
{
fsw_s32 i ;
for ( i = 0 ; i < 32 ; i + + ) {
if ( ( size > > i ) = = 0 )
return i - 1 ;
}
return 0 ;
}
/**
* Mount an HFS + volume . Reads the superblock and constructs the
* root directory dnode .
*/
static fsw_status_t fsw_hfs_volume_mount ( struct fsw_hfs_volume * vol )
{
fsw_status_t status , rv ;
void * buffer = NULL ;
HFSPlusVolumeHeader * voldesc ;
fsw_u32 blockno ;
struct fsw_string s ;
HFSMasterDirectoryBlock * mdb ;
INTN i ;
rv = FSW_UNSUPPORTED ;
vol - > primary_voldesc = NULL ;
fsw_set_blocksize ( vol , HFS_BLOCKSIZE , HFS_BLOCKSIZE ) ;
blockno = HFS_SUPERBLOCK_BLOCKNO ;
# define CHECK(s) \
if ( status ) { \
rv = status ; \
break ; \
}
vol - > emb_block_off = 0 ;
vol - > hfs_kind = 0 ;
do {
fsw_u16 signature ;
BTHeaderRec tree_header ;
fsw_s32 r ;
fsw_u32 block_size ;
status = fsw_block_get ( vol , blockno , 0 , & buffer ) ;
CHECK ( status ) ;
voldesc = ( HFSPlusVolumeHeader * ) buffer ;
mdb = ( HFSMasterDirectoryBlock * ) buffer ;
signature = be16_to_cpu ( voldesc - > signature ) ;
if ( ( signature = = kHFSPlusSigWord ) | | ( signature = = kHFSXSigWord ) ) { //H+ or HX
if ( vol - > hfs_kind = = 0 ) {
// DBG("found HFS+\n");
vol - > hfs_kind = FSW_HFS_PLUS ;
}
} else if ( signature = = kHFSSigWord ) { // 'BD'
// HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer;
//VolumeName = mdb->drVN 28bytes
if ( be16_to_cpu ( mdb - > drEmbedSigWord ) = = kHFSPlusSigWord ) {
DBG ( " found HFS+ inside HFS, untested \n " ) ;
vol - > hfs_kind = FSW_HFS_PLUS_EMB ;
vol - > emb_block_off = be32_to_cpu ( mdb - > drEmbedExtent . startBlock ) ;
fsw_block_release ( vol , blockno , buffer ) ;
blockno + = vol - > emb_block_off ;
/* retry */
continue ;
} else {
DBG ( " found plain HFS, unsupported \n " ) ;
vol - > hfs_kind = FSW_HFS_PLAIN ;
}
rv = FSW_UNSUPPORTED ;
break ;
} else {
rv = FSW_UNSUPPORTED ;
break ;
}
status = fsw_memdup ( ( void * * ) & vol - > primary_voldesc , voldesc ,
sizeof ( HFSPlusVolumeHeader ) ) ;
CHECK ( status ) ;
block_size = be32_to_cpu ( voldesc - > blockSize ) ;
vol - > block_size_shift = fsw_hfs_compute_shift ( block_size ) ;
// DBG("vol block_size=%d\n", block_size);
fsw_block_release ( vol , blockno , buffer ) ;
buffer = NULL ;
voldesc = NULL ;
fsw_set_blocksize ( vol , block_size , block_size ) ;
/* get volume name */
for ( i = kHFSMaxVolumeNameChars ; i > 0 ; i - - )
if ( mdb - > drVN [ i - 1 ] ! = ' ' )
break ;
s . type = FSW_STRING_TYPE_ISO88591 ;
s . size = s . len = ( int ) i ;
s . data = & mdb - > drVN ; //"HFS+ volume";
//fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src)
status = fsw_strdup_coerce ( & vol - > g . label , vol - > g . host_string_type , & s ) ;
CHECK ( status ) ;
DBG ( " HW Volume label: " ) ;
for ( i = 0 ; i < s . len ; i + + ) {
DBG ( " %02x " , ( ( fsw_u16 * ) vol - > g . label . data ) [ i ] ) ;
}
DBG ( " \n " ) ;
/* Setup catalog dnode */
status = fsw_dnode_create_root ( vol , kHFSCatalogFileID , & vol - > catalog_tree . file ) ;
CHECK ( status ) ;
//Slice - why copy structure?
fsw_memcpy ( vol - > catalog_tree . file - > extents ,
vol - > primary_voldesc - > catalogFile . extents ,
sizeof vol - > catalog_tree . file - > extents ) ;
vol - > catalog_tree . file - > g . size =
be64_to_cpu_ua ( & vol - > primary_voldesc - > catalogFile . logicalSize ) ;
/* Setup extents overflow file */
/*status = */ fsw_dnode_create_root ( vol , kHFSExtentsFileID , & vol - > extents_tree . file ) ;
fsw_memcpy ( vol - > extents_tree . file - > extents ,
vol - > primary_voldesc - > extentsFile . extents ,
sizeof vol - > extents_tree . file - > extents ) ;
vol - > extents_tree . file - > g . size =
be64_to_cpu_ua ( & vol - > primary_voldesc - > extentsFile . logicalSize ) ;
/* Setup the root dnode */
status = fsw_dnode_create_root ( vol , kHFSRootFolderID , & vol - > g . root ) ;
CHECK ( status ) ;
/*
* Read catalog file , we know that first record is in the first node , right after
* the node descriptor .
*/
r = fsw_hfs_read_file ( vol - > catalog_tree . file ,
sizeof ( BTNodeDescriptor ) ,
sizeof ( BTHeaderRec ) , ( fsw_u8 * ) & tree_header ) ;
if ( r < = 0 ) {
status = FSW_VOLUME_CORRUPTED ;
DBG ( " bad read catalog file \n " ) ;
break ;
}
vol - > case_sensitive =
( signature = = kHFSXSigWord ) & &
( tree_header . keyCompareType = = kHFSBinaryCompare ) ;
vol - > catalog_tree . root_node = be32_to_cpu ( tree_header . rootNode ) ;
vol - > catalog_tree . node_size = be16_to_cpu ( tree_header . nodeSize ) ;
//nms42
/* Take Volume Name before tree_header overwritten */
{
fsw_u32 firstLeafNum ;
fsw_u64 catfOffset ;
fsw_u8 cbuff [ sizeof ( BTNodeDescriptor ) + sizeof ( HFSPlusCatalogKey ) ] ;
firstLeafNum = be32_to_cpu ( tree_header . firstLeafNode ) ;
catfOffset = firstLeafNum * vol - > catalog_tree . node_size ;
r = fsw_hfs_read_file ( vol - > catalog_tree . file , catfOffset , sizeof ( cbuff ) , cbuff ) ;
if ( r = = sizeof ( cbuff ) ) {
BTNodeDescriptor * btnd ;
HFSPlusCatalogKey * ck ;
btnd = ( BTNodeDescriptor * ) cbuff ;
ck = ( HFSPlusCatalogKey * ) ( cbuff + sizeof ( BTNodeDescriptor ) ) ;
// DBG(" btnd->kind ==%d expected -1\n", btnd->kind);
// DBG(" ck->parentID ==%d expected 1\n", be32_to_cpu (ck->parentID));
if ( btnd - > kind = = kBTLeafNode & &
be32_to_cpu_ua ( ( fsw_u32 * ) ( ( ( UINT8 * ) ck ) + OFFSET_OF ( HFSPlusCatalogKey , parentID ) ) ) = = kHFSRootParentID ) {
struct fsw_string vn ;
vn . type = FSW_STRING_TYPE_UTF16_BE ;
vn . len = be16_to_cpu ( ck - > nodeName . length ) ;
vn . size = vn . len * sizeof ( fsw_u16 ) ;
vn . data = ck - > nodeName . unicode ;
fsw_strfree ( & vol - > g . label ) ;
status = fsw_strdup_coerce ( & vol - > g . label , FSW_STRING_TYPE_UTF16 , & vn ) ;
DBG ( " Volume label: " ) ;
for ( i = 0 ; i < vn . len ; i + + ) {
DBG ( " %02x " , ( ( fsw_u16 * ) vol - > g . label . data ) [ i ] ) ;
}
DBG ( " \n " ) ;
CHECK ( status ) ;
}
}
}
/* Read extents overflow file */
r = fsw_hfs_read_file ( vol - > extents_tree . file ,
sizeof ( BTNodeDescriptor ) ,
sizeof ( BTHeaderRec ) , ( fsw_u8 * ) & tree_header ) ;
if ( r ! = sizeof ( BTHeaderRec ) ) {
status = FSW_VOLUME_CORRUPTED ;
DBG ( " extents overflow file CORRUPTED \n " ) ;
break ;
}
vol - > extents_tree . root_node = be32_to_cpu ( tree_header . rootNode ) ;
vol - > extents_tree . node_size = be16_to_cpu ( tree_header . nodeSize ) ;
rv = FSW_SUCCESS ;
break ;
} while ( 1 ) ;
# undef CHECK
if ( buffer ! = NULL ) {
fsw_block_release ( vol , blockno , buffer ) ;
}
return rv ;
}
/**
* Free the volume data structure . Called by the core after an unmount or after
* an unsuccessful mount to release the memory used by the file system type specific
* part of the volume structure .
*/
static void fsw_hfs_volume_free ( struct fsw_hfs_volume * vol )
{
if ( vol - > primary_voldesc ) {
fsw_free ( vol - > primary_voldesc ) ;
vol - > primary_voldesc = NULL ;
}
if ( vol - > catalog_tree . file ) {
fsw_dnode_release ( ( struct fsw_dnode * ) ( vol - > catalog_tree . file ) ) ;
vol - > catalog_tree . file = NULL ;
}
if ( vol - > extents_tree . file ) {
fsw_dnode_release ( ( struct fsw_dnode * ) ( vol - > extents_tree . file ) ) ;
vol - > extents_tree . file = NULL ;
}
}
/**
* Get in - depth information on a volume .
*/
static fsw_status_t fsw_hfs_volume_stat ( struct fsw_hfs_volume * vol , struct fsw_volume_stat * sb )
{
sb - > total_bytes = LShiftU64 ( be32_to_cpu ( vol - > primary_voldesc - > totalBlocks ) , vol - > block_size_shift ) ;
sb - > free_bytes = LShiftU64 ( be32_to_cpu ( vol - > primary_voldesc - > freeBlocks ) , vol - > block_size_shift ) ;
return FSW_SUCCESS ;
}
/**
* Get full information on a dnode from disk . This function is called by the core
* whenever it needs to access fields in the dnode structure that may not
* be filled immediately upon creation of the dnode .
*/
static fsw_status_t fsw_hfs_dnode_fill ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno )
{
// Print(L"dnode_fill\n");
return FSW_SUCCESS ;
}
/**
* Free the dnode data structure . Called by the core when deallocating a dnode
* structure to release the memory used by the file system type specific part
* of the dnode structure .
*/
static void fsw_hfs_dnode_free ( struct fsw_hfs_volume * vol , struct fsw_hfs_dnode * dno )
{
}
static fsw_u32 mac_to_posix ( fsw_u32 mac_time )
{
/* Mac time is 1904 year based */
return mac_time ? mac_time - 2082844800 : 0 ;
}
/**
* Get in - depth information on a dnode . The core makes sure that fsw_hfs_dnode_fill
* has been called on the dnode before this function is called . Note that some
* data is not directly stored into the structure , but passed to a host - specific
* callback that converts it to the host - specific format .
*/
static fsw_status_t fsw_hfs_dnode_stat ( struct fsw_hfs_volume * vol ,
struct fsw_hfs_dnode * dno ,
struct fsw_dnode_stat_str * sb )
{
sb - > used_bytes = dno - > used_bytes ;
// DBG("dnode_stat used_bytes =%ld\n", sb->used_bytes);
sb - > store_time_posix ( sb , FSW_DNODE_STAT_CTIME , mac_to_posix ( dno - > ctime ) ) ;
sb - > store_time_posix ( sb , FSW_DNODE_STAT_MTIME , mac_to_posix ( dno - > mtime ) ) ;
sb - > store_time_posix ( sb , FSW_DNODE_STAT_ATIME , 0 ) ;
sb - > store_attr_posix ( sb , 0700 ) ;
return FSW_SUCCESS ;
}
static int
fsw_hfs_find_block ( HFSPlusExtentRecord * exts ,
fsw_u32 * lbno ,
fsw_u32 * pbno )
{
int i ;
fsw_u32 cur_lbno = * lbno ;
// DBG("search for lbno=%d\n", *lbno);
for ( i = 0 ; i < 8 ; i + + ) {
fsw_u32 start = be32_to_cpu ( ( * exts ) [ i ] . startBlock ) ;
fsw_u32 count = be32_to_cpu ( ( * exts ) [ i ] . blockCount ) ;
if ( cur_lbno < count ) {
* pbno = start + cur_lbno ;
// DBG("found block %d in segment %d\n", *pbno, i);
return 1 ;
}
cur_lbno - = count ;
}
* lbno = cur_lbno ;
return 0 ;
}
//
// Find record offset, numbering starts from the end
//
static fsw_u32
fsw_hfs_btree_recoffset ( struct fsw_hfs_btree * btree ,
BTNodeDescriptor * node ,
fsw_u32 index )
{ //+
fsw_u8 * cnode = ( fsw_u8 * ) node ;
fsw_u16 * recptr ;
fsw_u32 offset = ( fsw_u32 ) be16_to_cpu ( node - > numRecords ) ;
if ( index > = offset ) {
DBG ( " index is too large %d > %d for this node \n " , index , offset ) ;
return 0 ; //impossible for good run
}
recptr = ( fsw_u16 * ) ( cnode + btree - > node_size - index * 2 - 2 ) ;
offset = ( fsw_u32 ) be16_to_cpu ( * recptr ) ;
return offset ;
}
//
// Pointer to the key record by index inside the node
//
static BTreeKey *
fsw_hfs_btree_rec ( struct fsw_hfs_btree * btree ,
BTNodeDescriptor * node ,
fsw_u32 index )
{ //+
fsw_u8 * cnode = ( fsw_u8 * ) node ;
fsw_u32 offset ;
offset = fsw_hfs_btree_recoffset ( btree , node , index ) ;
if ( offset < sizeof ( BTNodeDescriptor ) | | offset > btree - > node_size ) {
DBG ( " wrong offset %d \n " , offset ) ;
return NULL ;
}
return ( BTreeKey * ) ( cnode + offset ) ;
}
//
// returns pointer to data of the index record
//
static fsw_u32
fsw_hfs_btree_next_node ( BTreeKey * currkey )
{ //+
fsw_u32 * pointer ;
pointer = ( fsw_u32 * ) ( ( char * ) currkey + be16_to_cpu ( currkey - > length16 ) + 2 ) ;
return be32_to_cpu_ua ( pointer ) ;
}
static fsw_status_t
fsw_hfs_btree_search ( struct fsw_hfs_btree * btree ,
BTreeKey * key ,
int ( * compare_keys ) ( BTreeKey * key1 , BTreeKey * key2 ) ,
BTNodeDescriptor * * result ,
fsw_u32 * key_offset )
{
fsw_status_t status ;
fsw_u8 * buffer = NULL ;
BTNodeDescriptor * node ;
fsw_u32 currnode ;
fsw_u32 recnum ;
# ifdef VBOXHFS_BTREE_BINSEARCH
fsw_u32 lower , upper ;
# endif
currnode = btree - > root_node ; //always from root?
status = fsw_alloc ( btree - > node_size , & buffer ) ;
if ( status ! = FSW_SUCCESS ) {
fsw_free ( buffer ) ;
return status ;
}
node = ( BTNodeDescriptor * ) buffer ;
for ( ; ; ) { //node cycle
fsw_s32 cmp = 0 ;
2021-02-10 13:32:07 +01:00
// int match;
2019-09-03 11:58:42 +02:00
fsw_u32 count ;
BTreeKey * currkey ;
2021-02-10 13:32:07 +01:00
// match = 0;
2019-09-03 11:58:42 +02:00
/* Read a node */
if ( ( fsw_u32 ) fsw_hfs_read_file ( btree - > file ,
MultU64x32 ( currnode , btree - > node_size ) ,
btree - > node_size , buffer ) ! =
btree - > node_size ) {
status = FSW_VOLUME_CORRUPTED ;
DBG ( " differ node size while read file \n " ) ;
break ;
}
//check record0 pointing to end of descriptor
if ( be16_to_cpu ( * ( fsw_u16 * ) ( buffer + btree - > node_size - 2 ) ) ! =
sizeof ( BTNodeDescriptor ) ) {
status = FSW_VOLUME_CORRUPTED ;
DBG ( " differ BTNodeDescriptor \n " ) ;
break ;
}
count = be16_to_cpu ( node - > numRecords ) ;
# ifndef VBOXHFS_BTREE_BINSEARCH
//preliminary test for last record. May be not needed look all for this node
currkey = fsw_hfs_btree_rec ( btree , node , count - 1 ) ;
cmp = compare_keys ( currkey , key ) ;
if ( ( cmp = = 0 ) & & ( node - > kind = = kBTLeafNode ) ) {
/* Found! */
* result = node ;
* key_offset = recnum ;
hardlink = 0 ;
return FSW_SUCCESS ;
} else if ( cmp < 0 ) { //all records has key less than searched
if ( node - > kind = = kBTIndexNode ) {
currnode = fsw_hfs_btree_next_node ( currkey ) ;
} else if ( node - > fLink ) {
currnode = be32_to_cpu ( node - > fLink ) ;
} else {
status = FSW_NOT_FOUND ;
break ;
}
continue ;
} //else search for all records
for ( recnum = 0 ; recnum < count ; recnum + + ) {
currkey = fsw_hfs_btree_rec ( btree , node , recnum ) ;
cmp = compare_keys ( currkey , key ) ; //fsw_hfs_cmpi_catkey
// DBG(": currnode %d rec=%d count %d cmp=%d kind=%d\n",
// currnode, recnum, count, cmp, node->kind);
/* Leaf node. */
if ( node - > kind = = kBTLeafNode ) {
if ( cmp = = 0 ) {
/* Found! */
* result = node ;
* key_offset = recnum ;
hardlink = 0 ;
return FSW_SUCCESS ;
}
} else if ( node - > kind = = kBTIndexNode ) {
if ( cmp > 0 )
break ;
currnode = fsw_hfs_btree_next_node ( currkey ) ; //largest that <= search
}
}
if ( node - > kind = = kBTLeafNode ) {
status = FSW_NOT_FOUND ;
break ;
}
if ( cmp < = 0 & & node - > fLink ) {
currnode = be32_to_cpu ( node - > fLink ) ;
}
# else
/* Perform binary search */
lower = 0 ;
upper = count - 1 ;
currkey = NULL ;
if ( count = = 0 ) {
status = FSW_NOT_FOUND ;
goto done ;
}
while ( lower < = upper ) {
recnum = ( lower + upper ) / 2 ;
currkey = fsw_hfs_btree_rec ( btree , node , recnum ) ;
cmp = compare_keys ( currkey , key ) ; //fsw_hfs_cmpi_catkey
// DBG(": currnode %d lower/recnum/upper %d/%d/%d (%d) cmp=%d kind=%d\n",
// currnode, lower, recnum, upper, count, cmp, node->kind);
if ( cmp > 0 ) {
upper = recnum - 1 ;
} else if ( cmp < 0 ) {
lower = recnum + 1 ;
} else if ( cmp = = 0 ) {
if ( node - > kind = = kBTLeafNode ) {
// Found!
* result = node ;
* key_offset = recnum ;
return FSW_SUCCESS ;
} else if ( node - > kind = = kBTIndexNode ) {
currnode = fsw_hfs_btree_next_node ( currkey ) ;
break ;
}
}
}
if ( cmp > 0 ) {
currkey = fsw_hfs_btree_rec ( btree , node , upper ) ;
}
if ( node - > kind = = kBTIndexNode & & currkey ! = NULL ) {
currnode = fsw_hfs_btree_next_node ( currkey ) ;
// DBG(": candidate for the next currnode is %d\n", currnode);
}
else {
status = FSW_NOT_FOUND ;
break ;
}
# endif
}
# ifdef VBOXHFS_BTREE_BINSEARCH
done :
# endif
if ( buffer ! = NULL & & status ! = FSW_SUCCESS )
fsw_free ( buffer ) ;
return status ;
}
static void
fill_fileinfo (
struct fsw_hfs_volume * vol ,
HFSPlusCatalogKey * key ,
file_info_t * finfo
)
{
fsw_u8 * base ;
fsw_u16 rec_type ;
fsw_u16 flags ;
/* for plain HFS "-(keySize & 1)" would be needed */
base = ( fsw_u8 * ) key + be16_to_cpu ( key - > keyLength ) + 2 ;
rec_type = be16_to_cpu ( * ( fsw_u16 * ) base ) ;
/** @todo: read additional info */
switch ( rec_type ) {
case kHFSPlusFolderRecord :
{
HFSPlusCatalogFolder * info = ( HFSPlusCatalogFolder * ) base ;
finfo - > id = be32_to_cpu_ua ( ( fsw_u32 * ) ( void * ) & info - > folderID ) ;
finfo - > type = FSW_DNODE_TYPE_DIR ;
/* @todo: return number of elements, maybe use smth else */
finfo - > size = be32_to_cpu ( info - > valence ) ; //this is wrong because of deleted entries
finfo - > used = be32_to_cpu ( info - > valence ) ;
finfo - > ctime = be32_to_cpu ( info - > createDate ) ;
finfo - > mtime = be32_to_cpu ( info - > contentModDate ) ;
// DBG("number of folder entries=%d\n", finfo->size);
// DBG(" fileMode=%x\n", finfo->fileMode);
// DBG(" flags=%x\n", be16_to_cpu(info->flags));
// DBG(" folderID=%d\n\n", finfo->id);
break ;
}
case kHFSPlusFileThreadRecord :
{ //never happen
HFSPlusCatalogThread * info = ( HFSPlusCatalogThread * ) base ;
finfo - > id = be32_to_cpu_ua ( ( fsw_u32 * ) ( void * ) & info - > parentID ) ;
finfo - > type = FSW_DNODE_TYPE_SPECIAL ;
// DBG(" CatalogThread\n");
// DBG(" folderID=%d\n\n", finfo->id);
}
case kHFSPlusFileRecord :
{
HFSPlusCatalogFile * info = ( HFSPlusCatalogFile * ) base ;
file_info_t tmp_finfo ;
fsw_status_t status ;
finfo - > id = be32_to_cpu ( info - > fileID ) ;
flags = be16_to_cpu ( info - > flags ) ;
finfo - > creator = be32_to_cpu ( info - > userInfo . fdCreator ) ;
finfo - > crtype = be32_to_cpu ( info - > userInfo . fdType ) ;
finfo - > type = FSW_DNODE_TYPE_FILE ;
finfo - > ilink = 0 ;
finfo - > isfilelink = 0 ;
finfo - > isdirlink = 0 ;
DBG ( " file creator=%x crtype=%x \n " , finfo - > creator , finfo - > crtype ) ;
/* Is the file any kind of link? */
if ( ( finfo - > creator = = kSymLinkCreator & & finfo - > crtype = = kSymLinkFileType ) | |
( finfo - > creator = = kHFSPlusCreator & & finfo - > crtype = = kHardLinkFileType ) ) {
// if (finfo->crtype == kSymLinkFileType) {
finfo - > type = FSW_DNODE_TYPE_SYMLINK ;
// }
finfo - > isfilelink = 1 ;
} else if ( ( flags & kHFSHasLinkChainMask ) & &
( finfo - > crtype = = kHFSAliasType ) & &
( finfo - > creator = = kHFSAliasCreator ) ) {
DBG ( " found dirlink, fileID=%d \n " , finfo - > id ) ;
finfo - > isdirlink = 0 ; //do nothing for now
}
if ( ( finfo - > isfilelink | | finfo - > isdirlink ) /* && !(flags & HFS_LOOKUP_HARDLINK)*/ ) {
finfo - > ilink = be32_to_cpu_ua ( ( fsw_u32 * ) ( void * ) & info - > bsdInfo . special . iNodeNum ) ;
if ( finfo - > type ! = FSW_DNODE_TYPE_SYMLINK ) {
status = fsw_hfs_dir_lookup_id ( vol , finfo - > ilink , & tmp_finfo ) ;
if ( ! status ) {
// DBG("hardlink resolved to fileID=%d\n", finfo->ilink);
finfo - > id = finfo - > ilink ;
finfo - > ilink = 0 ;
finfo - > size = tmp_finfo . size ;
finfo - > used = tmp_finfo . used ;
finfo - > ctime = be32_to_cpu ( info - > createDate ) ;
finfo - > mtime = be32_to_cpu ( info - > contentModDate ) ;
fsw_memcpy ( & finfo - > extents , & tmp_finfo . extents ,
sizeof ( HFSPlusExtentRecord ) ) ;
break ;
}
}
}
/* DBG("file type=");
if ( finfo - > isfilelink & & finfo - > crtype = = kHardLinkFileType ) {
DBG ( " hardlink to fileID=%d \n " , finfo - > ilink ) ;
} else if ( finfo - > isdirlink ) {
DBG ( " dirlink \n " ) ;
} else {
switch ( finfo - > type ) {
case FSW_DNODE_TYPE_SYMLINK :
DBG ( " symlink \n " ) ;
break ;
case FSW_DNODE_TYPE_FILE :
DBG ( " file \n " ) ;
break ;
default :
DBG ( " xxx \n " ) ;
break ;
}
} */
finfo - > fileMode = be16_to_cpu ( info - > bsdInfo . fileMode ) ;
// DBG(" fileMode=%x\n", finfo->fileMode);
// DBG(" flags=%x\n", flags);
// DBG(" fileID=%d\n", finfo->id);
finfo - > size = be64_to_cpu_ua ( ( fsw_u64 * ) ( void * ) & info - > dataFork . logicalSize ) ;
finfo - > used =
LShiftU64 ( ( fsw_u64 ) be32_to_cpu_ua ( ( fsw_u32 * ) ( void * ) & info - > dataFork . totalBlocks ) ,
vol - > block_size_shift ) ;
// DBG("file size=%ld, used=%ld\n\n", finfo->size, finfo->used);
// if (finfo->size == 0) {
//TODO - how to find the real file?
// DBG("file info:\n");
// DBG("\tdataFork.logicalSize=%d\n", info->dataFork.logicalSize);
// DBG("\tdataFork.totalBlocks=%d\n", info->dataFork.totalBlocks);
// DBG("\tresourceFork.logicalSize=%d\n", info->resourceFork.logicalSize);
// DBG("\tresourceFork.totalBlocks=%d\n", info->resourceFork.totalBlocks);
// }
finfo - > ctime = be32_to_cpu ( info - > createDate ) ;
finfo - > mtime = be32_to_cpu ( info - > contentModDate ) ;
fsw_memcpy ( & finfo - > extents , & info - > dataFork . extents ,
sizeof ( HFSPlusExtentRecord ) ) ;
break ;
}
default :
finfo - > type = FSW_DNODE_TYPE_UNKNOWN ;
break ;
}
}
typedef struct {
fsw_u32 cur_pos ; /* current position */
fsw_u32 parent ;
struct fsw_hfs_volume * vol ;
struct fsw_shandle * shandle ; /* this one track iterator's state */
file_info_t file_info ;
} visitor_parameter_t ;
///
// at input: record = fsw_hfs_btree_rec (btree, node, i) - having
// param contains file name and folder ID - wishing
// for directory list name=0
//
static int
fsw_hfs_btree_visit_node ( BTreeKey * record , void * param )
{
visitor_parameter_t * vp = ( visitor_parameter_t * ) param ;
fsw_u8 * base = ( fsw_u8 * ) record - > rawData + be16_to_cpu ( record - > length16 ) + 2 ; //data next to key
fsw_u16 rec_type = be16_to_cpu ( * ( fsw_u16 * ) base ) ;
HFSPlusCatalogKey * cat_key = ( HFSPlusCatalogKey * ) record ;
fsw_u16 name_len ;
fsw_u16 * name_ptr ;
fsw_u32 i ;
struct fsw_string * file_name ;
i = be32_to_cpu ( cat_key - > parentID ) ;
if ( vp - > parent ! = 0 & & i ! = vp - > parent ) { //in some cases we wish no check parentID
// DBG("cat ID=%x while parentID=%x\n", i, vp->parent);
// vp->shandle->pos++;
return - 1 ;
}
/* not smth we care about */
if ( vp - > shandle - > pos ! = vp - > cur_pos + + )
return 0 ;
fill_fileinfo ( vp - > vol , cat_key , & vp - > file_info ) ;
switch ( rec_type ) {
case kHFSPlusFolderThreadRecord :
case kHFSPlusFileThreadRecord :
{
// DBG("skip thread\n");
vp - > shandle - > pos + + ;
return 0 ;
}
default :
break ;
}
name_len = be16_to_cpu ( cat_key - > nodeName . length ) ;
file_name = vp - > file_info . name ;
file_name - > len = name_len ;
fsw_memdup ( & file_name - > data , & cat_key - > nodeName . unicode [ 0 ] , 2 * name_len ) ;
file_name - > size = 2 * name_len ;
file_name - > type = FSW_STRING_TYPE_UTF16 ;
name_ptr = ( fsw_u16 * ) file_name - > data ;
for ( i = 0 ; i < name_len ; i + + ) {
name_ptr [ i ] = be16_to_cpu ( name_ptr [ i ] ) ;
}
vp - > shandle - > pos + + ;
/* DBG(" visited file=", name_ptr);
for ( i = 0 ; i < name_len ; i + + ) {
DBG ( " %c " , name_ptr [ i ] ? name_ptr [ i ] : L ' @ ' ) ;
}
DBG ( " \n " ) ; */
return 1 ;
}
static fsw_status_t
fsw_hfs_btree_iterate_node ( struct fsw_hfs_btree * btree ,
BTNodeDescriptor * first_node ,
fsw_u32 first_rec ,
int ( * callback ) ( BTreeKey * record , void * param ) ,
void * param )
{
fsw_status_t status ;
/* We modify node, so make a copy */
BTNodeDescriptor * node = first_node ;
fsw_u8 * buffer = NULL ;
if ( ! btree | | ! btree - > node_size ) {
DBG ( " no node_size \n " ) ;
return FSW_NOT_FOUND ;
}
status = fsw_alloc ( btree - > node_size , & buffer ) ;
if ( status ) {
if ( buffer ! = NULL )
fsw_free ( buffer ) ;
return status ;
}
for ( ; ; ) {
fsw_u32 i ;
fsw_u32 count = be16_to_cpu ( node - > numRecords ) ;
fsw_u32 next_node ;
// DBG("first_rec=%d, count=%d, kind=%d\n", first_rec, count, node->kind);
status = FSW_NOT_FOUND ;
// Iterate over all records in this node.
i = first_rec ;
while ( i < count ) {
int rv = callback ( fsw_hfs_btree_rec ( btree , node , i ) , param ) ; //fsw_hfs_btree_visit_node
// DBG("test record %d, status=%d\n", i, rv);
if ( rv = = 1 ) {
status = FSW_SUCCESS ;
goto done ;
} else if ( rv = = - 1 ) {
// DBG("tested record %d, status=%d\n", i, rv);
status = FSW_NOT_FOUND ; //it means the node has other owner
goto done ; //no need to test more
}
/* if callback returned 0 - continue */
// status = FSW_SUCCESS;
i + + ;
}
next_node = be32_to_cpu ( node - > fLink ) ;
// DBG("next_node=%d\n", next_node);
if ( ! next_node ) {
status = FSW_NOT_FOUND ;
// DBG("FSW_NOT_FOUND at next_node\n");
break ;
}
if ( ( fsw_u32 ) fsw_hfs_read_file ( btree - > file , next_node * btree - > node_size ,
btree - > node_size , buffer ) ! = btree - > node_size ) {
status = FSW_VOLUME_CORRUPTED ;
// DBG("differ node size btree->node_size=%d\n", btree->node_size);
break ;
}
node = ( BTNodeDescriptor * ) buffer ;
first_rec = 0 ;
}
done :
if ( buffer )
fsw_free ( buffer ) ;
return status ;
}
static int
fsw_hfs_cmp_extkey ( BTreeKey * key1 , BTreeKey * key2 )
{
HFSPlusExtentKey * ekey1 = ( HFSPlusExtentKey * ) key1 ;
HFSPlusExtentKey * ekey2 = ( HFSPlusExtentKey * ) key2 ;
int result ;
/* First key is read from the FS data, second is in-memory in CPU endianess */
result = be32_to_cpu ( ekey1 - > fileID ) - ekey2 - > fileID ;
if ( result )
return result ;
result = ekey1 - > forkType - ekey2 - > forkType ;
if ( result )
return result ;
result = be32_to_cpu ( ekey1 - > startBlock ) - ekey2 - > startBlock ;
return result ;
}
static int
fsw_hfs_cmp_catkey ( BTreeKey * key1 , BTreeKey * key2 )
{
HFSPlusCatalogKey * ckey1 = ( HFSPlusCatalogKey * ) key1 ;
HFSPlusCatalogKey * ckey2 = ( HFSPlusCatalogKey * ) key2 ;
int apos , bpos , lc ;
fsw_u16 ac , bc ;
fsw_u32 parentId1 ;
int key1Len ;
fsw_u16 * p1 ;
fsw_u16 * p2 ;
parentId1 = be32_to_cpu ( ckey1 - > parentID ) ;
if ( parentId1 > ckey2 - > parentID )
return 1 ;
if ( parentId1 < ckey2 - > parentID )
return - 1 ;
p1 = & ckey1 - > nodeName . unicode [ 0 ] ;
p2 = & ckey2 - > nodeName . unicode [ 0 ] ;
key1Len = be16_to_cpu ( ckey1 - > nodeName . length ) ;
apos = bpos = 0 ;
while ( 1 ) {
/* get next valid character from ckey1 */
for ( lc = 0 ; lc = = 0 & & apos < key1Len ; apos + + ) {
ac = be16_to_cpu ( p1 [ apos ] ) ;
lc = ac ;
}
ac = ( fsw_u16 ) lc ;
/* get next valid character from ckey2 */
for ( lc = 0 ; lc = = 0 & & bpos < ckey2 - > nodeName . length ; bpos + + ) {
bc = p2 [ bpos ] ;
lc = bc ;
}
bc = ( fsw_u16 ) lc ;
if ( ac ! = bc | | ( ac = = 0 & & bc = = 0 ) )
return ac - bc ;
}
}
static int
fsw_hfs_cmpi_catkey ( BTreeKey * key1 , BTreeKey * key2 )
{
HFSPlusCatalogKey * ckey1 = ( HFSPlusCatalogKey * ) key1 ;
HFSPlusCatalogKey * ckey2 = ( HFSPlusCatalogKey * ) key2 ;
int apos , bpos , lc = 0 ;
fsw_u16 ac , bc ;
fsw_u32 parentId1 ;
int key1Len ;
int key2Len ;
fsw_u16 * p1 ;
fsw_u16 * p2 ;
parentId1 = be32_to_cpu_ua ( ( fsw_u32 * ) ( ( ( UINT8 * ) ckey1 ) + OFFSET_OF ( HFSPlusCatalogKey , parentID ) ) ) ; // MSC warns about a possibly unaligned result of '&' (C4366)
// if (hardlink) {
// DBG("parents: %d <-> %d\n", parentId1, ckey2->parentID);
// }
if ( parentId1 > ckey2 - > parentID ) {
return 1 ;
} else if ( parentId1 < ckey2 - > parentID ) {
return - 1 ;
} else {
key1Len = be16_to_cpu ( ckey1 - > nodeName . length ) ;
key2Len = ckey2 - > nodeName . length ; //it is constructed so CPU endiness
// if (hardlink) {
// DBG("keylen: %d <-> %d\n", key1Len, key2Len);
// }
if ( key1Len = = 0 | | key2Len = = 0 ) {
return key1Len - key2Len ;
}
p1 = & ckey1 - > nodeName . unicode [ 0 ] ;
p2 = & ckey2 - > nodeName . unicode [ 0 ] ;
/* if (hardlink) {
DBG ( " name: " ) ;
for ( apos = 0 ; apos < key1Len ; apos + + ) {
ac = be16_to_cpu ( p1 [ apos ] ) ;
DBG ( " %c " , ac ? ac : L ' @ ' ) ;
}
DBG ( " : " ) ;
for ( apos = 0 ; apos < key2Len ; apos + + ) {
ac = p2 [ apos ] ;
DBG ( " %c " , ac ? ac : L ' @ ' ) ;
}
DBG ( " : \n " ) ;
}
*/
apos = bpos = 0 ;
while ( 1 ) {
/* get next valid character from ckey1 */
for ( lc = 0 ; lc = = 0 & & apos < key1Len ; apos + + ) {
// for (lc = 0; apos < key1Len; apos++) {
ac = be16_to_cpu ( p1 [ apos ] ) ;
lc = ac ? fsw_to_lower ( ac ) : 0xFFFF ;
}
ac = ( fsw_u16 ) lc ;
/* get next valid character from ckey2 */
for ( lc = 0 ; lc = = 0 & & bpos < key2Len ; bpos + + ) {
// for (lc = 0; bpos < key2Len; bpos++) {
bc = p2 [ bpos ] ;
lc = bc ? fsw_to_lower ( bc ) : 0xFFFF ; ;
}
bc = ( fsw_u16 ) lc ;
// if (hardlink) {
// DBG("compare: %c:%c\n", ac?ac:L'@', bc?bc:L'@');
// }
if ( ac ! = bc ) {
break ;
}
if ( bpos = = key1Len ) {
return ( key1Len - key2Len ) ;
}
}
if ( ac = = bc )
return 0 ;
else if ( ac < bc )
return - 1 ;
else
return 1 ;
}
}
//
// helper function to compare fileID with node
// key1 is catalog key, param contains FileID to find in "parent" field
//
static int
fsw_hfs_cmp_id ( BTreeKey * key1 , void * param )
{
visitor_parameter_t * vp = ( visitor_parameter_t * ) param ;
HFSPlusCatalogFile * crec1 = ( HFSPlusCatalogFile * ) ( ( char * ) key1 + be16_to_cpu ( key1 - > length16 ) ) ;
fsw_u32 fileID = be32_to_cpu ( crec1 - > fileID ) ;
fsw_u32 rec_type = be16_to_cpu ( crec1 - > recordType ) ;
if ( ( rec_type ! = kHFSPlusFileRecord ) & &
( rec_type ! = kHFSPlusFolderRecord ) ) {
DBG ( " bad record type? =%d fileID=%d \n " , rec_type , fileID ) ;
return 2 ;
}
if ( fileID ! = vp - > parent ) {
return 0 ;
}
return 1 ;
}
/**
* Retrieve file data mapping information . This function is called by the core when
* fsw_shandle_read needs to know where on the disk the required piece of the file ' s
* data can be found . The core makes sure that fsw_hfs_dnode_fill has been called
* on the dnode before . Our task here is to get the physical disk block number for
* the requested logical block number .
*/
static fsw_status_t fsw_hfs_get_extent ( struct fsw_hfs_volume * vol ,
struct fsw_hfs_dnode * dno ,
struct fsw_extent * extent )
{
fsw_status_t status ;
fsw_u32 lbno ;
HFSPlusExtentRecord * exts ;
BTNodeDescriptor * node = NULL ;
struct HFSPlusExtentKey * key ;
struct HFSPlusExtentKey overflowkey ;
fsw_u32 ptr ;
fsw_u32 phys_bno ;
extent - > type = FSW_EXTENT_TYPE_PHYSBLOCK ;
extent - > log_count = 1 ;
lbno = extent - > log_start ;
/* we only care about data forks atm, do we? */
exts = & dno - > extents ;
while ( 1 ) {
if ( fsw_hfs_find_block ( exts , & lbno , & phys_bno ) ) {
extent - > phys_start = phys_bno + vol - > emb_block_off ;
status = FSW_SUCCESS ;
break ;
}
/* Find appropriate overflow record */
overflowkey . forkType = 0 ; //data fork
overflowkey . fileID = dno - > g . dnode_id ;
overflowkey . startBlock = extent - > log_start - lbno ;
if ( node ! = NULL ) {
fsw_free ( node ) ;
node = NULL ;
}
status = fsw_hfs_btree_search ( & vol - > extents_tree ,
( BTreeKey * ) & overflowkey ,
fsw_hfs_cmp_extkey ,
& node , & ptr ) ;
if ( status ) {
break ;
}
key = ( struct HFSPlusExtentKey * ) fsw_hfs_btree_rec ( & vol - > extents_tree , node , ptr ) ;
exts = ( HFSPlusExtentRecord * ) ( key + 1 ) ;
}
if ( node ! = NULL )
fsw_free ( node ) ;
return status ;
}
static fsw_status_t
create_hfs_dnode ( struct fsw_hfs_dnode * dno ,
file_info_t * file_info ,
struct fsw_hfs_dnode * * child_dno_out )
{
fsw_status_t status ;
struct fsw_hfs_dnode * baby = NULL ;
if ( ! file_info | | ! child_dno_out ) {
return FSW_NOT_FOUND ;
}
status = fsw_dnode_create ( dno , file_info - > id , file_info - > type ,
file_info - > name , & baby ) ;
if ( status | | ! baby )
return status ;
baby - > g . size = file_info - > size ;
baby - > used_bytes = file_info - > used ;
baby - > ctime = file_info - > ctime ;
baby - > mtime = file_info - > mtime ;
/* Fill-in extents info */
if ( file_info - > type = = FSW_DNODE_TYPE_FILE ) {
fsw_memcpy ( baby - > extents , & file_info - > extents , sizeof ( file_info - > extents ) ) ;
}
// Fill-in link file info
if ( file_info - > isfilelink ) {
baby - > creator = file_info - > creator ;
baby - > crtype = file_info - > crtype ;
baby - > ilink = file_info - > ilink ;
fsw_memcpy ( baby - > extents , & file_info - > extents , sizeof file_info - > extents ) ;
}
* child_dno_out = baby ;
return FSW_SUCCESS ;
}
/**
* Lookup a directory ' s child dnode by name . This function is called on a directory
* to retrieve the directory entry with the given name . A dnode is constructed for
* this entry and returned . The core makes sure that fsw_hfs_dnode_fill has been called
* and the dnode is actually a directory .
*/
static fsw_status_t fsw_hfs_dir_lookup ( struct fsw_hfs_volume * vol ,
struct fsw_hfs_dnode * dno ,
struct fsw_string * lookup_name ,
struct fsw_hfs_dnode * * child_dno_out )
{
fsw_status_t status ;
HFSPlusCatalogKey catkey ;
fsw_u32 ptr ;
// fsw_u16 rec_type;
BTNodeDescriptor * node = NULL ;
struct fsw_string rec_name ;
int free_data = 0 ; //, i;
HFSPlusCatalogKey * file_key ;
file_info_t file_info ;
fsw_memzero ( & file_info , sizeof ( file_info_t ) ) ;
file_info . name = & rec_name ;
catkey . parentID = dno - > g . dnode_id ;
catkey . nodeName . length = ( fsw_u16 ) lookup_name - > len ;
// no need to allocate anything
if ( lookup_name - > type = = FSW_STRING_TYPE_UTF16 ) {
fsw_memcpy ( catkey . nodeName . unicode , lookup_name - > data , lookup_name - > size ) ;
// rec_name = *lookup_name;
fsw_memcpy ( & rec_name , lookup_name , sizeof ( struct fsw_string ) ) ;
} else {
status = fsw_strdup_coerce ( & rec_name , FSW_STRING_TYPE_UTF16 , lookup_name ) ;
// nothing allocated so far
if ( status )
goto done ;
free_data = 1 ;
fsw_memcpy ( catkey . nodeName . unicode , rec_name . data , rec_name . size ) ;
}
catkey . keyLength = ( fsw_u16 ) ( 6 + rec_name . size ) ;
status = fsw_hfs_btree_search ( & vol - > catalog_tree ,
( BTreeKey * ) & catkey ,
vol - > case_sensitive ?
fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey ,
& node , & ptr ) ;
if ( status ) {
// DBG("fsw_hfs_btree_search dir lookup status %a\n", fsw_errors[status]);
goto done ;
}
file_key = ( HFSPlusCatalogKey * ) fsw_hfs_btree_rec ( & vol - > catalog_tree , node , ptr ) ;
fill_fileinfo ( vol , file_key , & file_info ) ;
status = create_hfs_dnode ( dno , & file_info , child_dno_out ) ; //&tmp_dno_out); //
// if (status) {
// DBG("create_hfs_dnode status %a\n", fsw_errors[status]);
// goto done;
// }
done :
if ( node ! = NULL )
fsw_free ( node ) ;
if ( free_data )
fsw_strfree ( & rec_name ) ;
return status ;
}
/**
* Lookup a directory ' s child dnode by ID . This function is called on a directory
* to retrieve the directory entry with the fileID . A dnode is constructed for
* this entry and returned .
*/
static fsw_status_t fsw_hfs_dir_lookup_id ( struct fsw_hfs_volume * vol ,
fsw_u32 lookup_id ,
file_info_t * file_info )
{
fsw_status_t status ;
fsw_u8 * buffer = NULL ;
fsw_u32 ptr = 0 ;
visitor_parameter_t param ;
BTNodeDescriptor * node = NULL ; //root
fsw_u32 currnode = vol - > catalog_tree . root_node ;
fsw_u32 nodeSize = vol - > catalog_tree . node_size ;
// DBG("...lookup for fileID=%d\n", lookup_id);
status = fsw_alloc ( nodeSize , & buffer ) ;
if ( status ! = FSW_SUCCESS ) {
fsw_free ( buffer ) ;
goto done ;
}
node = ( BTNodeDescriptor * ) buffer ;
/* Read a node */
if ( ( fsw_u32 ) fsw_hfs_read_file ( vol - > catalog_tree . file ,
MultU64x32 ( currnode , nodeSize ) ,
nodeSize , buffer ) ! = nodeSize ) {
status = FSW_VOLUME_CORRUPTED ;
// DBG("differ node size while read file\n");
return status ;
}
fsw_memzero ( & param , sizeof ( visitor_parameter_t ) ) ;
param . parent = lookup_id ;
status = fsw_hfs_btree_iterate_node ( & vol - > catalog_tree ,
node ,
ptr ,
fsw_hfs_cmp_id ,
& param ) ;
if ( status ) {
goto done ;
}
fsw_memcpy ( file_info , & param . file_info , sizeof ( file_info_t ) ) ;
// status = create_hfs_dnode(dno, ¶m.file_info, child_dno_out); //&tmp_dno_out); //
// if (status) {
// DBG("create_hfs_dnode status %a\n", fsw_errors[status]);
// goto done;
// }
done :
if ( node ! = NULL )
fsw_free ( node ) ;
// DBG("fsw_hfs_dir_lookup_id return status %a\n", fsw_errors[status]);
return status ;
}
/**
* Get the next directory entry when reading a directory . This function is called during
* directory iteration to retrieve the next directory entry . A dnode is constructed for
* the entry and returned . The core makes sure that fsw_hfs_dnode_fill has been called
* and the dnode is actually a directory . The shandle provided by the caller is used to
* record the position in the directory between calls .
*/
static fsw_status_t fsw_hfs_dir_read ( struct fsw_hfs_volume * vol ,
struct fsw_hfs_dnode * dno ,
struct fsw_shandle * shand ,
struct fsw_hfs_dnode * * child_dno_out )
{
BTNodeDescriptor * node = NULL ;
fsw_u32 ptr ;
HFSPlusCatalogKey catkey ;
fsw_status_t status ;
visitor_parameter_t param ;
struct fsw_string rec_name ;
catkey . parentID = dno - > g . dnode_id ;
catkey . nodeName . length = 0 ;
fsw_memzero ( & param , sizeof ( param ) ) ;
rec_name . type = FSW_STRING_TYPE_EMPTY ;
param . file_info . name = & rec_name ;
param . file_info . id = 0 ;
// we are searching a node with name=0 and parentID?
status = fsw_hfs_btree_search ( & vol - > catalog_tree ,
( BTreeKey * ) & catkey ,
vol - > case_sensitive ?
fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey ,
& node , & ptr ) ;
if ( status ) {
// DBG("fsw_hfs_btree_search dir read status %a\n", fsw_errors[status]);
goto done ;
}
/* Iterator updates shand state */
param . vol = vol ;
param . shandle = shand ;
param . parent = dno - > g . dnode_id ;
if ( dno - > ilink ! = 0 ) {
param . parent = 0 ;
}
param . cur_pos = 0 ;
status = fsw_hfs_btree_iterate_node ( & vol - > catalog_tree ,
node ,
ptr ,
fsw_hfs_btree_visit_node ,
& param ) ;
if ( ! status ) {
status = create_hfs_dnode ( dno , & param . file_info , child_dno_out ) ;
// if (status) {
//never DBG("create_hfs_dnode status %a\n", fsw_errors[status]);
// }
} else {
// DBG("fsw_hfs_btree_iterate_node status %a\n", fsw_errors[status]);
}
done :
if ( node )
fsw_free ( node ) ;
fsw_strfree ( & rec_name ) ;
return status ;
}
static const char metaprefix [ ] = " / \0 \0 \0 \0 HFS+ Private Data/iNode0123456789 " ;
/**
* Get the target path of a link . This function is called when a
* link needs to be resolved . The core makes sure that the fsw_hfs_dnode_fill has been
* called on the dnode .
*
*/
static fsw_status_t fsw_hfs_readlink ( struct fsw_hfs_volume * vol ,
struct fsw_hfs_dnode * dno ,
struct fsw_string * link_target )
{
/*
* XXX : Hardlinks for directories - - it is alias to folder .
* Hex dump visual inspection of Apple hfsplus { 32 , 64 } . efi
* revealed no signs of directory hardlinks support .
finder links , not properly documented
kHFSAliasType = 0x66647270 , // 'fdrp' - finder alias for folder
kHFSAliasCreator = 0x4D414353 // 'MACS'
kHFSAliasFile = 0x616C6973 // 'alis' - finder alias for file - not implemented yet
*/
fsw_u32 sz = 0 ;
// int i;
// fsw_u16 ch;
fsw_status_t status = FSW_UNSUPPORTED ;
if ( dno - > creator = = kHFSPlusCreator & & dno - > crtype = = kHardLinkFileType ) {
# define MPRFSIZE (sizeof (metaprefix))
# define MPRFINUM (MPRFSIZE - 1 - 10)
struct fsw_string * tmp_target ;
status = fsw_alloc ( sizeof ( struct fsw_string ) , & tmp_target ) ;
if ( status ) {
DBG ( " can't alloc tmp_target \n " ) ;
}
tmp_target - > type = FSW_STRING_TYPE_ISO88591 ;
tmp_target - > size = MPRFSIZE ;
DBG ( " hfs readlink: size=%d, iLink=%d \n " , tmp_target - > size , dno - > ilink ) ;
fsw_memdup ( ( void * * ) & tmp_target - > data , ( void * ) & metaprefix [ 0 ] , tmp_target - > size ) ;
sz = ( fsw_u32 ) AsciiSPrint ( ( ( char * ) tmp_target - > data ) + MPRFINUM , 10 , " %d " , dno - > ilink ) ;
tmp_target - > len = MPRFINUM + sz ;
// DBG(" iNode name len=%d\n", tmp_target->len);
status = fsw_strdup_coerce ( link_target , vol - > g . host_string_type , tmp_target ) ;
hardlink = 1 ;
/*
for ( i = 0 ; i < tmp_target - > len ; i + + ) {
ch = ( ( fsw_u16 * ) link_target - > data ) [ i ] ;
DBG ( " %c " , ch ? ch : L ' @ ' ) ;
}
DBG ( " \n " ) ;
*/
fsw_strfree ( tmp_target ) ;
fsw_free ( tmp_target ) ;
return status ;
# undef MPRFINUM
# undef MPRFSIZE
} else if ( dno - > creator = = kSymLinkCreator & & dno - > crtype = = kSymLinkFileType ) {
hardlink = 1 ;
return fsw_dnode_readlink_data ( dno , link_target ) ;
} else if ( dno - > creator = = kHFSAliasCreator & & dno - > crtype = = kHFSAliasType ) {
CHAR16 inodename [ 48 ] ;
sz = ( fsw_u32 ) UnicodeSPrint ( inodename , 48 , L " .HFS+ Private Directory Data/dir_%d " , dno - > ilink ) ;
link_target - > type = FSW_STRING_TYPE_UTF16 ;
link_target - > size = 96 ;
link_target - > len = sz ;
fsw_memdup ( & link_target - > data , & inodename [ 0 ] , link_target - > size ) ;
hardlink = 1 ;
return FSW_SUCCESS ;
}
/* Unknown link type */
return FSW_UNSUPPORTED ;
}
// EOF