twenty three ksmbd server fixes, many DFS related
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmeSiqgACgkQiiy9cAdy T1GNvQv+LARJuvBmJiUegC422l4IQi+ecYYrtAI1KU161vMSFj4oePdxvbtXbU7k sO6rRq6W6FEwKcBohF1uo/GB7FeTdOsiFm0Y9a3umtRaiJWf9gKo6x0H/Tvi+9pG ZEBMpILZytxZgbrINhbmU21LusGXBOt+5ejpGHfpgU4IPnwkvs68qfVeSmJACdvD AkugE4s9E42MR9wEz3CNtaXv5NaaxHgWQERKiSAw5wCjofj0tYzkqaaRjfOn683r sKuqSvVrAZba4/O2X3EkC++QTpSuPJQFJ7eF03zjAojtvdorMbnHH+8t0aUZxt08 6vmwk4hAS7qBKXqNZkWIk659tj0boHSBnO9zPfs6N587V0GKPtgPMDUgVvlKdW6I 6qbUNTywqgJZ8wvES2CCu1ViCXOt5hi7Mu8389POZPRwU4e9zwprbrSUaK5GBVGQ qrNZSbyooOw4ExO4mC+bQZ5DQGFgkZ9UghXgX1YqD28rFETbTib+wW60mZMjjUjG 9bpd/S4h =lTCw -----END PGP SIGNATURE----- Merge tag 'v6.14-rc-smb3-client-fixes-part' of git://git.samba.org/sfrench/cifs-2.6 Pull smb client updates from Steve French: - Fix oops in DebugData when link speed 0 - Two reparse point fixes - Ten DFS (global namespace) fixes - Symlink error handling fix - Two SMB1 fixes - Four cleanup fixes - Improved debugging of status codes - Fix incorrect output of tracepoints for compounding, and add missing compounding tracepoint * tag 'v6.14-rc-smb3-client-fixes-part' of git://git.samba.org/sfrench/cifs-2.6: (23 commits) smb: client: handle lack of EA support in smb2_query_path_info() smb: client: don't check for @leaf_fullpath in match_server() smb: client: get rid of TCP_Server_Info::refpath_lock cifs: Remove duplicate struct reparse_symlink_data and SYMLINK_FLAG_RELATIVE cifs: Do not attempt to call CIFSGetSrvInodeNumber() without CAP_INFOLEVEL_PASSTHRU cifs: Do not attempt to call CIFSSMBRenameOpenFile() without CAP_INFOLEVEL_PASSTHRU cifs: Remove declaration of dead CIFSSMBQuerySymLink function cifs: Fix printing Status code into dmesg cifs: Add missing NT_STATUS_* codes from nterr.h to nterr.c cifs: Fix endian types in struct rfc1002_session_packet cifs: Use cifs_autodisable_serverino() for disabling CIFS_MOUNT_SERVER_INUM in readdir.c smb3: add missing tracepoint for querying wsl EAs smb: client: fix order of arguments of tracepoints smb: client: fix oops due to unset link speed smb: client: correctly handle ErrorContextData as a flexible array smb: client: don't retry DFS targets on server shutdown smb: client: fix return value of parse_dfs_referrals() smb: client: optimize referral walk on failed link targets smb: client: provide dns_resolve_{unc,name} helpers smb: client: parse DNS domain name from domain= option ...
This commit is contained in:
commit
e0b1f59142
25 changed files with 495 additions and 448 deletions
|
@ -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.
|
||||
*/
|
||||
static int
|
||||
find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
|
||||
#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)
|
||||
{
|
||||
unsigned int attrsize;
|
||||
unsigned int type;
|
||||
unsigned int onesize = sizeof(struct ntlmssp2_name);
|
||||
unsigned char *blobptr;
|
||||
unsigned char *blobend;
|
||||
struct ntlmssp2_name *attrptr;
|
||||
u16 len;
|
||||
u8 *end;
|
||||
|
||||
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_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 */
|
||||
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_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
|
||||
{
|
||||
const struct nls_table *nlsc = ses->local_nls;
|
||||
struct ntlmssp2_name *av;
|
||||
u16 len, nlen;
|
||||
|
||||
if (*name)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
len = strlen(unc);
|
||||
if (len < 3) {
|
||||
cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Discount leading slashes for cifs */
|
||||
len -= 2;
|
||||
hostname = unc + 2;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Try to interpret hostname as an IPv4 or IPv6 address */
|
||||
rc = cifs_convert_address(ip_addr, hostname, len);
|
||||
if (rc > 0) {
|
||||
cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n", __func__, len, len,
|
||||
hostname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform the upcall */
|
||||
rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
|
||||
NULL, &ip, expiry, false);
|
||||
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__, len, len, hostname);
|
||||
__func__, (int)namelen, (int)namelen, name);
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
|
||||
__func__, len, len, hostname, ip,
|
||||
expiry ? (*expiry) : 0);
|
||||
cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
|
||||
__func__, (int)namelen, (int)namelen, name, ip);
|
||||
|
||||
rc = cifs_convert_address(ip_addr, ip, strlen(ip));
|
||||
rc = cifs_convert_address(addr, ip, strlen(ip));
|
||||
kfree(ip);
|
||||
|
||||
if (!rc) {
|
||||
cifs_dbg(FYI, "%s: unable to determine ip address\n", __func__);
|
||||
cifs_dbg(FYI, "%s: unable to determine ip address\n",
|
||||
__func__);
|
||||
rc = -EHOSTUNREACH;
|
||||
} else
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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, name, namelen);
|
||||
if (rc > 0) {
|
||||
cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n",
|
||||
__func__, (int)namelen, (int)namelen, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
scnprintf(s, len, "%.*s.%s", (int)namelen, name, dom);
|
||||
rc = resolve_name(s, len - 1, ip_addr);
|
||||
kfree(s);
|
||||
if (!rc)
|
||||
return 0;
|
||||
}
|
||||
return resolve_name(name, namelen, ip_addr);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
cifs_dbg(FYI, "%s: hostname=%.*s\n", __func__, (int)hostlen, host);
|
||||
|
||||
*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);
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(target, (struct sockaddr *)&ss, NULL);
|
||||
kfree(target);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_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_ses *ses = tcon->ses;
|
||||
struct TCP_Server_Info *server;
|
||||
int num_rqst = 0, i;
|
||||
int resp_buftype[MAX_COMPOUND];
|
||||
struct smb2_query_info_rsp *qi_rsp = NULL;
|
||||
struct smb2_compound_vars *vars = NULL;
|
||||
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
struct cifs_open_info_data *idata;
|
||||
struct inode *inode = NULL;
|
||||
int flags = 0;
|
||||
__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned int size[2];
|
||||
void *data[2];
|
||||
unsigned int len;
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
struct reparse_data_buffer *rbuf;
|
||||
struct TCP_Server_Info *server;
|
||||
int resp_buftype[MAX_COMPOUND];
|
||||
int retries = 0, cur_sleep = 1;
|
||||
__u8 delete_pending[8] = {1,};
|
||||
struct kvec *rsp_iov, *iov;
|
||||
struct inode *inode = NULL;
|
||||
__le16 *utf16_path = NULL;
|
||||
struct smb_rqst *rqst;
|
||||
unsigned int size[2];
|
||||
struct cifs_fid fid;
|
||||
int num_rqst = 0, i;
|
||||
unsigned int len;
|
||||
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 cifs_open_parms oparms;
|
||||
__u32 create_options = 0;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct kvec in_iov[3], out_iov[5] = {};
|
||||
struct cached_fid *cfid = NULL;
|
||||
struct cifs_open_parms oparms;
|
||||
struct cifsFileInfo *cfile;
|
||||
__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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue