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 { ...@@ -132,7 +132,7 @@ struct cifs_ace {
/* /*
* The current SMB3 form of security descriptor is similar to what was used for * 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 * 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 * 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. * match the MS-DTYP and MS-SMB2 specs which define the wire format.
*/ */
...@@ -178,7 +178,7 @@ struct smb3_acl { ...@@ -178,7 +178,7 @@ struct smb3_acl {
/* /*
* Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid * 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 { struct owner_sid {
u8 Revision; u8 Revision;
......
...@@ -1466,7 +1466,7 @@ struct cifsInodeInfo { ...@@ -1466,7 +1466,7 @@ struct cifsInodeInfo {
struct list_head llist; /* locks helb by this inode */ struct list_head llist; /* locks helb by this inode */
/* /*
* NOTE: Some code paths call down_read(lock_sem) twice, so * 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. * for this semaphore to avoid deadlocks.
*/ */
struct rw_semaphore lock_sem; /* protect the fields above */ struct rw_semaphore lock_sem; /* protect the fields above */
......
...@@ -154,6 +154,7 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length, ...@@ -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 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 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_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 */ , extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifs_tcon *, int /* length of const struct cifs_tcon *, int /* length of
fixed section (word count) in two byte units */); 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); ...@@ -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_free_llist(struct list_head *llist);
extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); 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, extern int cifs_negotiate_protocol(const unsigned int xid,
struct cifs_ses *ses); struct cifs_ses *ses);
extern int cifs_setup_session(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, ...@@ -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, extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data, const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage, 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, extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
const FILE_BASIC_INFO *data, __u16 fid, const FILE_BASIC_INFO *data, __u16 fid,
__u32 pid_of_opener); __u32 pid_of_opener);
...@@ -613,8 +617,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, ...@@ -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); 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, const char *prefix, int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
size_t prefix_len);
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, 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) ...@@ -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 */ /* reconnect the socket, tcon, and smb session if needed */
static int static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
...@@ -338,7 +228,7 @@ 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); 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); mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
...@@ -5913,10 +5803,42 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -5913,10 +5803,42 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
return rc; 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 int
CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data, 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_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL;
...@@ -5925,6 +5847,7 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -5925,6 +5847,7 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
int bytes_returned = 0; int bytes_returned = 0;
char *data_offset; char *data_offset;
__u16 params, param_offset, offset, byte_count, count; __u16 params, param_offset, offset, byte_count, count;
int remap = cifs_remap(cifs_sb);
cifs_dbg(FYI, "In SetTimes\n"); cifs_dbg(FYI, "In SetTimes\n");
...@@ -5987,6 +5910,10 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -5987,6 +5910,10 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
if (rc == -EAGAIN) if (rc == -EAGAIN)
goto SetTimesRetry; goto SetTimesRetry;
if (rc == -EOPNOTSUPP)
return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
nls_codepage, cifs_sb);
return rc; return rc;
} }
......
This diff is collapsed.
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
struct cache_dfs_tgt { struct cache_dfs_tgt {
char *name; char *name;
int path_consumed;
struct list_head list; struct list_head list;
}; };
...@@ -350,7 +351,7 @@ static inline struct timespec64 get_expire_time(int ttl) ...@@ -350,7 +351,7 @@ static inline struct timespec64 get_expire_time(int ttl)
} }
/* Allocate a new DFS target */ /* 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; struct cache_dfs_tgt *t;
...@@ -362,6 +363,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name) ...@@ -362,6 +363,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name)
kfree(t); kfree(t);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
t->path_consumed = path_consumed;
INIT_LIST_HEAD(&t->list); INIT_LIST_HEAD(&t->list);
return t; return t;
} }
...@@ -384,7 +386,7 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, ...@@ -384,7 +386,7 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
for (i = 0; i < numrefs; i++) { for (i = 0; i < numrefs; i++) {
struct cache_dfs_tgt *t; 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)) { if (IS_ERR(t)) {
free_tgts(ce); free_tgts(ce);
return PTR_ERR(t); return PTR_ERR(t);
...@@ -490,16 +492,7 @@ static int add_cache_entry(const char *path, unsigned int hash, ...@@ -490,16 +492,7 @@ static int add_cache_entry(const char *path, unsigned int hash,
return 0; return 0;
} }
/* static struct cache_entry *__lookup_cache_entry(const char *path)
* 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; struct cache_entry *ce;
unsigned int h; unsigned int h;
...@@ -517,9 +510,75 @@ static struct cache_entry *lookup_cache_entry(const char *path, ...@@ -517,9 +510,75 @@ static struct cache_entry *lookup_cache_entry(const char *path,
if (!found) if (!found)
ce = ERR_PTR(-ENOENT); 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) if (hash)
*hash = h; *hash = h;
kfree(npath);
return ce; return ce;
} }
...@@ -773,6 +832,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) ...@@ -773,6 +832,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_it; goto err_free_it;
} }
it->it_path_consumed = t->path_consumed;
if (ce->tgthint == t) if (ce->tgthint == t)
list_add(&it->it_list, head); list_add(&it->it_list, head);
...@@ -1263,23 +1323,26 @@ void dfs_cache_del_vol(const char *fullpath) ...@@ -1263,23 +1323,26 @@ void dfs_cache_del_vol(const char *fullpath)
/** /**
* dfs_cache_get_tgt_share - parse a DFS target * dfs_cache_get_tgt_share - parse a DFS target
* *
* @path: DFS full path
* @it: DFS target iterator. * @it: DFS target iterator.
* @share: tree name. * @share: tree name.
* @share_len: length of tree name.
* @prefix: prefix path. * @prefix: prefix path.
* @prefix_len: length of prefix path.
* *
* Return zero if target was parsed correctly, otherwise non-zero. * Return zero if target was parsed correctly, otherwise non-zero.
*/ */
int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
const char **share, size_t *share_len, char **share, char **prefix)
const char **prefix, size_t *prefix_len)
{ {
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; return -EINVAL;
*share = NULL;
*prefix = NULL;
sep = it->it_name[0]; sep = it->it_name[0];
if (sep != '\\' && sep != '/') if (sep != '\\' && sep != '/')
return -EINVAL; return -EINVAL;
...@@ -1288,13 +1351,38 @@ int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, ...@@ -1288,13 +1351,38 @@ int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
if (!s) if (!s)
return -EINVAL; return -EINVAL;
/* point to prefix in target node */
s = strchrnul(s + 1, sep); s = strchrnul(s + 1, sep);
*share = it->it_name; /* extract target share */
*share_len = s - it->it_name; *share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
*prefix = *s ? s + 1 : s; if (!*share)
*prefix_len = &it->it_name[strlen(it->it_name)] - *prefix; 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; return 0;
} }
......
...@@ -19,6 +19,7 @@ struct dfs_cache_tgt_list { ...@@ -19,6 +19,7 @@ struct dfs_cache_tgt_list {
struct dfs_cache_tgt_iterator { struct dfs_cache_tgt_iterator {
char *it_name; char *it_name;
int it_path_consumed;
struct list_head it_list; struct list_head it_list;
}; };
...@@ -48,10 +49,8 @@ extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, ...@@ -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, extern int dfs_cache_update_vol(const char *fullpath,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
extern void dfs_cache_del_vol(const char *fullpath); extern void dfs_cache_del_vol(const char *fullpath);
extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, char **share, char **prefix);
const char **share, size_t *share_len,
const char **prefix, size_t *prefix_len);
static inline struct dfs_cache_tgt_iterator * static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
......
...@@ -1086,7 +1086,6 @@ smb311_posix_get_inode_info(struct inode **inode, ...@@ -1086,7 +1086,6 @@ smb311_posix_get_inode_info(struct inode **inode,
struct super_block *sb, unsigned int xid) struct super_block *sb, unsigned int xid)
{ {
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
bool adjust_tz = false; bool adjust_tz = false;
...@@ -1100,7 +1099,6 @@ smb311_posix_get_inode_info(struct inode **inode, ...@@ -1100,7 +1099,6 @@ smb311_posix_get_inode_info(struct inode **inode,
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/* /*
* 1. Fetch file metadata * 1. Fetch file metadata
......
...@@ -1164,8 +1164,7 @@ static inline void cifs_put_tcon_super(struct super_block *sb) ...@@ -1164,8 +1164,7 @@ static inline void cifs_put_tcon_super(struct super_block *sb)
} }
#endif #endif
int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
size_t prefix_len)
{ {
struct super_block *sb; struct super_block *sb;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
...@@ -1179,8 +1178,8 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, ...@@ -1179,8 +1178,8 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
kfree(cifs_sb->prepath); kfree(cifs_sb->prepath);
if (*prefix && prefix_len) { if (prefix && *prefix) {
cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC); cifs_sb->prepath = kstrndup(prefix, strlen(prefix), GFP_ATOMIC);
if (!cifs_sb->prepath) { if (!cifs_sb->prepath) {
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
......
...@@ -881,6 +881,33 @@ map_smb_to_linux_error(char *buf, bool logErr) ...@@ -881,6 +881,33 @@ map_smb_to_linux_error(char *buf, bool logErr)
return rc; 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 * 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 * portion, the number of word parameters and the data portion of the message
......
...@@ -938,8 +938,7 @@ sess_sendreceive(struct sess_data *sess_data) ...@@ -938,8 +938,7 @@ sess_sendreceive(struct sess_data *sess_data)
struct kvec rsp_iov = { NULL, 0 }; struct kvec rsp_iov = { NULL, 0 };
count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
smb_buf->smb_buf_length = be32_add_cpu(&smb_buf->smb_buf_length, count);
cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
put_bcc(count, smb_buf); put_bcc(count, smb_buf);
rc = SendReceive2(sess_data->xid, sess_data->ses, 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) ...@@ -1705,7 +1704,6 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
#else #else
cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
return -ENOSYS; return -ENOSYS;
break;
#endif /* CONFIG_CIFS_UPCALL */ #endif /* CONFIG_CIFS_UPCALL */
case RawNTLMSSP: case RawNTLMSSP:
sess_data->func = sess_auth_rawntlmssp_negotiate; sess_data->func = sess_auth_rawntlmssp_negotiate;
......
...@@ -688,7 +688,7 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path, ...@@ -688,7 +688,7 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
info.Attributes = cpu_to_le32(dosattrs); info.Attributes = cpu_to_le32(dosattrs);
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls,
cifs_remap(cifs_sb)); cifs_sb);
if (rc == 0) if (rc == 0)
cifsInode->cifsAttrs = dosattrs; cifsInode->cifsAttrs = dosattrs;
} }
...@@ -783,7 +783,7 @@ smb_set_file_info(struct inode *inode, const char *full_path, ...@@ -783,7 +783,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls,
cifs_remap(cifs_sb)); cifs_sb);
if (rc == 0) { if (rc == 0) {
cinode->cifsAttrs = le32_to_cpu(buf->Attributes); cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
goto out; goto out;
......
...@@ -508,15 +508,31 @@ cifs_ses_oplock_break(struct work_struct *work) ...@@ -508,15 +508,31 @@ cifs_ses_oplock_break(struct work_struct *work)
kfree(lw); 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 static bool
smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp)
struct smb2_lease_break_work *lw)
{ {
bool found;
__u8 lease_state; __u8 lease_state;
struct list_head *tmp; struct list_head *tmp;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cifs_pending_open *open;
struct cifsInodeInfo *cinode; struct cifsInodeInfo *cinode;
int ack_req = le32_to_cpu(rsp->Flags & int ack_req = le32_to_cpu(rsp->Flags &
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
...@@ -546,22 +562,29 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, ...@@ -546,22 +562,29 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
cfile->oplock_level = lease_state; cfile->oplock_level = lease_state;
cifs_queue_oplock_break(cfile); cifs_queue_oplock_break(cfile);
kfree(lw);
return true; 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) { list_for_each_entry(open, &tcon->pending_opens, olist) {
if (memcmp(open->lease_key, rsp->LeaseKey, if (memcmp(open->lease_key, rsp->LeaseKey,
SMB2_LEASE_KEY_SIZE)) SMB2_LEASE_KEY_SIZE))
continue; continue;
if (!found && ack_req) { if (!found && ack_req) {
found = true; found = open;
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);
} }
cifs_dbg(FYI, "found in the pending open list\n"); cifs_dbg(FYI, "found in the pending open list\n");
...@@ -582,14 +605,7 @@ smb2_is_valid_lease_break(char *buffer) ...@@ -582,14 +605,7 @@ smb2_is_valid_lease_break(char *buffer)
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct smb2_lease_break_work *lw; struct cifs_pending_open *open;
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;
cifs_dbg(FYI, "Checking for lease break\n"); cifs_dbg(FYI, "Checking for lease break\n");
...@@ -607,11 +623,27 @@ smb2_is_valid_lease_break(char *buffer) ...@@ -607,11 +623,27 @@ smb2_is_valid_lease_break(char *buffer)
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
cifs_stats_inc( cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks); &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(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return true; 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); spin_unlock(&tcon->open_file_lock);
if (tcon->crfid.is_valid && if (tcon->crfid.is_valid &&
...@@ -629,7 +661,6 @@ smb2_is_valid_lease_break(char *buffer) ...@@ -629,7 +661,6 @@ smb2_is_valid_lease_break(char *buffer)
} }
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
kfree(lw);
cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); cifs_dbg(FYI, "Can not process lease break - no lease matched\n");
return false; return false;
} }
......
...@@ -152,117 +152,6 @@ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd, ...@@ -152,117 +152,6 @@ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
return; 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 static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server) struct TCP_Server_Info *server)
...@@ -409,7 +298,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -409,7 +298,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (tcon->use_persistent) if (tcon->use_persistent)
tcon->need_reopen_files = true; 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); mutex_unlock(&tcon->ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
...@@ -1387,6 +1276,8 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1387,6 +1276,8 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
spnego_key = cifs_get_spnego_key(ses); spnego_key = cifs_get_spnego_key(ses);
if (IS_ERR(spnego_key)) { if (IS_ERR(spnego_key)) {
rc = PTR_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; spnego_key = NULL;
goto out; goto out;
} }
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
* Note that, due to trying to use names similar to the protocol specifications, * 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 * 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 * 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 * SMB2 commands
* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses * 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, ...@@ -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? */ /* 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 * 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