cifs: get rid of mount options string parsing
authorPaulo Alcantara <pc@cjr.nz>
Tue, 4 Oct 2022 21:41:27 +0000 (18:41 -0300)
committerSteve French <stfrench@microsoft.com>
Mon, 19 Dec 2022 14:03:11 +0000 (08:03 -0600)
After switching to filesystem context support, we no longer need to
handle mount options string when chasing dfs referrals.  Now, we set
the new values directly into smb3_fs_context.

Start working on a separate source file to handle most dfs related
mount functions as connect.c has already became too big.  The
remaining functions will be moved gradually in follow-up patches.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/Makefile
fs/cifs/cifs_dfs_ref.c
fs/cifs/cifsfs.c
fs/cifs/cifsproto.h
fs/cifs/connect.c
fs/cifs/dfs.c [new file with mode: 0644]
fs/cifs/dfs.h [new file with mode: 0644]

index 7c97859..304a7f6 100644 (file)
@@ -21,7 +21,7 @@ cifs-$(CONFIG_CIFS_XATTR) += xattr.o
 
 cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
-cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o
+cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o dfs.o
 
 cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
 
index 020e71f..cae8a52 100644 (file)
@@ -60,7 +60,7 @@ void cifs_dfs_release_automount_timer(void)
  * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
  * for freeing the returned string.
  */
-static char *
+char *
 cifs_build_devname(char *nodename, const char *prepath)
 {
        size_t pplen;
@@ -119,145 +119,6 @@ cifs_build_devname(char *nodename, const char *prepath)
        return dev;
 }
 
-
-/**
- * cifs_compose_mount_options  -       creates mount options for referral
- * @sb_mountdata:      parent/root DFS mount options (template)
- * @fullpath:          full path in UNC format
- * @ref:               optional server's referral
- * @devname:           return the built cifs device name if passed pointer not NULL
- * creates mount options for submount based on template options sb_mountdata
- * and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
- *
- * Returns: pointer to new mount options or ERR_PTR.
- * Caller is responsible for freeing returned value if it is not error.
- */
-char *cifs_compose_mount_options(const char *sb_mountdata,
-                                const char *fullpath,
-                                const struct dfs_info3_param *ref,
-                                char **devname)
-{
-       int rc;
-       char *name;
-       char *mountdata = NULL;
-       const char *prepath = NULL;
-       int md_len;
-       char *tkn_e;
-       char *srvIP = NULL;
-       char sep = ',';
-       int off, noff;
-
-       if (sb_mountdata == NULL)
-               return ERR_PTR(-EINVAL);
-
-       if (ref) {
-               if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0))
-                       return ERR_PTR(-EINVAL);
-
-               if (strlen(fullpath) - ref->path_consumed) {
-                       prepath = fullpath + ref->path_consumed;
-                       /* skip initial delimiter */
-                       if (*prepath == '/' || *prepath == '\\')
-                               prepath++;
-               }
-
-               name = cifs_build_devname(ref->node_name, prepath);
-               if (IS_ERR(name)) {
-                       rc = PTR_ERR(name);
-                       name = NULL;
-                       goto compose_mount_options_err;
-               }
-       } else {
-               name = cifs_build_devname((char *)fullpath, NULL);
-               if (IS_ERR(name)) {
-                       rc = PTR_ERR(name);
-                       name = NULL;
-                       goto compose_mount_options_err;
-               }
-       }
-
-       rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL);
-       if (rc < 0) {
-               cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
-                        __func__, name, rc);
-               goto compose_mount_options_err;
-       }
-
-       /*
-        * In most cases, we'll be building a shorter string than the original,
-        * but we do have to assume that the address in the ip= option may be
-        * much longer than the original. Add the max length of an address
-        * string to the length of the original string to allow for worst case.
-        */
-       md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
-       mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL);
-       if (mountdata == NULL) {
-               rc = -ENOMEM;
-               goto compose_mount_options_err;
-       }
-
-       /* copy all options except of unc,ip,prefixpath */
-       off = 0;
-       if (strncmp(sb_mountdata, "sep=", 4) == 0) {
-                       sep = sb_mountdata[4];
-                       strncpy(mountdata, sb_mountdata, 5);
-                       off += 5;
-       }
-
-       do {
-               tkn_e = strchr(sb_mountdata + off, sep);
-               if (tkn_e == NULL)
-                       noff = strlen(sb_mountdata + off);
-               else
-                       noff = tkn_e - (sb_mountdata + off) + 1;
-
-               if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) {
-                       off += noff;
-                       continue;
-               }
-               if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
-                       off += noff;
-                       continue;
-               }
-               if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) {
-                       off += noff;
-                       continue;
-               }
-               if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
-                       off += noff;
-                       continue;
-               }
-               strncat(mountdata, sb_mountdata + off, noff);
-               off += noff;
-       } while (tkn_e);
-       strcat(mountdata, sb_mountdata + off);
-       mountdata[md_len] = '\0';
-
-       /* copy new IP and ref share name */
-       if (mountdata[strlen(mountdata) - 1] != sep)
-               strncat(mountdata, &sep, 1);
-       strcat(mountdata, "ip=");
-       strcat(mountdata, srvIP);
-
-       if (devname)
-               *devname = name;
-       else
-               kfree(name);
-
-       /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
-       /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
-
-compose_mount_options_out:
-       kfree(srvIP);
-       return mountdata;
-
-compose_mount_options_err:
-       kfree(mountdata);
-       mountdata = ERR_PTR(rc);
-       kfree(name);
-       goto compose_mount_options_out;
-}
-
 /*
  * Create a vfsmount that we can automount
  */
