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