bcachefs: Improve FS_IOC_GOINGDOWN ioctl
authorKent Overstreet <kent.overstreet@gmail.com>
Sun, 23 May 2021 01:13:17 +0000 (21:13 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:04 +0000 (17:09 -0400)
We weren't interpreting the flags argument at all.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/fs-ioctl.c

index 09a9567b402c34e0b31e584ac066bab04e332ab0..6d6368555875094c6f2a7e6dac4c2f66024b7045 100644 (file)
@@ -13,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;
@@ -247,11 +250,54 @@ err1:
        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:
@@ -276,15 +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 |= SB_RDONLY;
-               if (bch2_fs_emergency_read_only(c))
-                       bch_err(c, "emergency read only due to ioctl");
-               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);