1 // SPDX-License-Identifier: GPL-2.0-or-later << 2 /* getroot.c: get the root dentry for an NFS m 1 /* getroot.c: get the root dentry for an NFS mount 3 * 2 * 4 * Copyright (C) 2006 Red Hat, Inc. All Rights 3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 5 * Written by David Howells (dhowells@redhat.c 4 * Written by David Howells (dhowells@redhat.com) >> 5 * >> 6 * This program is free software; you can redistribute it and/or >> 7 * modify it under the terms of the GNU General Public License >> 8 * as published by the Free Software Foundation; either version >> 9 * 2 of the License, or (at your option) any later version. 6 */ 10 */ 7 11 8 #include <linux/module.h> 12 #include <linux/module.h> 9 #include <linux/init.h> 13 #include <linux/init.h> 10 14 11 #include <linux/time.h> 15 #include <linux/time.h> 12 #include <linux/kernel.h> 16 #include <linux/kernel.h> 13 #include <linux/mm.h> 17 #include <linux/mm.h> 14 #include <linux/string.h> 18 #include <linux/string.h> 15 #include <linux/stat.h> 19 #include <linux/stat.h> 16 #include <linux/errno.h> 20 #include <linux/errno.h> 17 #include <linux/unistd.h> 21 #include <linux/unistd.h> 18 #include <linux/sunrpc/clnt.h> 22 #include <linux/sunrpc/clnt.h> 19 #include <linux/sunrpc/stats.h> 23 #include <linux/sunrpc/stats.h> 20 #include <linux/nfs_fs.h> 24 #include <linux/nfs_fs.h> 21 #include <linux/nfs_mount.h> 25 #include <linux/nfs_mount.h> >> 26 #include <linux/nfs4_mount.h> 22 #include <linux/lockd/bind.h> 27 #include <linux/lockd/bind.h> 23 #include <linux/seq_file.h> 28 #include <linux/seq_file.h> 24 #include <linux/mount.h> 29 #include <linux/mount.h> >> 30 #include <linux/nfs_idmap.h> 25 #include <linux/vfs.h> 31 #include <linux/vfs.h> 26 #include <linux/namei.h> 32 #include <linux/namei.h> 27 #include <linux/security.h> 33 #include <linux/security.h> 28 34 29 #include <linux/uaccess.h> !! 35 #include <asm/system.h> >> 36 #include <asm/uaccess.h> 30 37 >> 38 #include "nfs4_fs.h" >> 39 #include "delegation.h" 31 #include "internal.h" 40 #include "internal.h" 32 41 33 #define NFSDBG_FACILITY NFSDBG_CLIENT 42 #define NFSDBG_FACILITY NFSDBG_CLIENT 34 43 35 /* 44 /* 36 * Set the superblock root dentry. 45 * Set the superblock root dentry. 37 * Note that this function frees the inode in 46 * Note that this function frees the inode in case of error. 38 */ 47 */ 39 static int nfs_superblock_set_dummy_root(struc 48 static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode) 40 { 49 { 41 /* The mntroot acts as the dummy root 50 /* The mntroot acts as the dummy root dentry for this superblock */ 42 if (sb->s_root == NULL) { 51 if (sb->s_root == NULL) { 43 sb->s_root = d_make_root(inode !! 52 sb->s_root = d_alloc_root(inode); 44 if (sb->s_root == NULL) !! 53 if (sb->s_root == NULL) { >> 54 iput(inode); 45 return -ENOMEM; 55 return -ENOMEM; 46 ihold(inode); !! 56 } >> 57 /* Circumvent igrab(): we know the inode is not being freed */ >> 58 atomic_inc(&inode->i_count); 47 /* 59 /* 48 * Ensure that this dentry is 60 * Ensure that this dentry is invisible to d_find_alias(). 49 * Otherwise, it may be splice 61 * Otherwise, it may be spliced into the tree by 50 * d_splice_alias if a parent !! 62 * d_materialise_unique if a parent directory from the same 51 * filesystem gets mounted at 63 * filesystem gets mounted at a later time. 52 * This again causes shrink_dc 64 * This again causes shrink_dcache_for_umount_subtree() to 53 * Oops, since the test for IS 65 * Oops, since the test for IS_ROOT() will fail. 54 */ 66 */ 55 spin_lock(&d_inode(sb->s_root) !! 67 spin_lock(&dcache_lock); 56 spin_lock(&sb->s_root->d_lock) !! 68 list_del_init(&sb->s_root->d_alias); 57 hlist_del_init(&sb->s_root->d_ !! 69 spin_unlock(&dcache_lock); 58 spin_unlock(&sb->s_root->d_loc << 59 spin_unlock(&d_inode(sb->s_roo << 60 } 70 } 61 return 0; 71 return 0; 62 } 72 } 63 73 64 /* 74 /* 65 * get an NFS2/NFS3 root dentry from the root 75 * get an NFS2/NFS3 root dentry from the root filehandle 66 */ 76 */ 67 int nfs_get_root(struct super_block *s, struct !! 77 struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) 68 { 78 { 69 struct nfs_fs_context *ctx = nfs_fc2co !! 79 struct nfs_server *server = NFS_SB(sb); 70 struct nfs_server *server = NFS_SB(s), << 71 struct nfs_fsinfo fsinfo; 80 struct nfs_fsinfo fsinfo; 72 struct dentry *root; !! 81 struct nfs_fattr fattr; >> 82 struct dentry *mntroot; 73 struct inode *inode; 83 struct inode *inode; 74 char *name; !! 84 int error; 75 int error = -ENOMEM; << 76 unsigned long kflags = 0, kflags_out = << 77 << 78 name = kstrdup(fc->source, GFP_KERNEL) << 79 if (!name) << 80 goto out; << 81 85 82 /* get the actual root for this mount 86 /* get the actual root for this mount */ 83 fsinfo.fattr = nfs_alloc_fattr_with_la !! 87 fsinfo.fattr = &fattr; 84 if (fsinfo.fattr == NULL) << 85 goto out_name; << 86 88 87 error = server->nfs_client->rpc_ops->g !! 89 error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 88 if (error < 0) { 90 if (error < 0) { 89 dprintk("nfs_get_root: getattr 91 dprintk("nfs_get_root: getattr error = %d\n", -error); 90 nfs_errorf(fc, "NFS: Couldn't !! 92 return ERR_PTR(error); 91 goto out_fattr; << 92 } 93 } 93 94 94 inode = nfs_fhget(s, ctx->mntfh, fsinf !! 95 inode = nfs_fhget(sb, mntfh, fsinfo.fattr); 95 if (IS_ERR(inode)) { 96 if (IS_ERR(inode)) { 96 dprintk("nfs_get_root: get roo 97 dprintk("nfs_get_root: get root inode failed\n"); 97 error = PTR_ERR(inode); !! 98 return ERR_CAST(inode); 98 nfs_errorf(fc, "NFS: Couldn't << 99 goto out_fattr; << 100 } 99 } 101 100 102 error = nfs_superblock_set_dummy_root( !! 101 error = nfs_superblock_set_dummy_root(sb, inode); 103 if (error != 0) 102 if (error != 0) 104 goto out_fattr; !! 103 return ERR_PTR(error); 105 104 106 /* root dentries normally start off an 105 /* root dentries normally start off anonymous and get spliced in later 107 * if the dentry tree reaches them; ho 106 * if the dentry tree reaches them; however if the dentry already 108 * exists, we'll pick it up at this po 107 * exists, we'll pick it up at this point and use it as the root 109 */ 108 */ 110 root = d_obtain_root(inode); !! 109 mntroot = d_obtain_alias(inode); 111 if (IS_ERR(root)) { !! 110 if (IS_ERR(mntroot)) { 112 dprintk("nfs_get_root: get roo 111 dprintk("nfs_get_root: get root dentry failed\n"); 113 error = PTR_ERR(root); !! 112 return mntroot; 114 nfs_errorf(fc, "NFS: Couldn't !! 113 } 115 goto out_fattr; !! 114 116 } !! 115 security_d_instantiate(mntroot, inode); 117 !! 116 118 security_d_instantiate(root, inode); !! 117 if (!mntroot->d_op) 119 spin_lock(&root->d_lock); !! 118 mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; 120 if (IS_ROOT(root) && !root->d_fsdata & !! 119 121 !(root->d_flags & DCACHE_NFSFS_REN !! 120 return mntroot; 122 root->d_fsdata = name; << 123 name = NULL; << 124 } << 125 spin_unlock(&root->d_lock); << 126 fc->root = root; << 127 if (server->caps & NFS_CAP_SECURITY_LA << 128 kflags |= SECURITY_LSM_NATIVE_ << 129 if (ctx->clone_data.sb) { << 130 if (d_inode(fc->root)->i_fop ! << 131 error = -ESTALE; << 132 goto error_splat_root; << 133 } << 134 /* clone lsm security options << 135 error = security_sb_clone_mnt_ << 136 << 137 if (error) << 138 goto error_splat_root; << 139 clone_server = NFS_SB(ctx->clo << 140 server->has_sec_mnt_opts = clo << 141 } else { << 142 error = security_sb_set_mnt_op << 143 << 144 } << 145 if (error) << 146 goto error_splat_root; << 147 if (server->caps & NFS_CAP_SECURITY_LA << 148 !(kflags_out & SECURITY_LSM_NA << 149 server->caps &= ~NFS_CAP_SECUR << 150 << 151 nfs_setsecurity(inode, fsinfo.fattr); << 152 error = 0; << 153 << 154 out_fattr: << 155 nfs_free_fattr(fsinfo.fattr); << 156 out_name: << 157 kfree(name); << 158 out: << 159 return error; << 160 error_splat_root: << 161 dput(fc->root); << 162 fc->root = NULL; << 163 goto out_fattr; << 164 } 121 } >> 122 >> 123 #ifdef CONFIG_NFS_V4 >> 124 >> 125 /* >> 126 * Do a simple pathwalk from the root FH of the server to the nominated target >> 127 * of the mountpoint >> 128 * - give error on symlinks >> 129 * - give error on ".." occurring in the path >> 130 * - follow traversals >> 131 */ >> 132 int nfs4_path_walk(struct nfs_server *server, >> 133 struct nfs_fh *mntfh, >> 134 const char *path) >> 135 { >> 136 struct nfs_fsinfo fsinfo; >> 137 struct nfs_fattr fattr; >> 138 struct nfs_fh lastfh; >> 139 struct qstr name; >> 140 int ret; >> 141 >> 142 dprintk("--> nfs4_path_walk(,,%s)\n", path); >> 143 >> 144 fsinfo.fattr = &fattr; >> 145 nfs_fattr_init(&fattr); >> 146 >> 147 /* Eat leading slashes */ >> 148 while (*path == '/') >> 149 path++; >> 150 >> 151 /* Start by getting the root filehandle from the server */ >> 152 ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); >> 153 if (ret < 0) { >> 154 dprintk("nfs4_get_root: getroot error = %d\n", -ret); >> 155 return ret; >> 156 } >> 157 >> 158 if (!S_ISDIR(fattr.mode)) { >> 159 printk(KERN_ERR "nfs4_get_root:" >> 160 " getroot encountered non-directory\n"); >> 161 return -ENOTDIR; >> 162 } >> 163 >> 164 /* FIXME: It is quite valid for the server to return a referral here */ >> 165 if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { >> 166 printk(KERN_ERR "nfs4_get_root:" >> 167 " getroot obtained referral\n"); >> 168 return -EREMOTE; >> 169 } >> 170 >> 171 next_component: >> 172 dprintk("Next: %s\n", path); >> 173 >> 174 /* extract the next bit of the path */ >> 175 if (!*path) >> 176 goto path_walk_complete; >> 177 >> 178 name.name = path; >> 179 while (*path && *path != '/') >> 180 path++; >> 181 name.len = path - (const char *) name.name; >> 182 >> 183 if (name.len > NFS4_MAXNAMLEN) >> 184 return -ENAMETOOLONG; >> 185 >> 186 eat_dot_dir: >> 187 while (*path == '/') >> 188 path++; >> 189 >> 190 if (path[0] == '.' && (path[1] == '/' || !path[1])) { >> 191 path += 2; >> 192 goto eat_dot_dir; >> 193 } >> 194 >> 195 /* FIXME: Why shouldn't the user be able to use ".." in the path? */ >> 196 if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) >> 197 ) { >> 198 printk(KERN_ERR "nfs4_get_root:" >> 199 " Mount path contains reference to \"..\"\n"); >> 200 return -EINVAL; >> 201 } >> 202 >> 203 /* lookup the next FH in the sequence */ >> 204 memcpy(&lastfh, mntfh, sizeof(lastfh)); >> 205 >> 206 dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path); >> 207 >> 208 ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name, >> 209 mntfh, &fattr); >> 210 if (ret < 0) { >> 211 dprintk("nfs4_get_root: getroot error = %d\n", -ret); >> 212 return ret; >> 213 } >> 214 >> 215 if (!S_ISDIR(fattr.mode)) { >> 216 printk(KERN_ERR "nfs4_get_root:" >> 217 " lookupfh encountered non-directory\n"); >> 218 return -ENOTDIR; >> 219 } >> 220 >> 221 /* FIXME: Referrals are quite valid here too */ >> 222 if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { >> 223 printk(KERN_ERR "nfs4_get_root:" >> 224 " lookupfh obtained referral\n"); >> 225 return -EREMOTE; >> 226 } >> 227 >> 228 goto next_component; >> 229 >> 230 path_walk_complete: >> 231 memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); >> 232 dprintk("<-- nfs4_path_walk() = 0\n"); >> 233 return 0; >> 234 } >> 235 >> 236 /* >> 237 * get an NFS4 root dentry from the root filehandle >> 238 */ >> 239 struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) >> 240 { >> 241 struct nfs_server *server = NFS_SB(sb); >> 242 struct nfs_fattr fattr; >> 243 struct dentry *mntroot; >> 244 struct inode *inode; >> 245 int error; >> 246 >> 247 dprintk("--> nfs4_get_root()\n"); >> 248 >> 249 /* get the info about the server and filesystem */ >> 250 error = nfs4_server_capabilities(server, mntfh); >> 251 if (error < 0) { >> 252 dprintk("nfs_get_root: getcaps error = %d\n", >> 253 -error); >> 254 return ERR_PTR(error); >> 255 } >> 256 >> 257 /* get the actual root for this mount */ >> 258 error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); >> 259 if (error < 0) { >> 260 dprintk("nfs_get_root: getattr error = %d\n", -error); >> 261 return ERR_PTR(error); >> 262 } >> 263 >> 264 inode = nfs_fhget(sb, mntfh, &fattr); >> 265 if (IS_ERR(inode)) { >> 266 dprintk("nfs_get_root: get root inode failed\n"); >> 267 return ERR_CAST(inode); >> 268 } >> 269 >> 270 error = nfs_superblock_set_dummy_root(sb, inode); >> 271 if (error != 0) >> 272 return ERR_PTR(error); >> 273 >> 274 /* root dentries normally start off anonymous and get spliced in later >> 275 * if the dentry tree reaches them; however if the dentry already >> 276 * exists, we'll pick it up at this point and use it as the root >> 277 */ >> 278 mntroot = d_obtain_alias(inode); >> 279 if (IS_ERR(mntroot)) { >> 280 dprintk("nfs_get_root: get root dentry failed\n"); >> 281 return mntroot; >> 282 } >> 283 >> 284 security_d_instantiate(mntroot, inode); >> 285 >> 286 if (!mntroot->d_op) >> 287 mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; >> 288 >> 289 dprintk("<-- nfs4_get_root()\n"); >> 290 return mntroot; >> 291 } >> 292 >> 293 #endif /* CONFIG_NFS_V4 */ 165 294
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.