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_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;
|
||||||
|
|
|
@ -57,6 +57,12 @@ struct smb_query_info {
|
||||||
/* char buffer[]; */
|
/* char buffer[]; */
|
||||||
} __packed;
|
} __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 {
|
struct smb3_key_debug_info {
|
||||||
__u64 Suid;
|
__u64 Suid;
|
||||||
__u16 cipher_type;
|
__u16 cipher_type;
|
||||||
|
@ -65,6 +71,18 @@ struct smb3_key_debug_info {
|
||||||
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
|
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||||
} __packed;
|
} __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 {
|
struct smb3_notify {
|
||||||
__u32 completion_filter;
|
__u32 completion_filter;
|
||||||
bool watch_tree;
|
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_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_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 linuxExtEnabled = true;
|
||||||
bool lookupCacheEnabled = true;
|
bool lookupCacheEnabled = true;
|
||||||
bool disable_legacy_dialects; /* false by default */
|
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 */
|
bool require_gcm_256; /* false by default */
|
||||||
unsigned int global_secflags = CIFSSEC_DEF;
|
unsigned int global_secflags = CIFSSEC_DEF;
|
||||||
/* unsigned int ntlmv2_support = 0; */
|
/* unsigned int ntlmv2_support = 0; */
|
||||||
|
@ -133,6 +133,7 @@ struct workqueue_struct *cifsiod_wq;
|
||||||
struct workqueue_struct *decrypt_wq;
|
struct workqueue_struct *decrypt_wq;
|
||||||
struct workqueue_struct *fileinfo_put_wq;
|
struct workqueue_struct *fileinfo_put_wq;
|
||||||
struct workqueue_struct *cifsoplockd_wq;
|
struct workqueue_struct *cifsoplockd_wq;
|
||||||
|
struct workqueue_struct *deferredclose_wq;
|
||||||
__u32 cifs_lock_secret;
|
__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; */
|
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
|
||||||
INIT_LIST_HEAD(&cifs_inode->openFileList);
|
INIT_LIST_HEAD(&cifs_inode->openFileList);
|
||||||
INIT_LIST_HEAD(&cifs_inode->llist);
|
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;
|
return &cifs_inode->vfs_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,13 +863,7 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cifs_setup_volume_info->smb3_parse_devname() redups UNC & prepath */
|
rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL);
|
||||||
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);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
root = ERR_PTR(rc);
|
root = ERR_PTR(rc);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1637,9 +1634,16 @@ init_cifs(void)
|
||||||
goto out_destroy_fileinfo_put_wq;
|
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();
|
rc = cifs_fscache_register();
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out_destroy_cifsoplockd_wq;
|
goto out_destroy_deferredclose_wq;
|
||||||
|
|
||||||
rc = cifs_init_inodecache();
|
rc = cifs_init_inodecache();
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -1707,6 +1711,8 @@ out_destroy_inodecache:
|
||||||
cifs_destroy_inodecache();
|
cifs_destroy_inodecache();
|
||||||
out_unreg_fscache:
|
out_unreg_fscache:
|
||||||
cifs_fscache_unregister();
|
cifs_fscache_unregister();
|
||||||
|
out_destroy_deferredclose_wq:
|
||||||
|
destroy_workqueue(deferredclose_wq);
|
||||||
out_destroy_cifsoplockd_wq:
|
out_destroy_cifsoplockd_wq:
|
||||||
destroy_workqueue(cifsoplockd_wq);
|
destroy_workqueue(cifsoplockd_wq);
|
||||||
out_destroy_fileinfo_put_wq:
|
out_destroy_fileinfo_put_wq:
|
||||||
|
@ -1741,6 +1747,7 @@ exit_cifs(void)
|
||||||
cifs_destroy_mids();
|
cifs_destroy_mids();
|
||||||
cifs_destroy_inodecache();
|
cifs_destroy_inodecache();
|
||||||
cifs_fscache_unregister();
|
cifs_fscache_unregister();
|
||||||
|
destroy_workqueue(deferredclose_wq);
|
||||||
destroy_workqueue(cifsoplockd_wq);
|
destroy_workqueue(cifsoplockd_wq);
|
||||||
destroy_workqueue(decrypt_wq);
|
destroy_workqueue(decrypt_wq);
|
||||||
destroy_workqueue(fileinfo_put_wq);
|
destroy_workqueue(fileinfo_put_wq);
|
||||||
|
|
|
@ -1154,6 +1154,14 @@ struct cifs_pending_open {
|
||||||
__u32 oplock;
|
__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 info hangs off the cifsFileInfo structure, pointed to by llist.
|
||||||
* This is used to track byte stream locks on the file
|
* This is used to track byte stream locks on the file
|
||||||
|
@ -1248,6 +1256,9 @@ struct cifsFileInfo {
|
||||||
struct cifs_search_info srch_inf;
|
struct cifs_search_info srch_inf;
|
||||||
struct work_struct oplock_break; /* work for oplock breaks */
|
struct work_struct oplock_break; /* work for oplock breaks */
|
||||||
struct work_struct put; /* work for the final part of _put */
|
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 {
|
struct cifs_io_parms {
|
||||||
|
@ -1392,6 +1403,7 @@ struct cifsInodeInfo {
|
||||||
#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
|
#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
|
||||||
#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
|
#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
|
||||||
#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
|
#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
|
||||||
|
#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
spinlock_t writers_lock;
|
spinlock_t writers_lock;
|
||||||
unsigned int writers; /* Number of writers on this inode */
|
unsigned int writers; /* Number of writers on this inode */
|
||||||
|
@ -1404,6 +1416,8 @@ struct cifsInodeInfo {
|
||||||
struct fscache_cookie *fscache;
|
struct fscache_cookie *fscache;
|
||||||
#endif
|
#endif
|
||||||
struct inode vfs_inode;
|
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 *
|
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_oplock_break(struct work_struct *work);
|
||||||
void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
|
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 *cifsiod_wq;
|
||||||
extern struct workqueue_struct *decrypt_wq;
|
extern struct workqueue_struct *decrypt_wq;
|
||||||
extern struct workqueue_struct *fileinfo_put_wq;
|
extern struct workqueue_struct *fileinfo_put_wq;
|
||||||
extern struct workqueue_struct *cifsoplockd_wq;
|
extern struct workqueue_struct *cifsoplockd_wq;
|
||||||
|
extern struct workqueue_struct *deferredclose_wq;
|
||||||
extern __u32 cifs_lock_secret;
|
extern __u32 cifs_lock_secret;
|
||||||
|
|
||||||
extern mempool_t *cifs_mid_poolp;
|
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 tcon_link *tlink,
|
||||||
struct cifs_pending_open *open);
|
struct cifs_pending_open *open);
|
||||||
extern void cifs_del_pending_open(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 struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
|
||||||
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
|
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
|
||||||
int from_reconnect);
|
int from_reconnect);
|
||||||
|
|
|
@ -392,16 +392,6 @@ cifs_echo_request(struct work_struct *work)
|
||||||
int rc;
|
int rc;
|
||||||
struct TCP_Server_Info *server = container_of(work,
|
struct TCP_Server_Info *server = container_of(work,
|
||||||
struct TCP_Server_Info, echo.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.
|
* 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 == CifsExiting ||
|
||||||
server->tcpStatus == CifsNew ||
|
server->tcpStatus == CifsNew ||
|
||||||
(server->ops->can_echo && !server->ops->can_echo(server)) ||
|
(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;
|
goto requeue_echo;
|
||||||
|
|
||||||
rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
|
rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
|
||||||
|
@ -476,6 +466,7 @@ server_unresponsive(struct TCP_Server_Info *server)
|
||||||
*/
|
*/
|
||||||
if ((server->tcpStatus == CifsGood ||
|
if ((server->tcpStatus == CifsGood ||
|
||||||
server->tcpStatus == CifsNeedNegotiate) &&
|
server->tcpStatus == CifsNeedNegotiate) &&
|
||||||
|
(!server->ops->can_echo || server->ops->can_echo(server)) &&
|
||||||
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
|
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
|
||||||
cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
|
cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
|
||||||
(3 * server->echo_interval) / HZ);
|
(3 * server->echo_interval) / HZ);
|
||||||
|
@ -3158,17 +3149,29 @@ out:
|
||||||
int
|
int
|
||||||
cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname)
|
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) {
|
if (mntopts) {
|
||||||
char *ip;
|
char *ip;
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: mntopts=%s\n", __func__, mntopts);
|
|
||||||
rc = smb3_parse_opt(mntopts, "ip", &ip);
|
rc = smb3_parse_opt(mntopts, "ip", &ip);
|
||||||
if (!rc && !cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip,
|
if (rc) {
|
||||||
strlen(ip))) {
|
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__);
|
cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -3188,7 +3191,7 @@ cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const c
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -430,6 +431,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
|
||||||
|
@ -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",
|
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))
|
||||||
|
@ -583,6 +590,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);
|
||||||
|
|
102
fs/cifs/file.c
102
fs/cifs/file.c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -322,9 +323,12 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||||
cfile->dentry = dget(dentry);
|
cfile->dentry = dget(dentry);
|
||||||
cfile->f_flags = file->f_flags;
|
cfile->f_flags = file->f_flags;
|
||||||
cfile->invalidHandle = false;
|
cfile->invalidHandle = false;
|
||||||
|
cfile->oplock_break_received = false;
|
||||||
|
cfile->deferred_scheduled = false;
|
||||||
cfile->tlink = cifs_get_tlink(tlink);
|
cfile->tlink = cifs_get_tlink(tlink);
|
||||||
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
|
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
|
||||||
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
|
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
|
||||||
|
INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close);
|
||||||
mutex_init(&cfile->fh_mutex);
|
mutex_init(&cfile->fh_mutex);
|
||||||
spin_lock_init(&cfile->file_info_lock);
|
spin_lock_init(&cfile->file_info_lock);
|
||||||
|
|
||||||
|
@ -539,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);
|
||||||
|
@ -565,6 +574,23 @@ int cifs_open(struct inode *inode, struct file *file)
|
||||||
file->f_op = &cifs_file_direct_ops;
|
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)
|
if (server->oplocks)
|
||||||
oplock = REQ_OPLOCK;
|
oplock = REQ_OPLOCK;
|
||||||
else
|
else
|
||||||
|
@ -846,11 +872,56 @@ reopen_error_exit:
|
||||||
return rc;
|
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)
|
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) {
|
if (file->private_data != NULL) {
|
||||||
_cifsFileInfo_put(file->private_data, true, false);
|
cfile = file->private_data;
|
||||||
file->private_data = NULL;
|
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 */
|
/* 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) {
|
if (total_written > 0) {
|
||||||
spin_lock(&d_inode(dentry)->i_lock);
|
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);
|
i_size_write(d_inode(dentry), *offset);
|
||||||
|
d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9;
|
||||||
|
}
|
||||||
spin_unlock(&d_inode(dentry)->i_lock);
|
spin_unlock(&d_inode(dentry)->i_lock);
|
||||||
}
|
}
|
||||||
mark_inode_dirty_sync(d_inode(dentry));
|
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()))
|
if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
|
||||||
continue;
|
continue;
|
||||||
if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
|
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 */
|
/* found a good file */
|
||||||
/* lock it so it will not be closed on us */
|
/* lock it so it will not be closed on us */
|
||||||
cifsFileInfo_get(open_file);
|
cifsFileInfo_get(open_file);
|
||||||
|
@ -2476,6 +2550,8 @@ retry:
|
||||||
if (cfile)
|
if (cfile)
|
||||||
cifsFileInfo_put(cfile);
|
cifsFileInfo_put(cfile);
|
||||||
free_xid(xid);
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2577,13 +2653,17 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
|
||||||
|
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
if (pos > inode->i_size)
|
if (pos > inode->i_size) {
|
||||||
i_size_write(inode, pos);
|
i_size_write(inode, pos);
|
||||||
|
inode->i_blocks = (512 - 1 + pos) >> 9;
|
||||||
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
put_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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -4744,6 +4824,8 @@ void cifs_oplock_break(struct work_struct *work)
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
bool purge_cache = false;
|
bool purge_cache = false;
|
||||||
|
bool is_deferred = false;
|
||||||
|
struct cifs_deferred_close *dclose;
|
||||||
|
|
||||||
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
|
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
|
||||||
TASK_UNINTERRUPTIBLE);
|
TASK_UNINTERRUPTIBLE);
|
||||||
|
@ -4790,6 +4872,18 @@ oplock_break_ack:
|
||||||
cinode);
|
cinode);
|
||||||
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
|
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);
|
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
|
||||||
cifs_done_oplock_break(cinode);
|
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 */
|
/* move "pos" up to delimiter or NULL */
|
||||||
pos += len;
|
pos += len;
|
||||||
|
kfree(ctx->UNC);
|
||||||
ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
|
ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
|
||||||
if (!ctx->UNC)
|
if (!ctx->UNC)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -486,6 +487,9 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
|
||||||
if (*pos == '/' || *pos == '\\')
|
if (*pos == '/' || *pos == '\\')
|
||||||
pos++;
|
pos++;
|
||||||
|
|
||||||
|
kfree(ctx->prepath);
|
||||||
|
ctx->prepath = NULL;
|
||||||
|
|
||||||
/* If pos is NULL then no prepath */
|
/* If pos is NULL then no prepath */
|
||||||
if (!*pos)
|
if (!*pos)
|
||||||
return 0;
|
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_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
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);
|
||||||
|
@ -1632,6 +1634,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
goto unlink_out;
|
goto unlink_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cifs_close_all_deferred_files(tcon);
|
||||||
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||||
rc = CIFSPOSIXDelFile(xid, tcon, full_path,
|
rc = CIFSPOSIXDelFile(xid, tcon, full_path,
|
||||||
|
@ -1872,6 +1875,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);
|
||||||
|
@ -1954,6 +1959,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);
|
||||||
|
@ -2088,6 +2098,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);
|
||||||
|
@ -2109,6 +2122,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
|
||||||
goto cifs_rename_exit;
|
goto cifs_rename_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cifs_close_all_deferred_files(tcon);
|
||||||
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
|
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
|
||||||
to_name);
|
to_name);
|
||||||
|
|
||||||
|
@ -2404,6 +2418,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.
|
||||||
|
@ -2476,6 +2493,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.
|
||||||
|
@ -2962,6 +2982,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);
|
||||||
|
|
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;
|
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)
|
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(filep);
|
struct inode *inode = file_inode(filep);
|
||||||
|
@ -304,6 +398,21 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||||
else
|
else
|
||||||
rc = 0;
|
rc = 0;
|
||||||
break;
|
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:
|
case CIFS_IOC_NOTIFY:
|
||||||
if (!S_ISDIR(inode->i_mode)) {
|
if (!S_ISDIR(inode->i_mode)) {
|
||||||
/* Notify can only be done on directories */
|
/* 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;
|
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 @@
|
||||||
#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,
|
||||||
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);
|
||||||
|
@ -679,9 +683,16 @@ cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *pTcon;
|
struct cifs_tcon *pTcon;
|
||||||
const char *full_path;
|
const char *full_path;
|
||||||
void *page = alloc_dentry_path();
|
void *page;
|
||||||
struct inode *newinode = NULL;
|
struct inode *newinode = NULL;
|
||||||
|
|
||||||
|
if (unlikely(cifs_forced_shutdown(cifs_sb)))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
page = alloc_dentry_path();
|
||||||
|
if (!page)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
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);
|
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
|
/* parses DFS refferal V3 structure
|
||||||
* caller is responsible for freeing target_nodes
|
* caller is responsible for freeing target_nodes
|
||||||
* returns:
|
* returns:
|
||||||
|
|
|
@ -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)
|
||||||
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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue