summaryrefslogtreecommitdiff
path: root/fs/smb/client/dfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client/dfs.c')
-rw-r--r--fs/smb/client/dfs.c287
1 files changed, 61 insertions, 226 deletions
diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
index 3f6077c68d68..f65a8a90ba27 100644
--- a/fs/smb/client/dfs.c
+++ b/fs/smb/client/dfs.c
@@ -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,31 @@ 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)
+ rc = cifs_mount_get_tcon(mnt_ctx);
+ if (rc) {
+ if (tgt.server_type == DFS_TYPE_LINK &&
+ DFS_INTERLINK(tgt.flags))
+ rc = -EREMOTE;
+ } else {
+ rc = cifs_is_path_remote(mnt_ctx);
+ 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 == -EREMOTE) {
+ rc = ref_walk_advance(rw);
if (!rc) {
- rc = -EREMOTE;
+ 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 +191,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 +254,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 +284,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,53 +304,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;
-}
-
-/* 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;
}
@@ -388,77 +333,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,74 +369,21 @@ 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;
}
-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
+int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
{
int rc;
struct TCP_Server_Info *server = tcon->ses->server;
@@ -588,7 +425,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
cifs_server_lock(server);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
cifs_server_unlock(server);
- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
+ rc = ops->tree_connect(xid, tcon->ses, tree,
+ tcon, tcon->ses->local_nls);
goto out;
}
@@ -596,14 +434,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
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;
}