Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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()

+2698 -914
+7
fs/hfs/.kunitconfig
··· 1 + CONFIG_KUNIT=y 2 + CONFIG_HFS_FS=y 3 + CONFIG_HFS_KUNIT_TEST=y 4 + CONFIG_BLOCK=y 5 + CONFIG_BUFFER_HEAD=y 6 + CONFIG_NLS=y 7 + CONFIG_LEGACY_DIRECT_IO=y
+15
fs/hfs/Kconfig
··· 13 13 14 14 To compile this file system support as a module, choose M here: the 15 15 module will be called hfs. 16 + 17 + config HFS_KUNIT_TEST 18 + tristate "KUnit tests for HFS filesystem" if !KUNIT_ALL_TESTS 19 + depends on HFS_FS && KUNIT 20 + default KUNIT_ALL_TESTS 21 + help 22 + This builds KUnit tests for the HFS filesystem. 23 + 24 + KUnit tests run during boot and output the results to the debug 25 + log in TAP format (https://testanything.org/). Only useful for 26 + kernel devs running KUnit test harness and are not for inclusion 27 + into a production build. 28 + 29 + For more information on KUnit and unit tests in general please 30 + refer to the KUnit documentation in Documentation/dev-tools/kunit/.
+2
fs/hfs/Makefile
··· 9 9 catalog.o dir.o extent.o inode.o attr.o mdb.o \ 10 10 part_tbl.o string.o super.o sysdep.o trans.o 11 11 12 + # KUnit tests 13 + obj-$(CONFIG_HFS_KUNIT_TEST) += string_test.o
+1 -1
fs/hfs/bfind.c
··· 167 167 return res; 168 168 } 169 169 170 - int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) 170 + int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len) 171 171 { 172 172 int res; 173 173
+26 -26
fs/hfs/bnode.c
··· 16 16 #include "btree.h" 17 17 18 18 static inline 19 - bool is_bnode_offset_valid(struct hfs_bnode *node, int off) 19 + bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) 20 20 { 21 21 bool is_valid = off < node->tree->node_size; 22 22 23 23 if (!is_valid) { 24 24 pr_err("requested invalid offset: " 25 25 "NODE: id %u, type %#x, height %u, " 26 - "node_size %u, offset %d\n", 26 + "node_size %u, offset %u\n", 27 27 node->this, node->type, node->height, 28 28 node->tree->node_size, off); 29 29 } ··· 32 32 } 33 33 34 34 static inline 35 - int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) 35 + u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) 36 36 { 37 37 unsigned int node_size; 38 38 ··· 42 42 node_size = node->tree->node_size; 43 43 44 44 if ((off + len) > node_size) { 45 - int new_len = (int)node_size - off; 45 + u32 new_len = node_size - off; 46 46 47 47 pr_err("requested length has been corrected: " 48 48 "NODE: id %u, type %#x, height %u, " 49 - "node_size %u, offset %d, " 50 - "requested_len %d, corrected_len %d\n", 49 + "node_size %u, offset %u, " 50 + "requested_len %u, corrected_len %u\n", 51 51 node->this, node->type, node->height, 52 52 node->tree->node_size, off, len, new_len); 53 53 ··· 57 57 return len; 58 58 } 59 59 60 - void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) 60 + void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) 61 61 { 62 62 struct page *page; 63 - int pagenum; 64 - int bytes_read; 65 - int bytes_to_read; 63 + u32 pagenum; 64 + u32 bytes_read; 65 + u32 bytes_to_read; 66 66 67 67 if (!is_bnode_offset_valid(node, off)) 68 68 return; ··· 70 70 if (len == 0) { 71 71 pr_err("requested zero length: " 72 72 "NODE: id %u, type %#x, height %u, " 73 - "node_size %u, offset %d, len %d\n", 73 + "node_size %u, offset %u, len %u\n", 74 74 node->this, node->type, node->height, 75 75 node->tree->node_size, off, len); 76 76 return; ··· 86 86 if (pagenum >= node->tree->pages_per_bnode) 87 87 break; 88 88 page = node->page[pagenum]; 89 - bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off); 89 + bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off); 90 90 91 91 memcpy_from_page(buf + bytes_read, page, off, bytes_to_read); 92 92 ··· 95 95 } 96 96 } 97 97 98 - u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) 98 + u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off) 99 99 { 100 100 __be16 data; 101 101 // optimize later... ··· 103 103 return be16_to_cpu(data); 104 104 } 105 105 106 - u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) 106 + u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off) 107 107 { 108 108 u8 data; 109 109 // optimize later... ··· 111 111 return data; 112 112 } 113 113 114 - void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) 114 + void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off) 115 115 { 116 116 struct hfs_btree *tree; 117 - int key_len; 117 + u32 key_len; 118 118 119 119 tree = node->tree; 120 120 if (node->type == HFS_NODE_LEAF || ··· 125 125 126 126 if (key_len > sizeof(hfs_btree_key) || key_len < 1) { 127 127 memset(key, 0, sizeof(hfs_btree_key)); 128 - pr_err("hfs: Invalid key length: %d\n", key_len); 128 + pr_err("hfs: Invalid key length: %u\n", key_len); 129 129 return; 130 130 } 131 131 132 132 hfs_bnode_read(node, key, off, key_len); 133 133 } 134 134 135 - void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) 135 + void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len) 136 136 { 137 137 struct page *page; 138 138 ··· 142 142 if (len == 0) { 143 143 pr_err("requested zero length: " 144 144 "NODE: id %u, type %#x, height %u, " 145 - "node_size %u, offset %d, len %d\n", 145 + "node_size %u, offset %u, len %u\n", 146 146 node->this, node->type, node->height, 147 147 node->tree->node_size, off, len); 148 148 return; ··· 157 157 set_page_dirty(page); 158 158 } 159 159 160 - void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) 160 + void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data) 161 161 { 162 162 __be16 v = cpu_to_be16(data); 163 163 // optimize later... 164 164 hfs_bnode_write(node, &v, off, 2); 165 165 } 166 166 167 - void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data) 167 + void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data) 168 168 { 169 169 // optimize later... 170 170 hfs_bnode_write(node, &data, off, 1); 171 171 } 172 172 173 - void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) 173 + void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len) 174 174 { 175 175 struct page *page; 176 176 ··· 180 180 if (len == 0) { 181 181 pr_err("requested zero length: " 182 182 "NODE: id %u, type %#x, height %u, " 183 - "node_size %u, offset %d, len %d\n", 183 + "node_size %u, offset %u, len %u\n", 184 184 node->this, node->type, node->height, 185 185 node->tree->node_size, off, len); 186 186 return; ··· 195 195 set_page_dirty(page); 196 196 } 197 197 198 - void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, 199 - struct hfs_bnode *src_node, int src, int len) 198 + void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, 199 + struct hfs_bnode *src_node, u32 src, u32 len) 200 200 { 201 201 struct page *src_page, *dst_page; 202 202 ··· 216 216 set_page_dirty(dst_page); 217 217 } 218 218 219 - void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) 219 + void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len) 220 220 { 221 221 struct page *page; 222 222 void *ptr;
+1 -1
fs/hfs/brec.c
··· 62 62 return retval; 63 63 } 64 64 65 - int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) 65 + int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len) 66 66 { 67 67 struct hfs_btree *tree; 68 68 struct hfs_bnode *node, *new_node;
+1 -1
fs/hfs/btree.c
··· 259 259 } 260 260 261 261 /* Make sure @tree has enough space for the @rsvd_nodes */ 262 - int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes) 262 + int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes) 263 263 { 264 264 struct inode *inode = tree->inode; 265 265 u32 count;
+36 -77
fs/hfs/btree.h
··· 86 86 87 87 88 88 /* btree.c */ 89 - extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp); 90 - extern void hfs_btree_close(struct hfs_btree *); 91 - extern void hfs_btree_write(struct hfs_btree *); 92 - extern int hfs_bmap_reserve(struct hfs_btree *, int); 93 - extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *); 89 + extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, 90 + btree_keycmp keycmp); 91 + extern void hfs_btree_close(struct hfs_btree *tree); 92 + extern void hfs_btree_write(struct hfs_btree *tree); 93 + extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes); 94 + extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree); 94 95 extern void hfs_bmap_free(struct hfs_bnode *node); 95 96 96 97 /* bnode.c */ 97 - extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int); 98 - extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int); 99 - extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int); 100 - extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int); 101 - extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int); 102 - extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); 103 - extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8); 104 - extern void hfs_bnode_clear(struct hfs_bnode *, int, int); 105 - extern void hfs_bnode_copy(struct hfs_bnode *, int, 106 - struct hfs_bnode *, int, int); 107 - extern void hfs_bnode_move(struct hfs_bnode *, int, int, int); 108 - extern void hfs_bnode_dump(struct hfs_bnode *); 109 - extern void hfs_bnode_unlink(struct hfs_bnode *); 110 - extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32); 111 - extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32); 112 - extern void hfs_bnode_unhash(struct hfs_bnode *); 113 - extern void hfs_bnode_free(struct hfs_bnode *); 114 - extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); 115 - extern void hfs_bnode_get(struct hfs_bnode *); 116 - extern void hfs_bnode_put(struct hfs_bnode *); 98 + extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len); 99 + extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off); 100 + extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off); 101 + extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off); 102 + extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len); 103 + extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data); 104 + extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data); 105 + extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len); 106 + extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, 107 + struct hfs_bnode *src_node, u32 src, u32 len); 108 + extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len); 109 + extern void hfs_bnode_dump(struct hfs_bnode *node); 110 + extern void hfs_bnode_unlink(struct hfs_bnode *node); 111 + extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid); 112 + extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num); 113 + extern void hfs_bnode_unhash(struct hfs_bnode *node); 114 + extern void hfs_bnode_free(struct hfs_bnode *node); 115 + extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num); 116 + extern void hfs_bnode_get(struct hfs_bnode *node); 117 + extern void hfs_bnode_put(struct hfs_bnode *node); 117 118 118 119 /* brec.c */ 119 - extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); 120 - extern u16 hfs_brec_keylen(struct hfs_bnode *, u16); 121 - extern int hfs_brec_insert(struct hfs_find_data *, void *, int); 122 - extern int hfs_brec_remove(struct hfs_find_data *); 120 + extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off); 121 + extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec); 122 + extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len); 123 + extern int hfs_brec_remove(struct hfs_find_data *fd); 123 124 124 125 /* bfind.c */ 125 - extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); 126 - extern void hfs_find_exit(struct hfs_find_data *); 127 - extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); 128 - extern int hfs_brec_find(struct hfs_find_data *); 129 - extern int hfs_brec_read(struct hfs_find_data *, void *, int); 130 - extern int hfs_brec_goto(struct hfs_find_data *, int); 131 - 132 - 133 - struct hfs_bnode_desc { 134 - __be32 next; /* (V) Number of the next node at this level */ 135 - __be32 prev; /* (V) Number of the prev node at this level */ 136 - u8 type; /* (F) The type of node */ 137 - u8 height; /* (F) The level of this node (leaves=1) */ 138 - __be16 num_recs; /* (V) The number of records in this node */ 139 - u16 reserved; 140 - } __packed; 141 - 142 - #define HFS_NODE_INDEX 0x00 /* An internal (index) node */ 143 - #define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ 144 - #define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ 145 - #define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ 146 - 147 - struct hfs_btree_header_rec { 148 - __be16 depth; /* (V) The number of levels in this B-tree */ 149 - __be32 root; /* (V) The node number of the root node */ 150 - __be32 leaf_count; /* (V) The number of leaf records */ 151 - __be32 leaf_head; /* (V) The number of the first leaf node */ 152 - __be32 leaf_tail; /* (V) The number of the last leaf node */ 153 - __be16 node_size; /* (F) The number of bytes in a node (=512) */ 154 - __be16 max_key_len; /* (F) The length of a key in an index node */ 155 - __be32 node_count; /* (V) The total number of nodes */ 156 - __be32 free_nodes; /* (V) The number of unused nodes */ 157 - u16 reserved1; 158 - __be32 clump_size; /* (F) clump size. not usually used. */ 159 - u8 btree_type; /* (F) BTree type */ 160 - u8 reserved2; 161 - __be32 attributes; /* (F) attributes */ 162 - u32 reserved3[16]; 163 - } __packed; 164 - 165 - #define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not 166 - used by hfsplus. */ 167 - #define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8. 168 - used by hfsplus. */ 169 - #define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of 170 - max key length. use din catalog 171 - b-tree but not in extents 172 - b-tree (hfsplus). */ 126 + extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd); 127 + extern void hfs_find_exit(struct hfs_find_data *fd); 128 + extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd); 129 + extern int hfs_brec_find(struct hfs_find_data *fd); 130 + extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len); 131 + extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
+1 -1
fs/hfs/catalog.c
··· 322 322 } 323 323 } 324 324 325 + node_id = node->prev; 325 326 hfs_bnode_put(node); 326 327 327 - node_id = node->prev; 328 328 } while (node_id >= leaf_head); 329 329 330 330 return -ENOENT;
+1 -268
fs/hfs/hfs.h
··· 9 9 #ifndef _HFS_H 10 10 #define _HFS_H 11 11 12 - /* offsets to various blocks */ 13 - #define HFS_DD_BLK 0 /* Driver Descriptor block */ 14 - #define HFS_PMAP_BLK 1 /* First block of partition map */ 15 - #define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */ 16 - 17 - /* magic numbers for various disk blocks */ 18 - #define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */ 19 - #define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */ 20 - #define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */ 21 - #define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */ 22 - #define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */ 23 - 24 - /* various FIXED size parameters */ 25 - #define HFS_SECTOR_SIZE 512 /* size of an HFS sector */ 26 - #define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */ 27 - #define HFS_NAMELEN 31 /* maximum length of an HFS filename */ 28 - #define HFS_MAX_NAMELEN 128 29 - #define HFS_MAX_VALENCE 32767U 30 - 31 - /* Meanings of the drAtrb field of the MDB, 32 - * Reference: _Inside Macintosh: Files_ p. 2-61 33 - */ 34 - #define HFS_SB_ATTRIB_HLOCK (1 << 7) 35 - #define HFS_SB_ATTRIB_UNMNT (1 << 8) 36 - #define HFS_SB_ATTRIB_SPARED (1 << 9) 37 - #define HFS_SB_ATTRIB_INCNSTNT (1 << 11) 38 - #define HFS_SB_ATTRIB_SLOCK (1 << 15) 39 - 40 - /* Some special File ID numbers */ 41 - #define HFS_POR_CNID 1 /* Parent Of the Root */ 42 - #define HFS_ROOT_CNID 2 /* ROOT directory */ 43 - #define HFS_EXT_CNID 3 /* EXTents B-tree */ 44 - #define HFS_CAT_CNID 4 /* CATalog B-tree */ 45 - #define HFS_BAD_CNID 5 /* BAD blocks file */ 46 - #define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */ 47 - #define HFS_START_CNID 7 /* STARTup file (HFS+) */ 48 - #define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */ 49 - #define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */ 50 - #define HFS_FIRSTUSER_CNID 16 51 - 52 - /* values for hfs_cat_rec.cdrType */ 53 - #define HFS_CDR_DIR 0x01 /* folder (directory) */ 54 - #define HFS_CDR_FIL 0x02 /* file */ 55 - #define HFS_CDR_THD 0x03 /* folder (directory) thread */ 56 - #define HFS_CDR_FTH 0x04 /* file thread */ 57 - 58 - /* legal values for hfs_ext_key.FkType and hfs_file.fork */ 59 - #define HFS_FK_DATA 0x00 60 - #define HFS_FK_RSRC 0xFF 61 - 62 - /* bits in hfs_fil_entry.Flags */ 63 - #define HFS_FIL_LOCK 0x01 /* locked */ 64 - #define HFS_FIL_THD 0x02 /* file thread */ 65 - #define HFS_FIL_DOPEN 0x04 /* data fork open */ 66 - #define HFS_FIL_ROPEN 0x08 /* resource fork open */ 67 - #define HFS_FIL_DIR 0x10 /* directory (always clear) */ 68 - #define HFS_FIL_NOCOPY 0x40 /* copy-protected file */ 69 - #define HFS_FIL_USED 0x80 /* open */ 70 - 71 - /* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */ 72 - #define HFS_DIR_LOCK 0x01 /* locked */ 73 - #define HFS_DIR_THD 0x02 /* directory thread */ 74 - #define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */ 75 - #define HFS_DIR_MOUNTED 0x08 /* mounted */ 76 - #define HFS_DIR_DIR 0x10 /* directory (always set) */ 77 - #define HFS_DIR_EXPFOLDER 0x20 /* share point */ 78 - 79 - /* bits hfs_finfo.fdFlags */ 80 - #define HFS_FLG_INITED 0x0100 81 - #define HFS_FLG_LOCKED 0x1000 82 - #define HFS_FLG_INVISIBLE 0x4000 83 - 84 - /*======== HFS structures as they appear on the disk ========*/ 85 - 86 - /* Pascal-style string of up to 31 characters */ 87 - struct hfs_name { 88 - u8 len; 89 - u8 name[HFS_NAMELEN]; 90 - } __packed; 91 - 92 - struct hfs_point { 93 - __be16 v; 94 - __be16 h; 95 - } __packed; 96 - 97 - struct hfs_rect { 98 - __be16 top; 99 - __be16 left; 100 - __be16 bottom; 101 - __be16 right; 102 - } __packed; 103 - 104 - struct hfs_finfo { 105 - __be32 fdType; 106 - __be32 fdCreator; 107 - __be16 fdFlags; 108 - struct hfs_point fdLocation; 109 - __be16 fdFldr; 110 - } __packed; 111 - 112 - struct hfs_fxinfo { 113 - __be16 fdIconID; 114 - u8 fdUnused[8]; 115 - __be16 fdComment; 116 - __be32 fdPutAway; 117 - } __packed; 118 - 119 - struct hfs_dinfo { 120 - struct hfs_rect frRect; 121 - __be16 frFlags; 122 - struct hfs_point frLocation; 123 - __be16 frView; 124 - } __packed; 125 - 126 - struct hfs_dxinfo { 127 - struct hfs_point frScroll; 128 - __be32 frOpenChain; 129 - __be16 frUnused; 130 - __be16 frComment; 131 - __be32 frPutAway; 132 - } __packed; 133 - 134 - union hfs_finder_info { 135 - struct { 136 - struct hfs_finfo finfo; 137 - struct hfs_fxinfo fxinfo; 138 - } file; 139 - struct { 140 - struct hfs_dinfo dinfo; 141 - struct hfs_dxinfo dxinfo; 142 - } dir; 143 - } __packed; 144 - 145 - /* Cast to a pointer to a generic bkey */ 146 - #define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X))) 147 - 148 - /* The key used in the catalog b-tree: */ 149 - struct hfs_cat_key { 150 - u8 key_len; /* number of bytes in the key */ 151 - u8 reserved; /* padding */ 152 - __be32 ParID; /* CNID of the parent dir */ 153 - struct hfs_name CName; /* The filename of the entry */ 154 - } __packed; 155 - 156 - /* The key used in the extents b-tree: */ 157 - struct hfs_ext_key { 158 - u8 key_len; /* number of bytes in the key */ 159 - u8 FkType; /* HFS_FK_{DATA,RSRC} */ 160 - __be32 FNum; /* The File ID of the file */ 161 - __be16 FABN; /* allocation blocks number*/ 162 - } __packed; 163 - 164 - typedef union hfs_btree_key { 165 - u8 key_len; /* number of bytes in the key */ 166 - struct hfs_cat_key cat; 167 - struct hfs_ext_key ext; 168 - } hfs_btree_key; 169 - 170 - #define HFS_MAX_CAT_KEYLEN (sizeof(struct hfs_cat_key) - sizeof(u8)) 171 - #define HFS_MAX_EXT_KEYLEN (sizeof(struct hfs_ext_key) - sizeof(u8)) 172 - 173 - typedef union hfs_btree_key btree_key; 174 - 175 - struct hfs_extent { 176 - __be16 block; 177 - __be16 count; 178 - }; 179 - typedef struct hfs_extent hfs_extent_rec[3]; 180 - 181 - /* The catalog record for a file */ 182 - struct hfs_cat_file { 183 - s8 type; /* The type of entry */ 184 - u8 reserved; 185 - u8 Flags; /* Flags such as read-only */ 186 - s8 Typ; /* file version number = 0 */ 187 - struct hfs_finfo UsrWds; /* data used by the Finder */ 188 - __be32 FlNum; /* The CNID */ 189 - __be16 StBlk; /* obsolete */ 190 - __be32 LgLen; /* The logical EOF of the data fork*/ 191 - __be32 PyLen; /* The physical EOF of the data fork */ 192 - __be16 RStBlk; /* obsolete */ 193 - __be32 RLgLen; /* The logical EOF of the rsrc fork */ 194 - __be32 RPyLen; /* The physical EOF of the rsrc fork */ 195 - __be32 CrDat; /* The creation date */ 196 - __be32 MdDat; /* The modified date */ 197 - __be32 BkDat; /* The last backup date */ 198 - struct hfs_fxinfo FndrInfo; /* more data for the Finder */ 199 - __be16 ClpSize; /* number of bytes to allocate 200 - when extending files */ 201 - hfs_extent_rec ExtRec; /* first extent record 202 - for the data fork */ 203 - hfs_extent_rec RExtRec; /* first extent record 204 - for the resource fork */ 205 - u32 Resrv; /* reserved by Apple */ 206 - } __packed; 207 - 208 - /* the catalog record for a directory */ 209 - struct hfs_cat_dir { 210 - s8 type; /* The type of entry */ 211 - u8 reserved; 212 - __be16 Flags; /* flags */ 213 - __be16 Val; /* Valence: number of files and 214 - dirs in the directory */ 215 - __be32 DirID; /* The CNID */ 216 - __be32 CrDat; /* The creation date */ 217 - __be32 MdDat; /* The modification date */ 218 - __be32 BkDat; /* The last backup date */ 219 - struct hfs_dinfo UsrInfo; /* data used by the Finder */ 220 - struct hfs_dxinfo FndrInfo; /* more data used by Finder */ 221 - u8 Resrv[16]; /* reserved by Apple */ 222 - } __packed; 223 - 224 - /* the catalog record for a thread */ 225 - struct hfs_cat_thread { 226 - s8 type; /* The type of entry */ 227 - u8 reserved[9]; /* reserved by Apple */ 228 - __be32 ParID; /* CNID of parent directory */ 229 - struct hfs_name CName; /* The name of this entry */ 230 - } __packed; 231 - 232 - /* A catalog tree record */ 233 - typedef union hfs_cat_rec { 234 - s8 type; /* The type of entry */ 235 - struct hfs_cat_file file; 236 - struct hfs_cat_dir dir; 237 - struct hfs_cat_thread thread; 238 - } hfs_cat_rec; 239 - 240 - struct hfs_mdb { 241 - __be16 drSigWord; /* Signature word indicating fs type */ 242 - __be32 drCrDate; /* fs creation date/time */ 243 - __be32 drLsMod; /* fs modification date/time */ 244 - __be16 drAtrb; /* fs attributes */ 245 - __be16 drNmFls; /* number of files in root directory */ 246 - __be16 drVBMSt; /* location (in 512-byte blocks) 247 - of the volume bitmap */ 248 - __be16 drAllocPtr; /* location (in allocation blocks) 249 - to begin next allocation search */ 250 - __be16 drNmAlBlks; /* number of allocation blocks */ 251 - __be32 drAlBlkSiz; /* bytes in an allocation block */ 252 - __be32 drClpSiz; /* clumpsize, the number of bytes to 253 - allocate when extending a file */ 254 - __be16 drAlBlSt; /* location (in 512-byte blocks) 255 - of the first allocation block */ 256 - __be32 drNxtCNID; /* CNID to assign to the next 257 - file or directory created */ 258 - __be16 drFreeBks; /* number of free allocation blocks */ 259 - u8 drVN[28]; /* the volume label */ 260 - __be32 drVolBkUp; /* fs backup date/time */ 261 - __be16 drVSeqNum; /* backup sequence number */ 262 - __be32 drWrCnt; /* fs write count */ 263 - __be32 drXTClpSiz; /* clumpsize for the extents B-tree */ 264 - __be32 drCTClpSiz; /* clumpsize for the catalog B-tree */ 265 - __be16 drNmRtDirs; /* number of directories in 266 - the root directory */ 267 - __be32 drFilCnt; /* number of files in the fs */ 268 - __be32 drDirCnt; /* number of directories in the fs */ 269 - u8 drFndrInfo[32]; /* data used by the Finder */ 270 - __be16 drEmbedSigWord; /* embedded volume signature */ 271 - __be32 drEmbedExtent; /* starting block number (xdrStABN) 272 - and number of allocation blocks 273 - (xdrNumABlks) occupied by embedded 274 - volume */ 275 - __be32 drXTFlSize; /* bytes in the extents B-tree */ 276 - hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */ 277 - __be32 drCTFlSize; /* bytes in the catalog B-tree */ 278 - hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */ 279 - } __packed; 12 + #include <linux/hfs_common.h> 280 13 281 14 /*======== Data structures kept in memory ========*/ 282 15
+52 -37
fs/hfs/hfs_fs.h
··· 18 18 19 19 #include <asm/byteorder.h> 20 20 #include <linux/uaccess.h> 21 - #include <linux/hfs_common.h> 22 21 23 22 #include "hfs.h" 24 23 ··· 139 140 #define HFS_FLG_ALT_MDB_DIRTY 2 140 141 141 142 /* bitmap.c */ 142 - extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *); 143 - extern int hfs_clear_vbm_bits(struct super_block *, u16, u16); 143 + extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits); 144 + extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count); 144 145 145 146 /* catalog.c */ 146 - extern int hfs_cat_keycmp(const btree_key *, const btree_key *); 147 + extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2); 147 148 struct hfs_find_data; 148 - extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *); 149 - extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *); 150 - extern int hfs_cat_delete(u32, struct inode *, const struct qstr *); 151 - extern int hfs_cat_move(u32, struct inode *, const struct qstr *, 152 - struct inode *, const struct qstr *); 153 - extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *); 149 + extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid, 150 + struct hfs_find_data *fd); 151 + extern int hfs_cat_create(u32 cnid, struct inode *dir, 152 + const struct qstr *str, struct inode *inode); 153 + extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str); 154 + extern int hfs_cat_move(u32 cnid, struct inode *src_dir, 155 + const struct qstr *src_name, 156 + struct inode *dst_dir, 157 + const struct qstr *dst_name); 158 + extern void hfs_cat_build_key(struct super_block *sb, btree_key *key, 159 + u32 parent, const struct qstr *name); 154 160 155 161 /* dir.c */ 156 162 extern const struct file_operations hfs_dir_operations; 157 163 extern const struct inode_operations hfs_dir_inode_operations; 158 164 159 165 /* extent.c */ 160 - extern int hfs_ext_keycmp(const btree_key *, const btree_key *); 166 + extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2); 161 167 extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off); 162 - extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int); 163 - extern int hfs_ext_write_extent(struct inode *); 164 - extern int hfs_extend_file(struct inode *); 165 - extern void hfs_file_truncate(struct inode *); 168 + extern int hfs_free_fork(struct super_block *sb, 169 + struct hfs_cat_file *file, int type); 170 + extern int hfs_ext_write_extent(struct inode *inode); 171 + extern int hfs_extend_file(struct inode *inode); 172 + extern void hfs_file_truncate(struct inode *inode); 166 173 167 - extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int); 174 + extern int hfs_get_block(struct inode *inode, sector_t block, 175 + struct buffer_head *bh_result, int create); 168 176 169 177 /* inode.c */ 170 178 extern const struct address_space_operations hfs_aops; 171 179 extern const struct address_space_operations hfs_btree_aops; 172 180 173 181 int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping, 174 - loff_t pos, unsigned len, struct folio **foliop, void **fsdata); 175 - extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t); 176 - extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *); 177 - extern int hfs_write_inode(struct inode *, struct writeback_control *); 178 - extern int hfs_inode_setattr(struct mnt_idmap *, struct dentry *, 179 - struct iattr *); 182 + loff_t pos, unsigned int len, struct folio **foliop, 183 + void **fsdata); 184 + extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, 185 + umode_t mode); 186 + extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext, 187 + __be32 *log_size, __be32 *phys_size); 188 + extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc); 189 + extern int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, 190 + struct iattr *attr); 180 191 extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, 181 - __be32 log_size, __be32 phys_size, u32 clump_size); 182 - extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *); 183 - extern void hfs_evict_inode(struct inode *); 184 - extern void hfs_delete_inode(struct inode *); 192 + __be32 __log_size, __be32 phys_size, 193 + u32 clump_size); 194 + extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key, 195 + hfs_cat_rec *rec); 196 + extern void hfs_evict_inode(struct inode *inode); 197 + extern void hfs_delete_inode(struct inode *inode); 185 198 186 199 /* attr.c */ 187 200 extern const struct xattr_handler * const hfs_xattr_handlers[]; 188 201 189 202 /* mdb.c */ 190 - extern int hfs_mdb_get(struct super_block *); 191 - extern void hfs_mdb_commit(struct super_block *); 192 - extern void hfs_mdb_close(struct super_block *); 193 - extern void hfs_mdb_put(struct super_block *); 203 + extern int hfs_mdb_get(struct super_block *sb); 204 + extern void hfs_mdb_commit(struct super_block *sb); 205 + extern void hfs_mdb_close(struct super_block *sb); 206 + extern void hfs_mdb_put(struct super_block *sb); 194 207 195 208 /* part_tbl.c */ 196 - extern int hfs_part_find(struct super_block *, sector_t *, sector_t *); 209 + extern int hfs_part_find(struct super_block *sb, 210 + sector_t *part_start, sector_t *part_size); 197 211 198 212 /* string.c */ 199 213 extern const struct dentry_operations hfs_dentry_operations; 200 214 201 - extern int hfs_hash_dentry(const struct dentry *, struct qstr *); 202 - extern int hfs_strcmp(const unsigned char *, unsigned int, 203 - const unsigned char *, unsigned int); 215 + extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this); 216 + extern int hfs_strcmp(const unsigned char *s1, unsigned int len1, 217 + const unsigned char *s2, unsigned int len2); 204 218 extern int hfs_compare_dentry(const struct dentry *dentry, 205 - unsigned int len, const char *str, const struct qstr *name); 219 + unsigned int len, const char *str, 220 + const struct qstr *name); 206 221 207 222 /* trans.c */ 208 - extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *); 209 - extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *); 223 + extern void hfs_asc2mac(struct super_block *sb, 224 + struct hfs_name *out, const struct qstr *in); 225 + extern int hfs_mac2asc(struct super_block *sb, 226 + char *out, const struct hfs_name *in); 210 227 211 228 /* super.c */ 212 229 extern void hfs_mark_mdb_dirty(struct super_block *sb);
+2 -1
fs/hfs/inode.c
··· 45 45 } 46 46 47 47 int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping, 48 - loff_t pos, unsigned len, struct folio **foliop, void **fsdata) 48 + loff_t pos, unsigned int len, struct folio **foliop, 49 + void **fsdata) 49 50 { 50 51 int ret; 51 52
+5
fs/hfs/string.c
··· 16 16 #include "hfs_fs.h" 17 17 #include <linux/dcache.h> 18 18 19 + #include <kunit/visibility.h> 20 + 19 21 /*================ File-local variables ================*/ 20 22 21 23 /* ··· 67 65 this->hash = end_name_hash(hash); 68 66 return 0; 69 67 } 68 + EXPORT_SYMBOL_IF_KUNIT(hfs_hash_dentry); 70 69 71 70 /* 72 71 * Compare two strings in the HFS filename character ordering ··· 90 87 } 91 88 return len1 - len2; 92 89 } 90 + EXPORT_SYMBOL_IF_KUNIT(hfs_strcmp); 93 91 94 92 /* 95 93 * Test for equality of two strings in the HFS filename character ordering. ··· 116 112 } 117 113 return 0; 118 114 } 115 + EXPORT_SYMBOL_IF_KUNIT(hfs_compare_dentry);
+133
fs/hfs/string_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * KUnit tests for HFS string operations 4 + * 5 + * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com> 6 + */ 7 + 8 + #include <kunit/test.h> 9 + #include <linux/dcache.h> 10 + #include "hfs_fs.h" 11 + 12 + /* Test hfs_strcmp function */ 13 + static void hfs_strcmp_test(struct kunit *test) 14 + { 15 + /* Test equal strings */ 16 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "hello", 5)); 17 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("test", 4, "test", 4)); 18 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("", 0, "", 0)); 19 + 20 + /* Test unequal strings */ 21 + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("hello", 5, "world", 5)); 22 + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("test", 4, "testing", 7)); 23 + 24 + /* Test different lengths */ 25 + KUNIT_EXPECT_LT(test, hfs_strcmp("test", 4, "testing", 7), 0); 26 + KUNIT_EXPECT_GT(test, hfs_strcmp("testing", 7, "test", 4), 0); 27 + 28 + /* Test case insensitive comparison (HFS should handle case) */ 29 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("Test", 4, "TEST", 4)); 30 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "HELLO", 5)); 31 + 32 + /* Test with special characters */ 33 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("file.txt", 8, "file.txt", 8)); 34 + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("file.txt", 8, "file.dat", 8)); 35 + 36 + /* Test boundary cases */ 37 + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("a", 1, "a", 1)); 38 + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("a", 1, "b", 1)); 39 + } 40 + 41 + /* Test hfs_hash_dentry function */ 42 + static void hfs_hash_dentry_test(struct kunit *test) 43 + { 44 + struct qstr test_name1, test_name2, test_name3; 45 + struct dentry dentry = {}; 46 + char name1[] = "testfile"; 47 + char name2[] = "TestFile"; 48 + char name3[] = "different"; 49 + 50 + /* Initialize test strings */ 51 + test_name1.name = name1; 52 + test_name1.len = strlen(name1); 53 + test_name1.hash = 0; 54 + 55 + test_name2.name = name2; 56 + test_name2.len = strlen(name2); 57 + test_name2.hash = 0; 58 + 59 + test_name3.name = name3; 60 + test_name3.len = strlen(name3); 61 + test_name3.hash = 0; 62 + 63 + /* Test hashing */ 64 + KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name1)); 65 + KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name2)); 66 + KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name3)); 67 + 68 + /* Case insensitive names should hash the same */ 69 + KUNIT_EXPECT_EQ(test, test_name1.hash, test_name2.hash); 70 + 71 + /* Different names should have different hashes */ 72 + KUNIT_EXPECT_NE(test, test_name1.hash, test_name3.hash); 73 + } 74 + 75 + /* Test hfs_compare_dentry function */ 76 + static void hfs_compare_dentry_test(struct kunit *test) 77 + { 78 + struct qstr test_name; 79 + struct dentry dentry = {}; 80 + char name[] = "TestFile"; 81 + 82 + test_name.name = name; 83 + test_name.len = strlen(name); 84 + 85 + /* Test exact match */ 86 + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8, 87 + "TestFile", &test_name)); 88 + 89 + /* Test case insensitive match */ 90 + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8, 91 + "testfile", &test_name)); 92 + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8, 93 + "TESTFILE", &test_name)); 94 + 95 + /* Test different names */ 96 + KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 8, 97 + "DiffFile", &test_name)); 98 + 99 + /* Test different lengths */ 100 + KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 7, 101 + "TestFil", &test_name)); 102 + KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 9, 103 + "TestFiles", &test_name)); 104 + 105 + /* Test empty string */ 106 + test_name.name = ""; 107 + test_name.len = 0; 108 + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 0, "", &test_name)); 109 + 110 + /* Test HFS_NAMELEN boundary */ 111 + test_name.name = "This_is_a_very_long_filename_that_exceeds_normal_limits"; 112 + test_name.len = strlen(test_name.name); 113 + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, HFS_NAMELEN, 114 + "This_is_a_very_long_filename_th", &test_name)); 115 + } 116 + 117 + static struct kunit_case hfs_string_test_cases[] = { 118 + KUNIT_CASE(hfs_strcmp_test), 119 + KUNIT_CASE(hfs_hash_dentry_test), 120 + KUNIT_CASE(hfs_compare_dentry_test), 121 + {} 122 + }; 123 + 124 + static struct kunit_suite hfs_string_test_suite = { 125 + .name = "hfs_string", 126 + .test_cases = hfs_string_test_cases, 127 + }; 128 + 129 + kunit_test_suite(hfs_string_test_suite); 130 + 131 + MODULE_DESCRIPTION("KUnit tests for HFS string operations"); 132 + MODULE_LICENSE("GPL"); 133 + MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+8
fs/hfsplus/.kunitconfig
··· 1 + CONFIG_KUNIT=y 2 + CONFIG_HFSPLUS_FS=y 3 + CONFIG_HFSPLUS_KUNIT_TEST=y 4 + CONFIG_BLOCK=y 5 + CONFIG_BUFFER_HEAD=y 6 + CONFIG_NLS=y 7 + CONFIG_NLS_UTF8=y 8 + CONFIG_LEGACY_DIRECT_IO=y
+15
fs/hfsplus/Kconfig
··· 14 14 MacOS 8. It includes all Mac specific filesystem data such as 15 15 data forks and creator codes, but it also has several UNIX 16 16 style features such as file ownership and permissions. 17 + 18 + config HFSPLUS_KUNIT_TEST 19 + tristate "KUnit tests for HFS+ filesystem" if !KUNIT_ALL_TESTS 20 + depends on HFSPLUS_FS && KUNIT 21 + default KUNIT_ALL_TESTS 22 + help 23 + This builds KUnit tests for the HFS+ filesystem. 24 + 25 + KUnit tests run during boot and output the results to the debug 26 + log in TAP format (https://testanything.org/). Only useful for 27 + kernel devs running KUnit test harness and are not for inclusion 28 + into a production build. 29 + 30 + For more information on KUnit and unit tests in general please 31 + refer to the KUnit documentation in Documentation/dev-tools/kunit/.
+3
fs/hfsplus/Makefile
··· 8 8 hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \ 9 9 bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \ 10 10 attributes.o xattr.o xattr_user.o xattr_security.o xattr_trusted.o 11 + 12 + # KUnit tests 13 + obj-$(CONFIG_HFSPLUS_KUNIT_TEST) += unicode_test.o
+1 -1
fs/hfsplus/bfind.c
··· 210 210 return res; 211 211 } 212 212 213 - int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) 213 + int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len) 214 214 { 215 215 int res; 216 216
+32 -32
fs/hfsplus/bnode.c
··· 20 20 21 21 22 22 /* Copy a specified range of bytes from the raw data of a node */ 23 - void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) 23 + void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) 24 24 { 25 25 struct page **pagep; 26 - int l; 26 + u32 l; 27 27 28 28 if (!is_bnode_offset_valid(node, off)) 29 29 return; ··· 31 31 if (len == 0) { 32 32 pr_err("requested zero length: " 33 33 "NODE: id %u, type %#x, height %u, " 34 - "node_size %u, offset %d, len %d\n", 34 + "node_size %u, offset %u, len %u\n", 35 35 node->this, node->type, node->height, 36 36 node->tree->node_size, off, len); 37 37 return; ··· 43 43 pagep = node->page + (off >> PAGE_SHIFT); 44 44 off &= ~PAGE_MASK; 45 45 46 - l = min_t(int, len, PAGE_SIZE - off); 46 + l = min_t(u32, len, PAGE_SIZE - off); 47 47 memcpy_from_page(buf, *pagep, off, l); 48 48 49 49 while ((len -= l) != 0) { 50 50 buf += l; 51 - l = min_t(int, len, PAGE_SIZE); 51 + l = min_t(u32, len, PAGE_SIZE); 52 52 memcpy_from_page(buf, *++pagep, 0, l); 53 53 } 54 54 } 55 55 56 - u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) 56 + u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off) 57 57 { 58 58 __be16 data; 59 59 /* TODO: optimize later... */ ··· 61 61 return be16_to_cpu(data); 62 62 } 63 63 64 - u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) 64 + u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off) 65 65 { 66 66 u8 data; 67 67 /* TODO: optimize later... */ ··· 69 69 return data; 70 70 } 71 71 72 - void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) 72 + void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off) 73 73 { 74 74 struct hfs_btree *tree; 75 - int key_len; 75 + u32 key_len; 76 76 77 77 tree = node->tree; 78 78 if (node->type == HFS_NODE_LEAF || ··· 84 84 85 85 if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) { 86 86 memset(key, 0, sizeof(hfsplus_btree_key)); 87 - pr_err("hfsplus: Invalid key length: %d\n", key_len); 87 + pr_err("hfsplus: Invalid key length: %u\n", key_len); 88 88 return; 89 89 } 90 90 91 91 hfs_bnode_read(node, key, off, key_len); 92 92 } 93 93 94 - void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) 94 + void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len) 95 95 { 96 96 struct page **pagep; 97 - int l; 97 + u32 l; 98 98 99 99 if (!is_bnode_offset_valid(node, off)) 100 100 return; ··· 102 102 if (len == 0) { 103 103 pr_err("requested zero length: " 104 104 "NODE: id %u, type %#x, height %u, " 105 - "node_size %u, offset %d, len %d\n", 105 + "node_size %u, offset %u, len %u\n", 106 106 node->this, node->type, node->height, 107 107 node->tree->node_size, off, len); 108 108 return; ··· 114 114 pagep = node->page + (off >> PAGE_SHIFT); 115 115 off &= ~PAGE_MASK; 116 116 117 - l = min_t(int, len, PAGE_SIZE - off); 117 + l = min_t(u32, len, PAGE_SIZE - off); 118 118 memcpy_to_page(*pagep, off, buf, l); 119 119 set_page_dirty(*pagep); 120 120 121 121 while ((len -= l) != 0) { 122 122 buf += l; 123 - l = min_t(int, len, PAGE_SIZE); 123 + l = min_t(u32, len, PAGE_SIZE); 124 124 memcpy_to_page(*++pagep, 0, buf, l); 125 125 set_page_dirty(*pagep); 126 126 } 127 127 } 128 128 129 - void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) 129 + void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data) 130 130 { 131 131 __be16 v = cpu_to_be16(data); 132 132 /* TODO: optimize later... */ 133 133 hfs_bnode_write(node, &v, off, 2); 134 134 } 135 135 136 - void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) 136 + void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len) 137 137 { 138 138 struct page **pagep; 139 - int l; 139 + u32 l; 140 140 141 141 if (!is_bnode_offset_valid(node, off)) 142 142 return; ··· 144 144 if (len == 0) { 145 145 pr_err("requested zero length: " 146 146 "NODE: id %u, type %#x, height %u, " 147 - "node_size %u, offset %d, len %d\n", 147 + "node_size %u, offset %u, len %u\n", 148 148 node->this, node->type, node->height, 149 149 node->tree->node_size, off, len); 150 150 return; ··· 156 156 pagep = node->page + (off >> PAGE_SHIFT); 157 157 off &= ~PAGE_MASK; 158 158 159 - l = min_t(int, len, PAGE_SIZE - off); 159 + l = min_t(u32, len, PAGE_SIZE - off); 160 160 memzero_page(*pagep, off, l); 161 161 set_page_dirty(*pagep); 162 162 163 163 while ((len -= l) != 0) { 164 - l = min_t(int, len, PAGE_SIZE); 164 + l = min_t(u32, len, PAGE_SIZE); 165 165 memzero_page(*++pagep, 0, l); 166 166 set_page_dirty(*pagep); 167 167 } 168 168 } 169 169 170 - void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, 171 - struct hfs_bnode *src_node, int src, int len) 170 + void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, 171 + struct hfs_bnode *src_node, u32 src, u32 len) 172 172 { 173 173 struct page **src_page, **dst_page; 174 - int l; 174 + u32 l; 175 175 176 176 hfs_dbg("dst %u, src %u, len %u\n", dst, src, len); 177 177 if (!len) ··· 188 188 dst &= ~PAGE_MASK; 189 189 190 190 if (src == dst) { 191 - l = min_t(int, len, PAGE_SIZE - src); 191 + l = min_t(u32, len, PAGE_SIZE - src); 192 192 memcpy_page(*dst_page, src, *src_page, src, l); 193 193 set_page_dirty(*dst_page); 194 194 195 195 while ((len -= l) != 0) { 196 - l = min_t(int, len, PAGE_SIZE); 196 + l = min_t(u32, len, PAGE_SIZE); 197 197 memcpy_page(*++dst_page, 0, *++src_page, 0, l); 198 198 set_page_dirty(*dst_page); 199 199 } ··· 225 225 } 226 226 } 227 227 228 - void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) 228 + void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len) 229 229 { 230 230 struct page **src_page, **dst_page; 231 231 void *src_ptr, *dst_ptr; 232 - int l; 232 + u32 l; 233 233 234 234 hfs_dbg("dst %u, src %u, len %u\n", dst, src, len); 235 235 if (!len) ··· 299 299 dst &= ~PAGE_MASK; 300 300 301 301 if (src == dst) { 302 - l = min_t(int, len, PAGE_SIZE - src); 302 + l = min_t(u32, len, PAGE_SIZE - src); 303 303 304 304 dst_ptr = kmap_local_page(*dst_page) + src; 305 305 src_ptr = kmap_local_page(*src_page) + src; ··· 309 309 kunmap_local(dst_ptr); 310 310 311 311 while ((len -= l) != 0) { 312 - l = min_t(int, len, PAGE_SIZE); 312 + l = min_t(u32, len, PAGE_SIZE); 313 313 dst_ptr = kmap_local_page(*++dst_page); 314 314 src_ptr = kmap_local_page(*++src_page); 315 315 memmove(dst_ptr, src_ptr, l); ··· 481 481 tree->node_hash[hash] = node; 482 482 tree->node_hash_cnt++; 483 483 } else { 484 + hfs_bnode_get(node2); 484 485 spin_unlock(&tree->hash_lock); 485 486 kfree(node); 486 487 wait_event(node2->lock_wq, ··· 705 704 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 706 705 const u32 volume_attr = be32_to_cpu(sbi->s_vhdr->attributes); 707 706 708 - return tree->cnid == HFSPLUS_CAT_CNID && 709 - volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX; 707 + return volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX; 710 708 }
+1 -1
fs/hfsplus/brec.c
··· 60 60 return retval; 61 61 } 62 62 63 - int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) 63 + int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len) 64 64 { 65 65 struct hfs_btree *tree; 66 66 struct hfs_bnode *node, *new_node;
+1 -1
fs/hfsplus/btree.c
··· 344 344 } 345 345 346 346 /* Make sure @tree has enough space for the @rsvd_nodes */ 347 - int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes) 347 + int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes) 348 348 { 349 349 struct inode *inode = tree->inode; 350 350 struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
+6 -1
fs/hfsplus/dir.c
··· 552 552 res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata, 553 553 old_dir, &old_dentry->d_name, 554 554 new_dir, &new_dentry->d_name); 555 - if (!res) 555 + if (!res) { 556 556 new_dentry->d_fsdata = old_dentry->d_fsdata; 557 + 558 + res = hfsplus_cat_write_inode(old_dir); 559 + if (!res) 560 + res = hfsplus_cat_write_inode(new_dir); 561 + } 557 562 return res; 558 563 } 559 564
+21 -20
fs/hfsplus/hfsplus_fs.h
··· 16 16 #include <linux/buffer_head.h> 17 17 #include <linux/blkdev.h> 18 18 #include <linux/fs_context.h> 19 - #include <linux/hfs_common.h> 20 19 #include "hfsplus_raw.h" 21 20 22 21 /* Runtime config options */ ··· 356 357 struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id); 357 358 void hfs_btree_close(struct hfs_btree *tree); 358 359 int hfs_btree_write(struct hfs_btree *tree); 359 - int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes); 360 + int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes); 360 361 struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree); 361 362 void hfs_bmap_free(struct hfs_bnode *node); 362 363 363 364 /* bnode.c */ 364 - void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len); 365 - u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off); 366 - u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off); 367 - void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off); 368 - void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len); 369 - void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data); 370 - void hfs_bnode_clear(struct hfs_bnode *node, int off, int len); 371 - void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, 372 - struct hfs_bnode *src_node, int src, int len); 373 - void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len); 365 + void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len); 366 + u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off); 367 + u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off); 368 + void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off); 369 + void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len); 370 + void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data); 371 + void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len); 372 + void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, 373 + struct hfs_bnode *src_node, u32 src, u32 len); 374 + void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len); 374 375 void hfs_bnode_dump(struct hfs_bnode *node); 375 376 void hfs_bnode_unlink(struct hfs_bnode *node); 376 377 struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid); ··· 385 386 /* brec.c */ 386 387 u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off); 387 388 u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec); 388 - int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len); 389 + int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len); 389 390 int hfs_brec_remove(struct hfs_find_data *fd); 390 391 391 392 /* bfind.c */ ··· 398 399 int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd, 399 400 search_strategy_t rec_found); 400 401 int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare); 401 - int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len); 402 + int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len); 402 403 int hfs_brec_goto(struct hfs_find_data *fd, int cnt); 403 404 404 405 /* catalog.c */ ··· 476 477 /* super.c */ 477 478 struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino); 478 479 void hfsplus_mark_mdb_dirty(struct super_block *sb); 480 + void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr); 481 + int hfsplus_commit_superblock(struct super_block *sb); 479 482 480 483 /* tables.c */ 481 484 extern u16 hfsplus_case_fold_table[]; ··· 550 549 } 551 550 552 551 static inline 553 - bool is_bnode_offset_valid(struct hfs_bnode *node, int off) 552 + bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) 554 553 { 555 554 bool is_valid = off < node->tree->node_size; 556 555 557 556 if (!is_valid) { 558 557 pr_err("requested invalid offset: " 559 558 "NODE: id %u, type %#x, height %u, " 560 - "node_size %u, offset %d\n", 559 + "node_size %u, offset %u\n", 561 560 node->this, node->type, node->height, 562 561 node->tree->node_size, off); 563 562 } ··· 566 565 } 567 566 568 567 static inline 569 - int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) 568 + u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) 570 569 { 571 570 unsigned int node_size; 572 571 ··· 576 575 node_size = node->tree->node_size; 577 576 578 577 if ((off + len) > node_size) { 579 - int new_len = (int)node_size - off; 578 + u32 new_len = node_size - off; 580 579 581 580 pr_err("requested length has been corrected: " 582 581 "NODE: id %u, type %#x, height %u, " 583 - "node_size %u, offset %d, " 584 - "requested_len %d, corrected_len %d\n", 582 + "node_size %u, offset %u, " 583 + "requested_len %u, corrected_len %u\n", 585 584 node->this, node->type, node->height, 586 585 node->tree->node_size, off, len, new_len); 587 586
+1 -393
fs/hfsplus/hfsplus_raw.h
··· 15 15 #define _LINUX_HFSPLUS_RAW_H 16 16 17 17 #include <linux/types.h> 18 - 19 - /* Some constants */ 20 - #define HFSPLUS_SECTOR_SIZE 512 21 - #define HFSPLUS_SECTOR_SHIFT 9 22 - #define HFSPLUS_VOLHEAD_SECTOR 2 23 - #define HFSPLUS_VOLHEAD_SIG 0x482b 24 - #define HFSPLUS_VOLHEAD_SIGX 0x4858 25 - #define HFSPLUS_SUPER_MAGIC 0x482b 26 - #define HFSPLUS_MIN_VERSION 4 27 - #define HFSPLUS_CURRENT_VERSION 5 28 - 29 - #define HFSP_WRAP_MAGIC 0x4244 30 - #define HFSP_WRAP_ATTRIB_SLOCK 0x8000 31 - #define HFSP_WRAP_ATTRIB_SPARED 0x0200 32 - 33 - #define HFSP_WRAPOFF_SIG 0x00 34 - #define HFSP_WRAPOFF_ATTRIB 0x0A 35 - #define HFSP_WRAPOFF_ABLKSIZE 0x14 36 - #define HFSP_WRAPOFF_ABLKSTART 0x1C 37 - #define HFSP_WRAPOFF_EMBEDSIG 0x7C 38 - #define HFSP_WRAPOFF_EMBEDEXT 0x7E 39 - 40 - #define HFSP_HIDDENDIR_NAME \ 41 - "\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data" 42 - 43 - #define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */ 44 - #define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */ 45 - 46 - #define HFSP_SYMLINK_TYPE 0x736c6e6b /* 'slnk' */ 47 - #define HFSP_SYMLINK_CREATOR 0x72686170 /* 'rhap' */ 48 - 49 - #define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */ 50 - 51 - /* Structures used on disk */ 52 - 53 - typedef __be32 hfsplus_cnid; 54 - typedef __be16 hfsplus_unichr; 55 - 56 - #define HFSPLUS_MAX_STRLEN 255 57 - #define HFSPLUS_ATTR_MAX_STRLEN 127 58 - 59 - /* A "string" as used in filenames, etc. */ 60 - struct hfsplus_unistr { 61 - __be16 length; 62 - hfsplus_unichr unicode[HFSPLUS_MAX_STRLEN]; 63 - } __packed; 64 - 65 - /* 66 - * A "string" is used in attributes file 67 - * for name of extended attribute 68 - */ 69 - struct hfsplus_attr_unistr { 70 - __be16 length; 71 - hfsplus_unichr unicode[HFSPLUS_ATTR_MAX_STRLEN]; 72 - } __packed; 73 - 74 - /* POSIX permissions */ 75 - struct hfsplus_perm { 76 - __be32 owner; 77 - __be32 group; 78 - u8 rootflags; 79 - u8 userflags; 80 - __be16 mode; 81 - __be32 dev; 82 - } __packed; 83 - 84 - #define HFSPLUS_FLG_NODUMP 0x01 85 - #define HFSPLUS_FLG_IMMUTABLE 0x02 86 - #define HFSPLUS_FLG_APPEND 0x04 87 - 88 - /* A single contiguous area of a file */ 89 - struct hfsplus_extent { 90 - __be32 start_block; 91 - __be32 block_count; 92 - } __packed; 93 - typedef struct hfsplus_extent hfsplus_extent_rec[8]; 94 - 95 - /* Information for a "Fork" in a file */ 96 - struct hfsplus_fork_raw { 97 - __be64 total_size; 98 - __be32 clump_size; 99 - __be32 total_blocks; 100 - hfsplus_extent_rec extents; 101 - } __packed; 102 - 103 - /* HFS+ Volume Header */ 104 - struct hfsplus_vh { 105 - __be16 signature; 106 - __be16 version; 107 - __be32 attributes; 108 - __be32 last_mount_vers; 109 - u32 reserved; 110 - 111 - __be32 create_date; 112 - __be32 modify_date; 113 - __be32 backup_date; 114 - __be32 checked_date; 115 - 116 - __be32 file_count; 117 - __be32 folder_count; 118 - 119 - __be32 blocksize; 120 - __be32 total_blocks; 121 - __be32 free_blocks; 122 - 123 - __be32 next_alloc; 124 - __be32 rsrc_clump_sz; 125 - __be32 data_clump_sz; 126 - hfsplus_cnid next_cnid; 127 - 128 - __be32 write_count; 129 - __be64 encodings_bmp; 130 - 131 - u32 finder_info[8]; 132 - 133 - struct hfsplus_fork_raw alloc_file; 134 - struct hfsplus_fork_raw ext_file; 135 - struct hfsplus_fork_raw cat_file; 136 - struct hfsplus_fork_raw attr_file; 137 - struct hfsplus_fork_raw start_file; 138 - } __packed; 139 - 140 - /* HFS+ volume attributes */ 141 - #define HFSPLUS_VOL_UNMNT (1 << 8) 142 - #define HFSPLUS_VOL_SPARE_BLK (1 << 9) 143 - #define HFSPLUS_VOL_NOCACHE (1 << 10) 144 - #define HFSPLUS_VOL_INCNSTNT (1 << 11) 145 - #define HFSPLUS_VOL_NODEID_REUSED (1 << 12) 146 - #define HFSPLUS_VOL_JOURNALED (1 << 13) 147 - #define HFSPLUS_VOL_SOFTLOCK (1 << 15) 148 - #define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31) 149 - 150 - /* HFS+ BTree node descriptor */ 151 - struct hfs_bnode_desc { 152 - __be32 next; 153 - __be32 prev; 154 - s8 type; 155 - u8 height; 156 - __be16 num_recs; 157 - u16 reserved; 158 - } __packed; 159 - 160 - /* HFS+ BTree node types */ 161 - #define HFS_NODE_INDEX 0x00 /* An internal (index) node */ 162 - #define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ 163 - #define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ 164 - #define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ 165 - 166 - /* HFS+ BTree header */ 167 - struct hfs_btree_header_rec { 168 - __be16 depth; 169 - __be32 root; 170 - __be32 leaf_count; 171 - __be32 leaf_head; 172 - __be32 leaf_tail; 173 - __be16 node_size; 174 - __be16 max_key_len; 175 - __be32 node_count; 176 - __be32 free_nodes; 177 - u16 reserved1; 178 - __be32 clump_size; 179 - u8 btree_type; 180 - u8 key_type; 181 - __be32 attributes; 182 - u32 reserved3[16]; 183 - } __packed; 184 - 185 - /* BTree attributes */ 186 - #define HFS_TREE_BIGKEYS 2 187 - #define HFS_TREE_VARIDXKEYS 4 188 - 189 - /* HFS+ BTree misc info */ 190 - #define HFSPLUS_TREE_HEAD 0 191 - #define HFSPLUS_NODE_MXSZ 32768 192 - #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 193 - #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 194 - #define HFSPLUS_BTREE_HDR_USER_BYTES 128 195 - 196 - /* Some special File ID numbers (stolen from hfs.h) */ 197 - #define HFSPLUS_POR_CNID 1 /* Parent Of the Root */ 198 - #define HFSPLUS_ROOT_CNID 2 /* ROOT directory */ 199 - #define HFSPLUS_EXT_CNID 3 /* EXTents B-tree */ 200 - #define HFSPLUS_CAT_CNID 4 /* CATalog B-tree */ 201 - #define HFSPLUS_BAD_CNID 5 /* BAD blocks file */ 202 - #define HFSPLUS_ALLOC_CNID 6 /* ALLOCation file */ 203 - #define HFSPLUS_START_CNID 7 /* STARTup file */ 204 - #define HFSPLUS_ATTR_CNID 8 /* ATTRibutes file */ 205 - #define HFSPLUS_EXCH_CNID 15 /* ExchangeFiles temp id */ 206 - #define HFSPLUS_FIRSTUSER_CNID 16 /* first available user id */ 207 - 208 - /* btree key type */ 209 - #define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */ 210 - #define HFSPLUS_KEY_BINARY 0xBC /* case-sensitive */ 211 - 212 - /* HFS+ catalog entry key */ 213 - struct hfsplus_cat_key { 214 - __be16 key_len; 215 - hfsplus_cnid parent; 216 - struct hfsplus_unistr name; 217 - } __packed; 218 - 219 - #define HFSPLUS_CAT_KEYLEN (sizeof(struct hfsplus_cat_key)) 220 - 221 - /* Structs from hfs.h */ 222 - struct hfsp_point { 223 - __be16 v; 224 - __be16 h; 225 - } __packed; 226 - 227 - struct hfsp_rect { 228 - __be16 top; 229 - __be16 left; 230 - __be16 bottom; 231 - __be16 right; 232 - } __packed; 233 - 234 - 235 - /* HFS directory info (stolen from hfs.h */ 236 - struct DInfo { 237 - struct hfsp_rect frRect; 238 - __be16 frFlags; 239 - struct hfsp_point frLocation; 240 - __be16 frView; 241 - } __packed; 242 - 243 - struct DXInfo { 244 - struct hfsp_point frScroll; 245 - __be32 frOpenChain; 246 - __be16 frUnused; 247 - __be16 frComment; 248 - __be32 frPutAway; 249 - } __packed; 250 - 251 - /* HFS+ folder data (part of an hfsplus_cat_entry) */ 252 - struct hfsplus_cat_folder { 253 - __be16 type; 254 - __be16 flags; 255 - __be32 valence; 256 - hfsplus_cnid id; 257 - __be32 create_date; 258 - __be32 content_mod_date; 259 - __be32 attribute_mod_date; 260 - __be32 access_date; 261 - __be32 backup_date; 262 - struct hfsplus_perm permissions; 263 - struct_group_attr(info, __packed, 264 - struct DInfo user_info; 265 - struct DXInfo finder_info; 266 - ); 267 - __be32 text_encoding; 268 - __be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */ 269 - } __packed; 270 - 271 - /* HFS file info (stolen from hfs.h) */ 272 - struct FInfo { 273 - __be32 fdType; 274 - __be32 fdCreator; 275 - __be16 fdFlags; 276 - struct hfsp_point fdLocation; 277 - __be16 fdFldr; 278 - } __packed; 279 - 280 - struct FXInfo { 281 - __be16 fdIconID; 282 - u8 fdUnused[8]; 283 - __be16 fdComment; 284 - __be32 fdPutAway; 285 - } __packed; 286 - 287 - /* HFS+ file data (part of a cat_entry) */ 288 - struct hfsplus_cat_file { 289 - __be16 type; 290 - __be16 flags; 291 - u32 reserved1; 292 - hfsplus_cnid id; 293 - __be32 create_date; 294 - __be32 content_mod_date; 295 - __be32 attribute_mod_date; 296 - __be32 access_date; 297 - __be32 backup_date; 298 - struct hfsplus_perm permissions; 299 - struct_group_attr(info, __packed, 300 - struct FInfo user_info; 301 - struct FXInfo finder_info; 302 - ); 303 - __be32 text_encoding; 304 - u32 reserved2; 305 - 306 - struct hfsplus_fork_raw data_fork; 307 - struct hfsplus_fork_raw rsrc_fork; 308 - } __packed; 309 - 310 - /* File and folder flag bits */ 311 - #define HFSPLUS_FILE_LOCKED 0x0001 312 - #define HFSPLUS_FILE_THREAD_EXISTS 0x0002 313 - #define HFSPLUS_XATTR_EXISTS 0x0004 314 - #define HFSPLUS_ACL_EXISTS 0x0008 315 - #define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count 316 - * (HFSX only) */ 317 - 318 - /* HFS+ catalog thread (part of a cat_entry) */ 319 - struct hfsplus_cat_thread { 320 - __be16 type; 321 - s16 reserved; 322 - hfsplus_cnid parentID; 323 - struct hfsplus_unistr nodeName; 324 - } __packed; 325 - 326 - #define HFSPLUS_MIN_THREAD_SZ 10 327 - 328 - /* A data record in the catalog tree */ 329 - typedef union { 330 - __be16 type; 331 - struct hfsplus_cat_folder folder; 332 - struct hfsplus_cat_file file; 333 - struct hfsplus_cat_thread thread; 334 - } __packed hfsplus_cat_entry; 335 - 336 - /* HFS+ catalog entry type */ 337 - #define HFSPLUS_FOLDER 0x0001 338 - #define HFSPLUS_FILE 0x0002 339 - #define HFSPLUS_FOLDER_THREAD 0x0003 340 - #define HFSPLUS_FILE_THREAD 0x0004 341 - 342 - /* HFS+ extents tree key */ 343 - struct hfsplus_ext_key { 344 - __be16 key_len; 345 - u8 fork_type; 346 - u8 pad; 347 - hfsplus_cnid cnid; 348 - __be32 start_block; 349 - } __packed; 350 - 351 - #define HFSPLUS_EXT_KEYLEN sizeof(struct hfsplus_ext_key) 352 - 353 - #define HFSPLUS_XATTR_FINDER_INFO_NAME "com.apple.FinderInfo" 354 - #define HFSPLUS_XATTR_ACL_NAME "com.apple.system.Security" 355 - 356 - #define HFSPLUS_ATTR_INLINE_DATA 0x10 357 - #define HFSPLUS_ATTR_FORK_DATA 0x20 358 - #define HFSPLUS_ATTR_EXTENTS 0x30 359 - 360 - /* HFS+ attributes tree key */ 361 - struct hfsplus_attr_key { 362 - __be16 key_len; 363 - __be16 pad; 364 - hfsplus_cnid cnid; 365 - __be32 start_block; 366 - struct hfsplus_attr_unistr key_name; 367 - } __packed; 368 - 369 - #define HFSPLUS_ATTR_KEYLEN sizeof(struct hfsplus_attr_key) 370 - 371 - /* HFS+ fork data attribute */ 372 - struct hfsplus_attr_fork_data { 373 - __be32 record_type; 374 - __be32 reserved; 375 - struct hfsplus_fork_raw the_fork; 376 - } __packed; 377 - 378 - /* HFS+ extension attribute */ 379 - struct hfsplus_attr_extents { 380 - __be32 record_type; 381 - __be32 reserved; 382 - struct hfsplus_extent extents; 383 - } __packed; 384 - 385 - #define HFSPLUS_MAX_INLINE_DATA_SIZE 3802 386 - 387 - /* HFS+ attribute inline data */ 388 - struct hfsplus_attr_inline_data { 389 - __be32 record_type; 390 - __be32 reserved1; 391 - u8 reserved2[6]; 392 - __be16 length; 393 - u8 raw_bytes[HFSPLUS_MAX_INLINE_DATA_SIZE]; 394 - } __packed; 395 - 396 - /* A data record in the attributes tree */ 397 - typedef union { 398 - __be32 record_type; 399 - struct hfsplus_attr_fork_data fork_data; 400 - struct hfsplus_attr_extents extents; 401 - struct hfsplus_attr_inline_data inline_data; 402 - } __packed hfsplus_attr_entry; 403 - 404 - /* HFS+ generic BTree key */ 405 - typedef union { 406 - __be16 key_len; 407 - struct hfsplus_cat_key cat; 408 - struct hfsplus_ext_key ext; 409 - struct hfsplus_attr_key attr; 410 - } __packed hfsplus_btree_key; 18 + #include <linux/hfs_common.h> 411 19 412 20 #endif
+37 -4
fs/hfsplus/inode.c
··· 180 180 .d_compare = hfsplus_compare_dentry, 181 181 }; 182 182 183 - static void hfsplus_get_perms(struct inode *inode, 184 - struct hfsplus_perm *perms, int dir) 183 + static int hfsplus_get_perms(struct inode *inode, 184 + struct hfsplus_perm *perms, int dir) 185 185 { 186 186 struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 187 187 u16 mode; 188 188 189 189 mode = be16_to_cpu(perms->mode); 190 + if (dir) { 191 + if (mode && !S_ISDIR(mode)) 192 + goto bad_type; 193 + } else if (mode) { 194 + switch (mode & S_IFMT) { 195 + case S_IFREG: 196 + case S_IFLNK: 197 + case S_IFCHR: 198 + case S_IFBLK: 199 + case S_IFIFO: 200 + case S_IFSOCK: 201 + break; 202 + default: 203 + goto bad_type; 204 + } 205 + } 190 206 191 207 i_uid_write(inode, be32_to_cpu(perms->owner)); 192 208 if ((test_bit(HFSPLUS_SB_UID, &sbi->flags)) || (!i_uid_read(inode) && !mode)) ··· 228 212 inode->i_flags |= S_APPEND; 229 213 else 230 214 inode->i_flags &= ~S_APPEND; 215 + return 0; 216 + bad_type: 217 + pr_err("invalid file type 0%04o for inode %lu\n", mode, inode->i_ino); 218 + return -EIO; 231 219 } 232 220 233 221 static int hfsplus_file_open(struct inode *inode, struct file *file) ··· 325 305 struct inode *inode = file->f_mapping->host; 326 306 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 327 307 struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 308 + struct hfsplus_vh *vhdr = sbi->s_vhdr; 328 309 int error = 0, error2; 329 310 330 311 error = file_write_and_wait_range(file, start, end); ··· 368 347 if (!error) 369 348 error = error2; 370 349 } 350 + 351 + mutex_lock(&sbi->vh_mutex); 352 + hfsplus_prepare_volume_header_for_commit(vhdr); 353 + mutex_unlock(&sbi->vh_mutex); 354 + 355 + error2 = hfsplus_commit_superblock(inode->i_sb); 356 + if (!error) 357 + error = error2; 371 358 372 359 if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags)) 373 360 blkdev_issue_flush(inode->i_sb->s_bdev); ··· 545 516 } 546 517 hfs_bnode_read(fd->bnode, &entry, fd->entryoffset, 547 518 sizeof(struct hfsplus_cat_folder)); 548 - hfsplus_get_perms(inode, &folder->permissions, 1); 519 + res = hfsplus_get_perms(inode, &folder->permissions, 1); 520 + if (res) 521 + goto out; 549 522 set_nlink(inode, 1); 550 523 inode->i_size = 2 + be32_to_cpu(folder->valence); 551 524 inode_set_atime_to_ts(inode, hfsp_mt2ut(folder->access_date)); ··· 576 545 577 546 hfsplus_inode_read_fork(inode, HFSPLUS_IS_RSRC(inode) ? 578 547 &file->rsrc_fork : &file->data_fork); 579 - hfsplus_get_perms(inode, &file->permissions, 0); 548 + res = hfsplus_get_perms(inode, &file->permissions, 0); 549 + if (res) 550 + goto out; 580 551 set_nlink(inode, 1); 581 552 if (S_ISREG(inode->i_mode)) { 582 553 if (file->permissions.dev)
+54 -33
fs/hfsplus/super.c
··· 187 187 } 188 188 } 189 189 190 - static int hfsplus_sync_fs(struct super_block *sb, int wait) 190 + int hfsplus_commit_superblock(struct super_block *sb) 191 191 { 192 192 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 193 193 struct hfsplus_vh *vhdr = sbi->s_vhdr; 194 194 int write_backup = 0; 195 - int error, error2; 196 - 197 - if (!wait) 198 - return 0; 195 + int error = 0, error2; 199 196 200 197 hfs_dbg("starting...\n"); 201 - 202 - /* 203 - * Explicitly write out the special metadata inodes. 204 - * 205 - * While these special inodes are marked as hashed and written 206 - * out peridocically by the flusher threads we redirty them 207 - * during writeout of normal inodes, and thus the life lock 208 - * prevents us from getting the latest state to disk. 209 - */ 210 - error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); 211 - error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); 212 - if (!error) 213 - error = error2; 214 - if (sbi->attr_tree) { 215 - error2 = 216 - filemap_write_and_wait(sbi->attr_tree->inode->i_mapping); 217 - if (!error) 218 - error = error2; 219 - } 220 - error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); 221 - if (!error) 222 - error = error2; 223 198 224 199 mutex_lock(&sbi->vh_mutex); 225 200 mutex_lock(&sbi->alloc_mutex); ··· 224 249 sbi->part_start + sbi->sect_count - 2, 225 250 sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE); 226 251 if (!error) 227 - error2 = error; 252 + error = error2; 228 253 out: 229 254 mutex_unlock(&sbi->alloc_mutex); 230 255 mutex_unlock(&sbi->vh_mutex); 256 + 257 + hfs_dbg("finished: err %d\n", error); 258 + 259 + return error; 260 + } 261 + 262 + static int hfsplus_sync_fs(struct super_block *sb, int wait) 263 + { 264 + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 265 + int error, error2; 266 + 267 + if (!wait) 268 + return 0; 269 + 270 + hfs_dbg("starting...\n"); 271 + 272 + /* 273 + * Explicitly write out the special metadata inodes. 274 + * 275 + * While these special inodes are marked as hashed and written 276 + * out peridocically by the flusher threads we redirty them 277 + * during writeout of normal inodes, and thus the life lock 278 + * prevents us from getting the latest state to disk. 279 + */ 280 + error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); 281 + error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); 282 + if (!error) 283 + error = error2; 284 + if (sbi->attr_tree) { 285 + error2 = 286 + filemap_write_and_wait(sbi->attr_tree->inode->i_mapping); 287 + if (!error) 288 + error = error2; 289 + } 290 + error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); 291 + if (!error) 292 + error = error2; 293 + 294 + error2 = hfsplus_commit_superblock(sb); 295 + if (!error) 296 + error = error2; 231 297 232 298 if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags)) 233 299 blkdev_issue_flush(sb->s_bdev); ··· 410 394 .statfs = hfsplus_statfs, 411 395 .show_options = hfsplus_show_options, 412 396 }; 397 + 398 + void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr) 399 + { 400 + vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); 401 + vhdr->modify_date = hfsp_now2mt(); 402 + be32_add_cpu(&vhdr->write_count, 1); 403 + vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); 404 + vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); 405 + } 413 406 414 407 static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) 415 408 { ··· 587 562 * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused 588 563 * all three are registered with Apple for our use 589 564 */ 590 - vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); 591 - vhdr->modify_date = hfsp_now2mt(); 592 - be32_add_cpu(&vhdr->write_count, 1); 593 - vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); 594 - vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); 565 + hfsplus_prepare_volume_header_for_commit(vhdr); 595 566 hfsplus_sync_fs(sb, 1); 596 567 597 568 if (!sbi->hidden_dir) {
+13 -3
fs/hfsplus/unicode.c
··· 11 11 12 12 #include <linux/types.h> 13 13 #include <linux/nls.h> 14 + 15 + #include <kunit/visibility.h> 16 + 14 17 #include "hfsplus_fs.h" 15 18 #include "hfsplus_raw.h" 16 19 ··· 75 72 return 0; 76 73 } 77 74 } 75 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcasecmp); 78 76 79 77 /* Compare names as a sequence of 16-bit unsigned integers */ 80 78 int hfsplus_strcmp(const struct hfsplus_unistr *s1, ··· 114 110 return len1 < len2 ? -1 : 115 111 len1 > len2 ? 1 : 0; 116 112 } 117 - 113 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcmp); 118 114 119 115 #define Hangul_SBase 0xac00 120 116 #define Hangul_LBase 0x1100 ··· 147 143 return NULL; 148 144 } 149 145 150 - static int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, 151 - int max_len, char *astr, int *len_p) 146 + static int hfsplus_uni2asc(struct super_block *sb, 147 + const struct hfsplus_unistr *ustr, 148 + int max_len, char *astr, int *len_p) 152 149 { 153 150 const hfsplus_unichr *ip; 154 151 struct nls_table *nls = HFSPLUS_SB(sb)->nls; ··· 290 285 { 291 286 return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p); 292 287 } 288 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_str); 293 289 294 290 inline int hfsplus_uni2asc_xattr_str(struct super_block *sb, 295 291 const struct hfsplus_attr_unistr *ustr, ··· 299 293 return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr, 300 294 HFSPLUS_ATTR_MAX_STRLEN, astr, len_p); 301 295 } 296 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_xattr_str); 302 297 303 298 /* 304 299 * Convert one or more ASCII characters into a single unicode character. ··· 427 420 return -ENAMETOOLONG; 428 421 return 0; 429 422 } 423 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_asc2uni); 430 424 431 425 /* 432 426 * Hash a string to an integer as appropriate for the HFS+ filesystem. ··· 480 472 481 473 return 0; 482 474 } 475 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_hash_dentry); 483 476 484 477 /* 485 478 * Compare strings with HFS+ filename ordering. ··· 572 563 return 1; 573 564 return 0; 574 565 } 566 + EXPORT_SYMBOL_IF_KUNIT(hfsplus_compare_dentry);
+1579
fs/hfsplus/unicode_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * KUnit tests for HFS+ Unicode string operations 4 + * 5 + * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com> 6 + */ 7 + 8 + #include <kunit/test.h> 9 + #include <linux/nls.h> 10 + #include <linux/dcache.h> 11 + #include <linux/stringhash.h> 12 + #include "hfsplus_fs.h" 13 + 14 + struct test_mock_string_env { 15 + struct hfsplus_unistr str1; 16 + struct hfsplus_unistr str2; 17 + char *buf; 18 + u32 buf_size; 19 + }; 20 + 21 + static struct test_mock_string_env *setup_mock_str_env(u32 buf_size) 22 + { 23 + struct test_mock_string_env *env; 24 + 25 + env = kzalloc(sizeof(struct test_mock_string_env), GFP_KERNEL); 26 + if (!env) 27 + return NULL; 28 + 29 + env->buf = kzalloc(buf_size, GFP_KERNEL); 30 + if (!env->buf) { 31 + kfree(env); 32 + return NULL; 33 + } 34 + 35 + env->buf_size = buf_size; 36 + 37 + return env; 38 + } 39 + 40 + static void free_mock_str_env(struct test_mock_string_env *env) 41 + { 42 + if (env->buf) 43 + kfree(env->buf); 44 + kfree(env); 45 + } 46 + 47 + /* Helper function to create hfsplus_unistr */ 48 + static void create_unistr(struct hfsplus_unistr *ustr, const char *ascii_str) 49 + { 50 + int len = strlen(ascii_str); 51 + int i; 52 + 53 + memset(ustr->unicode, 0, sizeof(ustr->unicode)); 54 + 55 + ustr->length = cpu_to_be16(len); 56 + for (i = 0; i < len && i < HFSPLUS_MAX_STRLEN; i++) 57 + ustr->unicode[i] = cpu_to_be16((u16)ascii_str[i]); 58 + } 59 + 60 + static void corrupt_unistr(struct hfsplus_unistr *ustr) 61 + { 62 + ustr->length = cpu_to_be16(U16_MAX); 63 + } 64 + 65 + /* Test hfsplus_strcasecmp function */ 66 + static void hfsplus_strcasecmp_test(struct kunit *test) 67 + { 68 + struct test_mock_string_env *mock_env; 69 + 70 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 71 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 72 + 73 + /* Test identical strings */ 74 + create_unistr(&mock_env->str1, "hello"); 75 + create_unistr(&mock_env->str2, "hello"); 76 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 77 + &mock_env->str2)); 78 + 79 + /* Test case insensitive comparison */ 80 + create_unistr(&mock_env->str1, "Hello"); 81 + create_unistr(&mock_env->str2, "hello"); 82 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 83 + &mock_env->str2)); 84 + 85 + create_unistr(&mock_env->str1, "HELLO"); 86 + create_unistr(&mock_env->str2, "hello"); 87 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 88 + &mock_env->str2)); 89 + 90 + /* Test different strings */ 91 + create_unistr(&mock_env->str1, "apple"); 92 + create_unistr(&mock_env->str2, "banana"); 93 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 94 + &mock_env->str2), 0); 95 + 96 + create_unistr(&mock_env->str1, "zebra"); 97 + create_unistr(&mock_env->str2, "apple"); 98 + KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 99 + &mock_env->str2), 0); 100 + 101 + /* Test different lengths */ 102 + create_unistr(&mock_env->str1, "test"); 103 + create_unistr(&mock_env->str2, "testing"); 104 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 105 + &mock_env->str2), 0); 106 + 107 + create_unistr(&mock_env->str1, "testing"); 108 + create_unistr(&mock_env->str2, "test"); 109 + KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 110 + &mock_env->str2), 0); 111 + 112 + /* Test empty strings */ 113 + create_unistr(&mock_env->str1, ""); 114 + create_unistr(&mock_env->str2, ""); 115 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 116 + &mock_env->str2)); 117 + 118 + create_unistr(&mock_env->str1, ""); 119 + create_unistr(&mock_env->str2, "test"); 120 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 121 + &mock_env->str2), 0); 122 + 123 + /* Test single characters */ 124 + create_unistr(&mock_env->str1, "A"); 125 + create_unistr(&mock_env->str2, "a"); 126 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 127 + &mock_env->str2)); 128 + 129 + create_unistr(&mock_env->str1, "A"); 130 + create_unistr(&mock_env->str2, "B"); 131 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 132 + &mock_env->str2), 0); 133 + 134 + /* Test maximum length strings */ 135 + memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 136 + mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0'; 137 + create_unistr(&mock_env->str1, mock_env->buf); 138 + create_unistr(&mock_env->str2, mock_env->buf); 139 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 140 + &mock_env->str2)); 141 + 142 + /* Change one character in the middle */ 143 + mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b'; 144 + create_unistr(&mock_env->str2, mock_env->buf); 145 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 146 + &mock_env->str2), 0); 147 + 148 + /* Test corrupted strings */ 149 + create_unistr(&mock_env->str1, ""); 150 + corrupt_unistr(&mock_env->str1); 151 + create_unistr(&mock_env->str2, ""); 152 + KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1, 153 + &mock_env->str2)); 154 + 155 + create_unistr(&mock_env->str1, ""); 156 + create_unistr(&mock_env->str2, ""); 157 + corrupt_unistr(&mock_env->str2); 158 + KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1, 159 + &mock_env->str2)); 160 + 161 + create_unistr(&mock_env->str1, "test"); 162 + corrupt_unistr(&mock_env->str1); 163 + create_unistr(&mock_env->str2, "testing"); 164 + KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 165 + &mock_env->str2), 0); 166 + 167 + create_unistr(&mock_env->str1, "test"); 168 + create_unistr(&mock_env->str2, "testing"); 169 + corrupt_unistr(&mock_env->str2); 170 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 171 + &mock_env->str2), 0); 172 + 173 + create_unistr(&mock_env->str1, "testing"); 174 + corrupt_unistr(&mock_env->str1); 175 + create_unistr(&mock_env->str2, "test"); 176 + KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 177 + &mock_env->str2), 0); 178 + 179 + create_unistr(&mock_env->str1, "testing"); 180 + create_unistr(&mock_env->str2, "test"); 181 + corrupt_unistr(&mock_env->str2); 182 + KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 183 + &mock_env->str2), 0); 184 + 185 + free_mock_str_env(mock_env); 186 + } 187 + 188 + /* Test hfsplus_strcmp function (case-sensitive) */ 189 + static void hfsplus_strcmp_test(struct kunit *test) 190 + { 191 + struct test_mock_string_env *mock_env; 192 + 193 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 194 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 195 + 196 + /* Test identical strings */ 197 + create_unistr(&mock_env->str1, "hello"); 198 + create_unistr(&mock_env->str2, "hello"); 199 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 200 + &mock_env->str2)); 201 + 202 + /* Test case sensitive comparison - should NOT be equal */ 203 + create_unistr(&mock_env->str1, "Hello"); 204 + create_unistr(&mock_env->str2, "hello"); 205 + KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 206 + &mock_env->str2)); 207 + /* 'H' < 'h' in Unicode */ 208 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 209 + &mock_env->str2), 0); 210 + 211 + /* Test lexicographic ordering */ 212 + create_unistr(&mock_env->str1, "apple"); 213 + create_unistr(&mock_env->str2, "banana"); 214 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 215 + &mock_env->str2), 0); 216 + 217 + create_unistr(&mock_env->str1, "zebra"); 218 + create_unistr(&mock_env->str2, "apple"); 219 + KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 220 + &mock_env->str2), 0); 221 + 222 + /* Test different lengths with common prefix */ 223 + create_unistr(&mock_env->str1, "test"); 224 + create_unistr(&mock_env->str2, "testing"); 225 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 226 + &mock_env->str2), 0); 227 + 228 + create_unistr(&mock_env->str1, "testing"); 229 + create_unistr(&mock_env->str2, "test"); 230 + KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 231 + &mock_env->str2), 0); 232 + 233 + /* Test empty strings */ 234 + create_unistr(&mock_env->str1, ""); 235 + create_unistr(&mock_env->str2, ""); 236 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 237 + &mock_env->str2)); 238 + 239 + /* Test maximum length strings */ 240 + memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 241 + mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0'; 242 + create_unistr(&mock_env->str1, mock_env->buf); 243 + create_unistr(&mock_env->str2, mock_env->buf); 244 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 245 + &mock_env->str2)); 246 + 247 + /* Change one character in the middle */ 248 + mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b'; 249 + create_unistr(&mock_env->str2, mock_env->buf); 250 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 251 + &mock_env->str2), 0); 252 + 253 + /* Test corrupted strings */ 254 + create_unistr(&mock_env->str1, ""); 255 + corrupt_unistr(&mock_env->str1); 256 + create_unistr(&mock_env->str2, ""); 257 + KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 258 + &mock_env->str2)); 259 + 260 + create_unistr(&mock_env->str1, ""); 261 + create_unistr(&mock_env->str2, ""); 262 + corrupt_unistr(&mock_env->str2); 263 + KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 264 + &mock_env->str2)); 265 + 266 + create_unistr(&mock_env->str1, "test"); 267 + corrupt_unistr(&mock_env->str1); 268 + create_unistr(&mock_env->str2, "testing"); 269 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 270 + &mock_env->str2), 0); 271 + 272 + create_unistr(&mock_env->str1, "test"); 273 + create_unistr(&mock_env->str2, "testing"); 274 + corrupt_unistr(&mock_env->str2); 275 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 276 + &mock_env->str2), 0); 277 + 278 + create_unistr(&mock_env->str1, "testing"); 279 + corrupt_unistr(&mock_env->str1); 280 + create_unistr(&mock_env->str2, "test"); 281 + KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 282 + &mock_env->str2), 0); 283 + 284 + create_unistr(&mock_env->str1, "testing"); 285 + create_unistr(&mock_env->str2, "test"); 286 + corrupt_unistr(&mock_env->str2); 287 + KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 288 + &mock_env->str2), 0); 289 + 290 + free_mock_str_env(mock_env); 291 + } 292 + 293 + /* Test Unicode edge cases */ 294 + static void hfsplus_unicode_edge_cases_test(struct kunit *test) 295 + { 296 + struct test_mock_string_env *mock_env; 297 + 298 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 299 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 300 + 301 + /* Test with special characters */ 302 + mock_env->str1.length = cpu_to_be16(3); 303 + mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */ 304 + mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */ 305 + mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 306 + 307 + mock_env->str2.length = cpu_to_be16(3); 308 + mock_env->str2.unicode[0] = cpu_to_be16(0x00E9); /* é */ 309 + mock_env->str2.unicode[1] = cpu_to_be16(0x00F1); /* ñ */ 310 + mock_env->str2.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 311 + 312 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 313 + &mock_env->str2)); 314 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 315 + &mock_env->str2)); 316 + 317 + /* Test with different special characters */ 318 + mock_env->str2.unicode[1] = cpu_to_be16(0x00F2); /* ò */ 319 + KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 320 + &mock_env->str2)); 321 + 322 + /* Test null characters within string (should be handled correctly) */ 323 + mock_env->str1.length = cpu_to_be16(3); 324 + mock_env->str1.unicode[0] = cpu_to_be16('a'); 325 + mock_env->str1.unicode[1] = cpu_to_be16(0x0000); /* null */ 326 + mock_env->str1.unicode[2] = cpu_to_be16('b'); 327 + 328 + mock_env->str2.length = cpu_to_be16(3); 329 + mock_env->str2.unicode[0] = cpu_to_be16('a'); 330 + mock_env->str2.unicode[1] = cpu_to_be16(0x0000); /* null */ 331 + mock_env->str2.unicode[2] = cpu_to_be16('b'); 332 + 333 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 334 + &mock_env->str2)); 335 + 336 + free_mock_str_env(mock_env); 337 + } 338 + 339 + /* Test boundary conditions */ 340 + static void hfsplus_unicode_boundary_test(struct kunit *test) 341 + { 342 + struct test_mock_string_env *mock_env; 343 + int i; 344 + 345 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 346 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 347 + 348 + /* Test maximum length boundary */ 349 + mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 350 + mock_env->str2.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 351 + 352 + for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) { 353 + mock_env->str1.unicode[i] = cpu_to_be16('A'); 354 + mock_env->str2.unicode[i] = cpu_to_be16('A'); 355 + } 356 + 357 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 358 + &mock_env->str2)); 359 + 360 + /* Change last character */ 361 + mock_env->str2.unicode[HFSPLUS_MAX_STRLEN - 1] = cpu_to_be16('B'); 362 + KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 363 + &mock_env->str2), 0); 364 + 365 + /* Test zero length strings */ 366 + mock_env->str1.length = cpu_to_be16(0); 367 + mock_env->str2.length = cpu_to_be16(0); 368 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 369 + &mock_env->str2)); 370 + KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 371 + &mock_env->str2)); 372 + 373 + /* Test one character vs empty */ 374 + mock_env->str1.length = cpu_to_be16(1); 375 + mock_env->str1.unicode[0] = cpu_to_be16('A'); 376 + mock_env->str2.length = cpu_to_be16(0); 377 + KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 378 + &mock_env->str2), 0); 379 + KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 380 + &mock_env->str2), 0); 381 + 382 + free_mock_str_env(mock_env); 383 + } 384 + 385 + /* Mock superblock and NLS table for testing hfsplus_uni2asc */ 386 + struct test_mock_sb { 387 + struct nls_table nls; 388 + struct hfsplus_sb_info sb_info; 389 + struct super_block sb; 390 + }; 391 + 392 + static struct test_mock_sb *setup_mock_sb(void) 393 + { 394 + struct test_mock_sb *ptr; 395 + 396 + ptr = kzalloc(sizeof(struct test_mock_sb), GFP_KERNEL); 397 + if (!ptr) 398 + return NULL; 399 + 400 + ptr->nls.charset = "utf8"; 401 + ptr->nls.uni2char = NULL; /* Will use default behavior */ 402 + ptr->sb_info.nls = &ptr->nls; 403 + ptr->sb.s_fs_info = &ptr->sb_info; 404 + 405 + /* Set default flags - no decomposition, no case folding */ 406 + clear_bit(HFSPLUS_SB_NODECOMPOSE, &ptr->sb_info.flags); 407 + clear_bit(HFSPLUS_SB_CASEFOLD, &ptr->sb_info.flags); 408 + 409 + return ptr; 410 + } 411 + 412 + static void free_mock_sb(struct test_mock_sb *ptr) 413 + { 414 + kfree(ptr); 415 + } 416 + 417 + /* Simple uni2char implementation for testing */ 418 + static int test_uni2char(wchar_t uni, unsigned char *out, int boundlen) 419 + { 420 + if (boundlen <= 0) 421 + return -ENAMETOOLONG; 422 + 423 + if (uni < 0x80) { 424 + *out = (unsigned char)uni; 425 + return 1; 426 + } 427 + 428 + /* For non-ASCII, just use '?' as fallback */ 429 + *out = '?'; 430 + return 1; 431 + } 432 + 433 + /* Test hfsplus_uni2asc basic functionality */ 434 + static void hfsplus_uni2asc_basic_test(struct kunit *test) 435 + { 436 + struct test_mock_sb *mock_sb; 437 + struct test_mock_string_env *mock_env; 438 + int len, result; 439 + 440 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 441 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 442 + 443 + mock_sb = setup_mock_sb(); 444 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 445 + 446 + mock_sb->nls.uni2char = test_uni2char; 447 + 448 + /* Test simple ASCII string conversion */ 449 + create_unistr(&mock_env->str1, "hello"); 450 + len = mock_env->buf_size; 451 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 452 + mock_env->buf, &len); 453 + 454 + KUNIT_EXPECT_EQ(test, 0, result); 455 + KUNIT_EXPECT_EQ(test, 5, len); 456 + KUNIT_EXPECT_STREQ(test, "hello", mock_env->buf); 457 + 458 + /* Test empty string */ 459 + create_unistr(&mock_env->str1, ""); 460 + len = mock_env->buf_size; 461 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 462 + mock_env->buf, &len); 463 + 464 + KUNIT_EXPECT_EQ(test, 0, result); 465 + KUNIT_EXPECT_EQ(test, 0, len); 466 + 467 + /* Test single character */ 468 + create_unistr(&mock_env->str1, "A"); 469 + len = mock_env->buf_size; 470 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 471 + mock_env->buf, &len); 472 + 473 + KUNIT_EXPECT_EQ(test, 0, result); 474 + KUNIT_EXPECT_EQ(test, 1, len); 475 + KUNIT_EXPECT_EQ(test, 'A', mock_env->buf[0]); 476 + 477 + free_mock_str_env(mock_env); 478 + free_mock_sb(mock_sb); 479 + } 480 + 481 + /* Test special character handling */ 482 + static void hfsplus_uni2asc_special_chars_test(struct kunit *test) 483 + { 484 + struct test_mock_sb *mock_sb; 485 + struct test_mock_string_env *mock_env; 486 + int len, result; 487 + 488 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 489 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 490 + 491 + mock_sb = setup_mock_sb(); 492 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 493 + 494 + mock_sb->nls.uni2char = test_uni2char; 495 + 496 + /* Test null character conversion (should become 0x2400) */ 497 + mock_env->str1.length = cpu_to_be16(1); 498 + mock_env->str1.unicode[0] = cpu_to_be16(0x0000); 499 + len = mock_env->buf_size; 500 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 501 + mock_env->buf, &len); 502 + 503 + KUNIT_EXPECT_EQ(test, 0, result); 504 + KUNIT_EXPECT_EQ(test, 1, len); 505 + /* Our test implementation returns '?' for non-ASCII */ 506 + KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]); 507 + 508 + /* Test forward slash conversion (should become colon) */ 509 + mock_env->str1.length = cpu_to_be16(1); 510 + mock_env->str1.unicode[0] = cpu_to_be16('/'); 511 + len = mock_env->buf_size; 512 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 513 + mock_env->buf, &len); 514 + 515 + KUNIT_EXPECT_EQ(test, 0, result); 516 + KUNIT_EXPECT_EQ(test, 1, len); 517 + KUNIT_EXPECT_EQ(test, ':', mock_env->buf[0]); 518 + 519 + /* Test string with mixed special characters */ 520 + mock_env->str1.length = cpu_to_be16(3); 521 + mock_env->str1.unicode[0] = cpu_to_be16('a'); 522 + mock_env->str1.unicode[1] = cpu_to_be16('/'); 523 + mock_env->str1.unicode[2] = cpu_to_be16('b'); 524 + len = mock_env->buf_size; 525 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 526 + mock_env->buf, &len); 527 + 528 + KUNIT_EXPECT_EQ(test, 0, result); 529 + KUNIT_EXPECT_EQ(test, 3, len); 530 + KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[0]); 531 + KUNIT_EXPECT_EQ(test, ':', mock_env->buf[1]); 532 + KUNIT_EXPECT_EQ(test, 'b', mock_env->buf[2]); 533 + 534 + free_mock_str_env(mock_env); 535 + free_mock_sb(mock_sb); 536 + } 537 + 538 + /* Test buffer length handling */ 539 + static void hfsplus_uni2asc_buffer_test(struct kunit *test) 540 + { 541 + struct test_mock_sb *mock_sb; 542 + struct test_mock_string_env *mock_env; 543 + int len, result; 544 + 545 + mock_env = setup_mock_str_env(10); 546 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 547 + 548 + mock_sb = setup_mock_sb(); 549 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 550 + 551 + mock_sb->nls.uni2char = test_uni2char; 552 + 553 + /* Test insufficient buffer space */ 554 + create_unistr(&mock_env->str1, "toolongstring"); 555 + len = 5; /* Buffer too small */ 556 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 557 + mock_env->buf, &len); 558 + 559 + KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 560 + KUNIT_EXPECT_EQ(test, 5, len); /* Should be set to consumed length */ 561 + 562 + /* Test exact buffer size */ 563 + create_unistr(&mock_env->str1, "exact"); 564 + len = 5; 565 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 566 + mock_env->buf, &len); 567 + 568 + KUNIT_EXPECT_EQ(test, 0, result); 569 + KUNIT_EXPECT_EQ(test, 5, len); 570 + 571 + /* Test zero length buffer */ 572 + create_unistr(&mock_env->str1, "test"); 573 + len = 0; 574 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 575 + mock_env->buf, &len); 576 + 577 + KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 578 + KUNIT_EXPECT_EQ(test, 0, len); 579 + 580 + free_mock_str_env(mock_env); 581 + free_mock_sb(mock_sb); 582 + } 583 + 584 + /* Test corrupted unicode string handling */ 585 + static void hfsplus_uni2asc_corrupted_test(struct kunit *test) 586 + { 587 + struct test_mock_sb *mock_sb; 588 + struct test_mock_string_env *mock_env; 589 + int len, result; 590 + 591 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 592 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 593 + 594 + mock_sb = setup_mock_sb(); 595 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 596 + 597 + mock_sb->nls.uni2char = test_uni2char; 598 + 599 + /* Test corrupted length (too large) */ 600 + create_unistr(&mock_env->str1, "test"); 601 + corrupt_unistr(&mock_env->str1); /* Sets length to U16_MAX */ 602 + len = mock_env->buf_size; 603 + 604 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 605 + mock_env->buf, &len); 606 + 607 + /* Should still work but with corrected length */ 608 + KUNIT_EXPECT_EQ(test, 0, result); 609 + /* 610 + * Length should be corrected to HFSPLUS_MAX_STRLEN 611 + * and processed accordingly 612 + */ 613 + KUNIT_EXPECT_GT(test, len, 0); 614 + 615 + free_mock_str_env(mock_env); 616 + free_mock_sb(mock_sb); 617 + } 618 + 619 + /* Test edge cases and boundary conditions */ 620 + static void hfsplus_uni2asc_edge_cases_test(struct kunit *test) 621 + { 622 + struct test_mock_sb *mock_sb; 623 + struct test_mock_string_env *mock_env; 624 + int len, result; 625 + int i; 626 + 627 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN * 2); 628 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 629 + 630 + mock_sb = setup_mock_sb(); 631 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 632 + 633 + mock_sb->nls.uni2char = test_uni2char; 634 + 635 + /* Test maximum length string */ 636 + mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 637 + for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) 638 + mock_env->str1.unicode[i] = cpu_to_be16('a'); 639 + 640 + len = mock_env->buf_size; 641 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 642 + mock_env->buf, &len); 643 + 644 + KUNIT_EXPECT_EQ(test, 0, result); 645 + KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, len); 646 + 647 + /* Verify all characters are 'a' */ 648 + for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) 649 + KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[i]); 650 + 651 + /* Test string with high Unicode values (non-ASCII) */ 652 + mock_env->str1.length = cpu_to_be16(3); 653 + mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */ 654 + mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */ 655 + mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 656 + len = mock_env->buf_size; 657 + result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 658 + mock_env->buf, &len); 659 + 660 + KUNIT_EXPECT_EQ(test, 0, result); 661 + KUNIT_EXPECT_EQ(test, 3, len); 662 + /* Our test implementation converts non-ASCII to '?' */ 663 + KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]); 664 + KUNIT_EXPECT_EQ(test, '?', mock_env->buf[1]); 665 + KUNIT_EXPECT_EQ(test, '?', mock_env->buf[2]); 666 + 667 + free_mock_str_env(mock_env); 668 + free_mock_sb(mock_sb); 669 + } 670 + 671 + /* Simple char2uni implementation for testing */ 672 + static int test_char2uni(const unsigned char *rawstring, 673 + int boundlen, wchar_t *uni) 674 + { 675 + if (boundlen <= 0) 676 + return -EINVAL; 677 + 678 + *uni = (wchar_t)*rawstring; 679 + return 1; 680 + } 681 + 682 + /* Helper function to check unicode string contents */ 683 + static void check_unistr_content(struct kunit *test, 684 + struct hfsplus_unistr *ustr, 685 + const char *expected_ascii) 686 + { 687 + int expected_len = strlen(expected_ascii); 688 + int actual_len = be16_to_cpu(ustr->length); 689 + int i; 690 + 691 + KUNIT_EXPECT_EQ(test, expected_len, actual_len); 692 + 693 + for (i = 0; i < expected_len && i < actual_len; i++) { 694 + u16 expected_char = (u16)expected_ascii[i]; 695 + u16 actual_char = be16_to_cpu(ustr->unicode[i]); 696 + 697 + KUNIT_EXPECT_EQ(test, expected_char, actual_char); 698 + } 699 + } 700 + 701 + /* Test hfsplus_asc2uni basic functionality */ 702 + static void hfsplus_asc2uni_basic_test(struct kunit *test) 703 + { 704 + struct test_mock_sb *mock_sb; 705 + struct test_mock_string_env *mock_env; 706 + int result; 707 + 708 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 709 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 710 + 711 + mock_sb = setup_mock_sb(); 712 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 713 + 714 + mock_sb->nls.char2uni = test_char2uni; 715 + 716 + /* Test simple ASCII string conversion */ 717 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 718 + HFSPLUS_MAX_STRLEN, "hello", 5); 719 + 720 + KUNIT_EXPECT_EQ(test, 0, result); 721 + check_unistr_content(test, &mock_env->str1, "hello"); 722 + 723 + /* Test empty string */ 724 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 725 + HFSPLUS_MAX_STRLEN, "", 0); 726 + 727 + KUNIT_EXPECT_EQ(test, 0, result); 728 + KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length)); 729 + 730 + /* Test single character */ 731 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 732 + HFSPLUS_MAX_STRLEN, "A", 1); 733 + 734 + KUNIT_EXPECT_EQ(test, 0, result); 735 + check_unistr_content(test, &mock_env->str1, "A"); 736 + 737 + /* Test null-terminated string with explicit length */ 738 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 739 + HFSPLUS_MAX_STRLEN, "test\0extra", 4); 740 + 741 + KUNIT_EXPECT_EQ(test, 0, result); 742 + check_unistr_content(test, &mock_env->str1, "test"); 743 + 744 + free_mock_str_env(mock_env); 745 + free_mock_sb(mock_sb); 746 + } 747 + 748 + /* Test special character handling in asc2uni */ 749 + static void hfsplus_asc2uni_special_chars_test(struct kunit *test) 750 + { 751 + struct test_mock_sb *mock_sb; 752 + struct test_mock_string_env *mock_env; 753 + int result; 754 + 755 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 756 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 757 + 758 + mock_sb = setup_mock_sb(); 759 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 760 + 761 + mock_sb->nls.char2uni = test_char2uni; 762 + 763 + /* Test colon conversion (should become forward slash) */ 764 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 765 + HFSPLUS_MAX_STRLEN, ":", 1); 766 + 767 + KUNIT_EXPECT_EQ(test, 0, result); 768 + KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length)); 769 + KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0])); 770 + 771 + /* Test string with mixed special characters */ 772 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 773 + HFSPLUS_MAX_STRLEN, "a:b", 3); 774 + 775 + KUNIT_EXPECT_EQ(test, 0, result); 776 + KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length)); 777 + KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(mock_env->str1.unicode[0])); 778 + KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1])); 779 + KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(mock_env->str1.unicode[2])); 780 + 781 + /* Test multiple special characters */ 782 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 783 + HFSPLUS_MAX_STRLEN, ":::", 3); 784 + 785 + KUNIT_EXPECT_EQ(test, 0, result); 786 + KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length)); 787 + KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0])); 788 + KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1])); 789 + KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[2])); 790 + 791 + free_mock_str_env(mock_env); 792 + free_mock_sb(mock_sb); 793 + } 794 + 795 + /* Test buffer length limits */ 796 + static void hfsplus_asc2uni_buffer_limits_test(struct kunit *test) 797 + { 798 + struct test_mock_sb *mock_sb; 799 + struct test_mock_string_env *mock_env; 800 + int result; 801 + 802 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 10); 803 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 804 + 805 + mock_sb = setup_mock_sb(); 806 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 807 + 808 + mock_sb->nls.char2uni = test_char2uni; 809 + 810 + /* Test exact maximum length */ 811 + memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 812 + result = hfsplus_asc2uni(&mock_sb->sb, 813 + &mock_env->str1, HFSPLUS_MAX_STRLEN, 814 + mock_env->buf, HFSPLUS_MAX_STRLEN); 815 + 816 + KUNIT_EXPECT_EQ(test, 0, result); 817 + KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, 818 + be16_to_cpu(mock_env->str1.length)); 819 + 820 + /* Test exceeding maximum length */ 821 + memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5); 822 + result = hfsplus_asc2uni(&mock_sb->sb, 823 + &mock_env->str1, HFSPLUS_MAX_STRLEN, 824 + mock_env->buf, HFSPLUS_MAX_STRLEN + 5); 825 + 826 + KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 827 + KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, 828 + be16_to_cpu(mock_env->str1.length)); 829 + 830 + /* Test with smaller max_unistr_len */ 831 + result = hfsplus_asc2uni(&mock_sb->sb, 832 + &mock_env->str1, 5, "toolongstring", 13); 833 + 834 + KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 835 + KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length)); 836 + 837 + /* Test zero max length */ 838 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4); 839 + 840 + KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 841 + KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length)); 842 + 843 + free_mock_str_env(mock_env); 844 + free_mock_sb(mock_sb); 845 + } 846 + 847 + /* Test error handling and edge cases */ 848 + static void hfsplus_asc2uni_edge_cases_test(struct kunit *test) 849 + { 850 + struct test_mock_sb *mock_sb; 851 + struct hfsplus_unistr ustr; 852 + char test_str[] = {'a', '\0', 'b'}; 853 + int result; 854 + 855 + mock_sb = setup_mock_sb(); 856 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 857 + 858 + mock_sb->nls.char2uni = test_char2uni; 859 + 860 + /* Test zero length input */ 861 + result = hfsplus_asc2uni(&mock_sb->sb, 862 + &ustr, HFSPLUS_MAX_STRLEN, "test", 0); 863 + 864 + KUNIT_EXPECT_EQ(test, 0, result); 865 + KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length)); 866 + 867 + /* Test input with length mismatch */ 868 + result = hfsplus_asc2uni(&mock_sb->sb, 869 + &ustr, HFSPLUS_MAX_STRLEN, "hello", 3); 870 + 871 + KUNIT_EXPECT_EQ(test, 0, result); 872 + check_unistr_content(test, &ustr, "hel"); 873 + 874 + /* Test with various printable ASCII characters */ 875 + result = hfsplus_asc2uni(&mock_sb->sb, 876 + &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9); 877 + 878 + KUNIT_EXPECT_EQ(test, 0, result); 879 + check_unistr_content(test, &ustr, "ABC123!@#"); 880 + 881 + /* Test null character in the middle */ 882 + result = hfsplus_asc2uni(&mock_sb->sb, 883 + &ustr, HFSPLUS_MAX_STRLEN, test_str, 3); 884 + 885 + KUNIT_EXPECT_EQ(test, 0, result); 886 + KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length)); 887 + KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(ustr.unicode[0])); 888 + KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.unicode[1])); 889 + KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(ustr.unicode[2])); 890 + 891 + free_mock_sb(mock_sb); 892 + } 893 + 894 + /* Test decomposition flag behavior */ 895 + static void hfsplus_asc2uni_decompose_test(struct kunit *test) 896 + { 897 + struct test_mock_sb *mock_sb; 898 + struct test_mock_string_env *mock_env; 899 + int result; 900 + 901 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 902 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 903 + 904 + mock_sb = setup_mock_sb(); 905 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 906 + 907 + mock_sb->nls.char2uni = test_char2uni; 908 + 909 + /* Test with decomposition disabled (default) */ 910 + clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 911 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 912 + HFSPLUS_MAX_STRLEN, "test", 4); 913 + 914 + KUNIT_EXPECT_EQ(test, 0, result); 915 + check_unistr_content(test, &mock_env->str1, "test"); 916 + 917 + /* Test with decomposition enabled */ 918 + set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 919 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2, 920 + HFSPLUS_MAX_STRLEN, "test", 4); 921 + 922 + KUNIT_EXPECT_EQ(test, 0, result); 923 + check_unistr_content(test, &mock_env->str2, "test"); 924 + 925 + /* For simple ASCII, both should produce the same result */ 926 + KUNIT_EXPECT_EQ(test, 927 + be16_to_cpu(mock_env->str1.length), 928 + be16_to_cpu(mock_env->str2.length)); 929 + 930 + free_mock_str_env(mock_env); 931 + free_mock_sb(mock_sb); 932 + } 933 + 934 + /* Mock dentry for testing hfsplus_hash_dentry */ 935 + static struct dentry test_dentry; 936 + 937 + static void setup_mock_dentry(struct super_block *sb) 938 + { 939 + memset(&test_dentry, 0, sizeof(test_dentry)); 940 + test_dentry.d_sb = sb; 941 + } 942 + 943 + /* Helper function to create qstr */ 944 + static void create_qstr(struct qstr *str, const char *name) 945 + { 946 + str->name = name; 947 + str->len = strlen(name); 948 + str->hash = 0; /* Will be set by hash function */ 949 + } 950 + 951 + /* Test hfsplus_hash_dentry basic functionality */ 952 + static void hfsplus_hash_dentry_basic_test(struct kunit *test) 953 + { 954 + struct test_mock_sb *mock_sb; 955 + struct qstr str1, str2; 956 + int result; 957 + 958 + mock_sb = setup_mock_sb(); 959 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 960 + 961 + setup_mock_dentry(&mock_sb->sb); 962 + mock_sb->nls.char2uni = test_char2uni; 963 + 964 + /* Test basic string hashing */ 965 + create_qstr(&str1, "hello"); 966 + result = hfsplus_hash_dentry(&test_dentry, &str1); 967 + 968 + KUNIT_EXPECT_EQ(test, 0, result); 969 + KUNIT_EXPECT_NE(test, 0, str1.hash); 970 + 971 + /* Test that identical strings produce identical hashes */ 972 + create_qstr(&str2, "hello"); 973 + result = hfsplus_hash_dentry(&test_dentry, &str2); 974 + 975 + KUNIT_EXPECT_EQ(test, 0, result); 976 + KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 977 + 978 + /* Test empty string */ 979 + create_qstr(&str1, ""); 980 + result = hfsplus_hash_dentry(&test_dentry, &str1); 981 + 982 + /* Empty string should still produce a hash */ 983 + KUNIT_EXPECT_EQ(test, 0, result); 984 + 985 + /* Test single character */ 986 + create_qstr(&str1, "A"); 987 + result = hfsplus_hash_dentry(&test_dentry, &str1); 988 + 989 + KUNIT_EXPECT_EQ(test, 0, result); 990 + KUNIT_EXPECT_NE(test, 0, str1.hash); 991 + 992 + free_mock_sb(mock_sb); 993 + } 994 + 995 + /* Test case folding behavior in hash */ 996 + static void hfsplus_hash_dentry_casefold_test(struct kunit *test) 997 + { 998 + struct test_mock_sb *mock_sb; 999 + struct qstr str1, str2; 1000 + int result; 1001 + 1002 + mock_sb = setup_mock_sb(); 1003 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1004 + 1005 + setup_mock_dentry(&mock_sb->sb); 1006 + mock_sb->nls.char2uni = test_char2uni; 1007 + 1008 + /* Test with case folding disabled (default) */ 1009 + clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1010 + 1011 + create_qstr(&str1, "Hello"); 1012 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1013 + KUNIT_EXPECT_EQ(test, 0, result); 1014 + 1015 + create_qstr(&str2, "hello"); 1016 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1017 + KUNIT_EXPECT_EQ(test, 0, result); 1018 + 1019 + /* 1020 + * Without case folding, different cases 1021 + * should produce different hashes 1022 + */ 1023 + KUNIT_EXPECT_NE(test, str1.hash, str2.hash); 1024 + 1025 + /* Test with case folding enabled */ 1026 + set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1027 + 1028 + create_qstr(&str1, "Hello"); 1029 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1030 + KUNIT_EXPECT_EQ(test, 0, result); 1031 + 1032 + create_qstr(&str2, "hello"); 1033 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1034 + KUNIT_EXPECT_EQ(test, 0, result); 1035 + 1036 + /* With case folding, different cases should produce same hash */ 1037 + KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1038 + 1039 + /* Test mixed case */ 1040 + create_qstr(&str1, "HeLLo"); 1041 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1042 + KUNIT_EXPECT_EQ(test, 0, result); 1043 + KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1044 + 1045 + free_mock_sb(mock_sb); 1046 + } 1047 + 1048 + /* Test special character handling in hash */ 1049 + static void hfsplus_hash_dentry_special_chars_test(struct kunit *test) 1050 + { 1051 + struct test_mock_sb *mock_sb; 1052 + struct qstr str1, str2; 1053 + int result; 1054 + 1055 + mock_sb = setup_mock_sb(); 1056 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1057 + 1058 + setup_mock_dentry(&mock_sb->sb); 1059 + mock_sb->nls.char2uni = test_char2uni; 1060 + 1061 + /* Test colon conversion (: becomes /) */ 1062 + create_qstr(&str1, "file:name"); 1063 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1064 + KUNIT_EXPECT_EQ(test, 0, result); 1065 + 1066 + create_qstr(&str2, "file/name"); 1067 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1068 + KUNIT_EXPECT_EQ(test, 0, result); 1069 + 1070 + /* After conversion, these should produce the same hash */ 1071 + KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1072 + 1073 + /* Test multiple special characters */ 1074 + create_qstr(&str1, ":::"); 1075 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1076 + KUNIT_EXPECT_EQ(test, 0, result); 1077 + 1078 + create_qstr(&str2, "///"); 1079 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1080 + KUNIT_EXPECT_EQ(test, 0, result); 1081 + 1082 + KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1083 + 1084 + free_mock_sb(mock_sb); 1085 + } 1086 + 1087 + /* Test decomposition flag behavior in hash */ 1088 + static void hfsplus_hash_dentry_decompose_test(struct kunit *test) 1089 + { 1090 + struct test_mock_sb *mock_sb; 1091 + struct qstr str1, str2; 1092 + int result; 1093 + 1094 + mock_sb = setup_mock_sb(); 1095 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1096 + 1097 + setup_mock_dentry(&mock_sb->sb); 1098 + mock_sb->nls.char2uni = test_char2uni; 1099 + 1100 + /* Test with decomposition disabled (default) */ 1101 + clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1102 + 1103 + create_qstr(&str1, "test"); 1104 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1105 + KUNIT_EXPECT_EQ(test, 0, result); 1106 + 1107 + /* Test with decomposition enabled */ 1108 + set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1109 + 1110 + create_qstr(&str2, "test"); 1111 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1112 + KUNIT_EXPECT_EQ(test, 0, result); 1113 + 1114 + /* 1115 + * For simple ASCII, decomposition shouldn't change 1116 + * the hash much but the function should still work correctly 1117 + */ 1118 + KUNIT_EXPECT_NE(test, 0, str2.hash); 1119 + 1120 + free_mock_sb(mock_sb); 1121 + } 1122 + 1123 + /* Test hash consistency and distribution */ 1124 + static void hfsplus_hash_dentry_consistency_test(struct kunit *test) 1125 + { 1126 + struct test_mock_sb *mock_sb; 1127 + struct qstr str1, str2, str3; 1128 + unsigned long hash1; 1129 + int result; 1130 + 1131 + mock_sb = setup_mock_sb(); 1132 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1133 + 1134 + setup_mock_dentry(&mock_sb->sb); 1135 + mock_sb->nls.char2uni = test_char2uni; 1136 + 1137 + /* Test that same string always produces same hash */ 1138 + create_qstr(&str1, "consistent"); 1139 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1140 + KUNIT_EXPECT_EQ(test, 0, result); 1141 + hash1 = str1.hash; 1142 + 1143 + create_qstr(&str2, "consistent"); 1144 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1145 + KUNIT_EXPECT_EQ(test, 0, result); 1146 + 1147 + KUNIT_EXPECT_EQ(test, hash1, str2.hash); 1148 + 1149 + /* Test that different strings produce different hashes */ 1150 + create_qstr(&str3, "different"); 1151 + result = hfsplus_hash_dentry(&test_dentry, &str3); 1152 + KUNIT_EXPECT_EQ(test, 0, result); 1153 + 1154 + KUNIT_EXPECT_NE(test, str1.hash, str3.hash); 1155 + 1156 + /* Test similar strings should have different hashes */ 1157 + create_qstr(&str1, "file1"); 1158 + result = hfsplus_hash_dentry(&test_dentry, &str1); 1159 + KUNIT_EXPECT_EQ(test, 0, result); 1160 + 1161 + create_qstr(&str2, "file2"); 1162 + result = hfsplus_hash_dentry(&test_dentry, &str2); 1163 + KUNIT_EXPECT_EQ(test, 0, result); 1164 + 1165 + KUNIT_EXPECT_NE(test, str1.hash, str2.hash); 1166 + 1167 + free_mock_sb(mock_sb); 1168 + } 1169 + 1170 + /* Test edge cases and boundary conditions */ 1171 + static void hfsplus_hash_dentry_edge_cases_test(struct kunit *test) 1172 + { 1173 + struct test_mock_sb *mock_sb; 1174 + struct test_mock_string_env *mock_env; 1175 + struct qstr str; 1176 + int result; 1177 + 1178 + mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 1179 + KUNIT_ASSERT_NOT_NULL(test, mock_env); 1180 + 1181 + mock_sb = setup_mock_sb(); 1182 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1183 + 1184 + setup_mock_dentry(&mock_sb->sb); 1185 + mock_sb->nls.char2uni = test_char2uni; 1186 + 1187 + /* Test very long filename */ 1188 + memset(mock_env->buf, 'a', mock_env->buf_size - 1); 1189 + mock_env->buf[mock_env->buf_size - 1] = '\0'; 1190 + 1191 + create_qstr(&str, mock_env->buf); 1192 + result = hfsplus_hash_dentry(&test_dentry, &str); 1193 + 1194 + KUNIT_EXPECT_EQ(test, 0, result); 1195 + KUNIT_EXPECT_NE(test, 0, str.hash); 1196 + 1197 + /* Test filename with all printable ASCII characters */ 1198 + create_qstr(&str, "!@#$%^&*()_+-=[]{}|;':\",./<>?"); 1199 + result = hfsplus_hash_dentry(&test_dentry, &str); 1200 + 1201 + KUNIT_EXPECT_EQ(test, 0, result); 1202 + KUNIT_EXPECT_NE(test, 0, str.hash); 1203 + 1204 + /* Test with embedded null (though not typical for filenames) */ 1205 + str.name = "file\0hidden"; 1206 + str.len = 11; /* Include the null and text after it */ 1207 + str.hash = 0; 1208 + result = hfsplus_hash_dentry(&test_dentry, &str); 1209 + 1210 + KUNIT_EXPECT_EQ(test, 0, result); 1211 + KUNIT_EXPECT_NE(test, 0, str.hash); 1212 + 1213 + free_mock_str_env(mock_env); 1214 + free_mock_sb(mock_sb); 1215 + } 1216 + 1217 + /* Test hfsplus_compare_dentry basic functionality */ 1218 + static void hfsplus_compare_dentry_basic_test(struct kunit *test) 1219 + { 1220 + struct test_mock_sb *mock_sb; 1221 + struct qstr name; 1222 + int result; 1223 + 1224 + mock_sb = setup_mock_sb(); 1225 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1226 + 1227 + setup_mock_dentry(&mock_sb->sb); 1228 + mock_sb->nls.char2uni = test_char2uni; 1229 + 1230 + /* Test identical strings */ 1231 + create_qstr(&name, "hello"); 1232 + result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1233 + KUNIT_EXPECT_EQ(test, 0, result); 1234 + 1235 + /* Test different strings - lexicographic order */ 1236 + create_qstr(&name, "world"); 1237 + result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1238 + KUNIT_EXPECT_LT(test, result, 0); /* "hello" < "world" */ 1239 + 1240 + result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name); 1241 + KUNIT_EXPECT_EQ(test, 0, result); 1242 + 1243 + create_qstr(&name, "hello"); 1244 + result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name); 1245 + KUNIT_EXPECT_GT(test, result, 0); /* "world" > "hello" */ 1246 + 1247 + /* Test empty strings */ 1248 + create_qstr(&name, ""); 1249 + result = hfsplus_compare_dentry(&test_dentry, 0, "", &name); 1250 + KUNIT_EXPECT_EQ(test, 0, result); 1251 + 1252 + /* Test one empty, one non-empty */ 1253 + create_qstr(&name, "test"); 1254 + result = hfsplus_compare_dentry(&test_dentry, 0, "", &name); 1255 + KUNIT_EXPECT_LT(test, result, 0); /* "" < "test" */ 1256 + 1257 + create_qstr(&name, ""); 1258 + result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1259 + KUNIT_EXPECT_GT(test, result, 0); /* "test" > "" */ 1260 + 1261 + free_mock_sb(mock_sb); 1262 + } 1263 + 1264 + /* Test case folding behavior in comparison */ 1265 + static void hfsplus_compare_dentry_casefold_test(struct kunit *test) 1266 + { 1267 + struct test_mock_sb *mock_sb; 1268 + struct qstr name; 1269 + int result; 1270 + 1271 + mock_sb = setup_mock_sb(); 1272 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1273 + 1274 + setup_mock_dentry(&mock_sb->sb); 1275 + mock_sb->nls.char2uni = test_char2uni; 1276 + 1277 + /* Test with case folding disabled (default) */ 1278 + clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1279 + 1280 + create_qstr(&name, "hello"); 1281 + result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name); 1282 + /* Case sensitive: "Hello" != "hello" */ 1283 + KUNIT_EXPECT_NE(test, 0, result); 1284 + 1285 + create_qstr(&name, "Hello"); 1286 + result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1287 + /* Case sensitive: "hello" != "Hello" */ 1288 + KUNIT_EXPECT_NE(test, 0, result); 1289 + 1290 + /* Test with case folding enabled */ 1291 + set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1292 + 1293 + create_qstr(&name, "hello"); 1294 + result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name); 1295 + /* Case insensitive: "Hello" == "hello" */ 1296 + KUNIT_EXPECT_EQ(test, 0, result); 1297 + 1298 + create_qstr(&name, "Hello"); 1299 + result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1300 + /* Case insensitive: "hello" == "Hello" */ 1301 + KUNIT_EXPECT_EQ(test, 0, result); 1302 + 1303 + /* Test mixed case */ 1304 + create_qstr(&name, "TeSt"); 1305 + result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1306 + KUNIT_EXPECT_EQ(test, 0, result); 1307 + 1308 + create_qstr(&name, "test"); 1309 + result = hfsplus_compare_dentry(&test_dentry, 4, "TEST", &name); 1310 + KUNIT_EXPECT_EQ(test, 0, result); 1311 + 1312 + free_mock_sb(mock_sb); 1313 + } 1314 + 1315 + /* Test special character handling in comparison */ 1316 + static void hfsplus_compare_dentry_special_chars_test(struct kunit *test) 1317 + { 1318 + struct test_mock_sb *mock_sb; 1319 + struct qstr name; 1320 + int result; 1321 + 1322 + mock_sb = setup_mock_sb(); 1323 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1324 + 1325 + setup_mock_dentry(&mock_sb->sb); 1326 + mock_sb->nls.char2uni = test_char2uni; 1327 + 1328 + /* Test colon conversion (: becomes /) */ 1329 + create_qstr(&name, "file/name"); 1330 + result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name); 1331 + /* "file:name" == "file/name" after conversion */ 1332 + KUNIT_EXPECT_EQ(test, 0, result); 1333 + 1334 + create_qstr(&name, "file:name"); 1335 + result = hfsplus_compare_dentry(&test_dentry, 9, "file/name", &name); 1336 + /* "file/name" == "file:name" after conversion */ 1337 + KUNIT_EXPECT_EQ(test, 0, result); 1338 + 1339 + /* Test multiple special characters */ 1340 + create_qstr(&name, "///"); 1341 + result = hfsplus_compare_dentry(&test_dentry, 3, ":::", &name); 1342 + KUNIT_EXPECT_EQ(test, 0, result); 1343 + 1344 + /* Test mixed special and regular characters */ 1345 + create_qstr(&name, "a/b:c"); 1346 + result = hfsplus_compare_dentry(&test_dentry, 5, "a:b/c", &name); 1347 + /* Both become "a/b/c" after conversion */ 1348 + KUNIT_EXPECT_EQ(test, 0, result); 1349 + 1350 + free_mock_sb(mock_sb); 1351 + } 1352 + 1353 + /* Test length differences */ 1354 + static void hfsplus_compare_dentry_length_test(struct kunit *test) 1355 + { 1356 + struct test_mock_sb *mock_sb; 1357 + struct qstr name; 1358 + int result; 1359 + 1360 + mock_sb = setup_mock_sb(); 1361 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1362 + 1363 + setup_mock_dentry(&mock_sb->sb); 1364 + mock_sb->nls.char2uni = test_char2uni; 1365 + 1366 + /* Test different lengths with common prefix */ 1367 + create_qstr(&name, "testing"); 1368 + result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1369 + KUNIT_EXPECT_LT(test, result, 0); /* "test" < "testing" */ 1370 + 1371 + create_qstr(&name, "test"); 1372 + result = hfsplus_compare_dentry(&test_dentry, 7, "testing", &name); 1373 + KUNIT_EXPECT_GT(test, result, 0); /* "testing" > "test" */ 1374 + 1375 + /* Test exact length match */ 1376 + create_qstr(&name, "exact"); 1377 + result = hfsplus_compare_dentry(&test_dentry, 5, "exact", &name); 1378 + KUNIT_EXPECT_EQ(test, 0, result); 1379 + 1380 + /* Test length parameter vs actual string content */ 1381 + create_qstr(&name, "hello"); 1382 + result = hfsplus_compare_dentry(&test_dentry, 3, "hel", &name); 1383 + KUNIT_EXPECT_LT(test, result, 0); /* "hel" < "hello" */ 1384 + 1385 + /* Test longer first string but shorter length parameter */ 1386 + create_qstr(&name, "hi"); 1387 + result = hfsplus_compare_dentry(&test_dentry, 2, "hello", &name); 1388 + /* "he" < "hi" (only first 2 chars compared) */ 1389 + KUNIT_EXPECT_LT(test, result, 0); 1390 + 1391 + free_mock_sb(mock_sb); 1392 + } 1393 + 1394 + /* Test decomposition flag behavior */ 1395 + static void hfsplus_compare_dentry_decompose_test(struct kunit *test) 1396 + { 1397 + struct test_mock_sb *mock_sb; 1398 + struct qstr name; 1399 + int result; 1400 + 1401 + mock_sb = setup_mock_sb(); 1402 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1403 + 1404 + setup_mock_dentry(&mock_sb->sb); 1405 + mock_sb->nls.char2uni = test_char2uni; 1406 + 1407 + /* Test with decomposition disabled (default) */ 1408 + clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1409 + 1410 + create_qstr(&name, "test"); 1411 + result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1412 + KUNIT_EXPECT_EQ(test, 0, result); 1413 + 1414 + /* Test with decomposition enabled */ 1415 + set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1416 + 1417 + create_qstr(&name, "test"); 1418 + result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1419 + KUNIT_EXPECT_EQ(test, 0, result); 1420 + 1421 + /* For simple ASCII, decomposition shouldn't affect the result */ 1422 + create_qstr(&name, "different"); 1423 + result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1424 + KUNIT_EXPECT_NE(test, 0, result); 1425 + 1426 + free_mock_sb(mock_sb); 1427 + } 1428 + 1429 + /* Test edge cases and boundary conditions */ 1430 + static void hfsplus_compare_dentry_edge_cases_test(struct kunit *test) 1431 + { 1432 + struct test_mock_sb *mock_sb; 1433 + struct qstr name; 1434 + char *long_str; 1435 + char *long_str2; 1436 + u32 str_size = HFSPLUS_MAX_STRLEN + 1; 1437 + struct qstr null_name = { 1438 + .name = "a\0b", 1439 + .len = 3, 1440 + .hash = 0 1441 + }; 1442 + int result; 1443 + 1444 + mock_sb = setup_mock_sb(); 1445 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1446 + 1447 + setup_mock_dentry(&mock_sb->sb); 1448 + mock_sb->nls.char2uni = test_char2uni; 1449 + 1450 + long_str = kzalloc(str_size, GFP_KERNEL); 1451 + KUNIT_ASSERT_NOT_NULL(test, long_str); 1452 + 1453 + long_str2 = kzalloc(str_size, GFP_KERNEL); 1454 + KUNIT_ASSERT_NOT_NULL(test, long_str2); 1455 + 1456 + /* Test very long strings */ 1457 + memset(long_str, 'a', str_size - 1); 1458 + long_str[str_size - 1] = '\0'; 1459 + 1460 + create_qstr(&name, long_str); 1461 + result = hfsplus_compare_dentry(&test_dentry, str_size - 1, 1462 + long_str, &name); 1463 + KUNIT_EXPECT_EQ(test, 0, result); 1464 + 1465 + /* Test with difference at the end of long strings */ 1466 + memset(long_str2, 'a', str_size - 1); 1467 + long_str2[str_size - 1] = '\0'; 1468 + long_str2[str_size - 2] = 'b'; 1469 + create_qstr(&name, long_str2); 1470 + result = hfsplus_compare_dentry(&test_dentry, str_size - 1, 1471 + long_str, &name); 1472 + KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */ 1473 + 1474 + /* Test single character differences */ 1475 + create_qstr(&name, "b"); 1476 + result = hfsplus_compare_dentry(&test_dentry, 1, "a", &name); 1477 + KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */ 1478 + 1479 + create_qstr(&name, "a"); 1480 + result = hfsplus_compare_dentry(&test_dentry, 1, "b", &name); 1481 + KUNIT_EXPECT_GT(test, result, 0); /* 'b' > 'a' */ 1482 + 1483 + /* Test with null characters in the middle */ 1484 + result = hfsplus_compare_dentry(&test_dentry, 3, "a\0b", &null_name); 1485 + KUNIT_EXPECT_EQ(test, 0, result); 1486 + 1487 + /* Test all printable ASCII characters */ 1488 + create_qstr(&name, "!@#$%^&*()"); 1489 + result = hfsplus_compare_dentry(&test_dentry, 10, "!@#$%^&*()", &name); 1490 + KUNIT_EXPECT_EQ(test, 0, result); 1491 + 1492 + kfree(long_str); 1493 + kfree(long_str2); 1494 + free_mock_sb(mock_sb); 1495 + } 1496 + 1497 + /* Test combined flag behaviors */ 1498 + static void hfsplus_compare_dentry_combined_flags_test(struct kunit *test) 1499 + { 1500 + struct test_mock_sb *mock_sb; 1501 + struct qstr name; 1502 + int result; 1503 + 1504 + mock_sb = setup_mock_sb(); 1505 + KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1506 + 1507 + setup_mock_dentry(&mock_sb->sb); 1508 + mock_sb->nls.char2uni = test_char2uni; 1509 + 1510 + /* Test with both casefold and decompose enabled */ 1511 + set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1512 + set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1513 + 1514 + create_qstr(&name, "hello"); 1515 + result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name); 1516 + KUNIT_EXPECT_EQ(test, 0, result); 1517 + 1518 + /* Test special chars with case folding */ 1519 + create_qstr(&name, "File/Name"); 1520 + result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name); 1521 + KUNIT_EXPECT_EQ(test, 0, result); 1522 + 1523 + /* Test with both flags disabled */ 1524 + clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1525 + clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1526 + 1527 + create_qstr(&name, "hello"); 1528 + result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name); 1529 + KUNIT_EXPECT_NE(test, 0, result); /* Case sensitive */ 1530 + 1531 + /* But special chars should still be converted */ 1532 + create_qstr(&name, "file/name"); 1533 + result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name); 1534 + KUNIT_EXPECT_EQ(test, 0, result); 1535 + 1536 + free_mock_sb(mock_sb); 1537 + } 1538 + 1539 + static struct kunit_case hfsplus_unicode_test_cases[] = { 1540 + KUNIT_CASE(hfsplus_strcasecmp_test), 1541 + KUNIT_CASE(hfsplus_strcmp_test), 1542 + KUNIT_CASE(hfsplus_unicode_edge_cases_test), 1543 + KUNIT_CASE(hfsplus_unicode_boundary_test), 1544 + KUNIT_CASE(hfsplus_uni2asc_basic_test), 1545 + KUNIT_CASE(hfsplus_uni2asc_special_chars_test), 1546 + KUNIT_CASE(hfsplus_uni2asc_buffer_test), 1547 + KUNIT_CASE(hfsplus_uni2asc_corrupted_test), 1548 + KUNIT_CASE(hfsplus_uni2asc_edge_cases_test), 1549 + KUNIT_CASE(hfsplus_asc2uni_basic_test), 1550 + KUNIT_CASE(hfsplus_asc2uni_special_chars_test), 1551 + KUNIT_CASE(hfsplus_asc2uni_buffer_limits_test), 1552 + KUNIT_CASE(hfsplus_asc2uni_edge_cases_test), 1553 + KUNIT_CASE(hfsplus_asc2uni_decompose_test), 1554 + KUNIT_CASE(hfsplus_hash_dentry_basic_test), 1555 + KUNIT_CASE(hfsplus_hash_dentry_casefold_test), 1556 + KUNIT_CASE(hfsplus_hash_dentry_special_chars_test), 1557 + KUNIT_CASE(hfsplus_hash_dentry_decompose_test), 1558 + KUNIT_CASE(hfsplus_hash_dentry_consistency_test), 1559 + KUNIT_CASE(hfsplus_hash_dentry_edge_cases_test), 1560 + KUNIT_CASE(hfsplus_compare_dentry_basic_test), 1561 + KUNIT_CASE(hfsplus_compare_dentry_casefold_test), 1562 + KUNIT_CASE(hfsplus_compare_dentry_special_chars_test), 1563 + KUNIT_CASE(hfsplus_compare_dentry_length_test), 1564 + KUNIT_CASE(hfsplus_compare_dentry_decompose_test), 1565 + KUNIT_CASE(hfsplus_compare_dentry_edge_cases_test), 1566 + KUNIT_CASE(hfsplus_compare_dentry_combined_flags_test), 1567 + {} 1568 + }; 1569 + 1570 + static struct kunit_suite hfsplus_unicode_test_suite = { 1571 + .name = "hfsplus_unicode", 1572 + .test_cases = hfsplus_unicode_test_cases, 1573 + }; 1574 + 1575 + kunit_test_suite(hfsplus_unicode_test_suite); 1576 + 1577 + MODULE_DESCRIPTION("KUnit tests for HFS+ Unicode string operations"); 1578 + MODULE_LICENSE("GPL"); 1579 + MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+10 -12
fs/hfsplus/xattr.c
··· 265 265 struct hfs_find_data cat_fd; 266 266 hfsplus_cat_entry entry; 267 267 u16 cat_entry_flags, cat_entry_type; 268 - u16 folder_finderinfo_len = sizeof(struct DInfo) + 269 - sizeof(struct DXInfo); 270 - u16 file_finderinfo_len = sizeof(struct FInfo) + 271 - sizeof(struct FXInfo); 268 + u16 folder_finderinfo_len = sizeof(DInfo) + sizeof(DXInfo); 269 + u16 file_finderinfo_len = sizeof(FInfo) + sizeof(FXInfo); 272 270 273 271 if ((!S_ISREG(inode->i_mode) && 274 272 !S_ISDIR(inode->i_mode)) || ··· 442 444 ssize_t res = 0; 443 445 struct hfs_find_data fd; 444 446 u16 entry_type; 445 - u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo); 446 - u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo); 447 + u16 folder_rec_len = sizeof(DInfo) + sizeof(DXInfo); 448 + u16 file_rec_len = sizeof(FInfo) + sizeof(FXInfo); 447 449 u16 record_len = max(folder_rec_len, file_rec_len); 448 - u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)]; 449 - u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)]; 450 + u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)]; 451 + u8 file_finder_info[sizeof(FInfo) + sizeof(FXInfo)]; 450 452 451 453 if (size >= record_len) { 452 454 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); ··· 610 612 struct inode *inode = d_inode(dentry); 611 613 struct hfs_find_data fd; 612 614 u16 entry_type; 613 - u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)]; 614 - u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)]; 615 + u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)]; 616 + u8 file_finder_info[sizeof(FInfo) + sizeof(FXInfo)]; 615 617 unsigned long len, found_bit; 616 618 int xattr_name_len, symbols_count; 617 619 ··· 627 629 628 630 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); 629 631 if (entry_type == HFSPLUS_FOLDER) { 630 - len = sizeof(struct DInfo) + sizeof(struct DXInfo); 632 + len = sizeof(DInfo) + sizeof(DXInfo); 631 633 hfs_bnode_read(fd.bnode, folder_finder_info, 632 634 fd.entryoffset + 633 635 offsetof(struct hfsplus_cat_folder, user_info), 634 636 len); 635 637 found_bit = find_first_bit((void *)folder_finder_info, len*8); 636 638 } else if (entry_type == HFSPLUS_FILE) { 637 - len = sizeof(struct FInfo) + sizeof(struct FXInfo); 639 + len = sizeof(FInfo) + sizeof(FXInfo); 638 640 hfs_bnode_read(fd.bnode, file_finder_info, 639 641 fd.entryoffset + 640 642 offsetof(struct hfsplus_cat_file, user_info),
+633
include/linux/hfs_common.h
··· 17 17 pr_debug("pid %d:%s:%d %s(): " fmt, \ 18 18 current->pid, __FILE__, __LINE__, __func__, ##__VA_ARGS__) \ 19 19 20 + /* 21 + * Format of structures on disk 22 + * Information taken from Apple Technote #1150 (HFS Plus Volume Format) 23 + */ 24 + 25 + /* offsets to various blocks */ 26 + #define HFS_DD_BLK 0 /* Driver Descriptor block */ 27 + #define HFS_PMAP_BLK 1 /* First block of partition map */ 28 + #define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */ 29 + 30 + /* magic numbers for various disk blocks */ 31 + #define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */ 32 + #define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */ 33 + #define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */ 34 + #define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */ 35 + #define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */ 36 + 37 + #define HFSPLUS_VOLHEAD_SIG 0x482b 38 + #define HFSPLUS_VOLHEAD_SIGX 0x4858 39 + #define HFSPLUS_SUPER_MAGIC 0x482b 40 + 41 + #define HFSP_WRAP_MAGIC 0x4244 42 + #define HFSP_WRAP_ATTRIB_SLOCK 0x8000 43 + #define HFSP_WRAP_ATTRIB_SPARED 0x0200 44 + 45 + #define HFSP_WRAPOFF_SIG 0x00 46 + #define HFSP_WRAPOFF_ATTRIB 0x0A 47 + #define HFSP_WRAPOFF_ABLKSIZE 0x14 48 + #define HFSP_WRAPOFF_ABLKSTART 0x1C 49 + #define HFSP_WRAPOFF_EMBEDSIG 0x7C 50 + #define HFSP_WRAPOFF_EMBEDEXT 0x7E 51 + 52 + #define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */ 53 + #define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */ 54 + 55 + #define HFSP_SYMLINK_TYPE 0x736c6e6b /* 'slnk' */ 56 + #define HFSP_SYMLINK_CREATOR 0x72686170 /* 'rhap' */ 57 + 58 + #define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */ 59 + 60 + #define HFSP_HIDDENDIR_NAME \ 61 + "\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data" 62 + 63 + /* various FIXED size parameters */ 64 + #define HFS_SECTOR_SIZE 512 /* size of an HFS sector */ 65 + #define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */ 66 + #define HFS_MAX_VALENCE 32767U 67 + 68 + #define HFSPLUS_SECTOR_SIZE HFS_SECTOR_SIZE 69 + #define HFSPLUS_SECTOR_SHIFT HFS_SECTOR_SIZE_BITS 70 + #define HFSPLUS_VOLHEAD_SECTOR 2 71 + #define HFSPLUS_MIN_VERSION 4 72 + #define HFSPLUS_CURRENT_VERSION 5 73 + 74 + #define HFS_NAMELEN 31 /* maximum length of an HFS filename */ 75 + #define HFS_MAX_NAMELEN 128 76 + 77 + #define HFSPLUS_MAX_STRLEN 255 78 + #define HFSPLUS_ATTR_MAX_STRLEN 127 79 + 80 + /* Meanings of the drAtrb field of the MDB, 81 + * Reference: _Inside Macintosh: Files_ p. 2-61 82 + */ 83 + #define HFS_SB_ATTRIB_HLOCK (1 << 7) 84 + #define HFS_SB_ATTRIB_UNMNT (1 << 8) 85 + #define HFS_SB_ATTRIB_SPARED (1 << 9) 86 + #define HFS_SB_ATTRIB_INCNSTNT (1 << 11) 87 + #define HFS_SB_ATTRIB_SLOCK (1 << 15) 88 + 89 + /* values for hfs_cat_rec.cdrType */ 90 + #define HFS_CDR_DIR 0x01 /* folder (directory) */ 91 + #define HFS_CDR_FIL 0x02 /* file */ 92 + #define HFS_CDR_THD 0x03 /* folder (directory) thread */ 93 + #define HFS_CDR_FTH 0x04 /* file thread */ 94 + 95 + /* legal values for hfs_ext_key.FkType and hfs_file.fork */ 96 + #define HFS_FK_DATA 0x00 97 + #define HFS_FK_RSRC 0xFF 98 + 99 + /* bits in hfs_fil_entry.Flags */ 100 + #define HFS_FIL_LOCK 0x01 /* locked */ 101 + #define HFS_FIL_THD 0x02 /* file thread */ 102 + #define HFS_FIL_DOPEN 0x04 /* data fork open */ 103 + #define HFS_FIL_ROPEN 0x08 /* resource fork open */ 104 + #define HFS_FIL_DIR 0x10 /* directory (always clear) */ 105 + #define HFS_FIL_NOCOPY 0x40 /* copy-protected file */ 106 + #define HFS_FIL_USED 0x80 /* open */ 107 + 108 + /* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */ 109 + #define HFS_DIR_LOCK 0x01 /* locked */ 110 + #define HFS_DIR_THD 0x02 /* directory thread */ 111 + #define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */ 112 + #define HFS_DIR_MOUNTED 0x08 /* mounted */ 113 + #define HFS_DIR_DIR 0x10 /* directory (always set) */ 114 + #define HFS_DIR_EXPFOLDER 0x20 /* share point */ 115 + 116 + /* bits hfs_finfo.fdFlags */ 117 + #define HFS_FLG_INITED 0x0100 118 + #define HFS_FLG_LOCKED 0x1000 119 + #define HFS_FLG_INVISIBLE 0x4000 120 + 121 + /* Some special File ID numbers */ 122 + #define HFS_POR_CNID 1 /* Parent Of the Root */ 123 + #define HFSPLUS_POR_CNID HFS_POR_CNID 124 + #define HFS_ROOT_CNID 2 /* ROOT directory */ 125 + #define HFSPLUS_ROOT_CNID HFS_ROOT_CNID 126 + #define HFS_EXT_CNID 3 /* EXTents B-tree */ 127 + #define HFSPLUS_EXT_CNID HFS_EXT_CNID 128 + #define HFS_CAT_CNID 4 /* CATalog B-tree */ 129 + #define HFSPLUS_CAT_CNID HFS_CAT_CNID 130 + #define HFS_BAD_CNID 5 /* BAD blocks file */ 131 + #define HFSPLUS_BAD_CNID HFS_BAD_CNID 132 + #define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */ 133 + #define HFSPLUS_ALLOC_CNID HFS_ALLOC_CNID 134 + #define HFS_START_CNID 7 /* STARTup file (HFS+) */ 135 + #define HFSPLUS_START_CNID HFS_START_CNID 136 + #define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */ 137 + #define HFSPLUS_ATTR_CNID HFS_ATTR_CNID 138 + #define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */ 139 + #define HFSPLUS_EXCH_CNID HFS_EXCH_CNID 140 + #define HFS_FIRSTUSER_CNID 16 /* first available user id */ 141 + #define HFSPLUS_FIRSTUSER_CNID HFS_FIRSTUSER_CNID 142 + 143 + /*======== HFS/HFS+ structures as they appear on the disk ========*/ 144 + 145 + typedef __be32 hfsplus_cnid; 146 + typedef __be16 hfsplus_unichr; 147 + 148 + /* Pascal-style string of up to 31 characters */ 149 + struct hfs_name { 150 + u8 len; 151 + u8 name[HFS_NAMELEN]; 152 + } __packed; 153 + 154 + /* A "string" as used in filenames, etc. */ 155 + struct hfsplus_unistr { 156 + __be16 length; 157 + hfsplus_unichr unicode[HFSPLUS_MAX_STRLEN]; 158 + } __packed; 159 + 160 + /* 161 + * A "string" is used in attributes file 162 + * for name of extended attribute 163 + */ 164 + struct hfsplus_attr_unistr { 165 + __be16 length; 166 + hfsplus_unichr unicode[HFSPLUS_ATTR_MAX_STRLEN]; 167 + } __packed; 168 + 169 + struct hfs_extent { 170 + __be16 block; 171 + __be16 count; 172 + }; 173 + typedef struct hfs_extent hfs_extent_rec[3]; 174 + 175 + /* A single contiguous area of a file */ 176 + struct hfsplus_extent { 177 + __be32 start_block; 178 + __be32 block_count; 179 + } __packed; 180 + typedef struct hfsplus_extent hfsplus_extent_rec[8]; 181 + 182 + /* Information for a "Fork" in a file */ 183 + struct hfsplus_fork_raw { 184 + __be64 total_size; 185 + __be32 clump_size; 186 + __be32 total_blocks; 187 + hfsplus_extent_rec extents; 188 + } __packed; 189 + 190 + struct hfs_mdb { 191 + __be16 drSigWord; /* Signature word indicating fs type */ 192 + __be32 drCrDate; /* fs creation date/time */ 193 + __be32 drLsMod; /* fs modification date/time */ 194 + __be16 drAtrb; /* fs attributes */ 195 + __be16 drNmFls; /* number of files in root directory */ 196 + __be16 drVBMSt; /* location (in 512-byte blocks) 197 + of the volume bitmap */ 198 + __be16 drAllocPtr; /* location (in allocation blocks) 199 + to begin next allocation search */ 200 + __be16 drNmAlBlks; /* number of allocation blocks */ 201 + __be32 drAlBlkSiz; /* bytes in an allocation block */ 202 + __be32 drClpSiz; /* clumpsize, the number of bytes to 203 + allocate when extending a file */ 204 + __be16 drAlBlSt; /* location (in 512-byte blocks) 205 + of the first allocation block */ 206 + __be32 drNxtCNID; /* CNID to assign to the next 207 + file or directory created */ 208 + __be16 drFreeBks; /* number of free allocation blocks */ 209 + u8 drVN[28]; /* the volume label */ 210 + __be32 drVolBkUp; /* fs backup date/time */ 211 + __be16 drVSeqNum; /* backup sequence number */ 212 + __be32 drWrCnt; /* fs write count */ 213 + __be32 drXTClpSiz; /* clumpsize for the extents B-tree */ 214 + __be32 drCTClpSiz; /* clumpsize for the catalog B-tree */ 215 + __be16 drNmRtDirs; /* number of directories in 216 + the root directory */ 217 + __be32 drFilCnt; /* number of files in the fs */ 218 + __be32 drDirCnt; /* number of directories in the fs */ 219 + u8 drFndrInfo[32]; /* data used by the Finder */ 220 + __be16 drEmbedSigWord; /* embedded volume signature */ 221 + __be32 drEmbedExtent; /* starting block number (xdrStABN) 222 + and number of allocation blocks 223 + (xdrNumABlks) occupied by embedded 224 + volume */ 225 + __be32 drXTFlSize; /* bytes in the extents B-tree */ 226 + hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */ 227 + __be32 drCTFlSize; /* bytes in the catalog B-tree */ 228 + hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */ 229 + } __packed; 230 + 231 + /* HFS+ Volume Header */ 232 + struct hfsplus_vh { 233 + __be16 signature; 234 + __be16 version; 235 + __be32 attributes; 236 + __be32 last_mount_vers; 237 + u32 reserved; 238 + 239 + __be32 create_date; 240 + __be32 modify_date; 241 + __be32 backup_date; 242 + __be32 checked_date; 243 + 244 + __be32 file_count; 245 + __be32 folder_count; 246 + 247 + __be32 blocksize; 248 + __be32 total_blocks; 249 + __be32 free_blocks; 250 + 251 + __be32 next_alloc; 252 + __be32 rsrc_clump_sz; 253 + __be32 data_clump_sz; 254 + hfsplus_cnid next_cnid; 255 + 256 + __be32 write_count; 257 + __be64 encodings_bmp; 258 + 259 + u32 finder_info[8]; 260 + 261 + struct hfsplus_fork_raw alloc_file; 262 + struct hfsplus_fork_raw ext_file; 263 + struct hfsplus_fork_raw cat_file; 264 + struct hfsplus_fork_raw attr_file; 265 + struct hfsplus_fork_raw start_file; 266 + } __packed; 267 + 268 + /* HFS+ volume attributes */ 269 + #define HFSPLUS_VOL_UNMNT (1 << 8) 270 + #define HFSPLUS_VOL_SPARE_BLK (1 << 9) 271 + #define HFSPLUS_VOL_NOCACHE (1 << 10) 272 + #define HFSPLUS_VOL_INCNSTNT (1 << 11) 273 + #define HFSPLUS_VOL_NODEID_REUSED (1 << 12) 274 + #define HFSPLUS_VOL_JOURNALED (1 << 13) 275 + #define HFSPLUS_VOL_SOFTLOCK (1 << 15) 276 + #define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31) 277 + 278 + struct hfs_point { 279 + __be16 v; 280 + __be16 h; 281 + } __packed; 282 + 283 + typedef struct hfs_point hfsp_point; 284 + 285 + struct hfs_rect { 286 + __be16 top; 287 + __be16 left; 288 + __be16 bottom; 289 + __be16 right; 290 + } __packed; 291 + 292 + typedef struct hfs_rect hfsp_rect; 293 + 294 + struct hfs_finfo { 295 + __be32 fdType; 296 + __be32 fdCreator; 297 + __be16 fdFlags; 298 + struct hfs_point fdLocation; 299 + __be16 fdFldr; 300 + } __packed; 301 + 302 + typedef struct hfs_finfo FInfo; 303 + 304 + struct hfs_fxinfo { 305 + __be16 fdIconID; 306 + u8 fdUnused[8]; 307 + __be16 fdComment; 308 + __be32 fdPutAway; 309 + } __packed; 310 + 311 + typedef struct hfs_fxinfo FXInfo; 312 + 313 + struct hfs_dinfo { 314 + struct hfs_rect frRect; 315 + __be16 frFlags; 316 + struct hfs_point frLocation; 317 + __be16 frView; 318 + } __packed; 319 + 320 + typedef struct hfs_dinfo DInfo; 321 + 322 + struct hfs_dxinfo { 323 + struct hfs_point frScroll; 324 + __be32 frOpenChain; 325 + __be16 frUnused; 326 + __be16 frComment; 327 + __be32 frPutAway; 328 + } __packed; 329 + 330 + typedef struct hfs_dxinfo DXInfo; 331 + 332 + union hfs_finder_info { 333 + struct { 334 + struct hfs_finfo finfo; 335 + struct hfs_fxinfo fxinfo; 336 + } file; 337 + struct { 338 + struct hfs_dinfo dinfo; 339 + struct hfs_dxinfo dxinfo; 340 + } dir; 341 + } __packed; 342 + 343 + /* The key used in the catalog b-tree: */ 344 + struct hfs_cat_key { 345 + u8 key_len; /* number of bytes in the key */ 346 + u8 reserved; /* padding */ 347 + __be32 ParID; /* CNID of the parent dir */ 348 + struct hfs_name CName; /* The filename of the entry */ 349 + } __packed; 350 + 351 + /* HFS+ catalog entry key */ 352 + struct hfsplus_cat_key { 353 + __be16 key_len; 354 + hfsplus_cnid parent; 355 + struct hfsplus_unistr name; 356 + } __packed; 357 + 358 + #define HFSPLUS_CAT_KEYLEN (sizeof(struct hfsplus_cat_key)) 359 + 360 + /* The key used in the extents b-tree: */ 361 + struct hfs_ext_key { 362 + u8 key_len; /* number of bytes in the key */ 363 + u8 FkType; /* HFS_FK_{DATA,RSRC} */ 364 + __be32 FNum; /* The File ID of the file */ 365 + __be16 FABN; /* allocation blocks number*/ 366 + } __packed; 367 + 368 + /* HFS+ extents tree key */ 369 + struct hfsplus_ext_key { 370 + __be16 key_len; 371 + u8 fork_type; 372 + u8 pad; 373 + hfsplus_cnid cnid; 374 + __be32 start_block; 375 + } __packed; 376 + 377 + #define HFSPLUS_EXT_KEYLEN sizeof(struct hfsplus_ext_key) 378 + 379 + typedef union hfs_btree_key { 380 + u8 key_len; /* number of bytes in the key */ 381 + struct hfs_cat_key cat; 382 + struct hfs_ext_key ext; 383 + } hfs_btree_key; 384 + 385 + #define HFS_MAX_CAT_KEYLEN (sizeof(struct hfs_cat_key) - sizeof(u8)) 386 + #define HFS_MAX_EXT_KEYLEN (sizeof(struct hfs_ext_key) - sizeof(u8)) 387 + 388 + typedef union hfs_btree_key btree_key; 389 + 390 + /* The catalog record for a file */ 391 + struct hfs_cat_file { 392 + s8 type; /* The type of entry */ 393 + u8 reserved; 394 + u8 Flags; /* Flags such as read-only */ 395 + s8 Typ; /* file version number = 0 */ 396 + struct hfs_finfo UsrWds; /* data used by the Finder */ 397 + __be32 FlNum; /* The CNID */ 398 + __be16 StBlk; /* obsolete */ 399 + __be32 LgLen; /* The logical EOF of the data fork*/ 400 + __be32 PyLen; /* The physical EOF of the data fork */ 401 + __be16 RStBlk; /* obsolete */ 402 + __be32 RLgLen; /* The logical EOF of the rsrc fork */ 403 + __be32 RPyLen; /* The physical EOF of the rsrc fork */ 404 + __be32 CrDat; /* The creation date */ 405 + __be32 MdDat; /* The modified date */ 406 + __be32 BkDat; /* The last backup date */ 407 + struct hfs_fxinfo FndrInfo; /* more data for the Finder */ 408 + __be16 ClpSize; /* number of bytes to allocate 409 + when extending files */ 410 + hfs_extent_rec ExtRec; /* first extent record 411 + for the data fork */ 412 + hfs_extent_rec RExtRec; /* first extent record 413 + for the resource fork */ 414 + u32 Resrv; /* reserved by Apple */ 415 + } __packed; 416 + 417 + /* the catalog record for a directory */ 418 + struct hfs_cat_dir { 419 + s8 type; /* The type of entry */ 420 + u8 reserved; 421 + __be16 Flags; /* flags */ 422 + __be16 Val; /* Valence: number of files and 423 + dirs in the directory */ 424 + __be32 DirID; /* The CNID */ 425 + __be32 CrDat; /* The creation date */ 426 + __be32 MdDat; /* The modification date */ 427 + __be32 BkDat; /* The last backup date */ 428 + struct hfs_dinfo UsrInfo; /* data used by the Finder */ 429 + struct hfs_dxinfo FndrInfo; /* more data used by Finder */ 430 + u8 Resrv[16]; /* reserved by Apple */ 431 + } __packed; 432 + 433 + /* the catalog record for a thread */ 434 + struct hfs_cat_thread { 435 + s8 type; /* The type of entry */ 436 + u8 reserved[9]; /* reserved by Apple */ 437 + __be32 ParID; /* CNID of parent directory */ 438 + struct hfs_name CName; /* The name of this entry */ 439 + } __packed; 440 + 441 + /* A catalog tree record */ 442 + typedef union hfs_cat_rec { 443 + s8 type; /* The type of entry */ 444 + struct hfs_cat_file file; 445 + struct hfs_cat_dir dir; 446 + struct hfs_cat_thread thread; 447 + } hfs_cat_rec; 448 + 449 + /* POSIX permissions */ 450 + struct hfsplus_perm { 451 + __be32 owner; 452 + __be32 group; 453 + u8 rootflags; 454 + u8 userflags; 455 + __be16 mode; 456 + __be32 dev; 457 + } __packed; 458 + 459 + #define HFSPLUS_FLG_NODUMP 0x01 460 + #define HFSPLUS_FLG_IMMUTABLE 0x02 461 + #define HFSPLUS_FLG_APPEND 0x04 462 + 463 + /* HFS/HFS+ BTree node descriptor */ 464 + struct hfs_bnode_desc { 465 + __be32 next; /* (V) Number of the next node at this level */ 466 + __be32 prev; /* (V) Number of the prev node at this level */ 467 + u8 type; /* (F) The type of node */ 468 + u8 height; /* (F) The level of this node (leaves=1) */ 469 + __be16 num_recs; /* (V) The number of records in this node */ 470 + u16 reserved; 471 + } __packed; 472 + 473 + /* HFS/HFS+ BTree node types */ 474 + #define HFS_NODE_INDEX 0x00 /* An internal (index) node */ 475 + #define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ 476 + #define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ 477 + #define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ 478 + 479 + /* HFS/HFS+ BTree header */ 480 + struct hfs_btree_header_rec { 481 + __be16 depth; /* (V) The number of levels in this B-tree */ 482 + __be32 root; /* (V) The node number of the root node */ 483 + __be32 leaf_count; /* (V) The number of leaf records */ 484 + __be32 leaf_head; /* (V) The number of the first leaf node */ 485 + __be32 leaf_tail; /* (V) The number of the last leaf node */ 486 + __be16 node_size; /* (F) The number of bytes in a node (=512) */ 487 + __be16 max_key_len; /* (F) The length of a key in an index node */ 488 + __be32 node_count; /* (V) The total number of nodes */ 489 + __be32 free_nodes; /* (V) The number of unused nodes */ 490 + u16 reserved1; 491 + __be32 clump_size; /* (F) clump size. not usually used. */ 492 + u8 btree_type; /* (F) BTree type */ 493 + u8 key_type; 494 + __be32 attributes; /* (F) attributes */ 495 + u32 reserved3[16]; 496 + } __packed; 497 + 498 + /* BTree attributes */ 499 + #define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not 500 + used by hfsplus. */ 501 + #define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8. 502 + used by hfsplus. */ 503 + #define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of 504 + max key length. use din catalog 505 + b-tree but not in extents 506 + b-tree (hfsplus). */ 507 + 508 + /* HFS+ BTree misc info */ 509 + #define HFSPLUS_TREE_HEAD 0 510 + #define HFSPLUS_NODE_MXSZ 32768 511 + #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 512 + #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 513 + #define HFSPLUS_BTREE_HDR_USER_BYTES 128 514 + 515 + /* btree key type */ 516 + #define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */ 517 + #define HFSPLUS_KEY_BINARY 0xBC /* case-sensitive */ 518 + 519 + /* HFS+ folder data (part of an hfsplus_cat_entry) */ 520 + struct hfsplus_cat_folder { 521 + __be16 type; 522 + __be16 flags; 523 + __be32 valence; 524 + hfsplus_cnid id; 525 + __be32 create_date; 526 + __be32 content_mod_date; 527 + __be32 attribute_mod_date; 528 + __be32 access_date; 529 + __be32 backup_date; 530 + struct hfsplus_perm permissions; 531 + struct_group_attr(info, __packed, 532 + DInfo user_info; 533 + DXInfo finder_info; 534 + ); 535 + __be32 text_encoding; 536 + __be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */ 537 + } __packed; 538 + 539 + /* HFS+ file data (part of a cat_entry) */ 540 + struct hfsplus_cat_file { 541 + __be16 type; 542 + __be16 flags; 543 + u32 reserved1; 544 + hfsplus_cnid id; 545 + __be32 create_date; 546 + __be32 content_mod_date; 547 + __be32 attribute_mod_date; 548 + __be32 access_date; 549 + __be32 backup_date; 550 + struct hfsplus_perm permissions; 551 + struct_group_attr(info, __packed, 552 + FInfo user_info; 553 + FXInfo finder_info; 554 + ); 555 + __be32 text_encoding; 556 + u32 reserved2; 557 + 558 + struct hfsplus_fork_raw data_fork; 559 + struct hfsplus_fork_raw rsrc_fork; 560 + } __packed; 561 + 562 + /* File and folder flag bits */ 563 + #define HFSPLUS_FILE_LOCKED 0x0001 564 + #define HFSPLUS_FILE_THREAD_EXISTS 0x0002 565 + #define HFSPLUS_XATTR_EXISTS 0x0004 566 + #define HFSPLUS_ACL_EXISTS 0x0008 567 + #define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count 568 + * (HFSX only) */ 569 + 570 + /* HFS+ catalog thread (part of a cat_entry) */ 571 + struct hfsplus_cat_thread { 572 + __be16 type; 573 + s16 reserved; 574 + hfsplus_cnid parentID; 575 + struct hfsplus_unistr nodeName; 576 + } __packed; 577 + 578 + #define HFSPLUS_MIN_THREAD_SZ 10 579 + 580 + /* A data record in the catalog tree */ 581 + typedef union { 582 + __be16 type; 583 + struct hfsplus_cat_folder folder; 584 + struct hfsplus_cat_file file; 585 + struct hfsplus_cat_thread thread; 586 + } __packed hfsplus_cat_entry; 587 + 588 + /* HFS+ catalog entry type */ 589 + #define HFSPLUS_FOLDER 0x0001 590 + #define HFSPLUS_FILE 0x0002 591 + #define HFSPLUS_FOLDER_THREAD 0x0003 592 + #define HFSPLUS_FILE_THREAD 0x0004 593 + 594 + #define HFSPLUS_XATTR_FINDER_INFO_NAME "com.apple.FinderInfo" 595 + #define HFSPLUS_XATTR_ACL_NAME "com.apple.system.Security" 596 + 597 + #define HFSPLUS_ATTR_INLINE_DATA 0x10 598 + #define HFSPLUS_ATTR_FORK_DATA 0x20 599 + #define HFSPLUS_ATTR_EXTENTS 0x30 600 + 601 + /* HFS+ attributes tree key */ 602 + struct hfsplus_attr_key { 603 + __be16 key_len; 604 + __be16 pad; 605 + hfsplus_cnid cnid; 606 + __be32 start_block; 607 + struct hfsplus_attr_unistr key_name; 608 + } __packed; 609 + 610 + #define HFSPLUS_ATTR_KEYLEN sizeof(struct hfsplus_attr_key) 611 + 612 + /* HFS+ fork data attribute */ 613 + struct hfsplus_attr_fork_data { 614 + __be32 record_type; 615 + __be32 reserved; 616 + struct hfsplus_fork_raw the_fork; 617 + } __packed; 618 + 619 + /* HFS+ extension attribute */ 620 + struct hfsplus_attr_extents { 621 + __be32 record_type; 622 + __be32 reserved; 623 + struct hfsplus_extent extents; 624 + } __packed; 625 + 626 + #define HFSPLUS_MAX_INLINE_DATA_SIZE 3802 627 + 628 + /* HFS+ attribute inline data */ 629 + struct hfsplus_attr_inline_data { 630 + __be32 record_type; 631 + __be32 reserved1; 632 + u8 reserved2[6]; 633 + __be16 length; 634 + u8 raw_bytes[HFSPLUS_MAX_INLINE_DATA_SIZE]; 635 + } __packed; 636 + 637 + /* A data record in the attributes tree */ 638 + typedef union { 639 + __be32 record_type; 640 + struct hfsplus_attr_fork_data fork_data; 641 + struct hfsplus_attr_extents extents; 642 + struct hfsplus_attr_inline_data inline_data; 643 + } __packed hfsplus_attr_entry; 644 + 645 + /* HFS+ generic BTree key */ 646 + typedef union { 647 + __be16 key_len; 648 + struct hfsplus_cat_key cat; 649 + struct hfsplus_ext_key ext; 650 + struct hfsplus_attr_key attr; 651 + } __packed hfsplus_btree_key; 652 + 20 653 #endif /* _HFS_COMMON_H_ */