]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
smb: client: don't try following DFS links in cifs_tree_connect()
authorPaulo Alcantara <pc@manguebit.com>
Tue, 26 Nov 2024 18:55:53 +0000 (15:55 -0300)
committerSteve French <stfrench@microsoft.com>
Wed, 27 Nov 2024 00:46:35 +0000 (18:46 -0600)
We can't properly support chasing DFS links in cifs_tree_connect()
because

  (1) We don't support creating new sessions while we're reconnecting,
      which would be required for DFS interlinks.

  (2) ->is_path_accessible() can't be called from cifs_tree_connect()
     as it would deadlock with smb2_reconnect().  This is required for
     checking if new DFS target is a nested DFS link.

By unconditionally trying to get an DFS referral from new DFS target
isn't correct because if the new DFS target (interlink) is an DFS
standalone namespace, then we would end up getting -ELOOP and then
potentially leaving tcon disconnected.

Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/dfs.c

index 5dc7708ed6000a95adc2183054c7c950411ed1ee..4647df9e1e3bfcdc258ad77eb18951d841e5fe89 100644 (file)
@@ -321,49 +321,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
        return rc;
 }
 
-/* Update dfs referral path of superblock */
-static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
-                                 const char *target)
-{
-       int rc = 0;
-       size_t len = strlen(target);
-       char *refpath, *npath;
-
-       if (unlikely(len < 2 || *target != '\\'))
-               return -EINVAL;
-
-       if (target[1] == '\\') {
-               len += 1;
-               refpath = kmalloc(len, GFP_KERNEL);
-               if (!refpath)
-                       return -ENOMEM;
-
-               scnprintf(refpath, len, "%s", target);
-       } else {
-               len += sizeof("\\");
-               refpath = kmalloc(len, GFP_KERNEL);
-               if (!refpath)
-                       return -ENOMEM;
-
-               scnprintf(refpath, len, "\\%s", target);
-       }
-
-       npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
-       kfree(refpath);
-
-       if (IS_ERR(npath)) {
-               rc = PTR_ERR(npath);
-       } else {
-               mutex_lock(&server->refpath_lock);
-               spin_lock(&server->srv_lock);
-               kfree(server->leaf_fullpath);
-               server->leaf_fullpath = npath;
-               spin_unlock(&server->srv_lock);
-               mutex_unlock(&server->refpath_lock);
-       }
-       return rc;
-}
-
 static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
                                       bool *target_match)
 {
@@ -388,77 +345,22 @@ static int target_share_matches_server(struct TCP_Server_Info *server, char *sha
        return rc;
 }
 
-static void __tree_connect_ipc(const unsigned int xid, char *tree,
-                              struct cifs_sb_info *cifs_sb,
-                              struct cifs_ses *ses)
-{
-       struct TCP_Server_Info *server = ses->server;
-       struct cifs_tcon *tcon = ses->tcon_ipc;
-       int rc;
-
-       spin_lock(&ses->ses_lock);
-       spin_lock(&ses->chan_lock);
-       if (cifs_chan_needs_reconnect(ses, server) ||
-           ses->ses_status != SES_GOOD) {
-               spin_unlock(&ses->chan_lock);
-               spin_unlock(&ses->ses_lock);
-               cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n",
-                               __func__);
-               return;
-       }
-       spin_unlock(&ses->chan_lock);
-       spin_unlock(&ses->ses_lock);
-
-       cifs_server_lock(server);
-       scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
-       cifs_server_unlock(server);
-
-       rc = server->ops->tree_connect(xid, ses, tree, tcon,
-                                      cifs_sb->local_nls);
-       cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc);
-       spin_lock(&tcon->tc_lock);
-       if (rc) {
-               tcon->status = TID_NEED_TCON;
-       } else {
-               tcon->status = TID_GOOD;
-               tcon->need_reconnect = false;
-       }
-       spin_unlock(&tcon->tc_lock);
-}
-
-static void tree_connect_ipc(const unsigned int xid, char *tree,
-                            struct cifs_sb_info *cifs_sb,
-                            struct cifs_tcon *tcon)
-{
-       struct cifs_ses *ses = tcon->ses;
-
-       __tree_connect_ipc(xid, tree, cifs_sb, ses);
-       __tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses));
-}
-
-static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
-                                    struct cifs_sb_info *cifs_sb, char *tree, bool islink,
-                                    struct dfs_cache_tgt_list *tl)
+static int tree_connect_dfs_target(const unsigned int xid,
+                                  struct cifs_tcon *tcon,
+                                  struct cifs_sb_info *cifs_sb,
+                                  char *tree, bool islink,
+                                  struct dfs_cache_tgt_list *tl)
 {
-       int rc;
+       const struct smb_version_operations *ops = tcon->ses->server->ops;
        struct TCP_Server_Info *server = tcon->ses->server;
-       const struct smb_version_operations *ops = server->ops;
-       struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
-       char *share = NULL, *prefix = NULL;
        struct dfs_cache_tgt_iterator *tit;
+       char *share = NULL, *prefix = NULL;
        bool target_match;
-
-       tit = dfs_cache_get_tgt_iterator(tl);
-       if (!tit) {
-               rc = -ENOENT;
-               goto out;
-       }
+       int rc = -ENOENT;
 
        /* Try to tree connect to all dfs targets */
-       for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
-               const char *target = dfs_cache_get_tgt_name(tit);
-               DFS_CACHE_TGT_LIST(ntl);
-
+       for (tit = dfs_cache_get_tgt_iterator(tl);
+            tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
                kfree(share);
                kfree(prefix);
                share = prefix = NULL;
@@ -479,69 +381,16 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
                }
 
                dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
-               tree_connect_ipc(xid, tree, cifs_sb, tcon);
-
                scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
-               if (!islink) {
-                       rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
-                       break;
-               }
-
-               /*
-                * If no dfs referrals were returned from link target, then just do a TREE_CONNECT
-                * to it.  Otherwise, cache the dfs referral and then mark current tcp ses for
-                * reconnect so either the demultiplex thread or the echo worker will reconnect to
-                * newly resolved target.
-                */
-               if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
-                                  NULL, &ntl)) {
-                       rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
-                       if (rc)
-                               continue;
-
+               rc = ops->tree_connect(xid, tcon->ses, tree,
+                                      tcon, tcon->ses->local_nls);
+               if (islink && !rc && cifs_sb)
                        rc = cifs_update_super_prepath(cifs_sb, prefix);
-               } else {
-                       /* Target is another dfs share */
-                       rc = update_server_fullpath(server, cifs_sb, target);
-                       dfs_cache_free_tgts(tl);
-
-                       if (!rc) {
-                               rc = -EREMOTE;
-                               list_replace_init(&ntl.tl_list, &tl->tl_list);
-                       } else
-                               dfs_cache_free_tgts(&ntl);
-               }
                break;
        }
 
