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