]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/fs.c
Update bcachefs sources to e99d29e402 bcachefs: zstd support, compression refactoring
[bcachefs-tools-debian] / libbcachefs / fs.c
index cb0397f1343beac8186a18e703d256f642b25eab..80962b5d16a10348fad375f1de1abd1bb3da9358 100644 (file)
@@ -15,6 +15,7 @@
 #include "io.h"
 #include "journal.h"
 #include "keylist.h"
+#include "quota.h"
 #include "super.h"
 #include "xattr.h"
 
@@ -84,10 +85,10 @@ int __must_check __bch2_write_inode(struct bch_fs *c,
        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;
@@ -116,6 +117,7 @@ int __must_check __bch2_write_inode(struct bch_fs *c,
                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);
@@ -131,8 +133,10 @@ int __must_check __bch2_write_inode(struct bch_fs *c,
                                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);
 
@@ -213,10 +217,8 @@ static struct bch_inode_info *bch2_vfs_inode_create(struct bch_fs *c,
 
 #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,
@@ -225,19 +227,20 @@ static struct bch_inode_info *bch2_vfs_inode_create(struct bch_fs *c,
                        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);
@@ -252,11 +255,18 @@ static struct bch_inode_info *bch2_vfs_inode_create(struct bch_fs *c,
        }
 
        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);
@@ -604,36 +614,101 @@ static int bch2_rename2(struct inode *old_vdir, struct dentry *old_dentry,
        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;
@@ -777,6 +852,7 @@ static const struct file_operations bch_file_operations = {
 };
 
 static const struct inode_operations bch_file_inode_operations = {
+       .getattr        = bch2_getattr,
        .setattr        = bch2_setattr,
        .fiemap         = bch2_fiemap,
        .listxattr      = bch2_xattr_list,
@@ -796,6 +872,7 @@ static const struct inode_operations bch_dir_inode_operations = {
        .rmdir          = bch2_rmdir,
        .mknod          = bch2_mknod,
        .rename         = bch2_rename2,
+       .getattr        = bch2_getattr,
        .setattr        = bch2_setattr,
        .tmpfile        = bch2_tmpfile,
        .listxattr      = bch2_xattr_list,
@@ -818,6 +895,7 @@ static const struct file_operations bch_dir_file_operations = {
 
 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
@@ -827,6 +905,7 @@ static const struct inode_operations bch_symlink_inode_operations = {
 };
 
 static const struct inode_operations bch_special_inode_operations = {
+       .getattr        = bch2_getattr,
        .setattr        = bch2_setattr,
        .listxattr      = bch2_xattr_list,
 #ifdef CONFIG_BCACHEFS_POSIX_ACL
@@ -910,6 +989,8 @@ static void bch2_vfs_inode_init(struct bch_fs *c,
        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;
 
@@ -994,7 +1075,13 @@ static void bch2_evict_inode(struct inode *vinode)
 
        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);
        }
@@ -1009,7 +1096,8 @@ static int bch2_statfs(struct dentry *dentry, struct kstatfs *buf)
        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;
@@ -1035,81 +1123,100 @@ static int bch2_sync_fs(struct super_block *sb, int wait)
        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;
 }
 
@@ -1232,8 +1339,8 @@ static struct dentry *bch2_mount(struct file_system_type *fs_type,
                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)) {
@@ -1259,6 +1366,10 @@ static struct dentry *bch2_mount(struct file_system_type *fs_type,
        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;