cifs: handle RESP_GET_DFS_REFERRAL.PathConsumed in reconnect
authorPaulo Alcantara <pc@cjr.nz>
Tue, 21 Jul 2020 12:36:42 +0000 (09:36 -0300)
committerSteve French <stfrench@microsoft.com>
Sun, 2 Aug 2020 23:00:26 +0000 (18:00 -0500)
Use PathConsumed field when parsing prefixes of referral paths that
either match a cache entry or are a complete prefix path of an
existing entry.

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/cifsproto.h
fs/cifs/connect.c
fs/cifs/dfs_cache.c
fs/cifs/dfs_cache.h
fs/cifs/misc.c

index c2ab937a704804b82c03dac5e1aee77026ef0ee5..bb68cbf8107408a8f8f12d6a5e530cbe14336916 100644 (file)
@@ -617,8 +617,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
 
 struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
-int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
-                        size_t prefix_len);
+int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
index 78e6460d1e4959fa14559faca3da86b7c5256314..48b5133580d56bc442088e6fe010d575f98e4879 100644 (file)
@@ -5547,6 +5547,7 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
        size_t tcp_host_len;
        const char *dfs_host;
        size_t dfs_host_len;
+       char *share = NULL, *prefix = NULL;
 
        tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
        if (!tree)
@@ -5569,11 +5570,12 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
        extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
 
        for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
-               const char *share, *prefix;
-               size_t share_len, prefix_len;
                bool target_match;
 
-               rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix, &prefix_len);
+               kfree(share);
+               kfree(prefix);
+
+               rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix);
                if (rc) {
                        cifs_dbg(VFS, "%s: failed to parse target share %d\n",
                                 __func__, rc);
@@ -5600,13 +5602,13 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
                }
 
                if (tcon->ipc) {
-                       scnprintf(tree, MAX_TREE_SIZE, "\\\\%.*s\\IPC$", (int)share_len, share);
+                       scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share);
                        rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
                } else {
-                       scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
+                       scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
                        rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
                        if (!rc) {
-                               rc = update_super_prepath(tcon, prefix, prefix_len);
+                               rc = update_super_prepath(tcon, prefix);
                                break;
                        }
                }
@@ -5614,6 +5616,9 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
                        break;
        }
 
+       kfree(share);
+       kfree(prefix);
+
        if (!rc) {
                if (it)
                        rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it);
index dae2f41e4f213ce0d095e79e5243f492b721ea92..a44f58bbf7abfce9c38ccb93466d9721610c06eb 100644 (file)
@@ -29,6 +29,7 @@
 
 struct cache_dfs_tgt {
        char *name;
+       int path_consumed;
        struct list_head list;
 };
 
@@ -350,7 +351,7 @@ static inline struct timespec64 get_expire_time(int ttl)
 }
 
 /* Allocate a new DFS target */
-static struct cache_dfs_tgt *alloc_target(const char *name)
+static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
 {
        struct cache_dfs_tgt *t;
 
@@ -362,6 +363,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name)
                kfree(t);
                return ERR_PTR(-ENOMEM);
        }
+       t->path_consumed = path_consumed;
        INIT_LIST_HEAD(&t->list);
        return t;
 }
@@ -384,7 +386,7 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
        for (i = 0; i < numrefs; i++) {
                struct cache_dfs_tgt *t;
 
-               t = alloc_target(refs[i].node_name);
+               t = alloc_target(refs[i].node_name, refs[i].path_consumed);
                if (IS_ERR(t)) {
                        free_tgts(ce);
                        return PTR_ERR(t);
@@ -830,6 +832,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
                        rc = -ENOMEM;
                        goto err_free_it;
                }
+               it->it_path_consumed = t->path_consumed;
 
                if (ce->tgthint == t)
                        list_add(&it->it_list, head);
@@ -1320,23 +1323,26 @@ void dfs_cache_del_vol(const char *fullpath)
 /**
  * dfs_cache_get_tgt_share - parse a DFS target
  *
+ * @path: DFS full path
  * @it: DFS target iterator.
  * @share: tree name.
- * @share_len: length of tree name.
  * @prefix: prefix path.
- * @prefix_len: length of prefix path.
  *
  * Return zero if target was parsed correctly, otherwise non-zero.
  */
-int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
-                           const char **share, size_t *share_len,
-                           const char **prefix, size_t *prefix_len)
+int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
+                           char **share, char **prefix)
 {
-       char *s, sep;
+       char *s, sep, *p;
+       size_t len;
+       size_t plen1, plen2;
 
-       if (!it || !share || !share_len || !prefix || !prefix_len)
+       if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
                return -EINVAL;
 
+       *share = NULL;
+       *prefix = NULL;
+
        sep = it->it_name[0];
        if (sep != '\\' && sep != '/')
                return -EINVAL;
@@ -1345,13 +1351,38 @@ int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
        if (!s)
                return -EINVAL;
 
+       /* point to prefix in target node */
        s = strchrnul(s + 1, sep);
 
-       *share = it->it_name;
-       *share_len = s - it->it_name;
-       *prefix = *s ? s + 1 : s;
-       *prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
+       /* extract target share */
+       *share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
+       if (!*share)
+               return -ENOMEM;
 
+       /* skip separator */
+       if (*s)
+               s++;
+       /* point to prefix in DFS path */
+       p = path + it->it_path_consumed;
+       if (*p == sep)
+               p++;
+
+       /* merge prefix paths from DFS path and target node */
+       plen1 = it->it_name + strlen(it->it_name) - s;
+       plen2 = path + strlen(path) - p;
+       if (plen1 || plen2) {
+               len = plen1 + plen2 + 2;
+               *prefix = kmalloc(len, GFP_KERNEL);
+               if (!*prefix) {
+                       kfree(*share);
+                       *share = NULL;
+                       return -ENOMEM;
+               }
+               if (plen1)
+                       scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p);
+               else
+                       strscpy(*prefix, p, len);
+       }
        return 0;
 }
 
index bf94d08cfb5a14d37488428027a629f061674741..3d7c05194536ee444689e5f7549873565495fa31 100644 (file)
@@ -19,6 +19,7 @@ struct dfs_cache_tgt_list {
 
 struct dfs_cache_tgt_iterator {
        char *it_name;
+       int it_path_consumed;
        struct list_head it_list;
 };
 
@@ -48,10 +49,8 @@ extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol,
 extern int dfs_cache_update_vol(const char *fullpath,
                                struct TCP_Server_Info *server);
 extern void dfs_cache_del_vol(const char *fullpath);
-
-extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
-                                  const char **share, size_t *share_len,
-                                  const char **prefix, size_t *prefix_len);
+extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
+                                  char **share, char **prefix);
 
 static inline struct dfs_cache_tgt_iterator *
 dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
index e44d049142d04054293b749fa55d2a013db03593..764234803071bf9dc4e2011b457e40584d6f7b19 100644 (file)
@@ -1164,8 +1164,7 @@ static inline void cifs_put_tcon_super(struct super_block *sb)
 }
 #endif
 
-int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
-                        size_t prefix_len)
+int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
 {
        struct super_block *sb;
        struct cifs_sb_info *cifs_sb;
@@ -1179,8 +1178,8 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
 
        kfree(cifs_sb->prepath);
 
-       if (*prefix && prefix_len) {
-               cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
+       if (prefix && *prefix) {
+               cifs_sb->prepath = kstrndup(prefix, strlen(prefix), GFP_ATOMIC);
                if (!cifs_sb->prepath) {
                        rc = -ENOMEM;
                        goto out;