18 cifs/smb3 fixes, half are related to multichannel and reconnect

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmHrkScACgkQiiy9cAdy
 T1EY6Av8C2by0N3vBK/hhjq+ocKhoLWzlziyOlVX6Hzrqa2BH5I9UBUrGmI6rXRI
 SP9QiyjO8WlVGajOYMAgQpjysKJjRnJTkGGvCb8iV4VfQPq8yYfuoLRaihO20WI4
 VaRAwKAZ6RjNrOtbYQ6KPx2OXDE0OUZ6Y8QuDfgn6jXzsMfjTCmM4BGntUNweiUU
 Yn7sBBIh0XWy1D3Fl3Dm47t6d1iW1/I9WIym/2vHWqxI8QkWv05z9Dk5Sijrl8v/
 pa9eQ05Ym8bRTI7dU9be8uySVTO21sdPitNZ4L4vNqH4KY0L3pKqUL4E9QzVcv4O
 wnO8Q7wtBIOEgREuRF041d14vJsRiMUKjxUVG5PubhCE7pSesHHhgh0DN4ymDmKi
 cBYkpYIcPLopfOvhE4CEPjULE3DI91KNM8x8ePB+fm/ZbnPHrXyhOcUCz3gzKYGf
 jsYHVVAofWEU1HRPQjTPtwxuof/3u1ZgkzIj+gqzbVOWIQO1JAsdBE2TQQKTadL9
 Y7aKEfci
 =/mdT
 -----END PGP SIGNATURE-----

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

Pull cifs fixes from Steve French:

 - multichannel fixes, addressing additional reconnect and DFS scenarios

 - reenabling fscache support (indexing rewrite, metadata caching e.g.)

 - send additional version information during NTLMSSP negotiate to
   improve debugging

 - fix for a mount race

 - DFS fixes

 - fix for a memory leak for stable

* tag '5.17-rc-part2-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: update internal module number
  smb3: send NTLMSSP version information
  cifs: Support fscache indexing rewrite
  cifs: cifs_ses_mark_for_reconnect should also update reconnect bits
  cifs: update tcpStatus during negotiate and sess setup
  cifs: make status checks in version independent callers
  cifs: remove repeated state change in dfs tree connect
  cifs: fix the cifs_reconnect path for DFS
  cifs: remove unused variable ses_selected
  cifs: protect all accesses to chan_* with chan_lock
  cifs: fix the connection state transitions with multichannel
  cifs: check reconnects for channels of active tcons too
  smb3: add new defines from protocol specification
  cifs: serialize all mount attempts
  cifs: quirk for STATUS_OBJECT_NAME_INVALID returned for non-ASCII dfs refs
  cifs: alloc_path_with_tree_prefix: do not append sep. if the path is empty
  cifs: clean up an inconsistent indenting
  cifs: free ntlmsspblob allocated in negotiate
This commit is contained in:
Linus Torvalds 2022-01-22 11:12:26 +02:00
commit 8205ae327e
25 changed files with 579 additions and 632 deletions

View File

@ -188,7 +188,7 @@ config CIFS_SMB_DIRECT
config CIFS_FSCACHE
bool "Provide CIFS client caching support"
depends on CIFS=m && FSCACHE_OLD_API || CIFS=y && FSCACHE_OLD_API=y
depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
help
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
to be cached locally on disk through the general filesystem cache

View File

@ -25,7 +25,7 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o
cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o
cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o

View File

@ -1,105 +0,0 @@
// SPDX-License-Identifier: LGPL-2.1
/*
* CIFS filesystem cache index structure definitions
*
* Copyright (c) 2010 Novell, Inc.
* Authors(s): Suresh Jayaraman (sjayaraman@suse.de>
*
*/
#include "fscache.h"
#include "cifs_debug.h"
/*
* CIFS filesystem definition for FS-Cache
*/
struct fscache_netfs cifs_fscache_netfs = {
.name = "cifs",
.version = 0,
};
/*
* Register CIFS for caching with FS-Cache
*/
int cifs_fscache_register(void)
{
return fscache_register_netfs(&cifs_fscache_netfs);
}
/*
* Unregister CIFS for caching
*/
void cifs_fscache_unregister(void)
{
fscache_unregister_netfs(&cifs_fscache_netfs);
}
/*
* Server object for FS-Cache
*/
const struct fscache_cookie_def cifs_fscache_server_index_def = {
.name = "CIFS.server",
.type = FSCACHE_COOKIE_TYPE_INDEX,
};
static enum
fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
const void *data,
uint16_t datalen,
loff_t object_size)
{
struct cifs_fscache_super_auxdata auxdata;
const struct cifs_tcon *tcon = cookie_netfs_data;
if (datalen != sizeof(auxdata))
return FSCACHE_CHECKAUX_OBSOLETE;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.resource_id = tcon->resource_id;
auxdata.vol_create_time = tcon->vol_create_time;
auxdata.vol_serial_number = tcon->vol_serial_number;
if (memcmp(data, &auxdata, datalen) != 0)
return FSCACHE_CHECKAUX_OBSOLETE;
return FSCACHE_CHECKAUX_OKAY;
}
/*
* Superblock object for FS-Cache
*/
const struct fscache_cookie_def cifs_fscache_super_index_def = {
.name = "CIFS.super",
.type = FSCACHE_COOKIE_TYPE_INDEX,
.check_aux = cifs_fscache_super_check_aux,
};
static enum
fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
const void *data,
uint16_t datalen,
loff_t object_size)
{
struct cifs_fscache_inode_auxdata auxdata;
struct cifsInodeInfo *cifsi = cookie_netfs_data;
if (datalen != sizeof(auxdata))
return FSCACHE_CHECKAUX_OBSOLETE;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
if (memcmp(data, &auxdata, datalen) != 0)
return FSCACHE_CHECKAUX_OBSOLETE;
return FSCACHE_CHECKAUX_OKAY;
}
const struct fscache_cookie_def cifs_fscache_inode_object_def = {
.name = "CIFS.uniqueid",
.type = FSCACHE_COOKIE_TYPE_DATAFILE,
.check_aux = cifs_fscache_inode_check_aux,
};

View File

@ -396,11 +396,11 @@ static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const ch
switch (state) {
case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
cifs_ses_mark_for_reconnect(swnreg->tcon->ses);
cifs_reconnect(swnreg->tcon->ses->server, true);
break;
case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
cifs_ses_mark_for_reconnect(swnreg->tcon->ses);
cifs_reconnect(swnreg->tcon->ses->server, true);
break;
case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
@ -498,10 +498,7 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a
goto unlock;
}
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->server->tcpStatus != CifsExiting)
tcon->ses->server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
cifs_reconnect(tcon->ses->server, false);
unlock:
mutex_unlock(&tcon->ses->server->srv_mutex);

View File

