#include "io.h"
#include "journal.h"
#include "keylist.h"
+#include "quota.h"
#include "super.h"
#include "xattr.h"
lockdep_assert_held(&inode->ei_update_lock);
bch2_btree_iter_init(&iter, c, BTREE_ID_INODES, POS(inum, 0),
- BTREE_ITER_INTENT);
+ BTREE_ITER_SLOTS|BTREE_ITER_INTENT);
do {
- struct bkey_s_c k = bch2_btree_iter_peek_with_holes(&iter);
+ struct bkey_s_c k = bch2_btree_iter_peek_slot(&iter);
if ((ret = btree_iter_err(k)))
goto out;
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_nlink= i_nlink - nlink_bias(inode->v.i_mode);
inode_u.bi_dev = inode->v.i_rdev;
inode_u.bi_atime= timespec_to_bch2_time(c, inode->v.i_atime);
BTREE_INSERT_ENTRY(&iter, &inode_p.inode.k_i));
} while (ret == -EINTR);
- if (!ret)
+ if (!ret) {
inode->ei_inode = inode_u;
+ inode->ei_qid = bch_qid(&inode_u);
+ }
out:
bch2_btree_iter_unlock(&iter);
#ifdef CONFIG_BCACHEFS_POSIX_ACL
ret = posix_acl_create(&dir->v, &inode->v.i_mode, &default_acl, &acl);
- if (ret) {
- make_bad_inode(&inode->v);
- goto err;
- }
+ if (ret)
+ goto err_make_bad;
#endif
bch2_inode_init(c, &inode_u,
inode->v.i_mode, rdev,
&dir->ei_inode);
+ inode_u.bi_project = dir->ei_qid.q[QTYP_PRJ];
+
+ ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1, BCH_QUOTA_PREALLOC);
+ if (ret)
+ goto err_make_bad;
+
ret = bch2_inode_create(c, &inode_u,
BLOCKDEV_INODE_MAX, 0,
&c->unused_inode_hint);
- if (unlikely(ret)) {
- /*
- * indicate to bch_evict_inode that the inode was never actually
- * created:
- */
- make_bad_inode(&inode->v);
- goto err;
- }
+ if (unlikely(ret))
+ goto err_acct_quota;
bch2_vfs_inode_init(c, inode, &inode_u);
+ atomic_long_inc(&c->nr_inodes);
if (default_acl) {
ret = bch2_set_acl(&inode->v, default_acl, ACL_TYPE_DEFAULT);
}
insert_inode_hash(&inode->v);
- atomic_long_inc(&c->nr_inodes);
out:
posix_acl_release(default_acl);
posix_acl_release(acl);
return inode;
+err_acct_quota:
+ bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, -1, BCH_QUOTA_WARN);
+err_make_bad:
+ /*
+ * indicate to bch_evict_inode that the inode was never actually
+ * created:
+ */
+ make_bad_inode(&inode->v);
err:
clear_nlink(&inode->v);
iput(&inode->v);
return bch2_rename(c, old_dir, old_dentry, new_dir, new_dentry);
}
-static int bch2_setattr(struct dentry *dentry, struct iattr *iattr)
+static int bch2_setattr_nonsize(struct bch_inode_info *inode, struct iattr *iattr)
{
- struct bch_inode_info *inode = to_bch_ei(dentry->d_inode);
struct bch_fs *c = inode->v.i_sb->s_fs_info;
- int ret = 0;
+ struct bch_qid qid = inode->ei_qid;
+ unsigned qtypes = 0;
+ int ret;
- lockdep_assert_held(&inode->v.i_rwsem);
+ mutex_lock(&inode->ei_update_lock);
- ret = setattr_prepare(dentry, iattr);
- if (ret)
- return ret;
+ 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;
+ }
- if (iattr->ia_valid & ATTR_SIZE) {
- ret = bch2_truncate(inode, iattr);
- } else {
- mutex_lock(&inode->ei_update_lock);
- setattr_copy(&inode->v, iattr);
- ret = bch2_write_inode(c, inode);
- mutex_unlock(&inode->ei_update_lock);
+ if (c->opts.grpquota &&
+ (iattr->ia_valid & ATTR_GID) &&
+ !gid_eq(iattr->ia_gid, inode->v.i_gid)) {
+ qid.q[QTYP_GRP] = from_kgid(&init_user_ns, iattr->ia_gid);
+ qtypes |= 1 << QTYP_GRP;
}
- if (unlikely(ret))
- return ret;
+ if (qtypes) {
+ ret = bch2_quota_transfer(c, qtypes, qid, inode->ei_qid,
+ inode->v.i_blocks +
+ inode->ei_quota_reserved);
+ if (ret)
+ goto out_unlock;
+ }
+
+ setattr_copy(&inode->v, iattr);
+
+ ret = bch2_write_inode(c, inode);
+out_unlock:
+ mutex_unlock(&inode->ei_update_lock);
- if (iattr->ia_valid & ATTR_MODE)
+ if (!ret &&
+ iattr->ia_valid & ATTR_MODE)
ret = posix_acl_chmod(&inode->v, inode->v.i_mode);
return ret;
}
+static int bch2_getattr(const struct path *path, struct kstat *stat,
+ u32 request_mask, unsigned query_flags)
+{
+ struct bch_inode_info *inode = to_bch_ei(d_inode(path->dentry));
+ struct bch_fs *c = inode->v.i_sb->s_fs_info;
+
+ stat->dev = inode->v.i_sb->s_dev;
+ stat->ino = inode->v.i_ino;
+ stat->mode = inode->v.i_mode;
+ stat->nlink = inode->v.i_nlink;
+ stat->uid = inode->v.i_uid;
+ stat->gid = inode->v.i_gid;
+ stat->rdev = inode->v.i_rdev;
+ stat->size = i_size_read(&inode->v);
+ stat->atime = inode->v.i_atime;
+ stat->mtime = inode->v.i_mtime;
+ stat->ctime = inode->v.i_ctime;
+ stat->blksize = block_bytes(c);
+ stat->blocks = inode->v.i_blocks;
+
+ if (request_mask & STATX_BTIME) {
+ stat->result_mask |= STATX_BTIME;
+ stat->btime = bch2_time_to_timespec(c, inode->ei_inode.bi_otime);
+ }
+
+ if (inode->ei_inode.bi_flags & BCH_INODE_IMMUTABLE)
+ stat->attributes |= STATX_ATTR_IMMUTABLE;
+ if (inode->ei_inode.bi_flags & BCH_INODE_APPEND)
+ stat->attributes |= STATX_ATTR_APPEND;
+ if (inode->ei_inode.bi_flags & BCH_INODE_NODUMP)
+ stat->attributes |= STATX_ATTR_NODUMP;
+
+ return 0;
+}
+
+static int bch2_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ struct bch_inode_info *inode = to_bch_ei(dentry->d_inode);
+ int ret;
+
+ lockdep_assert_held(&inode->v.i_rwsem);
+
+ ret = setattr_prepare(dentry, iattr);
+ if (ret)
+ return ret;
+
+ return iattr->ia_valid & ATTR_SIZE
+ ? bch2_truncate(inode, iattr)
+ : bch2_setattr_nonsize(inode, iattr);
+}
+
static int bch2_tmpfile(struct inode *vdir, struct dentry *dentry, umode_t mode)
{
struct bch_fs *c = vdir->i_sb->s_fs_info;
};
static const struct inode_operations bch_file_inode_operations = {
+ .getattr = bch2_getattr,
.setattr = bch2_setattr,
.fiemap = bch2_fiemap,
.listxattr = bch2_xattr_list,
.rmdir = bch2_rmdir,
.mknod = bch2_mknod,
.rename = bch2_rename2,
+ .getattr = bch2_getattr,
.setattr = bch2_setattr,
.tmpfile = bch2_tmpfile,
.listxattr = bch2_xattr_list,
static const struct inode_operations bch_symlink_inode_operations = {
.get_link = page_get_link,
+ .getattr = bch2_getattr,
.setattr = bch2_setattr,
.listxattr = bch2_xattr_list,
#ifdef CONFIG_BCACHEFS_POSIX_ACL
};
static const struct inode_operations bch_special_inode_operations = {
+ .getattr = bch2_getattr,
.setattr = bch2_setattr,
.listxattr = bch2_xattr_list,
#ifdef CONFIG_BCACHEFS_POSIX_ACL
inode->v.i_ctime = bch2_time_to_timespec(c, bi->bi_ctime);
inode->ei_journal_seq = 0;
+ inode->ei_quota_reserved = 0;
+ inode->ei_qid = bch_qid(bi);
inode->ei_str_hash = bch2_hash_info_init(c, bi);
inode->ei_inode = *bi;
clear_inode(&inode->v);
+ BUG_ON(!is_bad_inode(&inode->v) && inode->ei_quota_reserved);
+
if (!inode->v.i_nlink && !is_bad_inode(&inode->v)) {
+ bch2_quota_acct(c, inode->ei_qid, Q_SPC, -((s64) inode->v.i_blocks),
+ BCH_QUOTA_WARN);
+ bch2_quota_acct(c, inode->ei_qid, Q_INO, -1,
+ BCH_QUOTA_WARN);
bch2_inode_rm(c, inode->v.i_ino);
atomic_long_dec(&c->nr_inodes);
}
buf->f_type = BCACHEFS_STATFS_MAGIC;
buf->f_bsize = sb->s_blocksize;
buf->f_blocks = c->capacity >> PAGE_SECTOR_SHIFT;
- buf->f_bfree = (c->capacity - bch2_fs_sectors_used(c)) >> PAGE_SECTOR_SHIFT;
+ buf->f_bfree = bch2_fs_sectors_free(c, bch2_fs_usage_read(c)) >>
+ PAGE_SECTOR_SHIFT;
buf->f_bavail = buf->f_bfree;
buf->f_files = atomic_long_read(&c->nr_inodes);
buf->f_ffree = U64_MAX;
return bch2_journal_flush(&c->journal);
}
-static struct bch_fs *bch2_open_as_blockdevs(const char *_dev_name,
- struct bch_opts opts)
+static struct bch_fs *bch2_path_to_fs(const char *dev)
{
- size_t nr_devs = 0, i = 0;
- char *dev_name, *s, **devs;
- struct bch_fs *c = NULL;
- const char *err = "cannot allocate memory";
+ struct bch_fs *c;
+ struct block_device *bdev = lookup_bdev(dev);
- dev_name = kstrdup(_dev_name, GFP_KERNEL);
- if (!dev_name)
- return NULL;
+ if (IS_ERR(bdev))
+ return ERR_CAST(bdev);
- for (s = dev_name; s; s = strchr(s + 1, ':'))
- nr_devs++;
+ c = bch2_bdev_to_fs(bdev);
+ bdput(bdev);
+ return c ?: ERR_PTR(-ENOENT);
+}
- devs = kcalloc(nr_devs, sizeof(const char *), GFP_KERNEL);
- if (!devs)
- goto err;
+static struct bch_fs *__bch2_open_as_blockdevs(const char *dev_name, char * const *devs,
+ unsigned nr_devs, struct bch_opts opts)
+{
+ struct bch_fs *c, *c1, *c2;
+ size_t i;
- for (i = 0, s = dev_name;
- s;
- (s = strchr(s, ':')) && (*s++ = '\0'))
- devs[i++] = s;
+ if (!nr_devs)
+ return ERR_PTR(-EINVAL);
+
+ c = bch2_fs_open(devs, nr_devs, opts);
- err = bch2_fs_open(devs, nr_devs, opts, &c);
- if (err) {
+ if (IS_ERR(c) && PTR_ERR(c) == -EBUSY) {
/*
* Already open?
* Look up each block device, make sure they all belong to a
* filesystem and they all belong to the _same_ filesystem
*/
- for (i = 0; i < nr_devs; i++) {
- struct block_device *bdev = lookup_bdev(devs[i]);
- struct bch_fs *c2;
+ c1 = bch2_path_to_fs(devs[0]);
+ if (!c1)
+ return c;
- if (IS_ERR(bdev))
- goto err;
-
- c2 = bch2_bdev_to_fs(bdev);
- bdput(bdev);
-
- if (!c)
- c = c2;
- else if (c2)
+ for (i = 1; i < nr_devs; i++) {
+ c2 = bch2_path_to_fs(devs[i]);
+ if (!IS_ERR(c2))
closure_put(&c2->cl);
- if (!c)
- goto err;
- if (c != c2) {
- closure_put(&c->cl);
- goto err;
+ if (c1 != c2) {
+ closure_put(&c1->cl);
+ return c;
}
}
- mutex_lock(&c->state_lock);
+ c = c1;
+ }
- if (!bch2_fs_running(c)) {
- mutex_unlock(&c->state_lock);
- closure_put(&c->cl);
- err = "incomplete filesystem";
- c = NULL;
- goto err;
- }
+ if (IS_ERR(c))
+ return c;
+
+ mutex_lock(&c->state_lock);
+ if (!bch2_fs_running(c)) {
mutex_unlock(&c->state_lock);
+ closure_put(&c->cl);
+ pr_err("err mounting %s: incomplete filesystem", dev_name);
+ return ERR_PTR(-EINVAL);
}
+ mutex_unlock(&c->state_lock);
+
set_bit(BCH_FS_BDEV_MOUNTED, &c->flags);
+ return c;
+}
+
+static struct bch_fs *bch2_open_as_blockdevs(const char *_dev_name,
+ struct bch_opts opts)
+{
+ char *dev_name = NULL, **devs = NULL, *s;
+ struct bch_fs *c = ERR_PTR(-ENOMEM);
+ size_t i, nr_devs = 0;
+
+ dev_name = kstrdup(_dev_name, GFP_KERNEL);
+ if (!dev_name)
+ goto err;
+
+ for (s = dev_name; s; s = strchr(s + 1, ':'))
+ nr_devs++;
+
+ devs = kcalloc(nr_devs, sizeof(const char *), GFP_KERNEL);
+ if (!devs)
+ goto err;
+
+ for (i = 0, s = dev_name;
+ s;
+ (s = strchr(s, ':')) && (*s++ = '\0'))
+ devs[i++] = s;
+
+ c = __bch2_open_as_blockdevs(_dev_name, devs, nr_devs, opts);
err:
kfree(devs);
kfree(dev_name);
-
- if (!c)
- pr_err("bch_fs_open err %s", err);
return c;
}
return ERR_PTR(ret);
c = bch2_open_as_blockdevs(dev_name, opts);
- if (!c)
- return ERR_PTR(-ENOENT);
+ if (IS_ERR(c))
+ return ERR_CAST(c);
sb = sget(fs_type, bch2_test_super, bch2_set_super, flags|MS_NOSEC, c);
if (IS_ERR(sb)) {
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_op = &bch_super_operations;
sb->s_export_op = &bch_export_ops;
+#ifdef CONFIG_BCACHEFS_QUOTA
+ sb->s_qcop = &bch2_quotactl_operations;
+ sb->s_quota_types = QTYPE_MASK_USR|QTYPE_MASK_GRP|QTYPE_MASK_PRJ;
+#endif
sb->s_xattr = bch2_xattr_handlers;
sb->s_magic = BCACHEFS_STATFS_MAGIC;
sb->s_time_gran = c->sb.time_precision;