forked from Minki/linux
23 cifs/smb3 fixes, including fixes for DFS, multichannel, reconnect, fscache and a compounding improvement
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmGP2W4ACgkQiiy9cAdy T1Eaawv/ZS5lgxqmCRBKlptlXvBwSFGNSwY8kZy5xAG08Pk8SMWBmpuPolZJhwmU UlfOwCr0WzKlkcm9e6SLdhpO+wfITqzD84xrOq9sM5C4XC4h8gn45WIcsvDV7bu8 r4SB8AEHlU0lQWW8syclXppCW0s3+aqgyZVrgW3u0uykBm326CN+DtnqoHV84enO deurEtPAbrJRzkGE3RHxfQEx5gt9IIkjZwoMk1AO6m/hEpdW7NTkDlMPFoAS+9PE 21m/fnilMV7gbLn6GNJKlYzM+ZCEq6tY2860fTR75uqgvo5qPKMSamv0Z/I9W23V NKP6fMDWVZmac7eieBw3Hjzxkfj0/1nmXRyTghlRZk0PUkOW6atZPXSIcbdEJgCu l/a+KV1U50FWp4/a5eTQpgJE6wDM71Lj9H5yomc5EafiRr8xAzhuJ1ZRoTVfNVRc NEIJ4H81pxatbIukESz/wxB1SgV2FebsZWEEaTyudd66hNLVGiek0Q7qDT52YiPU hvQzGkTx =iwLL -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
commit
c8103c2718
@ -271,7 +271,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
c = 0;
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
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;
|
||||
|
||||
c++;
|
||||
@ -358,6 +359,8 @@ skip_rdma:
|
||||
seq_printf(m, " signed");
|
||||
if (server->posix_ext_supported)
|
||||
seq_printf(m, " posix");
|
||||
if (server->nosharesock)
|
||||
seq_printf(m, " nosharesock");
|
||||
|
||||
if (server->rdma)
|
||||
seq_printf(m, "\nRDMA ");
|
||||
@ -412,12 +415,14 @@ skip_rdma:
|
||||
from_kuid(&init_user_ns, ses->linux_uid),
|
||||
from_kuid(&init_user_ns, ses->cred_uid));
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (ses->chan_count > 1) {
|
||||
seq_printf(m, "\n\n\tExtra Channels: %zu ",
|
||||
ses->chan_count-1);
|
||||
for (j = 1; j < ses->chan_count; j++)
|
||||
cifs_dump_channel(m, j, &ses->chans[j]);
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
seq_puts(m, "\n\n\tShares: ");
|
||||
j = 0;
|
||||
|
@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
|
||||
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
void *page;
|
||||
char *full_path, *root_path;
|
||||
unsigned int xid;
|
||||
int rc;
|
||||
char *full_path;
|
||||
struct vfsmount *mnt;
|
||||
|
||||
cifs_dbg(FYI, "in %s\n", __func__);
|
||||
@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
* the double backslashes usually used in the UNC. This function
|
||||
* gives us the latter, so we must adjust the result.
|
||||
*/
|
||||
mnt = ERR_PTR(-ENOMEM);
|
||||
|
||||
cifs_sb = CIFS_SB(mntpt->d_sb);
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
|
||||
mnt = ERR_PTR(-EREMOTE);
|
||||
@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
}
|
||||
|
||||
convert_delimiter(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);
|
||||
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
|
||||
full_path + 1, mnt);
|
||||
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
|
||||
|
||||
free_root_path:
|
||||
kfree(root_path);
|
||||
free_full_path:
|
||||
free_dentry_path(page);
|
||||
cdda_exit:
|
||||
|
@ -61,11 +61,6 @@ struct cifs_sb_info {
|
||||
/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
|
||||
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 */
|
||||
uuid_t dfs_mount_id;
|
||||
/*
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/utsname.h>
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifsacl.h"
|
||||
#include <crypto/internal/hash.h>
|
||||
@ -75,7 +76,8 @@
|
||||
#define SMB_ECHO_INTERVAL_MAX 600
|
||||
#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
|
||||
|
||||
/* maximum number of PDUs in one compound */
|
||||
@ -99,6 +101,8 @@
|
||||
#define XATTR_DOS_ATTRIB "user.DOSATTRIB"
|
||||
#endif
|
||||
|
||||
#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */
|
||||
|
||||
/*
|
||||
* CIFS vfs client Status information (based on what we know.)
|
||||
*/
|
||||
@ -592,6 +596,7 @@ struct TCP_Server_Info {
|
||||
struct list_head pending_mid_q;
|
||||
bool noblocksnd; /* use blocking sendmsg */
|
||||
bool noautotune; /* do not autotune send buf sizes */
|
||||
bool nosharesock;
|
||||
bool tcp_nodelay;
|
||||
unsigned int credits; /* send no more requests at once */
|
||||
unsigned int max_credits; /* can override large 32000 default at mnt */
|
||||
@ -685,13 +690,34 @@ struct TCP_Server_Info {
|
||||
*/
|
||||
int nr_targets;
|
||||
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
|
||||
bool use_swn_dstaddr;
|
||||
struct sockaddr_storage swn_dstaddr;
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
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
|
||||
};
|
||||
|
||||
@ -908,6 +934,7 @@ struct cifs_ses {
|
||||
and after mount option parsing we fill it */
|
||||
char *domainName;
|
||||
char *password;
|
||||
char *workstation_name;
|
||||
struct session_key auth_key;
|
||||
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
|
||||
enum securityEnum sectype; /* what security flavor was specified? */
|
||||
@ -933,16 +960,21 @@ struct cifs_ses {
|
||||
* iface_lock should be taken when accessing any of these fields
|
||||
*/
|
||||
spinlock_t iface_lock;
|
||||
/* ========= begin: protected by iface_lock ======== */
|
||||
struct cifs_server_iface *iface_list;
|
||||
size_t iface_count;
|
||||
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
|
||||
struct cifs_chan chans[CIFS_MAX_CHANNELS];
|
||||
struct cifs_chan *binding_chan;
|
||||
size_t chan_count;
|
||||
size_t chan_max;
|
||||
atomic_t chan_seq; /* round robin state */
|
||||
/* ========= end: protected by chan_lock ======== */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1091,7 +1123,6 @@ struct cifs_tcon {
|
||||
struct cached_fid crfid; /* Cached root fid */
|
||||
/* BB add field for back pointer to sb struct(s)? */
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
char *dfs_path; /* canonical DFS path */
|
||||
struct list_head ulist; /* cache update list */
|
||||
#endif
|
||||
};
|
||||
@ -1942,4 +1973,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
|
||||
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 */
|
||||
|
@ -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,
|
||||
const char *path);
|
||||
|
||||
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
|
||||
extern struct TCP_Server_Info *
|
||||
cifs_get_tcp_session(struct smb3_fs_context *ctx,
|
||||
struct TCP_Server_Info *primary_server);
|
||||
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
|
||||
int from_reconnect);
|
||||
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,
|
||||
|
||||
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
|
||||
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_sharename(const char *unc);
|
||||
|
||||
@ -634,4 +635,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int 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 */
|
||||
|
1524
fs/cifs/connect.c
1524
fs/cifs/connect.c
File diff suppressed because it is too large
Load Diff
@ -283,7 +283,7 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
|
||||
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",
|
||||
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",
|
||||
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
|
||||
}
|
||||
|
||||
/* 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 cache_entry *ce;
|
||||
struct dfs_info3_param *refs = NULL;
|
||||
@ -1422,6 +1422,20 @@ out:
|
||||
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
|
||||
*
|
||||
@ -1435,6 +1449,7 @@ out:
|
||||
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct mount_group *mg;
|
||||
struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
|
||||
int rc;
|
||||
@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
|
||||
return -EINVAL;
|
||||
|
||||
tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
if (!tcon->dfs_path) {
|
||||
cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__);
|
||||
server = tcon->ses->server;
|
||||
|
||||
if (!server->origin_fullpath) {
|
||||
cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
if (IS_ERR(mg)) {
|
||||
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);
|
||||
}
|
||||
kref_get(&mg->refcount);
|
||||
@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions)
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
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(tcon, &ses->tcon_list, tcon_list) {
|
||||
if (tcon->dfs_path) {
|
||||
if (!tcon->ipc && !tcon->need_reconnect) {
|
||||
tcon->tc_count++;
|
||||
list_add_tail(&tcon->ulist, &tcons);
|
||||
}
|
||||
@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -2692,12 +2692,23 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
|
||||
tcon = tlink_tcon(smbfile->tlink);
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
|
||||
server = tcon->ses->server;
|
||||
if (server->ops->flush)
|
||||
rc = server->ops->flush(xid, tcon, &smbfile->fid);
|
||||
else
|
||||
if (server->ops->flush == NULL) {
|
||||
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);
|
||||
return rc;
|
||||
}
|
||||
@ -2709,6 +2720,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifsFileInfo *smbfile = file->private_data;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
|
||||
|
||||
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)
|
||||
tcon = tlink_tcon(smbfile->tlink);
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
|
||||
server = tcon->ses->server;
|
||||
if (server->ops->flush)
|
||||
rc = server->ops->flush(xid, tcon, &smbfile->fid);
|
||||
else
|
||||
if (server->ops->flush == NULL) {
|
||||
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);
|
||||
return rc;
|
||||
}
|
||||
|
@ -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->username = NULL;
|
||||
new_ctx->password = NULL;
|
||||
new_ctx->server_hostname = NULL;
|
||||
new_ctx->domainname = NULL;
|
||||
new_ctx->workstation_name = NULL;
|
||||
new_ctx->UNC = NULL;
|
||||
new_ctx->source = NULL;
|
||||
new_ctx->iocharset = NULL;
|
||||
@ -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(source);
|
||||
DUP_CTX_STR(domainname);
|
||||
DUP_CTX_STR(workstation_name);
|
||||
DUP_CTX_STR(nodename);
|
||||
DUP_CTX_STR(iocharset);
|
||||
|
||||
@ -459,6 +462,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
|
||||
return -EINVAL;
|
||||
|
||||
/* record the server hostname */
|
||||
kfree(ctx->server_hostname);
|
||||
ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL);
|
||||
if (!ctx->server_hostname)
|
||||
return -ENOMEM;
|
||||
@ -720,6 +724,11 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
|
||||
cifs_errorf(fc, "can not change domainname during remount\n");
|
||||
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 &&
|
||||
(!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) {
|
||||
cifs_errorf(fc, "can not change nodename during remount\n");
|
||||
@ -753,7 +762,8 @@ static int smb3_reconfigure(struct fs_context *fc)
|
||||
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
|
||||
* just use what we already have in cifs_sb->ctx.
|
||||
*/
|
||||
@ -762,6 +772,7 @@ static int smb3_reconfigure(struct fs_context *fc)
|
||||
STEAL_STRING(cifs_sb, ctx, username);
|
||||
STEAL_STRING(cifs_sb, ctx, password);
|
||||
STEAL_STRING(cifs_sb, ctx, domainname);
|
||||
STEAL_STRING(cifs_sb, ctx, workstation_name);
|
||||
STEAL_STRING(cifs_sb, ctx, nodename);
|
||||
STEAL_STRING(cifs_sb, ctx, iocharset);
|
||||
|
||||
@ -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 rc;
|
||||
struct smb3_fs_context *ctx;
|
||||
char *nodename = utsname()->nodename;
|
||||
int i;
|
||||
|
||||
ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL);
|
||||
if (unlikely(!ctx))
|
||||
return -ENOMEM;
|
||||
if (unlikely(!ctx)) {
|
||||
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
|
||||
@ -1493,6 +1513,14 @@ int smb3_init_fs_context(struct fs_context *fc)
|
||||
fc->fs_private = ctx;
|
||||
fc->ops = &smb3_fs_context_ops;
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
if (ctx) {
|
||||
kfree(ctx->workstation_name);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1518,6 +1546,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
|
||||
ctx->source = NULL;
|
||||
kfree(ctx->domainname);
|
||||
ctx->domainname = NULL;
|
||||
kfree(ctx->workstation_name);
|
||||
ctx->workstation_name = NULL;
|
||||
kfree(ctx->nodename);
|
||||
ctx->nodename = NULL;
|
||||
kfree(ctx->iocharset);
|
||||
|
@ -170,6 +170,7 @@ struct smb3_fs_context {
|
||||
char *server_hostname;
|
||||
char *UNC;
|
||||
char *nodename;
|
||||
char *workstation_name;
|
||||
char *iocharset; /* local code page for mapping to and from Unicode */
|
||||
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt 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)
|
||||
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 == NULL)
|
||||
return;
|
||||
|
||||
sharename = extract_sharename(tcon->treeName);
|
||||
if (IS_ERR(sharename)) {
|
||||
cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
|
||||
|
@ -75,6 +75,7 @@ sesInfoAlloc(void)
|
||||
INIT_LIST_HEAD(&ret_buf->tcon_list);
|
||||
mutex_init(&ret_buf->session_mutex);
|
||||
spin_lock_init(&ret_buf->iface_lock);
|
||||
spin_lock_init(&ret_buf->chan_lock);
|
||||
}
|
||||
return ret_buf;
|
||||
}
|
||||
@ -94,6 +95,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
|
||||
kfree_sensitive(buf_to_free->password);
|
||||
kfree(buf_to_free->user_name);
|
||||
kfree(buf_to_free->domainName);
|
||||
kfree(buf_to_free->workstation_name);
|
||||
kfree_sensitive(buf_to_free->auth_key.response);
|
||||
kfree(buf_to_free->iface_list);
|
||||
kfree_sensitive(buf_to_free);
|
||||
@ -138,9 +140,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
|
||||
kfree(buf_to_free->nativeFileSystem);
|
||||
kfree_sensitive(buf_to_free->password);
|
||||
kfree(buf_to_free->crfid.fid);
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
kfree(buf_to_free->dfs_path);
|
||||
#endif
|
||||
kfree(buf_to_free);
|
||||
}
|
||||
|
||||
@ -1287,69 +1286,20 @@ out:
|
||||
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);
|
||||
|
||||
if (prefix && *prefix) {
|
||||
cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC);
|
||||
if (!cifs_sb->prepath) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!cifs_sb->prepath)
|
||||
return -ENOMEM;
|
||||
|
||||
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
|
||||
} else
|
||||
cifs_sb->prepath = NULL;
|
||||
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
|
||||
|
||||
out:
|
||||
cifs_put_tcon_super(sb);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -119,7 +119,9 @@ typedef struct _AUTHENTICATE_MESSAGE {
|
||||
*/
|
||||
|
||||
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,
|
||||
struct cifs_ses *ses,
|
||||
const struct nls_table *nls_cp);
|
||||
|
240
fs/cifs/sess.c
240
fs/cifs/sess.c
@ -54,41 +54,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
{
|
||||
int i;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
if (is_server_using_iface(ses->chans[i].server, iface))
|
||||
if (is_server_using_iface(ses->chans[i].server, iface)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* returns number of channels added */
|
||||
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
{
|
||||
int old_chan_count = ses->chan_count;
|
||||
int left = ses->chan_max - ses->chan_count;
|
||||
int old_chan_count, new_chan_count;
|
||||
int left;
|
||||
int i = 0;
|
||||
int rc = 0;
|
||||
int tries = 0;
|
||||
struct cifs_server_iface *ifaces = NULL;
|
||||
size_t iface_count;
|
||||
|
||||
if (ses->server->dialect < SMB30_PROT_ID) {
|
||||
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
|
||||
new_chan_count = old_chan_count = ses->chan_count;
|
||||
left = ses->chan_max - ses->chan_count;
|
||||
|
||||
if (left <= 0) {
|
||||
cifs_dbg(FYI,
|
||||
"ses already at max_channels (%zu), nothing to open\n",
|
||||
ses->chan_max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ses->server->dialect < SMB30_PROT_ID) {
|
||||
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
|
||||
cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname);
|
||||
ses->chan_max = 1;
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/*
|
||||
* Make a copy of the iface list at the time and use that
|
||||
@ -142,10 +154,11 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
|
||||
i);
|
||||
left--;
|
||||
new_chan_count++;
|
||||
}
|
||||
|
||||
kfree(ifaces);
|
||||
return ses->chan_count - old_chan_count;
|
||||
return new_chan_count - old_chan_count;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -157,10 +170,14 @@ cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
{
|
||||
int i;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
if (ses->chans[i].server == server)
|
||||
if (ses->chans[i].server == server) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return &ses->chans[i];
|
||||
}
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -168,6 +185,7 @@ static int
|
||||
cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface)
|
||||
{
|
||||
struct TCP_Server_Info *chan_server;
|
||||
struct cifs_chan *chan;
|
||||
struct smb3_fs_context ctx = {NULL};
|
||||
static const char unc_fmt[] = "\\%s\\foo";
|
||||
@ -240,18 +258,19 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
ctx.use_client_guid = true;
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
chan_server = cifs_get_tcp_session(&ctx, ses->server);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan = ses->binding_chan = &ses->chans[ses->chan_count];
|
||||
chan->server = cifs_get_tcp_session(&ctx);
|
||||
chan->server = chan_server;
|
||||
if (IS_ERR(chan->server)) {
|
||||
rc = PTR_ERR(chan->server);
|
||||
chan->server = NULL;
|
||||
spin_unlock(&ses->chan_lock);
|
||||
goto out;
|
||||
}
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
chan->server->is_channel = true;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/*
|
||||
* We need to allocate the server crypto now as we will need
|
||||
@ -283,8 +302,11 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
* ses to the new server.
|
||||
*/
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
ses->chan_count++;
|
||||
atomic_set(&ses->chan_seq, 0);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
out:
|
||||
ses->binding = false;
|
||||
ses->binding_chan = NULL;
|
||||
@ -599,18 +621,85 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size)
|
||||
{
|
||||
int sz = base_size + ses->auth_key.len
|
||||
- CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
|
||||
|
||||
if (ses->domainName)
|
||||
sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
|
||||
else
|
||||
sz += sizeof(__le16);
|
||||
|
||||
if (ses->user_name)
|
||||
sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
|
||||
else
|
||||
sz += sizeof(__le16);
|
||||
|
||||
sz += sizeof(__le16) * strnlen(ses->workstation_name, CIFS_MAX_WORKSTATION_LEN);
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf,
|
||||
char *str_value,
|
||||
int str_length,
|
||||
unsigned char *pstart,
|
||||
unsigned char **pcur,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
unsigned char *tmp = pstart;
|
||||
int len;
|
||||
|
||||
if (!pbuf)
|
||||
return;
|
||||
|
||||
if (!pcur)
|
||||
pcur = &tmp;
|
||||
|
||||
if (!str_value) {
|
||||
pbuf->BufferOffset = cpu_to_le32(*pcur - pstart);
|
||||
pbuf->Length = 0;
|
||||
pbuf->MaximumLength = 0;
|
||||
*pcur += sizeof(__le16);
|
||||
} else {
|
||||
len = cifs_strtoUTF16((__le16 *)*pcur,
|
||||
str_value,
|
||||
str_length,
|
||||
nls_cp);
|
||||
len *= sizeof(__le16);
|
||||
pbuf->BufferOffset = cpu_to_le32(*pcur - pstart);
|
||||
pbuf->Length = cpu_to_le16(len);
|
||||
pbuf->MaximumLength = cpu_to_le16(len);
|
||||
*pcur += len;
|
||||
}
|
||||
}
|
||||
|
||||
/* BB Move to ntlmssp.c eventually */
|
||||
|
||||
/* We do not malloc the blob, it is passed in pbuffer, because
|
||||
it is fixed size, and small, making this approach cleaner */
|
||||
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 rc = 0;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
|
||||
NEGOTIATE_MESSAGE *sec_blob;
|
||||
__u32 flags;
|
||||
unsigned char *tmp;
|
||||
int len;
|
||||
|
||||
memset(pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));
|
||||
len = size_of_ntlmssp_blob(ses, sizeof(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_neg_ret;
|
||||
}
|
||||
sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer;
|
||||
|
||||
memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));
|
||||
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
|
||||
sec_blob->MessageType = NtLmNegotiate;
|
||||
|
||||
@ -624,34 +713,25 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
||||
if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
|
||||
flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
|
||||
|
||||
tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE);
|
||||
sec_blob->NegotiateFlags = cpu_to_le32(flags);
|
||||
|
||||
sec_blob->WorkstationName.BufferOffset = 0;
|
||||
sec_blob->WorkstationName.Length = 0;
|
||||
sec_blob->WorkstationName.MaximumLength = 0;
|
||||
/* 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);
|
||||
|
||||
/* Domain name is sent on the Challenge not Negotiate NTLMSSP request */
|
||||
sec_blob->DomainName.BufferOffset = 0;
|
||||
sec_blob->DomainName.Length = 0;
|
||||
sec_blob->DomainName.MaximumLength = 0;
|
||||
}
|
||||
cifs_security_buffer_from_str(&sec_blob->WorkstationName,
|
||||
NULL,
|
||||
CIFS_MAX_WORKSTATION_LEN,
|
||||
*pbuffer, &tmp,
|
||||
nls_cp);
|
||||
|
||||
static int size_of_ntlmssp_blob(struct cifs_ses *ses)
|
||||
{
|
||||
int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
|
||||
- CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
|
||||
|
||||
if (ses->domainName)
|
||||
sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
|
||||
else
|
||||
sz += 2;
|
||||
|
||||
if (ses->user_name)
|
||||
sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
|
||||
else
|
||||
sz += 2;
|
||||
|
||||
return sz;
|
||||
*buflen = tmp - *pbuffer;
|
||||
setup_ntlm_neg_ret:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int build_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
@ -663,6 +743,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
AUTHENTICATE_MESSAGE *sec_blob;
|
||||
__u32 flags;
|
||||
unsigned char *tmp;
|
||||
int len;
|
||||
|
||||
rc = setup_ntlmv2_rsp(ses, nls_cp);
|
||||
if (rc) {
|
||||
@ -670,7 +751,9 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
*buflen = 0;
|
||||
goto setup_ntlmv2_ret;
|
||||
}
|
||||
*pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL);
|
||||
|
||||
len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE));
|
||||
*pbuffer = kmalloc(len, GFP_KERNEL);
|
||||
if (!*pbuffer) {
|
||||
rc = -ENOMEM;
|
||||
cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
|
||||
@ -686,7 +769,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
|
||||
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
|
||||
NTLMSSP_NEGOTIATE_SEAL;
|
||||
NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
|
||||
if (ses->server->sign)
|
||||
flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
|
||||
@ -719,42 +802,23 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
sec_blob->NtChallengeResponse.MaximumLength = 0;
|
||||
}
|
||||
|
||||
if (ses->domainName == NULL) {
|
||||
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
|
||||
sec_blob->DomainName.Length = 0;
|
||||
sec_blob->DomainName.MaximumLength = 0;
|
||||
tmp += 2;
|
||||
} else {
|
||||
int len;
|
||||
len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
|
||||
CIFS_MAX_DOMAINNAME_LEN, nls_cp);
|
||||
len *= 2; /* unicode is 2 bytes each */
|
||||
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
|
||||
sec_blob->DomainName.Length = cpu_to_le16(len);
|
||||
sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
|
||||
tmp += len;
|
||||
}
|
||||
cifs_security_buffer_from_str(&sec_blob->DomainName,
|
||||
ses->domainName,
|
||||
CIFS_MAX_DOMAINNAME_LEN,
|
||||
*pbuffer, &tmp,
|
||||
nls_cp);
|
||||
|
||||
if (ses->user_name == NULL) {
|
||||
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
|
||||
sec_blob->UserName.Length = 0;
|
||||
sec_blob->UserName.MaximumLength = 0;
|
||||
tmp += 2;
|
||||
} else {
|
||||
int len;
|
||||
len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
|
||||
CIFS_MAX_USERNAME_LEN, nls_cp);
|
||||
len *= 2; /* unicode is 2 bytes each */
|
||||
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
|
||||
sec_blob->UserName.Length = cpu_to_le16(len);
|
||||
sec_blob->UserName.MaximumLength = cpu_to_le16(len);
|
||||
tmp += len;
|
||||
}
|
||||
cifs_security_buffer_from_str(&sec_blob->UserName,
|
||||
ses->user_name,
|
||||
CIFS_MAX_USERNAME_LEN,
|
||||
*pbuffer, &tmp,
|
||||
nls_cp);
|
||||
|
||||
sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
|
||||
sec_blob->WorkstationName.Length = 0;
|
||||
sec_blob->WorkstationName.MaximumLength = 0;
|
||||
tmp += 2;
|
||||
cifs_security_buffer_from_str(&sec_blob->WorkstationName,
|
||||
ses->workstation_name,
|
||||
CIFS_MAX_WORKSTATION_LEN,
|
||||
*pbuffer, &tmp,
|
||||
nls_cp);
|
||||
|
||||
if (((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) ||
|
||||
(ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
|
||||
@ -1230,6 +1294,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
__u16 bytes_remaining;
|
||||
char *bcc_ptr;
|
||||
unsigned char *ntlmsspblob = NULL;
|
||||
u16 blob_len;
|
||||
|
||||
cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
|
||||
@ -1253,10 +1318,15 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
|
||||
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
|
||||
|
||||
/* Build security blob before we assemble the request */
|
||||
build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses);
|
||||
sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
|
||||
sess_data->iov[1].iov_base = pSMB->req.SecurityBlob;
|
||||
pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
|
||||
rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
|
||||
&blob_len, ses,
|
||||
sess_data->nls_cp);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
sess_data->iov[1].iov_len = blob_len;
|
||||
sess_data->iov[1].iov_base = ntlmsspblob;
|
||||
pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
|
||||
|
||||
rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
|
||||
if (rc)
|
||||
|
@ -46,6 +46,10 @@ struct cop_vars {
|
||||
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
|
||||
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
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,
|
||||
create_options |= OPEN_REPARSE_POINT;
|
||||
|
||||
/* 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,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN,
|
||||
create_options, ACL_NO_MODE,
|
||||
smb2_data, SMB2_OP_QUERY_INFO, NULL);
|
||||
smb2_data, SMB2_OP_QUERY_INFO, cfile);
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -587,10 +592,11 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
create_options |= OPEN_REPARSE_POINT;
|
||||
|
||||
/* 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,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN,
|
||||
create_options, ACL_NO_MODE,
|
||||
smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL);
|
||||
smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -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)
|
||||
{
|
||||
__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,
|
||||
FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
|
||||
&eof, SMB2_OP_SET_EOF, NULL);
|
||||
&eof, SMB2_OP_SET_EOF, cfile);
|
||||
}
|
||||
|
||||
int
|
||||
@ -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 tcon_link *tlink;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifsFileInfo *cfile;
|
||||
int rc;
|
||||
|
||||
if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
|
||||
@ -729,10 +739,12 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_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,
|
||||
0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, NULL);
|
||||
0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
@ -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 get_dfs_referral_rsp *dfs_rsp = NULL;
|
||||
u32 dfs_req_size = 0, dfs_rsp_size = 0;
|
||||
int retry_count = 0;
|
||||
|
||||
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,
|
||||
true /* is_fsctl */,
|
||||
(char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
|
||||
(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 != -ENOENT) && (rc != -EOPNOTSUPP))
|
||||
cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc);
|
||||
if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
|
||||
cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
if (tcon == NULL)
|
||||
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;
|
||||
|
||||
if (tcon->tidStatus == CifsExiting) {
|
||||
@ -253,7 +257,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
/*
|
||||
* If we are reconnecting an extra channel, bind
|
||||
*/
|
||||
if (server->is_channel) {
|
||||
if (CIFS_SERVER_IS_CHAN(server)) {
|
||||
ses->binding = true;
|
||||
ses->binding_chan = cifs_ses_find_chan(ses, server);
|
||||
}
|
||||
@ -1456,7 +1460,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
char *ntlmssp_blob = NULL;
|
||||
unsigned char *ntlmssp_blob = NULL;
|
||||
bool use_spnego = false; /* else use raw ntlmssp */
|
||||
u16 blob_length = 0;
|
||||
|
||||
@ -1475,22 +1479,17 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
|
||||
GFP_KERNEL);
|
||||
if (ntlmssp_blob == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob,
|
||||
&blob_length, ses,
|
||||
sess_data->nls_cp);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
|
||||
if (use_spnego) {
|
||||
/* BB eventually need to add this */
|
||||
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
||||
rc = -EOPNOTSUPP;
|
||||
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_len = blob_length;
|
||||
@ -1841,7 +1840,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
|
||||
cifs_small_buf_release(req);
|
||||
rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
|
||||
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);
|
||||
tcon->need_reconnect = true;
|
||||
goto tcon_error_exit;
|
||||
@ -2669,7 +2668,18 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
|
||||
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;
|
||||
if (rsp == NULL) {
|
||||
rc = -EIO;
|
||||
kfree(pc_buf);
|
||||
goto err_free_req;
|
||||
}
|
||||
|
||||
trace_smb3_posix_mkdir_done(xid, le64_to_cpu(rsp->PersistentFileId),
|
||||
tcon->tid,
|
||||
ses->Suid, CREATE_NOT_FILE,
|
||||
@ -2942,7 +2952,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
||||
tcon->need_reconnect = true;
|
||||
}
|
||||
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),
|
||||
tcon->tid,
|
||||
ses->Suid, oparms->create_options,
|
||||
@ -3163,6 +3175,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
if ((plen == NULL) || (out_data == NULL))
|
||||
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);
|
||||
|
||||
/* 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)
|
||||
if (!ses)
|
||||
return NULL;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!ses->binding) {
|
||||
/* round robin */
|
||||
if (ses->chan_count > 1) {
|
||||
index = (uint)atomic_inc_return(&ses->chan_seq);
|
||||
index %= ses->chan_count;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return ses->chans[index].server;
|
||||
} else {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return cifs_ses_server(ses);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user