diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/Makefile | 2 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 141 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 6 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 7 | ||||
-rw-r--r-- | fs/cifs/connect.c | 100 | ||||
-rw-r--r-- | fs/cifs/dfs.c | 76 | ||||
-rw-r--r-- | fs/cifs/dfs.h | 16 |
7 files changed, 101 insertions, 247 deletions
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 7c9785973f49..304a7f6cc13a 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -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 diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 020e71fe1454..cae8a52c6d9a 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -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 */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 914cbb9de482..10e00c624922 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -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); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7d4b37eeec98..4b1f7315ca16 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -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); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b04706835e02..94d1741ced21 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -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 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; +} diff --git a/fs/cifs/dfs.h b/fs/cifs/dfs.h new file mode 100644 index 000000000000..af09903b435a --- /dev/null +++ b/fs/cifs/dfs.h @@ -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 */ |