cifs: fix dfs domain referrals
authorRonnie Sahlberg <lsahlber@redhat.com>
Fri, 29 Jan 2021 03:35:10 +0000 (21:35 -0600)
committerSteve French <stfrench@microsoft.com>
Fri, 29 Jan 2021 03:40:43 +0000 (21:40 -0600)
The new mount API requires additional changes to how DFS
is handled. Additional testing of DFS uncovered problems
with domain based DFS referrals (a follow on patch addresses
DFS links) which this patch addresses.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifs_dfs_ref.c
fs/cifs/cifsfs.c
fs/cifs/cifsproto.h
fs/cifs/connect.c
fs/cifs/dfs_cache.c
fs/cifs/fs_context.c

index e4c6ae4..6b1ce4e 100644 (file)
@@ -133,8 +133,9 @@ cifs_build_devname(char *nodename, const char *prepath)
  * 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)
+                                const char *fullpath,
+                                const struct dfs_info3_param *ref,
+                                char **devname)
 {
        int rc;
        char *name;
@@ -231,7 +232,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
        strcat(mountdata, "ip=");
        strcat(mountdata, srvIP);
 
-       kfree(name);
+       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 );*/
@@ -278,7 +282,7 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
 
        /* strip first '\' from fullpath */
        mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
-                                              fullpath + 1, NULL);
+                                              fullpath + 1, NULL, NULL);
        if (IS_ERR(mountdata)) {
                kfree(devname);
                return (struct vfsmount *)mountdata;
index ce0d003..e46da53 100644 (file)
@@ -822,7 +822,7 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
                goto out;
        }
 
-       rc = cifs_setup_volume_info(cifs_sb->ctx);
+       rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, old_ctx->UNC);
        if (rc) {
                root = ERR_PTR(rc);
                goto out;
index 340ff81..32f7a01 100644 (file)
@@ -78,7 +78,8 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
                                     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);
+               const char *fullpath, const struct dfs_info3_param *ref,
+               char **devname);
 /* extern void renew_parental_timestamps(struct dentry *direntry);*/
 extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
                                        struct TCP_Server_Info *server);
@@ -89,6 +90,7 @@ extern void cifs_wake_up_task(struct mid_q_entry *mid);
 extern int cifs_handle_standard(struct TCP_Server_Info *server,
                                struct mid_q_entry *mid);
 extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
+extern int smb3_parse_opt(const char *options, const char *key, char **val);
 extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
 extern int cifs_call_async(struct TCP_Server_Info *server,
@@ -549,7 +551,7 @@ extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
                        unsigned char *p24);
 
 extern int
-cifs_setup_volume_info(struct smb3_fs_context *ctx);
+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 c8ef24b..10fe6d6 100644 (file)
@@ -2972,17 +2972,20 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
        rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
                            ref_path, &referral, NULL);
        if (!rc) {
+               char *fake_devname = NULL;
+
                mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
-                                                  full_path + 1, &referral);
+                                                  full_path + 1, &referral,
+                                                  &fake_devname);
                free_dfs_info_param(&referral);
 
                if (IS_ERR(mdata)) {
                        rc = PTR_ERR(mdata);
                        mdata = NULL;
                } else {
-                       smb3_cleanup_fs_context_contents(ctx);
-                       rc = cifs_setup_volume_info(ctx);
+                       rc = cifs_setup_volume_info(ctx, mdata, fake_devname);
                }
+               kfree(fake_devname);
                kfree(cifs_sb->ctx->mount_options);
                cifs_sb->ctx->mount_options = mdata;
        }
@@ -3036,6 +3039,7 @@ static int setup_dfs_tgt_conn(const char *path, const char *full_path,
        struct dfs_info3_param ref = {0};
        char *mdata = NULL;
        struct smb3_fs_context fake_ctx = {NULL};
+       char *fake_devname = NULL;
 
        cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path);
 
@@ -3044,16 +3048,18 @@ static int setup_dfs_tgt_conn(const char *path, const char *full_path,
                return rc;
 
        mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
-                                          full_path + 1, &ref);
+                                          full_path + 1, &ref,
+                                          &fake_devname);
        free_dfs_info_param(&ref);
 
        if (IS_ERR(mdata)) {
                rc = PTR_ERR(mdata);
                mdata = NULL;
        } else
-               rc = cifs_setup_volume_info(&fake_ctx);
+               rc = cifs_setup_volume_info(&fake_ctx, mdata, fake_devname);
 
        kfree(mdata);
+       kfree(fake_devname);
 
        if (!rc) {
                /*
@@ -3122,10 +3128,24 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
  * we should pass a clone of the original context?
  */
 int
-cifs_setup_volume_info(struct smb3_fs_context *ctx)
+cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname)
 {
        int rc = 0;
 
+       smb3_parse_devname(devname, ctx);
+
+       if (mntopts) {
+               char *ip;
+
+               cifs_dbg(FYI, "%s: mntopts=%s\n", __func__, mntopts);
+               rc = smb3_parse_opt(mntopts, "ip", &ip);
+               if (!rc && !cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip,
+                                                strlen(ip))) {
+                       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);
index 0fdb0de..4950ab0 100644 (file)
@@ -1417,7 +1417,7 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
        int rc;
        struct cache_entry *ce;
        struct dfs_info3_param ref = {0};
-       char *mdata = NULL;
+       char *mdata = NULL, *devname = NULL;
        struct TCP_Server_Info *server;
        struct cifs_ses *ses;
        struct smb3_fs_context ctx = {NULL};
@@ -1444,7 +1444,8 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
 
        up_read(&htable_rw_lock);
 
-       mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref);
+       mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref,
+                                          &devname);
        free_dfs_info_param(&ref);
 
        if (IS_ERR(mdata)) {
@@ -1453,7 +1454,7 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
                goto out;
        }
 
-       rc = cifs_setup_volume_info(&ctx);
+       rc = cifs_setup_volume_info(&ctx, NULL, devname);
 
        if (rc) {
                ses = ERR_PTR(rc);
@@ -1472,6 +1473,7 @@ out:
        smb3_cleanup_fs_context_contents(&ctx);
        kfree(mdata);
        kfree(rpath);
+       kfree(devname);
 
        return ses;
 }
index 2735441..5111aad 100644 (file)
@@ -401,6 +401,37 @@ cifs_parse_smb_version(char *value, struct smb3_fs_context *ctx, bool is_smb3)
        return 0;
 }
 
+int smb3_parse_opt(const char *options, const char *key, char **val)
+{
+       int rc = -ENOENT;
+       char *opts, *orig, *p;
+
+       orig = opts = kstrdup(options, GFP_KERNEL);
+       if (!opts)
+               return -ENOMEM;
+
+       while ((p = strsep(&opts, ","))) {
+               char *nval;
+
+               if (!*p)
+                       continue;
+               if (strncasecmp(p, key, strlen(key)))
+                       continue;
+               nval = strchr(p, '=');
+               if (nval) {
+                       if (nval == p)
+                               continue;
+                       *nval++ = 0;
+                       *val = kstrndup(nval, strlen(nval), GFP_KERNEL);
+                       rc = !*val ? -ENOMEM : 0;
+                       goto out;
+               }
+       }
+out:
+       kfree(orig);
+       return rc;
+}
+
 /*
  * Parse a devname into substrings and populate the ctx->UNC and ctx->prepath
  * fields with the result. Returns 0 on success and an error otherwise