index 914cbb9..10e00c6 100644 (file)
@@ -896,12 +896,6 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
                goto out;
        }
 
-       rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL);
-       if (rc) {
-               root = ERR_PTR(rc);
-               goto out;
-       }
-
        rc = cifs_setup_cifs_sb(cifs_sb);
        if (rc) {
                root = ERR_PTR(rc);
index 7d4b37e..4b1f731 100644 (file)
@@ -75,9 +75,7 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
                                     struct cifs_tcon *tcon,
                                     int add_treename);
 extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
-extern char *cifs_compose_mount_options(const char *sb_mountdata,
-               const char *fullpath, const struct dfs_info3_param *ref,
-               char **devname);
+char *cifs_build_devname(char *nodename, const char *prepath);
 extern void delete_mid(struct mid_q_entry *mid);
 extern void release_mid(struct mid_q_entry *mid);
 extern void cifs_wake_up_task(struct mid_q_entry *mid);
@@ -561,9 +559,6 @@ extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
 extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
                        const struct nls_table *codepage);
 
-extern int
-cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname);
-
 extern struct TCP_Server_Info *
 cifs_find_tcp_session(struct smb3_fs_context *ctx);
 
index b047068..94d1741 100644 (file)
@@ -46,6 +46,7 @@
 #include "smbdirect.h"
 #include "dns_resolve.h"
 #ifdef CONFIG_CIFS_DFS_UPCALL
+#include "dfs.h"
 #include "dfs_cache.h"
 #endif
 #include "fs_context.h"
@@ -3397,95 +3398,8 @@ build_unc_path_to_root(const struct smb3_fs_context *ctx,
        cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);
        return full_path;
 }