@ -397,6 +397,9 @@ static void
cifs_evict_inode(struct inode *inode)
{
truncate_inode_pages_final(&inode->i_data);
if (inode->i_state & I_PINNING_FSCACHE_WB)
cifs_fscache_unuse_inode_cookie(inode, true);
cifs_fscache_release_inode_cookie(inode);
clear_inode(inode);
}
@ -721,6 +724,12 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)
}
#endif
static int cifs_write_inode(struct inode *inode, struct writeback_control *wbc)
{
fscache_unpin_writeback(wbc, cifs_inode_cookie(inode));
return 0;
}
static int cifs_drop_inode(struct inode *inode)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
@ -733,6 +742,7 @@ static int cifs_drop_inode(struct inode *inode)
static const struct super_operations cifs_super_ops = {
.statfs = cifs_statfs,
.alloc_inode = cifs_alloc_inode,
.write_inode = cifs_write_inode,
.free_inode = cifs_free_inode,
.drop_inode = cifs_drop_inode,
.evict_inode = cifs_evict_inode,
@ -1625,13 +1635,9 @@ init_cifs(void)
goto out_destroy_cifsoplockd_wq;
}
rc = cifs_fscache_register();
if (rc)
goto out_destroy_deferredclose_wq;
rc = cifs_init_inodecache();
if (rc)
goto out_unreg_fscache;
goto out_destroy_deferredclose_wq;
rc = cifs_init_mids();
if (rc)
@ -1693,8 +1699,6 @@ out_destroy_mids:
cifs_destroy_mids();
out_destroy_inodecache:
cifs_destroy_inodecache();
out_unreg_fscache:
cifs_fscache_unregister();
out_destroy_deferredclose_wq:
destroy_workqueue(deferredclose_wq);
out_destroy_cifsoplockd_wq:
@ -1730,7 +1734,6 @@ exit_cifs(void)
cifs_destroy_request_bufs();
cifs_destroy_mids();
cifs_destroy_inodecache();
cifs_fscache_unregister();
destroy_workqueue(deferredclose_wq);
destroy_workqueue(cifsoplockd_wq);
destroy_workqueue(decrypt_wq);

View File

@ -152,5 +152,6 @@ extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type,
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
#define CIFS_VERSION "2.34"
#define SMB3_PRODUCT_BUILD 35
#define CIFS_VERSION "2.35"
#endif /* _CIFSFS_H */

View File

@ -117,6 +117,7 @@ enum statusEnum {
CifsInSessSetup,
CifsNeedTcon,
CifsInTcon,
CifsNeedFilesInvalidate,
CifsInFilesInvalidate
};
@ -667,9 +668,6 @@ struct TCP_Server_Info {
unsigned int total_read; /* total amount of data read in this pass */
atomic_t in_send; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache; /* client index cache cookie */
#endif
#ifdef CONFIG_CIFS_STATS2
atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */
atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
@ -923,6 +921,7 @@ struct cifs_chan {
*/
struct cifs_ses {
struct list_head smb_ses_list;
struct list_head rlist; /* reconnect list */
struct list_head tcon_list;
struct cifs_tcon *tcon_ipc;
struct mutex session_mutex;
@ -1110,7 +1109,7 @@ struct cifs_tcon {
__u32 max_bytes_copy;
#ifdef CONFIG_CIFS_FSCACHE
u64 resource_id; /* server resource id */
struct fscache_cookie *fscache; /* cookie for share */
struct fscache_volume *fscache; /* cookie for share */
#endif
struct list_head pending_opens; /* list of incomplete opens */
struct cached_fid crfid; /* Cached root fid */

View File

@ -131,6 +131,9 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
struct smb_hdr *in_buf ,
struct smb_hdr *out_buf,
int *bytes_returned);
void
cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session);
extern int cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session);
extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
@ -647,6 +650,11 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
int match_target_ip(struct TCP_Server_Info *server,
const char *share, size_t share_len,
bool *result);
int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *dfs_link_path);
#endif
static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)

View File

@ -168,7 +168,7 @@ static void cifs_resolve_server(struct work_struct *work)
* @server needs to be previously set to CifsNeedReconnect.
*
*/
static void
void
cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
{
@ -181,24 +181,26 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
server->maxBuf = 0;
server->max_read = 0;
cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname);
/*
* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they
* are not used until reconnected.
*/
cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", __func__);
cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__);
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
spin_lock(&ses->chan_lock);
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
goto next_session;
cifs_chan_set_need_reconnect(ses, server);
if (mark_smb_session)
CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses);
else
cifs_chan_set_need_reconnect(ses, server);
/* If all channels need reconnect, then tcon needs reconnect */
if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses))
@ -218,13 +220,8 @@ next_session:
}
spin_unlock(&cifs_tcp_ses_lock);
/*
* before reconnecting the tcp session, mark the smb session (uid)
* and the tid bad so they are not used until reconnected
*/
cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect and tearing down socket\n",
__func__);
/* do not want to be sending data on a socket we are freeing */
cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
mutex_lock(&server->srv_mutex);
if (server->ssocket) {
cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state,
@ -280,7 +277,12 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
wake_up(&server->response_q);
return false;
}
cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
trace_smb3_reconnect(server->CurrentMid, server->conn_id,
server->hostname);
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
@ -335,11 +337,14 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex);
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
}
} while (server->tcpStatus == CifsNeedReconnect);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedNegotiate)
mod_delayed_work(cifsiod_wq, &server->echo, 0);
spin_unlock(&cifs_tcp_ses_lock);
wake_up(&server->response_q);
return rc;
@ -454,6 +459,7 @@ reconnect_dfs_server(struct TCP_Server_Info *server,
spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex);
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
} while (server->tcpStatus == CifsNeedReconnect);
if (target_hint)
@ -633,7 +639,6 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
if (server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_reconnect(server, false);
return -ECONNABORTED;
}
spin_unlock(&cifs_tcp_ses_lock);
@ -1439,10 +1444,6 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
cifs_crypto_secmech_release(server);
/* fscache server cookies are based on primary channel only */
if (!CIFS_SERVER_IS_CHAN(server))
cifs_fscache_release_client_cookie(server);
kfree(server->session_key.response);
server->session_key.response = NULL;
server->session_key.len = 0;
@ -1604,14 +1605,6 @@ smbd_connected:
list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
/* fscache server cookies are based on primary channel only */
if (!CIFS_SERVER_IS_CHAN(tcp_ses))
cifs_fscache_get_client_cookie(tcp_ses);
#ifdef CONFIG_CIFS_FSCACHE
else
tcp_ses->fscache = tcp_ses->primary_server->fscache;
#endif /* CONFIG_CIFS_FSCACHE */
/* queue echo request delayed work */
queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
@ -1832,7 +1825,6 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
spin_lock(&ses->chan_lock);
chan_count = ses->chan_count;
spin_unlock(&ses->chan_lock);
/* close any extra channels */
if (chan_count > 1) {
@ -1849,6 +1841,7 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
ses->chans[i].server = NULL;
}
}
spin_unlock(&ses->chan_lock);
sesInfoFree(ses);
cifs_put_tcp_session(server, 0);
@ -2124,8 +2117,10 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
mutex_unlock(&ses->session_mutex);
/* each channel uses a different signing key */
spin_lock(&ses->chan_lock);
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
sizeof(ses->smb3signingkey));
spin_unlock(&ses->chan_lock);
if (rc)
goto get_ses_fail;
@ -3121,7 +3116,8 @@ static int mount_get_conns(struct mount_ctx *mnt_ctx)
* Inside cifs_fscache_get_super_cookie it checks
* that we do not get super cookie twice.
*/
cifs_fscache_get_super_cookie(tcon);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE)
cifs_fscache_get_super_cookie(tcon);
out:
mnt_ctx->server = server;
@ -3374,6 +3370,11 @@ static int is_path_remote(struct mount_ctx *mnt_ctx)
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
full_path);
#ifdef CONFIG_CIFS_DFS_UPCALL
if (rc == -ENOENT && is_tcon_dfs(tcon))
rc = cifs_dfs_query_info_nonascii_quirk(xid, tcon, cifs_sb,
full_path);
#endif
if (rc != 0 && rc != -EREMOTE) {
kfree(full_path);
return rc;
@ -3761,10 +3762,6 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
if (rc == 0) {
bool is_unicode;
spin_lock(&cifs_tcp_ses_lock);
tcon->tidStatus = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
tcon->need_reconnect = false;
tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response);
bytes_left = get_bcc(smb_buffer_response);
@ -3879,6 +3876,11 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
else
rc = -EHOSTDOWN;
spin_unlock(&cifs_tcp_ses_lock);
} else {
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsInNegotiate)
server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&cifs_tcp_ses_lock);
}
return rc;
@ -3898,7 +3900,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
ses->status = CifsInSessSetup;
server->tcpStatus = CifsInSessSetup;
spin_unlock(&cifs_tcp_ses_lock);
spin_lock(&ses->chan_lock);
@ -3925,8 +3927,24 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
if (server->ops->sess_setup)
rc = server->ops->sess_setup(xid, ses, server, nls_info);
if (rc)
if (rc) {
cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsInSessSetup)
server->tcpStatus = CifsNeedSessSetup;
spin_unlock(&cifs_tcp_ses_lock);
} else {
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsInSessSetup)
server->tcpStatus = CifsGood;
/* Even if one channel is active, session is in good state */
ses->status = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
spin_lock(&ses->chan_lock);
cifs_chan_clear_need_reconnect(ses, server);
spin_unlock(&ses->chan_lock);
}
return rc;
}
@ -4271,17 +4289,6 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
struct dfs_cache_tgt_iterator *tit;
bool target_match;
/* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->status != CifsGood ||
(tcon->tidStatus != CifsNew &&
tcon->tidStatus != CifsNeedTcon)) {
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock);
extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
tit = dfs_cache_get_tgt_iterator(tl);
@ -4381,7 +4388,7 @@ static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tco
*/
if (rc && server->current_fullpath != server->origin_fullpath) {
server->current_fullpath = server->origin_fullpath;
cifs_ses_mark_for_reconnect(tcon->ses);
cifs_reconnect(tcon->ses->server, true);
}
dfs_cache_free_tgts(tl);
@ -4399,9 +4406,22 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
char *tree;
struct dfs_info3_param ref = {0};
/* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->status != CifsGood ||
(tcon->tidStatus != CifsNew &&
tcon->tidStatus != CifsNeedTcon)) {
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock);
tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
if (!tree)
return -ENOMEM;
if (!tree) {
rc = -ENOMEM;
goto out;
}
if (tcon->ipc) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
@ -4433,11 +4453,25 @@ out:
kfree(tree);
cifs_put_tcp_super(sb);
if (rc) {
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsInTcon)
tcon->tidStatus = CifsNeedTcon;
spin_unlock(&cifs_tcp_ses_lock);
} else {
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsInTcon)
tcon->tidStatus = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
tcon->need_reconnect = false;
}
return rc;
}
#else
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
{
int rc;
const struct smb_version_operations *ops = tcon->ses->server->ops;
/* only send once per connect */
@ -4451,6 +4485,20 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock);
return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
if (rc) {
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsInTcon)
tcon->tidStatus = CifsNeedTcon;
spin_unlock(&cifs_tcp_ses_lock);
} else {
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsInTcon)
tcon->tidStatus = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
tcon->need_reconnect = false;
}
return rc;
}
#endif

View File

@ -1355,7 +1355,7 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach
}
cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__);
cifs_ses_mark_for_reconnect(tcon->ses);
cifs_reconnect(tcon->ses->server, true);
}
/* Refresh dfs referral of tcon and mark it for reconnect if needed */

View File

@ -22,6 +22,7 @@
#include "cifs_unicode.h"
#include "fs_context.h"
#include "cifs_ioctl.h"
#include "fscache.h"
static void
renew_parental_timestamps(struct dentry *direntry)
@ -507,8 +508,12 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
server->ops->close(xid, tcon, &fid);
cifs_del_pending_open(&open);
rc = -ENOMEM;
goto out;
}
fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
file->f_mode & FMODE_WRITE);
out:
cifs_put_tlink(tlink);
out_free_xid:

View File

@ -376,8 +376,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
struct cifsLockInfo *li, *tmp;
struct super_block *sb = inode->i_sb;
cifs_fscache_release_inode_cookie(inode);
/*
* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
@ -570,7 +568,7 @@ int cifs_open(struct inode *inode, struct file *file)
spin_lock(&CIFS_I(inode)->deferred_lock);
cifs_del_deferred_close(cfile);
spin_unlock(&CIFS_I(inode)->deferred_lock);
goto out;
goto use_cache;
} else {
_cifsFileInfo_put(cfile, true, false);
}
@ -632,8 +630,6 @@ int cifs_open(struct inode *inode, struct file *file)
goto out;
}
cifs_fscache_set_inode_cookie(inode, file);
if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) {
/*
* Time to set mode which we can not set earlier due to
@ -652,6 +648,15 @@ int cifs_open(struct inode *inode, struct file *file)
cfile->pid);
}
use_cache:
fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
file->f_mode & FMODE_WRITE);
if (file->f_flags & O_DIRECT &&
(!((file->f_flags & O_ACCMODE) != O_RDONLY) ||
file->f_flags & O_APPEND))
cifs_invalidate_cache(file_inode(file),
FSCACHE_INVAL_DIO_WRITE);
out:
free_dentry_path(page);
free_xid(xid);
@ -876,6 +881,8 @@ int cifs_close(struct inode *inode, struct file *file)
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_deferred_close *dclose;
cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE);
if (file->private_data != NULL) {
cfile = file->private_data;
file->private_data = NULL;
@ -886,7 +893,6 @@ int cifs_close(struct inode *inode, struct file *file)
dclose) {
if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) {
inode->i_ctime = inode->i_mtime = current_time(inode);
cifs_fscache_update_inode_cookie(inode);
}
spin_lock(&cinode->deferred_lock);
cifs_add_deferred_close(cfile, dclose);
@ -4198,10 +4204,12 @@ static vm_fault_t
cifs_page_mkwrite(struct vm_fault *vmf)
{
struct page *page = vmf->page;
struct file *file = vmf->vma->vm_file;
struct inode *inode = file_inode(file);
cifs_fscache_wait_on_page_write(inode, page);
#ifdef CONFIG_CIFS_FSCACHE
if (PageFsCache(page) &&
wait_on_page_fscache_killable(page) < 0)
return VM_FAULT_RETRY;
#endif
lock_page(page);
return VM_FAULT_LOCKED;
@ -4275,8 +4283,6 @@ cifs_readv_complete(struct work_struct *work)
if (rdata->result == 0 ||
(rdata->result == -EAGAIN && got_bytes))
cifs_readpage_to_fscache(rdata->mapping->host, page);
else
cifs_fscache_uncache_page(rdata->mapping->host, page);
got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes);
@ -4593,11 +4599,6 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
kref_put(&rdata->refcount, cifs_readdata_release);
}
/* Any pages that have been shown to fscache but didn't get added to
* the pagecache must be uncached before they get returned to the
* allocator.
*/
cifs_fscache_readpages_cancel(mapping->host, page_list);
free_xid(xid);
return rc;
}
@ -4801,17 +4802,19 @@ static int cifs_release_page(struct page *page, gfp_t gfp)
{
if (PagePrivate(page))
return 0;
return cifs_fscache_release_page(page, gfp);
if (PageFsCache(page)) {
if (current_is_kswapd() || !(gfp & __GFP_FS))
return false;
wait_on_page_fscache(page);
}
fscache_note_page_release(cifs_inode_cookie(page->mapping->host));
return true;
}
static void cifs_invalidate_page(struct page *page, unsigned int offset,
unsigned int length)
{
struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host);
if (offset == 0 && length == PAGE_SIZE)
cifs_fscache_invalidate_page(page, &cifsi->vfs_inode);
wait_on_page_fscache(page);
}
static int cifs_launder_page(struct page *page)
@ -4831,7 +4834,7 @@ static int cifs_launder_page(struct page *page)
if (clear_page_dirty_for_io(page))
rc = cifs_writepage_locked(page, &wbc);
cifs_fscache_invalidate_page(page, page->mapping->host);
wait_on_page_fscache(page);
return rc;
}
@ -4988,6 +4991,19 @@ static void cifs_swap_deactivate(struct file *file)
/* do we need to unpin (or unlock) the file */
}
/*
* Mark a page as having been made dirty and thus needing writeback. We also
* need to pin the cache object to write back to.
*/
#ifdef CONFIG_CIFS_FSCACHE
static int cifs_set_page_dirty(struct page *page)
{
return fscache_set_page_dirty(page, cifs_inode_cookie(page->mapping->host));
}
#else
#define cifs_set_page_dirty __set_page_dirty_nobuffers
#endif
const struct address_space_operations cifs_addr_ops = {
.readpage = cifs_readpage,
.readpages = cifs_readpages,
@ -4995,7 +5011,7 @@ const struct address_space_operations cifs_addr_ops = {
.writepages = cifs_writepages,
.write_begin = cifs_write_begin,
.write_end = cifs_write_end,
.set_page_dirty = __set_page_dirty_nobuffers,
.set_page_dirty = cifs_set_page_dirty,
.releasepage = cifs_release_page,
.direct_IO = cifs_direct_io,
.invalidatepage = cifs_invalidate_page,
@ -5020,7 +5036,7 @@ const struct address_space_operations cifs_addr_ops_smallbuf = {
.writepages = cifs_writepages,
.write_begin = cifs_write_begin,
.write_end = cifs_write_end,
.set_page_dirty = __set_page_dirty_nobuffers,
.set_page_dirty = cifs_set_page_dirty,
.releasepage = cifs_release_page,
.invalidatepage = cifs_invalidate_page,
.launder_page = cifs_launder_page,

View File

@ -37,6 +37,8 @@
#include "rfc1002pdu.h"
#include "fs_context.h"
static DEFINE_MUTEX(cifs_mount_mutex);
static const match_table_t cifs_smb_version_tokens = {
{ Smb_1, SMB1_VERSION_STRING },
{ Smb_20, SMB20_VERSION_STRING},
@ -707,10 +709,14 @@ static int smb3_get_tree_common(struct fs_context *fc)
static int smb3_get_tree(struct fs_context *fc)
{
int err = smb3_fs_context_validate(fc);
int ret;
if (err)
return err;
return smb3_get_tree_common(fc);
mutex_lock(&cifs_mount_mutex);
ret = smb3_get_tree_common(fc);
mutex_unlock(&cifs_mount_mutex);
return ret;
}
static void smb3_fs_context_free(struct fs_context *fc)

View File

@ -12,250 +12,136 @@
#include "cifs_fs_sb.h"
#include "cifsproto.h"
/*
* Key layout of CIFS server cache index object
*/
struct cifs_server_key {
__u64 conn_id;
} __packed;
/*
* Get a cookie for a server object keyed by {IPaddress,port,family} tuple
*/
void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server)
static void cifs_fscache_fill_volume_coherency(
struct cifs_tcon *tcon,
struct cifs_fscache_volume_coherency_data *cd)
{
struct cifs_server_key key;
memset(cd, 0, sizeof(*cd));
cd->resource_id = cpu_to_le64(tcon->resource_id);
cd->vol_create_time = tcon->vol_create_time;
cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number);
}
/*
* Check if cookie was already initialized so don't reinitialize it.
* In the future, as we integrate with newer fscache features,
* we may want to instead add a check if cookie has changed
*/
if (server->fscache)
return;
int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
{
struct cifs_fscache_volume_coherency_data cd;
struct TCP_Server_Info *server = tcon->ses->server;
struct fscache_volume *vcookie;
const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr;
size_t slen, i;
char *sharename;
char *key;
int ret = -ENOMEM;
tcon->fscache = NULL;
switch (sa->sa_family) {
case AF_INET:
case AF_INET6:
break;
default:
cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);
return -EINVAL;
}
memset(&key, 0, sizeof(key));
key.conn_id = server->conn_id;
server->fscache =
fscache_acquire_cookie(cifs_fscache_netfs.primary_index,
&cifs_fscache_server_index_def,
&key, sizeof(key),
NULL, 0,
server, 0, true);
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
__func__, server, server->fscache);
}
void cifs_fscache_release_client_cookie(struct TCP_Server_Info *server)
{
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
__func__, server, server->fscache);
fscache_relinquish_cookie(server->fscache, NULL, false);
server->fscache = NULL;
}
void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
{
struct TCP_Server_Info *server = tcon->ses->server;
char *sharename;
struct cifs_fscache_super_auxdata auxdata;
/*
* Check if cookie was already initialized so don't reinitialize it.
* In the future, as we integrate with newer fscache features,
* we may want to instead add a check if cookie has changed
*/
if (tcon->fscache)
return;
sharename = extract_sharename(tcon->treeName);
if (IS_ERR(sharename)) {
cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
tcon->fscache = NULL;
return;
return -EINVAL;
}
memset(&auxdata, 0, sizeof(auxdata));
auxdata.resource_id = tcon->resource_id;
auxdata.vol_create_time = tcon->vol_create_time;
auxdata.vol_serial_number = tcon->vol_serial_number;
slen = strlen(sharename);
for (i = 0; i < slen; i++)
if (sharename[i] == '/')
sharename[i] = ';';
tcon->fscache =
fscache_acquire_cookie(server->fscache,
&cifs_fscache_super_index_def,
sharename, strlen(sharename),
&auxdata, sizeof(auxdata),
tcon, 0, true);
key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename);
if (!key)
goto out;
cifs_fscache_fill_volume_coherency(tcon, &cd);
vcookie = fscache_acquire_volume(key,
NULL, /* preferred_cache */
&cd, sizeof(cd));
cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie);
if (IS_ERR(vcookie)) {
if (vcookie != ERR_PTR(-EBUSY)) {
ret = PTR_ERR(vcookie);
goto out_2;
}
pr_err("Cache volume key already in use (%s)\n", key);
vcookie = NULL;
}
tcon->fscache = vcookie;
ret = 0;
out_2:
kfree(key);
out:
kfree(sharename);
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
__func__, server->fscache, tcon->fscache);
return ret;
}
void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
{
struct cifs_fscache_super_auxdata auxdata;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.resource_id = tcon->resource_id;
auxdata.vol_create_time = tcon->vol_create_time;
auxdata.vol_serial_number = tcon->vol_serial_number;
struct cifs_fscache_volume_coherency_data cd;
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache);
fscache_relinquish_cookie(tcon->fscache, &auxdata, false);
cifs_fscache_fill_volume_coherency(tcon, &cd);
fscache_relinquish_volume(tcon->fscache, &cd, false);
tcon->fscache = NULL;
}
static void cifs_fscache_acquire_inode_cookie(struct cifsInodeInfo *cifsi,
struct cifs_tcon *tcon)
{
struct cifs_fscache_inode_auxdata auxdata;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
cifsi->fscache =
fscache_acquire_cookie(tcon->fscache,
&cifs_fscache_inode_object_def,
&cifsi->uniqueid, sizeof(cifsi->uniqueid),
&auxdata, sizeof(auxdata),
cifsi, cifsi->vfs_inode.i_size, true);
}
static void cifs_fscache_enable_inode_cookie(struct inode *inode)
void cifs_fscache_get_inode_cookie(struct inode *inode)
{
struct cifs_fscache_inode_coherency_data cd;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
if (cifsi->fscache)
return;
cifs_fscache_fill_coherency(&cifsi->vfs_inode, &cd);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE))
return;
cifsi->fscache =
fscache_acquire_cookie(tcon->fscache, 0,
&cifsi->uniqueid, sizeof(cifsi->uniqueid),
&cd, sizeof(cd),
i_size_read(&cifsi->vfs_inode));
}
cifs_fscache_acquire_inode_cookie(cifsi, tcon);
void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update)
{
if (update) {
struct cifs_fscache_inode_coherency_data cd;
loff_t i_size = i_size_read(inode);
cifs_dbg(FYI, "%s: got FH cookie (0x%p/0x%p)\n",
__func__, tcon->fscache, cifsi->fscache);
cifs_fscache_fill_coherency(inode, &cd);
fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size);
} else {
fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL);
}
}
void cifs_fscache_release_inode_cookie(struct inode *inode)
{
struct cifs_fscache_inode_auxdata auxdata;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
if (cifsi->fscache) {
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
/* fscache_relinquish_cookie does not seem to update auxdata */
fscache_update_cookie(cifsi->fscache, &auxdata);
fscache_relinquish_cookie(cifsi->fscache, &auxdata, false);
fscache_relinquish_cookie(cifsi->fscache, false);
cifsi->fscache = NULL;
}
}
void cifs_fscache_update_inode_cookie(struct inode *inode)
{
struct cifs_fscache_inode_auxdata auxdata;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
if (cifsi->fscache) {
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
fscache_update_cookie(cifsi->fscache, &auxdata);
}
}
void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp)
{
cifs_fscache_enable_inode_cookie(inode);
}
void cifs_fscache_reset_inode_cookie(struct inode *inode)
{
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct fscache_cookie *old = cifsi->fscache;
if (cifsi->fscache) {
/* retire the current fscache cache and get a new one */
fscache_relinquish_cookie(cifsi->fscache, NULL, true);
cifs_fscache_acquire_inode_cookie(cifsi, tcon);
cifs_dbg(FYI, "%s: new cookie 0x%p oldcookie 0x%p\n",
__func__, cifsi->fscache, old);
}
}
int cifs_fscache_release_page(struct page *page, gfp_t gfp)
{
if (PageFsCache(page)) {
struct inode *inode = page->mapping->host;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
__func__, page, cifsi->fscache);
if (!fscache_maybe_release_page(cifsi->fscache, page, gfp))
return 0;
}
return 1;
}
static void cifs_readpage_from_fscache_complete(struct page *page, void *ctx,
int error)
{
cifs_dbg(FYI, "%s: (0x%p/%d)\n", __func__, page, error);
if (!error)
SetPageUptodate(page);
unlock_page(page);
}
/*
* Retrieve a page from FS-Cache
*/
int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)
{
int ret;
cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n",
__func__, CIFS_I(inode)->fscache, page, inode);
ret = fscache_read_or_alloc_page(CIFS_I(inode)->fscache, page,
cifs_readpage_from_fscache_complete,
NULL,
GFP_KERNEL);
switch (ret) {
case 0: /* page found in fscache, read submitted */
cifs_dbg(FYI, "%s: submitted\n", __func__);
return ret;
case -ENOBUFS: /* page won't be cached */
case -ENODATA: /* page not in cache */
cifs_dbg(FYI, "%s: %d\n", __func__, ret);
return 1;
default:
cifs_dbg(VFS, "unknown error ret = %d\n", ret);
}
return ret;
return -ENOBUFS; // Needs conversion to using netfslib
}
/*
@ -266,78 +152,19 @@ int __cifs_readpages_from_fscache(struct inode *inode,
struct list_head *pages,
unsigned *nr_pages)
{
int ret;
cifs_dbg(FYI, "%s: (0x%p/%u/0x%p)\n",
__func__, CIFS_I(inode)->fscache, *nr_pages, inode);
ret = fscache_read_or_alloc_pages(CIFS_I(inode)->fscache, mapping,
pages, nr_pages,
cifs_readpage_from_fscache_complete,
NULL,
mapping_gfp_mask(mapping));
switch (ret) {
case 0: /* read submitted to the cache for all pages */
cifs_dbg(FYI, "%s: submitted\n", __func__);
return ret;
case -ENOBUFS: /* some pages are not cached and can't be */
case -ENODATA: /* some pages are not cached */
cifs_dbg(FYI, "%s: no page\n", __func__);
return 1;
default:
cifs_dbg(FYI, "unknown error ret = %d\n", ret);
}
return ret;
return -ENOBUFS; // Needs conversion to using netfslib
}
void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)
{
struct cifsInodeInfo *cifsi = CIFS_I(inode);
int ret;
WARN_ON(!cifsi->fscache);
cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n",
__func__, cifsi->fscache, page, inode);
ret = fscache_write_page(cifsi->fscache, page,
cifsi->vfs_inode.i_size, GFP_KERNEL);
if (ret != 0)
fscache_uncache_page(cifsi->fscache, page);
}
void __cifs_fscache_readpages_cancel(struct inode *inode, struct list_head *pages)
{
cifs_dbg(FYI, "%s: (fsc: %p, i: %p)\n",
__func__, CIFS_I(inode)->fscache, inode);
fscache_readpages_cancel(CIFS_I(inode)->fscache, pages);
}
void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode)
{
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct fscache_cookie *cookie = cifsi->fscache;
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie);
fscache_wait_on_page_write(cookie, page);
fscache_uncache_page(cookie, page);
}
void __cifs_fscache_wait_on_page_write(struct inode *inode, struct page *page)
{
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct fscache_cookie *cookie = cifsi->fscache;
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie);
fscache_wait_on_page_write(cookie, page);
}
void __cifs_fscache_uncache_page(struct inode *inode, struct page *page)
{
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct fscache_cookie *cookie = cifsi->fscache;
cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie);
fscache_uncache_page(cookie, page);
// Needs conversion to using netfslib
}

View File

@ -13,84 +13,71 @@
#include "cifsglob.h"
#ifdef CONFIG_CIFS_FSCACHE
/*
* Auxiliary data attached to CIFS superblock within the cache
* Coherency data attached to CIFS volume within the cache
*/
struct cifs_fscache_super_auxdata {
u64 resource_id; /* unique server resource id */
struct cifs_fscache_volume_coherency_data {
__le64 resource_id; /* unique server resource id */
__le64 vol_create_time;
u32 vol_serial_number;
__le32 vol_serial_number;
} __packed;
/*
* Auxiliary data attached to CIFS inode within the cache
* Coherency data attached to CIFS inode within the cache.
*/
struct cifs_fscache_inode_auxdata {
u64 last_write_time_sec;
u64 last_change_time_sec;
u32 last_write_time_nsec;
u32 last_change_time_nsec;
u64 eof;
struct cifs_fscache_inode_coherency_data {
__le64 last_write_time_sec;
__le64 last_change_time_sec;
__le32 last_write_time_nsec;
__le32 last_change_time_nsec;
};
/*
* cache.c
*/
extern struct fscache_netfs cifs_fscache_netfs;
extern const struct fscache_cookie_def cifs_fscache_server_index_def;
extern const struct fscache_cookie_def cifs_fscache_super_index_def;
extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
extern int cifs_fscache_register(void);
extern void cifs_fscache_unregister(void);
#ifdef CONFIG_CIFS_FSCACHE
/*
* fscache.c
*/
extern void cifs_fscache_get_client_cookie(struct TCP_Server_Info *);
extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *);
extern void cifs_fscache_get_super_cookie(struct cifs_tcon *);
extern int cifs_fscache_get_super_cookie(struct cifs_tcon *);
extern void cifs_fscache_release_super_cookie(struct cifs_tcon *);
extern void cifs_fscache_get_inode_cookie(struct inode *inode);
extern void cifs_fscache_release_inode_cookie(struct inode *);
extern void cifs_fscache_update_inode_cookie(struct inode *inode);
extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *);
extern void cifs_fscache_reset_inode_cookie(struct inode *);
extern void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update);
static inline
void cifs_fscache_fill_coherency(struct inode *inode,
struct cifs_fscache_inode_coherency_data *cd)
{
struct cifsInodeInfo *cifsi = CIFS_I(inode);
memset(cd, 0, sizeof(*cd));
cd->last_write_time_sec = cpu_to_le64(cifsi->vfs_inode.i_mtime.tv_sec);
cd->last_write_time_nsec = cpu_to_le32(cifsi->vfs_inode.i_mtime.tv_nsec);
cd->last_change_time_sec = cpu_to_le64(cifsi->vfs_inode.i_ctime.tv_sec);
cd->last_change_time_nsec = cpu_to_le32(cifsi->vfs_inode.i_ctime.tv_nsec);
}
extern void __cifs_fscache_invalidate_page(struct page *, struct inode *);
extern void __cifs_fscache_wait_on_page_write(struct inode *inode, struct page *page);
extern void __cifs_fscache_uncache_page(struct inode *inode, struct page *page);
extern int cifs_fscache_release_page(struct page *page, gfp_t gfp);
extern int __cifs_readpage_from_fscache(struct inode *, struct page *);
extern int __cifs_readpages_from_fscache(struct inode *,
struct address_space *,
struct list_head *,
unsigned *);
extern void __cifs_fscache_readpages_cancel(struct inode *, struct list_head *);
extern void __cifs_readpage_to_fscache(struct inode *, struct page *);
static inline void cifs_fscache_invalidate_page(struct page *page,
struct inode *inode)
static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode)
{
if (PageFsCache(page))
__cifs_fscache_invalidate_page(page, inode);
return CIFS_I(inode)->fscache;
}
static inline void cifs_fscache_wait_on_page_write(struct inode *inode,
struct page *page)
static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags)
{
if (PageFsCache(page))
__cifs_fscache_wait_on_page_write(inode, page);
}
struct cifs_fscache_inode_coherency_data cd;
static inline void cifs_fscache_uncache_page(struct inode *inode,
struct page *page)
{
if (PageFsCache(page))
__cifs_fscache_uncache_page(inode, page);
cifs_fscache_fill_coherency(inode, &cd);
fscache_invalidate(cifs_inode_cookie(inode), &cd,
i_size_read(inode), flags);
}
static inline int cifs_readpage_from_fscache(struct inode *inode,
@ -120,41 +107,21 @@ static inline void cifs_readpage_to_fscache(struct inode *inode,
__cifs_readpage_to_fscache(inode, page);
}
static inline void cifs_fscache_readpages_cancel(struct inode *inode,
struct list_head *pages)
{
if (CIFS_I(inode)->fscache)
return __cifs_fscache_readpages_cancel(inode, pages);
}
#else /* CONFIG_CIFS_FSCACHE */
static inline int cifs_fscache_register(void) { return 0; }
static inline void cifs_fscache_unregister(void) {}
static inline void
cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {}
static inline void
cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) {}
static inline void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) {}
static inline void
cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {}
static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
static inline void cifs_fscache_update_inode_cookie(struct inode *inode) {}
static inline void cifs_fscache_set_inode_cookie(struct inode *inode,
struct file *filp) {}
static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {}
static inline int cifs_fscache_release_page(struct page *page, gfp_t gfp)
static inline
void cifs_fscache_fill_coherency(struct inode *inode,
struct cifs_fscache_inode_coherency_data *cd)
{
return 1; /* May release page */
}
static inline void cifs_fscache_invalidate_page(struct page *page,
struct inode *inode) {}
static inline void cifs_fscache_wait_on_page_write(struct inode *inode,
struct page *page) {}
static inline void cifs_fscache_uncache_page(struct inode *inode,
struct page *page) {}
static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { return 0; }
static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {}
static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {}
static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {}
static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; }
static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {}
static inline int
cifs_readpage_from_fscache(struct inode *inode, struct page *page)
@ -173,11 +140,6 @@ static inline int cifs_readpages_from_fscache(struct inode *inode,
static inline void cifs_readpage_to_fscache(struct inode *inode,
struct page *page) {}
static inline void cifs_fscache_readpages_cancel(struct inode *inode,
struct list_head *pages)
{
}
#endif /* CONFIG_CIFS_FSCACHE */
#endif /* _CIFS_FSCACHE_H */

View File

@ -952,6 +952,12 @@ cifs_get_inode_info(struct inode **inode,
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
full_path, tmp_data,
&adjust_tz, &is_reparse_point);
#ifdef CONFIG_CIFS_DFS_UPCALL
if (rc == -ENOENT && is_tcon_dfs(tcon))
rc = cifs_dfs_query_info_nonascii_quirk(xid, tcon,
cifs_sb,
full_path);
#endif
data = tmp_data;
}
@ -1298,10 +1304,7 @@ retry_iget5_locked:
inode->i_flags |= S_NOATIME | S_NOCMTIME;
if (inode->i_state & I_NEW) {
inode->i_ino = hash;
#ifdef CONFIG_CIFS_FSCACHE
/* initialize per-inode cache cookie pointer */
CIFS_I(inode)->fscache = NULL;
#endif
cifs_fscache_get_inode_cookie(inode);
unlock_new_inode(inode);
}
}
@ -1370,6 +1373,7 @@ iget_no_retry:
iget_failed(inode);
inode = ERR_PTR(rc);
}
out:
kfree(path);
free_xid(xid);
@ -2257,6 +2261,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)
int
cifs_invalidate_mapping(struct inode *inode)
{
struct cifs_fscache_inode_coherency_data cd;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
int rc = 0;
if (inode->i_mapping && inode->i_mapping->nrpages != 0) {
@ -2266,7 +2272,8 @@ cifs_invalidate_mapping(struct inode *inode)
__func__, inode);
}
cifs_fscache_reset_inode_cookie(inode);
cifs_fscache_fill_coherency(&cifsi->vfs_inode, &cd);
fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0);
return rc;
}
@ -2771,8 +2778,10 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
goto out;
if ((attrs->ia_valid & ATTR_SIZE) &&
attrs->ia_size != i_size_read(inode))
attrs->ia_size != i_size_read(inode)) {
truncate_setsize(inode, attrs->ia_size);
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
}
setattr_copy(&init_user_ns, inode, attrs);
mark_inode_dirty(inode);
@ -2967,8 +2976,10 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
goto cifs_setattr_exit;
if ((attrs->ia_valid & ATTR_SIZE) &&
attrs->ia_size != i_size_read(inode))
attrs->ia_size != i_size_read(inode)) {
truncate_setsize(inode, attrs->ia_size);
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
}
setattr_copy(&init_user_ns, inode, attrs);
mark_inode_dirty(inode);

