Commit 7c9e41e0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.13-rc-smb3-part2' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs updates from Steve French:
 "Ten CIFS/SMB3 changes - including two marked for stable - including
  some important multichannel fixes, as well as support for handle
  leases (deferred close) and shutdown support:

   - some important multichannel fixes

   - support for handle leases (deferred close)

   - shutdown support (which is also helpful since it enables multiple
     xfstests)

   - enable negotiating stronger encryption by default (GCM256)

   - improve wireshark debugging by allowing more options for root to
     dump decryption keys

  SambaXP and the SMB3 Plugfest test event are going on now so I am
  expecting more patches over the next few days due to extra testing
  (including more multichannel fixes)"

* tag '5.13-rc-smb3-part2' of git://git.samba.org/sfrench/cifs-2.6:
  fs/cifs: Fix resource leak
  Cifs: Fix kernel oops caused by deferred close for files.
  cifs: fix regression when mounting shares with prefix paths
  cifs: use echo_interval even when connection not ready.
  cifs: detect dead connections only when echoes are enabled.
  smb3.1.1: allow dumping keys for multiuser mounts
  smb3.1.1: allow dumping GCM256 keys to improve debugging of encrypted shares
  cifs: add shutdown support
  cifs: Deferred close for files
  smb3.1.1: enable negotiating stronger encryption by default