-
-/*
- * expand_dfs_referral - Update cifs_sb from dfs referral path
- *
- * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the
- * submount.  Otherwise it will be left untouched.
- */
-static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path,
-                              struct dfs_info3_param *referral)
-{
-       int rc;
-       struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
-       struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
-       char *fake_devname = NULL, *mdata = NULL;
-
-       mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral,
-                                          &fake_devname);
-       if (IS_ERR(mdata)) {
-               rc = PTR_ERR(mdata);
-               mdata = NULL;
-       } else {
-               /*
-                * We can not clear out the whole structure since we no longer have an explicit
-                * function to parse a mount-string. Instead we need to clear out the individual
-                * fields that are no longer valid.
-                */
-               kfree(ctx->prepath);
-               ctx->prepath = NULL;
-               rc = cifs_setup_volume_info(ctx, mdata, fake_devname);
-       }
-       kfree(fake_devname);
-       kfree(cifs_sb->ctx->mount_options);
-       cifs_sb->ctx->mount_options = mdata;
-
-       return rc;
-}
 #endif
 
-/* TODO: all callers to this are broken. We are not parsing mount_options here
- * we should pass a clone of the original context?
- */
-int
-cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname)
-{
-       int rc;
-
-       if (devname) {
-               cifs_dbg(FYI, "%s: devname=%s\n", __func__, devname);
-               rc = smb3_parse_devname(devname, ctx);
-               if (rc) {
-                       cifs_dbg(VFS, "%s: failed to parse %s: %d\n", __func__, devname, rc);
-                       return rc;
-               }
-       }
-
-       if (mntopts) {
-               char *ip;
-
-               rc = smb3_parse_opt(mntopts, "ip", &ip);
-               if (rc) {
-                       cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc);
-                       return rc;
-               }
-
-               rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip));
-               kfree(ip);
-               if (!rc) {
-                       cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__);
-                       return -EINVAL;
-               }
-       }
-
-       if (ctx->nullauth) {
-               cifs_dbg(FYI, "Anonymous login\n");
-               kfree(ctx->username);
-               ctx->username = NULL;
-       } else if (ctx->username) {
-               /* BB fixme parse for domain name here */
-               cifs_dbg(FYI, "Username: %s\n", ctx->username);
-       } else {
-               cifs_dbg(VFS, "No username specified\n");
-       /* In userspace mount helper we can get user name from alternate
-          locations such as env variables and files on disk */
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static int
 cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
                                        unsigned int xid,
@@ -3630,7 +3544,6 @@ static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path,
        int rc;
        struct dfs_info3_param ref = {};
        struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
-       char *oldmnt = cifs_sb->ctx->mount_options;
 
        cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path,
                 dfs_cache_get_tgt_name(tit));
@@ -3639,15 +3552,14 @@ static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path,
        if (rc)
                goto out;
 
-       rc = expand_dfs_referral(mnt_ctx, full_path, &ref);
+       rc = dfs_parse_target_referral(full_path + 1, &ref, mnt_ctx->fs_ctx);
        if (rc)
                goto out;
 
-       /* Connect to new target only if we were redirected (e.g. mount options changed) */
-       if (oldmnt != cifs_sb->ctx->mount_options) {
-               mount_put_conns(mnt_ctx);
-               rc = mount_get_dfs_conns(mnt_ctx);
-       }
+       /* XXX: maybe check if we were actually redirected and avoid reconnecting? */
+       mount_put_conns(mnt_ctx);
+       rc = mount_get_dfs_conns(mnt_ctx);
+
        if (!rc) {
                if (cifs_is_referral_server(mnt_ctx->tcon, &ref))
                        set_root_ses(mnt_ctx);
diff --git a/fs/cifs/dfs.c b/fs/cifs/dfs.c
new file mode 100644 (file)
index 0000000..0b15d7e
--- /dev/null
@@ -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;
+}
diff --git a/fs/cifs/dfs.h b/fs/cifs/dfs.h
new file mode 100644 (file)
index 0000000..af09903
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
+ */
+
+#ifndef _CIFS_DFS_H
+#define _CIFS_DFS_H
+
+#include "cifsglob.h"
+#include "fs_context.h"
+
+int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
+                             struct smb3_fs_context *ctx);
+
+
+#endif /* _CIFS_DFS_H */