View File

@ -1302,4 +1302,53 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
return 0;
}
/** cifs_dfs_query_info_nonascii_quirk
* Handle weird Windows SMB server behaviour. It responds with
* STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
* for "\<server>\<dfsname>\<linkpath>" DFS reference,
* where <dfsname> contains non-ASCII unicode symbols.
*
* Check such DFS reference and emulate -ENOENT if it is actual.
*/
int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *linkpath)
{
char *treename, *dfspath, sep;
int treenamelen, linkpathlen, rc;
treename = tcon->treeName;
/* MS-DFSC: All paths in REQ_GET_DFS_REFERRAL and RESP_GET_DFS_REFERRAL
* messages MUST be encoded with exactly one leading backslash, not two
* leading backslashes.
*/
sep = CIFS_DIR_SEP(cifs_sb);
if (treename[0] == sep && treename[1] == sep)
treename++;
linkpathlen = strlen(linkpath);
treenamelen = strnlen(treename, MAX_TREE_SIZE + 1);
dfspath = kzalloc(treenamelen + linkpathlen + 1, GFP_KERNEL);
if (!dfspath)
return -ENOMEM;
if (treenamelen)
memcpy(dfspath, treename, treenamelen);
memcpy(dfspath + treenamelen, linkpath, linkpathlen);
rc = dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls,
cifs_remap(cifs_sb), dfspath, NULL, NULL);
if (rc == 0) {
cifs_dbg(FYI, "DFS ref '%s' is found, emulate -EREMOTE\n",
dfspath);
rc = -EREMOTE;
} else if (rc == -EEXIST) {
cifs_dbg(FYI, "DFS ref '%s' is not found, emulate -ENOENT\n",
dfspath);
rc = -ENOENT;
} else {
cifs_dbg(FYI, "%s: dfs_cache_find returned %d\n", __func__, rc);
}
kfree(dfspath);
return rc;
}
#endif

