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 static bool is_dfs_mount(struct dentry *dentry) 121 { 122 struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); 123 struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 124 bool ret; 125 126 spin_lock(&tcon->tc_lock); 127 ret = !!tcon->origin_fullpath; 128 spin_unlock(&tcon->tc_lock); 129 return ret; 130 } 131 132 /* Return full path out of a dentry set for automount */ 133 static char *automount_fullpath(struct dentry *dentry, void *page) 134 { 135 struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); 136 struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 137 size_t len; 138 char *s; 139 140 spin_lock(&tcon->tc_lock); 141 if (!tcon->origin_fullpath) { 142 spin_unlock(&tcon->tc_lock); 143 return build_path_from_dentry_optional_prefix(dentry, 144 page, 145 true); 146 } 147 spin_unlock(&tcon->tc_lock); 148 149 s = dentry_path_raw(dentry, page, PATH_MAX); 150 if (IS_ERR(s)) 151 return s; 152 /* for root, we want "" */ 153 if (!s[1]) 154 s++; 155 156 spin_lock(&tcon->tc_lock); 157 len = strlen(tcon->origin_fullpath); 158 if (s < (char *)page + len) { 159 spin_unlock(&tcon->tc_lock); 160 return ERR_PTR(-ENAMETOOLONG); 161 } 162 163 s -= len; 164 memcpy(s, tcon->origin_fullpath, len); 165 spin_unlock(&tcon->tc_lock); 166 convert_delimiter(s, '/'); 167 168 return s; 169 } 170 171 /* 172 * Create a vfsmount that we can automount 173 */ 174 static struct vfsmount *cifs_do_automount(struct path *path) 175 { 176 int rc; 177 struct dentry *mntpt = path->dentry; 178 struct fs_context *fc; 179 void *page = NULL; 180 struct smb3_fs_context *ctx, *cur_ctx; 181 struct smb3_fs_context tmp; 182 char *full_path; 183 struct vfsmount *mnt; 184 185 if (IS_ROOT(mntpt)) 186 return ERR_PTR(-ESTALE); 187 188 cur_ctx = CIFS_SB(mntpt->d_sb)->ctx; 189 190 fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt); 191 if (IS_ERR(fc)) 192 return ERR_CAST(fc); 193 194 ctx = smb3_fc2context(fc); 195 196 page = alloc_dentry_path(); 197 full_path = automount_fullpath(mntpt, page); 198 if (IS_ERR(full_path)) { 199 mnt = ERR_CAST(full_path); 200 goto out; 201 } 202 203 tmp = *cur_ctx; 204 tmp.source = NULL; 205 tmp.leaf_fullpath = NULL; 206 tmp.UNC = tmp.prepath = NULL; 207 tmp.dfs_root_ses = NULL; 208 209 rc = smb3_fs_context_dup(ctx, &tmp); 210 if (rc) { 211 mnt = ERR_PTR(rc); 212 goto out; 213 } 214 215 rc = smb3_parse_devname(full_path, ctx); 216 if (rc) { 217 mnt = ERR_PTR(rc); 218 goto out; 219 } 220 221 ctx->source = smb3_fs_context_fullpath(ctx, '/'); 222 if (IS_ERR(ctx->source)) { 223 mnt = ERR_CAST(ctx->source); 224 ctx->source = NULL; 225 goto out; 226 } 227 ctx->dfs_automount = is_dfs_mount(mntpt); 228 cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n", 229 __func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount); 230 231 mnt = fc_mount(fc); 232 out: 233 put_fs_context(fc); 234 free_dentry_path(page); 235 return mnt; 236 } 237 238 /* 239 * Attempt to automount the referral 240 */ 241 struct vfsmount *cifs_d_automount(struct path *path) 242 { 243 struct vfsmount *newmnt; 244 245 cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry); 246 247 newmnt = cifs_do_automount(path); 248 if (IS_ERR(newmnt)) { 249 cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); 250 return newmnt; 251 } 252 253 mntget(newmnt); /* prevent immediate expiration */ 254 mnt_set_expiry(newmnt, &cifs_automount_list); 255 schedule_delayed_work(&cifs_automount_task, 256 cifs_mountpoint_expiry_timeout); 257 cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); 258 return newmnt; 259 } 260 261 const struct inode_operations cifs_namespace_inode_operations = { 262 }; 263