]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/fs-ioctl.c
Update bcachefs sources to 3913e0cac3 bcachefs: Journal space calculation fix
[bcachefs-tools-debian] / libbcachefs / fs-ioctl.c
index 0eb0a0112a84f8c8a4daf7e5d42a4eba46acb924..d8cc32e043df83d3d22d1ae55aded1160e3585d2 100644 (file)
@@ -1,8 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
 #ifndef NO_BCACHEFS_FS
 
 #include "bcachefs.h"
 #include "chardev.h"
+#include "dirent.h"
 #include "fs.h"
+#include "fs-common.h"
 #include "fs-ioctl.h"
 #include "quota.h"
 
@@ -10,6 +13,9 @@
 #include <linux/mount.h>
 
 #define FS_IOC_GOINGDOWN            _IOR('X', 125, __u32)
+#define FSOP_GOING_FLAGS_DEFAULT       0x0     /* going down */
+#define FSOP_GOING_FLAGS_LOGFLUSH      0x1     /* flush log but not data */
+#define FSOP_GOING_FLAGS_NOLOGFLUSH    0x2     /* don't flush log nor data */
 
 struct flags_set {
        unsigned                mask;
@@ -102,35 +108,16 @@ static int bch2_ioc_fsgetxattr(struct bch_inode_info *inode,
        return copy_to_user(arg, &fa, sizeof(fa));
 }
 
-static int bch2_set_projid(struct bch_fs *c,
-                          struct bch_inode_info *inode,
-                          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;
-
-       return 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;
-}
-
 static int fssetxattr_inode_update_fn(struct bch_inode_info *inode,
                                      struct bch_inode_unpacked *bi,
                                      void *p)
 {
        struct flags_set *s = p;
 
-       bi->bi_project = s->projid;
+       if (s->projid != bi->bi_project) {
+               bi->bi_fields_set |= 1U << Inode_opt_project;
+               bi->bi_project = s->projid;
+       }
 
        return bch2_inode_flags_set(inode, bi, p);
 }
@@ -151,7 +138,14 @@ static int bch2_ioc_fssetxattr(struct bch_fs *c,
        if (fa.fsx_xflags)
                return -EOPNOTSUPP;
 
-       s.projid = fa.fsx_projid;
+       if (fa.fsx_projid >= U32_MAX)
+               return -EINVAL;
+
+       /*
+        * inode fields accessible via the xattr interface are stored with a +1
+        * bias, so that 0 means unset:
+        */
+       s.projid = fa.fsx_projid + 1;
 
        ret = mnt_want_write_file(file);
        if (ret)
@@ -178,11 +172,132 @@ err:
        return ret;
 }
 
+static int bch2_reinherit_attrs_fn(struct bch_inode_info *inode,
+                                  struct bch_inode_unpacked *bi,
+                                  void *p)
+{
+       struct bch_inode_info *dir = p;
+
+       return !bch2_reinherit_attrs(bi, &dir->ei_inode);
+}
+
+static int bch2_ioc_reinherit_attrs(struct bch_fs *c,
+                                   struct file *file,
+                                   struct bch_inode_info *src,
+                                   const char __user *name)
+{
+       struct bch_hash_info hash = bch2_hash_info_init(c, &src->ei_inode);
+       struct bch_inode_info *dst;
+       struct inode *vinode = NULL;
+       char *kname = NULL;
+       struct qstr qstr;
+       int ret = 0;
+       u64 inum;
+
+       kname = kmalloc(BCH_NAME_MAX + 1, GFP_KERNEL);
+       if (!kname)
+               return -ENOMEM;
+
+       ret = strncpy_from_user(kname, name, BCH_NAME_MAX);
+       if (unlikely(ret < 0))
+               goto err1;
+
+       qstr.len        = ret;
+       qstr.name       = kname;
+
+       ret = -ENOENT;
+       inum = bch2_dirent_lookup(c, src->v.i_ino, &hash,
+                                 &qstr);
+       if (!inum)
+               goto err1;
+
+       vinode = bch2_vfs_inode_get(c, inum);
+       ret = PTR_ERR_OR_ZERO(vinode);
+       if (ret)
+               goto err1;
+
+       dst = to_bch_ei(vinode);
+
+       ret = mnt_want_write_file(file);
+       if (ret)
+               goto err2;
+
+       bch2_lock_inodes(INODE_UPDATE_LOCK, 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(INODE_UPDATE_LOCK, src, dst);
+
+       /* return true if we did work */
+       if (ret >= 0)
+               ret = !ret;
+
+       mnt_drop_write_file(file);
+err2:
+       iput(vinode);
+err1:
+       kfree(kname);
+
+       return ret;
+}
+
+static int bch2_ioc_goingdown(struct bch_fs *c, u32 __user *arg)
+{
+       u32 flags;
+       int ret = 0;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (get_user(flags, arg))
+               return -EFAULT;
+
+       bch_notice(c, "shutdown by ioctl type %u", flags);
+
+       down_write(&c->vfs_sb->s_umount);
+
+       switch (flags) {
+       case FSOP_GOING_FLAGS_DEFAULT:
+               ret = freeze_bdev(c->vfs_sb->s_bdev);
+               if (ret)
+                       goto err;
+
+               bch2_journal_flush(&c->journal);
+               c->vfs_sb->s_flags |= SB_RDONLY;
+               bch2_fs_emergency_read_only(c);
+               thaw_bdev(c->vfs_sb->s_bdev);
+               break;
+
+       case FSOP_GOING_FLAGS_LOGFLUSH:
+               bch2_journal_flush(&c->journal);
+               fallthrough;
+
+       case FSOP_GOING_FLAGS_NOLOGFLUSH:
+               c->vfs_sb->s_flags |= SB_RDONLY;
+               bch2_fs_emergency_read_only(c);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+err:
+       up_write(&c->vfs_sb->s_umount);
+       return ret;
+}
+
 long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
        struct bch_inode_info *inode = file_bch_inode(file);
-       struct super_block *sb = inode->v.i_sb;
-       struct bch_fs *c = sb->s_fs_info;
+       struct bch_fs *c = inode->v.i_sb->s_fs_info;
 
        switch (cmd) {
        case FS_IOC_GETFLAGS:
@@ -194,7 +309,12 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
        case FS_IOC_FSGETXATTR:
                return bch2_ioc_fsgetxattr(inode, (void __user *) arg);
        case FS_IOC_FSSETXATTR:
-               return bch2_ioc_fssetxattr(c, file, inode, (void __user *) arg);
+               return bch2_ioc_fssetxattr(c, file, inode,
+                                          (void __user *) arg);
+
+       case BCHFS_IOC_REINHERIT_ATTRS:
+               return bch2_ioc_reinherit_attrs(c, file, inode,
+                                               (void __user *) arg);
 
        case FS_IOC_GETVERSION:
                return -ENOTTY;
@@ -202,14 +322,7 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                return -ENOTTY;
 
        case FS_IOC_GOINGDOWN:
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-
-               down_write(&sb->s_umount);
-               sb->s_flags |= MS_RDONLY;
-               bch2_fs_emergency_read_only(c);
-               up_write(&sb->s_umount);
-               return 0;
+               return bch2_ioc_goingdown(c, (u32 __user *) arg);
 
        default:
                return bch2_fs_ioctl(c, cmd, (void __user *) arg);