Commit 327a8d76 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull cifs updates from Steve French:
 "16 cifs/smb3 fixes, about half DFS related, two fixes for stable.

  Still working on and testing an additional set of fixes (including
  updates to mount, and some fallocate scenario improvements) for later
  in the merge window"

* tag '5.9-rc-smb3-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: document and cleanup dfs mount
  cifs: only update prefix path of DFS links in cifs_tree_connect()
  cifs: fix double free error on share and prefix
  cifs: handle RESP_GET_DFS_REFERRAL.PathConsumed in reconnect
  cifs: handle empty list of targets in cifs_reconnect()
  cifs: rename reconn_inval_dfs_target()
  cifs: reduce number of referral requests in DFS link lookups
  cifs: merge __{cifs,smb2}_reconnect[_tcon]() into cifs_tree_connect()
  cifs: convert to use be32_add_cpu()
  cifs: delete duplicated words in header files
  cifs: Remove the superfluous break
  cifs: smb1: Try failing back to SetFileInfo if SetPathInfo fails
  cifs`: handle ERRBaduid for SMB1
  cifs: remove unused variable 'server'
  smb3: warn on confusing error scenario with sec=krb5
  cifs: Fix leak when handling lease break for cached root fid
parents 96e3f3c1 7efd0815
......@@ -132,7 +132,7 @@ struct cifs_ace {
/*
* The current SMB3 form of security descriptor is similar to what was used for
* cifs (see above) but some fields are split, and fields in the struct below
* matches names of fields to the the spec, MS-DTYP (see sections 2.4.5 and
* matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and
* 2.4.6). Note that "CamelCase" fields are used in this struct in order to
* match the MS-DTYP and MS-SMB2 specs which define the wire format.
*/
......@@ -178,7 +178,7 @@ struct smb3_acl {
/*
* Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid
* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
*/
struct owner_sid {
u8 Revision;
......
......@@ -1466,7 +1466,7 @@ struct cifsInodeInfo {
struct list_head llist; /* locks helb by this inode */
/*
* NOTE: Some code paths call down_read(lock_sem) twice, so
* we must always use use cifs_down_write() instead of down_write()
* we must always use cifs_down_write() instead of down_write()
* for this semaphore to avoid deadlocks.
*/
struct rw_semaphore lock_sem; /* protect the fields above */
......
......@@ -154,6 +154,7 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length,
extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port);
extern int map_smb_to_linux_error(char *buf, bool logErr);
extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr);
extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifs_tcon *, int /* length of
fixed section (word count) in two byte units */);
......@@ -271,6 +272,9 @@ extern void cifs_move_llist(struct list_head *source, struct list_head *dest);
extern void cifs_free_llist(struct list_head *llist);
extern void cifs_del_lock_waiters(struct cifsLockInfo *lock);
extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nlsc);
extern int cifs_negotiate_protocol(const unsigned int xid,
struct cifs_ses *ses);
extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
......@@ -344,7 +348,7 @@ extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage,
int remap_special_chars);
struct cifs_sb_info *cifs_sb);
extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
const FILE_BASIC_INFO *data, __u16 fid,
__u32 pid_of_opener);
......@@ -613,8 +617,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, const char *prefix,
size_t prefix_len);
int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
#ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
......
......@@ -124,116 +124,6 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
*/
}
#ifdef CONFIG_CIFS_DFS_UPCALL
static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
struct cifs_tcon *tcon)
{
int rc;
struct TCP_Server_Info *server = tcon->ses->server;
struct dfs_cache_tgt_list tl;
struct dfs_cache_tgt_iterator *it = NULL;
char *tree;
const char *tcp_host;
size_t tcp_host_len;
const char *dfs_host;
size_t dfs_host_len;
tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
if (!tree)
return -ENOMEM;
if (!tcon->dfs_path) {
if (tcon->ipc) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
server->hostname);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
} else {
rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc);
}
goto out;
}
rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl);
if (rc)
goto out;
extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
for (it = dfs_cache_get_tgt_iterator(&tl); it;
it = dfs_cache_get_next_tgt(&tl, it)) {
const char *share, *prefix;
size_t share_len, prefix_len;
bool target_match;
rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
&prefix_len);
if (rc) {
cifs_dbg(VFS, "%s: failed to parse target share %d\n",
__func__, rc);
continue;
}
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n",
__func__,
(int)dfs_host_len, dfs_host,
(int)tcp_host_len, tcp_host);
rc = match_target_ip(server, dfs_host, dfs_host_len,
&target_match);
if (rc) {
cifs_dbg(VFS, "%s: failed to match target ip: %d\n",
__func__, rc);
break;
}
if (!target_match) {
cifs_dbg(FYI, "%s: skipping target\n", __func__);
continue;
}
}
if (tcon->ipc) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%.*s\\IPC$",
(int)share_len, share);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
} else {
scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len,
share);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
if (!rc) {
rc = update_super_prepath(tcon, prefix,
prefix_len);
break;
}
}
if (rc == -EREMOTE)
break;
}
if (!rc) {
if (it)
rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1,
it);
else
rc = -ENOENT;
}
dfs_cache_free_tgts(&tl);
out:
kfree(tree);
return rc;
}
#else
static inline int __cifs_reconnect_tcon(const struct nls_table *nlsc,
struct cifs_tcon *tcon)
{
return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc);
}
#endif
/* reconnect the socket, tcon, and smb session if needed */
static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
......@@ -338,7 +228,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
}
cifs_mark_open_files_invalid(tcon);
rc = __cifs_reconnect_tcon(nls_codepage, tcon);
rc = cifs_tree_connect(0, tcon, nls_codepage);
mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
......@@ -5913,10 +5803,42 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
static int
CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb)
{
int oplock = 0;
struct cifs_open_parms oparms;
struct cifs_fid fid;
int rc;
oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb;
oparms.desired_access = GENERIC_WRITE;
oparms.create_options = cifs_create_options(cifs_sb, 0);
oparms.disposition = FILE_OPEN;
oparms.path = fileName;
oparms.fid = &fid;
oparms.reconnect = false;
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (rc)
goto out;
rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid);
CIFSSMBClose(xid, tcon, fid.netfid);
out:
return rc;
}
int
CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage, int remap)
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb)
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
......@@ -5925,6 +5847,7 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
int bytes_returned = 0;
char *data_offset;
__u16 params, param_offset, offset, byte_count, count;
int remap = cifs_remap(cifs_sb);
cifs_dbg(FYI, "In SetTimes\n");
......@@ -5987,6 +5910,10 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
if (rc == -EAGAIN)
goto SetTimesRetry;
if (rc == -EOPNOTSUPP)
return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
nls_codepage, cifs_sb);
return rc;
}
......
This diff is collapsed.
......@@ -29,6 +29,7 @@
struct cache_dfs_tgt {
char *name;
int path_consumed;
struct list_head list;
};
......@@ -350,7 +351,7 @@ static inline struct timespec64 get_expire_time(int ttl)
}
/* Allocate a new DFS target */
static struct cache_dfs_tgt *alloc_target(const char *name)
static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
{
struct cache_dfs_tgt *t;
......@@ -362,6 +363,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name)
kfree(t);
return ERR_PTR(-ENOMEM);
}
t->path_consumed = path_consumed;
INIT_LIST_HEAD(&t->list);
return t;
}
......@@ -384,7 +386,7 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
for (i = 0; i < numrefs; i++) {
struct cache_dfs_tgt *t;
t = alloc_target(refs[i].node_name);
t = alloc_target(refs[i].node_name, refs[i].path_consumed);
if (IS_ERR(t)) {
free_tgts(ce);
return PTR_ERR(t);
......@@ -490,16 +492,7 @@ static int add_cache_entry(const char *path, unsigned int hash,
return 0;
}
/*
* Find a DFS cache entry in hash table and optionally check prefix path against
* @path.
* Use whole path components in the match.
* Must be called with htable_rw_lock held.
*
* Return ERR_PTR(-ENOENT) if the entry is not found.
*/
static struct cache_entry *lookup_cache_entry(const char *path,
unsigned int *hash)
static struct cache_entry *__lookup_cache_entry(const char *path)
{
struct cache_entry *ce;
unsigned int h;
......@@ -517,9 +510,75 @@ static struct cache_entry *lookup_cache_entry(const char *path,
if (!found)
ce = ERR_PTR(-ENOENT);
return ce;
}
/*
* Find a DFS cache entry in hash table and optionally check prefix path against
* @path.
* Use whole path components in the match.
* Must be called with htable_rw_lock held.
*
* Return ERR_PTR(-ENOENT) if the entry is not found.
*/
static struct cache_entry *lookup_cache_entry(const char *path, unsigned int *hash)
{
struct cache_entry *ce = ERR_PTR(-ENOENT);
unsigned int h;
int cnt = 0;
char *npath;
char *s, *e;
char sep;
npath = kstrndup(path, strlen(path), GFP_KERNEL);
if (!npath)
return ERR_PTR(-ENOMEM);
s = npath;
sep = *npath;
while ((s = strchr(s, sep)) && ++cnt < 3)
s++;
if (cnt < 3) {
h = cache_entry_hash(path, strlen(path));
ce = __lookup_cache_entry(path);
goto out;
}
/*
* Handle paths that have more than two path components and are a complete prefix of the DFS
* referral request path (@path).
*
* See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request".
*/
h = cache_entry_hash(npath, strlen(npath));
e = npath + strlen(npath) - 1;
while (e > s) {
char tmp;
/* skip separators */
while (e > s && *e == sep)
e--;
if (e == s)
goto out;
tmp = *(e+1);
*(e+1) = 0;
ce = __lookup_cache_entry(npath);
if (!IS_ERR(ce)) {
h = cache_entry_hash(npath, strlen(npath));
break;
}
*(e+1) = tmp;
/* backward until separator */
while (e > s && *e != sep)
e--;
}
out:
if (hash)
*hash = h;
kfree(npath);
return ce;
}
......@@ -773,6 +832,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
rc = -ENOMEM;
goto err_free_it;
}
it->it_path_consumed = t->path_consumed;
if (ce->tgthint == t)
list_add(&it->it_list, head);
......@@ -1263,23 +1323,26 @@ void dfs_cache_del_vol(const char *fullpath)
/**
* dfs_cache_get_tgt_share - parse a DFS target
*
* @path: DFS full path
* @it: DFS target iterator.
* @share: tree name.
* @share_len: length of tree name.
* @prefix: prefix path.
* @prefix_len: length of prefix path.
*
* Return zero if target was parsed correctly, otherwise non-zero.
*/
int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
const char **share, size_t *share_len,
const char **prefix, size_t *prefix_len)
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
char **share, char **prefix)
{
char *s, sep;
char *s, sep, *p;
size_t len;
size_t plen1, plen2;
if (!it || !share || !share_len || !prefix || !prefix_len)
if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
return -EINVAL;
*share = NULL;
*prefix = NULL;
sep = it->it_name[0];
if (sep != '\\' && sep != '/')
return -EINVAL;
......@@ -1288,13 +1351,38 @@ int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
if (!s)
return -EINVAL;
/* point to prefix in target node */
s = strchrnul(s + 1, sep);
*share = it->it_name;
*share_len = s - it->it_name;
*prefix = *s ? s + 1 : s;
*prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
/* extract target share */
*share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
if (!*share)
return -ENOMEM;
/* skip separator */
if (*s)
s++;
/* point to prefix in DFS path */
p = path + it->it_path_consumed;
if (*p == sep)
p++;
/* merge prefix paths from DFS path and target node */
plen1 = it->it_name + strlen(it->it_name) - s;
plen2 = path + strlen(path) - p;
if (plen1 || plen2) {
len = plen1 + plen2 + 2;
*prefix = kmalloc(len, GFP_KERNEL);
if (!*prefix) {
kfree(*share);
*share = NULL;
return -ENOMEM;
}
if (plen1)
scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p);
else
strscpy(*prefix, p, len);
}
return 0;
}
......
......@@ -19,6 +19,7 @@ struct dfs_cache_tgt_list {
struct dfs_cache_tgt_iterator {
char *it_name;
int it_path_consumed;
struct list_head it_list;
};
......@@ -48,10 +49,8 @@ extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol,
extern int dfs_cache_update_vol(const char *fullpath,
struct TCP_Server_Info *server);
extern void dfs_cache_del_vol(const char *fullpath);
extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
const char **share, size_t *share_len,
const char **prefix, size_t *prefix_len);
extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
char **share, char **prefix);
static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
......
......@@ -1086,7 +1086,6 @@ smb311_posix_get_inode_info(struct inode **inode,
struct super_block *sb, unsigned int xid)
{
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
bool adjust_tz = false;
......@@ -1100,7 +1099,6 @@ smb311_posix_get_inode_info(struct inode **inode,
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/*
* 1. Fetch file metadata
......
......@@ -1164,8 +1164,7 @@ static inline void cifs_put_tcon_super(struct super_block *sb)
}
#endif
int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
size_t prefix_len)
int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
{
struct super_block *sb;
struct cifs_sb_info *cifs_sb;
......@@ -1179,8 +1178,8 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
kfree(cifs_sb->prepath);
if (*prefix && prefix_len) {
cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
if (prefix && *prefix) {
cifs_sb->prepath = kstrndup(prefix, strlen(prefix), GFP_ATOMIC);
if (!cifs_sb->prepath) {
rc = -ENOMEM;
goto out;
......
......@@ -881,6 +881,33 @@ map_smb_to_linux_error(char *buf, bool logErr)
return rc;
}
int
map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)
{
int rc;
struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf;
rc = map_smb_to_linux_error((char *)smb, logErr);
if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) {
/* possible ERRBaduid */
__u8 class = smb->Status.DosError.ErrorClass;
__u16 code = le16_to_cpu(smb->Status.DosError.Error);
/* switch can be used to handle different errors */
if (class == ERRSRV && code == ERRbaduid) {
cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
code);
spin_lock(&GlobalMid_Lock);
if (mid->server->tcpStatus != CifsExiting)
mid->server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock);
}
}
return rc;
}
/*
* calculate the size of the SMB message based on the fixed header
* portion, the number of word parameters and the data portion of the message
......
......@@ -938,8 +938,7 @@ sess_sendreceive(struct sess_data *sess_data)
struct kvec rsp_iov = { NULL, 0 };
count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
smb_buf->smb_buf_length =
cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
be32_add_cpu(&smb_buf->smb_buf_length, count);
put_bcc(count, smb_buf);
rc = SendReceive2(sess_data->xid, sess_data->ses,
......@@ -1705,7 +1704,6 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
#else
cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
return -ENOSYS;
break;
#endif /* CONFIG_CIFS_UPCALL */
case RawNTLMSSP:
sess_data->func = sess_auth_rawntlmssp_negotiate;
......
......@@ -688,7 +688,7 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
info.Attributes = cpu_to_le32(dosattrs);
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls,
cifs_remap(cifs_sb));
cifs_sb);
if (rc == 0)
cifsInode->cifsAttrs = dosattrs;
}
......@@ -783,7 +783,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
tcon = tlink_tcon(tlink);
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls,
cifs_remap(cifs_sb));
cifs_sb);
if (rc == 0) {
cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
goto out;
......
......@@ -508,15 +508,31 @@ cifs_ses_oplock_break(struct work_struct *work)
kfree(lw);
}
static void
smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key,
__le32 new_lease_state)
{
struct smb2_lease_break_work *lw;
lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
if (!lw) {
cifs_put_tlink(tlink);
return;
}
INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
lw->tlink = tlink;
lw->lease_state = new_lease_state;
memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE);
queue_work(cifsiod_wq, &lw->lease_break);
}
static bool
smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
struct smb2_lease_break_work *lw)
smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp)
{
bool found;
__u8 lease_state;
struct list_head *tmp;
struct cifsFileInfo *cfile;
struct cifs_pending_open *open;
struct cifsInodeInfo *cinode;
int ack_req = le32_to_cpu(rsp->Flags &
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
......@@ -546,22 +562,29 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
cfile->oplock_level = lease_state;
cifs_queue_oplock_break(cfile);
kfree(lw);
return true;
}
found = false;
return false;
}
static struct cifs_pending_open *
smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon,
struct smb2_lease_break *rsp)
{
__u8 lease_state = le32_to_cpu(rsp->NewLeaseState);
int ack_req = le32_to_cpu(rsp->Flags &
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
struct cifs_pending_open *open;
struct cifs_pending_open *found = NULL;
list_for_each_entry(open, &tcon->pending_opens, olist) {
if (memcmp(open->lease_key, rsp->LeaseKey,
SMB2_LEASE_KEY_SIZE))
continue;
if (!found && ack_req) {
found = true;
memcpy(lw->lease_key, open->lease_key,
SMB2_LEASE_KEY_SIZE);
lw->tlink = cifs_get_tlink(open->tlink);
queue_work(cifsiod_wq, &lw->lease_break);
found = open;
}
cifs_dbg(FYI, "found in the pending open list\n");
......@@ -582,14 +605,7 @@ smb2_is_valid_lease_break(char *buffer)
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct smb2_lease_break_work *lw;
lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
if (!lw)
return false;
INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
lw->lease_state = rsp->NewLeaseState;
struct cifs_pending_open *open;
cifs_dbg(FYI, "Checking for lease break\n");
......@@ -607,11 +623,27 @@ smb2_is_valid_lease_break(char *buffer)
spin_lock(&tcon->open_file_lock);
cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks);
if (smb2_tcon_has_lease(tcon, rsp, lw)) {
if (smb2_tcon_has_lease(tcon, rsp)) {
spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
open = smb2_tcon_find_pending_open_lease(tcon,
rsp);
if (open) {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
struct tcon_link *tlink;
tlink = cifs_get_tlink(open->tlink);
memcpy(lease_key, open->lease_key,
SMB2_LEASE_KEY_SIZE);
spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
smb2_queue_pending_open_break(tlink,
lease_key,
rsp->NewLeaseState);
return true;
}
spin_unlock(&tcon->open_file_lock);
if (tcon->crfid.is_valid &&
......@@ -629,7 +661,6 @@ smb2_is_valid_lease_break(char *buffer)
}
}
spin_unlock(&cifs_tcp_ses_lock);
kfree(lw);
cifs_dbg(FYI, "Can not process lease break - no lease matched\n");
return false;
}
......
......@@ -152,117 +152,6 @@ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
return;
}
#ifdef CONFIG_CIFS_DFS_UPCALL
static int __smb2_reconnect(const struct nls_table *nlsc,
struct cifs_tcon *tcon)
{
int rc;
struct TCP_Server_Info *server = tcon->ses->server;
struct dfs_cache_tgt_list tl;
struct dfs_cache_tgt_iterator *it = NULL;
char *tree;
const char *tcp_host;
size_t tcp_host_len;
const char *dfs_host;
size_t dfs_host_len;
tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
if (!tree)
return -ENOMEM;
if (!tcon->dfs_path) {
if (tcon->ipc) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
server->hostname);
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
} else {
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon,
nlsc);
}
goto out;
}
rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl);
if (rc)
goto out;
extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
for (it = dfs_cache_get_tgt_iterator(&tl); it;
it = dfs_cache_get_next_tgt(&tl, it)) {
const char *share, *prefix;
size_t share_len, prefix_len;
bool target_match;
rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
&prefix_len);
if (rc) {
cifs_dbg(VFS, "%s: failed to parse target share %d\n",
__func__, rc);
continue;
}
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n",
__func__,
(int)dfs_host_len, dfs_host,
(int)tcp_host_len, tcp_host);
rc = match_target_ip(server, dfs_host, dfs_host_len,
&target_match);
if (rc) {
cifs_dbg(VFS, "%s: failed to match target ip: %d\n",
__func__, rc);
break;
}
if (!target_match) {
cifs_dbg(FYI, "%s: skipping target\n", __func__);
continue;
}
}
if (tcon->ipc) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%.*s\\IPC$",
(int)share_len, share);
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
} else {
scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len,
share);
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
if (!rc) {
rc = update_super_prepath(tcon, prefix,
prefix_len);
break;
}
}
if (rc == -EREMOTE)
break;
}
if (!rc) {
if (it)
rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1,
it);
else
rc = -ENOENT;
}
dfs_cache_free_tgts(&tl);
out:
kfree(tree);
return rc;
}
#else
static inline int __smb2_reconnect(const struct nls_table *nlsc,
struct cifs_tcon *tcon)
{
return SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nlsc);
}
#endif
static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server)
......@@ -409,7 +298,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (tcon->use_persistent)
tcon->need_reopen_files = true;
rc = __smb2_reconnect(nls_codepage, tcon);
rc = cifs_tree_connect(0, tcon, nls_codepage);
mutex_unlock(&tcon->ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
......@@ -1387,6 +1276,8 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
spnego_key = cifs_get_spnego_key(ses);
if (IS_ERR(spnego_key)) {
rc = PTR_ERR(spnego_key);
if (rc == -ENOKEY)
cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n");
spnego_key = NULL;
goto out;
}
......
......@@ -31,7 +31,7 @@
* Note that, due to trying to use names similar to the protocol specifications,
* there are many mixed case field names in the structures below. Although
* this does not match typical Linux kernel style, it is necessary to be
* be able to match against the protocol specfication.
* able to match against the protocol specfication.
*
* SMB2 commands
* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
......
......@@ -936,7 +936,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
}
/* BB special case reconnect tid and uid here? */
return map_smb_to_linux_error(mid->resp_buf, log_error);
return map_and_check_smb_error(mid, log_error);
}
struct mid_q_entry *
......
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