summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/smb/client/cifsencrypt.c162
-rw-r--r--fs/smb/client/cifsglob.h35
-rw-r--r--fs/smb/client/cifspdu.h18
-rw-r--r--fs/smb/client/cifsproto.h5
-rw-r--r--fs/smb/client/connect.c133
-rw-r--r--fs/smb/client/dfs.c80
-rw-r--r--fs/smb/client/dfs.h44
-rw-r--r--fs/smb/client/dfs_cache.c20
-rw-r--r--fs/smb/client/dir.c6
-rw-r--r--fs/smb/client/dns_resolve.c108
-rw-r--r--fs/smb/client/dns_resolve.h23
-rw-r--r--fs/smb/client/fs_context.c4
-rw-r--r--fs/smb/client/fs_context.h1
-rw-r--r--fs/smb/client/inode.c7
-rw-r--r--fs/smb/client/misc.c29
-rw-r--r--fs/smb/client/netmisc.c4
-rw-r--r--fs/smb/client/nterr.c8
-rw-r--r--fs/smb/client/readdir.c2
-rw-r--r--fs/smb/client/rfc1002pdu.h6
-rw-r--r--fs/smb/client/smb1ops.c6
-rw-r--r--fs/smb/client/smb2file.c4
-rw-r--r--fs/smb/client/smb2inode.c202
-rw-r--r--fs/smb/client/smb2ops.c9
-rw-r--r--fs/smb/client/smb2pdu.h2
-rw-r--r--fs/smb/client/trace.h1
25 files changed, 483 insertions, 436 deletions
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 7a43daacc815..e69968e88fe7 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -315,59 +315,72 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
return 0;
}
-/* Server has provided av pairs/target info in the type 2 challenge
- * packet and we have plucked it and stored within smb session.
- * We parse that blob here to find netbios domain name to be used
- * as part of ntlmv2 authentication (in Target String), if not already
- * specified on the command line.
- * If this function returns without any error but without fetching
- * domain name, authentication may fail against some server but
- * may not fail against other (those who are not very particular
- * about target string i.e. for some, just user name might suffice.
+#define AV_TYPE(av) (le16_to_cpu(av->type))
+#define AV_LEN(av) (le16_to_cpu(av->length))
+#define AV_DATA_PTR(av) ((void *)av->data)
+
+#define av_for_each_entry(ses, av) \
+ for (av = NULL; (av = find_next_av(ses, av));)
+
+static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
+ struct ntlmssp2_name *av)
+{
+ u16 len;
+ u8 *end;
+
+ end = (u8 *)ses->auth_key.response + ses->auth_key.len;
+ if (!av) {
+ if (unlikely(!ses->auth_key.response || !ses->auth_key.len))
+ return NULL;
+ av = (void *)ses->auth_key.response;
+ } else {
+ av = (void *)((u8 *)av + sizeof(*av) + AV_LEN(av));
+ }
+
+ if ((u8 *)av + sizeof(*av) > end)
+ return NULL;
+
+ len = AV_LEN(av);
+ if (AV_TYPE(av) == NTLMSSP_AV_EOL)
+ return NULL;
+ if (!len || (u8 *)av + sizeof(*av) + len > end)
+ return NULL;
+ return av;
+}
+
+/*
+ * Check if server has provided av pair of @type in the NTLMSSP
+ * CHALLENGE_MESSAGE blob.
*/
-static int
-find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
+static int find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
{
- unsigned int attrsize;
- unsigned int type;
- unsigned int onesize = sizeof(struct ntlmssp2_name);
- unsigned char *blobptr;
- unsigned char *blobend;
- struct ntlmssp2_name *attrptr;
+ const struct nls_table *nlsc = ses->local_nls;
+ struct ntlmssp2_name *av;
+ u16 len, nlen;
- if (!ses->auth_key.len || !ses->auth_key.response)
+ if (*name)
return 0;
- blobptr = ses->auth_key.response;
- blobend = blobptr + ses->auth_key.len;
-
- while (blobptr + onesize < blobend) {
- attrptr = (struct ntlmssp2_name *) blobptr;
- type = le16_to_cpu(attrptr->type);
- if (type == NTLMSSP_AV_EOL)
- break;
- blobptr += 2; /* advance attr type */
- attrsize = le16_to_cpu(attrptr->length);
- blobptr += 2; /* advance attr size */
- if (blobptr + attrsize > blobend)
+ av_for_each_entry(ses, av) {
+ len = AV_LEN(av);
+ if (AV_TYPE(av) != type)
+ continue;
+ if (!IS_ALIGNED(len, sizeof(__le16))) {
+ cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
+ __func__, len, type);
+ continue;
+ }
+ nlen = len / sizeof(__le16);
+ if (nlen <= maxlen) {
+ ++nlen;
+ *name = kmalloc(nlen, GFP_KERNEL);
+ if (!*name)
+ return -ENOMEM;
+ cifs_from_utf16(*name, AV_DATA_PTR(av), nlen,
+ len, nlsc, NO_MAP_UNI_RSVD);
break;
- if (type == NTLMSSP_AV_NB_DOMAIN_NAME) {
- if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN)
- break;
- if (!ses->domainName) {
- ses->domainName =
- kmalloc(attrsize + 1, GFP_KERNEL);
- if (!ses->domainName)
- return -ENOMEM;
- cifs_from_utf16(ses->domainName,
- (__le16 *)blobptr, attrsize, attrsize,
- nls_cp, NO_MAP_UNI_RSVD);
- break;
- }
}
- blobptr += attrsize; /* advance attr value */
}
-
return 0;
}
@@ -377,40 +390,16 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
* as part of ntlmv2 authentication (or local current time as
* default in case of failure)
*/
-static __le64
-find_timestamp(struct cifs_ses *ses)
+static __le64 find_timestamp(struct cifs_ses *ses)
{
- unsigned int attrsize;
- unsigned int type;
- unsigned int onesize = sizeof(struct ntlmssp2_name);
- unsigned char *blobptr;
- unsigned char *blobend;
- struct ntlmssp2_name *attrptr;
+ struct ntlmssp2_name *av;
struct timespec64 ts;
- if (!ses->auth_key.len || !ses->auth_key.response)
- return 0;
-
- blobptr = ses->auth_key.response;
- blobend = blobptr + ses->auth_key.len;
-
- while (blobptr + onesize < blobend) {
- attrptr = (struct ntlmssp2_name *) blobptr;
- type = le16_to_cpu(attrptr->type);
- if (type == NTLMSSP_AV_EOL)
- break;
- blobptr += 2; /* advance attr type */
- attrsize = le16_to_cpu(attrptr->length);
- blobptr += 2; /* advance attr size */
- if (blobptr + attrsize > blobend)
- break;
- if (type == NTLMSSP_AV_TIMESTAMP) {
- if (attrsize == sizeof(u64))
- return *((__le64 *)blobptr);
- }
- blobptr += attrsize; /* advance attr value */
+ av_for_each_entry(ses, av) {
+ if (AV_TYPE(av) == NTLMSSP_AV_TIMESTAMP &&
+ AV_LEN(av) == sizeof(u64))
+ return *((__le64 *)AV_DATA_PTR(av));
}
-
ktime_get_real_ts64(&ts);
return cpu_to_le64(cifs_UnixTimeToNT(ts));
}
@@ -563,16 +552,29 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
if (!ses->domainName) {
if (ses->domainAuto) {
- rc = find_domain_name(ses, nls_cp);
- if (rc) {
- cifs_dbg(VFS, "error %d finding domain name\n",
- rc);
+ /*
+ * Domain (workgroup) hasn't been specified in
+ * mount options, so try to find it in
+ * CHALLENGE_MESSAGE message and then use it as
+ * part of NTLMv2 authentication.
+ */
+ rc = find_av_name(ses, NTLMSSP_AV_NB_DOMAIN_NAME,
+ &ses->domainName,
+ CIFS_MAX_DOMAINNAME_LEN);
+ if (rc)
goto setup_ntlmv2_rsp_ret;
- }
} else {
ses->domainName = kstrdup("", GFP_KERNEL);
+ if (!ses->domainName) {
+ rc = -ENOMEM;
+ goto setup_ntlmv2_rsp_ret;
+ }
}
}
+ rc = find_av_name(ses, NTLMSSP_AV_DNS_DOMAIN_NAME,
+ &ses->dns_dom, CIFS_MAX_DOMAINNAME_LEN);
+ if (rc)
+ goto setup_ntlmv2_rsp_ret;
} else {
rc = build_avpair_blob(ses, nls_cp);
if (rc) {
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 6e63abe461fd..49ffc040f736 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -811,23 +811,15 @@ struct TCP_Server_Info {
bool use_swn_dstaddr;
struct sockaddr_storage swn_dstaddr;
#endif
- struct mutex refpath_lock; /* protects leaf_fullpath */
/*
- * leaf_fullpath: Canonical DFS referral path related to this
- * connection.
- * It is used in DFS cache refresher, reconnect and may
- * change due to nested DFS links.
- *
- * Protected by @refpath_lock and @srv_lock. The @refpath_lock is
- * mostly used for not requiring a copy of @leaf_fullpath when getting
- * cached or new DFS referrals (which might also sleep during I/O).
- * While @srv_lock is held for making string and NULL comparisons against
- * both fields as in mount(2) and cache refresh.
+ * Canonical DFS referral path used in cifs_reconnect() for failover as
+ * well as in DFS cache refresher.
*
* format: \\HOST\SHARE[\OPTIONAL PATH]
*/
char *leaf_fullpath;
bool dfs_conn:1;
+ char dns_dom[CIFS_MAX_DOMAINNAME_LEN + 1];
};
static inline bool is_smb1(struct TCP_Server_Info *server)
@@ -1154,6 +1146,7 @@ struct cifs_ses {
/* ========= end: protected by chan_lock ======== */
struct cifs_ses *dfs_root_ses;
struct nls_table *local_nls;
+ char *dns_dom; /* FQDN of the domain */
};
static inline bool
@@ -2311,4 +2304,24 @@ static inline bool cifs_ses_exiting(struct cifs_ses *ses)
return ret;
}
+static inline bool cifs_netbios_name(const char *name, size_t namelen)
+{
+ bool ret = false;
+ size_t i;
+
+ if (namelen >= 1 && namelen <= RFC1001_NAME_LEN) {
+ for (i = 0; i < namelen; i++) {
+ const unsigned char c = name[i];
+
+ if (c == '\\' || c == '/' || c == ':' || c == '*' ||
+ c == '?' || c == '"' || c == '<' || c == '>' ||
+ c == '|' || c == '.')
+ return false;
+ if (!ret && isalpha(c))
+ ret = true;
+ }
+ }
+ return ret;
+}
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h
index ee78bb6741d6..5c047b00516f 100644
--- a/fs/smb/client/cifspdu.h
+++ b/fs/smb/client/cifspdu.h
@@ -649,7 +649,7 @@ typedef union smb_com_session_setup_andx {
struct ntlmssp2_name {
__le16 type;
__le16 length;
-/* char name[length]; */
+ __u8 data[];
} __attribute__((packed));
struct ntlmv2_resp {
@@ -1484,22 +1484,6 @@ struct file_notify_information {
__u8 FileName[];
} __attribute__((packed));
-/* For IO_REPARSE_TAG_SYMLINK */
-struct reparse_symlink_data {
- __le32 ReparseTag;
- __le16 ReparseDataLength;
- __u16 Reserved;
- __le16 SubstituteNameOffset;
- __le16 SubstituteNameLength;
- __le16 PrintNameOffset;
- __le16 PrintNameLength;
- __le32 Flags;
- char PathBuffer[];
-} __attribute__((packed));
-
-/* Flag above */
-#define SYMLINK_FLAG_RELATIVE 0x00000001
-
/* For IO_REPARSE_TAG_NFS */
#define NFS_SPECFILE_LNK 0x00000000014B4E4C
#define NFS_SPECFILE_CHR 0x0000000000524843
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index d26f9bbb5382..223e5e231f42 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -474,9 +474,6 @@ extern int cifs_query_reparse_point(const unsigned int xid,
const char *full_path,
u32 *tag, struct kvec *rsp,
int *rsp_buftype);
-extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
- __u16 fid, char **symlinkinfo,
- const struct nls_table *nls_codepage);
extern int CIFSSMB_set_compression(const unsigned int xid,
struct cifs_tcon *tcon, __u16 fid);
extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms,
@@ -680,7 +677,7 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
}
int match_target_ip(struct TCP_Server_Info *server,
- const char *share, size_t share_len,
+ const char *host, size_t hostlen,
bool *result);
int cifs_inval_name_dfs_link_error(const unsigned int xid,
struct cifs_tcon *tcon,
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index eaa6be4456d0..880d7cf8b730 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -72,10 +72,8 @@ static void cifs_prune_tlinks(struct work_struct *work);
*/
static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
{
- int rc;
- int len;
- char *unc;
struct sockaddr_storage ss;
+ int rc;
if (!server->hostname)
return -EINVAL;
@@ -84,32 +82,18 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
if (server->hostname[0] == '\0')
return 0;
- len = strlen(server->hostname) + 3;
-
- unc = kmalloc(len, GFP_KERNEL);
- if (!unc) {
- cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
- return -ENOMEM;
- }
- scnprintf(unc, len, "\\\\%s", server->hostname);
-
spin_lock(&server->srv_lock);
ss = server->dstaddr;
spin_unlock(&server->srv_lock);
- rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
- kfree(unc);
-
- if (rc < 0) {
- cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
- __func__, server->hostname, rc);
- } else {
+ rc = dns_resolve_name(server->dns_dom, server->hostname,
+ strlen(server->hostname),
+ (struct sockaddr *)&ss);
+ if (!rc) {
spin_lock(&server->srv_lock);
memcpy(&server->dstaddr, &ss, sizeof(server->dstaddr));
spin_unlock(&server->srv_lock);
- rc = 0;
}
-
return rc;
}
@@ -438,7 +422,8 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
}
#ifdef CONFIG_CIFS_DFS_UPCALL
-static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target)
+static int __reconnect_target_locked(struct TCP_Server_Info *server,
+ const char *target)
{
int rc;
char *hostname;
@@ -471,34 +456,43 @@ static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const cha
return rc;
}
-static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl,
- struct dfs_cache_tgt_iterator **target_hint)
+static int reconnect_target_locked(struct TCP_Server_Info *server,
+ struct dfs_cache_tgt_list *tl,
+ struct dfs_cache_tgt_iterator **target_hint)
{
- int rc;
struct dfs_cache_tgt_iterator *tit;
+ int rc;
*target_hint = NULL;
/* If dfs target list is empty, then reconnect to last server */
tit = dfs_cache_get_tgt_iterator(tl);
if (!tit)
- return __reconnect_target_unlocked(server, server->hostname);
+ return __reconnect_target_locked(server, server->hostname);
/* Otherwise, try every dfs target in @tl */
- for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
- rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit));
+ do {
+ const char *target = dfs_cache_get_tgt_name(tit);
+
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus != CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ return -ECONNRESET;
+ }
+ spin_unlock(&server->srv_lock);
+ rc = __reconnect_target_locked(server, target);
if (!rc) {
*target_hint = tit;
break;
}
- }
+ } while ((tit = dfs_cache_get_next_tgt(tl, tit)));
return rc;
}
static int reconnect_dfs_server(struct TCP_Server_Info *server)
{
struct dfs_cache_tgt_iterator *target_hint = NULL;
-
+ const char *ref_path = server->leaf_fullpath + 1;
DFS_CACHE_TGT_LIST(tl);
int num_targets = 0;
int rc = 0;
@@ -511,10 +505,8 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
* through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
* refreshing the referral, so, in this case, default it to 1.
*/
- mutex_lock(&server->refpath_lock);
- if (!dfs_cache_noreq_find(server->leaf_fullpath + 1, NULL, &tl))
+ if (!dfs_cache_noreq_find(ref_path, NULL, &tl))
num_targets = dfs_cache_get_nr_tgts(&tl);
- mutex_unlock(&server->refpath_lock);
if (!num_targets)
num_targets = 1;
@@ -534,7 +526,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
try_to_freeze();
cifs_server_lock(server);
- rc = reconnect_target_unlocked(server, &tl, &target_hint);
+ rc = reconnect_target_locked(server, &tl, &target_hint);
if (rc) {
/* Failed to reconnect socket */
cifs_server_unlock(server);
@@ -558,9 +550,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
} while (server->tcpStatus == CifsNeedReconnect);
- mutex_lock(&server->refpath_lock);
- dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, target_hint);
- mutex_unlock(&server->refpath_lock);
+ dfs_cache_noreq_update_tgthint(ref_path, target_hint);
dfs_cache_free_tgts(&tl);
/* Need to set up echo worker again once connection has been established */
@@ -575,13 +565,8 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{
- mutex_lock(&server->refpath_lock);
- if (!server->leaf_fullpath) {
- mutex_unlock(&server->refpath_lock);
+ if (!server->leaf_fullpath)
return __cifs_reconnect(server, mark_smb_session);
- }
- mutex_unlock(&server->refpath_lock);
-
return reconnect_dfs_server(server);
}
#else
@@ -1541,42 +1526,10 @@ static int match_server(struct TCP_Server_Info *server,
if (!cifs_match_ipaddr((struct sockaddr *)&ctx->srcaddr,
(struct sockaddr *)&server->srcaddr))
return 0;
- /*
- * When matching cifs.ko superblocks (@match_super == true), we can't
- * really match either @server->leaf_fullpath or @server->dstaddr
- * directly since this @server might belong to a completely different
- * server -- in case of domain-based DFS referrals or DFS links -- as
- * provided earlier by mount(2) through 'source' and 'ip' options.
- *
- * Otherwise, match the DFS referral in @server->leaf_fullpath or the
- * destination address in @server->dstaddr.
- *
- * When using 'nodfs' mount option, we avoid sharing it with DFS
- * connections as they might failover.
- */
- if (!match_super) {
- if (!ctx->nodfs) {
- if (server->leaf_fullpath) {
- if (!ctx->leaf_fullpath ||
- strcasecmp(server->leaf_fullpath,
- ctx->leaf_fullpath))
- return 0;
- } else if (ctx->leaf_fullpath) {
- return 0;
- }
- } else if (server->leaf_fullpath) {
- return 0;
- }
- }
- /*
- * Match for a regular connection (address/hostname/port) which has no
- * DFS referrals set.
- */
- if (!server->leaf_fullpath &&
- (strcasecmp(server->hostname, ctx->server_hostname) ||
- !match_server_address(server, addr) ||
- !match_port(server, addr)))
+ if (strcasecmp(server->hostname, ctx->server_hostname) ||
+ !match_server_address(server, addr) ||
+ !match_port(server, addr))
return 0;
if (!match_security(server, ctx))
@@ -1710,6 +1663,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
goto out_err;
}
}
+ if (ctx->dns_dom)
+ strscpy(tcp_ses->dns_dom, ctx->dns_dom);
if (ctx->nosharesock)
tcp_ses->nosharesock = true;
@@ -1758,9 +1713,6 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
mutex_init(&tcp_ses->reconnect_mutex);
-#ifdef CONFIG_CIFS_DFS_UPCALL
- mutex_init(&tcp_ses->refpath_lock);
-#endif
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
@@ -2276,12 +2228,13 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
- int rc = 0;
- int retries = 0;
- unsigned int xid;
- struct cifs_ses *ses;
- struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
+ struct cifs_ses *ses;
+ unsigned int xid;
+ int retries = 0;
+ size_t len;
+ int rc = 0;
xid = get_xid();
@@ -2371,6 +2324,14 @@ retry_old_session:
ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
if (!ses->domainName)
goto get_ses_fail;
+
+ len = strnlen(ctx->domainname, CIFS_MAX_DOMAINNAME_LEN);
+ if (!cifs_netbios_name(ctx->domainname, len)) {
+ ses->dns_dom = kstrndup(ctx->domainname,
+ len, GFP_KERNEL);
+ if (!ses->dns_dom)
+ goto get_ses_fail;
+ }
}
strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name));
diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
index 4647df9e1e3b..dad521336b5e 100644
--- a/fs/smb/client/dfs.c
+++ b/fs/smb/client/dfs.c
@@ -9,6 +9,8 @@
#include "fs_context.h"
#include "dfs.h"
+#define DFS_DOM(ctx) (ctx->dfs_root_ses ? ctx->dfs_root_ses->dns_dom : NULL)
+
/**
* dfs_parse_target_referral - set fs context for dfs target referral
*
@@ -46,8 +48,8 @@ int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_para
if (rc)
goto out;
- rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL);
-
+ rc = dns_resolve_unc(DFS_DOM(ctx), path,
+ (struct sockaddr *)&ctx->dstaddr);
out:
kfree(path);
return rc;
@@ -59,8 +61,9 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
int rc;
ctx->leaf_fullpath = (char *)full_path;
+ ctx->dns_dom = DFS_DOM(ctx);
rc = cifs_mount_get_session(mnt_ctx);
- ctx->leaf_fullpath = NULL;
+ ctx->leaf_fullpath = ctx->dns_dom = NULL;
return rc;
}
@@ -95,15 +98,16 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx,
return rc;
}
-static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
- struct dfs_info3_param *tgt,
- struct dfs_ref_walk *rw)
+static int setup_dfs_ref(struct dfs_info3_param *tgt, struct dfs_ref_walk *rw)
{
- struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
- struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+ struct cifs_sb_info *cifs_sb = rw->mnt_ctx->cifs_sb;
+ struct smb3_fs_context *ctx = rw->mnt_ctx->fs_ctx;
char *ref_path, *full_path;
int rc;
+ set_root_smb_session(rw->mnt_ctx);
+ ref_walk_ses(rw) = ctx->dfs_root_ses;
+
full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb));
if (IS_ERR(full_path))
return PTR_ERR(full_path);
@@ -120,35 +124,22 @@ static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
}
ref_walk_path(rw) = ref_path;
ref_walk_fpath(rw) = full_path;
- ref_walk_ses(rw) = ctx->dfs_root_ses;
- return 0;
+
+ return dfs_get_referral(rw->mnt_ctx,
+ ref_walk_path(rw) + 1,
+ ref_walk_tl(rw));
}
-static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
- struct dfs_ref_walk *rw)
+static int __dfs_referral_walk(struct dfs_ref_walk *rw)
{
- struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+ struct smb3_fs_context *ctx = rw->mnt_ctx->fs_ctx;
+ struct cifs_mount_ctx *mnt_ctx = rw->mnt_ctx;
struct dfs_info3_param tgt = {};
int rc = -ENOENT;
again:
do {
ctx->dfs_root_ses = ref_walk_ses(rw);
- if (ref_walk_empty(rw)) {
- rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
- NULL, ref_walk_tl(rw));
- if (rc) {
- rc = cifs_mount_get_tcon(mnt_ctx);
- if (!rc)
- rc = cifs_is_path_remote(mnt_ctx);
- continue;
- }
- if (!ref_walk_num_tgts(rw)) {
- rc = -ENOENT;
- continue;
- }
- }
-
while (ref_walk_next_tgt(rw)) {
rc = parse_dfs_target(ctx, rw, &tgt);
if (rc)
@@ -159,32 +150,29 @@ again:
if (rc)
continue;
- ref_walk_set_tgt_hint(rw);
if (tgt.flags & DFSREF_STORAGE_SERVER) {
rc = cifs_mount_get_tcon(mnt_ctx);
if (!rc)
rc = cifs_is_path_remote(mnt_ctx);
- if (!rc)
+ if (!rc) {
+ ref_walk_set_tgt_hint(rw);
break;
+ }
if (rc != -EREMOTE)
continue;
}
- set_root_smb_session(mnt_ctx);
rc = ref_walk_advance(rw);
if (!rc) {
- rc = setup_dfs_ref(mnt_ctx, &tgt, rw);
- if (!rc) {
- rc = -EREMOTE;
- goto again;
- }
+ rc = setup_dfs_ref(&tgt, rw);
+ if (rc)
+ break;
+ ref_walk_mark_end(rw);
+ goto again;
}
- if (rc != -ELOOP)
- goto out;
}
} while (rc && ref_walk_descend(rw));
-out:
free_dfs_info_param(&tgt);
return rc;
}
@@ -201,10 +189,10 @@ static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
return rc;
}
- ref_walk_init(*rw);
- rc = setup_dfs_ref(mnt_ctx, NULL, *rw);
+ ref_walk_init(*rw, mnt_ctx);
+ rc = setup_dfs_ref(NULL, *rw);
if (!rc)
- rc = __dfs_referral_walk(mnt_ctx, *rw);
+ rc = __dfs_referral_walk(*rw);
return rc;
}
@@ -264,7 +252,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
int rc = 0;
if (!ctx->nodfs && ctx->dfs_automount) {
- rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
+ rc = dns_resolve_unc(NULL, ctx->source, addr);
if (!rc)
cifs_set_port(addr, ctx->port);
ctx->dfs_automount = false;
@@ -294,7 +282,7 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
* to respond with PATH_NOT_COVERED to requests that include the prefix.
*/
if (!nodfs) {
- rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);
+ rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL);
if (rc) {
cifs_dbg(FYI, "%s: no dfs referral for %s: %d\n",
__func__, ctx->UNC + 1, rc);
@@ -314,10 +302,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
cifs_mount_put_conns(mnt_ctx);
rc = get_session(mnt_ctx, NULL);
}
- if (!rc) {
- set_root_smb_session(mnt_ctx);
+ if (!rc)
rc = __dfs_mount_share(mnt_ctx);
- }
return rc;
}
diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h
index 1aa2bc65b3bc..ed4cd7cf1ec6 100644
--- a/fs/smb/client/dfs.h
+++ b/fs/smb/client/dfs.h
@@ -12,6 +12,7 @@
#include "dfs_cache.h"
#include "cifs_unicode.h"
#include <linux/namei.h>
+#include <linux/errno.h>
#define DFS_INTERLINK(v) \
(((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER))
@@ -25,8 +26,9 @@ struct dfs_ref {
};
struct dfs_ref_walk {
- struct dfs_ref *ref;
- struct dfs_ref refs[MAX_NESTED_LINKS];
+ struct cifs_mount_ctx *mnt_ctx;
+ struct dfs_ref *ref;
+ struct dfs_ref refs[MAX_NESTED_LINKS];
};
#define ref_walk_start(w) ((w)->refs)
@@ -35,7 +37,6 @@ struct dfs_ref_walk {
#define ref_walk_descend(w) (--ref_walk_cur(w) >= ref_walk_start(w))
#define ref_walk_tit(w) (ref_walk_cur(w)->tit)
-#define ref_walk_empty(w) (!ref_walk_tit(w))
#define ref_walk_path(w) (ref_walk_cur(w)->path)
#define ref_walk_fpath(w) (ref_walk_cur(w)->full_path)
#define ref_walk_tl(w) (&ref_walk_cur(w)->tl)
@@ -51,9 +52,11 @@ static inline struct dfs_ref_walk *ref_walk_alloc(void)
return rw;
}
-static inline void ref_walk_init(struct dfs_ref_walk *rw)
+static inline void ref_walk_init(struct dfs_ref_walk *rw,
+ struct cifs_mount_ctx *mnt_ctx)
{
memset(rw, 0, sizeof(*rw));
+ rw->mnt_ctx = mnt_ctx;
ref_walk_cur(rw) = ref_walk_start(rw);
}
@@ -93,15 +96,23 @@ static inline int ref_walk_advance(struct dfs_ref_walk *rw)
static inline struct dfs_cache_tgt_iterator *
ref_walk_next_tgt(struct dfs_ref_walk *rw)
{
- struct dfs_cache_tgt_iterator *tit;
struct dfs_ref *ref = ref_walk_cur(rw);
+ struct dfs_cache_tgt_iterator *tit;
+
+ if (IS_ERR(ref->tit))
+ return NULL;
if (!ref->tit)
tit = dfs_cache_get_tgt_iterator(&ref->tl);
else
tit = dfs_cache_get_next_tgt(&ref->tl, ref->tit);
+
+ if (!tit) {
+ ref->tit = ERR_PTR(-ENOENT);
+ return NULL;
+ }
ref->tit = tit;
- return tit;
+ return ref->tit;
}
static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,
@@ -112,11 +123,6 @@ static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,
ref_walk_tit(rw), tgt);
}
-static inline int ref_walk_num_tgts(struct dfs_ref_walk *rw)
-{
- return dfs_cache_get_nr_tgts(ref_walk_tl(rw));
-}
-
static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)
{
dfs_cache_noreq_update_tgthint(ref_walk_path(rw) + 1,
@@ -136,6 +142,15 @@ static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw,
}
}
+static inline void ref_walk_mark_end(struct dfs_ref_walk *rw)
+{
+ struct dfs_ref *ref = ref_walk_cur(rw) - 1;
+
+ WARN_ON_ONCE(ref < ref_walk_start(rw));
+ dfs_cache_noreq_update_tgthint(ref->path + 1, ref->tit);
+ ref->tit = ERR_PTR(-ENOENT); /* end marker */
+}
+
int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
struct smb3_fs_context *ctx);
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx);
@@ -145,15 +160,16 @@ static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path)
return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));
}
-static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *path,
- struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tl)
+static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx,
+ const char *path,
+ struct dfs_cache_tgt_list *tl)
{
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses;
return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls,
- cifs_remap(cifs_sb), path, ref, tl);
+ cifs_remap(cifs_sb), path, NULL, tl);
}
/*
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 541608b0267e..5022bb1f122a 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -1096,11 +1096,8 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
{
struct TCP_Server_Info *server = tcon->ses->server;
- struct sockaddr_storage ss;
- const char *host;
const char *s2 = &tcon->tree_name[1];
- size_t hostlen;
- char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0};
+ struct sockaddr_storage ss;
bool match;
int rc;
@@ -1111,18 +1108,13 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
* Resolve share's hostname and check if server address matches. Otherwise just ignore it
* as we could not have upcall to resolve hostname or failed to convert ip address.
*/
- extract_unc_hostname(s1, &host, &hostlen);
- scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host);
-
- rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n",
- __func__, (int)hostlen, host);
+ rc = dns_resolve_unc(server->dns_dom, s1, (struct sockaddr *)&ss);
+ if (rc < 0)
return true;
- }
cifs_server_lock(server);
match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
+ cifs_dbg(FYI, "%s: [share=%s] ipaddr matched: %s\n", __func__, s1, str_yes_no(match));
cifs_server_unlock(server);
return match;
@@ -1149,13 +1141,11 @@ static char *get_ses_refpath(struct cifs_ses *ses)
struct TCP_Server_Info *server = ses->server;
char *path = ERR_PTR(-ENOENT);
- mutex_lock(&server->refpath_lock);
if (server->leaf_fullpath) {
- path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC);
+ path = kstrdup(server->leaf_fullpath + 1, GFP_KERNEL);
if (!path)
path = ERR_PTR(-ENOMEM);
}
- mutex_unlock(&server->refpath_lock);
return path;
}
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index 864b194dbaa0..1822493dd084 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -627,7 +627,7 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
goto mknod_out;
}
- trace_smb3_mknod_enter(xid, tcon->ses->Suid, tcon->tid, full_path);
+ trace_smb3_mknod_enter(xid, tcon->tid, tcon->ses->Suid, full_path);
rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
full_path, mode,
@@ -635,9 +635,9 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
mknod_out:
if (rc)
- trace_smb3_mknod_err(xid, tcon->ses->Suid, tcon->tid, rc);
+ trace_smb3_mknod_err(xid, tcon->tid, tcon->ses->Suid, rc);
else
- trace_smb3_mknod_done(xid, tcon->ses->Suid, tcon->tid);
+ trace_smb3_mknod_done(xid, tcon->tid, tcon->ses->Suid);
free_dentry_path(page);
free_xid(xid);
diff --git a/fs/smb/client/dns_resolve.c b/fs/smb/client/dns_resolve.c
index 8bf8978bc5d6..de7f4b384718 100644
--- a/fs/smb/client/dns_resolve.c
+++ b/fs/smb/client/dns_resolve.c
@@ -20,69 +20,77 @@
#include "cifsproto.h"
#include "cifs_debug.h"
-/**
- * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
- * @unc: UNC path specifying the server (with '/' as delimiter)
- * @ip_addr: Where to return the IP address.
- * @expiry: Where to return the expiry time for the dns record.
- *
- * Returns zero success, -ve on error.
- */
-int
-dns_resolve_server_name_to_ip(const char *unc, struct sockaddr *ip_addr, time64_t *expiry)
+static int resolve_name(const char *name, size_t namelen, struct sockaddr *addr)
{
- const char *hostname, *sep;
char *ip;
- int len, rc;
+ int rc;
- if (!ip_addr || !unc)
- return -EINVAL;
+ rc = dns_query(current->nsproxy->net_ns, NULL, name,
+ namelen, NULL, &ip, NULL, false);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
+ __func__, (int)namelen, (int)namelen, name);
+ } else {
+ cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
+ __func__, (int)namelen, (int)namelen, name, ip);
- len = strlen(unc);
- if (len < 3) {
- cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc);
- return -EINVAL;
+ rc = cifs_convert_address(addr, ip, strlen(ip));
+ kfree(ip);
+ if (!rc) {
+ cifs_dbg(FYI, "%s: unable to determine ip address\n",
+ __func__);
+ rc = -EHOSTUNREACH;
+ } else {
+ rc = 0;
+ }
}
+ return rc;
+}
- /* Discount leading slashes for cifs */
- len -= 2;
- hostname = unc + 2;
+/**
+ * dns_resolve_name - Perform an upcall to resolve hostname to an ip address.
+ * @dom: DNS domain name (or NULL)
+ * @name: Name to look up
+ * @namelen: Length of name
+ * @ip_addr: Where to return the IP address
+ *
+ * Returns zero on success, -ve code otherwise.
+ */
+int dns_resolve_name(const char *dom, const char *name,
+ size_t namelen, struct sockaddr *ip_addr)
+{
+ size_t len;
+ char *s;
+ int rc;
- /* Search for server name delimiter */
- sep = memchr(hostname, '/', len);
- if (sep)
- len = sep - hostname;
- else
- cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n",
- __func__, unc);
+ cifs_dbg(FYI, "%s: dom=%s name=%.*s\n", __func__, dom, (int)namelen, name);
+ if (!ip_addr || !name || !*name || !namelen)
+ return -EINVAL;
+ cifs_dbg(FYI, "%s: hostname=%.*s\n", __func__, (int)namelen, name);
/* Try to interpret hostname as an IPv4 or IPv6 address */
- rc = cifs_convert_address(ip_addr, hostname, len);
+ rc = cifs_convert_address(ip_addr, name, namelen);
if (rc > 0) {
- cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n", __func__, len, len,
- hostname);
+ cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n",
+ __func__, (int)namelen, (int)namelen, name);
return 0;
}
- /* Perform the upcall */
- rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
- NULL, &ip, expiry, false);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
- __func__, len, len, hostname);
- } else {
- cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
- __func__, len, len, hostname, ip,
- expiry ? (*expiry) : 0);
-
- rc = cifs_convert_address(ip_addr, ip, strlen(ip));
- kfree(ip);
+ /*
+ * If @name contains a NetBIOS name and @dom has been specified, then
+ * convert @name to an FQDN and try resolving it first.
+ */
+ if (dom && *dom && cifs_netbios_name(name, namelen)) {
+ len = strnlen(dom, CIFS_MAX_DOMAINNAME_LEN) + namelen + 2;
+ s = kmalloc(len, GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
- if (!rc) {
- cifs_dbg(FYI, "%s: unable to determine ip address\n", __func__);
- rc = -EHOSTUNREACH;
- } else
- rc = 0;
+ scnprintf(s, len, "%.*s.%s", (int)namelen, name, dom);
+ rc = resolve_name(s, len - 1, ip_addr);
+ kfree(s);
+ if (!rc)
+ return 0;
}
- return rc;
+ return resolve_name(name, namelen, ip_addr);
}
diff --git a/fs/smb/client/dns_resolve.h b/fs/smb/client/dns_resolve.h
index 6eb0c15a2440..0dc706f2c422 100644
--- a/fs/smb/client/dns_resolve.h
+++ b/fs/smb/client/dns_resolve.h
@@ -12,9 +12,30 @@
#define _DNS_RESOLVE_H
#include <linux/net.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
#ifdef __KERNEL__
-int dns_resolve_server_name_to_ip(const char *unc, struct sockaddr *ip_addr, time64_t *expiry);
+
+int dns_resolve_name(const char *dom, const char *name,
+ size_t namelen, struct sockaddr *ip_addr);
+
+static inline int dns_resolve_unc(const char *dom, const char *unc,
+ struct sockaddr *ip_addr)
+{
+ const char *name;
+ size_t namelen;
+
+ if (!unc || strlen(unc) < 3)
+ return -EINVAL;
+
+ extract_unc_hostname(unc, &name, &namelen);
+ if (!namelen)
+ return -EINVAL;
+
+ return dns_resolve_name(dom, name, namelen, ip_addr);
+}
+
#endif /* KERNEL */
#endif /* _DNS_RESOLVE_H */
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 49123f458d0c..5381f05420bc 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -385,6 +385,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
new_ctx->source = NULL;
new_ctx->iocharset = NULL;
new_ctx->leaf_fullpath = NULL;
+ new_ctx->dns_dom = NULL;
/*
* Make sure to stay in sync with smb3_cleanup_fs_context_contents()
*/
@@ -399,6 +400,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
DUP_CTX_STR(nodename);
DUP_CTX_STR(iocharset);
DUP_CTX_STR(leaf_fullpath);
+ DUP_CTX_STR(dns_dom);
return 0;
}
@@ -1863,6 +1865,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
ctx->prepath = NULL;
kfree(ctx->leaf_fullpath);
ctx->leaf_fullpath = NULL;
+ kfree(ctx->dns_dom);
+ ctx->dns_dom = NULL;
}
void
diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
index ac6baa774ad3..8813533345ee 100644
--- a/fs/smb/client/fs_context.h
+++ b/fs/smb/client/fs_context.h
@@ -295,6 +295,7 @@ struct smb3_fs_context {
bool dfs_automount:1; /* set for dfs automount only */
enum cifs_reparse_type reparse_type;
bool dfs_conn:1; /* set for dfs mounts */
+ char *dns_dom;
};
extern const struct fs_parameter_spec smb3_fs_parameters[];
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index f146e06c97eb..93e9188b2632 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -2392,6 +2392,13 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
if (to_dentry->d_parent != from_dentry->d_parent)
goto do_rename_exit;
+ /*
+ * CIFSSMBRenameOpenFile() uses SMB_SET_FILE_RENAME_INFORMATION
+ * which is SMB PASSTHROUGH level.
+ */
+ if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU))
+ goto do_rename_exit;
+
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 4373dd64b66d..b328dc5c7988 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -101,6 +101,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
kfree_sensitive(buf_to_free->password2);
kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
+ kfree(buf_to_free->dns_dom);
kfree_sensitive(buf_to_free->auth_key.response);
spin_lock(&buf_to_free->iface_lock);
list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list,
@@ -908,9 +909,9 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
*num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
if (*num_of_nodes < 1) {
- cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n",
- *num_of_nodes);
- rc = -EINVAL;
+ cifs_dbg(VFS | ONCE, "%s: [path=%s] num_referrals must be at least > 0, but we got %d\n",
+ __func__, searchName, *num_of_nodes);
+ rc = -ENOENT;
goto parse_DFS_referrals_exit;
}
@@ -1171,33 +1172,25 @@ void cifs_put_tcp_super(struct super_block *sb)
#ifdef CONFIG_CIFS_DFS_UPCALL
int match_target_ip(struct TCP_Server_Info *server,
- const char *share, size_t share_len,
+ const char *host, size_t hostlen,
bool *result)
{
- int rc;
- char *target;
struct sockaddr_storage ss;
+ int rc;
- *result = false;
-
- target = kzalloc(share_len + 3, GFP_KERNEL);
- if (!target)
- return -ENOMEM;
-
- scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share);
-
- cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
+ cifs_dbg(FYI, "%s: hostname=%.*s\n", __func__, (int)hostlen, host);
- rc = dns_resolve_server_name_to_ip(target, (struct sockaddr *)&ss, NULL);
- kfree(target);
+ *result = false;
+ rc = dns_resolve_name(server->dns_dom, host, hostlen,
+ (struct sockaddr *)&ss);
if (rc < 0)
return rc;
spin_lock(&server->srv_lock);
*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
spin_unlock(&server->srv_lock);
- cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
+ cifs_dbg(FYI, "%s: ip addresses matched: %s\n", __func__, str_yes_no(*result));
return 0;
}
diff --git a/fs/smb/client/netmisc.c b/fs/smb/client/netmisc.c
index 2a8d71221e5e..17b3e21ea868 100644
--- a/fs/smb/client/netmisc.c
+++ b/fs/smb/client/netmisc.c
@@ -775,10 +775,10 @@ cifs_print_status(__u32 status_code)
int idx = 0;
while (nt_errs[idx].nt_errstr != NULL) {
- if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) ==
- (status_code & 0xFFFFFF)) {
+ if (nt_errs[idx].nt_errcode == status_code) {
pr_notice("Status code returned 0x%08x %s\n",
status_code, nt_errs[idx].nt_errstr);
+ return;
}
idx++;
}
diff --git a/fs/smb/client/nterr.c b/fs/smb/client/nterr.c
index 358a766375b4..d396a8e98a81 100644
--- a/fs/smb/client/nterr.c
+++ b/fs/smb/client/nterr.c
@@ -13,6 +13,13 @@
const struct nt_err_code_struct nt_errs[] = {
{"NT_STATUS_OK", NT_STATUS_OK},
+ {"NT_STATUS_MEDIA_CHANGED", NT_STATUS_MEDIA_CHANGED},
+ {"NT_STATUS_END_OF_MEDIA", NT_STATUS_END_OF_MEDIA},
+ {"NT_STATUS_MEDIA_CHECK", NT_STATUS_MEDIA_CHECK},
+ {"NT_STATUS_NO_DATA_DETECTED", NT_STATUS_NO_DATA_DETECTED},
+ {"NT_STATUS_STOPPED_ON_SYMLINK", NT_STATUS_STOPPED_ON_SYMLINK},
+ {"NT_STATUS_DEVICE_REQUIRES_CLEANING", NT_STATUS_DEVICE_REQUIRES_CLEANING},
+ {"NT_STATUS_DEVICE_DOOR_OPEN", NT_STATUS_DEVICE_DOOR_OPEN},
{"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL},
{"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED},
{"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS},
@@ -670,5 +677,6 @@ const struct nt_err_code_struct nt_errs[] = {
{"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
{"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES},
{"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED},
+ {"NT_STATUS_NO_SUCH_JOB", NT_STATUS_NO_SUCH_JOB},
{NULL, 0}
};
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index 273358d20a46..50f96259d9ad 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -413,7 +413,7 @@ ffirst_retry:
cifsFile->invalidHandle = false;
} else if ((rc == -EOPNOTSUPP) &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
- cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
+ cifs_autodisable_serverino(cifs_sb);
goto ffirst_retry;
}
error_exit:
diff --git a/fs/smb/client/rfc1002pdu.h b/fs/smb/client/rfc1002pdu.h
index ae1d025da294..ac82c2f3a4a2 100644
--- a/fs/smb/client/rfc1002pdu.h
+++ b/fs/smb/client/rfc1002pdu.h
@@ -24,7 +24,7 @@
struct rfc1002_session_packet {
__u8 type;
__u8 flags;
- __u16 length;
+ __be16 length;
union {
struct {
__u8 called_len;
@@ -35,8 +35,8 @@ struct rfc1002_session_packet {
__u8 scope2; /* null */
} __attribute__((packed)) session_req;
struct {
- __u32 retarget_ip_addr;
- __u16 port;
+ __be32 retarget_ip_addr;
+ __be16 port;
} __attribute__((packed)) retarget_resp;
__u8 neg_ses_resp_error_code;
/* POSITIVE_SESSION_RESPONSE packet does not include trailer.
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index db3695eddcf9..749a83cd0deb 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -614,7 +614,13 @@ static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
* There may be higher info levels that work but are there Windows
* server or network appliances for which IndexNumber field is not
* guaranteed unique?
+ *
+ * CIFSGetSrvInodeNumber() uses SMB_QUERY_FILE_INTERNAL_INFO
+ * which is SMB PASSTHROUGH level therefore check for capability.
+ * Note that this function can be called with tcon == NULL.
*/
+ if (tcon && !(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU))
+ return -EOPNOTSUPP;
return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid,
cifs_sb->local_nls,
cifs_remap(cifs_sb));
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index e836bc2193dd..9ec44eab8dbc 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -42,14 +42,14 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
do {
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
- sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData;
+ sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
break;
}
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
- p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len);
+ p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
} while (p < end);
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index a55f0044d30b..c97f14757c27 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -176,27 +176,27 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
{
- struct reparse_data_buffer *rbuf;
+ struct smb2_query_info_rsp *qi_rsp = NULL;
struct smb2_compound_vars *vars = NULL;
- struct kvec *rsp_iov, *iov;
- struct smb_rqst *rqst;
- int rc;
- __le16 *utf16_path = NULL;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
- struct cifs_fid fid;
+ struct cifs_open_info_data *idata;
struct cifs_ses *ses = tcon->ses;
+ struct reparse_data_buffer *rbuf;
struct TCP_Server_Info *server;
- int num_rqst = 0, i;
int resp_buftype[MAX_COMPOUND];
- struct smb2_query_info_rsp *qi_rsp = NULL;
- struct cifs_open_info_data *idata;
+ int retries = 0, cur_sleep = 1;
+ __u8 delete_pending[8] = {1,};
+ struct kvec *rsp_iov, *iov;
struct inode *inode = NULL;
- int flags = 0;
- __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
+ __le16 *utf16_path = NULL;
+ struct smb_rqst *rqst;
unsigned int size[2];
- void *data[2];
+ struct cifs_fid fid;
+ int num_rqst = 0, i;
unsigned int len;
- int retries = 0, cur_sleep = 1;
+ int tmp_rc, rc;
+ int flags = 0;
+ void *data[2];
replay_again:
/* reinitialize for possible replay */
@@ -298,8 +298,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_query_info_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_query_info_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_POSIX_QUERY_INFO:
rqst[num_rqst].rq_iov = &vars->qi_iov;
@@ -334,18 +334,18 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_DELETE:
- trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_delete_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_MKDIR:
/*
* Directories are created through parameters in the
* SMB2_open() call.
*/
- trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_mkdir_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_RMDIR:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -363,7 +363,7 @@ replay_again:
goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]);
- trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_rmdir_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_EOF:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -398,7 +398,7 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_set_eof_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_INFO:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -429,8 +429,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_set_info_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_set_info_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_RENAME:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -469,7 +469,7 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_rename_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_HARDLINK:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -496,7 +496,7 @@ replay_again:
goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]);
- trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_hardlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_REPARSE:
rqst[num_rqst].rq_iov = vars->io_iov;
@@ -523,8 +523,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_set_reparse_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_GET_REPARSE:
rqst[num_rqst].rq_iov = vars->io_iov;
@@ -549,8 +549,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_get_reparse_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_QUERY_WSL_EA:
rqst[num_rqst].rq_iov = &vars->ea_iov;
@@ -584,6 +584,8 @@ replay_again:
goto finished;
}
num_rqst++;
+ trace_smb3_query_wsl_ea_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
default:
cifs_dbg(VFS, "Invalid command\n");
@@ -637,7 +639,14 @@ finished:
tcon->need_reconnect = true;
}
+ tmp_rc = rc;
for (i = 0; i < num_cmds; i++) {
+ char *buf = rsp_iov[i + i].iov_base;
+
+ if (buf && resp_buftype[i + 1] != CIFS_NO_BUFFER)
+ rc = server->ops->map_error(buf, false);
+ else
+ rc = tmp_rc;
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
idata = in_iov[i].iov_base;
@@ -656,11 +665,11 @@ finished:
}
SMB2_query_info_free(&rqst[num_rqst++]);
if (rc)
- trace_smb3_query_info_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_query_info_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
else
- trace_smb3_query_info_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_query_info_compound_done(xid, tcon->tid,
+ ses->Suid);
break;
case SMB2_OP_POSIX_QUERY_INFO:
idata = in_iov[i].iov_base;
@@ -683,15 +692,15 @@ finished:
SMB2_query_info_free(&rqst[num_rqst++]);
if (rc)
- trace_smb3_posix_query_info_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_posix_query_info_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
else
- trace_smb3_posix_query_info_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_posix_query_info_compound_done(xid, tcon->tid,
+ ses->Suid);
break;
case SMB2_OP_DELETE:
if (rc)
- trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_delete_err(xid, tcon->tid, ses->Suid, rc);
else {
/*
* If dentry (hence, inode) is NULL, lease break is going to
@@ -699,59 +708,59 @@ finished:
*/
if (inode)
cifs_mark_open_handles_for_deleted_file(inode, full_path);
- trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_delete_done(xid, tcon->tid, ses->Suid);
}
break;
case SMB2_OP_MKDIR:
if (rc)
- trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_mkdir_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_mkdir_done(xid, tcon->tid, ses->Suid);
break;
case SMB2_OP_HARDLINK:
if (rc)
- trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_hardlink_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_hardlink_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_RENAME:
if (rc)
- trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_rename_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_rename_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_RMDIR:
if (rc)
- trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_rmdir_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_rmdir_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_SET_EOF:
if (rc)
- trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_set_eof_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_set_eof_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_SET_INFO:
if (rc)
- trace_smb3_set_info_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_set_info_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
else
- trace_smb3_set_info_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_set_info_compound_done(xid, tcon->tid,
+ ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_SET_REPARSE:
if (rc) {
- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_set_reparse_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
} else {
- trace_smb3_set_reparse_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_set_reparse_compound_done(xid, tcon->tid,
+ ses->Suid);
}
SMB2_ioctl_free(&rqst[num_rqst++]);
break;
@@ -764,18 +773,18 @@ finished:
rbuf = reparse_buf_ptr(iov);
if (IS_ERR(rbuf)) {
rc = PTR_ERR(rbuf);
- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_get_reparse_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
} else {
idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
- trace_smb3_set_reparse_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_get_reparse_compound_done(xid, tcon->tid,
+ ses->Suid);
}
memset(iov, 0, sizeof(*iov));
resp_buftype[i + 1] = CIFS_NO_BUFFER;
} else {
- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_get_reparse_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
}
SMB2_ioctl_free(&rqst[num_rqst++]);
break;
@@ -792,17 +801,18 @@ finished:
}
}
if (!rc) {
- trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_query_wsl_ea_compound_done(xid, tcon->tid,
+ ses->Suid);
} else {
- trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_query_wsl_ea_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
}
SMB2_query_info_free(&rqst[num_rqst++]);
break;
}
}
SMB2_close_free(&rqst[num_rqst]);
+ rc = tmp_rc;
num_cmds += 2;
if (out_iov && out_buftype) {
@@ -858,22 +868,52 @@ static int parse_create_response(struct cifs_open_info_data *data,
return rc;
}
+/* Check only if SMB2_OP_QUERY_WSL_EA command failed in the compound chain */
+static bool ea_unsupported(int *cmds, int num_cmds,
+ struct kvec *out_iov, int *out_buftype)
+{
+ int i;
+
+ if (cmds[num_cmds - 1] != SMB2_OP_QUERY_WSL_EA)
+ return false;
+
+ for (i = 1; i < num_cmds - 1; i++) {
+ struct smb2_hdr *hdr = out_iov[i].iov_base;
+
+ if (out_buftype[i] == CIFS_NO_BUFFER || !hdr ||
+ hdr->Status != STATUS_SUCCESS)
+ return false;
+ }
+ return true;
+}
+
+static inline void free_rsp_iov(struct kvec *iovs, int *buftype, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ free_rsp_buf(buftype[i], iovs[i].iov_base);
+ memset(&iovs[i], 0, sizeof(*iovs));
+ buftype[i] = CIFS_NO_BUFFER;
+ }
+}
+
int smb2_query_path_info(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data)
{
+ struct kvec in_iov[3], out_iov[5] = {};
+ struct cached_fid *cfid = NULL;
struct cifs_open_parms oparms;
- __u32 create_options = 0;
struct cifsFileInfo *cfile;
- struct cached_fid *cfid = NULL;
+ __u32 create_options = 0;
+ int out_buftype[5] = {};
struct smb2_hdr *hdr;
- struct kvec in_iov[3], out_iov[3] = {};
- int out_buftype[3] = {};
+ int num_cmds = 0;
int cmds[3];
bool islink;
- int i, num_cmds = 0;
int rc, rc2;
data->adjust_tz = false;
@@ -943,14 +983,14 @@ int smb2_query_path_info(const unsigned int xid,
if (rc || !data->reparse_point)
goto out;
- if (!tcon->posix_extensions)
- cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
/*
* Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
* response.
*/
if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
+ if (!tcon->posix_extensions)
+ cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
FILE_READ_ATTRIBUTES |
@@ -958,9 +998,18 @@ int smb2_query_path_info(const unsigned int xid,
FILE_OPEN, create_options |
OPEN_REPARSE_POINT, ACL_NO_MODE);
cifs_get_readable_path(tcon, full_path, &cfile);
+ free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
&oparms, in_iov, cmds, num_cmds,
- cfile, NULL, NULL, NULL);
+ cfile, out_iov, out_buftype, NULL);
+ if (rc && ea_unsupported(cmds, num_cmds,
+ out_iov, out_buftype)) {
+ if (data->reparse.tag != IO_REPARSE_TAG_LX_BLK &&
+ data->reparse.tag != IO_REPARSE_TAG_LX_CHR)
+ rc = 0;
+ else
+ rc = -EOPNOTSUPP;
+ }
break;
case -EREMOTE:
break;
@@ -978,8 +1027,7 @@ int smb2_query_path_info(const unsigned int xid,
}
out:
- for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
- free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+ free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
return rc;
}
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 7121d9e0f404..d640dcabc305 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -658,7 +658,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
while (bytes_left >= (ssize_t)sizeof(*p)) {
memset(&tmp_iface, 0, sizeof(tmp_iface));
- tmp_iface.speed = le64_to_cpu(p->LinkSpeed);
+ /* default to 1Gbps when link speed is unset */
+ tmp_iface.speed = le64_to_cpu(p->LinkSpeed) ?: 1000000000;
tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
@@ -3007,9 +3008,9 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
num_of_nodes, target_nodes,
nls_codepage, remap, search_name,
true /* is_unicode */);
- if (rc) {
- cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc);
- goto out;
+ if (rc && rc != -ENOENT) {
+ cifs_tcon_dbg(VFS, "%s: failed to parse DFS referral %s: %d\n",
+ __func__, search_name, rc);
}
out:
diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
index 076d9e83e1a0..3c09a58dfd07 100644
--- a/fs/smb/client/smb2pdu.h
+++ b/fs/smb/client/smb2pdu.h
@@ -79,7 +79,7 @@ struct smb2_symlink_err_rsp {
struct smb2_error_context_rsp {
__le32 ErrorDataLength;
__le32 ErrorId;
- __u8 ErrorContextData; /* ErrorDataLength long array */
+ __u8 ErrorContextData[] __counted_by_le(ErrorDataLength);
} __packed;
/* ErrorId values */
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 12cbd3428a6d..52bcb55d9952 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -674,6 +674,7 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter);
+DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_wsl_ea_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);