diff options
-rw-r--r-- | fs/bcachefs/fs-ioctl.c | 105 | ||||
-rw-r--r-- | fs/bcachefs/fs.c | 174 | ||||
-rw-r--r-- | fs/bcachefs/fs.h | 55 | ||||
-rw-r--r-- | fs/bcachefs/inode.h | 22 | ||||
-rw-r--r-- | fs/bcachefs/quota.c | 24 | ||||
-rw-r--r-- | fs/bcachefs/quota.h | 10 | ||||
-rw-r--r-- | fs/bcachefs/quota_types.h | 6 | ||||
-rw-r--r-- | fs/bcachefs/xattr.c | 25 |
8 files changed, 264 insertions, 157 deletions
diff --git a/fs/bcachefs/fs-ioctl.c b/fs/bcachefs/fs-ioctl.c index 9fd195f6c85f..afac13fe0a27 100644 --- a/fs/bcachefs/fs-ioctl.c +++ b/fs/bcachefs/fs-ioctl.c @@ -109,21 +109,12 @@ static int bch2_set_projid(struct bch_fs *c, u32 projid) { struct bch_qid qid = inode->ei_qid; - int ret; - - if (projid == inode->ei_qid.q[QTYP_PRJ]) - return 0; qid.q[QTYP_PRJ] = projid; - ret = bch2_quota_transfer(c, 1 << QTYP_PRJ, qid, inode->ei_qid, - inode->v.i_blocks + - inode->ei_quota_reserved); - if (ret) - return ret; - - inode->ei_qid.q[QTYP_PRJ] = projid; - return 0; + return bch2_fs_quota_transfer(c, inode, qid, + 1 << QTYP_PRJ, + KEY_TYPE_QUOTA_PREALLOC); } static int fssetxattr_inode_update_fn(struct bch_inode_info *inode, @@ -174,10 +165,7 @@ static int bch2_ioc_fssetxattr(struct bch_fs *c, mutex_lock(&inode->ei_update_lock); - mutex_lock(&inode->ei_quota_lock); ret = bch2_set_projid(c, inode, fa.fsx_projid); - mutex_unlock(&inode->ei_quota_lock); - if (ret) goto err_unlock; @@ -191,56 +179,12 @@ err: return ret; } -static int reinherit_attrs_fn(struct bch_inode_info *inode, - struct bch_inode_unpacked *bi, - void *p) -{ - struct bch_inode_info *dir = p; - u64 src, dst; - unsigned id; - int ret = 1; - - for (id = 0; id < Inode_opt_nr; id++) { - if (bi->bi_fields_set & (1 << id)) - continue; - - src = bch2_inode_opt_get(&dir->ei_inode, id); - dst = bch2_inode_opt_get(bi, id); - - if (src == dst) - continue; - - __bch2_inode_opt_set(bi, id, src); - ret = 0; - } - - return ret; -} - -static int __reinherit_attrs(struct bch_fs *c, - struct bch_inode_info *dir, - struct bch_inode_info *inode) -{ - int ret; - - mutex_lock(&inode->ei_update_lock); - ret = bch2_write_inode(c, inode, reinherit_attrs_fn, dir, 0); - mutex_unlock(&inode->ei_update_lock); - - if (ret < 0) - return ret; - - return !ret; -} - -static int reinherit_attrs_trans(struct btree_trans *trans, - struct bch_inode_info *inode, - static int bch2_ioc_reinherit_attrs(struct bch_fs *c, struct file *file, - struct bch_inode_info *inode, + struct bch_inode_info *src, const char __user *name) { + struct bch_inode_info *dst; struct inode *vinode = NULL; char *kname = NULL; struct qstr qstr; @@ -253,33 +197,52 @@ static int bch2_ioc_reinherit_attrs(struct bch_fs *c, ret = strncpy_from_user(kname, name, BCH_NAME_MAX); if (unlikely(ret < 0)) - goto err; + goto err1; qstr.hash_len = ret; qstr.name = kname; ret = -ENOENT; - inum = bch2_dirent_lookup(c, inode->v.i_ino, - &inode->ei_str_hash, + inum = bch2_dirent_lookup(c, src->v.i_ino, + &src->ei_str_hash, &qstr); if (!inum) - goto err; + goto err1; vinode = bch2_vfs_inode_get(c, inum); ret = PTR_ERR_OR_ZERO(vinode); if (ret) - goto err; + goto err1; + + dst = to_bch_ei(vinode); ret = mnt_want_write_file(file); if (ret) - goto err; + goto err2; - ret = __reinherit_attrs(c, inode, to_bch_ei(vinode)); + bch2_lock_inodes(src, dst); + + if (inode_attr_changing(src, dst, Inode_opt_project)) { + ret = bch2_fs_quota_transfer(c, dst, + src->ei_qid, + 1 << QTYP_PRJ, + KEY_TYPE_QUOTA_PREALLOC); + if (ret) + goto err3; + } + + ret = bch2_write_inode(c, dst, bch2_reinherit_attrs_fn, src, 0); +err3: + bch2_unlock_inodes(src, dst); + + /* return true if we did work */ + if (ret >= 0) + ret = !ret; mnt_drop_write_file(file); -err: - if (vinode) - iput(vinode); +err2: + iput(vinode); +err1: kfree(kname); return ret; diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index de85497772d8..4bb05f098bd8 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -47,30 +47,6 @@ static void journal_seq_copy(struct bch_inode_info *dst, } while ((v = cmpxchg(&dst->ei_journal_seq, old, journal_seq)) != old); } -static inline int ptrcmp(void *l, void *r) -{ - return (l > r) - (l < r); -} - -#define __bch2_lock_inodes(_lock, ...) \ -do { \ - struct bch_inode_info *a[] = { NULL, __VA_ARGS__ }; \ - unsigned i; \ - \ - bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp); \ - \ - for (i = ARRAY_SIZE(a) - 1; a[i]; --i) \ - if (a[i] != a[i - 1]) { \ - if (_lock) \ - mutex_lock_nested(&a[i]->ei_update_lock, i);\ - else \ - mutex_unlock(&a[i]->ei_update_lock); \ - } \ -} while (0) - -#define bch2_lock_inodes(...) __bch2_lock_inodes(true, __VA_ARGS__) -#define bch2_unlock_inodes(...) __bch2_lock_inodes(false, __VA_ARGS__) - /* * I_SIZE_DIRTY requires special handling: * @@ -119,7 +95,6 @@ void bch2_inode_update_after_write(struct bch_fs *c, inode->v.i_ctime = bch2_time_to_timespec(c, bi->bi_ctime); inode->ei_inode = *bi; - inode->ei_qid = bch_qid(bi); bch2_inode_flags_to_vfs(inode); } @@ -197,6 +172,67 @@ retry: return ret < 0 ? ret : 0; } +int bch2_fs_quota_transfer(struct bch_fs *c, + struct bch_inode_info *inode, + struct bch_qid new_qid, + unsigned qtypes, + enum quota_acct_mode mode) +{ + unsigned i; + int ret; + + qtypes &= enabled_qtypes(c); + + for (i = 0; i < QTYP_NR; i++) + if (new_qid.q[i] == inode->ei_qid.q[i]) + qtypes &= ~(1U << i); + + if (!qtypes) + return 0; + + mutex_lock(&inode->ei_quota_lock); + + ret = bch2_quota_transfer(c, qtypes, new_qid, + inode->ei_qid, + inode->v.i_blocks + + inode->ei_quota_reserved, + mode); + if (!ret) + for (i = 0; i < QTYP_NR; i++) + if (qtypes & (1 << i)) + inode->ei_qid.q[i] = new_qid.q[i]; + + mutex_unlock(&inode->ei_quota_lock); + + return ret; +} + +int bch2_reinherit_attrs_fn(struct bch_inode_info *inode, + struct bch_inode_unpacked *bi, + void *p) +{ + struct bch_inode_info *dir = p; + u64 src, dst; + unsigned id; + int ret = 1; + + for (id = 0; id < Inode_opt_nr; id++) { + if (bi->bi_fields_set & (1 << id)) + continue; + + src = bch2_inode_opt_get(&dir->ei_inode, id); + dst = bch2_inode_opt_get(bi, id); + + if (src == dst) + continue; + + bch2_inode_opt_set(bi, id, src); + ret = 0; + } + + return ret; +} + struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum) { struct bch_inode_unpacked inode_u; @@ -663,6 +699,7 @@ static int inode_update_for_rename_fn(struct bch_inode_info *inode, void *p) { struct rename_info *info = p; + int ret; if (inode == info->src_dir) { bi->bi_nlink -= S_ISDIR(info->src_inode->v.i_mode); @@ -677,6 +714,19 @@ static int inode_update_for_rename_fn(struct bch_inode_info *inode, S_ISDIR(info->dst_inode->v.i_mode); } + if (inode == info->src_inode) { + ret = bch2_reinherit_attrs_fn(inode, bi, info->dst_dir); + + BUG_ON(!ret && S_ISDIR(info->src_inode->v.i_mode)); + } + + if (inode == info->dst_inode && + info->mode == BCH_RENAME_EXCHANGE) { + ret = bch2_reinherit_attrs_fn(inode, bi, info->src_dir); + + BUG_ON(!ret && S_ISDIR(info->dst_inode->v.i_mode)); + } + if (inode == info->dst_inode && info->mode == BCH_RENAME_OVERWRITE) { BUG_ON(bi->bi_nlink && @@ -741,6 +791,39 @@ static int bch2_rename2(struct inode *src_vdir, struct dentry *src_dentry, i.dst_inode); bch2_trans_init(&trans, c); + + if (S_ISDIR(i.src_inode->v.i_mode) && + inode_attrs_changing(i.dst_dir, i.src_inode)) { + ret = -EXDEV; + goto err; + } + + if (i.mode == BCH_RENAME_EXCHANGE && + S_ISDIR(i.dst_inode->v.i_mode) && + inode_attrs_changing(i.src_dir, i.dst_inode)) { + ret = -EXDEV; + goto err; + } + + if (inode_attr_changing(i.dst_dir, i.src_inode, Inode_opt_project)) { + ret = bch2_fs_quota_transfer(c, i.src_inode, + i.dst_dir->ei_qid, + 1 << QTYP_PRJ, + KEY_TYPE_QUOTA_PREALLOC); + if (ret) + goto err; + } + + if (i.mode == BCH_RENAME_EXCHANGE && + inode_attr_changing(i.src_dir, i.dst_inode, Inode_opt_project)) { + ret = bch2_fs_quota_transfer(c, i.dst_inode, + i.src_dir->ei_qid, + 1 << QTYP_PRJ, + KEY_TYPE_QUOTA_PREALLOC); + if (ret) + goto err; + } + retry: bch2_trans_begin(&trans); i.now = bch2_current_time(c); @@ -791,6 +874,17 @@ retry: ATTR_CTIME); err: bch2_trans_exit(&trans); + + bch2_fs_quota_transfer(c, i.src_inode, + bch_qid(&i.src_inode->ei_inode), + 1 << QTYP_PRJ, + KEY_TYPE_QUOTA_NOCHECK); + if (i.dst_inode) + bch2_fs_quota_transfer(c, i.dst_inode, + bch_qid(&i.dst_inode->ei_inode), + 1 << QTYP_PRJ, + KEY_TYPE_QUOTA_NOCHECK); + bch2_unlock_inodes(i.src_dir, i.dst_dir, i.src_inode, @@ -837,36 +931,26 @@ static int inode_update_for_setattr_fn(struct bch_inode_info *inode, 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 bch_qid qid; struct btree_trans trans; struct bch_inode_unpacked inode_u; struct posix_acl *acl = NULL; - unsigned qtypes = 0; int ret; mutex_lock(&inode->ei_update_lock); - if (c->opts.usrquota && - (iattr->ia_valid & ATTR_UID) && - !uid_eq(iattr->ia_uid, inode->v.i_uid)) { - qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid), - qtypes |= 1 << QTYP_USR; - } + qid = inode->ei_qid; + + if (iattr->ia_valid & ATTR_UID) + qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid); - if (c->opts.grpquota && - (iattr->ia_valid & ATTR_GID) && - !gid_eq(iattr->ia_gid, inode->v.i_gid)) { + if (iattr->ia_valid & ATTR_GID) qid.q[QTYP_GRP] = from_kgid(&init_user_ns, iattr->ia_gid); - qtypes |= 1 << QTYP_GRP; - } - if (qtypes) { - ret = bch2_quota_transfer(c, qtypes, qid, inode->ei_qid, - inode->v.i_blocks + - inode->ei_quota_reserved); - if (ret) - goto err; - } + ret = bch2_fs_quota_transfer(c, inode, qid, ~0, + KEY_TYPE_QUOTA_PREALLOC); + if (ret) + goto err; bch2_trans_init(&trans, c); retry: diff --git a/fs/bcachefs/fs.h b/fs/bcachefs/fs.h index 21fb0ef1ec2f..b9dc87da87f5 100644 --- a/fs/bcachefs/fs.h +++ b/fs/bcachefs/fs.h @@ -29,6 +29,30 @@ struct bch_inode_info { #define to_bch_ei(_inode) \ container_of_or_null(_inode, struct bch_inode_info, v) +static inline int ptrcmp(void *l, void *r) +{ + return (l > r) - (l < r); +} + +#define __bch2_lock_inodes(_lock, ...) \ +do { \ + struct bch_inode_info *a[] = { NULL, __VA_ARGS__ }; \ + unsigned i; \ + \ + bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp); \ + \ + for (i = ARRAY_SIZE(a) - 1; a[i]; --i) \ + if (a[i] != a[i - 1]) { \ + if (_lock) \ + mutex_lock_nested(&a[i]->ei_update_lock, i);\ + else \ + mutex_unlock(&a[i]->ei_update_lock); \ + } \ +} while (0) + +#define bch2_lock_inodes(...) __bch2_lock_inodes(true, __VA_ARGS__) +#define bch2_unlock_inodes(...) __bch2_lock_inodes(false, __VA_ARGS__) + static inline struct bch_inode_info *file_bch_inode(struct file *file) { return to_bch_ei(file_inode(file)); @@ -49,12 +73,39 @@ static inline u64 bch2_current_time(struct bch_fs *c) return timespec_to_bch2_time(c, current_kernel_time64()); } +static inline bool inode_attr_changing(struct bch_inode_info *dir, + struct bch_inode_info *inode, + enum inode_opt_id id) +{ + return !(inode->ei_inode.bi_fields_set & (1 << id)) && + bch2_inode_opt_get(&dir->ei_inode, id) != + bch2_inode_opt_get(&inode->ei_inode, id); +} + +static inline bool inode_attrs_changing(struct bch_inode_info *dir, + struct bch_inode_info *inode) +{ + unsigned id; + + for (id = 0; id < Inode_opt_nr; id++) + if (inode_attr_changing(dir, inode, id)) + return true; + + return false; +} + struct bch_inode_unpacked; #ifndef NO_BCACHEFS_FS struct inode *bch2_vfs_inode_get(struct bch_fs *, u64); +int bch2_fs_quota_transfer(struct bch_fs *, + struct bch_inode_info *, + struct bch_qid, + unsigned, + enum quota_acct_mode); + /* returns 0 if we want to do the update, or error is passed up */ typedef int (*inode_set_fn)(struct bch_inode_info *, struct bch_inode_unpacked *, void *); @@ -70,6 +121,10 @@ int __must_check bch2_write_inode_trans(struct btree_trans *, int __must_check bch2_write_inode(struct bch_fs *, struct bch_inode_info *, inode_set_fn, void *, unsigned); +int bch2_reinherit_attrs_fn(struct bch_inode_info *, + struct bch_inode_unpacked *, + void *); + void bch2_vfs_exit(void); int bch2_vfs_init(void); diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h index e50e788369bc..0d609985d15b 100644 --- a/fs/bcachefs/inode.h +++ b/fs/bcachefs/inode.h @@ -89,19 +89,13 @@ static inline struct bch_io_opts bch2_inode_opts_get(struct bch_inode_unpacked * return ret; } -/* XXX switch to inode_opt_id */ - -static inline void __bch2_inode_opt_set(struct bch_inode_unpacked *inode, - enum inode_opt_id id, u64 v) +static inline void bch2_inode_opt_set(struct bch_inode_unpacked *inode, + enum inode_opt_id id, u64 v) { switch (id) { #define x(_name, ...) \ case Inode_opt_##_name: \ inode->bi_##_name = v; \ - if (v) \ - inode->bi_fields_set |= 1U << Inode_opt_##_name;\ - else \ - inode->bi_fields_set &= ~(1U << Inode_opt_##_name);\ break; BCH_INODE_OPTS() #undef x @@ -110,18 +104,6 @@ static inline void __bch2_inode_opt_set(struct bch_inode_unpacked *inode, } } -static inline void bch2_inode_opt_set(struct bch_inode_unpacked *inode, - enum inode_opt_id id, u64 v) -{ - return __bch2_inode_opt_set(inode, id, v + 1); -} - -static inline void bch2_inode_opt_clear(struct bch_inode_unpacked *inode, - enum inode_opt_id id) -{ - return __bch2_inode_opt_set(inode, id, 0); -} - static inline u64 bch2_inode_opt_get(struct bch_inode_unpacked *inode, enum inode_opt_id id) { diff --git a/fs/bcachefs/quota.c b/fs/bcachefs/quota.c index 95ff0caea199..3a6a00e15c77 100644 --- a/fs/bcachefs/quota.c +++ b/fs/bcachefs/quota.c @@ -217,17 +217,19 @@ static int bch2_quota_check_limit(struct bch_fs *c, return 0; } -int bch2_quota_acct(struct bch_fs *c, struct bch_qid qid, - enum quota_counters counter, s64 v, - enum quota_acct_mode mode) +int __bch2_quota_acct(struct bch_fs *c, unsigned qtypes, + struct bch_qid qid, + enum quota_counters counter, s64 v, + enum quota_acct_mode mode) { - unsigned qtypes = enabled_qtypes(c); struct bch_memquota_type *q; struct bch_memquota *mq[QTYP_NR]; struct quota_msgs msgs; unsigned i; int ret = 0; + qtypes &= enabled_qtypes(c); + memset(&msgs, 0, sizeof(msgs)); for_each_set_qtype(c, i, q, qtypes) @@ -256,6 +258,13 @@ err: return ret; } +int bch2_quota_acct(struct bch_fs *c, struct bch_qid qid, + enum quota_counters counter, s64 v, + enum quota_acct_mode mode) +{ + return __bch2_quota_acct(c, ~0, qid, counter, v, mode); +} + static void __bch2_quota_transfer(struct bch_memquota *src_q, struct bch_memquota *dst_q, enum quota_counters counter, s64 v) @@ -269,7 +278,8 @@ static void __bch2_quota_transfer(struct bch_memquota *src_q, int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes, struct bch_qid dst, - struct bch_qid src, u64 space) + struct bch_qid src, u64 space, + enum quota_acct_mode mode) { struct bch_memquota_type *q; struct bch_memquota *src_q[3], *dst_q[3]; @@ -295,13 +305,13 @@ int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes, ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_SPC, dst_q[i]->c[Q_SPC].v + space, - KEY_TYPE_QUOTA_PREALLOC); + mode); if (ret) goto err; ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_INO, dst_q[i]->c[Q_INO].v + 1, - KEY_TYPE_QUOTA_PREALLOC); + mode); if (ret) goto err; } diff --git a/fs/bcachefs/quota.h b/fs/bcachefs/quota.h index 0c3eb6973de1..5da9189b8971 100644 --- a/fs/bcachefs/quota.h +++ b/fs/bcachefs/quota.h @@ -14,12 +14,6 @@ void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); .val_to_text = bch2_quota_to_text, \ } -enum quota_acct_mode { - KEY_TYPE_QUOTA_PREALLOC, - KEY_TYPE_QUOTA_WARN, - KEY_TYPE_QUOTA_NOCHECK, -}; - static inline struct bch_qid bch_qid(struct bch_inode_unpacked *u) { return (struct bch_qid) { @@ -38,11 +32,13 @@ static inline unsigned enabled_qtypes(struct bch_fs *c) #ifdef CONFIG_BCACHEFS_QUOTA +int __bch2_quota_acct(struct bch_fs *, unsigned, struct bch_qid, + enum quota_counters, s64, enum quota_acct_mode); int bch2_quota_acct(struct bch_fs *, struct bch_qid, enum quota_counters, s64, enum quota_acct_mode); int bch2_quota_transfer(struct bch_fs *, unsigned, struct bch_qid, - struct bch_qid, u64); + struct bch_qid, u64, enum quota_acct_mode); void bch2_fs_quota_exit(struct bch_fs *); void bch2_fs_quota_init(struct bch_fs *); diff --git a/fs/bcachefs/quota_types.h b/fs/bcachefs/quota_types.h index bcaed4ea8345..e978dc54b6aa 100644 --- a/fs/bcachefs/quota_types.h +++ b/fs/bcachefs/quota_types.h @@ -7,6 +7,12 @@ struct bch_qid { u32 q[QTYP_NR]; }; +enum quota_acct_mode { + KEY_TYPE_QUOTA_PREALLOC, + KEY_TYPE_QUOTA_WARN, + KEY_TYPE_QUOTA_NOCHECK, +}; + struct memquota_counter { u64 v; u64 hardlimit; diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c index 85187bf887b2..407177b25e24 100644 --- a/fs/bcachefs/xattr.c +++ b/fs/bcachefs/xattr.c @@ -443,13 +443,12 @@ static int inode_opt_set_fn(struct bch_inode_info *inode, { struct inode_opt_set *s = p; - if (s->defined) { - bch2_inode_opt_set(bi, s->id, s->v); + if (s->defined) bi->bi_fields_set |= 1U << s->id; - } else { - bch2_inode_opt_clear(bi, s->id); + else bi->bi_fields_set &= ~(1U << s->id); - } + + bch2_inode_opt_set(bi, s->id, s->v); return 0; } @@ -479,24 +478,36 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler, s.id = inode_opt_id; if (value) { + u64 v = 0; + buf = kmalloc(size + 1, GFP_KERNEL); if (!buf) return -ENOMEM; memcpy(buf, value, size); buf[size] = '\0'; - ret = bch2_opt_parse(c, opt, buf, &s.v); + ret = bch2_opt_parse(c, opt, buf, &v); kfree(buf); if (ret < 0) return ret; - ret = bch2_opt_check_may_set(c, opt_id, s.v); + ret = bch2_opt_check_may_set(c, opt_id, v); if (ret < 0) return ret; + s.v = v + 1; s.defined = true; } else { + if (!IS_ROOT(dentry)) { + struct bch_inode_info *dir = + to_bch_ei(d_inode(dentry->d_parent)); + + s.v = bch2_inode_opt_get(&dir->ei_inode, inode_opt_id); + } else { + s.v = 0; + } + s.defined = false; } |