cifs: reduce number of referral requests in DFS link lookups
authorPaulo Alcantara <pc@cjr.nz>
Tue, 21 Jul 2020 12:36:39 +0000 (09:36 -0300)
committerSteve French <stfrench@microsoft.com>
Sun, 2 Aug 2020 23:00:26 +0000 (18:00 -0500)
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>
fs/cifs/dfs_cache.c

index df81c71..dae2f41 100644 (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;
 }