hfs/hfsplus updates for v6.19

- hfs/hfsplus: move on-disk layout declarations into hfs_common.h
 - hfsplus: fix volume corruption issue for generic/101
 - hfsplus: introduce KUnit tests for HFS+ string operations
 - hfs: introduce KUnit tests for HFS string operations
 - hfsplus: fix volume corruption issue for generic/073
 - hfsplus: Verify inode mode when loading from disk
 - hfsplus: fix volume corruption issue for generic/070
 - hfs/hfsplus: prevent getting negative values of offset/length
 - hfsplus: fix missing hfs_bnode_get() in __hfs_bnode_create
 - hfs: fix potential use after free in hfs_correct_next_unused_CNID()
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQT4wVoLCG92poNnMFAhI4xTh21NnQUCaSnmHAAKCRAhI4xTh21N
 nWt0AQDQ4hDGj4VkHNzWWGfh6GL+RhSwKgEzf897tJlUZDewogD/TE9bZnzOKjOw
 YhWPXHEH4xy9+QaDXRgXk2DnWS+YKwg=
 =mAL6
 -----END PGP SIGNATURE-----

Merge tag 'hfs-v6.19-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs

Pull hfs/hfsplus updates from Viacheslav Dubeyko:
 "Several fixes for syzbot reported issues, HFS/HFS+ fixes of xfstests
  failures, Kunit-based unit-tests introduction, and code cleanup:

   - Dan Carpenter fixed a potential use-after-free issue in
     hfs_correct_next_unused_CNID() method. Tetsuo Handa has made nice
     fix of syzbot reported issue related to incorrect inode->i_mode
     management if volume has been corrupted somehow. Yang Chenzhi has
     made really good fix of potential race condition in
     __hfs_bnode_create() method for HFS+ file system.

   - Several fixes to xfstests failures. Particularly, generic/070,
     generic/073, and generic/101 test-cases finish successfully for the
     case of HFS+ file system right now.

   - HFS and HFS+ drivers share multiple structures of on-disk layout
     declarations. Some structures are used without any change. However,
     we had two independent declarations of the same structures in HFS
     and HFS+ drivers.

     The on-disk layout declarations have been moved into
     include/linux/hfs_common.h with the goal to exclude the
     declarations duplication and to keep the HFS/HFS+ on-disk layout
     declarations in one place.

     Also, this patch prepares the basis for creating a hfslib that can
     aggregate common functionality without necessity to duplicate the
     same code in HFS and HFS+ drivers.

   - HFS/HFS+ really need unit-tests because of multiple xfstests
     failures. The first two patches introduce Kunit-based unit-tests
     for the case string operations in HFS/HFS+ file system drivers"

* tag 'hfs-v6.19-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs:
  hfs/hfsplus: move on-disk layout declarations into hfs_common.h
  hfsplus: fix volume corruption issue for generic/101
  hfsplus: introduce KUnit tests for HFS+ string operations
  hfs: introduce KUnit tests for HFS string operations
  hfsplus: fix volume corruption issue for generic/073
  hfsplus: Verify inode mode when loading from disk
  hfsplus: fix volume corruption issue for generic/070
  hfs/hfsplus: prevent getting negative values of offset/length
  hfsplus: fix missing hfs_bnode_get() in __hfs_bnode_create
  hfs: fix potential use after free in hfs_correct_next_unused_CNID()
This commit is contained in:
Linus Torvalds 2025-12-03 20:08:32 -08:00
commit ca010e2ef6
30 changed files with 2698 additions and 914 deletions

7
fs/hfs/.kunitconfig Normal file
View File

@ -0,0 +1,7 @@
CONFIG_KUNIT=y
CONFIG_HFS_FS=y
CONFIG_HFS_KUNIT_TEST=y
CONFIG_BLOCK=y
CONFIG_BUFFER_HEAD=y
CONFIG_NLS=y
CONFIG_LEGACY_DIRECT_IO=y

View File

@ -13,3 +13,18 @@ config HFS_FS
To compile this file system support as a module, choose M here: the
module will be called hfs.
config HFS_KUNIT_TEST
tristate "KUnit tests for HFS filesystem" if !KUNIT_ALL_TESTS
depends on HFS_FS && KUNIT
default KUNIT_ALL_TESTS
help
This builds KUnit tests for the HFS filesystem.
KUnit tests run during boot and output the results to the debug
log in TAP format (https://testanything.org/). Only useful for
kernel devs running KUnit test harness and are not for inclusion
into a production build.
For more information on KUnit and unit tests in general please
refer to the KUnit documentation in Documentation/dev-tools/kunit/.

View File

@ -9,3 +9,5 @@ hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \
catalog.o dir.o extent.o inode.o attr.o mdb.o \
part_tbl.o string.o super.o sysdep.o trans.o
# KUnit tests
obj-$(CONFIG_HFS_KUNIT_TEST) += string_test.o

View File

@ -167,7 +167,7 @@ int hfs_brec_find(struct hfs_find_data *fd)
return res;
}
int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
{
int res;

View File

@ -16,14 +16,14 @@
#include "btree.h"
static inline
bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
{
bool is_valid = off < node->tree->node_size;
if (!is_valid) {
pr_err("requested invalid offset: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d\n",
"node_size %u, offset %u\n",
node->this, node->type, node->height,
node->tree->node_size, off);
}
@ -32,7 +32,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
}
static inline
int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
{
unsigned int node_size;
@ -42,12 +42,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
node_size = node->tree->node_size;
if ((off + len) > node_size) {
int new_len = (int)node_size - off;
u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, "
"requested_len %d, corrected_len %d\n",
"node_size %u, offset %u, "
"requested_len %u, corrected_len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len, new_len);
@ -57,12 +57,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
return len;
}
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page *page;
int pagenum;
int bytes_read;
int bytes_to_read;
u32 pagenum;
u32 bytes_read;
u32 bytes_to_read;
if (!is_bnode_offset_valid(node, off))
return;
@ -70,7 +70,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, len %d\n",
"node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@ -86,7 +86,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (pagenum >= node->tree->pages_per_bnode)
break;
page = node->page[pagenum];
bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off);
memcpy_from_page(buf + bytes_read, page, off, bytes_to_read);
@ -95,7 +95,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
}
}
u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
{
__be16 data;
// optimize later...
@ -103,7 +103,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
return be16_to_cpu(data);
}
u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
{
u8 data;
// optimize later...
@ -111,10 +111,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
return data;
}
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
{
struct hfs_btree *tree;
int key_len;
u32 key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
@ -125,14 +125,14 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
if (key_len > sizeof(hfs_btree_key) || key_len < 1) {
memset(key, 0, sizeof(hfs_btree_key));
pr_err("hfs: Invalid key length: %d\n", key_len);
pr_err("hfs: Invalid key length: %u\n", key_len);
return;
}
hfs_bnode_read(node, key, off, key_len);
}
void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page *page;
@ -142,7 +142,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, len %d\n",
"node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@ -157,20 +157,20 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
set_page_dirty(page);
}
void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
{
__be16 v = cpu_to_be16(data);
// optimize later...
hfs_bnode_write(node, &v, off, 2);
}
void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data)
{
// optimize later...
hfs_bnode_write(node, &data, off, 1);
}
void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
{
struct page *page;
@ -180,7 +180,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, len %d\n",
"node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@ -195,8 +195,8 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
set_page_dirty(page);
}
void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
struct hfs_bnode *src_node, int src, int len)
void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
struct hfs_bnode *src_node, u32 src, u32 len)
{
struct page *src_page, *dst_page;
@ -216,7 +216,7 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
set_page_dirty(dst_page);
}
void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
{
struct page *page;
void *ptr;

View File

@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
return retval;
}
int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
{
struct hfs_btree *tree;
struct hfs_bnode *node, *new_node;

View File

@ -259,7 +259,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
}
/* Make sure @tree has enough space for the @rsvd_nodes */
int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
{
struct inode *inode = tree->inode;
u32 count;

View File

@ -86,87 +86,46 @@ struct hfs_find_data {
/* btree.c */
extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
extern void hfs_btree_close(struct hfs_btree *);
extern void hfs_btree_write(struct hfs_btree *);
extern int hfs_bmap_reserve(struct hfs_btree *, int);
extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id,
btree_keycmp keycmp);
extern void hfs_btree_close(struct hfs_btree *tree);
extern void hfs_btree_write(struct hfs_btree *tree);
extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
extern void hfs_bmap_free(struct hfs_bnode *node);
/* bnode.c */
extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int);
extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int);
extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int);
extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int);
extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int);
extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16);
extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8);
extern void hfs_bnode_clear(struct hfs_bnode *, int, int);
extern void hfs_bnode_copy(struct hfs_bnode *, int,
struct hfs_bnode *, int, int);
extern void hfs_bnode_move(struct hfs_bnode *, int, int, int);
extern void hfs_bnode_dump(struct hfs_bnode *);
extern void hfs_bnode_unlink(struct hfs_bnode *);
extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32);
extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32);
extern void hfs_bnode_unhash(struct hfs_bnode *);
extern void hfs_bnode_free(struct hfs_bnode *);
extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
extern void hfs_bnode_get(struct hfs_bnode *);
extern void hfs_bnode_put(struct hfs_bnode *);
extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data);
extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
struct hfs_bnode *src_node, u32 src, u32 len);
extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
extern void hfs_bnode_dump(struct hfs_bnode *node);
extern void hfs_bnode_unlink(struct hfs_bnode *node);
extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num);
extern void hfs_bnode_unhash(struct hfs_bnode *node);
extern void hfs_bnode_free(struct hfs_bnode *node);
extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num);
extern void hfs_bnode_get(struct hfs_bnode *node);
extern void hfs_bnode_put(struct hfs_bnode *node);
/* brec.c */
extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
extern u16 hfs_brec_keylen(struct hfs_bnode *, u16);
extern int hfs_brec_insert(struct hfs_find_data *, void *, int);
extern int hfs_brec_remove(struct hfs_find_data *);
extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
extern int hfs_brec_remove(struct hfs_find_data *fd);
/* bfind.c */
extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
extern void hfs_find_exit(struct hfs_find_data *);
extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *);
extern int hfs_brec_find(struct hfs_find_data *);
extern int hfs_brec_read(struct hfs_find_data *, void *, int);
extern int hfs_brec_goto(struct hfs_find_data *, int);
struct hfs_bnode_desc {
__be32 next; /* (V) Number of the next node at this level */
__be32 prev; /* (V) Number of the prev node at this level */
u8 type; /* (F) The type of node */
u8 height; /* (F) The level of this node (leaves=1) */
__be16 num_recs; /* (V) The number of records in this node */
u16 reserved;
} __packed;
#define HFS_NODE_INDEX 0x00 /* An internal (index) node */
#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */
#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */
#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */
struct hfs_btree_header_rec {
__be16 depth; /* (V) The number of levels in this B-tree */
__be32 root; /* (V) The node number of the root node */
__be32 leaf_count; /* (V) The number of leaf records */
__be32 leaf_head; /* (V) The number of the first leaf node */
__be32 leaf_tail; /* (V) The number of the last leaf node */
__be16 node_size; /* (F) The number of bytes in a node (=512) */
__be16 max_key_len; /* (F) The length of a key in an index node */
__be32 node_count; /* (V) The total number of nodes */
__be32 free_nodes; /* (V) The number of unused nodes */
u16 reserved1;
__be32 clump_size; /* (F) clump size. not usually used. */
u8 btree_type; /* (F) BTree type */
u8 reserved2;
__be32 attributes; /* (F) attributes */
u32 reserved3[16];
} __packed;
#define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not
used by hfsplus. */
#define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8.
used by hfsplus. */
#define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of
max key length. use din catalog
b-tree but not in extents
b-tree (hfsplus). */
extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd);
extern void hfs_find_exit(struct hfs_find_data *fd);
extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd);
extern int hfs_brec_find(struct hfs_find_data *fd);
extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt);

View File

@ -322,9 +322,9 @@ int hfs_correct_next_unused_CNID(struct super_block *sb, u32 cnid)
}
}
node_id = node->prev;
hfs_bnode_put(node);
node_id = node->prev;
} while (node_id >= leaf_head);
return -ENOENT;

View File

@ -9,274 +9,7 @@
#ifndef _HFS_H
#define _HFS_H
/* offsets to various blocks */
#define HFS_DD_BLK 0 /* Driver Descriptor block */
#define HFS_PMAP_BLK 1 /* First block of partition map */
#define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */
/* magic numbers for various disk blocks */
#define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */
#define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */
#define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */
#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */
#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */
/* various FIXED size parameters */
#define HFS_SECTOR_SIZE 512 /* size of an HFS sector */
#define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */
#define HFS_NAMELEN 31 /* maximum length of an HFS filename */
#define HFS_MAX_NAMELEN 128
#define HFS_MAX_VALENCE 32767U
/* Meanings of the drAtrb field of the MDB,
* Reference: _Inside Macintosh: Files_ p. 2-61
*/
#define HFS_SB_ATTRIB_HLOCK (1 << 7)
#define HFS_SB_ATTRIB_UNMNT (1 << 8)
#define HFS_SB_ATTRIB_SPARED (1 << 9)
#define HFS_SB_ATTRIB_INCNSTNT (1 << 11)
#define HFS_SB_ATTRIB_SLOCK (1 << 15)
/* Some special File ID numbers */
#define HFS_POR_CNID 1 /* Parent Of the Root */
#define HFS_ROOT_CNID 2 /* ROOT directory */
#define HFS_EXT_CNID 3 /* EXTents B-tree */
#define HFS_CAT_CNID 4 /* CATalog B-tree */
#define HFS_BAD_CNID 5 /* BAD blocks file */
#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */
#define HFS_START_CNID 7 /* STARTup file (HFS+) */
#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */
#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */
#define HFS_FIRSTUSER_CNID 16
/* values for hfs_cat_rec.cdrType */
#define HFS_CDR_DIR 0x01 /* folder (directory) */
#define HFS_CDR_FIL 0x02 /* file */
#define HFS_CDR_THD 0x03 /* folder (directory) thread */
#define HFS_CDR_FTH 0x04 /* file thread */
/* legal values for hfs_ext_key.FkType and hfs_file.fork */
#define HFS_FK_DATA 0x00
#define HFS_FK_RSRC 0xFF
/* bits in hfs_fil_entry.Flags */
#define HFS_FIL_LOCK 0x01 /* locked */
#define HFS_FIL_THD 0x02 /* file thread */
#define HFS_FIL_DOPEN 0x04 /* data fork open */
#define HFS_FIL_ROPEN 0x08 /* resource fork open */
#define HFS_FIL_DIR 0x10 /* directory (always clear) */
#define HFS_FIL_NOCOPY 0x40 /* copy-protected file */
#define HFS_FIL_USED 0x80 /* open */
/* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */
#define HFS_DIR_LOCK 0x01 /* locked */
#define HFS_DIR_THD 0x02 /* directory thread */
#define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */
#define HFS_DIR_MOUNTED 0x08 /* mounted */
#define HFS_DIR_DIR 0x10 /* directory (always set) */
#define HFS_DIR_EXPFOLDER 0x20 /* share point */
/* bits hfs_finfo.fdFlags */
#define HFS_FLG_INITED 0x0100
#define HFS_FLG_LOCKED 0x1000
#define HFS_FLG_INVISIBLE 0x4000
/*======== HFS structures as they appear on the disk ========*/
/* Pascal-style string of up to 31 characters */
struct hfs_name {
u8 len;
u8 name[HFS_NAMELEN];
} __packed;
struct hfs_point {
__be16 v;
__be16 h;
} __packed;
struct hfs_rect {
__be16 top;
__be16 left;
__be16 bottom;
__be16 right;
} __packed;
struct hfs_finfo {
__be32 fdType;
__be32 fdCreator;
__be16 fdFlags;
struct hfs_point fdLocation;
__be16 fdFldr;
} __packed;
struct hfs_fxinfo {
__be16 fdIconID;
u8 fdUnused[8];
__be16 fdComment;
__be32 fdPutAway;
} __packed;
struct hfs_dinfo {
struct hfs_rect frRect;
__be16 frFlags;
struct hfs_point frLocation;
__be16 frView;
} __packed;
struct hfs_dxinfo {
struct hfs_point frScroll;
__be32 frOpenChain;
__be16 frUnused;
__be16 frComment;
__be32 frPutAway;
} __packed;
union hfs_finder_info {
struct {
struct hfs_finfo finfo;
struct hfs_fxinfo fxinfo;
} file;
struct {
struct hfs_dinfo dinfo;
struct hfs_dxinfo dxinfo;
} dir;
} __packed;
/* Cast to a pointer to a generic bkey */
#define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X)))
/* The key used in the catalog b-tree: */
struct hfs_cat_key {
u8 key_len; /* number of bytes in the key */
u8 reserved; /* padding */
__be32 ParID; /* CNID of the parent dir */
struct hfs_name CName; /* The filename of the entry */
} __packed;
/* The key used in the extents b-tree: */
struct hfs_ext_key {
u8 key_len; /* number of bytes in the key */
u8 FkType; /* HFS_FK_{DATA,RSRC} */
__be32 FNum; /* The File ID of the file */
__be16 FABN; /* allocation blocks number*/
} __packed;
typedef union hfs_btree_key {
u8 key_len; /* number of bytes in the key */
struct hfs_cat_key cat;
struct hfs_ext_key ext;
} hfs_btree_key;
#define HFS_MAX_CAT_KEYLEN (sizeof(struct hfs_cat_key) - sizeof(u8))
#define HFS_MAX_EXT_KEYLEN (sizeof(struct hfs_ext_key) - sizeof(u8))
typedef union hfs_btree_key btree_key;
struct hfs_extent {
__be16 block;
__be16 count;
};
typedef struct hfs_extent hfs_extent_rec[3];
/* The catalog record for a file */
struct hfs_cat_file {
s8 type; /* The type of entry */
u8 reserved;
u8 Flags; /* Flags such as read-only */
s8 Typ; /* file version number = 0 */
struct hfs_finfo UsrWds; /* data used by the Finder */
__be32 FlNum; /* The CNID */
__be16 StBlk; /* obsolete */
__be32 LgLen; /* The logical EOF of the data fork*/
__be32 PyLen; /* The physical EOF of the data fork */
__be16 RStBlk; /* obsolete */
__be32 RLgLen; /* The logical EOF of the rsrc fork */
__be32 RPyLen; /* The physical EOF of the rsrc fork */
__be32 CrDat; /* The creation date */
__be32 MdDat; /* The modified date */
__be32 BkDat; /* The last backup date */
struct hfs_fxinfo FndrInfo; /* more data for the Finder */
__be16 ClpSize; /* number of bytes to allocate
when extending files */
hfs_extent_rec ExtRec; /* first extent record
for the data fork */
hfs_extent_rec RExtRec; /* first extent record
for the resource fork */
u32 Resrv; /* reserved by Apple */
} __packed;
/* the catalog record for a directory */
struct hfs_cat_dir {
s8 type; /* The type of entry */
u8 reserved;
__be16 Flags; /* flags */
__be16 Val; /* Valence: number of files and
dirs in the directory */
__be32 DirID; /* The CNID */
__be32 CrDat; /* The creation date */
__be32 MdDat; /* The modification date */
__be32 BkDat; /* The last backup date */
struct hfs_dinfo UsrInfo; /* data used by the Finder */
struct hfs_dxinfo FndrInfo; /* more data used by Finder */
u8 Resrv[16]; /* reserved by Apple */
} __packed;
/* the catalog record for a thread */
struct hfs_cat_thread {
s8 type; /* The type of entry */
u8 reserved[9]; /* reserved by Apple */
__be32 ParID; /* CNID of parent directory */
struct hfs_name CName; /* The name of this entry */
} __packed;
/* A catalog tree record */
typedef union hfs_cat_rec {
s8 type; /* The type of entry */
struct hfs_cat_file file;
struct hfs_cat_dir dir;
struct hfs_cat_thread thread;
} hfs_cat_rec;
struct hfs_mdb {
__be16 drSigWord; /* Signature word indicating fs type */
__be32 drCrDate; /* fs creation date/time */
__be32 drLsMod; /* fs modification date/time */
__be16 drAtrb; /* fs attributes */
__be16 drNmFls; /* number of files in root directory */
__be16 drVBMSt; /* location (in 512-byte blocks)
of the volume bitmap */
__be16 drAllocPtr; /* location (in allocation blocks)
to begin next allocation search */
__be16 drNmAlBlks; /* number of allocation blocks */
__be32 drAlBlkSiz; /* bytes in an allocation block */
__be32 drClpSiz; /* clumpsize, the number of bytes to
allocate when extending a file */
__be16 drAlBlSt; /* location (in 512-byte blocks)
of the first allocation block */
__be32 drNxtCNID; /* CNID to assign to the next
file or directory created */
__be16 drFreeBks; /* number of free allocation blocks */
u8 drVN[28]; /* the volume label */
__be32 drVolBkUp; /* fs backup date/time */
__be16 drVSeqNum; /* backup sequence number */
__be32 drWrCnt; /* fs write count */
__be32 drXTClpSiz; /* clumpsize for the extents B-tree */
__be32 drCTClpSiz; /* clumpsize for the catalog B-tree */
__be16 drNmRtDirs; /* number of directories in
the root directory */
__be32 drFilCnt; /* number of files in the fs */
__be32 drDirCnt; /* number of directories in the fs */
u8 drFndrInfo[32]; /* data used by the Finder */
__be16 drEmbedSigWord; /* embedded volume signature */
__be32 drEmbedExtent; /* starting block number (xdrStABN)
and number of allocation blocks
(xdrNumABlks) occupied by embedded
volume */
__be32 drXTFlSize; /* bytes in the extents B-tree */
hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */
__be32 drCTFlSize; /* bytes in the catalog B-tree */
hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */
} __packed;
#include <linux/hfs_common.h>
/*======== Data structures kept in memory ========*/