View File

@ -896,10 +896,7 @@ map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)
if (class == ERRSRV && code == ERRbaduid) {
cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
code);
spin_lock(&cifs_tcp_ses_lock);
if (mid->server->tcpStatus != CifsExiting)
mid->server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
cifs_reconnect(mid->server, false);
}
}

View File

@ -40,7 +40,7 @@
#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000
#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000
/* #define reserved4 0x1000000 */
#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */
#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we only set for SMB2+ */
/* #define reserved3 0x4000000 */
/* #define reserved2 0x8000000 */
/* #define reserved1 0x10000000 */
@ -87,6 +87,30 @@ typedef struct _NEGOTIATE_MESSAGE {
/* followed by WorkstationString */
} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
#define NTLMSSP_REVISION_W2K3 0x0F
/* See MS-NLMP section 2.2.2.10 */
struct ntlmssp_version {
__u8 ProductMajorVersion;
__u8 ProductMinorVersion;
__le16 ProductBuild; /* we send the cifs.ko module version here */
__u8 Reserved[3];
__u8 NTLMRevisionCurrent; /* currently 0x0F */
} __packed;
/* see MS-NLMP section 2.2.1.1 */
struct negotiate_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmNegotiate = 1 */
__le32 NegotiateFlags;
SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */
SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */
struct ntlmssp_version Version;
/* SECURITY_BUFFER */
char DomainString[0];
/* followed by WorkstationString */
} __packed;
typedef struct _CHALLENGE_MESSAGE {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmChallenge = 2 */
@ -123,6 +147,10 @@ int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp);
int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp);
int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
struct TCP_Server_Info *server,

