diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2018-07-06 15:13:08 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-07-16 03:57:03 -0400 |
commit | c767c31b951212e31dc13ba8f7a61c0ee9523934 (patch) | |
tree | 2af7d8f872223804dec7364f3cc9ebdf9e43a04b | |
parent | 97c5d32aecb6dab5695af71c2c4db6ab5763d45b (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.c | 55 |
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; } |