summaryrefslogtreecommitdiff
path: root/fs/cifs/dfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/dfs.c')
-rw-r--r--fs/cifs/dfs.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/fs/cifs/dfs.c b/fs/cifs/dfs.c
new file mode 100644
index 000000000000..0b15d7e9f818
--- /dev/null
+++ b/fs/cifs/dfs.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
+ */
+
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "dns_resolve.h"
+#include "fs_context.h"
+#include "dfs.h"
+
+/* Resolve UNC server name and set destination ip address in fs context */
+static int resolve_unc(const char *path, struct smb3_fs_context *ctx)
+{
+ int rc;
+ char *ip = NULL;
+
+ rc = dns_resolve_server_name_to_ip(path, &ip, NULL);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: failed to resolve UNC server name: %d\n", __func__, rc);
+ return rc;
+ }
+
+ if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip))) {
+ cifs_dbg(VFS, "%s: could not determinate destination address\n", __func__);
+ rc = -EHOSTUNREACH;
+ } else
+ rc = 0;
+
+ kfree(ip);
+ return rc;
+}
+
+/**
+ * dfs_parse_target_referral - set fs context for dfs target referral
+ *
+ * @full_path: full path in UNC format.
+ * @ref: dfs referral pointer.
+ * @ctx: smb3 fs context pointer.
+ *
+ * Return zero if dfs referral was parsed correctly, otherwise non-zero.
+ */
+int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
+ struct smb3_fs_context *ctx)
+{
+ int rc;
+ const char *prepath = NULL;
+ char *path;
+
+ if (!full_path || !*full_path || !ref || !ctx)
+ return -EINVAL;
+
+ if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0))
+ return -EINVAL;
+
+ if (strlen(full_path) - ref->path_consumed) {
+ prepath = full_path + ref->path_consumed;
+ /* skip initial delimiter */
+ if (*prepath == '/' || *prepath == '\\')
+ prepath++;
+ }
+
+ path = cifs_build_devname(ref->node_name, prepath);
+ if (IS_ERR(path))
+ return PTR_ERR(path);
+
+ rc = smb3_parse_devname(path, ctx);
+ if (rc)
+ goto out;
+
+ rc = resolve_unc(path, ctx);
+
+out:
+ kfree(path);
+ return rc;
+}