]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
cifs: add shutdown support
authorSteve French <stfrench@microsoft.com>
Thu, 29 Apr 2021 05:18:43 +0000 (00:18 -0500)
committerSteve French <stfrench@microsoft.com>
Mon, 3 May 2021 16:21:22 +0000 (11:21 -0500)
Various filesystem support the shutdown ioctl which is used by various
xfstests. The shutdown ioctl sets a flag on the superblock which
prevents open, unlink, symlink, hardlink, rmdir, create etc.
on the file system until unmount and remounted. The two flags supported
in this patch are:

  FSOP_GOING_FLAGS_LOGFLUSH and FSOP_GOING_FLAGS_NOLOGFLUSH

which require very little other than blocking new operations (since
we do not cache writes to metadata on the client with cifs.ko).
FSOP_GOING_FLAGS_DEFAULT is not supported yet, but could be added in
the future but would need to call syncfs or equivalent to write out
pending data on the mount.

With this patch various xfstests now work including tests 043 through
046 for example.

Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
fs/cifs/cifs_fs_sb.h
fs/cifs/cifs_ioctl.h
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/fs_context.c
fs/cifs/inode.c
fs/cifs/ioctl.c
fs/cifs/link.c
fs/cifs/xattr.c

index 2a5325a7ae49e3171ee95b6416b639d6b338de7b..9c45b3a82ad9e441e9967268a87f6f9b9280d548 100644 (file)
@@ -55,6 +55,7 @@
 #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */
 #define CIFS_MOUNT_RO_CACHE    0x20000000  /* assumes share will not change */
 #define CIFS_MOUNT_RW_CACHE    0x40000000  /* assumes only client accessing */
