1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Contains the CIFS DFS referral mounting routines used for handling 4 * traversal via DFS junction point 5 * 6 * Copyright (c) 2007 Igor Mammedov 7 * Copyright (C) International Business Machines Corp., 2008 8 * Author(s): Igor Mammedov (niallain@gmail.com) 9 * Steve French (sfrench@us.ibm.com) 10 */ 11 12 #include <linux/dcache.h> 13 #include <linux/mount.h> 14 #include <linux/namei.h> 15 #include <linux/slab.h> 16 #include <linux/vfs.h> 17 #include <linux/fs.h> 18 #include <linux/inet.h> 19 #include "cifsglob.h" 20 #include "cifsproto.h" 21 #include "cifsfs.h" 22 #include "dns_resolve.h" 23 #include "cifs_debug.h" 24 #include "dfs.h" 25 #include "fs_context.h" 26 27 static LIST_HEAD(cifs_dfs_automount_list); 28 29 static void cifs_dfs_expire_automounts(struct work_struct *work); 30 static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, 31 cifs_dfs_expire_automounts); 32 static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; 33 34 static void cifs_dfs_expire_automounts(struct work_struct *work) 35 { 36 struct list_head *list = &cifs_dfs_automount_list; 37 38 mark_mounts_for_expiry(list); 39 if (!list_empty(list)) 40 schedule_delayed_work(&cifs_dfs_automount_task, 41 cifs_dfs_mountpoint_expiry_timeout); 42 } 43 44 void cifs_dfs_release_automount_timer(void) 45 { 46 BUG_ON(!list_empty(&cifs_dfs_automount_list)); 47 cancel_delayed_work_sync(&cifs_dfs_automount_task); 48 } 49 50 /** 51 * cifs_build_devname - build a devicename from a UNC and optional prepath 52 * @nodename: pointer to UNC string 53 * @prepath: pointer to prefixpath (or NULL if there isn't one) 54 * 55 * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer 56 * big enough to hold the final thing. Copy the UNC from the nodename, and 57 * concatenate the prepath onto the end of it if there is one. 58 * 59 * Returns pointer to the built string, or a ERR_PTR. Caller is responsible 60 * for freeing the returned string. 61 */ 62 char * 63 cifs_build_devname(char *nodename, const char *prepath) 64 { 65 size_t pplen; 66 size_t unclen; 67 char *dev; 68 char *pos; 69 70 /* skip over any preceding delimiters */ 71 nodename += strspn(nodename, "\\"); 72 if (!*nodename) 73 return ERR_PTR(-EINVAL); 74 75 /* get length of UNC and set pos to last char */ 76 unclen = strlen(nodename); 77 pos = nodename + unclen - 1; 78 79 /* trim off any trailing delimiters */ 80 while (*pos == '\\') { 81 --pos; 82 --unclen; 83 } 84 85 /* allocate a buffer: 86 * +2 for preceding "//" 87 * +1 for delimiter between UNC and prepath 88 * +1 for trailing NULL 89 */ 90 pplen = prepath ? strlen(prepath) : 0; 91 dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); 92 if (!dev) 93 return ERR_PTR(-ENOMEM); 94 95 pos = dev; 96 /* add the initial "//" */ 97 *pos = '/'; 98 ++pos; 99 *pos = '/'; 100 ++pos; 101 102 /* copy in the UNC portion from referral */ 103 memcpy(pos, nodename, unclen); 104 pos += unclen; 105 106 /* copy the prefixpath remainder (if there is one) */ 107 if (pplen) { 108 *pos = '/'; 109 ++pos; 110 memcpy(pos, prepath, pplen); 111 pos += pplen; 112 } 113 114 /* NULL terminator */ 115 *pos = '\0'; 116 117 convert_delimiter(dev, '/'); 118 return dev; 119 } 120 121 static int set_dest_addr(struct smb3_fs_context *ctx) 122 { 123 struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; 124 int rc; 125 126 rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL); 127 if (!rc) 128 cifs_set_port(addr, ctx->port); 129 return rc; 130 } 131 132 /* 133 * Create a vfsmount that we can automount 134 */ 135 static struct vfsmount *cifs_dfs_do_automount(struct path *path) 136 { 137 int rc; 138 struct dentry *mntpt = path->dentry; 139 struct fs_context *fc; 140 struct cifs_sb_info *cifs_sb; 141 void *page = NULL; 142 struct smb3_fs_context *ctx, *cur_ctx; 143 struct smb3_fs_context tmp; 144 char *full_path; 145 struct vfsmount *mnt; 146 147 if (IS_ROOT(mntpt)) 148 return ERR_PTR(-ESTALE); 149 150 /* 151 * The MSDFS spec states that paths in DFS referral requests and 152 * responses must be prefixed by a single '\' character instead of 153 * the double backslashes usually used in the UNC. This function 154 * gives us the latter, so we must adjust the result. 155 */ 156 cifs_sb = CIFS_SB(mntpt->d_sb); 157 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) 158 return ERR_PTR(-EREMOTE); 159 160 cur_ctx = cifs_sb->ctx; 161 162 fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt); 163 if (IS_ERR(fc)) 164 return ERR_CAST(fc); 165 166 ctx = smb3_fc2context(fc); 167 168 page = alloc_dentry_path(); 169 full_path = dfs_get_automount_devname(mntpt, page); 170 if (IS_ERR(full_path)) { 171 mnt = ERR_CAST(full_path); 172 goto out; 173 } 174 175 tmp = *cur_ctx; 176 tmp.source = NULL; 177 tmp.leaf_fullpath = NULL; 178 tmp.UNC = tmp.prepath = NULL; 179 tmp.dfs_root_ses = NULL; 180 181 rc = smb3_fs_context_dup(ctx, &tmp); 182 if (rc) { 183 mnt = ERR_PTR(rc); 184 goto out; 185 } 186 187 rc = smb3_parse_devname(full_path, ctx); 188 if (rc) { 189 mnt = ERR_PTR(rc); 190 goto out; 191 } 192 193 ctx->source = smb3_fs_context_fullpath(ctx, '/'); 194 if (IS_ERR(ctx->source)) { 195 mnt = ERR_CAST(ctx->source); 196 ctx->source = NULL; 197 goto out; 198 } 199 cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dstaddr=%pISpc\n", 200 __func__, ctx->source, ctx->UNC, ctx->prepath, &ctx->dstaddr); 201 202 rc = set_dest_addr(ctx); 203 if (!rc) 204 mnt = fc_mount(fc); 205 else 206 mnt = ERR_PTR(rc); 207 208 out: 209 put_fs_context(fc); 210 free_dentry_path(page); 211 return mnt; 212 } 213 214 /* 215 * Attempt to automount the referral 216 */ 217 struct vfsmount *cifs_dfs_d_automount(struct path *path) 218 { 219 struct vfsmount *newmnt; 220 221 cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry); 222 223 newmnt = cifs_dfs_do_automount(path); 224 if (IS_ERR(newmnt)) { 225 cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); 226 return newmnt; 227 } 228 229 mntget(newmnt); /* prevent immediate expiration */ 230 mnt_set_expiry(newmnt, &cifs_dfs_automount_list); 231 schedule_delayed_work(&cifs_dfs_automount_task, 232 cifs_dfs_mountpoint_expiry_timeout); 233 cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); 234 return newmnt; 235 } 236 237 const struct inode_operations cifs_dfs_referral_inode_operations = { 238 }; 239