mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
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:
commit
8205ae327e
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
105
fs/cifs/cache.c
105
fs/cifs/cache.c
@ -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,
|
||||
};
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
112
fs/cifs/sess.c
112
fs/cifs/sess.c
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user