parents 16bb86b5 bae4c0c1
......@@ -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;
......
......@@ -57,6 +57,12 @@ struct smb_query_info {
/* char buffer[]; */
} __packed;
/*
* Dumping the commonly used 16 byte (e.g. CCM and GCM128) keys still supported
* for backlevel compatibility, but is not sufficient for dumping the less
* frequently used GCM256 (32 byte) keys (see the newer "CIFS_DUMP_FULL_KEY"
* ioctl for dumping decryption info for GCM256 mounts)
*/
struct smb3_key_debug_info {
__u64 Suid;
__u16 cipher_type;
......@@ -65,6 +71,18 @@ struct smb3_key_debug_info {
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
} __packed;
/*
* Dump full key (32 byte encrypt/decrypt keys instead of 16 bytes)
* is needed if GCM256 (stronger encryption) negotiated
*/
struct smb3_full_key_debug_info {
__u64 Suid;
__u16 cipher_type;
__u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */
__u8 smb3encryptionkey[32]; /* SMB3_ENC_DEC_KEY_SIZE */
__u8 smb3decryptionkey[32]; /* SMB3_ENC_DEC_KEY_SIZE */
} __packed;
struct smb3_notify {
__u32 completion_filter;
bool watch_tree;
......@@ -78,3 +96,20 @@ 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_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info)
#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;
}
......@@ -75,7 +75,7 @@ bool enable_oplocks = true;
bool linuxExtEnabled = true;
bool lookupCacheEnabled = true;
bool disable_legacy_dialects; /* false by default */
bool enable_gcm_256; /* false by default, change when more servers support it */
bool enable_gcm_256 = true;
bool require_gcm_256; /* false by default */
unsigned int global_secflags = CIFSSEC_DEF;
/* unsigned int ntlmv2_support = 0; */
......@@ -133,6 +133,7 @@ struct workqueue_struct *cifsiod_wq;
struct workqueue_struct *decrypt_wq;
struct workqueue_struct *fileinfo_put_wq;
struct workqueue_struct *cifsoplockd_wq;
struct workqueue_struct *deferredclose_wq;
__u32 cifs_lock_secret;
/*
......@@ -390,6 +391,8 @@ cifs_alloc_inode(struct super_block *sb)
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
INIT_LIST_HEAD(&cifs_inode->openFileList);
INIT_LIST_HEAD(&cifs_inode->llist);
INIT_LIST_HEAD(&cifs_inode->deferred_closes);
spin_lock_init(&cifs_inode->deferred_lock);
return &cifs_inode->vfs_inode;
}
......@@ -860,13 +863,7 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
goto out;
}
/* cifs_setup_volume_info->smb3_parse_devname() redups UNC & prepath */
kfree(cifs_sb->ctx->UNC);
cifs_sb->ctx->UNC = NULL;
kfree(cifs_sb->ctx->prepath);
cifs_sb->ctx->prepath = NULL;
rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, old_ctx->UNC);
rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL);
if (rc) {
root = ERR_PTR(rc);
goto out;
......@@ -1637,9 +1634,16 @@ init_cifs(void)
goto out_destroy_fileinfo_put_wq;
}
deferredclose_wq = alloc_workqueue("deferredclose",
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!deferredclose_wq) {
rc = -ENOMEM;
goto out_destroy_cifsoplockd_wq;
}
rc = cifs_fscache_register();
if (rc)
goto out_destroy_cifsoplockd_wq;
goto out_destroy_deferredclose_wq;
rc = cifs_init_inodecache();
if (rc)
......@@ -1707,6 +1711,8 @@ init_cifs(void)
cifs_destroy_inodecache();
out_unreg_fscache:
cifs_fscache_unregister();
out_destroy_deferredclose_wq:
destroy_workqueue(deferredclose_wq);
out_destroy_cifsoplockd_wq:
destroy_workqueue(cifsoplockd_wq);
out_destroy_fileinfo_put_wq:
......@@ -1741,6 +1747,7 @@ exit_cifs(void)
cifs_destroy_mids();
cifs_destroy_inodecache();
cifs_fscache_unregister();
destroy_workqueue(deferredclose_wq);
destroy_workqueue(cifsoplockd_wq);
destroy_workqueue(decrypt_wq);
destroy_workqueue(fileinfo_put_wq);
......
......@@ -1154,6 +1154,14 @@ struct cifs_pending_open {
__u32 oplock;
};
struct cifs_deferred_close {
struct list_head dlist;
struct tcon_link *tlink;
__u16 netfid;
__u64 persistent_fid;
__u64 volatile_fid;
};
/*
* This info hangs off the cifsFileInfo structure, pointed to by llist.
* This is used to track byte stream locks on the file
......@@ -1248,6 +1256,9 @@ struct cifsFileInfo {
struct cifs_search_info srch_inf;
struct work_struct oplock_break; /* work for oplock breaks */
struct work_struct put; /* work for the final part of _put */
struct delayed_work deferred;
bool oplock_break_received; /* Flag to indicate oplock break */
bool deferred_scheduled;
};
struct cifs_io_parms {
......@@ -1392,6 +1403,7 @@ struct cifsInodeInfo {
#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */
unsigned long flags;
spinlock_t writers_lock;
unsigned int writers; /* Number of writers on this inode */
......@@ -1404,6 +1416,8 @@ struct cifsInodeInfo {
struct fscache_cookie *fscache;
#endif
struct inode vfs_inode;
struct list_head deferred_closes; /* list of deferred closes */
spinlock_t deferred_lock; /* protection on deferred list */
};
static inline struct cifsInodeInfo *
......@@ -1871,11 +1885,14 @@ extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */
void cifs_oplock_break(struct work_struct *work);
void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
void smb2_deferred_work_close(struct work_struct *work);
extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq;
extern struct workqueue_struct *decrypt_wq;
extern struct workqueue_struct *fileinfo_put_wq;
extern struct workqueue_struct *cifsoplockd_wq;
extern struct workqueue_struct *deferredclose_wq;
extern __u32 cifs_lock_secret;
extern mempool_t *cifs_mid_poolp;
......
......@@ -267,6 +267,19 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
struct tcon_link *tlink,
struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open);
extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile,
struct cifs_deferred_close **dclose);
extern void cifs_add_deferred_close(struct cifsFileInfo *cfile,
struct cifs_deferred_close *dclose);
extern void cifs_del_deferred_close(struct cifsFileInfo *cfile);
extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
int from_reconnect);
......
......@@ -392,16 +392,6 @@ cifs_echo_request(struct work_struct *work)
int rc;
struct TCP_Server_Info *server = container_of(work,
struct TCP_Server_Info, echo.work);
unsigned long echo_interval;
/*
* If we need to renegotiate, set echo interval to zero to
* immediately call echo service where we can renegotiate.
*/
if (server->tcpStatus == CifsNeedNegotiate)
echo_interval = 0;
else
echo_interval = server->echo_interval;
/*
* We cannot send an echo if it is disabled.
......@@ -412,7 +402,7 @@ cifs_echo_request(struct work_struct *work)
server->tcpStatus == CifsExiting ||
server->tcpStatus == CifsNew ||
(server->ops->can_echo && !server->ops->can_echo(server)) ||
time_before(jiffies, server->lstrp + echo_interval - HZ))
time_before(jiffies, server->lstrp + server->echo_interval - HZ))
goto requeue_echo;
rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
......@@ -476,6 +466,7 @@ server_unresponsive(struct TCP_Server_Info *server)
*/
if ((server->tcpStatus == CifsGood ||
server->tcpStatus == CifsNeedNegotiate) &&
(!server->ops->can_echo || server->ops->can_echo(server)) &&
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
(3 * server->echo_interval) / HZ);
......@@ -3158,17 +3149,29 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
int
cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname)
{
int rc = 0;
int rc;
smb3_parse_devname(devname, ctx);
if (devname) {
cifs_dbg(FYI, "%s: devname=%s\n", __func__, devname);
rc = smb3_parse_devname(devname, ctx);
if (rc) {
cifs_dbg(VFS, "%s: failed to parse %s: %d\n", __func__, devname, rc);
return rc;
}
}
if (mntopts) {
char *ip;
cifs_dbg(FYI, "%s: mntopts=%s\n", __func__, mntopts);
rc = smb3_parse_opt(mntopts, "ip", &ip);
if (!rc && !cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip,
strlen(ip))) {
if (rc) {
cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc);
return rc;
}
rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip));
kfree(ip);
if (!rc) {
cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__);
return -EINVAL;
}
......@@ -3188,7 +3191,7 @@ cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const c
return -EINVAL;
}
return rc;
return 0;
}
static int
......
......@@ -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)
......@@ -430,6 +431,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
......@@ -546,6 +550,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))
......@@ -583,6 +590,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)
{
......@@ -322,9 +323,12 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->dentry = dget(dentry);
cfile->f_flags = file->f_flags;
cfile->invalidHandle = false;
cfile->oplock_break_received = false;
cfile->deferred_scheduled = false;
cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close);
mutex_init(&cfile->fh_mutex);
spin_lock_init(&cfile->file_info_lock);
......@@ -539,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);
......@@ -565,6 +574,23 @@ int cifs_open(struct inode *inode, struct file *file)
file->f_op = &cifs_file_direct_ops;
}
spin_lock(&CIFS_I(inode)->deferred_lock);
/* Get the cached handle as SMB2 close is deferred */
rc = cifs_get_readable_path(tcon, full_path, &cfile);
if (rc == 0) {
if (file->f_flags == cfile->f_flags) {
file->private_data = cfile;
cifs_del_deferred_close(cfile);
spin_unlock(&CIFS_I(inode)->deferred_lock);
goto out;
} else {
spin_unlock(&CIFS_I(inode)->deferred_lock);
_cifsFileInfo_put(cfile, true, false);
}
} else {
spin_unlock(&CIFS_I(inode)->deferred_lock);
}
if (server->oplocks)
oplock = REQ_OPLOCK;
else
......@@ -846,11 +872,56 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
return rc;
}
void smb2_deferred_work_close(struct work_struct *work)
{
struct cifsFileInfo *cfile = container_of(work,
struct cifsFileInfo, deferred.work);
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
if (!cfile->deferred_scheduled) {
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
return;
}
cifs_del_deferred_close(cfile);
cfile->deferred_scheduled = false;
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
_cifsFileInfo_put(cfile, true, false);
}
int cifs_close(struct inode *inode, struct file *file)
{
struct cifsFileInfo *cfile;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_deferred_close *dclose;
if (file->private_data != NULL) {
_cifsFileInfo_put(file->private_data, true, false);
cfile = file->private_data;
file->private_data = NULL;
dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
if ((cinode->oplock == CIFS_CACHE_RHW_FLG) &&
dclose) {
if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags))
inode->i_ctime = inode->i_mtime = current_time(inode);
spin_lock(&cinode->deferred_lock);
cifs_add_deferred_close(cfile, dclose);
if (cfile->deferred_scheduled) {
mod_delayed_work(deferredclose_wq,
&cfile->deferred, cifs_sb->ctx->acregmax);
} else {
/* Deferred close for files */
queue_delayed_work(deferredclose_wq,
&cfile->deferred, cifs_sb->ctx->acregmax);
cfile->deferred_scheduled = true;
spin_unlock(&cinode->deferred_lock);
return 0;
}
spin_unlock(&cinode->deferred_lock);
_cifsFileInfo_put(cfile, true, false);
} else {
_cifsFileInfo_put(cfile, true, false);
kfree(dclose);
}
}
/* return code from the ->release op is always ignored */
......@@ -1920,8 +1991,10 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
if (total_written > 0) {
spin_lock(&d_inode(dentry)->i_lock);
if (*offset > d_inode(dentry)->i_size)
if (*offset > d_inode(dentry)->i_size) {
i_size_write(d_inode(dentry), *offset);
d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9;
}
spin_unlock(&d_inode(dentry)->i_lock);
}
mark_inode_dirty_sync(d_inode(dentry));
......@@ -1947,7 +2020,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
continue;
if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
if (!open_file->invalidHandle) {
if ((!open_file->invalidHandle) &&
(!open_file->oplock_break_received)) {
/* found a good file */
/* lock it so it will not be closed on us */
cifsFileInfo_get(open_file);
......@@ -2476,6 +2550,8 @@ static int cifs_writepages(struct address_space *mapping,
if (cfile)
cifsFileInfo_put(cfile);
free_xid(xid);
/* Indication to update ctime and mtime as close is deferred */
set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
return rc;
}
......@@ -2577,13 +2653,17 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
if (rc > 0) {
spin_lock(&inode->i_lock);
if (pos > inode->i_size)
if (pos > inode->i_size) {
i_size_write(inode, pos);
inode->i_blocks = (512 - 1 + pos) >> 9;
}
spin_unlock(&inode->i_lock);
}
unlock_page(page);
put_page(page);
/* Indication to update ctime and mtime as close is deferred */
set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
return rc;
}
......@@ -4744,6 +4824,8 @@ void cifs_oplock_break(struct work_struct *work)
struct TCP_Server_Info *server = tcon->ses->server;
int rc = 0;
bool purge_cache = false;
bool is_deferred = false;
struct cifs_deferred_close *dclose;
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
TASK_UNINTERRUPTIBLE);
......@@ -4790,6 +4872,18 @@ void cifs_oplock_break(struct work_struct *work)
cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
}
/*
* When oplock break is received and there are no active
* file handles but cached, then set the flag oplock_break_received.
* So, new open will not use cached handle.
*/
spin_lock(&CIFS_I(inode)->deferred_lock);
is_deferred = cifs_is_deferred_close(cfile, &dclose);
if (is_deferred && cfile->deferred_scheduled) {
cfile->oplock_break_received = true;
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
}
spin_unlock(&CIFS_I(inode)->deferred_lock);
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
cifs_done_oplock_break(cinode);
}
......
......@@ -476,6 +476,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
/* move "pos" up to delimiter or NULL */
pos += len;
kfree(ctx->UNC);
ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
if (!ctx->UNC)
return -ENOMEM;
......@@ -486,6 +487,9 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
if (*pos == '/' || *pos == '\\')
pos++;
kfree(ctx->prepath);
ctx->prepath = NULL;
/* If pos is NULL then no prepath */
if (!*pos)
return 0;
......@@ -1642,6 +1646,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)
{
......@@ -1610,6 +1609,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);
......@@ -1632,6 +1634,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
goto unlink_out;
}
cifs_close_all_deferred_files(tcon);
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
rc = CIFSPOSIXDelFile(xid, tcon, full_path,
......@@ -1872,6 +1875,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);
......@@ -1954,6 +1959,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);
......@@ -2088,6 +2098,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);
......@@ -2109,6 +2122,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
goto cifs_rename_exit;
}
cifs_close_all_deferred_files(tcon);
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name);
......@@ -2404,6 +2418,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.
......@@ -2476,6 +2493,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.
......@@ -2962,6 +2982,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,100 @@ 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;
}
static int cifs_dump_full_key(struct cifs_tcon *tcon, unsigned long arg)
{
struct smb3_full_key_debug_info pfull_key_inf;
__u64 suid;
struct list_head *tmp;
struct cifs_ses *ses;
bool found = false;
if (!smb3_encryption_required(tcon))
return -EOPNOTSUPP;
ses = tcon->ses; /* default to user id for current user */
if (get_user(suid, (__u64 __user *)arg))
suid = 0;
if (suid) {
/* search to see if there is a session with a matching SMB UID */
spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &tcon->ses->server->smb_ses_list) {
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
if (ses->Suid == suid) {
found = true;
break;
}
}
spin_unlock(&cifs_tcp_ses_lock);
if (found == false)
return -EINVAL;
} /* else uses default user's SMB UID (ie current user) */
pfull_key_inf.cipher_type = le16_to_cpu(ses->server->cipher_type);
pfull_key_inf.Suid = ses->Suid;
memcpy(pfull_key_inf.auth_key, ses->auth_key.response,
16 /* SMB2_NTLMV2_SESSKEY_SIZE */);
memcpy(pfull_key_inf.smb3decryptionkey, ses->smb3decryptionkey,
32 /* SMB3_ENC_DEC_KEY_SIZE */);
memcpy(pfull_key_inf.smb3encryptionkey,
ses->smb3encryptionkey, 32 /* SMB3_ENC_DEC_KEY_SIZE */);
if (copy_to_user((void __user *)arg, &pfull_key_inf,
sizeof(struct smb3_full_key_debug_info)))
return -EFAULT;
return 0;
}
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
{
struct inode *inode = file_inode(filep);
......@@ -304,6 +398,21 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
else
rc = 0;
break;
/*
* Dump full key (32 bytes instead of 16 bytes) is
* needed if GCM256 (stronger encryption) negotiated
*/
case CIFS_DUMP_FULL_KEY:
if (pSMBFile == NULL)
break;
if (!capable(CAP_SYS_ADMIN)) {
rc = -EACCES;
break;
}
tcon = tlink_tcon(pSMBFile->tlink);
rc = cifs_dump_full_key(tcon, arg);
break;
case CIFS_IOC_NOTIFY:
if (!S_ISDIR(inode->i_mode)) {
/* Notify can only be done on directories */
......@@ -325,6 +434,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);
......@@ -679,9 +683,16 @@ cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
const char *full_path;
void *page = alloc_dentry_path();
void *page;
struct inode *newinode = NULL;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
page = alloc_dentry_path();
if (!page)
return -ENOMEM;
xid = get_xid();
tlink = cifs_sb_tlink(cifs_sb);
......
......@@ -672,6 +672,85 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
}
bool
cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose)
{
struct cifs_deferred_close *dclose;
list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) {
if ((dclose->netfid == cfile->fid.netfid) &&
(dclose->persistent_fid == cfile->fid.persistent_fid) &&
(dclose->volatile_fid == cfile->fid.volatile_fid)) {
*pdclose = dclose;
return true;
}
}
return false;
}
void
cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose)
{
bool is_deferred = false;
struct cifs_deferred_close *pdclose;
is_deferred = cifs_is_deferred_close(cfile, &pdclose);
if (is_deferred) {
kfree(dclose);
return;
}
dclose->tlink = cfile->tlink;
dclose->netfid = cfile->fid.netfid;
dclose->persistent_fid = cfile->fid.persistent_fid;
dclose->volatile_fid = cfile->fid.volatile_fid;
list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes);
}
void
cifs_del_deferred_close(struct cifsFileInfo *cfile)
{
bool is_deferred = false;
struct cifs_deferred_close *dclose;
is_deferred = cifs_is_deferred_close(cfile, &dclose);
if (!is_deferred)
return;
list_del(&dclose->dlist);
kfree(dclose);
}
void
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
{
struct cifsFileInfo *cfile = NULL;
struct cifs_deferred_close *dclose;
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
spin_lock(&cifs_inode->deferred_lock);
if (cifs_is_deferred_close(cfile, &dclose))
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
spin_unlock(&cifs_inode->deferred_lock);
}
}
void
cifs_close_all_deferred_files(struct cifs_tcon *tcon)
{
struct cifsFileInfo *cfile;
struct cifsInodeInfo *cinode;
struct list_head *tmp;
spin_lock(&tcon->open_file_lock);
list_for_each(tmp, &tcon->openFileList) {
cfile = list_entry(tmp, struct cifsFileInfo, tlist);
cinode = CIFS_I(d_inode(cfile->dentry));
if (delayed_work_pending(&cfile->deferred))
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
}
spin_unlock(&tcon->open_file_lock);
}
/* parses DFS refferal V3 structure
* caller is responsible for freeing target_nodes
* returns:
......
......@@ -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