Commit 087f757b authored by Steve French's avatar Steve French

cifs: add shutdown support

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: default avatarSteve French <stfrench@microsoft.com>
Reviewed-by: default avatarAurelien Aptel <aaptel@suse.com>
parent c3f207ab
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ #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_RO_CACHE 0x20000000 /* assumes share will not change */
#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */ #define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */
#define CIFS_MOUNT_SHUTDOWN 0x80000000
struct cifs_sb_info { struct cifs_sb_info {
struct rb_root tlink_tree; struct rb_root tlink_tree;
......
...@@ -78,3 +78,19 @@ struct smb3_notify { ...@@ -78,3 +78,19 @@ struct smb3_notify {
#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info) #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_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_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;
}
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "fs_context.h" #include "fs_context.h"
#include "cifs_ioctl.h"
static void static void
renew_parental_timestamps(struct dentry *direntry) renew_parental_timestamps(struct dentry *direntry)
...@@ -429,6 +430,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -429,6 +430,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
__u32 oplock; __u32 oplock;
struct cifsFileInfo *file_info; 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 * 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 * 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, ...@@ -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", cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
inode, direntry, direntry); inode, direntry, direntry);
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
return -EIO;
tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
rc = PTR_ERR(tlink); rc = PTR_ERR(tlink);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
...@@ -582,6 +589,9 @@ int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -582,6 +589,9 @@ int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode,
return -EINVAL; return -EINVAL;
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "fscache.h" #include "fscache.h"
#include "smbdirect.h" #include "smbdirect.h"
#include "fs_context.h" #include "fs_context.h"
#include "cifs_ioctl.h"
static inline int cifs_convert_flags(unsigned int flags) static inline int cifs_convert_flags(unsigned int flags)
{ {
...@@ -542,6 +543,11 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -542,6 +543,11 @@ int cifs_open(struct inode *inode, struct file *file)
xid = get_xid(); xid = get_xid();
cifs_sb = CIFS_SB(inode->i_sb); 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); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) { if (IS_ERR(tlink)) {
free_xid(xid); free_xid(xid);
......
...@@ -1642,6 +1642,7 @@ void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb) ...@@ -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_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
} }
} }
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN;
return; return;
} }
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/wait_bit.h> #include <linux/wait_bit.h>
#include <linux/fiemap.h> #include <linux/fiemap.h>
#include <asm/div64.h> #include <asm/div64.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifspdu.h" #include "cifspdu.h"
...@@ -38,7 +37,7 @@ ...@@ -38,7 +37,7 @@
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "fscache.h" #include "fscache.h"
#include "fs_context.h" #include "fs_context.h"
#include "cifs_ioctl.h"
static void cifs_set_ops(struct inode *inode) static void cifs_set_ops(struct inode *inode)
{ {
...@@ -1623,6 +1622,9 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -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); 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); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
...@@ -1876,6 +1878,8 @@ int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -1876,6 +1878,8 @@ int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode,
mode, inode); mode, inode);
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
...@@ -1958,6 +1962,11 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) ...@@ -1958,6 +1962,11 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
} }
cifs_sb = CIFS_SB(inode->i_sb); 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); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) { if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink); rc = PTR_ERR(tlink);
...@@ -2092,6 +2101,9 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, ...@@ -2092,6 +2101,9 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
return -EINVAL; return -EINVAL;
cifs_sb = CIFS_SB(source_dir->i_sb); cifs_sb = CIFS_SB(source_dir->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
...@@ -2408,6 +2420,9 @@ int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path, ...@@ -2408,6 +2420,9 @@ int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path,
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
int rc; 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 * We need to be sure that all dirty pages are written and the server
* has actual ctime, mtime and file length. * has actual ctime, mtime and file length.
...@@ -2480,6 +2495,9 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, ...@@ -2480,6 +2495,9 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
int rc; int rc;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
/* /*
* We need to be sure that all dirty pages are written as they * We need to be sure that all dirty pages are written as they
* might fill holes on the server. * might fill holes on the server.
...@@ -2966,6 +2984,9 @@ cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry, ...@@ -2966,6 +2984,9 @@ cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry,
struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);
int rc, retries = 0; int rc, retries = 0;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
do { do {
if (pTcon->unix_ext) if (pTcon->unix_ext)
rc = cifs_setattr_unix(direntry, attrs); rc = cifs_setattr_unix(direntry, attrs);
......
...@@ -164,6 +164,56 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, ...@@ -164,6 +164,56 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
return rc; 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) long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
{ {
struct inode *inode = file_inode(filep); struct inode *inode = file_inode(filep);
...@@ -325,6 +375,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -325,6 +375,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
break; break;
case CIFS_IOC_SHUTDOWN:
rc = cifs_shutdown(inode->i_sb, arg);
break;
default: default:
cifs_dbg(FYI, "unsupported ioctl\n"); cifs_dbg(FYI, "unsupported ioctl\n");
break; break;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "smb2proto.h" #include "smb2proto.h"
#include "cifs_ioctl.h"
/* /*
* M-F Symlink Functions - Begin * M-F Symlink Functions - Begin
...@@ -518,6 +519,9 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, ...@@ -518,6 +519,9 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifsInodeInfo *cifsInode; struct cifsInodeInfo *cifsInode;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
...@@ -682,6 +686,9 @@ cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -682,6 +686,9 @@ cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
void *page = alloc_dentry_path(); void *page = alloc_dentry_path();
struct inode *newinode = NULL; struct inode *newinode = NULL;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
xid = get_xid(); xid = get_xid();
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "cifs_ioctl.h"
#define MAX_EA_VALUE_SIZE CIFSMaxBufSize #define MAX_EA_VALUE_SIZE CIFSMaxBufSize
#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ #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) ...@@ -421,6 +422,9 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
const char *full_path; const char *full_path;
void *page; void *page;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment