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 if (unlikely(!page)) 150 return ERR_PTR(-ENOMEM); 151 152 s = dentry_path_raw(dentry, page, PATH_MAX); 153 if (IS_ERR(s)) 154 return s; 155 /* for root, we want "" */ 156 if (!s[1]) 157 s++; 158 159 spin_lock(&tcon->tc_lock); 160 len = strlen(tcon->origin_fullpath); 161 if (s < (char *)page + len) { 162 spin_unlock(&tcon->tc_lock); 163 return ERR_PTR(-ENAMETOOLONG); 164 } 165 166 s -= len; 167 memcpy(s, tcon->origin_fullpath, len); 168 spin_unlock(&tcon->tc_lock); 169 convert_delimiter(s, '/'); 170 171 return s; 172 } 173 174 static void fs_context_set_ids(struct smb3_fs_context *ctx) 175 { 176 kuid_t uid = current_fsuid(); 177 kgid_t gid = current_fsgid(); 178 179 if (ctx->multiuser) { 180 if (!ctx->uid_specified) 181 ctx->linux_uid = uid; 182 if (!ctx->gid_specified) 183 ctx->linux_gid = gid; 184 } 185 if (!ctx->cruid_specified) 186 ctx->cred_uid = uid; 187 } 188 189 /* 190 * Create a vfsmount that we can automount 191 */ 192 static struct vfsmount *cifs_do_automount(struct path *path) 193 { 194 int rc; 195 struct dentry *mntpt = path->dentry; 196 struct fs_context *fc; 197 void *page = NULL; 198 struct smb3_fs_context *ctx, *cur_ctx; 199 struct smb3_fs_context tmp; 200 char *full_path; 201 struct vfsmount *mnt; 202 struct cifs_sb_info *mntpt_sb; 203 struct cifs_ses *ses; 204 205 if (IS_ROOT(mntpt)) 206 return ERR_PTR(-ESTALE); 207 208 mntpt_sb = CIFS_SB(mntpt->d_sb); 209 ses = cifs_sb_master_tcon(mntpt_sb)->ses; 210 cur_ctx = mntpt_sb->ctx; 211 212 /* 213 * At this point, the root session should be in the mntpt sb. We should 214 * bring the sb context passwords in sync with the root session's 215 * passwords. This would help prevent unnecessary retries and password 216 * swaps for automounts. 217 */ 218 mutex_lock(&ses->session_mutex); 219 rc = smb3_sync_session_ctx_passwords(mntpt_sb, ses); 220 mutex_unlock(&ses->session_mutex); 221 222 if (rc) 223 return ERR_PTR(rc); 224 225 fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt); 226 if (IS_ERR(fc)) 227 return ERR_CAST(fc); 228 229 ctx = smb3_fc2context(fc); 230 231 page = alloc_dentry_path(); 232 full_path = automount_fullpath(mntpt, page); 233 if (IS_ERR(full_path)) { 234 mnt = ERR_CAST(full_path); 235 goto out; 236 } 237 238 tmp = *cur_ctx; 239 tmp.source = NULL; 240 tmp.leaf_fullpath = NULL; 241 tmp.UNC = tmp.prepath = NULL; 242 tmp.dfs_root_ses = NULL; 243 fs_context_set_ids(&tmp); 244 245 rc = smb3_fs_context_dup(ctx, &tmp); 246 if (rc) { 247 mnt = ERR_PTR(rc); 248 goto out; 249 } 250 251 rc = smb3_parse_devname(full_path, ctx); 252 if (rc) { 253 mnt = ERR_PTR(rc); 254 goto out; 255 } 256 257 ctx->source = smb3_fs_context_fullpath(ctx, '/'); 258 if (IS_ERR(ctx->source)) { 259 mnt = ERR_CAST(ctx->source); 260 ctx->source = NULL; 261 goto out; 262 } 263 ctx->dfs_automount = ctx->dfs_conn = is_dfs_mount(mntpt); 264 cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n", 265 __func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount); 266 267 mnt = fc_mount(fc); 268 out: 269 put_fs_context(fc); 270 free_dentry_path(page); 271 return mnt; 272 } 273 274 /* 275 * Attempt to automount the referral 276 */ 277 struct vfsmount *cifs_d_automount(struct path *path) 278 { 279 struct vfsmount *newmnt; 280 281 cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry); 282 283 newmnt = cifs_do_automount(path); 284 if (IS_ERR(newmnt)) { 285 cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); 286 return newmnt; 287 } 288 289 mnt_set_expiry(newmnt, &cifs_automount_list); 290 schedule_delayed_work(&cifs_automount_task, 291 cifs_mountpoint_expiry_timeout); 292 cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); 293 return newmnt; 294 } 295 296 const struct inode_operations cifs_namespace_inode_operations = { 297 }; 298