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

Configure Feed

Select the types of activity you want to include in your feed.

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

Pull hfsplus updates from Viacheslav Dubeyko:
"This contains several fixes of syzbot reported issues and HFS+ fixes
of xfstests failures.

- Fix a syzbot reported issue of a KMSAN uninit-value in
hfsplus_strcasecmp().

The root cause was that hfs_brec_read() doesn't validate that the
on-disk record size matches the expected size for the record type
being read. The fix introduced hfsplus_brec_read_cat() wrapper that
validates the record size based on the type field and returns -EIO
if size doesn't match (Deepanshu Kartikey)

- Fix a syzbot reported issue of processing corrupted HFS+ images
where the b-tree allocation bitmap indicates that the header node
(Node 0) is free. Node 0 must always be allocated. Violating this
invariant leads to allocator corruption, which cascades into kernel
panics or undefined behavior.

Prevent trusting a corrupted allocator state by adding a validation
check during hfs_btree_open(). If corruption is detected, print a
warning identifying the specific corrupted tree and force the
filesystem to mount read-only (SB_RDONLY).

This prevents kernel panics from corrupted images while enabling
data recovery (Shardul Bankar)

- Fix a potential deadlock in hfsplus_fill_super().

hfsplus_fill_super() calls hfs_find_init() to initialize a search
structure, which acquires tree->tree_lock. If the subsequent call
to hfsplus_cat_build_key() fails, the function jumps to the
out_put_root error label without releasing the lock.

Fix this by adding the missing hfs_find_exit(&fd) call before
jumping to the out_put_root error label. This ensures that
tree->tree_lock is properly released on the error path (Zilin Guan)

- Update a files ctime after rename in hfsplus_rename() (Yangtao Li)

The rest of the patches introduce the HFS+ fixes for the case of
generic/348, generic/728, generic/533, generic/523, and generic/642
test-cases of xfstests suite"

* tag 'hfs-v7.1-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs:
hfsplus: fix generic/642 failure
hfsplus: rework logic of map nodes creation in xattr b-tree
hfsplus: fix logic of alloc/free b-tree node
hfsplus: fix error processing issue in hfs_bmap_free()
hfsplus: fix potential race conditions in b-tree functionality
hfsplus: extract hidden directory search into a helper function
hfsplus: fix held lock freed on hfsplus_fill_super()
hfsplus: fix generic/523 test-case failure
hfsplus: validate b-tree node 0 bitmap at mount time
hfsplus: refactor b-tree map page access and add node-type validation
hfsplus: fix to update ctime after rename
hfsplus: fix generic/533 test-case failure
hfsplus: set ctime after setxattr and removexattr
hfsplus: fix uninit-value by validating catalog record size
hfsplus: fix potential Allocation File corruption after fsync

+682 -182
+34 -10
fs/hfsplus/attributes.c
··· 57 57 if (name) { 58 58 int res = hfsplus_asc2uni(sb, 59 59 (struct hfsplus_unistr *)&key->attr.key_name, 60 - HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name)); 60 + HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name), 61 + HFS_XATTR_NAME); 61 62 if (res) 62 63 return res; 63 64 len = be16_to_cpu(key->attr.key_name.length); ··· 154 153 if (err) 155 154 goto failed_find_attr; 156 155 err = hfs_brec_find(fd, hfs_find_rec_by_key); 157 - if (err) 156 + if (err == -ENOENT) { 157 + /* file exists but xattr is absent */ 158 + err = -ENODATA; 159 + goto failed_find_attr; 160 + } else if (err) 158 161 goto failed_find_attr; 159 162 } else { 160 163 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL); 161 164 if (err) 162 165 goto failed_find_attr; 163 166 err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid); 164 - if (err) 167 + if (err == -ENOENT) { 168 + /* file exists but xattr is absent */ 169 + err = -ENODATA; 170 + goto failed_find_attr; 171 + } else if (err) 165 172 goto failed_find_attr; 166 173 } 167 174 ··· 182 173 int err = 0; 183 174 struct super_block *sb = inode->i_sb; 184 175 struct hfs_find_data fd; 176 + 177 + hfs_dbg("name %s, ino %llu\n", 178 + name ? name : NULL, inode->i_ino); 185 179 186 180 if (!HFSPLUS_SB(sb)->attr_tree) 187 181 return 0; ··· 253 241 return err; 254 242 } 255 243 244 + hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(sb), HFSPLUS_I_ATTR_DIRTY); 256 245 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); 257 246 258 247 return 0; ··· 305 292 static int __hfsplus_delete_attr(struct inode *inode, u32 cnid, 306 293 struct hfs_find_data *fd) 307 294 { 308 - int err = 0; 295 + int err; 309 296 __be32 found_cnid, record_type; 310 297 298 + found_cnid = U32_MAX; 311 299 hfs_bnode_read(fd->bnode, &found_cnid, 312 300 fd->keyoffset + 313 301 offsetof(struct hfsplus_attr_key, cnid), 314 302 sizeof(__be32)); 315 303 if (cnid != be32_to_cpu(found_cnid)) 316 - return -ENOENT; 304 + return -ENODATA; 317 305 318 306 hfs_bnode_read(fd->bnode, &record_type, 319 307 fd->entryoffset, sizeof(record_type)); ··· 340 326 if (err) 341 327 return err; 342 328 329 + hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(inode->i_sb), 330 + HFSPLUS_I_ATTR_DIRTY); 343 331 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); 344 - return err; 332 + return 0; 345 333 } 346 334 347 335 static ··· 367 351 } 368 352 369 353 err = hfs_brec_find(fd, hfs_find_rec_by_key); 370 - if (err) 354 + if (err == -ENOENT) { 355 + /* file exists but xattr is absent */ 356 + return -ENODATA; 357 + } else if (err) 371 358 return err; 372 359 373 360 err = __hfsplus_delete_attr(inode, inode->i_ino, fd); ··· 430 411 431 412 for (;;) { 432 413 err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd); 433 - if (err) { 434 - if (err != -ENOENT) 435 - pr_err("xattr search failed\n"); 414 + if (err == -ENOENT || err == -ENODATA) { 415 + /* 416 + * xattr has not been found 417 + */ 418 + err = -ENODATA; 419 + goto end_delete_all; 420 + } else if (err) { 421 + pr_err("xattr search failed\n"); 436 422 goto end_delete_all; 437 423 } 438 424
+51
fs/hfsplus/bfind.c
··· 287 287 fd->bnode = bnode; 288 288 return res; 289 289 } 290 + 291 + /** 292 + * hfsplus_brec_read_cat - read and validate a catalog record 293 + * @fd: find data structure 294 + * @entry: pointer to catalog entry to read into 295 + * 296 + * Reads a catalog record and validates its size matches the expected 297 + * size based on the record type. 298 + * 299 + * Returns 0 on success, or negative error code on failure. 300 + */ 301 + int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry) 302 + { 303 + int res; 304 + u32 expected_size; 305 + 306 + res = hfs_brec_read(fd, entry, sizeof(hfsplus_cat_entry)); 307 + if (res) 308 + return res; 309 + 310 + /* Validate catalog record size based on type */ 311 + switch (be16_to_cpu(entry->type)) { 312 + case HFSPLUS_FOLDER: 313 + expected_size = sizeof(struct hfsplus_cat_folder); 314 + break; 315 + case HFSPLUS_FILE: 316 + expected_size = sizeof(struct hfsplus_cat_file); 317 + break; 318 + case HFSPLUS_FOLDER_THREAD: 319 + case HFSPLUS_FILE_THREAD: 320 + /* Ensure we have at least the fixed fields before reading nodeName.length */ 321 + if (fd->entrylength < HFSPLUS_MIN_THREAD_SZ) { 322 + pr_err("thread record too short (got %u)\n", fd->entrylength); 323 + return -EIO; 324 + } 325 + expected_size = hfsplus_cat_thread_size(&entry->thread); 326 + break; 327 + default: 328 + pr_err("unknown catalog record type %d\n", 329 + be16_to_cpu(entry->type)); 330 + return -EIO; 331 + } 332 + 333 + if (fd->entrylength != expected_size) { 334 + pr_err("catalog record size mismatch (type %d, got %u, expected %u)\n", 335 + be16_to_cpu(entry->type), fd->entrylength, expected_size); 336 + return -EIO; 337 + } 338 + 339 + return 0; 340 + }
+3
fs/hfsplus/bnode.c
··· 420 420 tree->root = 0; 421 421 tree->depth = 0; 422 422 } 423 + 424 + spin_lock(&tree->hash_lock); 423 425 set_bit(HFS_BNODE_DELETED, &node->flags); 426 + spin_unlock(&tree->hash_lock); 424 427 } 425 428 426 429 static inline int hfs_bnode_hash(u32 num)
+20 -12
fs/hfsplus/brec.c
··· 239 239 struct hfs_bnode_desc node_desc; 240 240 int num_recs, new_rec_off, new_off, old_rec_off; 241 241 int data_start, data_end, size; 242 + size_t rec_off_tbl_size; 243 + size_t node_desc_size = sizeof(struct hfs_bnode_desc); 244 + size_t rec_size = sizeof(__be16); 242 245 243 246 tree = fd->tree; 244 247 node = fd->bnode; ··· 268 265 return next_node; 269 266 } 270 267 271 - size = tree->node_size / 2 - node->num_recs * 2 - 14; 272 - old_rec_off = tree->node_size - 4; 268 + rec_off_tbl_size = node->num_recs * rec_size; 269 + size = tree->node_size / 2; 270 + size -= node_desc_size; 271 + size -= rec_off_tbl_size; 272 + old_rec_off = tree->node_size - (2 * rec_size); 273 + 273 274 num_recs = 1; 274 275 for (;;) { 275 276 data_start = hfs_bnode_read_u16(node, old_rec_off); 276 277 if (data_start > size) 277 278 break; 278 - old_rec_off -= 2; 279 + old_rec_off -= rec_size; 279 280 if (++num_recs < node->num_recs) 280 281 continue; 281 - /* panic? */ 282 282 hfs_bnode_put(node); 283 + hfs_bnode_unlink(new_node); 283 284 hfs_bnode_put(new_node); 284 285 if (next_node) 285 286 hfs_bnode_put(next_node); ··· 294 287 /* new record is in the lower half, 295 288 * so leave some more space there 296 289 */ 297 - old_rec_off += 2; 290 + old_rec_off += rec_size; 298 291 num_recs--; 299 292 data_start = hfs_bnode_read_u16(node, old_rec_off); 300 293 } else { ··· 302 295 hfs_bnode_get(new_node); 303 296 fd->bnode = new_node; 304 297 fd->record -= num_recs; 305 - fd->keyoffset -= data_start - 14; 306 - fd->entryoffset -= data_start - 14; 298 + fd->keyoffset -= data_start - node_desc_size; 299 + fd->entryoffset -= data_start - node_desc_size; 307 300 } 308 301 new_node->num_recs = node->num_recs - num_recs; 309 302 node->num_recs = num_recs; 310 303 311 - new_rec_off = tree->node_size - 2; 312 - new_off = 14; 304 + new_rec_off = tree->node_size - rec_size; 305 + new_off = node_desc_size; 313 306 size = data_start - new_off; 314 307 num_recs = new_node->num_recs; 315 308 data_end = data_start; 316 309 while (num_recs) { 317 310 hfs_bnode_write_u16(new_node, new_rec_off, new_off); 318 - old_rec_off -= 2; 319 - new_rec_off -= 2; 311 + old_rec_off -= rec_size; 312 + new_rec_off -= rec_size; 320 313 data_end = hfs_bnode_read_u16(node, old_rec_off); 321 314 new_off = data_end - size; 322 315 num_recs--; 323 316 } 324 317 hfs_bnode_write_u16(new_node, new_rec_off, new_off); 325 - hfs_bnode_copy(new_node, 14, node, data_start, data_end - data_start); 318 + hfs_bnode_copy(new_node, node_desc_size, 319 + node, data_start, data_end - data_start); 326 320 327 321 /* update new bnode header */ 328 322 node_desc.next = cpu_to_be32(new_node->next);
+194 -47
fs/hfsplus/btree.c
··· 129 129 return clump_size; 130 130 } 131 131 132 + /* Context for iterating b-tree map pages 133 + * @page_idx: The index of the page within the b-node's page array 134 + * @off: The byte offset within the mapped page 135 + * @len: The remaining length of the map record 136 + */ 137 + struct hfs_bmap_ctx { 138 + unsigned int page_idx; 139 + unsigned int off; 140 + u16 len; 141 + }; 142 + 143 + /* 144 + * Finds the specific page containing the requested byte offset within the map 145 + * record. Automatically handles the difference between header and map nodes. 146 + * Returns the struct page pointer, or an ERR_PTR on failure. 147 + * Note: The caller is responsible for mapping/unmapping the returned page. 148 + */ 149 + static struct page *hfs_bmap_get_map_page(struct hfs_bnode *node, 150 + struct hfs_bmap_ctx *ctx, 151 + u32 byte_offset) 152 + { 153 + u16 rec_idx, off16; 154 + unsigned int page_off; 155 + 156 + if (node->this == HFSPLUS_TREE_HEAD) { 157 + if (node->type != HFS_NODE_HEADER) { 158 + pr_err("hfsplus: invalid btree header node\n"); 159 + return ERR_PTR(-EIO); 160 + } 161 + rec_idx = HFSPLUS_BTREE_HDR_MAP_REC_INDEX; 162 + } else { 163 + if (node->type != HFS_NODE_MAP) { 164 + pr_err("hfsplus: invalid btree map node\n"); 165 + return ERR_PTR(-EIO); 166 + } 167 + rec_idx = HFSPLUS_BTREE_MAP_NODE_REC_INDEX; 168 + } 169 + 170 + ctx->len = hfs_brec_lenoff(node, rec_idx, &off16); 171 + if (!ctx->len) 172 + return ERR_PTR(-ENOENT); 173 + 174 + if (!is_bnode_offset_valid(node, off16)) 175 + return ERR_PTR(-EIO); 176 + 177 + ctx->len = check_and_correct_requested_length(node, off16, ctx->len); 178 + 179 + if (byte_offset >= ctx->len) 180 + return ERR_PTR(-EINVAL); 181 + 182 + page_off = (u32)off16 + node->page_offset + byte_offset; 183 + ctx->page_idx = page_off >> PAGE_SHIFT; 184 + ctx->off = page_off & ~PAGE_MASK; 185 + 186 + return node->page[ctx->page_idx]; 187 + } 188 + 189 + /** 190 + * hfs_bmap_test_bit - test a bit in the b-tree map 191 + * @node: the b-tree node containing the map record 192 + * @node_bit_idx: the relative bit index within the node's map record 193 + * 194 + * Returns true if set, false if clear or on failure. 195 + */ 196 + static bool hfs_bmap_test_bit(struct hfs_bnode *node, u32 node_bit_idx) 197 + { 198 + struct hfs_bmap_ctx ctx; 199 + struct page *page; 200 + u8 *bmap, byte, mask; 201 + 202 + page = hfs_bmap_get_map_page(node, &ctx, node_bit_idx / BITS_PER_BYTE); 203 + if (IS_ERR(page)) 204 + return false; 205 + 206 + bmap = kmap_local_page(page); 207 + byte = bmap[ctx.off]; 208 + kunmap_local(bmap); 209 + 210 + mask = 1 << (7 - (node_bit_idx % BITS_PER_BYTE)); 211 + return (byte & mask) != 0; 212 + } 213 + 214 + 215 + /** 216 + * hfs_bmap_clear_bit - clear a bit in the b-tree map 217 + * @node: the b-tree node containing the map record 218 + * @node_bit_idx: the relative bit index within the node's map record 219 + * 220 + * Returns 0 on success, -EINVAL if already clear, or negative error code. 221 + */ 222 + static int hfs_bmap_clear_bit(struct hfs_bnode *node, u32 node_bit_idx) 223 + { 224 + struct hfs_bmap_ctx ctx; 225 + struct page *page; 226 + u8 *bmap, mask; 227 + 228 + page = hfs_bmap_get_map_page(node, &ctx, node_bit_idx / BITS_PER_BYTE); 229 + if (IS_ERR(page)) 230 + return PTR_ERR(page); 231 + 232 + bmap = kmap_local_page(page); 233 + 234 + mask = 1 << (7 - (node_bit_idx % BITS_PER_BYTE)); 235 + 236 + if (!(bmap[ctx.off] & mask)) { 237 + kunmap_local(bmap); 238 + return -EINVAL; 239 + } 240 + 241 + bmap[ctx.off] &= ~mask; 242 + set_page_dirty(page); 243 + kunmap_local(bmap); 244 + 245 + return 0; 246 + } 247 + 248 + #define HFS_EXTENT_TREE_NAME "Extents Overflow File" 249 + #define HFS_CATALOG_TREE_NAME "Catalog File" 250 + #define HFS_ATTR_TREE_NAME "Attributes File" 251 + #define HFS_UNKNOWN_TREE_NAME "Unknown B-tree" 252 + 253 + static const char *hfs_btree_name(u32 cnid) 254 + { 255 + switch (cnid) { 256 + case HFSPLUS_EXT_CNID: 257 + return HFS_EXTENT_TREE_NAME; 258 + case HFSPLUS_CAT_CNID: 259 + return HFS_CATALOG_TREE_NAME; 260 + case HFSPLUS_ATTR_CNID: 261 + return HFS_ATTR_TREE_NAME; 262 + default: 263 + return HFS_UNKNOWN_TREE_NAME; 264 + } 265 + } 266 + 132 267 /* Get a reference to a B*Tree and do some initial checks */ 133 268 struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) 134 269 { 135 270 struct hfs_btree *tree; 136 271 struct hfs_btree_header_rec *head; 137 272 struct address_space *mapping; 273 + struct hfs_bnode *node; 138 274 struct inode *inode; 139 275 struct page *page; 140 276 unsigned int size; ··· 378 242 379 243 kunmap_local(head); 380 244 put_page(page); 245 + 246 + node = hfs_bnode_find(tree, HFSPLUS_TREE_HEAD); 247 + if (IS_ERR(node)) 248 + goto free_inode; 249 + 250 + if (!hfs_bmap_test_bit(node, 0)) { 251 + pr_warn("(%s): %s (cnid 0x%x) map record invalid or bitmap corruption detected, forcing read-only.\n", 252 + sb->s_id, hfs_btree_name(id), id); 253 + pr_warn("Run fsck.hfsplus to repair.\n"); 254 + sb->s_flags |= SB_RDONLY; 255 + } 256 + 257 + hfs_bnode_put(node); 258 + 381 259 return tree; 382 260 383 261 fail_page: ··· 501 351 u32 count; 502 352 int res; 503 353 354 + lockdep_assert_held(&tree->tree_lock); 355 + 504 356 if (rsvd_nodes <= 0) 505 357 return 0; 506 358 ··· 526 374 struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) 527 375 { 528 376 struct hfs_bnode *node, *next_node; 529 - struct page **pagep; 377 + struct hfs_bmap_ctx ctx; 378 + struct page *page; 530 379 u32 nidx, idx; 531 - unsigned off; 532 - u16 off16; 533 - u16 len; 534 380 u8 *data, byte, m; 535 381 int i, res; 382 + 383 + lockdep_assert_held(&tree->tree_lock); 536 384 537 385 res = hfs_bmap_reserve(tree, 1); 538 386 if (res) ··· 542 390 node = hfs_bnode_find(tree, nidx); 543 391 if (IS_ERR(node)) 544 392 return node; 545 - len = hfs_brec_lenoff(node, 2, &off16); 546 - off = off16; 547 393 548 - if (!is_bnode_offset_valid(node, off)) { 394 + page = hfs_bmap_get_map_page(node, &ctx, 0); 395 + if (IS_ERR(page)) { 396 + res = PTR_ERR(page); 549 397 hfs_bnode_put(node); 550 - return ERR_PTR(-EIO); 398 + return ERR_PTR(res); 551 399 } 552 - len = check_and_correct_requested_length(node, off, len); 553 400 554 - off += node->page_offset; 555 - pagep = node->page + (off >> PAGE_SHIFT); 556 - data = kmap_local_page(*pagep); 557 - off &= ~PAGE_MASK; 401 + data = kmap_local_page(page); 558 402 idx = 0; 559 403 560 404 for (;;) { 561 - while (len) { 562 - byte = data[off]; 405 + while (ctx.len) { 406 + byte = data[ctx.off]; 563 407 if (byte != 0xff) { 564 408 for (m = 0x80, i = 0; i < 8; m >>= 1, i++) { 565 409 if (!(byte & m)) { 566 410 idx += i; 567 - data[off] |= m; 568 - set_page_dirty(*pagep); 411 + data[ctx.off] |= m; 412 + set_page_dirty(page); 569 413 kunmap_local(data); 570 414 tree->free_nodes--; 415 + hfs_btree_write(tree); 571 416 mark_inode_dirty(tree->inode); 572 417 hfs_bnode_put(node); 573 418 return hfs_bnode_create(tree, ··· 572 423 } 573 424 } 574 425 } 575 - if (++off >= PAGE_SIZE) { 426 + if (++ctx.off >= PAGE_SIZE) { 576 427 kunmap_local(data); 577 - data = kmap_local_page(*++pagep); 578 - off = 0; 428 + page = node->page[++ctx.page_idx]; 429 + data = kmap_local_page(page); 430 + ctx.off = 0; 579 431 } 580 432 idx += 8; 581 - len--; 433 + ctx.len--; 582 434 } 583 435 kunmap_local(data); 584 436 nidx = node->next; 585 437 if (!nidx) { 586 438 hfs_dbg("create new bmap node\n"); 587 439 next_node = hfs_bmap_new_bmap(node, idx); 440 + hfs_btree_write(tree); 588 441 } else 589 442 next_node = hfs_bnode_find(tree, nidx); 590 443 hfs_bnode_put(node); ··· 594 443 return next_node; 595 444 node = next_node; 596 445 597 - len = hfs_brec_lenoff(node, 0, &off16); 598 - off = off16; 599 - off += node->page_offset; 600 - pagep = node->page + (off >> PAGE_SHIFT); 601 - data = kmap_local_page(*pagep); 602 - off &= ~PAGE_MASK; 446 + page = hfs_bmap_get_map_page(node, &ctx, 0); 447 + if (IS_ERR(page)) { 448 + res = PTR_ERR(page); 449 + hfs_bnode_put(node); 450 + return ERR_PTR(res); 451 + } 452 + data = kmap_local_page(page); 603 453 } 604 454 } 605 455 606 456 void hfs_bmap_free(struct hfs_bnode *node) 607 457 { 608 458 struct hfs_btree *tree; 609 - struct page *page; 610 459 u16 off, len; 611 460 u32 nidx; 612 - u8 *data, byte, m; 461 + int res; 613 462 614 463 hfs_dbg("node %u\n", node->this); 615 464 BUG_ON(!node->this); 616 465 tree = node->tree; 466 + lockdep_assert_held(&tree->tree_lock); 617 467 nidx = node->this; 618 468 node = hfs_bnode_find(tree, 0); 619 469 if (IS_ERR(node)) ··· 647 495 } 648 496 len = hfs_brec_lenoff(node, 0, &off); 649 497 } 650 - off += node->page_offset + nidx / 8; 651 - page = node->page[off >> PAGE_SHIFT]; 652 - data = kmap_local_page(page); 653 - off &= ~PAGE_MASK; 654 - m = 1 << (~nidx & 7); 655 - byte = data[off]; 656 - if (!(byte & m)) { 657 - pr_crit("trying to free free bnode " 658 - "%u(%d)\n", 659 - node->this, node->type); 660 - kunmap_local(data); 661 - hfs_bnode_put(node); 662 - return; 498 + 499 + res = hfs_bmap_clear_bit(node, nidx); 500 + if (res == -EINVAL) { 501 + pr_crit("trying to free the freed bnode %u(%d)\n", 502 + nidx, node->type); 503 + } else if (res) { 504 + pr_crit("fail to free bnode %u(%d)\n", 505 + nidx, node->type); 506 + } else { 507 + tree->free_nodes++; 508 + hfs_btree_write(tree); 509 + mark_inode_dirty(tree->inode); 663 510 } 664 - data[off] = byte & ~m; 665 - set_page_dirty(page); 666 - kunmap_local(data); 511 + 667 512 hfs_bnode_put(node); 668 - tree->free_nodes++; 669 - mark_inode_dirty(tree->inode); 670 513 }
+7 -4
fs/hfsplus/catalog.c
··· 47 47 48 48 key->cat.parent = cpu_to_be32(parent); 49 49 err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN, 50 - str->name, str->len); 50 + str->name, str->len, HFS_REGULAR_NAME); 51 51 if (unlikely(err < 0)) 52 52 return err; 53 53 ··· 183 183 entry->thread.reserved = 0; 184 184 entry->thread.parentID = cpu_to_be32(parentid); 185 185 err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN, 186 - str->name, str->len); 186 + str->name, str->len, HFS_REGULAR_NAME); 187 187 if (unlikely(err < 0)) 188 188 return err; 189 189 ··· 194 194 int hfsplus_find_cat(struct super_block *sb, u32 cnid, 195 195 struct hfs_find_data *fd) 196 196 { 197 - hfsplus_cat_entry tmp; 197 + hfsplus_cat_entry tmp = {0}; 198 198 int err; 199 199 u16 type; 200 200 201 201 hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid); 202 - err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry)); 202 + err = hfsplus_brec_read_cat(fd, &tmp); 203 203 if (err) 204 204 return err; 205 205 ··· 313 313 if (S_ISDIR(inode->i_mode)) 314 314 hfsplus_subfolders_inc(dir); 315 315 inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); 316 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), HFSPLUS_I_CAT_DIRTY); 316 317 hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); 317 318 318 319 hfs_find_exit(&fd); ··· 419 418 if (type == HFSPLUS_FOLDER) 420 419 hfsplus_subfolders_dec(dir); 421 420 inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); 421 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), HFSPLUS_I_CAT_DIRTY); 422 422 hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); 423 423 424 424 if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) { ··· 542 540 } 543 541 err = hfs_brec_insert(&dst_fd, &entry, entry_size); 544 542 543 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), HFSPLUS_I_CAT_DIRTY); 545 544 hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY); 546 545 hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY); 547 546 out:
+20 -3
fs/hfsplus/dir.c
··· 49 49 if (unlikely(err < 0)) 50 50 goto fail; 51 51 again: 52 - err = hfs_brec_read(&fd, &entry, sizeof(entry)); 52 + err = hfsplus_brec_read_cat(&fd, &entry); 53 53 if (err) { 54 54 if (err == -ENOENT) { 55 55 hfs_find_exit(&fd); ··· 478 478 if (!inode) 479 479 goto out; 480 480 481 + hfs_dbg("dir->i_ino %llu, inode->i_ino %llu\n", 482 + dir->i_ino, inode->i_ino); 483 + 481 484 res = page_symlink(inode, symname, strlen(symname) + 1); 482 485 if (res) 483 486 goto out_err; ··· 528 525 inode = hfsplus_new_inode(dir->i_sb, dir, mode); 529 526 if (!inode) 530 527 goto out; 528 + 529 + hfs_dbg("dir->i_ino %llu, inode->i_ino %llu\n", 530 + dir->i_ino, inode->i_ino); 531 531 532 532 if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) 533 533 init_special_inode(inode, mode, rdev); ··· 603 597 old_dir, &old_dentry->d_name, 604 598 new_dir, &new_dentry->d_name); 605 599 if (!res) { 600 + struct inode *inode = d_inode(old_dentry); 601 + 606 602 new_dentry->d_fsdata = old_dentry->d_fsdata; 607 603 604 + inode_set_ctime_current(inode); 605 + mark_inode_dirty(inode); 606 + 608 607 res = hfsplus_cat_write_inode(old_dir); 609 - if (!res) 610 - res = hfsplus_cat_write_inode(new_dir); 608 + if (res) 609 + return res; 610 + 611 + res = hfsplus_cat_write_inode(new_dir); 612 + if (res) 613 + return res; 614 + 615 + res = hfsplus_cat_write_inode(inode); 611 616 } 612 617 return res; 613 618 }
+7
fs/hfsplus/extents.c
··· 121 121 * redirty the inode. Instead the callers have to be careful 122 122 * to explicily mark the inode dirty, too. 123 123 */ 124 + set_bit(HFSPLUS_I_EXT_DIRTY, 125 + &HFSPLUS_I(HFSPLUS_EXT_TREE_I(inode->i_sb))->flags); 124 126 set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags); 125 127 126 128 return 0; ··· 515 513 if (!res) { 516 514 hip->alloc_blocks += len; 517 515 mutex_unlock(&hip->extents_lock); 516 + hfsplus_mark_inode_dirty(HFSPLUS_SB(sb)->alloc_file, 517 + HFSPLUS_I_ALLOC_DIRTY); 518 518 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); 519 519 return 0; 520 520 } ··· 586 582 /* XXX: We lack error handling of hfsplus_file_truncate() */ 587 583 return; 588 584 } 585 + 589 586 while (1) { 590 587 if (alloc_cnt == hip->first_blocks) { 591 588 mutex_unlock(&fd.tree->tree_lock); ··· 628 623 hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> 629 624 sb->s_blocksize_bits; 630 625 inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); 626 + hfsplus_mark_inode_dirty(HFSPLUS_SB(sb)->alloc_file, 627 + HFSPLUS_I_ALLOC_DIRTY); 631 628 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); 632 629 }
+24 -2
fs/hfsplus/hfsplus_fs.h
··· 238 238 return container_of(inode, struct hfsplus_inode_info, vfs_inode); 239 239 } 240 240 241 + #define HFSPLUS_CAT_TREE_I(sb) \ 242 + HFSPLUS_SB(sb)->cat_tree->inode 243 + #define HFSPLUS_EXT_TREE_I(sb) \ 244 + HFSPLUS_SB(sb)->ext_tree->inode 245 + #define HFSPLUS_ATTR_TREE_I(sb) \ 246 + HFSPLUS_SB(sb)->attr_tree->inode 247 + 241 248 /* 242 249 * Mark an inode dirty, and also mark the btree in which the 243 250 * specific type of metadata is stored. ··· 506 499 const struct hfsplus_attr_unistr *ustr, 507 500 char *astr, int *len_p); 508 501 int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, 509 - int max_unistr_len, const char *astr, int len); 502 + int max_unistr_len, const char *astr, int len, 503 + int name_type); 510 504 int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str); 511 505 int hfsplus_compare_dentry(const struct dentry *dentry, unsigned int len, 512 506 const char *str, const struct qstr *name); ··· 516 508 int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf, 517 509 void **data, blk_opf_t opf); 518 510 int hfsplus_read_wrapper(struct super_block *sb); 511 + 512 + static inline u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread) 513 + { 514 + return offsetof(struct hfsplus_cat_thread, nodeName) + 515 + offsetof(struct hfsplus_unistr, unicode) + 516 + be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr); 517 + } 518 + 519 + int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry); 519 520 520 521 /* 521 522 * time helpers: convert between 1904-base and 1970-base timestamps ··· 572 555 static inline 573 556 bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) 574 557 { 575 - bool is_valid = off < node->tree->node_size; 558 + bool is_valid; 559 + 560 + if (!node || !node->tree) 561 + return false; 562 + 563 + is_valid = off < node->tree->node_size; 576 564 577 565 if (!is_valid) { 578 566 pr_err("requested invalid offset: "
+27 -15
fs/hfsplus/inode.c
··· 324 324 { 325 325 struct inode *inode = file->f_mapping->host; 326 326 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 327 + struct super_block *sb = inode->i_sb; 327 328 struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 328 329 struct hfsplus_vh *vhdr = sbi->s_vhdr; 329 330 int error = 0, error2; ··· 345 344 /* 346 345 * And explicitly write out the btrees. 347 346 */ 348 - if (test_and_clear_bit(HFSPLUS_I_CAT_DIRTY, &hip->flags)) 347 + if (test_and_clear_bit(HFSPLUS_I_CAT_DIRTY, 348 + &HFSPLUS_I(HFSPLUS_CAT_TREE_I(sb))->flags)) { 349 + clear_bit(HFSPLUS_I_CAT_DIRTY, &hip->flags); 349 350 error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); 351 + } 350 352 351 - if (test_and_clear_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags)) { 353 + if (test_and_clear_bit(HFSPLUS_I_EXT_DIRTY, 354 + &HFSPLUS_I(HFSPLUS_EXT_TREE_I(sb))->flags)) { 355 + clear_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags); 352 356 error2 = 353 357 filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); 354 358 if (!error) 355 359 error = error2; 356 360 } 357 361 358 - if (test_and_clear_bit(HFSPLUS_I_ATTR_DIRTY, &hip->flags)) { 359 - if (sbi->attr_tree) { 362 + if (sbi->attr_tree) { 363 + if (test_and_clear_bit(HFSPLUS_I_ATTR_DIRTY, 364 + &HFSPLUS_I(HFSPLUS_ATTR_TREE_I(sb))->flags)) { 365 + clear_bit(HFSPLUS_I_ATTR_DIRTY, &hip->flags); 360 366 error2 = 361 367 filemap_write_and_wait( 362 368 sbi->attr_tree->inode->i_mapping); 363 369 if (!error) 364 370 error = error2; 365 - } else { 366 - pr_err("sync non-existent attributes tree\n"); 367 371 } 372 + } else { 373 + if (test_and_clear_bit(HFSPLUS_I_ATTR_DIRTY, &hip->flags)) 374 + pr_err("sync non-existent attributes tree\n"); 368 375 } 369 376 370 - if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags)) { 377 + if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, 378 + &HFSPLUS_I(sbi->alloc_file)->flags)) { 379 + clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags); 371 380 error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); 372 381 if (!error) 373 382 error = error2; ··· 720 709 sizeof(struct hfsplus_cat_file)); 721 710 } 722 711 712 + res = hfs_btree_write(tree); 713 + if (res) { 714 + pr_err("b-tree write err: %d, ino %llu\n", 715 + res, inode->i_ino); 716 + goto out; 717 + } 718 + 719 + set_bit(HFSPLUS_I_CAT_DIRTY, 720 + &HFSPLUS_I(HFSPLUS_CAT_TREE_I(inode->i_sb))->flags); 723 721 set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags); 724 722 out: 725 723 hfs_find_exit(&fd); 726 - 727 - if (!res) { 728 - res = hfs_btree_write(tree); 729 - if (res) { 730 - pr_err("b-tree write err: %d, ino %llu\n", 731 - res, inode->i_ino); 732 - } 733 - } 734 724 735 725 return res; 736 726 }
+37 -10
fs/hfsplus/super.c
··· 153 153 } 154 154 hfsplus_inode_write_fork(inode, fork); 155 155 if (tree) { 156 + mutex_lock_nested(&tree->tree_lock, 157 + hfsplus_btree_lock_class(tree)); 156 158 int err = hfs_btree_write(tree); 159 + mutex_unlock(&tree->tree_lock); 157 160 158 161 if (err) { 159 162 pr_err("b-tree write err: %d, ino %llu\n", ··· 427 424 vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); 428 425 } 429 426 427 + static inline int hfsplus_get_hidden_dir_entry(struct super_block *sb, 428 + const struct qstr *str, 429 + hfsplus_cat_entry *entry) 430 + { 431 + struct hfs_find_data fd; 432 + int err; 433 + 434 + err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); 435 + if (unlikely(err)) 436 + return err; 437 + 438 + err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, str); 439 + if (unlikely(err)) 440 + goto free_fd; 441 + 442 + err = hfsplus_brec_read_cat(&fd, entry); 443 + if (err) 444 + err = -ENOENT; 445 + 446 + free_fd: 447 + hfs_find_exit(&fd); 448 + return err; 449 + } 450 + 430 451 static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) 431 452 { 432 453 struct hfsplus_vh *vhdr; 433 454 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 434 455 hfsplus_cat_entry entry; 435 - struct hfs_find_data fd; 436 456 struct inode *root, *inode; 437 457 struct qstr str; 438 458 struct nls_table *nls; ··· 591 565 592 566 str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; 593 567 str.name = HFSP_HIDDENDIR_NAME; 594 - err = hfs_find_init(sbi->cat_tree, &fd); 595 - if (err) 568 + err = hfsplus_get_hidden_dir_entry(sb, &str, &entry); 569 + if (err == -ENOENT) { 570 + /* 571 + * Hidden directory is absent or it cannot be read. 572 + */ 573 + } else if (unlikely(err)) { 596 574 goto out_put_root; 597 - err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); 598 - if (unlikely(err < 0)) 599 - goto out_put_root; 600 - if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { 601 - hfs_find_exit(&fd); 575 + } else { 602 576 if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) { 603 577 err = -EIO; 604 578 goto out_put_root; ··· 609 583 goto out_put_root; 610 584 } 611 585 sbi->hidden_dir = inode; 612 - } else 613 - hfs_find_exit(&fd); 586 + } 614 587 615 588 if (!sb_rdonly(sb)) { 616 589 /* ··· 650 625 } 651 626 652 627 mutex_unlock(&sbi->vh_mutex); 628 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), 629 + HFSPLUS_I_CAT_DIRTY); 653 630 hfsplus_mark_inode_dirty(sbi->hidden_dir, 654 631 HFSPLUS_I_CAT_DIRTY); 655 632 }
+87 -34
fs/hfsplus/unicode.c
··· 147 147 return NULL; 148 148 } 149 149 150 + /* 151 + * In HFS+, a filename can contain / because : is the separator. 152 + * The slash is a valid filename character on macOS. 153 + * But on Linux, / is the path separator and 154 + * it cannot appear in a filename component. 155 + * There's a parallel mapping for the NUL character (0 -> U+2400). 156 + * NUL terminates strings in C/POSIX but is valid in HFS+ filenames. 157 + */ 158 + static inline 159 + void hfsplus_mac2linux_compatibility_check(u16 symbol, u16 *conversion, 160 + int name_type) 161 + { 162 + *conversion = symbol; 163 + 164 + switch (name_type) { 165 + case HFS_XATTR_NAME: 166 + /* ignore conversion */ 167 + return; 168 + 169 + default: 170 + /* continue logic */ 171 + break; 172 + } 173 + 174 + switch (symbol) { 175 + case 0: 176 + *conversion = 0x2400; 177 + break; 178 + case '/': 179 + *conversion = ':'; 180 + break; 181 + } 182 + } 183 + 150 184 static int hfsplus_uni2asc(struct super_block *sb, 151 185 const struct hfsplus_unistr *ustr, 152 - int max_len, char *astr, int *len_p) 186 + int max_len, char *astr, int *len_p, 187 + int name_type) 153 188 { 154 189 const hfsplus_unichr *ip; 155 190 struct nls_table *nls = HFSPLUS_SB(sb)->nls; ··· 252 217 hfsplus_compose_table, c1); 253 218 if (ce1) 254 219 break; 255 - switch (c0) { 256 - case 0: 257 - c0 = 0x2400; 258 - break; 259 - case '/': 260 - c0 = ':'; 261 - break; 262 - } 220 + hfsplus_mac2linux_compatibility_check(c0, &c0, 221 + name_type); 263 222 res = nls->uni2char(c0, op, len); 264 223 if (res < 0) { 265 224 if (res == -ENAMETOOLONG) ··· 286 257 } 287 258 } 288 259 same: 289 - switch (c0) { 290 - case 0: 291 - cc = 0x2400; 292 - break; 293 - case '/': 294 - cc = ':'; 295 - break; 296 - default: 297 - cc = c0; 298 - } 260 + hfsplus_mac2linux_compatibility_check(c0, &cc, 261 + name_type); 299 262 done: 300 263 res = nls->uni2char(cc, op, len); 301 264 if (res < 0) { ··· 309 288 const struct hfsplus_unistr *ustr, char *astr, 310 289 int *len_p) 311 290 { 312 - return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p); 291 + return hfsplus_uni2asc(sb, 292 + ustr, HFSPLUS_MAX_STRLEN, 293 + astr, len_p, 294 + HFS_REGULAR_NAME); 313 295 } 314 296 EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_str); 315 297 ··· 321 297 char *astr, int *len_p) 322 298 { 323 299 return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr, 324 - HFSPLUS_ATTR_MAX_STRLEN, astr, len_p); 300 + HFSPLUS_ATTR_MAX_STRLEN, astr, len_p, 301 + HFS_XATTR_NAME); 325 302 } 326 303 EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_xattr_str); 327 304 328 305 /* 329 - * Convert one or more ASCII characters into a single unicode character. 330 - * Returns the number of ASCII characters corresponding to the unicode char. 306 + * In HFS+, a filename can contain / because : is the separator. 307 + * The slash is a valid filename character on macOS. 308 + * But on Linux, / is the path separator and 309 + * it cannot appear in a filename component. 310 + * There's a parallel mapping for the NUL character (0 -> U+2400). 311 + * NUL terminates strings in C/POSIX but is valid in HFS+ filenames. 331 312 */ 332 - static inline int asc2unichar(struct super_block *sb, const char *astr, int len, 333 - wchar_t *uc) 313 + static inline 314 + void hfsplus_linux2mac_compatibility_check(wchar_t *uc, int name_type) 334 315 { 335 - int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc); 336 - if (size <= 0) { 337 - *uc = '?'; 338 - size = 1; 316 + switch (name_type) { 317 + case HFS_XATTR_NAME: 318 + /* ignore conversion */ 319 + return; 320 + 321 + default: 322 + /* continue logic */ 323 + break; 339 324 } 325 + 340 326 switch (*uc) { 341 327 case 0x2400: 342 328 *uc = 0; ··· 355 321 *uc = '/'; 356 322 break; 357 323 } 324 + } 325 + 326 + /* 327 + * Convert one or more ASCII characters into a single unicode character. 328 + * Returns the number of ASCII characters corresponding to the unicode char. 329 + */ 330 + static inline int asc2unichar(struct super_block *sb, const char *astr, int len, 331 + wchar_t *uc, int name_type) 332 + { 333 + int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc); 334 + 335 + if (size <= 0) { 336 + *uc = '?'; 337 + size = 1; 338 + } 339 + 340 + hfsplus_linux2mac_compatibility_check(uc, name_type); 358 341 return size; 359 342 } 360 343 ··· 446 395 447 396 int hfsplus_asc2uni(struct super_block *sb, 448 397 struct hfsplus_unistr *ustr, int max_unistr_len, 449 - const char *astr, int len) 398 + const char *astr, int len, int name_type) 450 399 { 451 400 int size, dsize, decompose; 452 401 u16 *dstr, outlen = 0; ··· 455 404 456 405 decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); 457 406 while (outlen < max_unistr_len && len > 0) { 458 - size = asc2unichar(sb, astr, len, &c); 407 + size = asc2unichar(sb, astr, len, &c, name_type); 459 408 460 409 if (decompose) 461 410 dstr = decompose_unichar(c, &dsize, dhangul); ··· 503 452 len = str->len; 504 453 while (len > 0) { 505 454 int dsize; 506 - size = asc2unichar(sb, astr, len, &c); 455 + size = asc2unichar(sb, astr, len, &c, HFS_REGULAR_NAME); 507 456 astr += size; 508 457 len -= size; 509 458 ··· 561 510 562 511 while (len1 > 0 && len2 > 0) { 563 512 if (!dsize1) { 564 - size = asc2unichar(sb, astr1, len1, &c); 513 + size = asc2unichar(sb, astr1, len1, &c, 514 + HFS_REGULAR_NAME); 565 515 astr1 += size; 566 516 len1 -= size; 567 517 ··· 577 525 } 578 526 579 527 if (!dsize2) { 580 - size = asc2unichar(sb, astr2, len2, &c); 528 + size = asc2unichar(sb, astr2, len2, &c, 529 + HFS_REGULAR_NAME); 581 530 astr2 += size; 582 531 len2 -= size; 583 532
+34 -17
fs/hfsplus/unicode_test.c
··· 715 715 716 716 /* Test simple ASCII string conversion */ 717 717 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 718 - HFSPLUS_MAX_STRLEN, "hello", 5); 718 + HFSPLUS_MAX_STRLEN, "hello", 5, 719 + HFS_REGULAR_NAME); 719 720 720 721 KUNIT_EXPECT_EQ(test, 0, result); 721 722 check_unistr_content(test, &mock_env->str1, "hello"); 722 723 723 724 /* Test empty string */ 724 725 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 725 - HFSPLUS_MAX_STRLEN, "", 0); 726 + HFSPLUS_MAX_STRLEN, "", 0, 727 + HFS_REGULAR_NAME); 726 728 727 729 KUNIT_EXPECT_EQ(test, 0, result); 728 730 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length)); 729 731 730 732 /* Test single character */ 731 733 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 732 - HFSPLUS_MAX_STRLEN, "A", 1); 734 + HFSPLUS_MAX_STRLEN, "A", 1, 735 + HFS_REGULAR_NAME); 733 736 734 737 KUNIT_EXPECT_EQ(test, 0, result); 735 738 check_unistr_content(test, &mock_env->str1, "A"); 736 739 737 740 /* Test null-terminated string with explicit length */ 738 741 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 739 - HFSPLUS_MAX_STRLEN, "test\0extra", 4); 742 + HFSPLUS_MAX_STRLEN, "test\0extra", 4, 743 + HFS_REGULAR_NAME); 740 744 741 745 KUNIT_EXPECT_EQ(test, 0, result); 742 746 check_unistr_content(test, &mock_env->str1, "test"); ··· 766 762 767 763 /* Test colon conversion (should become forward slash) */ 768 764 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 769 - HFSPLUS_MAX_STRLEN, ":", 1); 765 + HFSPLUS_MAX_STRLEN, ":", 1, 766 + HFS_REGULAR_NAME); 770 767 771 768 KUNIT_EXPECT_EQ(test, 0, result); 772 769 KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length)); ··· 775 770 776 771 /* Test string with mixed special characters */ 777 772 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 778 - HFSPLUS_MAX_STRLEN, "a:b", 3); 773 + HFSPLUS_MAX_STRLEN, "a:b", 3, 774 + HFS_REGULAR_NAME); 779 775 780 776 KUNIT_EXPECT_EQ(test, 0, result); 781 777 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length)); ··· 786 780 787 781 /* Test multiple special characters */ 788 782 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 789 - HFSPLUS_MAX_STRLEN, ":::", 3); 783 + HFSPLUS_MAX_STRLEN, ":::", 3, 784 + HFS_REGULAR_NAME); 790 785 791 786 KUNIT_EXPECT_EQ(test, 0, result); 792 787 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length)); ··· 818 811 memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 819 812 result = hfsplus_asc2uni(&mock_sb->sb, 820 813 &mock_env->str1, HFSPLUS_MAX_STRLEN, 821 - mock_env->buf, HFSPLUS_MAX_STRLEN); 814 + mock_env->buf, HFSPLUS_MAX_STRLEN, 815 + HFS_REGULAR_NAME); 822 816 823 817 KUNIT_EXPECT_EQ(test, 0, result); 824 818 KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, ··· 829 821 memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5); 830 822 result = hfsplus_asc2uni(&mock_sb->sb, 831 823 &mock_env->str1, HFSPLUS_MAX_STRLEN, 832 - mock_env->buf, HFSPLUS_MAX_STRLEN + 5); 824 + mock_env->buf, HFSPLUS_MAX_STRLEN + 5, 825 + HFS_REGULAR_NAME); 833 826 834 827 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 835 828 KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, ··· 838 829 839 830 /* Test with smaller max_unistr_len */ 840 831 result = hfsplus_asc2uni(&mock_sb->sb, 841 - &mock_env->str1, 5, "toolongstring", 13); 832 + &mock_env->str1, 5, "toolongstring", 13, 833 + HFS_REGULAR_NAME); 842 834 843 835 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 844 836 KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length)); 845 837 846 838 /* Test zero max length */ 847 - result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4); 839 + result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4, 840 + HFS_REGULAR_NAME); 848 841 849 842 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 850 843 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length)); ··· 870 859 871 860 /* Test zero length input */ 872 861 result = hfsplus_asc2uni(&mock_sb->sb, 873 - &ustr, HFSPLUS_MAX_STRLEN, "test", 0); 862 + &ustr, HFSPLUS_MAX_STRLEN, "test", 0, 863 + HFS_REGULAR_NAME); 874 864 875 865 KUNIT_EXPECT_EQ(test, 0, result); 876 866 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length)); 877 867 878 868 /* Test input with length mismatch */ 879 869 result = hfsplus_asc2uni(&mock_sb->sb, 880 - &ustr, HFSPLUS_MAX_STRLEN, "hello", 3); 870 + &ustr, HFSPLUS_MAX_STRLEN, "hello", 3, 871 + HFS_REGULAR_NAME); 881 872 882 873 KUNIT_EXPECT_EQ(test, 0, result); 883 874 check_unistr_content(test, &ustr, "hel"); 884 875 885 876 /* Test with various printable ASCII characters */ 886 877 result = hfsplus_asc2uni(&mock_sb->sb, 887 - &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9); 878 + &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9, 879 + HFS_REGULAR_NAME); 888 880 889 881 KUNIT_EXPECT_EQ(test, 0, result); 890 882 check_unistr_content(test, &ustr, "ABC123!@#"); 891 883 892 884 /* Test null character in the middle */ 893 885 result = hfsplus_asc2uni(&mock_sb->sb, 894 - &ustr, HFSPLUS_MAX_STRLEN, test_str, 3); 886 + &ustr, HFSPLUS_MAX_STRLEN, test_str, 3, 887 + HFS_REGULAR_NAME); 895 888 896 889 KUNIT_EXPECT_EQ(test, 0, result); 897 890 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length)); ··· 924 909 /* Test with decomposition disabled (default) */ 925 910 clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 926 911 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 927 - HFSPLUS_MAX_STRLEN, "test", 4); 912 + HFSPLUS_MAX_STRLEN, "test", 4, 913 + HFS_REGULAR_NAME); 928 914 929 915 KUNIT_EXPECT_EQ(test, 0, result); 930 916 check_unistr_content(test, &mock_env->str1, "test"); ··· 933 917 /* Test with decomposition enabled */ 934 918 set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 935 919 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2, 936 - HFSPLUS_MAX_STRLEN, "test", 4); 920 + HFSPLUS_MAX_STRLEN, "test", 4, 921 + HFS_REGULAR_NAME); 937 922 938 923 KUNIT_EXPECT_EQ(test, 0, result); 939 924 check_unistr_content(test, &mock_env->str2, "test");
+128 -28
fs/hfsplus/xattr.c
··· 50 50 return true; 51 51 } 52 52 53 - static void hfsplus_init_header_node(struct inode *attr_file, 53 + static u32 hfsplus_init_header_node(struct inode *attr_file, 54 54 u32 clump_size, 55 55 char *buf, u16 node_size) 56 56 { ··· 59 59 u16 offset; 60 60 __be16 *rec_offsets; 61 61 u32 hdr_node_map_rec_bits; 62 + u32 map_nodes = 0; 62 63 char *bmp; 63 64 u32 used_nodes; 64 65 u32 used_bmp_bytes; ··· 94 93 hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16))); 95 94 if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) { 96 95 u32 map_node_bits; 97 - u32 map_nodes; 98 96 99 97 desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1); 100 98 map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) - ··· 116 116 *bmp = ~(0xFF >> used_nodes); 117 117 offset += hdr_node_map_rec_bits / 8; 118 118 *--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; 119 196 } 120 197 121 198 static int hfsplus_create_attributes_file(struct super_block *sb) 122 199 { 123 - int err = 0; 124 200 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 125 201 struct inode *attr_file; 126 202 struct hfsplus_inode_info *hip; 203 + struct hfs_bnode_desc *desc; 127 204 u32 clump_size; 128 205 u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE; 206 + u32 next_node; 207 + u32 map_node_idx; 208 + u32 map_nodes; 129 209 char *buf; 130 - int index, written; 131 - struct address_space *mapping; 132 - struct page *page; 210 + int index; 133 211 int old_state = HFSPLUS_EMPTY_ATTR_TREE; 212 + int err = 0; 134 213 135 214 hfs_dbg("ino %d\n", HFSPLUS_ATTR_CNID); 136 215 ··· 274 195 } 275 196 276 197 while (hip->alloc_blocks < hip->clump_blocks) { 277 - err = hfsplus_file_extend(attr_file, false); 198 + err = hfsplus_file_extend(attr_file, true); 278 199 if (unlikely(err)) { 279 200 pr_err("failed to extend attributes file\n"); 280 201 goto end_attr_file_creation; ··· 291 212 goto end_attr_file_creation; 292 213 } 293 214 294 - hfsplus_init_header_node(attr_file, clump_size, buf, node_size); 215 + map_nodes = hfsplus_init_header_node(attr_file, clump_size, buf, node_size); 295 216 296 - mapping = attr_file->i_mapping; 217 + desc = (struct hfs_bnode_desc *)buf; 218 + next_node = be32_to_cpu(desc->next); 297 219 298 220 index = 0; 299 - written = 0; 300 - for (; written < node_size; index++, written += PAGE_SIZE) { 301 - void *kaddr; 302 221 303 - page = read_mapping_page(mapping, index, NULL); 304 - if (IS_ERR(page)) { 305 - err = PTR_ERR(page); 222 + err = hfsplus_write_attributes_file_node(attr_file, buf, 223 + node_size, &index); 224 + if (unlikely(err)) 225 + goto failed_header_node_init; 226 + 227 + for (map_node_idx = 0; map_node_idx < map_nodes; map_node_idx++) { 228 + if (next_node >= map_nodes) 229 + next_node = 0; 230 + 231 + hfsplus_init_map_node(buf, node_size, next_node); 232 + 233 + err = hfsplus_write_attributes_file_node(attr_file, buf, 234 + node_size, &index); 235 + if (unlikely(err)) 306 236 goto failed_header_node_init; 307 - } 308 237 309 - kaddr = kmap_atomic(page); 310 - memcpy(kaddr, buf + written, 311 - min_t(size_t, PAGE_SIZE, node_size - written)); 312 - kunmap_atomic(kaddr); 313 - 314 - set_page_dirty(page); 315 - put_page(page); 238 + next_node++; 316 239 } 317 240 241 + hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(sb), HFSPLUS_I_ATTR_DIRTY); 318 242 hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY); 319 243 320 244 sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); ··· 396 314 hfs_bnode_write(cat_fd.bnode, &entry, 397 315 cat_fd.entryoffset, 398 316 sizeof(struct hfsplus_cat_folder)); 399 - hfsplus_mark_inode_dirty(inode, 317 + hfsplus_mark_inode_dirty( 318 + HFSPLUS_CAT_TREE_I(inode->i_sb), 400 319 HFSPLUS_I_CAT_DIRTY); 320 + hfsplus_mark_inode_dirty(inode, 321 + HFSPLUS_I_CAT_DIRTY); 401 322 } else { 402 323 err = -ERANGE; 403 324 goto end_setxattr; ··· 412 327 hfs_bnode_write(cat_fd.bnode, &entry, 413 328 cat_fd.entryoffset, 414 329 sizeof(struct hfsplus_cat_file)); 415 - hfsplus_mark_inode_dirty(inode, 330 + hfsplus_mark_inode_dirty( 331 + HFSPLUS_CAT_TREE_I(inode->i_sb), 416 332 HFSPLUS_I_CAT_DIRTY); 333 + hfsplus_mark_inode_dirty(inode, 334 + HFSPLUS_I_CAT_DIRTY); 417 335 } else { 418 336 err = -ERANGE; 419 337 goto end_setxattr; ··· 469 381 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 470 382 offsetof(struct hfsplus_cat_folder, flags), 471 383 cat_entry_flags); 384 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(inode->i_sb), 385 + HFSPLUS_I_CAT_DIRTY); 472 386 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 473 387 } else if (cat_entry_type == HFSPLUS_FILE) { 474 388 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode, ··· 482 392 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 483 393 offsetof(struct hfsplus_cat_file, flags), 484 394 cat_entry_flags); 395 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(inode->i_sb), 396 + HFSPLUS_I_CAT_DIRTY); 485 397 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 486 398 } else { 487 399 pr_err("invalid catalog entry type\n"); 488 400 err = -EIO; 489 401 goto end_setxattr; 490 402 } 403 + 404 + inode_set_ctime_current(inode); 491 405 492 406 end_setxattr: 493 407 hfs_find_exit(&cat_fd); ··· 643 549 644 550 res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd); 645 551 if (res) { 646 - if (res == -ENOENT) 552 + if (res == -ENOENT || res == -ENODATA) 647 553 res = -ENODATA; 648 554 else 649 - pr_err("xattr searching failed\n"); 555 + pr_err("xattr search failed\n"); 650 556 goto out; 651 557 } 652 558 ··· 838 744 839 745 err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd); 840 746 if (err) { 841 - if (err == -ENOENT) { 747 + if (err == -ENOENT || err == -ENODATA) { 842 748 res = 0; 843 749 goto end_listxattr; 844 750 } else { ··· 956 862 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 957 863 offsetof(struct hfsplus_cat_folder, flags), 958 864 flags); 865 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(inode->i_sb), 866 + HFSPLUS_I_CAT_DIRTY); 959 867 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 960 868 } else if (cat_entry_type == HFSPLUS_FILE) { 961 869 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset + ··· 969 873 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 970 874 offsetof(struct hfsplus_cat_file, flags), 971 875 flags); 876 + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(inode->i_sb), 877 + HFSPLUS_I_CAT_DIRTY); 972 878 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 973 879 } else { 974 880 pr_err("invalid catalog entry type\n"); 975 881 err = -EIO; 976 882 goto end_removexattr; 977 883 } 884 + 885 + inode_set_ctime_current(inode); 978 886 979 887 end_removexattr: 980 888 hfs_find_exit(&cat_fd);
+9
include/linux/hfs_common.h
··· 166 166 hfsplus_unichr unicode[HFSPLUS_ATTR_MAX_STRLEN]; 167 167 } __packed; 168 168 169 + enum { 170 + HFS_REGULAR_NAME, 171 + HFS_XATTR_NAME, 172 + }; 173 + 169 174 struct hfs_extent { 170 175 __be16 block; 171 176 __be16 count; ··· 515 510 #define HFSPLUS_NODE_MXSZ 32768 516 511 #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 517 512 #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 513 + #define HFSPLUS_BTREE_HDR_MAP_REC_INDEX 2 /* Map (bitmap) record in Header node */ 514 + #define HFSPLUS_BTREE_MAP_NODE_REC_INDEX 0 /* Map record in Map Node */ 518 515 #define HFSPLUS_BTREE_HDR_USER_BYTES 128 516 + #define HFSPLUS_BTREE_MAP_NODE_RECS_COUNT 2 517 + #define HFSPLUS_BTREE_MAP_NODE_RESERVED_BYTES 2 519 518 520 519 /* btree key type */ 521 520 #define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */