mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 13:41:51 +00:00
10 CIFS/SMB3 changesets including some important multichannel fixes, as well as support for handle leases (deferred close) and shutdown support
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmCSKIIACgkQiiy9cAdy T1F+Hgv+L3NkOFwMvBgGjHP9b+Lkv/YWGKeJkLwQW1xqoHIUHn0/+C5+9ScJGBZc WVuzp4pqEIgv4my4UQiyqVwzcmz4BqY2KTDJzBYtqANt6pVp1w6YtC2GplgJE3J2 qoQh1RwZaqSXfjcoPSRnv5EiSF6DbHlBUhPMd53qOE9pwaf/38i9/M3d9G7EIB8h rRNmpGtFuzBHdtGQ2b+4+8ftCIpEBDu/OXcA6QXMUMcvKGaruU39NOxBuW6a/VO5 9P47Nsof3dlN758uesoQT2VMEc0pcpwAs9BwOkinfXWGUyNqJmbPNvddIOlaP/dv vG58n/+JqvWUKgEnrNk5h+wD7wmXpgxpQ523sD5k6bID1hc+vh4lXf+O+iltbYtc 1ce9ITglSVxA7z4qwFWhtawBy1j1YyvltTAGvhnzdtKZLRk6e5AYIFOUn9O+AMJw Eofk4lD0kNTdXyMkveGluRMBXrOzKMdmfw5FW/9hObYgebEGpTQkGyIMpaStraZM 8hDNAGTk =BFOj -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
7c9e41e0ef
@ -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 @@ out_destroy_inodecache:
|
||||
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 @@ out:
|
||||
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);
|
||||
|
102
fs/cifs/file.c
102
fs/cifs/file.c
@ -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 @@ reopen_error_exit:
|
||||
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 @@ retry:
|
||||
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 @@ oplock_break_ack:
|
||||
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);
|
||||
|
112
fs/cifs/ioctl.c
112
fs/cifs/ioctl.c
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user