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