Skip to content

Commit 732af3a

Browse files
committed
hfsplus: rework logic of map nodes creation in xattr b-tree
In hfsplus_init_header_node() when node_count > 63488 (header bitmap capacity), the code calculates map_nodes, subtracts them from free_nodes, and marks their positions used in the bitmap. However, it doesn't write the actual map node structure (type, record offsets, bitmap) for those physical positions, only node 0 is written. This patch reworks hfsplus_create_attributes_file() logic by introducing a specialized method of hfsplus_init_map_node() and writing the allocated map b-tree's nodes by means of hfsplus_write_attributes_file_node() method. cc: John Paul Adrian Glaubitz <[email protected]> cc: Yangtao Li <[email protected]> cc: [email protected] Signed-off-by: Viacheslav Dubeyko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Viacheslav Dubeyko <[email protected]>
1 parent 63584d7 commit 732af3a

2 files changed

Lines changed: 106 additions & 23 deletions

File tree

fs/hfsplus/xattr.c

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static bool is_known_namespace(const char *name)
5050
return true;
5151
}
5252

53-
static void hfsplus_init_header_node(struct inode *attr_file,
53+
static u32 hfsplus_init_header_node(struct inode *attr_file,
5454
u32 clump_size,
5555
char *buf, u16 node_size)
5656
{
@@ -59,6 +59,7 @@ static void hfsplus_init_header_node(struct inode *attr_file,
5959
u16 offset;
6060
__be16 *rec_offsets;
6161
u32 hdr_node_map_rec_bits;
62+
u32 map_nodes = 0;
6263
char *bmp;
6364
u32 used_nodes;
6465
u32 used_bmp_bytes;
@@ -93,7 +94,6 @@ static void hfsplus_init_header_node(struct inode *attr_file,
9394
hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
9495
if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
9596
u32 map_node_bits;
96-
u32 map_nodes;
9797

9898
desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
9999
map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
@@ -116,21 +116,100 @@ static void hfsplus_init_header_node(struct inode *attr_file,
116116
*bmp = ~(0xFF >> used_nodes);
117117
offset += hdr_node_map_rec_bits / 8;
118118
*--rec_offsets = cpu_to_be16(offset);
119+
120+
return map_nodes;
121+
}
122+
123+
/*
124+
* Initialize a map node buffer. Map nodes have a single bitmap record.
125+
*/
126+
static void hfsplus_init_map_node(u8 *buf, u16 node_size, u32 next_node)
127+
{
128+
struct hfs_bnode_desc *desc;
129+
__be16 *rec_offsets;
130+
size_t rec_size = sizeof(__be16);
131+
u16 offset;
132+
133+
memset(buf, 0, node_size);
134+
135+
desc = (struct hfs_bnode_desc *)buf;
136+
desc->type = HFS_NODE_MAP;
137+
desc->num_recs = cpu_to_be16(1);
138+
desc->next = cpu_to_be32(next_node);
139+
140+
/*
141+
* A map node consists of the node descriptor and a single
142+
* map record. The map record is a continuation of the map
143+
* record contained in the header node. The size of the map
144+
* record is the size of the node, minus the size of
145+
* the node descriptor (14 bytes), minus the size of two offsets
146+
* (4 bytes), minus two bytes of free space. That is, the size of
147+
* the map record is the size of the node minus 20 bytes;
148+
* this keeps the length of the map record an even multiple of 4 bytes.
149+
* The start of the map record is not aligned to a 4-byte boundary:
150+
* it starts immediately after the node descriptor
151+
* (at an offset of 14 bytes).
152+
*
153+
* Two record offsets stored at the end of the node:
154+
* record[1] = start of record 0 -> sizeof(hfs_bnode_desc)
155+
* record[2] = start of free space
156+
*/
157+
rec_offsets = (__be16 *)(buf + node_size);
158+
159+
/* record #1 */
160+
offset = sizeof(struct hfs_bnode_desc);
161+
*--rec_offsets = cpu_to_be16(offset);
162+
163+
/* record #2 */
164+
offset = node_size;
165+
offset -= (u16)HFSPLUS_BTREE_MAP_NODE_RECS_COUNT * rec_size;
166+
offset -= HFSPLUS_BTREE_MAP_NODE_RESERVED_BYTES;
167+
*--rec_offsets = cpu_to_be16(offset);
168+
}
169+
170+
static inline
171+
int hfsplus_write_attributes_file_node(struct inode *attr_file, char *buf,
172+
u16 node_size, int *index)
173+
{
174+
struct address_space *mapping;
175+
struct page *page;
176+
void *kaddr;
177+
u32 written = 0;
178+
179+
mapping = attr_file->i_mapping;
180+
181+
for (; written < node_size; (*index)++, written += PAGE_SIZE) {
182+
page = read_mapping_page(mapping, *index, NULL);
183+
if (IS_ERR(page))
184+
return PTR_ERR(page);
185+
186+
kaddr = kmap_local_page(page);
187+
memcpy(kaddr, buf + written,
188+
min_t(size_t, PAGE_SIZE, node_size - written));
189+
kunmap_local(kaddr);
190+
191+
set_page_dirty(page);
192+
put_page(page);
193+
}
194+
195+
return 0;
119196
}
120197

121198
static int hfsplus_create_attributes_file(struct super_block *sb)
122199
{
123-
int err = 0;
124200
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
125201
struct inode *attr_file;
126202
struct hfsplus_inode_info *hip;
203+
struct hfs_bnode_desc *desc;
127204
u32 clump_size;
128205
u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
206+
u32 next_node;
207+
u32 map_node_idx;
208+
u32 map_nodes;
129209
char *buf;
130-
int index, written;
131-
struct address_space *mapping;
132-
struct page *page;
210+
int index;
133211
int old_state = HFSPLUS_EMPTY_ATTR_TREE;
212+
int err = 0;
134213

135214
hfs_dbg("ino %d\n", HFSPLUS_ATTR_CNID);
136215

@@ -195,7 +274,7 @@ static int hfsplus_create_attributes_file(struct super_block *sb)
195274
}
196275

197276
while (hip->alloc_blocks < hip->clump_blocks) {
198-
err = hfsplus_file_extend(attr_file, false);
277+
err = hfsplus_file_extend(attr_file, true);
199278
if (unlikely(err)) {
200279
pr_err("failed to extend attributes file\n");
201280
goto end_attr_file_creation;
@@ -212,28 +291,30 @@ static int hfsplus_create_attributes_file(struct super_block *sb)
212291
goto end_attr_file_creation;
213292
}
214293

215-
hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
294+
map_nodes = hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
216295

217-
mapping = attr_file->i_mapping;
296+
desc = (struct hfs_bnode_desc *)buf;
297+
next_node = be32_to_cpu(desc->next);
218298

219299
index = 0;
220-
written = 0;
221-
for (; written < node_size; index++, written += PAGE_SIZE) {
222-
void *kaddr;
223300

224-
page = read_mapping_page(mapping, index, NULL);
225-
if (IS_ERR(page)) {
226-
err = PTR_ERR(page);
227-
goto failed_header_node_init;
228-
}
301+
err = hfsplus_write_attributes_file_node(attr_file, buf,
302+
node_size, &index);
303+
if (unlikely(err))
304+
goto failed_header_node_init;
229305

230-
kaddr = kmap_atomic(page);
231-
memcpy(kaddr, buf + written,
232-
min_t(size_t, PAGE_SIZE, node_size - written));
233-
kunmap_atomic(kaddr);
306+
for (map_node_idx = 0; map_node_idx < map_nodes; map_node_idx++) {
307+
if (next_node >= map_nodes)
308+
next_node = 0;
234309

235-
set_page_dirty(page);
236-
put_page(page);
310+
hfsplus_init_map_node(buf, node_size, next_node);
311+
312+
err = hfsplus_write_attributes_file_node(attr_file, buf,
313+
node_size, &index);
314+
if (unlikely(err))
315+
goto failed_header_node_init;
316+
317+
next_node++;
237318
}
238319

239320
hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(sb), HFSPLUS_I_ATTR_DIRTY);

include/linux/hfs_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,8 @@ struct hfs_btree_header_rec {
518518
#define HFSPLUS_BTREE_HDR_MAP_REC_INDEX 2 /* Map (bitmap) record in Header node */
519519
#define HFSPLUS_BTREE_MAP_NODE_REC_INDEX 0 /* Map record in Map Node */
520520
#define HFSPLUS_BTREE_HDR_USER_BYTES 128
521+
#define HFSPLUS_BTREE_MAP_NODE_RECS_COUNT 2
522+
#define HFSPLUS_BTREE_MAP_NODE_RESERVED_BYTES 2
521523

522524
/* btree key type */
523525
#define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */

0 commit comments

Comments
 (0)