/* $Id$ */
/** @file
* fsw_hfs.c - HFS file system driver code, see
*
*
* Current limitations:
* - Doesn't support permissions
* - Complete Unicode case-insensitiveness disabled (large tables)
* - No links
* - 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;
* 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
#include "fsw_hfs.h"
#ifdef HOST_POSIX
#else
#define CONCAT(x,y) x##y
#endif
// functions
#if 0
{
int i;
for (i=0; i<len; i++)
{
}
}
#endif
struct fsw_dnode_stat *sb);
struct fsw_extent *extent);
#if 0
static fsw_status_t fsw_hfs_read_dirrec(struct fsw_shandle *shand, struct hfs_dirrec_buffer *dirrec_buffer);
#endif
struct fsw_string *link);
//
// Dispatch Table
//
sizeof(struct fsw_hfs_volume),
sizeof(struct fsw_hfs_dnode),
};
static fsw_s32
{
if (status)
return status;
if (status)
return status;
return FSW_SUCCESS;
}
/* Read data from HFS file. */
static fsw_s32
{
while (len > 0)
{
if ( next_len >= 0
if (status)
return -1;
}
return read;
}
static fsw_s32
{
fsw_s32 i;
for (i=0; i<32; i++)
{
if ((size >> i) == 0)
return i - 1;
}
BP("BUG\n");
return 0;
}
/**
* Mount an HFS+ volume. Reads the superblock and constructs the
* root directory dnode.
*/
{
struct fsw_string s;
#define CHECK(s) \
if (status) { \
break; \
}
vol->emb_block_off = 0;
do {
fsw_s32 r;
{
{
DPRINT("found HFS+\n");
}
}
else if (signature == kHFSSigWord)
{
{
DPRINT("found HFS+ inside HFS, untested\n");
/* retry */
continue;
}
else
{
DPRINT("found plain HFS, unsupported\n");
}
break;
}
else
{
break;
}
sizeof(*voldesc));
/* get volume name */
s.data = "HFS+ volume";
/* Setup catalog dnode */
/* Setup extents overflow file */
/* Setup the root dnode */
/*
* Read catalog file, we know that first record is in the first node, right after
* the node descriptor.
*/
sizeof (BTNodeDescriptor),
if (r <= 0)
{
break;
}
(signature == kHFSXSigWord) &&
/* Read extents overflow file */
sizeof (BTNodeDescriptor),
if (r <= 0)
{
break;
}
rv = FSW_SUCCESS;
} while (0);
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.
*/
{
if (vol->primary_voldesc)
{
}
}
/**
* Get in-depth information on a volume.
*/
{
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.
*/
{
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.
*/
{
}
{
/* Mac time is 1904 year based */
}
/**
* 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.
*/
struct fsw_hfs_dnode *dno,
struct fsw_dnode_stat *sb)
{
return FSW_SUCCESS;
}
static int
{
int i;
for (i = 0; i < 8; i++)
{
{
return 1;
}
}
return 0;
}
/* Find record offset, numbering starts from the end */
static fsw_u32
{
return be16_to_cpu(*recptr);
}
/* Pointer to the key inside node */
static BTreeKey *
{
}
static fsw_status_t
{
if (status)
return status;
while (1)
{
int cmp = 0;
int match;
match = 0;
/* Read a node. */
{
break;
}
BP("corrupted node\n");
#if 1
{
//fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
/* Leaf node. */
{
if (cmp == 0)
{
/* Found! */
*key_offset = rec;
goto done;
}
}
{
if (cmp > 0)
break;
+ 2);
match = 1;
}
}
{
goto readnode;
}
else if (!match)
{
break;
}
#else
/* Perform binary search */
if (count == 0)
{
goto done;
}
{
if (cmp == 0)
{
/* Found! */
*key_offset = rec;
goto done;
}
}
if (cmp < 0)
{
+ 2);
}
else
{
break;
}
#endif
}
done:
return status;
}
typedef struct
{
} file_info_t;
typedef struct
{
static int
{
fsw_u32 i;
return -1;
/* not smth we care about */
return 0;
switch (rec_type)
{
case kHFSPlusFolderRecord:
{
break;
}
case kHFSPlusFileRecord:
{
break;
}
case kHFSPlusFileThreadRecord:
{
return 0;
}
default:
BP("unknown file type\n");
break;
}
for (i=0; i<name_len; i++)
{
}
return 1;
}
static fsw_status_t
void * param)
{
/* We modify node, so make a copy */
if (status)
return status;
while (1)
{
fsw_u32 i;
/* Iterate over all records in this node. */
{
switch (rv)
{
case 1:
goto done;
case -1:
goto done;
}
/* if callback returned 0 - continue */
}
if (!next_node)
{
break;
}
{
return 1;
}
first_rec = 0;
}
done:
if (buffer)
return status;
}
#if 0
{
int i;
for (i=0; i<len; i++)
{
}
printf("\n");
}
#endif
static int
{
int result;
/* First key is read from the FS data, second is in-memory in CPU endianess */
if (result)
return result;
if (result)
return result;
return result;
}
static int
{
int key1Len;
return 1;
return -1;
while(1)
{
/* get next valid character from ckey1 */
};
/* get next valid character from ckey2 */
};
}
}
static int
{
int key1Len;
return 1;
return -1;
return 0;
while(1)
{
/* get next valid character from ckey1 */
};
/* get next valid character from ckey2 */
};
}
}
/**
* 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.
*/
struct fsw_hfs_dnode * dno,
struct fsw_extent * extent)
{
/* we only care about data forks atm, do we? */
while (1)
{
{
break;
}
/* Find appropriate overflow record */
{
}
(BTreeKey*)&overflowkey,
if (status)
break;
key = (struct HFSPlusExtentKey *)
}
return status;
}
{
//L"AppleIntelCPUPowerManagement.kext",
};
//#define HFS_FILE_INJECTION
#ifdef HFS_FILE_INJECTION
static struct
{
} g_injectList[] =
{
{
L"/System/Library/Extensions",
L"ApplePS2Controller.kext"
},
{
NULL,
}
};
#endif
static fsw_status_t
struct fsw_hfs_dnode ** child_dno_out)
{
if (status)
return status;
/* Fill-in extents info */
{
}
*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.
*/
struct fsw_hfs_dnode * dno,
struct fsw_string * lookup_name,
struct fsw_hfs_dnode ** child_dno_out)
{
int free_data = 0, i;
/* no need to allocate anything */
{
rec_name = *lookup_name;
} else
{
/* nothing allocated so far */
if (status)
goto done;
free_data = 1;
}
/* Dirty hack: blacklisting of certain files on FS driver level */
for (i = 0; g_blacklist[i]; i++)
{
{
goto done;
}
}
#ifdef HFS_FILE_INJECTION
if (fsw_hfs_inject(vol,
dno,
&file_info))
{
goto create;
}
#endif
if (status)
goto done;
/* for plain HFS "-(keySize & 1)" would be needed */
/** @todo: read additional info */
switch (rec_type)
{
case kHFSPlusFolderRecord:
{
/* @todo: return number of elements, maybe use smth else */
break;
}
case kHFSPlusFileRecord:
{
break;
}
default:
BP("unknown file type\n");
break;
}
#ifdef HFS_FILE_INJECTION
#endif
if (status)
goto done;
done:
if (free_data)
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.
*/
struct fsw_hfs_dnode *dno,
struct fsw_shandle *shand,
struct fsw_hfs_dnode **child_dno_out)
{
if (status)
goto done;
/* Iterator updates shand state */
node,
ptr,
¶m);
if (status)
goto done;
if (status)
goto done;
done:
return status;
}
/**
* Get the target path of a symbolic link. This function is called when a symbolic
* link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
* called on the dnode and that it really is a symlink.
*
*/
struct fsw_string *link_target)
{
return FSW_UNSUPPORTED;
}
// EOF