-out:
        kfree(share);
        kfree(prefix);
-
-       return rc;
-}
-
-static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
-                                  struct cifs_sb_info *cifs_sb, char *tree, bool islink,
-                                  struct dfs_cache_tgt_list *tl)
-{
-       int rc;
-       int num_links = 0;
-       struct TCP_Server_Info *server = tcon->ses->server;
-       char *old_fullpath = server->leaf_fullpath;
-
-       do {
-               rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
-               if (!rc || rc != -EREMOTE)
-                       break;
-       } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
-       /*
-        * If we couldn't tree connect to any targets from last referral path, then
-        * retry it from newly resolved dfs referral.
-        */
-       if (rc && server->leaf_fullpath != old_fullpath)
-               cifs_signal_cifsd_for_reconnect(server, true);
-
        dfs_cache_free_tgts(tl);
        return rc;
 }
@@ -597,14 +446,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
        if (!IS_ERR(sb))
                cifs_sb = CIFS_SB(sb);
 
-       /*
-        * Tree connect to last share in @tcon->tree_name whether dfs super or
-        * cached dfs referral was not found.
-        */
-       if (!cifs_sb || !server->leaf_fullpath ||
+       /* Tree connect to last share in @tcon->tree_name if no DFS referral */
+       if (!server->leaf_fullpath ||
            dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
-               rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon,
-                                      cifs_sb ? cifs_sb->local_nls : nlsc);
+               rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
+                                      tcon, tcon->ses->local_nls);
                goto out;
        }