Commit c8103c27 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull more cifs updates from Steve French:

 - improvements to reconnect and multichannel

 - a performance improvement (additional use of SMB3 compounding)

 - DFS code cleanup and improvements

 - various trivial Coverity fixes

 - two fscache fixes

 - an fsync fix

* tag '5.16-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: (23 commits)
  cifs: do not duplicate fscache cookie for secondary channels
  cifs: connect individual channel servers to primary channel server
  cifs: protect session channel fields with chan_lock
  cifs: do not negotiate session if session already exists
  smb3: do not setup the fscache_super_cookie until fsinfo initialized
  cifs: fix potential use-after-free bugs
  cifs: fix memory leak of smb3_fs_context_dup::server_hostname
  smb3: add additional null check in SMB311_posix_mkdir
  cifs: release lock earlier in dequeue_mid error case
  smb3: add additional null check in SMB2_tcon
  smb3: add additional null check in SMB2_open
  smb3: add additional null check in SMB2_ioctl
  smb3: remove trivial dfs compile warning
  cifs: support nested dfs links over reconnect
  smb3: do not error on fsync when readonly
  cifs: for compound requests, use open handle if possible
  cifs: set a minimum of 120s for next dns resolution
  cifs: split out dfs code from cifs_reconnect()
  cifs: convert list_for_each to entry variant
  cifs: introduce new helper for cifs_reconnect()
  ...
parents d0b51bfb 46bb1b94
...@@ -271,7 +271,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -271,7 +271,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
c = 0; c = 0;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
if (server->is_channel) /* channel info will be printed as a part of sessions below */
if (CIFS_SERVER_IS_CHAN(server))
continue; continue;
c++; c++;
...@@ -358,6 +359,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -358,6 +359,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, " signed"); seq_printf(m, " signed");
if (server->posix_ext_supported) if (server->posix_ext_supported)
seq_printf(m, " posix"); seq_printf(m, " posix");
if (server->nosharesock)
seq_printf(m, " nosharesock");
if (server->rdma) if (server->rdma)
seq_printf(m, "\nRDMA "); seq_printf(m, "\nRDMA ");
...@@ -412,12 +415,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -412,12 +415,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
from_kuid(&init_user_ns, ses->linux_uid), from_kuid(&init_user_ns, ses->linux_uid),
from_kuid(&init_user_ns, ses->cred_uid)); from_kuid(&init_user_ns, ses->cred_uid));
spin_lock(&ses->chan_lock);
if (ses->chan_count > 1) { if (ses->chan_count > 1) {
seq_printf(m, "\n\n\tExtra Channels: %zu ", seq_printf(m, "\n\n\tExtra Channels: %zu ",
ses->chan_count-1); ses->chan_count-1);
for (j = 1; j < ses->chan_count; j++) for (j = 1; j < ses->chan_count; j++)
cifs_dump_channel(m, j, &ses->chans[j]); cifs_dump_channel(m, j, &ses->chans[j]);
} }
spin_unlock(&ses->chan_lock);
seq_puts(m, "\n\n\tShares: "); seq_puts(m, "\n\n\tShares: ");
j = 0; j = 0;
......
...@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, ...@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
{ {
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
void *page; void *page;
char *full_path, *root_path; char *full_path;
unsigned int xid;
int rc;
struct vfsmount *mnt; struct vfsmount *mnt;
cifs_dbg(FYI, "in %s\n", __func__); cifs_dbg(FYI, "in %s\n", __func__);
...@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) ...@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
* the double backslashes usually used in the UNC. This function * the double backslashes usually used in the UNC. This function
* gives us the latter, so we must adjust the result. * gives us the latter, so we must adjust the result.
*/ */
mnt = ERR_PTR(-ENOMEM);
cifs_sb = CIFS_SB(mntpt->d_sb); cifs_sb = CIFS_SB(mntpt->d_sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
mnt = ERR_PTR(-EREMOTE); mnt = ERR_PTR(-EREMOTE);
...@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) ...@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
} }
convert_delimiter(full_path, '\\'); convert_delimiter(full_path, '\\');
cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
if (!cifs_sb_master_tlink(cifs_sb)) {
cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
goto free_full_path;
}
tcon = cifs_sb_master_tcon(cifs_sb);
if (!tcon) {
cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
goto free_full_path;
}
root_path = kstrdup(tcon->treeName, GFP_KERNEL);
if (!root_path) {
mnt = ERR_PTR(-ENOMEM);
goto free_full_path;
}
cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);
ses = tcon->ses;
xid = get_xid();
/*
* If DFS root has been expired, then unconditionally fetch it again to
* refresh DFS referral cache.
*/
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
root_path + 1, NULL, NULL);
if (!rc) {
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
cifs_remap(cifs_sb), full_path + 1,
NULL, NULL);
}
free_xid(xid);
if (rc) {
mnt = ERR_PTR(rc);
goto free_root_path;
}
/*
* OK - we were able to get and cache a referral for @full_path.
*
* Now, pass it down to cifs_mount() and it will retry every available
* node server in case of failures - no need to do it here.
*/
mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
full_path + 1, mnt);
free_root_path:
kfree(root_path);
free_full_path: free_full_path:
free_dentry_path(page); free_dentry_path(page);
cdda_exit: cdda_exit:
......
...@@ -61,11 +61,6 @@ struct cifs_sb_info { ...@@ -61,11 +61,6 @@ struct cifs_sb_info {
/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
char *prepath; char *prepath;
/*
* Canonical DFS path initially provided by the mount call. We might connect to something
* different via DFS but we want to keep it to do failover properly.
*/
char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
uuid_t dfs_mount_id; uuid_t dfs_mount_id;
/* /*
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/utsname.h>
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifsacl.h" #include "cifsacl.h"
#include <crypto/internal/hash.h> #include <crypto/internal/hash.h>
...@@ -75,7 +76,8 @@ ...@@ -75,7 +76,8 @@
#define SMB_ECHO_INTERVAL_MAX 600 #define SMB_ECHO_INTERVAL_MAX 600
#define SMB_ECHO_INTERVAL_DEFAULT 60 #define SMB_ECHO_INTERVAL_DEFAULT 60
/* dns resolution interval in seconds */ /* dns resolution intervals in seconds */
#define SMB_DNS_RESOLVE_INTERVAL_MIN 120
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600 #define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
/* maximum number of PDUs in one compound */ /* maximum number of PDUs in one compound */
...@@ -99,6 +101,8 @@ ...@@ -99,6 +101,8 @@
#define XATTR_DOS_ATTRIB "user.DOSATTRIB" #define XATTR_DOS_ATTRIB "user.DOSATTRIB"
#endif #endif
#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */
/* /*
* CIFS vfs client Status information (based on what we know.) * CIFS vfs client Status information (based on what we know.)
*/ */
...@@ -592,6 +596,7 @@ struct TCP_Server_Info { ...@@ -592,6 +596,7 @@ struct TCP_Server_Info {
struct list_head pending_mid_q; struct list_head pending_mid_q;
bool noblocksnd; /* use blocking sendmsg */ bool noblocksnd; /* use blocking sendmsg */
bool noautotune; /* do not autotune send buf sizes */ bool noautotune; /* do not autotune send buf sizes */
bool nosharesock;
bool tcp_nodelay; bool tcp_nodelay;
unsigned int credits; /* send no more requests at once */ unsigned int credits; /* send no more requests at once */
unsigned int max_credits; /* can override large 32000 default at mnt */ unsigned int max_credits; /* can override large 32000 default at mnt */
...@@ -685,13 +690,34 @@ struct TCP_Server_Info { ...@@ -685,13 +690,34 @@ struct TCP_Server_Info {
*/ */
int nr_targets; int nr_targets;
bool noblockcnt; /* use non-blocking connect() */ bool noblockcnt; /* use non-blocking connect() */
bool is_channel; /* if a session channel */
/*
* If this is a session channel,
* primary_server holds the ref-counted
* pointer to primary channel connection for the session.
*/
#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server)
struct TCP_Server_Info *primary_server;
#ifdef CONFIG_CIFS_SWN_UPCALL #ifdef CONFIG_CIFS_SWN_UPCALL
bool use_swn_dstaddr; bool use_swn_dstaddr;
struct sockaddr_storage swn_dstaddr; struct sockaddr_storage swn_dstaddr;
#endif #endif
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
bool is_dfs_conn; /* if a dfs connection */ bool is_dfs_conn; /* if a dfs connection */
struct mutex refpath_lock; /* protects leaf_fullpath */
/*
* Canonical DFS full paths that were used to chase referrals in mount and reconnect.
*
* origin_fullpath: first or original referral path
* leaf_fullpath: last referral path (might be changed due to nested links in reconnect)
*
* current_fullpath: pointer to either origin_fullpath or leaf_fullpath
* NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect()
*
* format: \\HOST\SHARE\[OPTIONAL PATH]
*/
char *origin_fullpath, *leaf_fullpath, *current_fullpath;
#endif #endif
}; };
...@@ -908,6 +934,7 @@ struct cifs_ses { ...@@ -908,6 +934,7 @@ struct cifs_ses {
and after mount option parsing we fill it */ and after mount option parsing we fill it */
char *domainName; char *domainName;
char *password; char *password;
char *workstation_name;
struct session_key auth_key; struct session_key auth_key;
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
enum securityEnum sectype; /* what security flavor was specified? */ enum securityEnum sectype; /* what security flavor was specified? */
...@@ -933,16 +960,21 @@ struct cifs_ses { ...@@ -933,16 +960,21 @@ struct cifs_ses {
* iface_lock should be taken when accessing any of these fields * iface_lock should be taken when accessing any of these fields
*/ */
spinlock_t iface_lock; spinlock_t iface_lock;
/* ========= begin: protected by iface_lock ======== */
struct cifs_server_iface *iface_list; struct cifs_server_iface *iface_list;
size_t iface_count; size_t iface_count;
unsigned long iface_last_update; /* jiffies */ unsigned long iface_last_update; /* jiffies */
/* ========= end: protected by iface_lock ======== */
spinlock_t chan_lock;
/* ========= begin: protected by chan_lock ======== */
#define CIFS_MAX_CHANNELS 16 #define CIFS_MAX_CHANNELS 16
struct cifs_chan chans[CIFS_MAX_CHANNELS]; struct cifs_chan chans[CIFS_MAX_CHANNELS];
struct cifs_chan *binding_chan; struct cifs_chan *binding_chan;
size_t chan_count; size_t chan_count;
size_t chan_max; size_t chan_max;
atomic_t chan_seq; /* round robin state */ atomic_t chan_seq; /* round robin state */
/* ========= end: protected by chan_lock ======== */
}; };
/* /*
...@@ -1091,7 +1123,6 @@ struct cifs_tcon { ...@@ -1091,7 +1123,6 @@ struct cifs_tcon {
struct cached_fid crfid; /* Cached root fid */ struct cached_fid crfid; /* Cached root fid */
/* BB add field for back pointer to sb struct(s)? */ /* BB add field for back pointer to sb struct(s)? */
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
char *dfs_path; /* canonical DFS path */
struct list_head ulist; /* cache update list */ struct list_head ulist; /* cache update list */
#endif #endif
}; };
...@@ -1942,4 +1973,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon) ...@@ -1942,4 +1973,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
} }
static inline bool cifs_is_referral_server(struct cifs_tcon *tcon,
const struct dfs_info3_param *ref)
{
/*
* Check if all targets are capable of handling DFS referrals as per
* MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
*/
return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER));
}
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */
...@@ -269,8 +269,9 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); ...@@ -269,8 +269,9 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
const char *path); const char *path);
extern struct TCP_Server_Info *
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx); cifs_get_tcp_session(struct smb3_fs_context *ctx,
struct TCP_Server_Info *primary_server);
extern void cifs_put_tcp_session(struct TCP_Server_Info *server, extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
int from_reconnect); int from_reconnect);
extern void cifs_put_tcon(struct cifs_tcon *tcon); extern void cifs_put_tcon(struct cifs_tcon *tcon);
...@@ -607,7 +608,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, ...@@ -607,7 +608,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
void cifs_put_tcp_super(struct super_block *sb); void cifs_put_tcp_super(struct super_block *sb);
int update_super_prepath(struct cifs_tcon *tcon, char *prefix); int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
char *extract_hostname(const char *unc); char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc); char *extract_sharename(const char *unc);
...@@ -634,4 +635,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) ...@@ -634,4 +635,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
return options; return options;
} }
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
void cifs_put_tcon_super(struct super_block *sb);
#endif /* _CIFSPROTO_H */ #endif /* _CIFSPROTO_H */
This diff is collapsed.
...@@ -283,7 +283,7 @@ static int dfscache_proc_show(struct seq_file *m, void *v) ...@@ -283,7 +283,7 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
seq_printf(m, seq_printf(m,
"cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
ce->ttl, ce->etime.tv_nsec, ce->ref_flags, ce->hdr_flags, ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags,
IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no",
ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no");
...@@ -1364,9 +1364,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach ...@@ -1364,9 +1364,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach
} }
/* Refresh dfs referral of tcon and mark it for reconnect if needed */ /* Refresh dfs referral of tcon and mark it for reconnect if needed */
static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon,
bool force_refresh)
{ {
const char *path = tcon->dfs_path + 1;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cache_entry *ce; struct cache_entry *ce;
struct dfs_info3_param *refs = NULL; struct dfs_info3_param *refs = NULL;
...@@ -1422,6 +1422,20 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool ...@@ -1422,6 +1422,20 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool
return rc; return rc;
} }
static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
{
struct TCP_Server_Info *server = tcon->ses->server;
mutex_lock(&server->refpath_lock);
if (strcasecmp(server->leaf_fullpath, server->origin_fullpath))
__refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh);
mutex_unlock(&server->refpath_lock);
__refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh);
return 0;
}
/** /**
* dfs_cache_remount_fs - remount a DFS share * dfs_cache_remount_fs - remount a DFS share
* *
...@@ -1435,6 +1449,7 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool ...@@ -1435,6 +1449,7 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
{ {
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct mount_group *mg; struct mount_group *mg;
struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
int rc; int rc;
...@@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) ...@@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
return -EINVAL; return -EINVAL;
tcon = cifs_sb_master_tcon(cifs_sb); tcon = cifs_sb_master_tcon(cifs_sb);
if (!tcon->dfs_path) { server = tcon->ses->server;
cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__);
if (!server->origin_fullpath) {
cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
return 0; return 0;
} }
if (uuid_is_null(&cifs_sb->dfs_mount_id)) { if (uuid_is_null(&cifs_sb->dfs_mount_id)) {
cifs_dbg(FYI, "%s: tcon has no dfs mount group id\n", __func__); cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__);
return -EINVAL; return -EINVAL;
} }
...@@ -1457,7 +1474,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) ...@@ -1457,7 +1474,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); mg = find_mount_group_locked(&cifs_sb->dfs_mount_id);
if (IS_ERR(mg)) { if (IS_ERR(mg)) {
mutex_unlock(&mount_group_list_lock); mutex_unlock(&mount_group_list_lock);
cifs_dbg(FYI, "%s: tcon has ipc session to refresh referral\n", __func__); cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__);
return PTR_ERR(mg); return PTR_ERR(mg);
} }
kref_get(&mg->refcount); kref_get(&mg->refcount);
...@@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions) ...@@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions)
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
if (!server->is_dfs_conn)
continue;
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->dfs_path) { if (!tcon->ipc && !tcon->need_reconnect) {
tcon->tc_count++; tcon->tc_count++;
list_add_tail(&tcon->ulist, &tcons); list_add_tail(&tcon->ulist, &tcons);
} }
...@@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions) ...@@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions)
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
struct TCP_Server_Info *server = tcon->ses->server;
list_del_init(&tcon->ulist); list_del_init(&tcon->ulist);
refresh_tcon(sessions, tcon, false);
mutex_lock(&server->refpath_lock);
if (strcasecmp(server->leaf_fullpath, server->origin_fullpath))
__refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false);
mutex_unlock(&server->refpath_lock);
__refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false);
cifs_put_tcon(tcon); cifs_put_tcon(tcon);
} }
} }
......
...@@ -2692,12 +2692,23 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, ...@@ -2692,12 +2692,23 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
tcon = tlink_tcon(smbfile->tlink); tcon = tlink_tcon(smbfile->tlink);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
server = tcon->ses->server; server = tcon->ses->server;
if (server->ops->flush) if (server->ops->flush == NULL) {
rc = server->ops->flush(xid, tcon, &smbfile->fid);
else
rc = -ENOSYS; rc = -ENOSYS;
goto strict_fsync_exit;
} }
if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
if (smbfile) {
rc = server->ops->flush(xid, tcon, &smbfile->fid);
cifsFileInfo_put(smbfile);
} else
cifs_dbg(FYI, "ignore fsync for file not open for write\n");
} else
rc = server->ops->flush(xid, tcon, &smbfile->fid);
}
strict_fsync_exit:
free_xid(xid); free_xid(xid);
return rc; return rc;
} }
...@@ -2709,6 +2720,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -2709,6 +2720,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifsFileInfo *smbfile = file->private_data; struct cifsFileInfo *smbfile = file->private_data;
struct inode *inode = file_inode(file);
struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
rc = file_write_and_wait_range(file, start, end); rc = file_write_and_wait_range(file, start, end);
...@@ -2725,12 +2737,23 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -2725,12 +2737,23 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
tcon = tlink_tcon(smbfile->tlink); tcon = tlink_tcon(smbfile->tlink);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
server = tcon->ses->server; server = tcon->ses->server;
if (server->ops->flush) if (server->ops->flush == NULL) {
rc = server->ops->flush(xid, tcon, &smbfile->fid);
else
rc = -ENOSYS; rc = -ENOSYS;
goto fsync_exit;
}
if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
if (smbfile) {
rc = server->ops->flush(xid, tcon, &smbfile->fid);
cifsFileInfo_put(smbfile);
} else
cifs_dbg(FYI, "ignore fsync for file not open for write\n");
} else
rc = server->ops->flush(xid, tcon, &smbfile->fid);
} }
fsync_exit:
free_xid(xid); free_xid(xid);
return rc; return rc;
} }
......
...@@ -308,7 +308,9 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx ...@@ -308,7 +308,9 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
new_ctx->nodename = NULL; new_ctx->nodename = NULL;
new_ctx->username = NULL; new_ctx->username = NULL;
new_ctx->password = NULL; new_ctx->password = NULL;
new_ctx->server_hostname = NULL;
new_ctx->domainname = NULL; new_ctx->domainname = NULL;
new_ctx->workstation_name = NULL;
new_ctx->UNC = NULL; new_ctx->UNC = NULL;
new_ctx->source = NULL; new_ctx->source = NULL;
new_ctx->iocharset = NULL; new_ctx->iocharset = NULL;
...@@ -323,6 +325,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx ...@@ -323,6 +325,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
DUP_CTX_STR(UNC); DUP_CTX_STR(UNC);
DUP_CTX_STR(source); DUP_CTX_STR(source);
DUP_CTX_STR(domainname); DUP_CTX_STR(domainname);
DUP_CTX_STR(workstation_name);
DUP_CTX_STR(nodename); DUP_CTX_STR(nodename);
DUP_CTX_STR(iocharset); DUP_CTX_STR(iocharset);
...@@ -459,6 +462,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) ...@@ -459,6 +462,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
return -EINVAL; return -EINVAL;
/* record the server hostname */ /* record the server hostname */
kfree(ctx->server_hostname);
ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL); ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL);
if (!ctx->server_hostname) if (!ctx->server_hostname)
return -ENOMEM; return -ENOMEM;
...@@ -720,6 +724,11 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc, ...@@ -720,6 +724,11 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
cifs_errorf(fc, "can not change domainname during remount\n"); cifs_errorf(fc, "can not change domainname during remount\n");
return -EINVAL; return -EINVAL;
} }
if (new_ctx->workstation_name &&
(!old_ctx->workstation_name || strcmp(new_ctx->workstation_name, old_ctx->workstation_name))) {
cifs_errorf(fc, "can not change workstation_name during remount\n");
return -EINVAL;
}
if (new_ctx->nodename && if (new_ctx->nodename &&
(!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) { (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) {
cifs_errorf(fc, "can not change nodename during remount\n"); cifs_errorf(fc, "can not change nodename during remount\n");
...@@ -753,7 +762,8 @@ static int smb3_reconfigure(struct fs_context *fc) ...@@ -753,7 +762,8 @@ static int smb3_reconfigure(struct fs_context *fc)
return rc; return rc;
/* /*
* We can not change UNC/username/password/domainname/nodename/iocharset * We can not change UNC/username/password/domainname/
* workstation_name/nodename/iocharset
* during reconnect so ignore what we have in the new context and * during reconnect so ignore what we have in the new context and
* just use what we already have in cifs_sb->ctx. * just use what we already have in cifs_sb->ctx.
*/ */
...@@ -762,6 +772,7 @@ static int smb3_reconfigure(struct fs_context *fc) ...@@ -762,6 +772,7 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, username); STEAL_STRING(cifs_sb, ctx, username);
STEAL_STRING(cifs_sb, ctx, password); STEAL_STRING(cifs_sb, ctx, password);
STEAL_STRING(cifs_sb, ctx, domainname); STEAL_STRING(cifs_sb, ctx, domainname);
STEAL_STRING(cifs_sb, ctx, workstation_name);
STEAL_STRING(cifs_sb, ctx, nodename); STEAL_STRING(cifs_sb, ctx, nodename);
STEAL_STRING(cifs_sb, ctx, iocharset); STEAL_STRING(cifs_sb, ctx, iocharset);
...@@ -1414,13 +1425,22 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, ...@@ -1414,13 +1425,22 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
int smb3_init_fs_context(struct fs_context *fc) int smb3_init_fs_context(struct fs_context *fc)
{ {
int rc;
struct smb3_fs_context *ctx; struct smb3_fs_context *ctx;
char *nodename = utsname()->nodename; char *nodename = utsname()->nodename;
int i; int i;
ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL);
if (unlikely(!ctx)) if (unlikely(!ctx)) {
return -ENOMEM; rc = -ENOMEM;
goto err_exit;
}
ctx->workstation_name = kstrdup(nodename, GFP_KERNEL);
if (unlikely(!ctx->workstation_name)) {
rc = -ENOMEM;
goto err_exit;
}
/* /*
* does not have to be perfect mapping since field is * does not have to be perfect mapping since field is
...@@ -1493,6 +1513,14 @@ int smb3_init_fs_context(struct fs_context *fc) ...@@ -1493,6 +1513,14 @@ int smb3_init_fs_context(struct fs_context *fc)
fc->fs_private = ctx; fc->fs_private = ctx;
fc->ops = &smb3_fs_context_ops; fc->ops = &smb3_fs_context_ops;
return 0; return 0;
err_exit:
if (ctx) {
kfree(ctx->workstation_name);
kfree(ctx);
}
return rc;
} }
void void
...@@ -1518,6 +1546,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx) ...@@ -1518,6 +1546,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
ctx->source = NULL; ctx->source = NULL;
kfree(ctx->domainname); kfree(ctx->domainname);
ctx->domainname = NULL; ctx->domainname = NULL;
kfree(ctx->workstation_name);
ctx->workstation_name = NULL;
kfree(ctx->nodename); kfree(ctx->nodename);
ctx->nodename = NULL; ctx->nodename = NULL;
kfree(ctx->iocharset); kfree(ctx->iocharset);
......
...@@ -170,6 +170,7 @@ struct smb3_fs_context { ...@@ -170,6 +170,7 @@ struct smb3_fs_context {
char *server_hostname; char *server_hostname;
char *UNC; char *UNC;
char *nodename; char *nodename;
char *workstation_name;
char *iocharset; /* local code page for mapping to and from Unicode */ char *iocharset; /* local code page for mapping to and from Unicode */
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
......
...@@ -87,6 +87,14 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) ...@@ -87,6 +87,14 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
char *sharename; char *sharename;
struct cifs_fscache_super_auxdata auxdata; 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 == NULL)
return;
sharename = extract_sharename(tcon->treeName); sharename = extract_sharename(tcon->treeName);
if (IS_ERR(sharename)) { if (IS_ERR(sharename)) {
cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
......
...@@ -75,6 +75,7 @@ sesInfoAlloc(void) ...@@ -75,6 +75,7 @@ sesInfoAlloc(void)
INIT_LIST_HEAD(&ret_buf->tcon_list); INIT_LIST_HEAD(&ret_buf->tcon_list);
mutex_init(&ret_buf->session_mutex); mutex_init(&ret_buf->session_mutex);
spin_lock_init(&ret_buf->iface_lock); spin_lock_init(&ret_buf->iface_lock);
spin_lock_init(&ret_buf->chan_lock);
} }
return ret_buf; return ret_buf;
} }
...@@ -94,6 +95,7 @@ sesInfoFree(struct cifs_ses *buf_to_free) ...@@ -94,6 +95,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
kfree_sensitive(buf_to_free->password); kfree_sensitive(buf_to_free->password);
kfree(buf_to_free->user_name); kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName); kfree(buf_to_free->domainName);
kfree(buf_to_free->workstation_name);
kfree_sensitive(buf_to_free->auth_key.response); kfree_sensitive(buf_to_free->auth_key.response);
kfree(buf_to_free->iface_list); kfree(buf_to_free->iface_list);
kfree_sensitive(buf_to_free); kfree_sensitive(buf_to_free);
...@@ -138,9 +140,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free) ...@@ -138,9 +140,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
kfree(buf_to_free->nativeFileSystem); kfree(buf_to_free->nativeFileSystem);
kfree_sensitive(buf_to_free->password); kfree_sensitive(buf_to_free->password);
kfree(buf_to_free->crfid.fid); kfree(buf_to_free->crfid.fid);
#ifdef CONFIG_CIFS_DFS_UPCALL
kfree(buf_to_free->dfs_path);
#endif
kfree(buf_to_free); kfree(buf_to_free);
} }
...@@ -1287,69 +1286,20 @@ int match_target_ip(struct TCP_Server_Info *server, ...@@ -1287,69 +1286,20 @@ int match_target_ip(struct TCP_Server_Info *server,
return rc; return rc;
} }
static void tcon_super_cb(struct super_block *sb, void *arg) int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
{
struct super_cb_data *sd = arg;
struct cifs_tcon *tcon = sd->data;
struct cifs_sb_info *cifs_sb;
if (sd->sb)
return;
cifs_sb = CIFS_SB(sb);
if (tcon->dfs_path && cifs_sb->origin_fullpath &&
!strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath))
sd->sb = sb;
}
static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
{
return __cifs_get_super(tcon_super_cb, tcon);
}
static inline void cifs_put_tcon_super(struct super_block *sb)
{
__cifs_put_super(sb);
}
#else
static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline void cifs_put_tcon_super(struct super_block *sb)
{
}
#endif
int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
{ {
struct super_block *sb;
struct cifs_sb_info *cifs_sb;
int rc = 0;
sb = cifs_get_tcon_super(tcon);
if (IS_ERR(sb))
return PTR_ERR(sb);
cifs_sb = CIFS_SB(sb);
kfree(cifs_sb->prepath); kfree(cifs_sb->prepath);
if (prefix && *prefix) { if (prefix && *prefix) {
cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC); cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC);
if (!cifs_sb->prepath) { if (!cifs_sb->prepath)
rc = -ENOMEM; return -ENOMEM;
goto out;
}
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
} else } else
cifs_sb->prepath = NULL; cifs_sb->prepath = NULL;
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
return 0;
out:
cifs_put_tcon_super(sb);
return rc;
} }
#endif
...@@ -119,7 +119,9 @@ typedef struct _AUTHENTICATE_MESSAGE { ...@@ -119,7 +119,9 @@ typedef struct _AUTHENTICATE_MESSAGE {
*/ */
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
const struct nls_table *nls_cp);
int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
This diff is collapsed.
...@@ -46,6 +46,10 @@ struct cop_vars { ...@@ -46,6 +46,10 @@ struct cop_vars {
struct smb2_file_link_info link_info; struct smb2_file_link_info link_info;
}; };
/*
* note: If cfile is passed, the reference to it is dropped here.
* So make sure that you do not reuse cfile after return from this func.
*/
static int static int
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
...@@ -536,10 +540,11 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -536,10 +540,11 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
create_options |= OPEN_REPARSE_POINT; create_options |= OPEN_REPARSE_POINT;
/* Failed on a symbolic link - query a reparse point info */ /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_READ_ATTRIBUTES, FILE_OPEN,
create_options, ACL_NO_MODE, create_options, ACL_NO_MODE,
smb2_data, SMB2_OP_QUERY_INFO, NULL); smb2_data, SMB2_OP_QUERY_INFO, cfile);
} }
if (rc) if (rc)
goto out; goto out;
...@@ -587,10 +592,11 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -587,10 +592,11 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
create_options |= OPEN_REPARSE_POINT; create_options |= OPEN_REPARSE_POINT;
/* Failed on a symbolic link - query a reparse point info */ /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_READ_ATTRIBUTES, FILE_OPEN,
create_options, ACL_NO_MODE, create_options, ACL_NO_MODE,
smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL); smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
} }
if (rc) if (rc)
goto out; goto out;
...@@ -707,10 +713,12 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -707,10 +713,12 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, bool set_alloc) struct cifs_sb_info *cifs_sb, bool set_alloc)
{ {
__le64 eof = cpu_to_le64(size); __le64 eof = cpu_to_le64(size);
struct cifsFileInfo *cfile;
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
return smb2_compound_op(xid, tcon, cifs_sb, full_path, return smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
&eof, SMB2_OP_SET_EOF, NULL); &eof, SMB2_OP_SET_EOF, cfile);
} }
int int
...@@ -719,6 +727,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path, ...@@ -719,6 +727,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_tcon *tcon;
struct cifsFileInfo *cfile;
int rc; int rc;
if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
...@@ -729,10 +739,12 @@ smb2_set_file_info(struct inode *inode, const char *full_path, ...@@ -729,10 +739,12 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path, cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, FILE_WRITE_ATTRIBUTES, FILE_OPEN,
0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, NULL); 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -2844,6 +2844,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, ...@@ -2844,6 +2844,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
struct fsctl_get_dfs_referral_req *dfs_req = NULL; struct fsctl_get_dfs_referral_req *dfs_req = NULL;
struct get_dfs_referral_rsp *dfs_rsp = NULL; struct get_dfs_referral_rsp *dfs_rsp = NULL;
u32 dfs_req_size = 0, dfs_rsp_size = 0; u32 dfs_req_size = 0, dfs_rsp_size = 0;
int retry_count = 0;
cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name);
...@@ -2895,11 +2896,14 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, ...@@ -2895,11 +2896,14 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
true /* is_fsctl */, true /* is_fsctl */,
(char *)dfs_req, dfs_req_size, CIFSMaxBufSize, (char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
(char **)&dfs_rsp, &dfs_rsp_size); (char **)&dfs_rsp, &dfs_rsp_size);
} while (rc == -EAGAIN); if (!is_retryable_error(rc))
break;
usleep_range(512, 2048);
} while (++retry_count < 5);
if (rc) { if (rc) {
if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
goto out; goto out;
} }
......
...@@ -155,7 +155,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -155,7 +155,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (tcon == NULL) if (tcon == NULL)
return 0; return 0;
if (smb2_command == SMB2_TREE_CONNECT) /*
* Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in
* cifs_tree_connect().
*/
if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
return 0; return 0;
if (tcon->tidStatus == CifsExiting) { if (tcon->tidStatus == CifsExiting) {
...@@ -253,7 +257,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -253,7 +257,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
/* /*
* If we are reconnecting an extra channel, bind * If we are reconnecting an extra channel, bind
*/ */
if (server->is_channel) { if (CIFS_SERVER_IS_CHAN(server)) {
ses->binding = true; ses->binding = true;
ses->binding_chan = cifs_ses_find_chan(ses, server); ses->binding_chan = cifs_ses_find_chan(ses, server);
} }
...@@ -1456,7 +1460,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1456,7 +1460,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
int rc; int rc;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct smb2_sess_setup_rsp *rsp = NULL; struct smb2_sess_setup_rsp *rsp = NULL;
char *ntlmssp_blob = NULL; unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */ bool use_spnego = false; /* else use raw ntlmssp */
u16 blob_length = 0; u16 blob_length = 0;
...@@ -1475,22 +1479,17 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1475,22 +1479,17 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
if (rc) if (rc)
goto out_err; goto out_err;
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob,
GFP_KERNEL); &blob_length, ses,
if (ntlmssp_blob == NULL) { sess_data->nls_cp);
rc = -ENOMEM; if (rc)
goto out; goto out_err;
}
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
if (use_spnego) { if (use_spnego) {
/* BB eventually need to add this */ /* BB eventually need to add this */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
goto out; goto out;
} else {
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
} }
sess_data->iov[1].iov_base = ntlmssp_blob; sess_data->iov[1].iov_base = ntlmssp_blob;
sess_data->iov[1].iov_len = blob_length; sess_data->iov[1].iov_len = blob_length;
...@@ -1841,7 +1840,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -1841,7 +1840,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
cifs_small_buf_release(req); cifs_small_buf_release(req);
rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc);
if (rc != 0) { if ((rc != 0) || (rsp == NULL)) {
cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE);
tcon->need_reconnect = true; tcon->need_reconnect = true;
goto tcon_error_exit; goto tcon_error_exit;
...@@ -2669,7 +2668,18 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, ...@@ -2669,7 +2668,18 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
goto err_free_rsp_buf; goto err_free_rsp_buf;
} }
/*
* Although unlikely to be possible for rsp to be null and rc not set,
* adding check below is slightly safer long term (and quiets Coverity
* warning)
*/
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
if (rsp == NULL) {
rc = -EIO;
kfree(pc_buf);
goto err_free_req;
}
trace_smb3_posix_mkdir_done(xid, le64_to_cpu(rsp->PersistentFileId), trace_smb3_posix_mkdir_done(xid, le64_to_cpu(rsp->PersistentFileId),
tcon->tid, tcon->tid,
ses->Suid, CREATE_NOT_FILE, ses->Suid, CREATE_NOT_FILE,
...@@ -2942,7 +2952,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -2942,7 +2952,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
tcon->need_reconnect = true; tcon->need_reconnect = true;
} }
goto creat_exit; goto creat_exit;
} else } else if (rsp == NULL) /* unlikely to happen, but safer to check */
goto creat_exit;
else
trace_smb3_open_done(xid, le64_to_cpu(rsp->PersistentFileId), trace_smb3_open_done(xid, le64_to_cpu(rsp->PersistentFileId),
tcon->tid, tcon->tid,
ses->Suid, oparms->create_options, ses->Suid, oparms->create_options,
...@@ -3163,6 +3175,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, ...@@ -3163,6 +3175,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
if ((plen == NULL) || (out_data == NULL)) if ((plen == NULL) || (out_data == NULL))
goto ioctl_exit; goto ioctl_exit;
/*
* Although unlikely to be possible for rsp to be null and rc not set,
* adding check below is slightly safer long term (and quiets Coverity
* warning)
*/
if (rsp == NULL) {
rc = -EIO;
goto ioctl_exit;
}
*plen = le32_to_cpu(rsp->OutputCount); *plen = le32_to_cpu(rsp->OutputCount);
/* We check for obvious errors in the output buffer length and offset */ /* We check for obvious errors in the output buffer length and offset */
......
...@@ -1044,14 +1044,17 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) ...@@ -1044,14 +1044,17 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
if (!ses) if (!ses)
return NULL; return NULL;
spin_lock(&ses->chan_lock);
if (!ses->binding) { if (!ses->binding) {
/* round robin */ /* round robin */
if (ses->chan_count > 1) { if (ses->chan_count > 1) {
index = (uint)atomic_inc_return(&ses->chan_seq); index = (uint)atomic_inc_return(&ses->chan_seq);
index %= ses->chan_count; index %= ses->chan_count;
} }
spin_unlock(&ses->chan_lock);
return ses->chans[index].server; return ses->chans[index].server;
} else { } else {
spin_unlock(&ses->chan_lock);
return cifs_ses_server(ses); return cifs_ses_server(ses);
} }
} }
......
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