xref: /kernel/linux/linux-5.10/fs/nfs/getroot.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* getroot.c: get the root dentry for an NFS mount
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/time.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/mm.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/stat.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/unistd.h>
188c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h>
198c2ecf20Sopenharmony_ci#include <linux/sunrpc/stats.h>
208c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h>
218c2ecf20Sopenharmony_ci#include <linux/nfs_mount.h>
228c2ecf20Sopenharmony_ci#include <linux/lockd/bind.h>
238c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
248c2ecf20Sopenharmony_ci#include <linux/mount.h>
258c2ecf20Sopenharmony_ci#include <linux/vfs.h>
268c2ecf20Sopenharmony_ci#include <linux/namei.h>
278c2ecf20Sopenharmony_ci#include <linux/security.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "internal.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_CLIENT
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Set the superblock root dentry.
378c2ecf20Sopenharmony_ci * Note that this function frees the inode in case of error.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	/* The mntroot acts as the dummy root dentry for this superblock */
428c2ecf20Sopenharmony_ci	if (sb->s_root == NULL) {
438c2ecf20Sopenharmony_ci		sb->s_root = d_make_root(inode);
448c2ecf20Sopenharmony_ci		if (sb->s_root == NULL)
458c2ecf20Sopenharmony_ci			return -ENOMEM;
468c2ecf20Sopenharmony_ci		ihold(inode);
478c2ecf20Sopenharmony_ci		/*
488c2ecf20Sopenharmony_ci		 * Ensure that this dentry is invisible to d_find_alias().
498c2ecf20Sopenharmony_ci		 * Otherwise, it may be spliced into the tree by
508c2ecf20Sopenharmony_ci		 * d_splice_alias if a parent directory from the same
518c2ecf20Sopenharmony_ci		 * filesystem gets mounted at a later time.
528c2ecf20Sopenharmony_ci		 * This again causes shrink_dcache_for_umount_subtree() to
538c2ecf20Sopenharmony_ci		 * Oops, since the test for IS_ROOT() will fail.
548c2ecf20Sopenharmony_ci		 */
558c2ecf20Sopenharmony_ci		spin_lock(&d_inode(sb->s_root)->i_lock);
568c2ecf20Sopenharmony_ci		spin_lock(&sb->s_root->d_lock);
578c2ecf20Sopenharmony_ci		hlist_del_init(&sb->s_root->d_u.d_alias);
588c2ecf20Sopenharmony_ci		spin_unlock(&sb->s_root->d_lock);
598c2ecf20Sopenharmony_ci		spin_unlock(&d_inode(sb->s_root)->i_lock);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * get an NFS2/NFS3 root dentry from the root filehandle
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ciint nfs_get_root(struct super_block *s, struct fs_context *fc)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
708c2ecf20Sopenharmony_ci	struct nfs_server *server = NFS_SB(s);
718c2ecf20Sopenharmony_ci	struct nfs_fsinfo fsinfo;
728c2ecf20Sopenharmony_ci	struct dentry *root;
738c2ecf20Sopenharmony_ci	struct inode *inode;
748c2ecf20Sopenharmony_ci	char *name;
758c2ecf20Sopenharmony_ci	int error = -ENOMEM;
768c2ecf20Sopenharmony_ci	unsigned long kflags = 0, kflags_out = 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	name = kstrdup(fc->source, GFP_KERNEL);
798c2ecf20Sopenharmony_ci	if (!name)
808c2ecf20Sopenharmony_ci		goto out;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* get the actual root for this mount */
838c2ecf20Sopenharmony_ci	fsinfo.fattr = nfs_alloc_fattr();
848c2ecf20Sopenharmony_ci	if (fsinfo.fattr == NULL)
858c2ecf20Sopenharmony_ci		goto out_name;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	fsinfo.fattr->label = nfs4_label_alloc(server, GFP_KERNEL);
888c2ecf20Sopenharmony_ci	if (IS_ERR(fsinfo.fattr->label))
898c2ecf20Sopenharmony_ci		goto out_fattr;
908c2ecf20Sopenharmony_ci	error = server->nfs_client->rpc_ops->getroot(server, ctx->mntfh, &fsinfo);
918c2ecf20Sopenharmony_ci	if (error < 0) {
928c2ecf20Sopenharmony_ci		dprintk("nfs_get_root: getattr error = %d\n", -error);
938c2ecf20Sopenharmony_ci		nfs_errorf(fc, "NFS: Couldn't getattr on root");
948c2ecf20Sopenharmony_ci		goto out_label;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	inode = nfs_fhget(s, ctx->mntfh, fsinfo.fattr, NULL);
988c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
998c2ecf20Sopenharmony_ci		dprintk("nfs_get_root: get root inode failed\n");
1008c2ecf20Sopenharmony_ci		error = PTR_ERR(inode);
1018c2ecf20Sopenharmony_ci		nfs_errorf(fc, "NFS: Couldn't get root inode");
1028c2ecf20Sopenharmony_ci		goto out_label;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	error = nfs_superblock_set_dummy_root(s, inode);
1068c2ecf20Sopenharmony_ci	if (error != 0)
1078c2ecf20Sopenharmony_ci		goto out_label;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* root dentries normally start off anonymous and get spliced in later
1108c2ecf20Sopenharmony_ci	 * if the dentry tree reaches them; however if the dentry already
1118c2ecf20Sopenharmony_ci	 * exists, we'll pick it up at this point and use it as the root
1128c2ecf20Sopenharmony_ci	 */
1138c2ecf20Sopenharmony_ci	root = d_obtain_root(inode);
1148c2ecf20Sopenharmony_ci	if (IS_ERR(root)) {
1158c2ecf20Sopenharmony_ci		dprintk("nfs_get_root: get root dentry failed\n");
1168c2ecf20Sopenharmony_ci		error = PTR_ERR(root);
1178c2ecf20Sopenharmony_ci		nfs_errorf(fc, "NFS: Couldn't get root dentry");
1188c2ecf20Sopenharmony_ci		goto out_label;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	security_d_instantiate(root, inode);
1228c2ecf20Sopenharmony_ci	spin_lock(&root->d_lock);
1238c2ecf20Sopenharmony_ci	if (IS_ROOT(root) && !root->d_fsdata &&
1248c2ecf20Sopenharmony_ci	    !(root->d_flags & DCACHE_NFSFS_RENAMED)) {
1258c2ecf20Sopenharmony_ci		root->d_fsdata = name;
1268c2ecf20Sopenharmony_ci		name = NULL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci	spin_unlock(&root->d_lock);
1298c2ecf20Sopenharmony_ci	fc->root = root;
1308c2ecf20Sopenharmony_ci	if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
1318c2ecf20Sopenharmony_ci		kflags |= SECURITY_LSM_NATIVE_LABELS;
1328c2ecf20Sopenharmony_ci	if (ctx->clone_data.sb) {
1338c2ecf20Sopenharmony_ci		if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
1348c2ecf20Sopenharmony_ci			error = -ESTALE;
1358c2ecf20Sopenharmony_ci			goto error_splat_root;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci		/* clone lsm security options from the parent to the new sb */
1388c2ecf20Sopenharmony_ci		error = security_sb_clone_mnt_opts(ctx->clone_data.sb,
1398c2ecf20Sopenharmony_ci						   s, kflags, &kflags_out);
1408c2ecf20Sopenharmony_ci	} else {
1418c2ecf20Sopenharmony_ci		error = security_sb_set_mnt_opts(s, fc->security,
1428c2ecf20Sopenharmony_ci							kflags, &kflags_out);
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci	if (error)
1458c2ecf20Sopenharmony_ci		goto error_splat_root;
1468c2ecf20Sopenharmony_ci	if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
1478c2ecf20Sopenharmony_ci		!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
1488c2ecf20Sopenharmony_ci		NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	nfs_setsecurity(inode, fsinfo.fattr, fsinfo.fattr->label);
1518c2ecf20Sopenharmony_ci	error = 0;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciout_label:
1548c2ecf20Sopenharmony_ci	nfs4_label_free(fsinfo.fattr->label);
1558c2ecf20Sopenharmony_ciout_fattr:
1568c2ecf20Sopenharmony_ci	nfs_free_fattr(fsinfo.fattr);
1578c2ecf20Sopenharmony_ciout_name:
1588c2ecf20Sopenharmony_ci	kfree(name);
1598c2ecf20Sopenharmony_ciout:
1608c2ecf20Sopenharmony_ci	return error;
1618c2ecf20Sopenharmony_cierror_splat_root:
1628c2ecf20Sopenharmony_ci	dput(fc->root);
1638c2ecf20Sopenharmony_ci	fc->root = NULL;
1648c2ecf20Sopenharmony_ci	goto out_label;
1658c2ecf20Sopenharmony_ci}
166