+#define CIFS_MOUNT_SHUTDOWN    0x80000000
 
 struct cifs_sb_info {
        struct rb_root tlink_tree;
index 153d5c842a9bcaa9613d87de79b51849e30378de..f262c64516bc724f218627fa5ad3264c76642861 100644 (file)
@@ -78,3 +78,19 @@ struct smb3_notify {
 #define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
 #define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
 #define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
+#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32)
+
+/*
+ * Flags for going down operation
+ */
+#define CIFS_GOING_FLAGS_DEFAULT                0x0     /* going down */
+#define CIFS_GOING_FLAGS_LOGFLUSH               0x1     /* flush log but not data */
+#define CIFS_GOING_FLAGS_NOLOGFLUSH             0x2     /* don't flush log nor data */
+
+static inline bool cifs_forced_shutdown(struct cifs_sb_info *sbi)
+{
+       if (CIFS_MOUNT_SHUTDOWN & sbi->mnt_cifs_flags)
+               return true;
+       else
+               return false;
+}
index 03afad8b24af728cf1031a5215b875bcbb1ef524..263720131986c7a738a0b07a6a7e50f1201141c3 100644 (file)
@@ -34,6 +34,7 @@
 #include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
 #include "fs_context.h"
+#include "cifs_ioctl.h"
 
 static void
 renew_parental_timestamps(struct dentry *direntry)
@@ -429,6 +430,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
        __u32 oplock;
        struct cifsFileInfo *file_info;
 
+       if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
+               return -EIO;
+
        /*
         * Posix open is only called (at lookup time) for file create now. For
         * opens (rather than creates), because we do not know if it is a file
@@ -545,6 +549,9 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
        cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
                 inode, direntry, direntry);
 
+       if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
+               return -EIO;
+
        tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
        rc = PTR_ERR(tlink);
        if (IS_ERR(tlink))
@@ -582,6 +589,9 @@ int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode,
                return -EINVAL;
 
        cifs_sb = CIFS_SB(inode->i_sb);
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
index 7e97aeabd61617b5cbb902a4eec937f0f167614e..c95893351b6c22a0c8225f554480f48e9a221f8d 100644 (file)
@@ -45,6 +45,7 @@
 #include "fscache.h"
 #include "smbdirect.h"
 #include "fs_context.h"
+#include "cifs_ioctl.h"
 
 static inline int cifs_convert_flags(unsigned int flags)
 {
@@ -542,6 +543,11 @@ int cifs_open(struct inode *inode, struct file *file)
        xid = get_xid();
 
        cifs_sb = CIFS_SB(inode->i_sb);
+       if (unlikely(cifs_forced_shutdown(cifs_sb))) {
+               free_xid(xid);
+               return -EIO;
+       }
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink)) {
                free_xid(xid);
index 3e0d016849e302d2060c749cbbedadab6875f5e9..1d6e0e15b034204ad73e7fd9eb7ca0cd62b2258a 100644 (file)
@@ -1642,6 +1642,7 @@ void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb)
                        cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
                }
        }
+       cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN;
 
        return;
 }
index b4326ffcefced4b2e019e30696fd20082d6fc892..728ff45b6667d3a1ac4aadf06f849a65876e0e0b 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/sched/signal.h>
 #include <linux/wait_bit.h>
 #include <linux/fiemap.h>
-
 #include <asm/div64.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -38,7 +37,7 @@
 #include "cifs_unicode.h"
 #include "fscache.h"
 #include "fs_context.h"
-
+#include "cifs_ioctl.h"
 
 static void cifs_set_ops(struct inode *inode)
 {
@@ -1623,6 +1622,9 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
 
        cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
 
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
@@ -1876,6 +1878,8 @@ int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode,
                 mode, inode);
 
        cifs_sb = CIFS_SB(inode->i_sb);
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
@@ -1958,6 +1962,11 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
        }
 
        cifs_sb = CIFS_SB(inode->i_sb);
+       if (unlikely(cifs_forced_shutdown(cifs_sb))) {
+               rc = -EIO;
+               goto rmdir_exit;
+       }
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink)) {
                rc = PTR_ERR(tlink);
@@ -2092,6 +2101,9 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
                return -EINVAL;
 
        cifs_sb = CIFS_SB(source_dir->i_sb);
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
@@ -2408,6 +2420,9 @@ int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path,
        struct inode *inode = d_inode(dentry);
        int rc;
 
+       if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
+               return -EIO;
+
        /*
         * We need to be sure that all dirty pages are written and the server
         * has actual ctime, mtime and file length.
@@ -2480,6 +2495,9 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
        struct cifsFileInfo *cfile;
        int rc;
 
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        /*
         * We need to be sure that all dirty pages are written as they
         * might fill holes on the server.
@@ -2966,6 +2984,9 @@ cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry,
        struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);
        int rc, retries = 0;
 
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        do {
                if (pTcon->unix_ext)
                        rc = cifs_setattr_unix(direntry, attrs);
index 08d99fec593e5194dc483781e055c17dc3809df4..ef41fa878793abd626836c390fb2183fec5d8199 100644 (file)
@@ -164,6 +164,56 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
+static int cifs_shutdown(struct super_block *sb, unsigned long arg)
+{
+       struct cifs_sb_info *sbi = CIFS_SB(sb);
+       __u32 flags;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (get_user(flags, (__u32 __user *)arg))
+               return -EFAULT;
+
+       if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH)
+               return -EINVAL;
+
+       if (cifs_forced_shutdown(sbi))
+               return 0;
+
+       cifs_dbg(VFS, "shut down requested (%d)", flags);
+/*     trace_cifs_shutdown(sb, flags);*/
+
+       /*
+        * see:
+        *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
+        * for more information and description of original intent of the flags
+        */
+       switch (flags) {
+       /*
+        * We could add support later for default flag which requires:
+        *     "Flush all dirty data and metadata to disk"
+        * would need to call syncfs or equivalent to flush page cache for
+        * the mount and then issue fsync to server (if nostrictsync not set)
+        */
+       case CIFS_GOING_FLAGS_DEFAULT:
+               cifs_dbg(FYI, "shutdown with default flag not supported\n");
+               return -EINVAL;
+       /*
+        * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not
+        * data) but metadata writes are not cached on the client, so can treat
+        * it similarly to NOLOGFLUSH
+        */
+       case CIFS_GOING_FLAGS_LOGFLUSH:
+       case CIFS_GOING_FLAGS_NOLOGFLUSH:
+               sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 {
        struct inode *inode = file_inode(filep);
@@ -325,6 +375,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
                                rc = -EOPNOTSUPP;
                        cifs_put_tlink(tlink);
                        break;
+               case CIFS_IOC_SHUTDOWN:
+                       rc = cifs_shutdown(inode->i_sb, arg);
+                       break;
                default:
                        cifs_dbg(FYI, "unsupported ioctl\n");
                        break;
index 616e1bc0cc0a05f29c470924c7505959d7d116e2..1cbe7ec7372838783b6e880e11db2b6f32db121e 100644 (file)
@@ -30,6 +30,7 @@
 #include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
 #include "smb2proto.h"
+#include "cifs_ioctl.h"
 
 /*
  * M-F Symlink Functions - Begin
@@ -518,6 +519,9 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
        struct TCP_Server_Info *server;
        struct cifsInodeInfo *cifsInode;
 
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
@@ -682,6 +686,9 @@ cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
        void *page = alloc_dentry_path();
        struct inode *newinode = NULL;
 
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        xid = get_xid();
 
        tlink = cifs_sb_tlink(cifs_sb);
index e351b945135bb4e938aff6671804f78e292d2ab1..aa3e8ca0457cd5e83532929e92e6a3a22f36a106 100644 (file)
@@ -30,6 +30,7 @@
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
+#include "cifs_ioctl.h"
 
 #define MAX_EA_VALUE_SIZE CIFSMaxBufSize
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */
@@ -421,6 +422,9 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
        const char *full_path;
        void *page;
 
+       if (unlikely(cifs_forced_shutdown(cifs_sb)))
+               return -EIO;
+
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                return -EOPNOTSUPP;