Thomas Gleixner | 2874c5f | 2019-05-27 08:55:01 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 2 | /* getroot.c: get the root dentry for an NFS mount |
| 3 | * |
| 4 | * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. |
| 5 | * Written by David Howells (dhowells@redhat.com) |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 6 | */ |
| 7 | |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 8 | #include <linux/module.h> |
| 9 | #include <linux/init.h> |
| 10 | |
| 11 | #include <linux/time.h> |
| 12 | #include <linux/kernel.h> |
| 13 | #include <linux/mm.h> |
| 14 | #include <linux/string.h> |
| 15 | #include <linux/stat.h> |
| 16 | #include <linux/errno.h> |
| 17 | #include <linux/unistd.h> |
| 18 | #include <linux/sunrpc/clnt.h> |
| 19 | #include <linux/sunrpc/stats.h> |
| 20 | #include <linux/nfs_fs.h> |
| 21 | #include <linux/nfs_mount.h> |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 22 | #include <linux/lockd/bind.h> |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 23 | #include <linux/seq_file.h> |
| 24 | #include <linux/mount.h> |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 25 | #include <linux/vfs.h> |
| 26 | #include <linux/namei.h> |
David Howells | 738a351 | 2006-07-30 14:58:27 -0400 | [diff] [blame] | 27 | #include <linux/security.h> |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 28 | |
Linus Torvalds | 7c0f6ba | 2016-12-24 11:46:01 -0800 | [diff] [blame] | 29 | #include <linux/uaccess.h> |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 30 | |
Stanislav Kinsbursky | 4e437e9 | 2012-10-02 14:19:14 +0400 | [diff] [blame] | 31 | #include "internal.h" |
| 32 | |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 33 | #define NFSDBG_FACILITY NFSDBG_CLIENT |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 34 | |
| 35 | /* |
Trond Myklebust | b09b941 | 2007-10-25 13:56:10 -0400 | [diff] [blame] | 36 | * Set the superblock root dentry. |
| 37 | * Note that this function frees the inode in case of error. |
| 38 | */ |
| 39 | static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode) |
| 40 | { |
| 41 | /* The mntroot acts as the dummy root dentry for this superblock */ |
| 42 | if (sb->s_root == NULL) { |
Al Viro | 48fde70 | 2012-01-08 22:15:13 -0500 | [diff] [blame] | 43 | sb->s_root = d_make_root(inode); |
| 44 | if (sb->s_root == NULL) |
Trond Myklebust | b09b941 | 2007-10-25 13:56:10 -0400 | [diff] [blame] | 45 | return -ENOMEM; |
Al Viro | 7de9c6ee | 2010-10-23 11:11:40 -0400 | [diff] [blame] | 46 | ihold(inode); |
Trond Myklebust | a10db50 | 2007-12-12 11:12:15 -0500 | [diff] [blame] | 47 | /* |
| 48 | * Ensure that this dentry is invisible to d_find_alias(). |
| 49 | * Otherwise, it may be spliced into the tree by |
Al Viro | 41d28bc | 2014-10-12 22:24:21 -0400 | [diff] [blame] | 50 | * d_splice_alias if a parent directory from the same |
Trond Myklebust | a10db50 | 2007-12-12 11:12:15 -0500 | [diff] [blame] | 51 | * filesystem gets mounted at a later time. |
| 52 | * This again causes shrink_dcache_for_umount_subtree() to |
| 53 | * Oops, since the test for IS_ROOT() will fail. |
| 54 | */ |
David Howells | 2b0143b | 2015-03-17 22:25:59 +0000 | [diff] [blame] | 55 | spin_lock(&d_inode(sb->s_root)->i_lock); |
Nick Piggin | b23fb0a | 2011-01-07 17:49:35 +1100 | [diff] [blame] | 56 | spin_lock(&sb->s_root->d_lock); |
Al Viro | 946e51f | 2014-10-26 19:19:16 -0400 | [diff] [blame] | 57 | hlist_del_init(&sb->s_root->d_u.d_alias); |
Nick Piggin | b23fb0a | 2011-01-07 17:49:35 +1100 | [diff] [blame] | 58 | spin_unlock(&sb->s_root->d_lock); |
David Howells | 2b0143b | 2015-03-17 22:25:59 +0000 | [diff] [blame] | 59 | spin_unlock(&d_inode(sb->s_root)->i_lock); |
Trond Myklebust | b09b941 | 2007-10-25 13:56:10 -0400 | [diff] [blame] | 60 | } |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | /* |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 65 | * get an NFS2/NFS3 root dentry from the root filehandle |
| 66 | */ |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 67 | int nfs_get_root(struct super_block *s, struct fs_context *fc) |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 68 | { |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 69 | struct nfs_fs_context *ctx = nfs_fc2context(fc); |
| 70 | struct nfs_server *server = NFS_SB(s); |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 71 | struct nfs_fsinfo fsinfo; |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 72 | struct dentry *root; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 73 | struct inode *inode; |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 74 | char *name; |
| 75 | int error = -ENOMEM; |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 76 | unsigned long kflags = 0, kflags_out = 0; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 77 | |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 78 | name = kstrdup(fc->source, GFP_KERNEL); |
Al Viro | b1942c5 | 2011-03-16 05:44:14 -0400 | [diff] [blame] | 79 | if (!name) |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 80 | goto out; |
Al Viro | b1942c5 | 2011-03-16 05:44:14 -0400 | [diff] [blame] | 81 | |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 82 | /* get the actual root for this mount */ |
Trond Myklebust | 8bac9db | 2010-04-16 16:22:48 -0400 | [diff] [blame] | 83 | fsinfo.fattr = nfs_alloc_fattr(); |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 84 | if (fsinfo.fattr == NULL) |
| 85 | goto out_name; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 86 | |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 87 | fsinfo.fattr->label = nfs4_label_alloc(server, GFP_KERNEL); |
| 88 | if (IS_ERR(fsinfo.fattr->label)) |
| 89 | goto out_fattr; |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 90 | error = server->nfs_client->rpc_ops->getroot(server, ctx->mntfh, &fsinfo); |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 91 | if (error < 0) { |
| 92 | dprintk("nfs_get_root: getattr error = %d\n", -error); |
Scott Mayhew | ce8866f0 | 2019-12-10 07:31:15 -0500 | [diff] [blame] | 93 | nfs_errorf(fc, "NFS: Couldn't getattr on root"); |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 94 | goto out_label; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 95 | } |
| 96 | |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 97 | inode = nfs_fhget(s, ctx->mntfh, fsinfo.fattr, NULL); |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 98 | if (IS_ERR(inode)) { |
| 99 | dprintk("nfs_get_root: get root inode failed\n"); |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 100 | error = PTR_ERR(inode); |
Scott Mayhew | ce8866f0 | 2019-12-10 07:31:15 -0500 | [diff] [blame] | 101 | nfs_errorf(fc, "NFS: Couldn't get root inode"); |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 102 | goto out_label; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 103 | } |
| 104 | |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 105 | error = nfs_superblock_set_dummy_root(s, inode); |
| 106 | if (error != 0) |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 107 | goto out_label; |
Trond Myklebust | b09b941 | 2007-10-25 13:56:10 -0400 | [diff] [blame] | 108 | |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 109 | /* root dentries normally start off anonymous and get spliced in later |
| 110 | * if the dentry tree reaches them; however if the dentry already |
| 111 | * exists, we'll pick it up at this point and use it as the root |
| 112 | */ |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 113 | root = d_obtain_root(inode); |
| 114 | if (IS_ERR(root)) { |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 115 | dprintk("nfs_get_root: get root dentry failed\n"); |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 116 | error = PTR_ERR(root); |
Scott Mayhew | ce8866f0 | 2019-12-10 07:31:15 -0500 | [diff] [blame] | 117 | nfs_errorf(fc, "NFS: Couldn't get root dentry"); |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 118 | goto out_label; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 119 | } |
| 120 | |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 121 | security_d_instantiate(root, inode); |
| 122 | spin_lock(&root->d_lock); |
| 123 | if (IS_ROOT(root) && !root->d_fsdata && |
| 124 | !(root->d_flags & DCACHE_NFSFS_RENAMED)) { |
| 125 | root->d_fsdata = name; |
Al Viro | b1942c5 | 2011-03-16 05:44:14 -0400 | [diff] [blame] | 126 | name = NULL; |
| 127 | } |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 128 | spin_unlock(&root->d_lock); |
| 129 | fc->root = root; |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 130 | if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL) |
| 131 | kflags |= SECURITY_LSM_NATIVE_LABELS; |
| 132 | if (ctx->clone_data.sb) { |
| 133 | if (d_inode(fc->root)->i_fop != &nfs_dir_operations) { |
| 134 | error = -ESTALE; |
| 135 | goto error_splat_root; |
| 136 | } |
| 137 | /* clone lsm security options from the parent to the new sb */ |
| 138 | error = security_sb_clone_mnt_opts(ctx->clone_data.sb, |
| 139 | s, kflags, &kflags_out); |
| 140 | } else { |
| 141 | error = security_sb_set_mnt_opts(s, fc->security, |
| 142 | kflags, &kflags_out); |
| 143 | } |
| 144 | if (error) |
| 145 | goto error_splat_root; |
| 146 | if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL && |
| 147 | !(kflags_out & SECURITY_LSM_NATIVE_LABELS)) |
| 148 | NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL; |
| 149 | |
| 150 | nfs_setsecurity(inode, fsinfo.fattr, fsinfo.fattr->label); |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 151 | error = 0; |
| 152 | |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 153 | out_label: |
| 154 | nfs4_label_free(fsinfo.fattr->label); |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 155 | out_fattr: |
Trond Myklebust | 8bac9db | 2010-04-16 16:22:48 -0400 | [diff] [blame] | 156 | nfs_free_fattr(fsinfo.fattr); |
Scott Mayhew | 62a55d0 | 2019-12-10 07:31:14 -0500 | [diff] [blame] | 157 | out_name: |
| 158 | kfree(name); |
| 159 | out: |
| 160 | return error; |
Scott Mayhew | 779df6a | 2020-03-03 17:58:37 -0500 | [diff] [blame] | 161 | error_splat_root: |
| 162 | dput(fc->root); |
| 163 | fc->root = NULL; |
| 164 | goto out_label; |
David Howells | 54ceac4 | 2006-08-22 20:06:13 -0400 | [diff] [blame] | 165 | } |