aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus
diff options
context:
space:
mode:
authorSergei Antonov <saproj@gmail.com>2014-06-06 17:36:28 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-06 19:08:10 -0400
commit2cd282a1bc6b9d111b8beee63bea0af735a8a1aa (patch)
tree80dff1bb5f089868bdbee433b0308098b2c2e6b4 /fs/hfsplus
parent915ab236d3c491f9bb0f6596d03e25b37f4d5a20 (diff)
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>
Diffstat (limited to 'fs/hfsplus')
-rw-r--r--fs/hfsplus/bnode.c17
-rw-r--r--fs/hfsplus/btree.c2
-rw-r--r--fs/hfsplus/extents.c10
-rw-r--r--fs/hfsplus/hfsplus_fs.h3
-rw-r--r--fs/hfsplus/hfsplus_raw.h1
-rw-r--r--fs/hfsplus/xattr.c2
6 files changed, 28 insertions, 7 deletions
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
index 285502af8df1..759708fd9331 100644
--- a/fs/hfsplus/bnode.c
+++ b/fs/hfsplus/bnode.c
@@ -646,8 +646,8 @@ void hfs_bnode_put(struct hfs_bnode *node)
646 if (test_bit(HFS_BNODE_DELETED, &node->flags)) { 646 if (test_bit(HFS_BNODE_DELETED, &node->flags)) {
647 hfs_bnode_unhash(node); 647 hfs_bnode_unhash(node);
648 spin_unlock(&tree->hash_lock); 648 spin_unlock(&tree->hash_lock);
649 hfs_bnode_clear(node, 0, 649 if (hfs_bnode_need_zeroout(tree))
650 PAGE_CACHE_SIZE * tree->pages_per_bnode); 650 hfs_bnode_clear(node, 0, tree->node_size);
651 hfs_bmap_free(node); 651 hfs_bmap_free(node);
652 hfs_bnode_free(node); 652 hfs_bnode_free(node);
653 return; 653 return;
@@ -656,3 +656,16 @@ void hfs_bnode_put(struct hfs_bnode *node)
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 */
663bool 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}
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 0fcec8b2a90b..3345c7553edc 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -358,7 +358,7 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
358 u32 count; 358 u32 count;
359 int res; 359 int res;
360 360
361 res = hfsplus_file_extend(inode); 361 res = hfsplus_file_extend(inode, hfs_bnode_need_zeroout(tree));
362 if (res) 362 if (res)
363 return ERR_PTR(res); 363 return ERR_PTR(res);
364 hip->phys_size = inode->i_size = 364 hip->phys_size = inode->i_size =
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index a7aafb35b624..a09fcb68c364 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -235,7 +235,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
235 if (iblock > hip->fs_blocks || !create) 235 if (iblock > hip->fs_blocks || !create)
236 return -EIO; 236 return -EIO;
237 if (ablock >= hip->alloc_blocks) { 237 if (ablock >= hip->alloc_blocks) {
238 res = hfsplus_file_extend(inode); 238 res = hfsplus_file_extend(inode, false);
239 if (res) 239 if (res)
240 return res; 240 return res;
241 } 241 }
@@ -425,7 +425,7 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,
425 return res; 425 return res;
426} 426}
427 427
428int hfsplus_file_extend(struct inode *inode) 428int hfsplus_file_extend(struct inode *inode, bool zeroout)
429{ 429{
430 struct super_block *sb = inode->i_sb; 430 struct super_block *sb = inode->i_sb;
431 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 431 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
@@ -463,6 +463,12 @@ int hfsplus_file_extend(struct inode *inode)
463 } 463 }
464 } 464 }
465 465
466 if (zeroout) {
467 res = sb_issue_zeroout(sb, start, len, GFP_NOFS);
468 if (res)
469 goto out;
470 }
471
466 hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); 472 hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
467 473
468 if (hip->alloc_blocks <= hip->first_blocks) { 474 if (hip->alloc_blocks <= hip->first_blocks) {
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 6c08ff6b11b2..d5ab79bd2f0f 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -414,6 +414,7 @@ void hfs_bnode_free(struct hfs_bnode *);
414struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); 414struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
415void hfs_bnode_get(struct hfs_bnode *); 415void hfs_bnode_get(struct hfs_bnode *);
416void hfs_bnode_put(struct hfs_bnode *); 416void hfs_bnode_put(struct hfs_bnode *);
417bool hfs_bnode_need_zeroout(struct hfs_btree *);
417 418
418/* brec.c */ 419/* brec.c */
419u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); 420u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
@@ -460,7 +461,7 @@ int hfsplus_ext_write_extent(struct inode *);
460int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int); 461int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int);
461int hfsplus_free_fork(struct super_block *, u32, 462int hfsplus_free_fork(struct super_block *, u32,
462 struct hfsplus_fork_raw *, int); 463 struct hfsplus_fork_raw *, int);
463int hfsplus_file_extend(struct inode *); 464int hfsplus_file_extend(struct inode *, bool zeroout);
464void hfsplus_file_truncate(struct inode *); 465void hfsplus_file_truncate(struct inode *);
465 466
466/* inode.c */ 467/* inode.c */
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h
index 5a126828d85e..8298d0985f81 100644
--- a/fs/hfsplus/hfsplus_raw.h
+++ b/fs/hfsplus/hfsplus_raw.h
@@ -144,6 +144,7 @@ struct hfsplus_vh {
144#define HFSPLUS_VOL_NODEID_REUSED (1 << 12) 144#define HFSPLUS_VOL_NODEID_REUSED (1 << 12)
145#define HFSPLUS_VOL_JOURNALED (1 << 13) 145#define HFSPLUS_VOL_JOURNALED (1 << 13)
146#define HFSPLUS_VOL_SOFTLOCK (1 << 15) 146#define HFSPLUS_VOL_SOFTLOCK (1 << 15)
147#define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31)
147 148
148/* HFS+ BTree node descriptor */ 149/* HFS+ BTree node descriptor */
149struct hfs_bnode_desc { 150struct hfs_bnode_desc {
diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c
index c03c94611cce..aab093c27c59 100644
--- a/fs/hfsplus/xattr.c
+++ b/fs/hfsplus/xattr.c
@@ -196,7 +196,7 @@ check_attr_tree_state_again:
196 } 196 }
197 197
198 while (hip->alloc_blocks < hip->clump_blocks) { 198 while (hip->alloc_blocks < hip->clump_blocks) {
199 err = hfsplus_file_extend(attr_file); 199 err = hfsplus_file_extend(attr_file, false);
200 if (unlikely(err)) { 200 if (unlikely(err)) {
201 pr_err("failed to extend attributes file\n"); 201 pr_err("failed to extend attributes file\n");
202 goto end_attr_file_creation; 202 goto end_attr_file_creation;