View File

@ -18,7 +18,6 @@
#include <asm/byteorder.h>
#include <linux/uaccess.h>
#include <linux/hfs_common.h>
#include "hfs.h"
@ -140,74 +139,90 @@ struct hfs_sb_info {
#define HFS_FLG_ALT_MDB_DIRTY 2
/* bitmap.c */
extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *);
extern int hfs_clear_vbm_bits(struct super_block *, u16, u16);
extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits);
extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count);
/* catalog.c */
extern int hfs_cat_keycmp(const btree_key *, const btree_key *);
extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2);
struct hfs_find_data;
extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *);
extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *);
extern int hfs_cat_delete(u32, struct inode *, const struct qstr *);
extern int hfs_cat_move(u32, struct inode *, const struct qstr *,
struct inode *, const struct qstr *);
extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *);
extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
struct hfs_find_data *fd);
extern int hfs_cat_create(u32 cnid, struct inode *dir,
const struct qstr *str, struct inode *inode);
extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str);
extern int hfs_cat_move(u32 cnid, struct inode *src_dir,
const struct qstr *src_name,
struct inode *dst_dir,
const struct qstr *dst_name);
extern void hfs_cat_build_key(struct super_block *sb, btree_key *key,
u32 parent, const struct qstr *name);
/* dir.c */
extern const struct file_operations hfs_dir_operations;
extern const struct inode_operations hfs_dir_inode_operations;
/* extent.c */
extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2);
extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off);
extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
extern int hfs_ext_write_extent(struct inode *);
extern int hfs_extend_file(struct inode *);
extern void hfs_file_truncate(struct inode *);
extern int hfs_free_fork(struct super_block *sb,
struct hfs_cat_file *file, int type);
extern int hfs_ext_write_extent(struct inode *inode);
extern int hfs_extend_file(struct inode *inode);
extern void hfs_file_truncate(struct inode *inode);
extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern int hfs_get_block(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create);
/* inode.c */
extern const struct address_space_operations hfs_aops;
extern const struct address_space_operations hfs_btree_aops;
int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, unsigned len, struct folio **foliop, void **fsdata);
extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t);
extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
extern int hfs_write_inode(struct inode *, struct writeback_control *);
extern int hfs_inode_setattr(struct mnt_idmap *, struct dentry *,
struct iattr *);
loff_t pos, unsigned int len, struct folio **foliop,
void **fsdata);
extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name,
umode_t mode);
extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext,
__be32 *log_size, __be32 *phys_size);
extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc);
extern int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr);
extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
__be32 log_size, __be32 phys_size, u32 clump_size);
extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *);
extern void hfs_evict_inode(struct inode *);
extern void hfs_delete_inode(struct inode *);
__be32 __log_size, __be32 phys_size,
u32 clump_size);
extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key,
hfs_cat_rec *rec);
extern void hfs_evict_inode(struct inode *inode);
extern void hfs_delete_inode(struct inode *inode);
/* attr.c */
extern const struct xattr_handler * const hfs_xattr_handlers[];
/* mdb.c */
extern int hfs_mdb_get(struct super_block *);
extern void hfs_mdb_commit(struct super_block *);
extern void hfs_mdb_close(struct super_block *);
extern void hfs_mdb_put(struct super_block *);
extern int hfs_mdb_get(struct super_block *sb);
extern void hfs_mdb_commit(struct super_block *sb);
extern void hfs_mdb_close(struct super_block *sb);
extern void hfs_mdb_put(struct super_block *sb);
/* part_tbl.c */
extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
extern int hfs_part_find(struct super_block *sb,
sector_t *part_start, sector_t *part_size);
/* string.c */
extern const struct dentry_operations hfs_dentry_operations;
extern int hfs_hash_dentry(const struct dentry *, struct qstr *);
extern int hfs_strcmp(const unsigned char *, unsigned int,
const unsigned char *, unsigned int);
extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this);
extern int hfs_strcmp(const unsigned char *s1, unsigned int len1,
const unsigned char *s2, unsigned int len2);
extern int hfs_compare_dentry(const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name);
unsigned int len, const char *str,
const struct qstr *name);
/* trans.c */
extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *);
extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *);
extern void hfs_asc2mac(struct super_block *sb,
struct hfs_name *out, const struct qstr *in);
extern int hfs_mac2asc(struct super_block *sb,
char *out, const struct hfs_name *in);
/* super.c */
extern void hfs_mark_mdb_dirty(struct super_block *sb);

View File

@ -45,7 +45,8 @@ static void hfs_write_failed(struct address_space *mapping, loff_t to)
}
int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
loff_t pos, unsigned int len, struct folio **foliop,
void **fsdata)
{
int ret;

View File

@ -16,6 +16,8 @@
#include "hfs_fs.h"
#include <linux/dcache.h>
#include <kunit/visibility.h>
/*================ File-local variables ================*/
/*
@ -65,6 +67,7 @@ int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this)
this->hash = end_name_hash(hash);
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(hfs_hash_dentry);
/*
* Compare two strings in the HFS filename character ordering
@ -87,6 +90,7 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1,
}
return len1 - len2;
}
EXPORT_SYMBOL_IF_KUNIT(hfs_strcmp);
/*
* Test for equality of two strings in the HFS filename character ordering.
@ -112,3 +116,4 @@ int hfs_compare_dentry(const struct dentry *dentry,
}
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(hfs_compare_dentry);

133
fs/hfs/string_test.c Normal file
View File

@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit tests for HFS string operations
*
* Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com>
*/
#include <kunit/test.h>
#include <linux/dcache.h>
#include "hfs_fs.h"
/* Test hfs_strcmp function */
static void hfs_strcmp_test(struct kunit *test)
{
/* Test equal strings */
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "hello", 5));
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("test", 4, "test", 4));
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("", 0, "", 0));
/* Test unequal strings */
KUNIT_EXPECT_NE(test, 0, hfs_strcmp("hello", 5, "world", 5));
KUNIT_EXPECT_NE(test, 0, hfs_strcmp("test", 4, "testing", 7));
/* Test different lengths */
KUNIT_EXPECT_LT(test, hfs_strcmp("test", 4, "testing", 7), 0);
KUNIT_EXPECT_GT(test, hfs_strcmp("testing", 7, "test", 4), 0);
/* Test case insensitive comparison (HFS should handle case) */
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("Test", 4, "TEST", 4));
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "HELLO", 5));
/* Test with special characters */
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("file.txt", 8, "file.txt", 8));
KUNIT_EXPECT_NE(test, 0, hfs_strcmp("file.txt", 8, "file.dat", 8));
/* Test boundary cases */
KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("a", 1, "a", 1));
KUNIT_EXPECT_NE(test, 0, hfs_strcmp("a", 1, "b", 1));
}
/* Test hfs_hash_dentry function */
static void hfs_hash_dentry_test(struct kunit *test)
{
struct qstr test_name1, test_name2, test_name3;
struct dentry dentry = {};
char name1[] = "testfile";
char name2[] = "TestFile";
char name3[] = "different";
/* Initialize test strings */
test_name1.name = name1;
test_name1.len = strlen(name1);
test_name1.hash = 0;
test_name2.name = name2;
test_name2.len = strlen(name2);
test_name2.hash = 0;
test_name3.name = name3;
test_name3.len = strlen(name3);
test_name3.hash = 0;
/* Test hashing */
KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name1));
KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name2));
KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name3));
/* Case insensitive names should hash the same */
KUNIT_EXPECT_EQ(test, test_name1.hash, test_name2.hash);
/* Different names should have different hashes */
KUNIT_EXPECT_NE(test, test_name1.hash, test_name3.hash);
}
/* Test hfs_compare_dentry function */
static void hfs_compare_dentry_test(struct kunit *test)
{
struct qstr test_name;
struct dentry dentry = {};
char name[] = "TestFile";
test_name.name = name;
test_name.len = strlen(name);
/* Test exact match */
KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8,
"TestFile", &test_name));
/* Test case insensitive match */
KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8,
"testfile", &test_name));
KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8,
"TESTFILE", &test_name));
/* Test different names */
KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 8,
"DiffFile", &test_name));
/* Test different lengths */
KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 7,
"TestFil", &test_name));
KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 9,
"TestFiles", &test_name));
/* Test empty string */
test_name.name = "";
test_name.len = 0;
KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 0, "", &test_name));
/* Test HFS_NAMELEN boundary */
test_name.name = "This_is_a_very_long_filename_that_exceeds_normal_limits";
test_name.len = strlen(test_name.name);
KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, HFS_NAMELEN,
"This_is_a_very_long_filename_th", &test_name));
}
static struct kunit_case hfs_string_test_cases[] = {
KUNIT_CASE(hfs_strcmp_test),
KUNIT_CASE(hfs_hash_dentry_test),
KUNIT_CASE(hfs_compare_dentry_test),
{}
};
static struct kunit_suite hfs_string_test_suite = {
.name = "hfs_string",
.test_cases = hfs_string_test_cases,
};
kunit_test_suite(hfs_string_test_suite);
MODULE_DESCRIPTION("KUnit tests for HFS string operations");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");

8
fs/hfsplus/.kunitconfig Normal file
View File

@ -0,0 +1,8 @@
CONFIG_KUNIT=y
CONFIG_HFSPLUS_FS=y
CONFIG_HFSPLUS_KUNIT_TEST=y
CONFIG_BLOCK=y
CONFIG_BUFFER_HEAD=y
CONFIG_NLS=y
CONFIG_NLS_UTF8=y
CONFIG_LEGACY_DIRECT_IO=y

View File

