summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-07-10 05:06:07 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2018-07-16 03:57:04 -0400
commit55965fe6c25fa6e5373a8e1e55dfdb219eed9e05 (patch)
treee124841f8c21409a02e3059a07980c53a4f2d39f
parente5a033b3fe442638c13604ddd318ad7f3dfa7517 (diff)
bcachefs: Make bch2_setattr_nonsize() fully atomic
updating i_mode may also require an update to the acl - the inode update and acl xattr updates are now atomic. Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--fs/bcachefs/acl.c43
-rw-r--r--fs/bcachefs/acl.h11
-rw-r--r--fs/bcachefs/fs.c96
3 files changed, 123 insertions, 27 deletions
diff --git a/fs/bcachefs/acl.c b/fs/bcachefs/acl.c
index 3f03fed2947f..de4a96f4d7d8 100644
--- a/fs/bcachefs/acl.c
+++ b/fs/bcachefs/acl.c
@@ -355,4 +355,47 @@ err:
return ret;
}
+int bch2_acl_chmod(struct btree_trans *trans,
+ struct bch_inode_info *inode,
+ umode_t mode,
+ struct posix_acl **new_acl)
+{
+ struct btree_iter *iter;
+ struct bkey_s_c_xattr xattr;
+ struct bkey_i_xattr *new;
+ struct posix_acl *acl;
+ int ret = 0;
+
+ iter = bch2_hash_lookup(trans, bch2_xattr_hash_desc,
+ &inode->ei_str_hash, inode->v.i_ino,
+ &X_SEARCH(BCH_XATTR_INDEX_POSIX_ACL_ACCESS, "", 0),
+ BTREE_ITER_INTENT);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter) != -ENOENT ? PTR_ERR(iter) : 0;
+
+ xattr = bkey_s_c_to_xattr(bch2_btree_iter_peek_slot(iter));
+
+ acl = bch2_acl_from_disk(xattr_val(xattr.v),
+ le16_to_cpu(xattr.v->x_val_len));
+ if (IS_ERR_OR_NULL(acl))
+ return PTR_ERR(acl);
+
+ ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
+ if (ret)
+ goto err;
+
+ new = bch2_acl_to_xattr(trans, acl, ACL_TYPE_ACCESS);
+ if (IS_ERR(new)) {
+ ret = PTR_ERR(new);
+ goto err;
+ }
+
+ bch2_trans_update(trans, iter, &new->k_i, 0);
+ *new_acl = acl;
+ acl = NULL;
+err:
+ kfree(acl);
+ return ret;
+}
+
#endif /* CONFIG_BCACHEFS_POSIX_ACL */
diff --git a/fs/bcachefs/acl.h b/fs/bcachefs/acl.h
index 2e5ab78c779a..1e15245e8040 100644
--- a/fs/bcachefs/acl.h
+++ b/fs/bcachefs/acl.h
@@ -3,6 +3,7 @@
struct bch_inode_unpacked;
struct bch_hash_info;
+struct bch_inode_info;
struct posix_acl;
#ifdef CONFIG_BCACHEFS_POSIX_ACL
@@ -32,6 +33,8 @@ int bch2_set_acl_trans(struct btree_trans *,
struct posix_acl *, int);
int __bch2_set_acl(struct inode *, struct posix_acl *, int);
int bch2_set_acl(struct inode *, struct posix_acl *, int);
+int bch2_acl_chmod(struct btree_trans *, struct bch_inode_info *,
+ umode_t, struct posix_acl **);
#else
@@ -48,6 +51,14 @@ static inline int __bch2_set_acl(struct inode *inode, struct posix_acl *acl, int
return 0;
}
+static inline int bch2_acl_chmod(struct btree_trans *trans,
+ struct bch_inode_info *inode,
+ umode_t mode,
+ struct posix_acl **new_acl)
+{
+ return 0;
+}
+
#endif /* CONFIG_BCACHEFS_POSIX_ACL */
#endif /* _BCACHEFS_ACL_H */
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index c255e3892e11..de7f44062d45 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -83,6 +83,9 @@ void bch2_inode_update_after_write(struct bch_fs *c,
set_nlink(&inode->v, bi->bi_flags & BCH_INODE_UNLINKED
? 0
: bi->bi_nlink + nlink_bias(inode->v.i_mode));
+ i_uid_write(&inode->v, bi->bi_uid);
+ i_gid_write(&inode->v, bi->bi_gid);
+ inode->v.i_mode = bi->bi_mode;
if (fields & ATTR_ATIME)
inode->v.i_atime = bch2_time_to_timespec(c, bi->bi_atime);
@@ -101,7 +104,6 @@ int __must_check bch2_write_inode_trans(struct btree_trans *trans,
inode_set_fn set,
void *p)
{
- struct bch_fs *c = trans->c;
struct btree_iter *iter;
struct bkey_inode_buf *inode_p;
struct bkey_s_c k;
@@ -134,15 +136,6 @@ int __must_check bch2_write_inode_trans(struct btree_trans *trans,
!(inode_u->bi_flags & BCH_INODE_I_SIZE_DIRTY) &&
inode_u->bi_size > i_size_read(&inode->v));
- inode_u->bi_mode = inode->v.i_mode;
- inode_u->bi_uid = i_uid_read(&inode->v);
- inode_u->bi_gid = i_gid_read(&inode->v);
- inode_u->bi_project = inode->ei_qid.q[QTYP_PRJ];
- inode_u->bi_dev = inode->v.i_rdev;
- inode_u->bi_atime = timespec_to_bch2_time(c, inode->v.i_atime);
- inode_u->bi_mtime = timespec_to_bch2_time(c, inode->v.i_mtime);
- inode_u->bi_ctime = timespec_to_bch2_time(c, inode->v.i_ctime);
-
if (set) {
ret = set(inode, inode_u, p);
if (ret)
@@ -191,12 +184,6 @@ retry:
return ret < 0 ? ret : 0;
}
-int __must_check bch2_write_inode(struct bch_fs *c,
- struct bch_inode_info *inode)
-{
- return __bch2_write_inode(c, inode, NULL, NULL, 0);
-}
-
static struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
{
struct bch_inode_unpacked inode_u;
@@ -848,10 +835,48 @@ err:
return ret;
}
+static int inode_update_for_setattr_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 iattr *attr = p;
+ unsigned int ia_valid = attr->ia_valid;
+
+ if (ia_valid & ATTR_UID)
+ bi->bi_uid = from_kuid(inode->v.i_sb->s_user_ns, attr->ia_uid);
+ if (ia_valid & ATTR_GID)
+ bi->bi_gid = from_kgid(inode->v.i_sb->s_user_ns, attr->ia_gid);
+
+ if (ia_valid & ATTR_ATIME)
+ bi->bi_atime = timespec_to_bch2_time(c, attr->ia_atime);
+ if (ia_valid & ATTR_MTIME)
+ bi->bi_mtime = timespec_to_bch2_time(c, attr->ia_mtime);
+ if (ia_valid & ATTR_CTIME)
+ bi->bi_ctime = timespec_to_bch2_time(c, attr->ia_ctime);
+
+ if (ia_valid & ATTR_MODE) {
+ umode_t mode = attr->ia_mode;
+ kgid_t gid = ia_valid & ATTR_GID
+ ? attr->ia_gid
+ : inode->v.i_gid;
+
+ if (!in_group_p(gid) &&
+ !capable_wrt_inode_uidgid(&inode->v, CAP_FSETID))
+ mode &= ~S_ISGID;
+ bi->bi_mode = mode;
+ }
+
+ return 0;
+}
+
static int bch2_setattr_nonsize(struct bch_inode_info *inode, struct iattr *iattr)
{
struct bch_fs *c = inode->v.i_sb->s_fs_info;
struct bch_qid qid = inode->ei_qid;
+ struct btree_trans trans;
+ struct bch_inode_unpacked inode_u;
+ struct posix_acl *acl = NULL;
unsigned qtypes = 0;
int ret;
@@ -876,18 +901,38 @@ static int bch2_setattr_nonsize(struct bch_inode_info *inode, struct iattr *iatt
inode->v.i_blocks +
inode->ei_quota_reserved);
if (ret)
- goto out_unlock;
+ goto err;
}
- setattr_copy(&inode->v, iattr);
+ bch2_trans_init(&trans, c);
+retry:
+ bch2_trans_begin(&trans);
+ kfree(acl);
+ acl = NULL;
- ret = bch2_write_inode(c, inode);
-out_unlock:
- mutex_unlock(&inode->ei_update_lock);
+ ret = bch2_write_inode_trans(&trans, inode, &inode_u,
+ inode_update_for_setattr_fn, iattr) ?:
+ (iattr->ia_valid & ATTR_MODE
+ ? bch2_acl_chmod(&trans, inode, iattr->ia_mode, &acl)
+ : 0) ?:
+ bch2_trans_commit(&trans, NULL, NULL,
+ &inode->ei_journal_seq,
+ BTREE_INSERT_ATOMIC|
+ BTREE_INSERT_NOUNLOCK|
+ BTREE_INSERT_NOFAIL);
+ if (ret == -EINTR)
+ goto retry;
+ if (unlikely(ret))
+ goto err_trans;
- if (!ret &&
- iattr->ia_valid & ATTR_MODE)
- ret = posix_acl_chmod(&inode->v, inode->v.i_mode);
+ bch2_inode_update_after_write(c, inode, &inode_u, iattr->ia_valid);
+
+ if (acl)
+ set_cached_acl(&inode->v, ACL_TYPE_ACCESS, acl);
+err_trans:
+ bch2_trans_exit(&trans);
+err:
+ mutex_unlock(&inode->ei_update_lock);
return ret;
}
@@ -1201,9 +1246,6 @@ static void bch2_vfs_inode_init(struct bch_fs *c,
{
bch2_inode_update_after_write(c, inode, bi, ~0);
- inode->v.i_mode = bi->bi_mode;
- i_uid_write(&inode->v, bi->bi_uid);
- i_gid_write(&inode->v, bi->bi_gid);
inode->v.i_blocks = bi->bi_sectors;
inode->v.i_ino = bi->bi_inum;
inode->v.i_rdev = bi->bi_dev;