View File

@ -17,6 +17,8 @@
#include "nterr.h"
#include <linux/utsname.h>
#include <linux/slab.h>
#include <linux/version.h>
#include "cifsfs.h"
#include "cifs_spnego.h"
#include "smb2proto.h"
#include "fs_context.h"
@ -65,6 +67,8 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
return false;
}
/* channel helper functions. assumed that chan_lock is held by caller. */
unsigned int
cifs_ses_get_chan_index(struct cifs_ses *ses,
struct TCP_Server_Info *server)
@ -134,10 +138,10 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
left = ses->chan_max - ses->chan_count;
if (left <= 0) {
spin_unlock(&ses->chan_lock);
cifs_dbg(FYI,
"ses already at max_channels (%zu), nothing to open\n",
ses->chan_max);
spin_unlock(&ses->chan_lock);
return 0;
}
@ -364,19 +368,6 @@ out:
return rc;
}
/* Mark all session channels for reconnect */
void cifs_ses_mark_for_reconnect(struct cifs_ses *ses)
{
int i;
for (i = 0; i < ses->chan_count; i++) {
spin_lock(&cifs_tcp_ses_lock);
if (ses->chans[i].server->tcpStatus != CifsExiting)
ses->chans[i].server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
}
}
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
struct TCP_Server_Info *server,
SESSION_SETUP_ANDX *pSMB)
@ -820,6 +811,74 @@ setup_ntlm_neg_ret:
return rc;
}
/*
* Build ntlmssp blob with additional fields, such as version,
* supported by modern servers. For safety limit to SMB3 or later
* See notes in MS-NLMP Section 2.2.2.1 e.g.
*/
int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer,
u16 *buflen,
struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp)
{
int rc = 0;
struct negotiate_message *sec_blob;
__u32 flags;
unsigned char *tmp;
int len;
len = size_of_ntlmssp_blob(ses, sizeof(struct negotiate_message));
*pbuffer = kmalloc(len, GFP_KERNEL);
if (!*pbuffer) {
rc = -ENOMEM;
cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
*buflen = 0;
goto setup_ntlm_smb3_neg_ret;
}
sec_blob = (struct negotiate_message *)*pbuffer;
memset(*pbuffer, 0, sizeof(struct negotiate_message));
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
sec_blob->MessageType = NtLmNegotiate;
/* BB is NTLMV2 session security format easier to use here? */
flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL |
NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION;
if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR;
sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL;
sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD);
sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
tmp = *pbuffer + sizeof(struct negotiate_message);
ses->ntlmssp->client_flags = flags;
sec_blob->NegotiateFlags = cpu_to_le32(flags);
/* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */
cifs_security_buffer_from_str(&sec_blob->DomainName,
NULL,
CIFS_MAX_DOMAINNAME_LEN,
*pbuffer, &tmp,
nls_cp);
cifs_security_buffer_from_str(&sec_blob->WorkstationName,
NULL,
CIFS_MAX_WORKSTATION_LEN,
*pbuffer, &tmp,
nls_cp);
*buflen = tmp - *pbuffer;
setup_ntlm_smb3_neg_ret:
return rc;
}
int build_ntlmssp_auth_blob(unsigned char **pbuffer,
u16 *buflen,
struct cifs_ses *ses,
@ -1048,16 +1107,6 @@ sess_establish_session(struct sess_data *sess_data)
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "CIFS session established successfully\n");
spin_lock(&ses->chan_lock);
cifs_chan_clear_need_reconnect(ses, server);
spin_unlock(&ses->chan_lock);
/* Even if one channel is active, session is in good state */
spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsGood;
ses->status = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
@ -1413,7 +1462,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
&blob_len, ses, server,
sess_data->nls_cp);
if (rc)
goto out;
goto out_free_ntlmsspblob;
sess_data->iov[1].iov_len = blob_len;
sess_data->iov[1].iov_base = ntlmsspblob;
@ -1421,7 +1470,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
if (rc)
goto out;
goto out_free_ntlmsspblob;
rc = sess_sendreceive(sess_data);
@ -1435,14 +1484,14 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
rc = 0;
if (rc)
goto out;
goto out_free_ntlmsspblob;
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
if (smb_buf->WordCount != 4) {
rc = -EIO;
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto out;
goto out_free_ntlmsspblob;
}
ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
@ -1456,10 +1505,13 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
cifs_dbg(VFS, "bad security blob length %d\n",
blob_len);
rc = -EINVAL;
goto out;
goto out_free_ntlmsspblob;
}
rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
out_free_ntlmsspblob:
kfree(ntlmsspblob);
out:
sess_free_buffer(sess_data);
@ -1574,7 +1626,7 @@ out_free_ntlmsspblob:
out:
sess_free_buffer(sess_data);
if (!rc)
if (!rc)
rc = sess_establish_session(sess_data);
/* Cleanup */

View File

@ -244,10 +244,10 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
spin_unlock(&ses->chan_lock);
return 0;
}
spin_unlock(&ses->chan_lock);
cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d",
tcon->ses->chans_need_reconnect,
tcon->need_reconnect);
spin_unlock(&ses->chan_lock);
nls_codepage = load_nls_default();
@ -289,14 +289,18 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
rc = -EHOSTDOWN;
goto failed;
}
}
if (rc || !tcon->need_reconnect) {
} else {
mutex_unlock(&ses->session_mutex);
goto out;
}
mutex_unlock(&ses->session_mutex);
skip_sess_setup:
mutex_lock(&ses->session_mutex);
if (!tcon->need_reconnect) {
mutex_unlock(&ses->session_mutex);
goto out;
}
cifs_mark_open_files_invalid(tcon);
if (tcon->use_persistent)
tcon->need_reopen_files = true;
@ -1382,17 +1386,6 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
spin_lock(&ses->chan_lock);
cifs_chan_clear_need_reconnect(ses, server);
spin_unlock(&ses->chan_lock);
/* Even if one channel is active, session is in good state */
spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsGood;
ses->status = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
return rc;
}
@ -1513,7 +1506,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
if (rc)
goto out_err;
rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob,
rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob,
&blob_length, ses, server,
sess_data->nls_cp);
if (rc)
@ -1920,10 +1913,6 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
spin_lock(&cifs_tcp_ses_lock);
tcon->tidStatus = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
tcon->need_reconnect = false;
tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId);
strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
@ -2587,8 +2576,13 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
cp = load_nls_default();
cifs_strtoUTF16(*out_path, treename, treename_len, cp);
UniStrcat(*out_path, sep);
UniStrcat(*out_path, path);
/* Do not append the separator if the path is empty */
if (path[0] != cpu_to_le16(0x0000)) {
UniStrcat(*out_path, sep);
UniStrcat(*out_path, path);
}
unload_nls(cp);
return 0;
@ -3782,27 +3776,35 @@ void smb2_reconnect_server(struct work_struct *work)
{
struct TCP_Server_Info *server = container_of(work,
struct TCP_Server_Info, reconnect.work);
struct cifs_ses *ses;
struct TCP_Server_Info *pserver;
struct cifs_ses *ses, *ses2;
struct cifs_tcon *tcon, *tcon2;
struct list_head tmp_list;
int tcon_exist = false;
struct list_head tmp_list, tmp_ses_list;
bool tcon_exist = false, ses_exist = false;
bool tcon_selected = false;
int rc;
int resched = false;
bool resched = false;
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock(&server->reconnect_mutex);
mutex_lock(&pserver->reconnect_mutex);
INIT_LIST_HEAD(&tmp_list);
cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
INIT_LIST_HEAD(&tmp_ses_list);
cifs_dbg(FYI, "Reconnecting tcons and channels\n");
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
tcon_selected = false;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->need_reconnect || tcon->need_reopen_files) {
tcon->tc_count++;
list_add_tail(&tcon->rlist, &tmp_list);
tcon_exist = true;
tcon_selected = tcon_exist = true;
}
}
/*
@ -3811,15 +3813,27 @@ void smb2_reconnect_server(struct work_struct *work)
*/
if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
tcon_exist = true;
tcon_selected = tcon_exist = true;
ses->ses_count++;
}
/*
* handle the case where channel needs to reconnect
* binding session, but tcon is healthy (some other channel
* is active)
*/
spin_lock(&ses->chan_lock);
if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) {
list_add_tail(&ses->rlist, &tmp_ses_list);
ses_exist = true;
ses->ses_count++;
}
spin_unlock(&ses->chan_lock);
}
/*
* Get the reference to server struct to be sure that the last call of
* cifs_put_tcon() in the loop below won't release the server pointer.
*/
if (tcon_exist)
if (tcon_exist || ses_exist)
server->srv_count++;
spin_unlock(&cifs_tcp_ses_lock);
@ -3837,13 +3851,41 @@ void smb2_reconnect_server(struct work_struct *work)
cifs_put_tcon(tcon);
}
cifs_dbg(FYI, "Reconnecting tcons finished\n");
if (!ses_exist)
goto done;
/* allocate a dummy tcon struct used for reconnect */
tcon = kzalloc(sizeof(struct cifs_tcon), GFP_KERNEL);
if (!tcon) {
resched = true;
list_del_init(&ses->rlist);
cifs_put_smb_ses(ses);
goto done;
}
tcon->tidStatus = CifsGood;
tcon->retry = false;
tcon->need_reconnect = false;
/* now reconnect sessions for necessary channels */
list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
tcon->ses = ses;
rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server);
if (rc)
resched = true;
list_del_init(&ses->rlist);
cifs_put_smb_ses(ses);
}
kfree(tcon);
done:
cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
if (resched)
queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
mutex_unlock(&server->reconnect_mutex);
mutex_unlock(&pserver->reconnect_mutex);
/* now we can safely release srv struct */
if (tcon_exist)
if (tcon_exist || ses_exist)
cifs_put_tcp_session(server, 1);
}

View File

@ -100,6 +100,7 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
goto out;
found:
spin_lock(&ses->chan_lock);
if (cifs_chan_needs_reconnect(ses, server) &&
!CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
/*
@ -108,6 +109,7 @@ found:
* session key
*/
memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
spin_unlock(&ses->chan_lock);
goto out;
}
@ -119,9 +121,11 @@ found:
chan = ses->chans + i;
if (chan->server == server) {
memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE);
spin_unlock(&ses->chan_lock);
goto out;
}
}
spin_unlock(&ses->chan_lock);
cifs_dbg(VFS,
"%s: Could not find channel signing key for session 0x%llx\n",
@ -430,8 +434,10 @@ generate_smb3signingkey(struct cifs_ses *ses,
return rc;
/* safe to access primary channel, since it will never go away */
spin_lock(&ses->chan_lock);
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
SMB3_SIGN_KEY_SIZE);
spin_unlock(&ses->chan_lock);
rc = generate_key(ses, ptriplet->encryption.label,
ptriplet->encryption.context,

View File

@ -431,7 +431,8 @@ unmask:
* socket so the server throws away the partial SMB
*/
spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsNeedReconnect;
if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
trace_smb3_partial_send_reconnect(server->CurrentMid,
server->conn_id, server->hostname);
@ -729,17 +730,6 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
struct mid_q_entry **ppmidQ)
{
spin_lock(&cifs_tcp_ses_lock);
if (ses->server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT;
}
if (ses->server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
return -EAGAIN;
}
if (ses->status == CifsNew) {
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE)) {
@ -1059,7 +1049,10 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
/* round robin */
index = (uint)atomic_inc_return(&ses->chan_seq);
spin_lock(&ses->chan_lock);
index %= ses->chan_count;
spin_unlock(&ses->chan_lock);
return ses->chans[index].server;
}

View File

@ -449,7 +449,7 @@ struct smb2_netname_neg_context {
*/
/* Flags */
#define SMB2_ACCEPT_TRANSFORM_LEVEL_SECURITY 0x00000001
#define SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY 0x00000001
struct smb2_transport_capabilities_context {
__le16 ContextType; /* 6 */

View File

@ -95,8 +95,10 @@
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
#define FSCTL_GET_REFS_VOLUME_DATA 0x000902D8 /* See MS-FSCC 2.3.24 */
#define FSCTL_SET_INTEGRITY_INFORMATION_EXT 0x00090380
#define FSCTL_GET_RETRIEVAL_POINTERS_AND_REFCOUNT 0x000903d3
#define FSCTL_GET_RETRIEVAL_POINTER_COUNT 0x0009042b
#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT 0x00090440
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */