From: "Gabriel L. Somlo" <gsomlo@gmail.com>
To: edk2-devel@ml01.01.org
Cc: lersek@redhat.com, jordan.l.justen@intel.com,
reza.jelveh@tuhh.de, agraf@suse.de, kraxel@redhat.com
Subject: [RFC PATCH 3/6] FswHfsPlus: implement FSW driver for the HFS+ file system
Date: Mon, 6 Mar 2017 22:14:22 -0500 [thread overview]
Message-ID: <1488856465-8965-4-git-send-email-gsomlo@gmail.com> (raw)
In-Reply-To: <1488856465-8965-1-git-send-email-gsomlo@gmail.com>
Implement read-only HFS+ access methods within the FSW framework.
Based on the HFS+ Volume Format spec (TN1150), available online at
https://developer.apple.com/legacy/library/technotes/tn/tn1150.html
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
OvmfPkg/FswHfsPlus/FswHfsPlus.inf | 57 ++++
OvmfPkg/FswHfsPlus/fsw_hfsplus.c | 535 ++++++++++++++++++++++++++++++++++++++
OvmfPkg/FswHfsPlus/fsw_hfsplus.h | 238 +++++++++++++++++
3 files changed, 830 insertions(+)
create mode 100644 OvmfPkg/FswHfsPlus/FswHfsPlus.inf
create mode 100644 OvmfPkg/FswHfsPlus/fsw_hfsplus.c
create mode 100644 OvmfPkg/FswHfsPlus/fsw_hfsplus.h
diff --git a/OvmfPkg/FswHfsPlus/FswHfsPlus.inf b/OvmfPkg/FswHfsPlus/FswHfsPlus.inf
new file mode 100644
index 0000000..a3581a9
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/FswHfsPlus.inf
@@ -0,0 +1,57 @@
+## @file
+# File System Wrapper (FSW) based HFS+ driver.
+#
+# Copyright (C) 2017, Gabriel L. Somlo <gsomlo@gmail.com>
+#
+# This program and the accompanying materials are licensed and made
+# available under the terms and conditions of the BSD License which
+# accompanies this distribution. The full text of the license may
+# be found at http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+# BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED.
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = FswHfsPlus
+ FILE_GUID = F474802F-26BB-4E4F-A923-2FC568E94125
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = fsw_efi_main
+
+[Sources]
+ fsw_base.h
+ fsw_efi_base.h
+ fsw_efi_edk2_base.h
+ fsw_strfunc.h
+ fsw_core.h
+ fsw_core.c
+ fsw_efi.h
+ fsw_efi.c
+ fsw_lib.c
+ fsw_efi_lib.c
+ fsw_hfsplus.h
+ fsw_hfsplus.c
+
+[BuildOptions]
+ GCC:*_*_*_CC_FLAGS = -DHOST_EFI -DFSTYPE=hfsplus
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ UefiLib
+ UefiDriverEntryPoint
+
+[Guids]
+ gEfiFileSystemInfoGuid
+ gEfiFileSystemVolumeLabelInfoIdGuid
+ gEfiFileInfoGuid
+
+[Protocols]
+ gEfiBlockIoProtocolGuid
+ gEfiDiskIoProtocolGuid
+ gEfiSimpleFileSystemProtocolGuid
diff --git a/OvmfPkg/FswHfsPlus/fsw_hfsplus.c b/OvmfPkg/FswHfsPlus/fsw_hfsplus.c
new file mode 100644
index 0000000..ef17cc9
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_hfsplus.c
@@ -0,0 +1,535 @@
+/** @file
+ HFS+ file system driver.
+
+ Copyright (C) 2017, Gabriel L. Somlo <gsomlo@gmail.com>
+
+ This program and the accompanying materials are licensed and made
+ available under the terms and conditions of the BSD License which
+ accompanies this distribution. The full text of the license may
+ be found at http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+ BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+ EXPRESS OR IMPLIED.
+**/
+
+#include "fsw_hfsplus.h"
+
+
+/* Given dnode 'd' and offset 'pos' into the data file it represents,
+ * retrieve 'len' bytes of data into buffer 'buf';
+ * Return FSW_SUCCESS or error code.
+ */
+static fsw_status_t
+fsw_hfsplus_read(struct fsw_hfsplus_dnode *d, fsw_u64 pos,
+ fsw_u32 len, void *buf)
+{
+ struct fsw_shandle sh;
+ fsw_u32 buflen;
+ fsw_status_t status;
+
+ status = fsw_shandle_open(d, &sh);
+ if (status)
+ return status;
+
+ sh.pos = pos;
+ buflen = len;
+ status = fsw_shandle_read(&sh, &buflen, buf);
+ if (!status && buflen != len)
+ status = FSW_IO_ERROR;
+
+ fsw_shandle_close(&sh);
+ return status;
+}
+
+/* Given the volume being mounted ('v'), and the ID & fork data of the B-Tree
+ * file being set up ('dn_id' and 'f', respectively), populate a cached,
+ * in-memory record of the B-Tree file at the location pointed to by 'btp';
+ * Return FSW_SUCCESS or error code.
+ */
+static fsw_status_t
+fsw_hfsplus_btf_setup(struct fsw_hfsplus_volume *v,
+ fsw_u32 dn_id, HFSPlusForkData *f,
+ struct fsw_hfsplus_dnode **btp)
+{
+ BTHeaderRec hdr_rec;
+ fsw_status_t status;
+
+ status = fsw_dnode_create_root(v, dn_id, btp);
+ if (status)
+ return status;
+
+ (*btp)->g.size = fsw_u64_be_swap(f->logicalSize);
+ fsw_memcpy((*btp)->extents, f->extents, sizeof(HFSPlusExtentRecord));
+
+ // read header record (from node 0, immediately past the node descriptor)
+ status = fsw_hfsplus_read(*btp, sizeof(BTNodeDescriptor),
+ sizeof(BTHeaderRec), &hdr_rec);
+ if (status)
+ return status;
+
+ // grab root node index and node size from header record
+ (*btp)->bt_root = fsw_u32_be_swap(hdr_rec.rootNode);
+ (*btp)->bt_ndsz = fsw_u16_be_swap(hdr_rec.nodeSize);
+
+ return FSW_SUCCESS;
+}
+
+/* Mount an HFS+ volume. Read volume header (equivalent of superblock),
+ * and set up dnodes for the root folder and B-Tree file(s).
+ * Return FSW_SUCCESS or error code.
+ */
+static fsw_status_t
+fsw_hfsplus_vol_mount(struct fsw_hfsplus_volume *v)
+{
+ void *buf;
+ fsw_u32 bs;
+ fsw_status_t status;
+
+ // allocate memory for vol. header
+ status = fsw_alloc(sizeof(HFSPlusVolumeHeader), &v->vh);
+ if (status)
+ return status;
+
+ // read vol. header into buffer
+ fsw_set_blocksize(v, kHFSBlockSize, kHFSBlockSize);
+ status = fsw_block_get(v, kMasterDirectoryBlock, 0, &buf);
+ if (status)
+ return status;
+ fsw_memcpy(v->vh, buf, sizeof(HFSPlusVolumeHeader));
+ fsw_block_release(v, kMasterDirectoryBlock, buf);
+
+ // check vol. header
+ if (fsw_u16_be_swap(v->vh->signature) != kHFSPlusSigWord)
+ return FSW_UNSUPPORTED;
+
+ // use block size specified by vol. header
+ bs = fsw_u32_be_swap(v->vh->blockSize);
+ fsw_set_blocksize(v, bs, bs);
+
+ // set up catalog B-Tree file:
+ status = fsw_hfsplus_btf_setup(v, kHFSCatalogFileID, &v->vh->catalogFile,
+ &v->catf);
+ if (status)
+ return status;
+
+ // set up root folder:
+ status = fsw_dnode_create_root(v, kHFSRootFolderID, &v->g.root);
+ if (status)
+ return status;
+
+ return FSW_SUCCESS;
+}
+
+/* Free the volume data structure. Called by the core after an unmount or
+ * unsuccessful mount, to release the memory used by the file system type
+ * specific part of the volume structure.
+ */
+static void
+fsw_hfsplus_vol_free(struct fsw_hfsplus_volume *v)
+{
+ if (v->vh)
+ fsw_free(v->vh);
+ if (v->catf)
+ fsw_dnode_release((struct fsw_dnode *)v->catf);
+}
+
+/* Get in-depth information on a volume.
+ */
+static fsw_status_t
+fsw_hfsplus_vol_stat(struct fsw_hfsplus_volume *v, struct fsw_volume_stat *s)
+{
+ // FIXME: not yet supported!
+ return FSW_UNSUPPORTED;
+}
+
+/* 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_hfsplus_dno_fill(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d)
+{
+ // NOTE: not applicable to HFS+ dnodes!
+ 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_hfsplus_dno_free(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d)
+{
+ // NOTE: not applicable to HFS+ dnodes!
+ return;
+}
+
+/* Get in-depth dnode information. The core ensures fsw_hfsplus_dno_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_hfsplus_dno_stat(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+ struct fsw_dnode_stat *s)
+{
+ // FIXME: not yet supported!
+ return FSW_SUCCESS;
+}
+
+/* Given a B-Tree node pointed to by 'btnode', with node size 'size',
+ * locate the record given by its record number 'rnum';
+ * Return a pointer to the B-Tree key at the beginning of the record.
+ */
+static HFSPlusBTKey *
+fsw_hfsplus_bt_get_rec(BTNodeDescriptor* btnode, fsw_u16 size, fsw_u16 rnum)
+{
+ fsw_u16 *off = (fsw_u16 *)((void *)btnode + size) - 1 - rnum;
+ return (HFSPlusBTKey *)((void *)btnode + fsw_u16_be_swap(*off));
+}
+
+/* Given a B-Tree record pointer 'k', return a pointer to the data
+ * immediately following the key record; IOW, skip the key record which
+ * prefixes the record data payload.
+ */
+static void *
+fsw_hfsplus_bt_rec_skip_key(HFSPlusBTKey *k)
+{
+ return (void *)k + sizeof(k->keyLength) + fsw_u16_be_swap(k->keyLength);
+}
+
+/* Return the child node number immediately following the key record 'k' of
+ * an index node
+ */
+static fsw_u32
+fsw_hfsplus_bt_idx_get_child(HFSPlusBTKey *k)
+{
+ fsw_u32 *child;
+ child = (fsw_u32 *)fsw_hfsplus_bt_rec_skip_key(k);
+ return fsw_u32_be_swap(*child);
+}
+
+/* key comparison procedure type */
+typedef int (*k_cmp_t)(HFSPlusBTKey*, HFSPlusBTKey*);
+
+/* Search an HFS+ special file's B-Tree (given by 'bt'), for a search key
+ * matching 'sk', using comparison procedure 'k_cmp' to determine when a key
+ * match occurs; Fill a caller-provided B-Tree node buffer ('btnode'), and
+ * return a pointer to the matching record data inside 'btnode' via 'data_ptr';
+ * On error, set fsw_status_t return code acoordingly.
+ *
+ * NOTE: A HFS+ volume has a few "special" files, linked directly from the
+ * volume header. For the purpose of this driver, we mainly care about
+ * two of them: the "catalog" and "extents" files. All of these files
+ * are organized as B-Tree structures. This means that, overlaid on
+ * top of the linear span of each file there is an array of nodes of
+ * a given size (node_size), internally cross-linked with "pointers"
+ * to parent/child/sibling nodes, which are essentially the "index"
+ * (or 'node-number') of the target node in this overlaid array.
+ * Ultimately, (node-number * node-size) is a byte offset into the
+ * file, to the location where the referenced node's data begins.
+ * Each B-Tree file's "dnode" information is available in the HFS+
+ * volume header. The node at the very beginning of each file (at
+ * index or node-number == 0) contains a "header node", which provides
+ * the 'node-number' of the B-Tree's "root" node, as well as the
+ * 'node-size' of all nodes in that B-Tree file.
+ */
+static fsw_status_t
+fsw_hfsplus_bt_search(struct fsw_hfsplus_dnode *bt,
+ HFSPlusBTKey *sk, k_cmp_t k_cmp,
+ BTNodeDescriptor *btnode, void **data_ptr)
+{
+ fsw_u32 node;
+ fsw_u16 rec, lo, hi;
+ HFSPlusBTKey *tk; // trial key
+ int cmp;
+ fsw_status_t status;
+
+ // start searching from the B-Tree root node:
+ node = bt->bt_root;
+
+ for (;;) {
+ // load data for current node into caller-provided buffer 'btnode'
+ status = fsw_hfsplus_read(bt, (fsw_u64)node * bt->bt_ndsz,
+ bt->bt_ndsz, btnode);
+ if (status)
+ return status;
+
+ // sanity check: record 0 located immediately after node descriptor
+ if ((void *)btnode + sizeof(BTNodeDescriptor) !=
+ (void *)fsw_hfsplus_bt_get_rec(btnode, bt->bt_ndsz, 0))
+ return FSW_VOLUME_CORRUPTED;
+
+ // search records within current node
+ lo = 0;
+ hi = fsw_u16_be_swap(btnode->numRecords) - 1;
+ while (lo <= hi) {
+ // access record data, then compare to search key 'sk'
+ rec = (lo + hi) >> 1;
+ tk = fsw_hfsplus_bt_get_rec(btnode, bt->bt_ndsz, rec);
+ cmp = k_cmp(tk, sk);
+
+ if (cmp < 0) // (tk < sk)
+ lo = rec + 1;
+ else if (cmp > 0) // (tk > sk)
+ hi = rec - 1;
+ else { // (tk == sk)
+ if (btnode->kind != kBTLeafNode) {
+ hi = rec;
+ break;
+ }
+ // success: return pointer to data immediately past trial key
+ *data_ptr = fsw_hfsplus_bt_rec_skip_key(tk);
+ return FSW_SUCCESS;
+ }
+ }
+
+ // NOTE: following the binary search, 'hi' now points at the
+ // record with the largest 'tk' for which (tk <= sk)
+
+ if (btnode->kind != kBTIndexNode)
+ break;
+
+ // on an index node, so descend to child
+ tk = fsw_hfsplus_bt_get_rec(btnode, bt->bt_ndsz, hi);
+ node = fsw_hfsplus_bt_idx_get_child(tk);
+ }
+
+ // search key 'sk' not found
+ return FSW_NOT_FOUND;
+}
+
+
+/* Compare unsigned integers 'a' and 'b';
+ * Return -1/0/1 if 'a' is less/equal/greater than 'b'.
+ */
+static int
+fsw_hfsplus_int_cmp(fsw_u32 a, fsw_u32 b)
+{
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+/* Basic latin unicode lowercase
+ */
+static fsw_u16
+fsw_hfsplus_ucblatin_tolower(fsw_u16 c)
+{
+ if (c == 0)
+ return 0xFFFF;
+ if (c == 0x00C6 || c == 0x00D0 || c == 0x00D8 || c == 0x00DE ||
+ (c >= 0x0041 && c <= 0x005A))
+ return c + 0x0020;
+ return c;
+ // FIXME: does edk2 have its own built-in function we could use here?
+}
+
+/* Compare an on-disk catalog B-Tree trial key ('tk') with an in-memory
+ * search key ('sk'). Precedence is parentID, nodeName (keyLength does not
+ * factor into the comparison).
+ * Return -1/0/1 if 'tk'is smaller/equal/larger than 'sk', respectively.
+ * NOTE: all 'tk' fields are stored as big-endian values and must be
+ * converted to CPU endianness before any comparison to corresponding
+ * fields in 'sk'.
+ */
+static int
+fsw_hfsplus_cat_cmp(HFSPlusBTKey *tk, HFSPlusBTKey *sk)
+{
+ fsw_u16 *t_str, *s_str;
+ fsw_u16 t_len, s_len;
+ fsw_u16 t_char, s_char;
+ int ret;
+
+ // compare parent IDs: if unequal, we're done!
+ ret = fsw_hfsplus_int_cmp(fsw_u32_be_swap(tk->catKey.parentID),
+ sk->catKey.parentID);
+ if (ret)
+ return ret;
+
+ // unicode string pointers and lengths:
+ t_len = fsw_u16_be_swap(tk->catKey.nodeName.length);
+ t_str = tk->catKey.nodeName.unicode;
+ s_len = sk->catKey.nodeName.length;
+ s_str = sk->catKey.nodeName.unicode;
+
+ for (;;) {
+ // start by assuming strings are empty:
+ t_char = s_char = 0;
+
+ // find next valid char from on-disk key string:
+ while (t_char == 0 && t_len > 0) {
+ t_char = fsw_hfsplus_ucblatin_tolower(fsw_u16_be_swap(*t_str));
+ t_len--;
+ t_str++;
+ }
+
+ // find next valid char from memory key string:
+ while (s_char == 0 && s_len > 0) {
+ s_char = fsw_hfsplus_ucblatin_tolower(*s_str);
+ s_len--;
+ s_str++;
+ }
+
+ // stop if difference or both strings exhausted:
+ ret = fsw_hfsplus_int_cmp(t_char, s_char);
+ if (ret || s_char == 0)
+ break;
+ }
+
+ return ret;
+}
+
+/* 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_hfsplus_dno_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.
+ * NOTE: logical and physical block sizes are the same (see mount method).
+ */
+static fsw_status_t
+fsw_hfsplus_get_ext(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+ struct fsw_extent *e)
+{
+ fsw_u32 off, bc;
+ HFSPlusExtentRecord *er;
+ int i;
+
+ // set initial offset to provided starting logical block number:
+ off = e->log_start;
+
+ // start with dnode's initial extent record:
+ er = &d->extents;
+
+ // search extent record:
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ // get block count for current extent descriptor:
+ bc = fsw_u32_be_swap((*er)[i].blockCount);
+
+ // have we exhausted all available extents?
+ if (bc == 0)
+ return FSW_NOT_FOUND;
+
+ // offset is relative to current extent's physical startBlock:
+ if (off < bc) {
+ e->type = FSW_EXTENT_TYPE_PHYSBLOCK;
+ e->phys_start = fsw_u32_be_swap((*er)[i].startBlock) + off;
+ e->log_count = bc - off;
+ return FSW_SUCCESS;
+ }
+
+ // update offset to NEXT extent descriptor:
+ off -= bc;
+ }
+
+ // FIXME: more than 8 fragments not yet supported!
+ return FSW_UNSUPPORTED;
+}
+
+
+/* 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_hfsplus_dno_fill has been called and the dnode is actually a directory.
+ */
+static fsw_status_t
+fsw_hfsplus_dir_get(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+ struct fsw_string *name, struct fsw_hfsplus_dnode **d_out)
+{
+ BTNodeDescriptor *btnode;
+ fsw_s16 child_rec_type;
+ fsw_u32 child_dno_id, child_dno_type;
+ HFSPlusCatalogKey k;
+ HFSPlusCatalogRecord *rec;
+ fsw_status_t status;
+
+ // we only support FSW_STRING_TYPE_UTF16 names:
+ if (name->type != FSW_STRING_TYPE_UTF16 ||
+ name->size > sizeof(k.nodeName.unicode))
+ return FSW_UNSUPPORTED;
+
+ // pre-allocate bt-node buffer for use by search function:
+ status = fsw_alloc(v->catf->bt_ndsz, &btnode);
+ if (status)
+ return status;
+
+ // search catalog file for child named by 'name':
+ k.parentID = d->g.dnode_id;
+ k.nodeName.length = name->len;
+ fsw_memcpy(k.nodeName.unicode, name->data, name->size);
+ // NOTE: keyLength not used in search, setting only for completeness:
+ k.keyLength = sizeof(k.parentID) + sizeof(k.nodeName.length) + name->size;
+ status = fsw_hfsplus_bt_search(v->catf,
+ (HFSPlusBTKey *)&k, fsw_hfsplus_cat_cmp,
+ btnode, (void **)&rec);
+ if (status)
+ goto done;
+
+ // child record immediately follows the record key data:
+ child_rec_type = fsw_u16_be_swap(rec->recordType);
+ if (child_rec_type == kHFSPlusFolderRecord) {
+ child_dno_id = fsw_u32_be_swap(rec->folderRecord.folderID);
+ child_dno_type = FSW_DNODE_TYPE_DIR;
+ } else if (child_rec_type == kHFSPlusFileRecord) {
+ child_dno_id = fsw_u32_be_swap(rec->fileRecord.fileID);
+ child_dno_type = FSW_DNODE_TYPE_FILE;
+ } else {
+ child_dno_id = 0;
+ child_dno_type = FSW_DNODE_TYPE_UNKNOWN;
+ }
+ status = fsw_dnode_create(d, child_dno_id, child_dno_type, name, d_out);
+ if (status)
+ goto done;
+
+ // if child node is a file, set size and initial extents:
+ if (child_rec_type == kHFSPlusFileRecord) {
+ (*d_out)->g.size =
+ fsw_u64_be_swap(rec->fileRecord.dataFork.logicalSize);
+ fsw_memcpy((*d_out)->extents, &rec->fileRecord.dataFork.extents,
+ sizeof(HFSPlusExtentRecord));
+ }
+
+done:
+ fsw_free(btnode);
+ 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_hfsplus_dno_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_hfsplus_dir_read(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+ struct fsw_shandle *sh, struct fsw_hfsplus_dnode **d_out)
+{
+ // FIXME: not yet supported!
+ return FSW_UNSUPPORTED;
+}
+
+/* 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_hfsplus_dno_fill has been called on the dnode and that it really is a
+ * symlink.
+ */
+static fsw_status_t
+fsw_hfsplus_readlink(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+ struct fsw_string *lnk_tgt)
+{
+ // FIXME: not yet supported!
+ return FSW_UNSUPPORTED;
+}
+
+
+/* HFS+ FSW Method Dispatch Table
+ */
+struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfsplus) = {
+ { FSW_STRING_TYPE_ISO88591, 4, 4, "hfsplus" },
+ sizeof(struct fsw_hfsplus_volume), sizeof(struct fsw_hfsplus_dnode),
+ fsw_hfsplus_vol_mount, fsw_hfsplus_vol_free, fsw_hfsplus_vol_stat,
+ fsw_hfsplus_dno_fill, fsw_hfsplus_dno_free, fsw_hfsplus_dno_stat,
+ fsw_hfsplus_get_ext, fsw_hfsplus_dir_get, fsw_hfsplus_dir_read,
+ fsw_hfsplus_readlink,
+};
diff --git a/OvmfPkg/FswHfsPlus/fsw_hfsplus.h b/OvmfPkg/FswHfsPlus/fsw_hfsplus.h
new file mode 100644
index 0000000..38f4631
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_hfsplus.h
@@ -0,0 +1,238 @@
+/** @file
+ HFS+ file system driver header.
+
+ Copyright (C) 2017, Gabriel L. Somlo <gsomlo@gmail.com>
+
+ This program and the accompanying materials are licensed and made
+ available under the terms and conditions of the BSD License which
+ accompanies this distribution. The full text of the license may
+ be found at http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+ BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+ EXPRESS OR IMPLIED.
+**/
+
+#ifndef _FSW_HFSPLUS_H_
+#define _FSW_HFSPLUS_H_
+
+#define VOLSTRUCTNAME fsw_hfsplus_volume
+#define DNODESTRUCTNAME fsw_hfsplus_dnode
+#include "fsw_core.h"
+
+/*============= HFS+ constants and data types from Apple TN1150 =============*/
+#pragma pack(1)
+
+#define kHFSBlockSize 512 // Minimum block size to transfer Vol.Hdr.
+#define kMasterDirectoryBlock 2 // Vol.Hdr. disk offset (x kHFSBlockSize)
+
+#define kHFSPlusSigWord 0x482B // HFS+ volume signature (ASCII for 'H+')
+
+#define kHFSPlusMaxFileNameChars 255 // Max. length of HFS+ folder or filename
+
+#define kHFSPlusExtentDensity 8 // Number of extent descriptors per record
+
+#define kHFSPlusDataFork 0x00 // data fork type
+#define kHFSPlusResourceFork 0xFF // resource fork type
+
+#define kHFSRootFolderID 2 // ID of the root folder
+#define kHFSExtentsFileID 3 // ID of the extent overflow file
+#define kHFSCatalogFileID 4 // ID of the catalog file
+
+#define kHFSPlusFolderRecord 1 // catalog folder record type
+#define kHFSPlusFileRecord 2 // catalog file record type
+
+#define kBTLeafNode -1 // B-Tree leaf node type
+#define kBTIndexNode 0 // B-Tree index node type
+#define kBTHeaderNode 1 // B-Tree header node type
+
+// file/folder name unicode string type
+typedef struct {
+ fsw_u16 length; // character count
+ fsw_u16 unicode[kHFSPlusMaxFileNameChars]; // character string
+} HFSUniStr255;
+
+// extent descriptor type
+typedef struct {
+ fsw_u32 startBlock; // first allocation block in the extent
+ fsw_u32 blockCount; // extent length, in allocation blocks
+} HFSPlusExtentDescriptor;
+
+// extent record type
+typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[kHFSPlusExtentDensity];
+
+// fork-data information type
+typedef struct {
+ fsw_u64 logicalSize; // size of valid fork data, in bytes
+ fsw_u32 clumpSize; // fork-specific clump size
+ fsw_u32 totalBlocks; // total blocks across all extents in fork
+ HFSPlusExtentRecord extents; // initial extents for the fork data
+} HFSPlusForkData;
+
+// volume header type
+typedef struct {
+ fsw_u16 signature; // should be kHFSPlusSigWord
+ fsw_u16 version; // should be kHFSPlusVersion
+ fsw_u32 attributes; // volume attributes
+ fsw_u32 lastMountedVersion; // unique ID of software that last wrote volume
+ fsw_u32 journalInfoBlock; // block no. of journal info
+ fsw_u32 createDate; // volume creation timestamp (date and time)
+ fsw_u32 modifyDate; // volume last modification timestamp
+ fsw_u32 backupDate; // timestamp of last backup
+ fsw_u32 checkedDate; // timestamp of last consistency check
+ fsw_u32 fileCount; // total number of files on volume
+ fsw_u32 folderCount; // total number of folders on volume
+ fsw_u32 blockSize; // allocation block size, in bytes
+ fsw_u32 totalBlocks; // total number of allocation blocks
+ fsw_u32 freeBlocks; // total number of unused allocation blocks
+ fsw_u32 nextAllocation; // block number to start next allocation search
+ fsw_u32 rsrcClumpSize; // dflt. resource fork clump size (in bytes)
+ fsw_u32 dataClumpSize; // dflt. data fork clump size (in bytes)
+ fsw_u32 nextCatalogID; // next unused catalog ID
+ fsw_u32 writeCount; // incr. each time volume is write-mounted
+ fsw_u64 encodingsBitmap; // keep track of text encodings used in names
+ fsw_u8 finderInfo[32]; // information used by the OS X Finder
+ HFSPlusForkData allocationFile; // info re. size & location of alloc. file
+ HFSPlusForkData extentsFile; // info re. size & location of extents file
+ HFSPlusForkData catalogFile; // info re. size & location of catalog file
+ HFSPlusForkData attributesFile; // info re. size & location of attr. file
+ HFSPlusForkData startupFile; // info re. size & location of startup file
+} HFSPlusVolumeHeader;
+
+// file permissions type
+typedef struct {
+ fsw_u32 ownerID; // owner UID (or hard-link previous-link)
+ fsw_u32 groupID; // owner GID (or hard-link next-link)
+ fsw_u8 adminFlags; // flags changeable by root only
+ fsw_u8 ownerFlags; // flags changeable by owner
+ fsw_u16 fileMode; // BSD file type and mode bits
+ union {
+ fsw_u32 iNodeNum; // link reference number, if hard-link
+ fsw_u32 linkCount; // ref-count of hard-links, if indirect node file
+ fsw_u32 rawDevice; // device number, if block/char special dev. file
+ } special; // reserved for directories and most files
+} HFSPlusBSDInfo;
+
+// finder info types
+typedef struct {
+ fsw_u8 opaque[16];
+} FndrFileInfo, FndrDirInfo, FndrOpaqueInfo;
+
+// catalog folder record type
+typedef struct {
+ fsw_s16 recordType; // should be kHFSPlusFolderRecord
+ fsw_u16 flags; // bit flags about the folder (reserved)
+ fsw_u32 valence; // items directly contained by this folder
+ fsw_u32 folderID; // CNID of this folder
+ fsw_u32 createDate; // folder creation timestamp (date and time)
+ fsw_u32 contentModDate; // folder content last modification timestamp
+ fsw_u32 attributeModDate; // ctime (last change to a catalog record field)
+ fsw_u32 accessDate; // atime (last access timestamp)
+ fsw_u32 backupDate; // timestamp of last backup
+ HFSPlusBSDInfo permissions; // folder permissions
+ FndrDirInfo userInfo; // information used by the OS X Finder
+ FndrOpaqueInfo finderInfo; // additional information for the OS X Finder
+ fsw_u32 textEncoding; // hint re. folder name text encoding
+ fsw_u32 reserved; // reserved field
+} HFSPlusCatalogFolder;
+
+// catalog file record type
+typedef struct {
+ fsw_s16 recordType; // should be kHFSPlusFileRecord
+ fsw_u16 flags; // bit flags about the file (reserved)
+ fsw_u32 reserved1; // reserved field
+ fsw_u32 fileID; // CNID of this file
+ fsw_u32 createDate; // file creation timestamp (date and time)
+ fsw_u32 contentModDate; // file content last modification timestamp
+ fsw_u32 attributeModDate; // ctime (last change to a catalog record field)
+ fsw_u32 accessDate; // atime (last access timestamp)
+ fsw_u32 backupDate; // timestamp of last backup
+ HFSPlusBSDInfo permissions; // file permissions
+ FndrFileInfo userInfo; // information used by the OS X Finder
+ FndrOpaqueInfo finderInfo; // additional information for the OS X Finder
+ fsw_u32 textEncoding; // hint re. file name text encoding
+ fsw_u32 reserved2; // reserved field
+ HFSPlusForkData dataFork; // info re. size & location of data fork
+ HFSPlusForkData resourceFork; // info re. size & location of resource fork
+} HFSPlusCatalogFile;
+
+// generic (union) catalog record type
+typedef union {
+ fsw_s16 recordType; // set to kHFSPlus[Folder|File]Record
+ HFSPlusCatalogFolder folderRecord; // catalog folder record fields
+ HFSPlusCatalogFile fileRecord; // catalog file record fields
+} HFSPlusCatalogRecord;
+
+// B-Tree node descriptor found at the start of each node
+typedef struct {
+ fsw_u32 fLink; // number of next node of this type (0 if we're last)
+ fsw_u32 bLink; // number of prev. node of this type (0 if we're first)
+ fsw_s8 kind; // node type (leaf, index, header, map)
+ fsw_u8 height; // node depth in B-Tree hierarchy (0 for header)
+ fsw_u16 numRecords; // number of records contained in this node
+ fsw_u16 reserved; // reserved field
+} BTNodeDescriptor;
+
+// B-Tree header record type (first record of a B-Tree header node)
+typedef struct {
+ fsw_u16 treeDepth; // current B-Tree depth (always == rootNode.height)
+ fsw_u32 rootNode; // node number of B-Tree root node
+ fsw_u32 leafRecords; // total number of records across all leaf nodes
+ fsw_u32 firstLeafNode; // node number of first leaf node
+ fsw_u32 lastLeafNode; // node number of last leaf node
+ fsw_u16 nodeSize; // node size (in bytes)
+ fsw_u16 maxKeyLength; // max. length of a key in index/leaf node
+ fsw_u32 totalNodes; // total number of nodes in the B-Tree
+ fsw_u32 freeNodes; // number of unused nodes in the B-Tree
+ fsw_u16 reserved1; // reserved field
+ fsw_u32 clumpSize; // reserved field (deprecated)
+ fsw_u8 btreeType; // reserved (0 for catalog, extents, attrib. file)
+ fsw_u8 keyCompareType; // case-sensitive string comparison (HFSX only)
+ fsw_u32 attributes; // B-Tree attributes
+ fsw_u32 reserved3[16]; // reserved field
+} BTHeaderRec;
+
+// extent overflow file key type
+typedef struct {
+ fsw_u16 keyLength; // key length (excluding this field)
+ fsw_u8 forkType; // data or resource fork
+ fsw_u8 pad; // ensure 32-bit alignment for subsequent fields
+ fsw_u32 fileID; // CNID of file to which this extent record applies
+ fsw_u32 startBlock; // start block of first extent described by this record
+} HFSPlusExtentKey;
+
+// catalog file key type
+typedef struct {
+ fsw_u16 keyLength; // key length (excluding this field)
+ fsw_u32 parentID; // ID of parent folder (or CNID if thread record)
+ HFSUniStr255 nodeName; // basename of file or folder
+} HFSPlusCatalogKey;
+
+// generic (union) B-Tree record key type
+typedef union {
+ fsw_u16 keyLength; // key length (excluding this field)
+ HFSPlusExtentKey extKey; // extent key fields
+ HFSPlusCatalogKey catKey; // catalog key fields
+} HFSPlusBTKey;
+
+#pragma pack()
+/*========= end HFS+ constants and data types from Apple TN1150 =============*/
+
+
+// FSW: HFS+ specific dnode
+struct fsw_hfsplus_dnode {
+ struct fsw_dnode g; // Generic (parent) dnode structure
+ HFSPlusExtentRecord extents; // HFS+ initial extent record
+ fsw_u32 bt_root; // root node index (if B-Tree file)
+ fsw_u16 bt_ndsz; // node size (if B-Tree file)
+};
+
+
+// FSW: HFS+ specific volume
+struct fsw_hfsplus_volume {
+ struct fsw_volume g; // Generic (parent) volume structure
+ HFSPlusVolumeHeader *vh; // Raw HFS+ Volume Header
+ struct fsw_hfsplus_dnode *catf; // Catalog file dnode
+};
+
+#endif // _FSW_HFSPLUS_H_
--
2.7.4
next prev parent reply other threads:[~2017-03-07 3:14 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-03-07 3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
2017-03-07 3:14 ` [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code Gabriel L. Somlo
2017-03-07 3:14 ` [RFC PATCH 2/6] FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies Gabriel L. Somlo
2017-03-07 3:14 ` Gabriel L. Somlo [this message]
2017-03-07 3:14 ` [RFC PATCH 4/6] EdkCompatibilityPkg: allow ConsoleControl protocol to be used Gabriel L. Somlo
2017-03-07 3:14 ` [RFC PATCH 5/6] OvmfPkg: add Apple boot support Gabriel L. Somlo
2017-03-07 16:14 ` Laszlo Ersek
2017-03-07 3:14 ` [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware Gabriel L. Somlo
2017-03-07 16:21 ` Laszlo Ersek
2017-03-07 15:41 ` [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Laszlo Ersek
2017-03-29 23:22 ` Phil Dennis-Jordan
2017-03-31 20:01 ` Gabriel L. Somlo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1488856465-8965-4-git-send-email-gsomlo@gmail.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox