Commit 8205ae32 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 1cb69c80 51620150
......@@ -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
......
// 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 @@ init_cifs(void)
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)
......
This diff is collapsed.
......@@ -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)
......
This diff is collapsed.
......@@ -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)
#else /* CONFIG_CIFS_FSCACHE */
static inline
void cifs_fscache_fill_coherency(struct inode *inode,
struct cifs_fscache_inode_coherency_data *cd)
{
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 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_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)
{
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 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 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
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 @@ struct inode *cifs_root_iget(struct super_block *sb)
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,
......
......@@ -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 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
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 @@ int build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
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);
......
......@@ -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);
/* 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 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
* session key
*/
memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
spin_unlock(&ses->chan_lock);
goto out;
}
......@@ -119,9 +121,11 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
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,6 +431,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
* socket so the server throws away the partial SMB
*/
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
trace_smb3_partial_send_reconnect(server->CurrentMid,
......@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment