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

hfsplus: fix "unused node is not erased" error

Zero newly allocated extents in the catalog tree if volume attributes
tell us to. Not doing so we risk getting the "unused node is not
erased" error. See kHFSUnusedNodeFix flag in Apple's source code for
reference.

There was a previous commit clearing the node when it is freed: commit
899bed05e9f6 ("hfsplus: fix issue with unzeroed unused b-tree nodes").
But it did not handle newly allocated extents (this patch fixes it).
And it zeroed nodes in all trees unconditionally which is an overkill.

This patch adds a condition and also switches to 'tree->node_size' as a
simpler method of getting the length to zero.

Signed-off-by: Sergei Antonov <saproj@gmail.com>
Cc: Anton Altaparmakov <aia21@cam.ac.uk>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Hin-Tak Leung <htl10@users.sourceforge.net>
Cc: Kyle Laracey <kalaracey@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Sergei Antonov and committed by
Linus Torvalds
2cd282a1 915ab236

+28 -7
+15 -2
fs/hfsplus/bnode.c
··· 646 646 if (test_bit(HFS_BNODE_DELETED, &node->flags)) { 647 647 hfs_bnode_unhash(node); 648 648 spin_unlock(&tree->hash_lock); 649 - hfs_bnode_clear(node, 0, 650 - PAGE_CACHE_SIZE * tree->pages_per_bnode); 649 + if (hfs_bnode_need_zeroout(tree)) 650 + hfs_bnode_clear(node, 0, tree->node_size); 651 651 hfs_bmap_free(node); 652 652 hfs_bnode_free(node); 653 653 return; ··· 656 656 } 657 657 } 658 658 659 + /* 660 + * Unused nodes have to be zeroed if this is the catalog tree and 661 + * a corresponding flag in the volume header is set. 662 + */ 663 + bool hfs_bnode_need_zeroout(struct hfs_btree *tree) 664 + { 665 + struct super_block *sb = tree->inode->i_sb; 666 + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 667 + const u32 volume_attr = be32_to_cpu(sbi->s_vhdr->attributes); 668 + 669 + return tree->cnid == HFSPLUS_CAT_CNID && 670 + volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX; 671 + }
+1 -1
fs/hfsplus/btree.c
··· 358 358 u32 count; 359 359 int res; 360 360 361 - res = hfsplus_file_extend(inode); 361 + res = hfsplus_file_extend(inode, hfs_bnode_need_zeroout(tree)); 362 362 if (res) 363 363 return ERR_PTR(res); 364 364 hip->phys_size = inode->i_size =
+8 -2
fs/hfsplus/extents.c
··· 235 235 if (iblock > hip->fs_blocks || !create) 236 236 return -EIO; 237 237 if (ablock >= hip->alloc_blocks) { 238 - res = hfsplus_file_extend(inode); 238 + res = hfsplus_file_extend(inode, false); 239 239 if (res) 240 240 return res; 241 241 } ··· 425 425 return res; 426 426 } 427 427 428 - int hfsplus_file_extend(struct inode *inode) 428 + int hfsplus_file_extend(struct inode *inode, bool zeroout) 429 429 { 430 430 struct super_block *sb = inode->i_sb; 431 431 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); ··· 461 461 res = -ENOSPC; 462 462 goto out; 463 463 } 464 + } 465 + 466 + if (zeroout) { 467 + res = sb_issue_zeroout(sb, start, len, GFP_NOFS); 468 + if (res) 469 + goto out; 464 470 } 465 471 466 472 hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
+2 -1
fs/hfsplus/hfsplus_fs.h
··· 414 414 struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); 415 415 void hfs_bnode_get(struct hfs_bnode *); 416 416 void hfs_bnode_put(struct hfs_bnode *); 417 + bool hfs_bnode_need_zeroout(struct hfs_btree *); 417 418 418 419 /* brec.c */ 419 420 u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); ··· 461 460 int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int); 462 461 int hfsplus_free_fork(struct super_block *, u32, 463 462 struct hfsplus_fork_raw *, int); 464 - int hfsplus_file_extend(struct inode *); 463 + int hfsplus_file_extend(struct inode *, bool zeroout); 465 464 void hfsplus_file_truncate(struct inode *); 466 465 467 466 /* inode.c */
+1
fs/hfsplus/hfsplus_raw.h
··· 144 144 #define HFSPLUS_VOL_NODEID_REUSED (1 << 12) 145 145 #define HFSPLUS_VOL_JOURNALED (1 << 13) 146 146 #define HFSPLUS_VOL_SOFTLOCK (1 << 15) 147 + #define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31) 147 148 148 149 /* HFS+ BTree node descriptor */ 149 150 struct hfs_bnode_desc {
+1 -1
fs/hfsplus/xattr.c
··· 196 196 } 197 197 198 198 while (hip->alloc_blocks < hip->clump_blocks) { 199 - err = hfsplus_file_extend(attr_file); 199 + err = hfsplus_file_extend(attr_file, false); 200 200 if (unlikely(err)) { 201 201 pr_err("failed to extend attributes file\n"); 202 202 goto end_attr_file_creation;