cifs: reduce number of referral requests in DFS link lookups

When looking up the DFS cache with a referral path that has more than
two path components, and is a complete prefix of an existing cache
entry, do not request another referral and just return the matched
entry as specified in MS-DFSC 3.2.5.5 Receiving a Root Referral
Request or Link Referral Request.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Paulo Alcantara 2020-07-21 09:36:39 -03:00 committed by Steve French
parent 565674d613
commit 2e5de42445

View File

@ -490,16 +490,7 @@ static int add_cache_entry(const char *path, unsigned int hash,
return 0;
}
/*
* Find a DFS cache entry in hash table and optionally check prefix path against
* @path.
* Use whole path components in the match.
* Must be called with htable_rw_lock held.
*
* Return ERR_PTR(-ENOENT) if the entry is not found.
*/
static struct cache_entry *lookup_cache_entry(const char *path,
unsigned int *hash)
static struct cache_entry *__lookup_cache_entry(const char *path)
{
struct cache_entry *ce;
unsigned int h;
@ -517,9 +508,75 @@ static struct cache_entry *lookup_cache_entry(const char *path,
if (!found)
ce = ERR_PTR(-ENOENT);
return ce;
}
/*
* Find a DFS cache entry in hash table and optionally check prefix path against
* @path.
* Use whole path components in the match.
* Must be called with htable_rw_lock held.
*
* Return ERR_PTR(-ENOENT) if the entry is not found.
*/
static struct cache_entry *lookup_cache_entry(const char *path, unsigned int *hash)
{
struct cache_entry *ce = ERR_PTR(-ENOENT);
unsigned int h;
int cnt = 0;
char *npath;
char *s, *e;
char sep;
npath = kstrndup(path, strlen(path), GFP_KERNEL);
if (!npath)
return ERR_PTR(-ENOMEM);
s = npath;
sep = *npath;
while ((s = strchr(s, sep)) && ++cnt < 3)
s++;
if (cnt < 3) {
h = cache_entry_hash(path, strlen(path));
ce = __lookup_cache_entry(path);
goto out;
}
/*
* Handle paths that have more than two path components and are a complete prefix of the DFS
* referral request path (@path).
*
* See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request".
*/
h = cache_entry_hash(npath, strlen(npath));
e = npath + strlen(npath) - 1;
while (e > s) {
char tmp;
/* skip separators */
while (e > s && *e == sep)
e--;
if (e == s)
goto out;
tmp = *(e+1);
*(e+1) = 0;
ce = __lookup_cache_entry(npath);
if (!IS_ERR(ce)) {
h = cache_entry_hash(npath, strlen(npath));
break;
}
*(e+1) = tmp;
/* backward until separator */
while (e > s && *e != sep)
e--;
}
out:
if (hash)
*hash = h;
kfree(npath);
return ce;
}