summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-07-06 15:13:08 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2018-07-16 03:57:03 -0400
commitc767c31b951212e31dc13ba8f7a61c0ee9523934 (patch)
tree2af7d8f872223804dec7364f3cc9ebdf9e43a04b
parent97c5d32aecb6dab5695af71c2c4db6ab5763d45b (diff)
bcachefs: Make hardlink path fully atomic
much like the create path, but simpler Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--fs/bcachefs/fs.c55
1 files changed, 44 insertions, 11 deletions
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index dad7564932ac..0a34306723c2 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -542,32 +542,65 @@ static int bch2_create(struct inode *vdir, struct dentry *dentry,
return __bch2_create(to_bch_ei(vdir), dentry, mode|S_IFREG, 0, false);
}
+static int inode_update_for_link_fn(struct bch_inode_info *inode,
+ struct bch_inode_unpacked *bi,
+ void *p)
+{
+ struct bch_fs *c = inode->v.i_sb->s_fs_info;
+ struct timespec now = current_time(&inode->v);
+
+ bi->bi_ctime = timespec_to_bch2_time(c, now);
+
+ if (bi->bi_flags & BCH_INODE_UNLINKED)
+ bi->bi_flags &= ~BCH_INODE_UNLINKED;
+ else
+ bi->bi_nlink++;
+
+ return 0;
+}
+
static int bch2_link(struct dentry *old_dentry, struct inode *vdir,
struct dentry *dentry)
{
struct bch_fs *c = vdir->i_sb->s_fs_info;
struct bch_inode_info *dir = to_bch_ei(vdir);
struct bch_inode_info *inode = to_bch_ei(old_dentry->d_inode);
+ struct btree_trans trans;
+ struct bch_inode_unpacked inode_u;
int ret;
lockdep_assert_held(&inode->v.i_rwsem);
- inode->v.i_ctime = current_time(&dir->v);
+ bch2_trans_init(&trans, c);
+retry:
+ bch2_trans_begin(&trans);
+
+ ret = __bch2_dirent_create(&trans, dir->v.i_ino,
+ &dir->ei_str_hash,
+ mode_to_type(inode->v.i_mode),
+ &dentry->d_name,
+ inode->v.i_ino,
+ BCH_HASH_SET_MUST_CREATE) ?:
+ bch2_write_inode_trans(&trans, inode, &inode_u,
+ inode_update_for_link_fn,
+ NULL) ?:
+ bch2_trans_commit(&trans, NULL, NULL,
+ &inode->ei_journal_seq,
+ BTREE_INSERT_ATOMIC|
+ BTREE_INSERT_NOUNLOCK);
- ret = bch2_inc_nlink(c, inode);
- if (ret)
- return ret;
+ if (ret == -EINTR)
+ goto retry;
- ihold(&inode->v);
+ if (likely(!ret))
+ bch2_inode_update_after_write(c, inode, &inode_u, ATTR_CTIME);
- ret = bch2_vfs_dirent_create(c, dir, mode_to_type(inode->v.i_mode),
- &dentry->d_name, inode->v.i_ino);
- if (unlikely(ret)) {
- bch2_dec_nlink(c, inode);
- iput(&inode->v);
+ bch2_trans_exit(&trans);
+
+ if (unlikely(ret))
return ret;
- }
+ ihold(&inode->v);
d_instantiate(dentry, &inode->v);
return 0;
}