@ -14,3 +14,18 @@ config HFSPLUS_FS
MacOS 8. It includes all Mac specific filesystem data such as
data forks and creator codes, but it also has several UNIX
style features such as file ownership and permissions.
config HFSPLUS_KUNIT_TEST
tristate "KUnit tests for HFS+ filesystem" if !KUNIT_ALL_TESTS
depends on HFSPLUS_FS && KUNIT
default KUNIT_ALL_TESTS
help
This builds KUnit tests for the HFS+ filesystem.
KUnit tests run during boot and output the results to the debug
log in TAP format (https://testanything.org/). Only useful for
kernel devs running KUnit test harness and are not for inclusion
into a production build.
For more information on KUnit and unit tests in general please
refer to the KUnit documentation in Documentation/dev-tools/kunit/.

View File

@ -8,3 +8,6 @@ obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \
attributes.o xattr.o xattr_user.o xattr_security.o xattr_trusted.o
# KUnit tests
obj-$(CONFIG_HFSPLUS_KUNIT_TEST) += unicode_test.o

View File

@ -210,7 +210,7 @@ int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare)
return res;
}
int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
{
int res;

View File

@ -20,10 +20,10 @@
/* Copy a specified range of bytes from the raw data of a node */
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page **pagep;
int l;
u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@ -31,7 +31,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, len %d\n",
"node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@ -43,17 +43,17 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
l = min_t(int, len, PAGE_SIZE - off);
l = min_t(u32, len, PAGE_SIZE - off);
memcpy_from_page(buf, *pagep, off, l);
while ((len -= l) != 0) {
buf += l;
l = min_t(int, len, PAGE_SIZE);
l = min_t(u32, len, PAGE_SIZE);
memcpy_from_page(buf, *++pagep, 0, l);
}
}
u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
{
__be16 data;
/* TODO: optimize later... */
@ -61,7 +61,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
return be16_to_cpu(data);
}
u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
{
u8 data;
/* TODO: optimize later... */
@ -69,10 +69,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
return data;
}
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
{
struct hfs_btree *tree;
int key_len;
u32 key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
@ -84,17 +84,17 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) {
memset(key, 0, sizeof(hfsplus_btree_key));
pr_err("hfsplus: Invalid key length: %d\n", key_len);
pr_err("hfsplus: Invalid key length: %u\n", key_len);
return;
}
hfs_bnode_read(node, key, off, key_len);
}
void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page **pagep;
int l;
u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@ -102,7 +102,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, len %d\n",
"node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@ -114,29 +114,29 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
l = min_t(int, len, PAGE_SIZE - off);
l = min_t(u32, len, PAGE_SIZE - off);
memcpy_to_page(*pagep, off, buf, l);
set_page_dirty(*pagep);
while ((len -= l) != 0) {
buf += l;
l = min_t(int, len, PAGE_SIZE);
l = min_t(u32, len, PAGE_SIZE);
memcpy_to_page(*++pagep, 0, buf, l);
set_page_dirty(*pagep);
}
}
void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
{
__be16 v = cpu_to_be16(data);
/* TODO: optimize later... */
hfs_bnode_write(node, &v, off, 2);
}
void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
{
struct page **pagep;
int l;
u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@ -144,7 +144,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, len %d\n",
"node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@ -156,22 +156,22 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
l = min_t(int, len, PAGE_SIZE - off);
l = min_t(u32, len, PAGE_SIZE - off);
memzero_page(*pagep, off, l);
set_page_dirty(*pagep);
while ((len -= l) != 0) {
l = min_t(int, len, PAGE_SIZE);
l = min_t(u32, len, PAGE_SIZE);
memzero_page(*++pagep, 0, l);
set_page_dirty(*pagep);
}
}
void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
struct hfs_bnode *src_node, int src, int len)
void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
struct hfs_bnode *src_node, u32 src, u32 len)
{
struct page **src_page, **dst_page;
int l;
u32 l;
hfs_dbg("dst %u, src %u, len %u\n", dst, src, len);
if (!len)
@ -188,12 +188,12 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
dst &= ~PAGE_MASK;
if (src == dst) {
l = min_t(int, len, PAGE_SIZE - src);
l = min_t(u32, len, PAGE_SIZE - src);
memcpy_page(*dst_page, src, *src_page, src, l);
set_page_dirty(*dst_page);
while ((len -= l) != 0) {
l = min_t(int, len, PAGE_SIZE);
l = min_t(u32, len, PAGE_SIZE);
memcpy_page(*++dst_page, 0, *++src_page, 0, l);
set_page_dirty(*dst_page);
}
@ -225,11 +225,11 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
}
}
void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
{
struct page **src_page, **dst_page;
void *src_ptr, *dst_ptr;
int l;
u32 l;
hfs_dbg("dst %u, src %u, len %u\n", dst, src, len);
if (!len)
@ -299,7 +299,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
dst &= ~PAGE_MASK;
if (src == dst) {
l = min_t(int, len, PAGE_SIZE - src);
l = min_t(u32, len, PAGE_SIZE - src);
dst_ptr = kmap_local_page(*dst_page) + src;
src_ptr = kmap_local_page(*src_page) + src;
@ -309,7 +309,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
kunmap_local(dst_ptr);
while ((len -= l) != 0) {
l = min_t(int, len, PAGE_SIZE);
l = min_t(u32, len, PAGE_SIZE);
dst_ptr = kmap_local_page(*++dst_page);
src_ptr = kmap_local_page(*++src_page);
memmove(dst_ptr, src_ptr, l);
@ -481,6 +481,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
tree->node_hash[hash] = node;
tree->node_hash_cnt++;
} else {
hfs_bnode_get(node2);
spin_unlock(&tree->hash_lock);
kfree(node);
wait_event(node2->lock_wq,
@ -704,6 +705,5 @@ bool hfs_bnode_need_zeroout(struct hfs_btree *tree)
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
const u32 volume_attr = be32_to_cpu(sbi->s_vhdr->attributes);
return tree->cnid == HFSPLUS_CAT_CNID &&
volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX;
return volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX;
}

View File

@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
return retval;
}
int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
{
struct hfs_btree *tree;
struct hfs_bnode *node, *new_node;

View File

@ -344,7 +344,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
}
/* Make sure @tree has enough space for the @rsvd_nodes */
int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
{
struct inode *inode = tree->inode;
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);

View File

@ -552,8 +552,13 @@ static int hfsplus_rename(struct mnt_idmap *idmap,
res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
old_dir, &old_dentry->d_name,
new_dir, &new_dentry->d_name);
if (!res)
if (!res) {
new_dentry->d_fsdata = old_dentry->d_fsdata;
res = hfsplus_cat_write_inode(old_dir);
if (!res)
res = hfsplus_cat_write_inode(new_dir);
}
return res;
}

View File

@ -16,7 +16,6 @@
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include <linux/fs_context.h>
#include <linux/hfs_common.h>
#include "hfsplus_raw.h"
/* Runtime config options */
@ -357,21 +356,21 @@ u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors,
struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id);
void hfs_btree_close(struct hfs_btree *tree);
int hfs_btree_write(struct hfs_btree *tree);
int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes);
int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
void hfs_bmap_free(struct hfs_bnode *node);
/* bnode.c */
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len);
u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off);
u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off);
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off);
void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len);
void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data);
void hfs_bnode_clear(struct hfs_bnode *node, int off, int len);
void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
struct hfs_bnode *src_node, int src, int len);
void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len);
void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
struct hfs_bnode *src_node, u32 src, u32 len);
void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
void hfs_bnode_dump(struct hfs_bnode *node);
void hfs_bnode_unlink(struct hfs_bnode *node);
struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
@ -386,7 +385,7 @@ bool hfs_bnode_need_zeroout(struct hfs_btree *tree);
/* brec.c */
u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len);
int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
int hfs_brec_remove(struct hfs_find_data *fd);
/* bfind.c */
@ -399,7 +398,7 @@ int hfs_find_rec_by_key(struct hfs_bnode *bnode, struct hfs_find_data *fd,
int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
search_strategy_t rec_found);
int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare);
int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len);
int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
/* catalog.c */
@ -477,6 +476,8 @@ int hfs_part_find(struct super_block *sb, sector_t *part_start,
/* super.c */
struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino);
void hfsplus_mark_mdb_dirty(struct super_block *sb);
void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr);
int hfsplus_commit_superblock(struct super_block *sb);
/* tables.c */
extern u16 hfsplus_case_fold_table[];
@ -549,14 +550,14 @@ hfsplus_btree_lock_class(struct hfs_btree *tree)
}
static inline
bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
{
bool is_valid = off < node->tree->node_size;
if (!is_valid) {
pr_err("requested invalid offset: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d\n",
"node_size %u, offset %u\n",
node->this, node->type, node->height,
node->tree->node_size, off);
}
@ -565,7 +566,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
}
static inline
int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
{
unsigned int node_size;
@ -575,12 +576,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
node_size = node->tree->node_size;
if ((off + len) > node_size) {
int new_len = (int)node_size - off;
u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
"NODE: id %u, type %#x, height %u, "
"node_size %u, offset %d, "
"requested_len %d, corrected_len %d\n",
"node_size %u, offset %u, "
"requested_len %u, corrected_len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len, new_len);

View File

@ -15,398 +15,6 @@
#define _LINUX_HFSPLUS_RAW_H
#include <linux/types.h>
/* Some constants */
#define HFSPLUS_SECTOR_SIZE 512
#define HFSPLUS_SECTOR_SHIFT 9
#define HFSPLUS_VOLHEAD_SECTOR 2
#define HFSPLUS_VOLHEAD_SIG 0x482b
#define HFSPLUS_VOLHEAD_SIGX 0x4858
#define HFSPLUS_SUPER_MAGIC 0x482b
#define HFSPLUS_MIN_VERSION 4
#define HFSPLUS_CURRENT_VERSION 5
#define HFSP_WRAP_MAGIC 0x4244
#define HFSP_WRAP_ATTRIB_SLOCK 0x8000
#define HFSP_WRAP_ATTRIB_SPARED 0x0200
#define HFSP_WRAPOFF_SIG 0x00
#define HFSP_WRAPOFF_ATTRIB 0x0A
#define HFSP_WRAPOFF_ABLKSIZE 0x14
#define HFSP_WRAPOFF_ABLKSTART 0x1C
#define HFSP_WRAPOFF_EMBEDSIG 0x7C
#define HFSP_WRAPOFF_EMBEDEXT 0x7E
#define HFSP_HIDDENDIR_NAME \
"\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data"
#define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */
#define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */
#define HFSP_SYMLINK_TYPE 0x736c6e6b /* 'slnk' */
#define HFSP_SYMLINK_CREATOR 0x72686170 /* 'rhap' */
#define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */
/* Structures used on disk */
typedef __be32 hfsplus_cnid;
typedef __be16 hfsplus_unichr;
#define HFSPLUS_MAX_STRLEN 255
#define HFSPLUS_ATTR_MAX_STRLEN 127
/* A "string" as used in filenames, etc. */
struct hfsplus_unistr {
__be16 length;
hfsplus_unichr unicode[HFSPLUS_MAX_STRLEN];
} __packed;
/*
* A "string" is used in attributes file
* for name of extended attribute
*/
struct hfsplus_attr_unistr {
__be16 length;
hfsplus_unichr unicode[HFSPLUS_ATTR_MAX_STRLEN];
} __packed;
/* POSIX permissions */
struct hfsplus_perm {
__be32 owner;
__be32 group;
u8 rootflags;
u8 userflags;
__be16 mode;
__be32 dev;
} __packed;
#define HFSPLUS_FLG_NODUMP 0x01
#define HFSPLUS_FLG_IMMUTABLE 0x02
#define HFSPLUS_FLG_APPEND 0x04
/* A single contiguous area of a file */
struct hfsplus_extent {
__be32 start_block;
__be32 block_count;
} __packed;
typedef struct hfsplus_extent hfsplus_extent_rec[8];
/* Information for a "Fork" in a file */
struct hfsplus_fork_raw {
__be64 total_size;
__be32 clump_size;
__be32 total_blocks;
hfsplus_extent_rec extents;
} __packed;
/* HFS+ Volume Header */
struct hfsplus_vh {
__be16 signature;
__be16 version;
__be32 attributes;
__be32 last_mount_vers;
u32 reserved;
__be32 create_date;
__be32 modify_date;
__be32 backup_date;
__be32 checked_date;
__be32 file_count;
__be32 folder_count;
__be32 blocksize;
__be32 total_blocks;
__be32 free_blocks;
__be32 next_alloc;
__be32 rsrc_clump_sz;
__be32 data_clump_sz;
hfsplus_cnid next_cnid;
__be32 write_count;
__be64 encodings_bmp;
u32 finder_info[8];
struct hfsplus_fork_raw alloc_file;
struct hfsplus_fork_raw ext_file;
struct hfsplus_fork_raw cat_file;
struct hfsplus_fork_raw attr_file;
struct hfsplus_fork_raw start_file;
} __packed;
/* HFS+ volume attributes */
#define HFSPLUS_VOL_UNMNT (1 << 8)
#define HFSPLUS_VOL_SPARE_BLK (1 << 9)
#define HFSPLUS_VOL_NOCACHE (1 << 10)
#define HFSPLUS_VOL_INCNSTNT (1 << 11)
#define HFSPLUS_VOL_NODEID_REUSED (1 << 12)
#define HFSPLUS_VOL_JOURNALED (1 << 13)
#define HFSPLUS_VOL_SOFTLOCK (1 << 15)
#define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31)
/* HFS+ BTree node descriptor */
struct hfs_bnode_desc {
__be32 next;
__be32 prev;
s8 type;
u8 height;
__be16 num_recs;
u16 reserved;
} __packed;
/* HFS+ BTree node types */
#define HFS_NODE_INDEX 0x00 /* An internal (index) node */
#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */
#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */
#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */
/* HFS+ BTree header */
struct hfs_btree_header_rec {
__be16 depth;
__be32 root;
__be32 leaf_count;
__be32 leaf_head;
__be32 leaf_tail;
__be16 node_size;
__be16 max_key_len;
__be32 node_count;
__be32 free_nodes;
u16 reserved1;
__be32 clump_size;
u8 btree_type;
u8 key_type;
__be32 attributes;
u32 reserved3[16];
} __packed;
/* BTree attributes */
#define HFS_TREE_BIGKEYS 2
#define HFS_TREE_VARIDXKEYS 4
/* HFS+ BTree misc info */
#define HFSPLUS_TREE_HEAD 0
#define HFSPLUS_NODE_MXSZ 32768
#define HFSPLUS_ATTR_TREE_NODE_SIZE 8192
#define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3
#define HFSPLUS_BTREE_HDR_USER_BYTES 128
/* Some special File ID numbers (stolen from hfs.h) */
#define HFSPLUS_POR_CNID 1 /* Parent Of the Root */
#define HFSPLUS_ROOT_CNID 2 /* ROOT directory */
#define HFSPLUS_EXT_CNID 3 /* EXTents B-tree */
#define HFSPLUS_CAT_CNID 4 /* CATalog B-tree */
#define HFSPLUS_BAD_CNID 5 /* BAD blocks file */
#define HFSPLUS_ALLOC_CNID 6 /* ALLOCation file */
#define HFSPLUS_START_CNID 7 /* STARTup file */
#define HFSPLUS_ATTR_CNID 8 /* ATTRibutes file */
#define HFSPLUS_EXCH_CNID 15 /* ExchangeFiles temp id */
#define HFSPLUS_FIRSTUSER_CNID 16 /* first available user id */
/* btree key type */
#define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */
#define HFSPLUS_KEY_BINARY 0xBC /* case-sensitive */
/* HFS+ catalog entry key */
struct hfsplus_cat_key {
__be16 key_len;
hfsplus_cnid parent;
struct hfsplus_unistr name;
} __packed;
#define HFSPLUS_CAT_KEYLEN (sizeof(struct hfsplus_cat_key))
/* Structs from hfs.h */
struct hfsp_point {
__be16 v;
__be16 h;
} __packed;
struct hfsp_rect {
__be16 top;
__be16 left;
__be16 bottom;
__be16 right;
} __packed;
/* HFS directory info (stolen from hfs.h */
struct DInfo {
struct hfsp_rect frRect;
__be16 frFlags;
struct hfsp_point frLocation;
__be16 frView;
} __packed;
struct DXInfo {
struct hfsp_point frScroll;
__be32 frOpenChain;
__be16 frUnused;
__be16 frComment;
__be32 frPutAway;
} __packed;
/* HFS+ folder data (part of an hfsplus_cat_entry) */
struct hfsplus_cat_folder {
__be16 type;
__be16 flags;
__be32 valence;
hfsplus_cnid id;
__be32 create_date;
__be32 content_mod_date;
__be32 attribute_mod_date;
__be32 access_date;
__be32 backup_date;
struct hfsplus_perm permissions;
struct_group_attr(info, __packed,
struct DInfo user_info;
struct DXInfo finder_info;
);
__be32 text_encoding;
__be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */
} __packed;
/* HFS file info (stolen from hfs.h) */
struct FInfo {
__be32 fdType;
__be32 fdCreator;
__be16 fdFlags;
struct hfsp_point fdLocation;
__be16 fdFldr;
} __packed;
struct FXInfo {
__be16 fdIconID;
u8 fdUnused[8];
__be16 fdComment;
__be32 fdPutAway;
} __packed;
/* HFS+ file data (part of a cat_entry) */
struct hfsplus_cat_file {
__be16 type;
__be16 flags;
u32 reserved1;
hfsplus_cnid id;
__be32 create_date;
__be32 content_mod_date;
__be32 attribute_mod_date;
__be32 access_date;
__be32 backup_date;
struct hfsplus_perm permissions;
struct_group_attr(info, __packed,
struct FInfo user_info;
struct FXInfo finder_info;
);
__be32 text_encoding;
u32 reserved2;
struct hfsplus_fork_raw data_fork;
struct hfsplus_fork_raw rsrc_fork;
} __packed;
/* File and folder flag bits */
#define HFSPLUS_FILE_LOCKED 0x0001
#define HFSPLUS_FILE_THREAD_EXISTS 0x0002
#define HFSPLUS_XATTR_EXISTS 0x0004
#define HFSPLUS_ACL_EXISTS 0x0008
#define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count
* (HFSX only) */
/* HFS+ catalog thread (part of a cat_entry) */
struct hfsplus_cat_thread {
__be16 type;
s16 reserved;
hfsplus_cnid parentID;
struct hfsplus_unistr nodeName;
} __packed;
#define HFSPLUS_MIN_THREAD_SZ 10
/* A data record in the catalog tree */
typedef union {
__be16 type;
struct hfsplus_cat_folder folder;
struct hfsplus_cat_file file;
struct hfsplus_cat_thread thread;
} __packed hfsplus_cat_entry;
/* HFS+ catalog entry type */
#define HFSPLUS_FOLDER 0x0001
#define HFSPLUS_FILE 0x0002
#define HFSPLUS_FOLDER_THREAD 0x0003
#define HFSPLUS_FILE_THREAD 0x0004
/* HFS+ extents tree key */
struct hfsplus_ext_key {
__be16 key_len;
u8 fork_type;
u8 pad;
hfsplus_cnid cnid;
__be32 start_block;
} __packed;
#define HFSPLUS_EXT_KEYLEN sizeof(struct hfsplus_ext_key)
#define HFSPLUS_XATTR_FINDER_INFO_NAME "com.apple.FinderInfo"
#define HFSPLUS_XATTR_ACL_NAME "com.apple.system.Security"
#define HFSPLUS_ATTR_INLINE_DATA 0x10
#define HFSPLUS_ATTR_FORK_DATA 0x20
#define HFSPLUS_ATTR_EXTENTS 0x30
/* HFS+ attributes tree key */
struct hfsplus_attr_key {
__be16 key_len;
__be16 pad;
hfsplus_cnid cnid;
__be32 start_block;
struct hfsplus_attr_unistr key_name;
} __packed;
#define HFSPLUS_ATTR_KEYLEN sizeof(struct hfsplus_attr_key)
/* HFS+ fork data attribute */
struct hfsplus_attr_fork_data {
__be32 record_type;
__be32 reserved;
struct hfsplus_fork_raw the_fork;
} __packed;
/* HFS+ extension attribute */
struct hfsplus_attr_extents {
__be32 record_type;
__be32 reserved;
struct hfsplus_extent extents;
} __packed;
#define HFSPLUS_MAX_INLINE_DATA_SIZE 3802
/* HFS+ attribute inline data */
struct hfsplus_attr_inline_data {
__be32 record_type;
__be32 reserved1;
u8 reserved2[6];
__be16 length;
u8 raw_bytes[HFSPLUS_MAX_INLINE_DATA_SIZE];
} __packed;
/* A data record in the attributes tree */
typedef union {
__be32 record_type;
struct hfsplus_attr_fork_data fork_data;
struct hfsplus_attr_extents extents;
struct hfsplus_attr_inline_data inline_data;
} __packed hfsplus_attr_entry;
/* HFS+ generic BTree key */
typedef union {
__be16 key_len;
struct hfsplus_cat_key cat;
struct hfsplus_ext_key ext;
struct hfsplus_attr_key attr;
} __packed hfsplus_btree_key;
#include <linux/hfs_common.h>
#endif

View File

@ -180,13 +180,29 @@ const struct dentry_operations hfsplus_dentry_operations = {
.d_compare = hfsplus_compare_dentry,
};
static void hfsplus_get_perms(struct inode *inode,
struct hfsplus_perm *perms, int dir)
static int hfsplus_get_perms(struct inode *inode,
struct hfsplus_perm *perms, int dir)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
u16 mode;
mode = be16_to_cpu(perms->mode);
if (dir) {
if (mode && !S_ISDIR(mode))
goto bad_type;
} else if (mode) {
switch (mode & S_IFMT) {
case S_IFREG:
case S_IFLNK:
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
case S_IFSOCK:
break;
default:
goto bad_type;
}
}
i_uid_write(inode, be32_to_cpu(perms->owner));
if ((test_bit(HFSPLUS_SB_UID, &sbi->flags)) || (!i_uid_read(inode) && !mode))
@ -212,6 +228,10 @@ static void hfsplus_get_perms(struct inode *inode,
inode->i_flags |= S_APPEND;
else
inode->i_flags &= ~S_APPEND;
return 0;
bad_type:
pr_err("invalid file type 0%04o for inode %lu\n", mode, inode->i_ino);
return -EIO;
}
static int hfsplus_file_open(struct inode *inode, struct file *file)
@ -305,6 +325,7 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
struct inode *inode = file->f_mapping->host;
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
struct hfsplus_vh *vhdr = sbi->s_vhdr;
int error = 0, error2;
error = file_write_and_wait_range(file, start, end);
@ -348,6 +369,14 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
error = error2;
}
mutex_lock(&sbi->vh_mutex);
hfsplus_prepare_volume_header_for_commit(vhdr);
mutex_unlock(&sbi->vh_mutex);
error2 = hfsplus_commit_superblock(inode->i_sb);
if (!error)
error = error2;
if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
blkdev_issue_flush(inode->i_sb->s_bdev);
@ -516,7 +545,9 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
}
hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
sizeof(struct hfsplus_cat_folder));
hfsplus_get_perms(inode, &folder->permissions, 1);
res = hfsplus_get_perms(inode, &folder->permissions, 1);
if (res)
goto out;
set_nlink(inode, 1);
inode->i_size = 2 + be32_to_cpu(folder->valence);
inode_set_atime_to_ts(inode, hfsp_mt2ut(folder->access_date));
@ -545,7 +576,9 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
hfsplus_inode_read_fork(inode, HFSPLUS_IS_RSRC(inode) ?
&file->rsrc_fork : &file->data_fork);
hfsplus_get_perms(inode, &file->permissions, 0);
res = hfsplus_get_perms(inode, &file->permissions, 0);
if (res)
goto out;
set_nlink(inode, 1);
if (S_ISREG(inode->i_mode)) {
if (file->permissions.dev)

View File

@ -187,40 +187,15 @@ static void hfsplus_evict_inode(struct inode *inode)
}
}
static int hfsplus_sync_fs(struct super_block *sb, int wait)
int hfsplus_commit_superblock(struct super_block *sb)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
struct hfsplus_vh *vhdr = sbi->s_vhdr;
int write_backup = 0;
int error, error2;
if (!wait)
return 0;
int error = 0, error2;
hfs_dbg("starting...\n");
/*
* Explicitly write out the special metadata inodes.
*
* While these special inodes are marked as hashed and written
* out peridocically by the flusher threads we redirty them
* during writeout of normal inodes, and thus the life lock
* prevents us from getting the latest state to disk.
*/
error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
if (!error)
error = error2;
if (sbi->attr_tree) {
error2 =
filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
if (!error)
error = error2;
}
error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
if (!error)
error = error2;
mutex_lock(&sbi->vh_mutex);
mutex_lock(&sbi->alloc_mutex);
vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
@ -249,11 +224,52 @@ static int hfsplus_sync_fs(struct super_block *sb, int wait)
sbi->part_start + sbi->sect_count - 2,
sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE);
if (!error)
error2 = error;
error = error2;
out:
mutex_unlock(&sbi->alloc_mutex);
mutex_unlock(&sbi->vh_mutex);
hfs_dbg("finished: err %d\n", error);
return error;
}
static int hfsplus_sync_fs(struct super_block *sb, int wait)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
int error, error2;
if (!wait)
return 0;
hfs_dbg("starting...\n");
/*
* Explicitly write out the special metadata inodes.
*
* While these special inodes are marked as hashed and written
* out peridocically by the flusher threads we redirty them
* during writeout of normal inodes, and thus the life lock
* prevents us from getting the latest state to disk.
*/
error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
if (!error)
error = error2;
if (sbi->attr_tree) {
error2 =
filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
if (!error)
error = error2;
}
error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
if (!error)
error = error2;
error2 = hfsplus_commit_superblock(sb);
if (!error)
error = error2;
if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
blkdev_issue_flush(sb->s_bdev);
@ -395,6 +411,15 @@ static const struct super_operations hfsplus_sops = {
.show_options = hfsplus_show_options,
};
void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr)
{
vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
vhdr->modify_date = hfsp_now2mt();
be32_add_cpu(&vhdr->write_count, 1);
vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
}
static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct hfsplus_vh *vhdr;
@ -562,11 +587,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
* all three are registered with Apple for our use
*/
vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
vhdr->modify_date = hfsp_now2mt();
be32_add_cpu(&vhdr->write_count, 1);
vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
hfsplus_prepare_volume_header_for_commit(vhdr);
hfsplus_sync_fs(sb, 1);
if (!sbi->hidden_dir) {

View File

@ -11,6 +11,9 @@
#include <linux/types.h>
#include <linux/nls.h>
#include <kunit/visibility.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
@ -72,6 +75,7 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *s1,
return 0;
}
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcasecmp);
/* Compare names as a sequence of 16-bit unsigned integers */
int hfsplus_strcmp(const struct hfsplus_unistr *s1,
@ -110,7 +114,7 @@ int hfsplus_strcmp(const struct hfsplus_unistr *s1,
return len1 < len2 ? -1 :
len1 > len2 ? 1 : 0;
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcmp);
#define Hangul_SBase 0xac00
#define Hangul_LBase 0x1100
@ -143,8 +147,9 @@ static u16 *hfsplus_compose_lookup(u16 *p, u16 cc)
return NULL;
}
static int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr,
int max_len, char *astr, int *len_p)
static int hfsplus_uni2asc(struct super_block *sb,
const struct hfsplus_unistr *ustr,
int max_len, char *astr, int *len_p)
{
const hfsplus_unichr *ip;
struct nls_table *nls = HFSPLUS_SB(sb)->nls;
@ -285,6 +290,7 @@ inline int hfsplus_uni2asc_str(struct super_block *sb,
{
return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p);
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_str);
inline int hfsplus_uni2asc_xattr_str(struct super_block *sb,
const struct hfsplus_attr_unistr *ustr,
@ -293,6 +299,7 @@ inline int hfsplus_uni2asc_xattr_str(struct super_block *sb,
return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr,
HFSPLUS_ATTR_MAX_STRLEN, astr, len_p);
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_xattr_str);
/*
* Convert one or more ASCII characters into a single unicode character.
@ -420,6 +427,7 @@ int hfsplus_asc2uni(struct super_block *sb,
return -ENAMETOOLONG;
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_asc2uni);
/*
* Hash a string to an integer as appropriate for the HFS+ filesystem.
@ -472,6 +480,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_hash_dentry);
/*
* Compare strings with HFS+ filename ordering.
@ -563,3 +572,4 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
return 1;
return 0;
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_compare_dentry);

1579
fs/hfsplus/unicode_test.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -265,10 +265,8 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,
struct hfs_find_data cat_fd;
hfsplus_cat_entry entry;
u16 cat_entry_flags, cat_entry_type;
u16 folder_finderinfo_len = sizeof(struct DInfo) +
sizeof(struct DXInfo);
u16 file_finderinfo_len = sizeof(struct FInfo) +
sizeof(struct FXInfo);
u16 folder_finderinfo_len = sizeof(DInfo) + sizeof(DXInfo);
u16 file_finderinfo_len = sizeof(FInfo) + sizeof(FXInfo);
if ((!S_ISREG(inode->i_mode) &&
!S_ISDIR(inode->i_mode)) ||
@ -444,11 +442,11 @@ static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
ssize_t res = 0;
struct hfs_find_data fd;
u16 entry_type;
u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
u16 folder_rec_len = sizeof(DInfo) + sizeof(DXInfo);
u16 file_rec_len = sizeof(FInfo) + sizeof(FXInfo);
u16 record_len = max(folder_rec_len, file_rec_len);
u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)];
u8 file_finder_info[sizeof(FInfo) + sizeof(FXInfo)];
if (size >= record_len) {
res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
@ -612,8 +610,8 @@ static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
struct inode *inode = d_inode(dentry);
struct hfs_find_data fd;
u16 entry_type;
u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)];
u8 file_finder_info[sizeof(FInfo) + sizeof(FXInfo)];
unsigned long len, found_bit;
int xattr_name_len, symbols_count;
@ -629,14 +627,14 @@ static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
if (entry_type == HFSPLUS_FOLDER) {
len = sizeof(struct DInfo) + sizeof(struct DXInfo);
len = sizeof(DInfo) + sizeof(DXInfo);
hfs_bnode_read(fd.bnode, folder_finder_info,
fd.entryoffset +
offsetof(struct hfsplus_cat_folder, user_info),
len);
found_bit = find_first_bit((void *)folder_finder_info, len*8);
} else if (entry_type == HFSPLUS_FILE) {
len = sizeof(struct FInfo) + sizeof(struct FXInfo);
len = sizeof(FInfo) + sizeof(FXInfo);
hfs_bnode_read(fd.bnode, file_finder_info,
fd.entryoffset +
offsetof(struct hfsplus_cat_file, user_info),

View File

@ -17,4 +17,637 @@
pr_debug("pid %d:%s:%d %s(): " fmt, \
current->pid, __FILE__, __LINE__, __func__, ##__VA_ARGS__) \
/*
* Format of structures on disk
* Information taken from Apple Technote #1150 (HFS Plus Volume Format)
*/
/* offsets to various blocks */
#define HFS_DD_BLK 0 /* Driver Descriptor block */
#define HFS_PMAP_BLK 1 /* First block of partition map */
#define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */
/* magic numbers for various disk blocks */
#define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */
#define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */
#define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */
#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */
#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */
#define HFSPLUS_VOLHEAD_SIG 0x482b
#define HFSPLUS_VOLHEAD_SIGX 0x4858
#define HFSPLUS_SUPER_MAGIC 0x482b
#define HFSP_WRAP_MAGIC 0x4244
#define HFSP_WRAP_ATTRIB_SLOCK 0x8000
#define HFSP_WRAP_ATTRIB_SPARED 0x0200
#define HFSP_WRAPOFF_SIG 0x00
#define HFSP_WRAPOFF_ATTRIB 0x0A
#define HFSP_WRAPOFF_ABLKSIZE 0x14
#define HFSP_WRAPOFF_ABLKSTART 0x1C
#define HFSP_WRAPOFF_EMBEDSIG 0x7C
#define HFSP_WRAPOFF_EMBEDEXT 0x7E
#define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */
#define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */
#define HFSP_SYMLINK_TYPE 0x736c6e6b /* 'slnk' */
#define HFSP_SYMLINK_CREATOR 0x72686170 /* 'rhap' */
#define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */
#define HFSP_HIDDENDIR_NAME \
"\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data"
/* various FIXED size parameters */
#define HFS_SECTOR_SIZE 512 /* size of an HFS sector */
#define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */
#define HFS_MAX_VALENCE 32767U
#define HFSPLUS_SECTOR_SIZE HFS_SECTOR_SIZE
#define HFSPLUS_SECTOR_SHIFT HFS_SECTOR_SIZE_BITS
#define HFSPLUS_VOLHEAD_SECTOR 2
#define HFSPLUS_MIN_VERSION 4
#define HFSPLUS_CURRENT_VERSION 5
#define HFS_NAMELEN 31 /* maximum length of an HFS filename */
#define HFS_MAX_NAMELEN 128
#define HFSPLUS_MAX_STRLEN 255
#define HFSPLUS_ATTR_MAX_STRLEN 127
/* Meanings of the drAtrb field of the MDB,
* Reference: _Inside Macintosh: Files_ p. 2-61
*/
#define HFS_SB_ATTRIB_HLOCK (1 << 7)
#define HFS_SB_ATTRIB_UNMNT (1 << 8)
#define HFS_SB_ATTRIB_SPARED (1 << 9)
#define HFS_SB_ATTRIB_INCNSTNT (1 << 11)
#define HFS_SB_ATTRIB_SLOCK (1 << 15)
/* values for hfs_cat_rec.cdrType */
#define HFS_CDR_DIR 0x01 /* folder (directory) */
#define HFS_CDR_FIL 0x02 /* file */
#define HFS_CDR_THD 0x03 /* folder (directory) thread */
#define HFS_CDR_FTH 0x04 /* file thread */
/* legal values for hfs_ext_key.FkType and hfs_file.fork */
#define HFS_FK_DATA 0x00
#define HFS_FK_RSRC 0xFF
/* bits in hfs_fil_entry.Flags */
#define HFS_FIL_LOCK 0x01 /* locked */
#define HFS_FIL_THD 0x02 /* file thread */
#define HFS_FIL_DOPEN 0x04 /* data fork open */
#define HFS_FIL_ROPEN 0x08 /* resource fork open */
#define HFS_FIL_DIR 0x10 /* directory (always clear) */
#define HFS_FIL_NOCOPY 0x40 /* copy-protected file */
#define HFS_FIL_USED 0x80 /* open */
/* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */
#define HFS_DIR_LOCK 0x01 /* locked */
#define HFS_DIR_THD 0x02 /* directory thread */
#define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */
#define HFS_DIR_MOUNTED 0x08 /* mounted */
#define HFS_DIR_DIR 0x10 /* directory (always set) */
#define HFS_DIR_EXPFOLDER 0x20 /* share point */
/* bits hfs_finfo.fdFlags */
#define HFS_FLG_INITED 0x0100
#define HFS_FLG_LOCKED 0x1000
#define HFS_FLG_INVISIBLE 0x4000
/* Some special File ID numbers */
#define HFS_POR_CNID 1 /* Parent Of the Root */
#define HFSPLUS_POR_CNID HFS_POR_CNID
#define HFS_ROOT_CNID 2 /* ROOT directory */
#define HFSPLUS_ROOT_CNID HFS_ROOT_CNID
#define HFS_EXT_CNID 3 /* EXTents B-tree */
#define HFSPLUS_EXT_CNID HFS_EXT_CNID
#define HFS_CAT_CNID 4 /* CATalog B-tree */
#define HFSPLUS_CAT_CNID HFS_CAT_CNID
#define HFS_BAD_CNID 5 /* BAD blocks file */
#define HFSPLUS_BAD_CNID HFS_BAD_CNID
#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */
#define HFSPLUS_ALLOC_CNID HFS_ALLOC_CNID
#define HFS_START_CNID 7 /* STARTup file (HFS+) */
#define HFSPLUS_START_CNID HFS_START_CNID
#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */
#define HFSPLUS_ATTR_CNID HFS_ATTR_CNID
#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */
#define HFSPLUS_EXCH_CNID HFS_EXCH_CNID
#define HFS_FIRSTUSER_CNID 16 /* first available user id */
#define HFSPLUS_FIRSTUSER_CNID HFS_FIRSTUSER_CNID
/*======== HFS/HFS+ structures as they appear on the disk ========*/
typedef __be32 hfsplus_cnid;
typedef __be16 hfsplus_unichr;
/* Pascal-style string of up to 31 characters */
struct hfs_name {
u8 len;
u8 name[HFS_NAMELEN];
} __packed;
/* A "string" as used in filenames, etc. */
struct hfsplus_unistr {
__be16 length;
hfsplus_unichr unicode[HFSPLUS_MAX_STRLEN];
} __packed;
/*
* A "string" is used in attributes file
* for name of extended attribute
*/
struct hfsplus_attr_unistr {
__be16 length;
hfsplus_unichr unicode[HFSPLUS_ATTR_MAX_STRLEN];
} __packed;
struct hfs_extent {
__be16 block;
__be16 count;
};
typedef struct hfs_extent hfs_extent_rec[3];
/* A single contiguous area of a file */
struct hfsplus_extent {
__be32 start_block;
__be32 block_count;
} __packed;
typedef struct hfsplus_extent hfsplus_extent_rec[8];
/* Information for a "Fork" in a file */
struct hfsplus_fork_raw {
__be64 total_size;
__be32 clump_size;
__be32 total_blocks;
hfsplus_extent_rec extents;
} __packed;
struct hfs_mdb {
__be16 drSigWord; /* Signature word indicating fs type */
__be32 drCrDate; /* fs creation date/time */
__be32 drLsMod; /* fs modification date/time */
__be16 drAtrb; /* fs attributes */
__be16 drNmFls; /* number of files in root directory */
__be16 drVBMSt; /* location (in 512-byte blocks)
of the volume bitmap */
__be16 drAllocPtr; /* location (in allocation blocks)
to begin next allocation search */
__be16 drNmAlBlks; /* number of allocation blocks */
__be32 drAlBlkSiz; /* bytes in an allocation block */
__be32 drClpSiz; /* clumpsize, the number of bytes to
allocate when extending a file */
__be16 drAlBlSt; /* location (in 512-byte blocks)
of the first allocation block */
__be32 drNxtCNID; /* CNID to assign to the next
file or directory created */
__be16 drFreeBks; /* number of free allocation blocks */
u8 drVN[28]; /* the volume label */
__be32 drVolBkUp; /* fs backup date/time */
__be16 drVSeqNum; /* backup sequence number */
__be32 drWrCnt; /* fs write count */
__be32 drXTClpSiz; /* clumpsize for the extents B-tree */
__be32 drCTClpSiz; /* clumpsize for the catalog B-tree */
__be16 drNmRtDirs; /* number of directories in
the root directory */
__be32 drFilCnt; /* number of files in the fs */
__be32 drDirCnt; /* number of directories in the fs */
u8 drFndrInfo[32]; /* data used by the Finder */
__be16 drEmbedSigWord; /* embedded volume signature */
__be32 drEmbedExtent; /* starting block number (xdrStABN)
and number of allocation blocks
(xdrNumABlks) occupied by embedded
volume */
__be32 drXTFlSize; /* bytes in the extents B-tree */
hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */
__be32 drCTFlSize; /* bytes in the catalog B-tree */
hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */
} __packed;
/* HFS+ Volume Header */
struct hfsplus_vh {
__be16 signature;
__be16 version;
__be32 attributes;
__be32 last_mount_vers;
u32 reserved;
__be32 create_date;
__be32 modify_date;
__be32 backup_date;
__be32 checked_date;
__be32 file_count;
__be32 folder_count;
__be32 blocksize;
__be32 total_blocks;
__be32 free_blocks;
__be32 next_alloc;
__be32 rsrc_clump_sz;
__be32 data_clump_sz;
hfsplus_cnid next_cnid;
__be32 write_count;
__be64 encodings_bmp;
u32 finder_info[8];
struct hfsplus_fork_raw alloc_file;
struct hfsplus_fork_raw ext_file;
struct hfsplus_fork_raw cat_file;
struct hfsplus_fork_raw attr_file;
struct hfsplus_fork_raw start_file;
} __packed;
/* HFS+ volume attributes */
#define HFSPLUS_VOL_UNMNT (1 << 8)
#define HFSPLUS_VOL_SPARE_BLK (1 << 9)
#define HFSPLUS_VOL_NOCACHE (1 << 10)
#define HFSPLUS_VOL_INCNSTNT (1 << 11)
#define HFSPLUS_VOL_NODEID_REUSED (1 << 12)
#define HFSPLUS_VOL_JOURNALED (1 << 13)
#define HFSPLUS_VOL_SOFTLOCK (1 << 15)
#define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31)
struct hfs_point {
__be16 v;
__be16 h;
} __packed;
typedef struct hfs_point hfsp_point;
struct hfs_rect {
__be16 top;
__be16 left;
__be16 bottom;
__be16 right;
} __packed;
typedef struct hfs_rect hfsp_rect;
struct hfs_finfo {
__be32 fdType;
__be32 fdCreator;
__be16 fdFlags;
struct hfs_point fdLocation;
__be16 fdFldr;
} __packed;
typedef struct hfs_finfo FInfo;
struct hfs_fxinfo {
__be16 fdIconID;
u8 fdUnused[8];
__be16 fdComment;
__be32 fdPutAway;
} __packed;
typedef struct hfs_fxinfo FXInfo;
struct hfs_dinfo {
struct hfs_rect frRect;
__be16 frFlags;
struct hfs_point frLocation;
__be16 frView;
} __packed;
typedef struct hfs_dinfo DInfo;
struct hfs_dxinfo {
struct hfs_point frScroll;
__be32 frOpenChain;
__be16 frUnused;
__be16 frComment;
__be32 frPutAway;
} __packed;
typedef struct hfs_dxinfo DXInfo;
union hfs_finder_info {
struct {
struct hfs_finfo finfo;
struct hfs_fxinfo fxinfo;
} file;
struct {
struct hfs_dinfo dinfo;
struct hfs_dxinfo dxinfo;
} dir;
} __packed;
/* The key used in the catalog b-tree: */
struct hfs_cat_key {
u8 key_len; /* number of bytes in the key */
u8 reserved; /* padding */
__be32 ParID; /* CNID of the parent dir */
struct hfs_name CName; /* The filename of the entry */
} __packed;
/* HFS+ catalog entry key */
struct hfsplus_cat_key {
__be16 key_len;
hfsplus_cnid parent;
struct hfsplus_unistr name;
} __packed;
#define HFSPLUS_CAT_KEYLEN (sizeof(struct hfsplus_cat_key))
/* The key used in the extents b-tree: */
struct hfs_ext_key {
u8 key_len; /* number of bytes in the key */
u8 FkType; /* HFS_FK_{DATA,RSRC} */
__be32 FNum; /* The File ID of the file */
__be16 FABN; /* allocation blocks number*/
} __packed;
/* HFS+ extents tree key */
struct hfsplus_ext_key {
__be16 key_len;
u8 fork_type;
u8 pad;
hfsplus_cnid cnid;
__be32 start_block;
} __packed;
#define HFSPLUS_EXT_KEYLEN sizeof(struct hfsplus_ext_key)
typedef union hfs_btree_key {
u8 key_len; /* number of bytes in the key */
struct hfs_cat_key cat;
struct hfs_ext_key ext;
} hfs_btree_key;
#define HFS_MAX_CAT_KEYLEN (sizeof(struct hfs_cat_key) - sizeof(u8))
#define HFS_MAX_EXT_KEYLEN (sizeof(struct hfs_ext_key) - sizeof(u8))
typedef union hfs_btree_key btree_key;
/* The catalog record for a file */
struct hfs_cat_file {
s8 type; /* The type of entry */
u8 reserved;
u8 Flags; /* Flags such as read-only */
s8 Typ; /* file version number = 0 */
struct hfs_finfo UsrWds; /* data used by the Finder */
__be32 FlNum; /* The CNID */
__be16 StBlk; /* obsolete */
__be32 LgLen; /* The logical EOF of the data fork*/
__be32 PyLen; /* The physical EOF of the data fork */
__be16 RStBlk; /* obsolete */
__be32 RLgLen; /* The logical EOF of the rsrc fork */
__be32 RPyLen; /* The physical EOF of the rsrc fork */
__be32 CrDat; /* The creation date */
__be32 MdDat; /* The modified date */
__be32 BkDat; /* The last backup date */
struct hfs_fxinfo FndrInfo; /* more data for the Finder */
__be16 ClpSize; /* number of bytes to allocate
when extending files */
hfs_extent_rec ExtRec; /* first extent record
for the data fork */
hfs_extent_rec RExtRec; /* first extent record
for the resource fork */
u32 Resrv; /* reserved by Apple */
} __packed;
/* the catalog record for a directory */
struct hfs_cat_dir {
s8 type; /* The type of entry */
u8 reserved;
__be16 Flags; /* flags */
__be16 Val; /* Valence: number of files and
dirs in the directory */
__be32 DirID; /* The CNID */
__be32 CrDat; /* The creation date */
__be32 MdDat; /* The modification date */
__be32 BkDat; /* The last backup date */
struct hfs_dinfo UsrInfo; /* data used by the Finder */
struct hfs_dxinfo FndrInfo; /* more data used by Finder */
u8 Resrv[16]; /* reserved by Apple */
} __packed;
/* the catalog record for a thread */
struct hfs_cat_thread {
s8 type; /* The type of entry */
u8 reserved[9]; /* reserved by Apple */
__be32 ParID; /* CNID of parent directory */
struct hfs_name CName; /* The name of this entry */
} __packed;
/* A catalog tree record */
typedef union hfs_cat_rec {
s8 type; /* The type of entry */
struct hfs_cat_file file;
struct hfs_cat_dir dir;
struct hfs_cat_thread thread;
} hfs_cat_rec;
/* POSIX permissions */
struct hfsplus_perm {
__be32 owner;
__be32 group;
u8 rootflags;
u8 userflags;
__be16 mode;
__be32 dev;
} __packed;
#define HFSPLUS_FLG_NODUMP 0x01
#define HFSPLUS_FLG_IMMUTABLE 0x02
#define HFSPLUS_FLG_APPEND 0x04
/* HFS/HFS+ BTree node descriptor */
struct hfs_bnode_desc {
__be32 next; /* (V) Number of the next node at this level */
__be32 prev; /* (V) Number of the prev node at this level */
u8 type; /* (F) The type of node */
u8 height; /* (F) The level of this node (leaves=1) */
__be16 num_recs; /* (V) The number of records in this node */
u16 reserved;
} __packed;
/* HFS/HFS+ BTree node types */
#define HFS_NODE_INDEX 0x00 /* An internal (index) node */
#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */
#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */
#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */
/* HFS/HFS+ BTree header */
struct hfs_btree_header_rec {
__be16 depth; /* (V) The number of levels in this B-tree */
__be32 root; /* (V) The node number of the root node */
__be32 leaf_count; /* (V) The number of leaf records */
__be32 leaf_head; /* (V) The number of the first leaf node */
__be32 leaf_tail; /* (V) The number of the last leaf node */
__be16 node_size; /* (F) The number of bytes in a node (=512) */
__be16 max_key_len; /* (F) The length of a key in an index node */
__be32 node_count; /* (V) The total number of nodes */
__be32 free_nodes; /* (V) The number of unused nodes */
u16 reserved1;
__be32 clump_size; /* (F) clump size. not usually used. */
u8 btree_type; /* (F) BTree type */
u8 key_type;
__be32 attributes; /* (F) attributes */
u32 reserved3[16];
} __packed;
/* BTree attributes */
#define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not
used by hfsplus. */
#define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8.
used by hfsplus. */
#define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of
max key length. use din catalog
b-tree but not in extents
b-tree (hfsplus). */
/* HFS+ BTree misc info */
#define HFSPLUS_TREE_HEAD 0
#define HFSPLUS_NODE_MXSZ 32768
#define HFSPLUS_ATTR_TREE_NODE_SIZE 8192
#define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3
#define HFSPLUS_BTREE_HDR_USER_BYTES 128
/* btree key type */
#define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */
#define HFSPLUS_KEY_BINARY 0xBC /* case-sensitive */
/* HFS+ folder data (part of an hfsplus_cat_entry) */
struct hfsplus_cat_folder {
__be16 type;
__be16 flags;
__be32 valence;
hfsplus_cnid id;
__be32 create_date;
__be32 content_mod_date;
__be32 attribute_mod_date;
__be32 access_date;
__be32 backup_date;
struct hfsplus_perm permissions;
struct_group_attr(info, __packed,
DInfo user_info;
DXInfo finder_info;
);
__be32 text_encoding;
__be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */
} __packed;
/* HFS+ file data (part of a cat_entry) */
struct hfsplus_cat_file {
__be16 type;
__be16 flags;
u32 reserved1;
hfsplus_cnid id;
__be32 create_date;
__be32 content_mod_date;
__be32 attribute_mod_date;
__be32 access_date;
__be32 backup_date;
struct hfsplus_perm permissions;
struct_group_attr(info, __packed,
FInfo user_info;
FXInfo finder_info;
);
__be32 text_encoding;
u32 reserved2;
struct hfsplus_fork_raw data_fork;
struct hfsplus_fork_raw rsrc_fork;
} __packed;
/* File and folder flag bits */
#define HFSPLUS_FILE_LOCKED 0x0001
#define HFSPLUS_FILE_THREAD_EXISTS 0x0002
#define HFSPLUS_XATTR_EXISTS 0x0004
#define HFSPLUS_ACL_EXISTS 0x0008
#define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count
* (HFSX only) */
/* HFS+ catalog thread (part of a cat_entry) */
struct hfsplus_cat_thread {
__be16 type;
s16 reserved;
hfsplus_cnid parentID;
struct hfsplus_unistr nodeName;
} __packed;
#define HFSPLUS_MIN_THREAD_SZ 10
/* A data record in the catalog tree */
typedef union {
__be16 type;
struct hfsplus_cat_folder folder;
struct hfsplus_cat_file file;
struct hfsplus_cat_thread thread;
} __packed hfsplus_cat_entry;
/* HFS+ catalog entry type */
#define HFSPLUS_FOLDER 0x0001
#define HFSPLUS_FILE 0x0002
#define HFSPLUS_FOLDER_THREAD 0x0003
#define HFSPLUS_FILE_THREAD 0x0004
#define HFSPLUS_XATTR_FINDER_INFO_NAME "com.apple.FinderInfo"
#define HFSPLUS_XATTR_ACL_NAME "com.apple.system.Security"
#define HFSPLUS_ATTR_INLINE_DATA 0x10
#define HFSPLUS_ATTR_FORK_DATA 0x20
#define HFSPLUS_ATTR_EXTENTS 0x30
/* HFS+ attributes tree key */
struct hfsplus_attr_key {
__be16 key_len;
__be16 pad;
hfsplus_cnid cnid;
__be32 start_block;
struct hfsplus_attr_unistr key_name;
} __packed;
#define HFSPLUS_ATTR_KEYLEN sizeof(struct hfsplus_attr_key)
/* HFS+ fork data attribute */
struct hfsplus_attr_fork_data {
__be32 record_type;
__be32 reserved;
struct hfsplus_fork_raw the_fork;
} __packed;
/* HFS+ extension attribute */
struct hfsplus_attr_extents {
__be32 record_type;
__be32 reserved;
struct hfsplus_extent extents;
} __packed;
#define HFSPLUS_MAX_INLINE_DATA_SIZE 3802
/* HFS+ attribute inline data */
struct hfsplus_attr_inline_data {
__be32 record_type;
__be32 reserved1;
u8 reserved2[6];
__be16 length;
u8 raw_bytes[HFSPLUS_MAX_INLINE_DATA_SIZE];
} __packed;
/* A data record in the attributes tree */
typedef union {
__be32 record_type;
struct hfsplus_attr_fork_data fork_data;
struct hfsplus_attr_extents extents;
struct hfsplus_attr_inline_data inline_data;
} __packed hfsplus_attr_entry;
/* HFS+ generic BTree key */
typedef union {
__be16 key_len;
struct hfsplus_cat_key cat;
struct hfsplus_ext_key ext;
struct hfsplus_attr_key attr;
} __packed hfsplus_btree_key;
#endif /* _HFS_COMMON_H_ */