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 @@
#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;
......
......@@ -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;
}
......@@ -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);
......
......@@ -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);
......
......@@ -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;
}
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......
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