154ceac45SDavid Howells /* getroot.c: get the root dentry for an NFS mount 254ceac45SDavid Howells * 354ceac45SDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 454ceac45SDavid Howells * Written by David Howells (dhowells@redhat.com) 554ceac45SDavid Howells * 654ceac45SDavid Howells * This program is free software; you can redistribute it and/or 754ceac45SDavid Howells * modify it under the terms of the GNU General Public License 854ceac45SDavid Howells * as published by the Free Software Foundation; either version 954ceac45SDavid Howells * 2 of the License, or (at your option) any later version. 1054ceac45SDavid Howells */ 1154ceac45SDavid Howells 1254ceac45SDavid Howells #include <linux/module.h> 1354ceac45SDavid Howells #include <linux/init.h> 1454ceac45SDavid Howells 1554ceac45SDavid Howells #include <linux/time.h> 1654ceac45SDavid Howells #include <linux/kernel.h> 1754ceac45SDavid Howells #include <linux/mm.h> 1854ceac45SDavid Howells #include <linux/string.h> 1954ceac45SDavid Howells #include <linux/stat.h> 2054ceac45SDavid Howells #include <linux/errno.h> 2154ceac45SDavid Howells #include <linux/unistd.h> 2254ceac45SDavid Howells #include <linux/sunrpc/clnt.h> 2354ceac45SDavid Howells #include <linux/sunrpc/stats.h> 2454ceac45SDavid Howells #include <linux/nfs_fs.h> 2554ceac45SDavid Howells #include <linux/nfs_mount.h> 2654ceac45SDavid Howells #include <linux/nfs4_mount.h> 2754ceac45SDavid Howells #include <linux/lockd/bind.h> 2854ceac45SDavid Howells #include <linux/smp_lock.h> 2954ceac45SDavid Howells #include <linux/seq_file.h> 3054ceac45SDavid Howells #include <linux/mount.h> 3154ceac45SDavid Howells #include <linux/nfs_idmap.h> 3254ceac45SDavid Howells #include <linux/vfs.h> 3354ceac45SDavid Howells #include <linux/namei.h> 346b3286edSKirill Korotaev #include <linux/mnt_namespace.h> 35738a3519SDavid Howells #include <linux/security.h> 3654ceac45SDavid Howells 3754ceac45SDavid Howells #include <asm/system.h> 3854ceac45SDavid Howells #include <asm/uaccess.h> 3954ceac45SDavid Howells 4054ceac45SDavid Howells #include "nfs4_fs.h" 4154ceac45SDavid Howells #include "delegation.h" 4254ceac45SDavid Howells #include "internal.h" 4354ceac45SDavid Howells 4454ceac45SDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 4554ceac45SDavid Howells #define NFS_PARANOIA 1 4654ceac45SDavid Howells 4754ceac45SDavid Howells /* 4854ceac45SDavid Howells * get an NFS2/NFS3 root dentry from the root filehandle 4954ceac45SDavid Howells */ 5054ceac45SDavid Howells struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) 5154ceac45SDavid Howells { 5254ceac45SDavid Howells struct nfs_server *server = NFS_SB(sb); 5354ceac45SDavid Howells struct nfs_fsinfo fsinfo; 5454ceac45SDavid Howells struct nfs_fattr fattr; 5554ceac45SDavid Howells struct dentry *mntroot; 5654ceac45SDavid Howells struct inode *inode; 5754ceac45SDavid Howells int error; 5854ceac45SDavid Howells 5954ceac45SDavid Howells /* create a dummy root dentry with dummy inode for this superblock */ 6054ceac45SDavid Howells if (!sb->s_root) { 6154ceac45SDavid Howells struct nfs_fh dummyfh; 6254ceac45SDavid Howells struct dentry *root; 6354ceac45SDavid Howells struct inode *iroot; 6454ceac45SDavid Howells 6554ceac45SDavid Howells memset(&dummyfh, 0, sizeof(dummyfh)); 6654ceac45SDavid Howells memset(&fattr, 0, sizeof(fattr)); 6754ceac45SDavid Howells nfs_fattr_init(&fattr); 6854ceac45SDavid Howells fattr.valid = NFS_ATTR_FATTR; 6954ceac45SDavid Howells fattr.type = NFDIR; 7054ceac45SDavid Howells fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; 7154ceac45SDavid Howells fattr.nlink = 2; 7254ceac45SDavid Howells 7354ceac45SDavid Howells iroot = nfs_fhget(sb, &dummyfh, &fattr); 7454ceac45SDavid Howells if (IS_ERR(iroot)) 7554ceac45SDavid Howells return ERR_PTR(PTR_ERR(iroot)); 7654ceac45SDavid Howells 7754ceac45SDavid Howells root = d_alloc_root(iroot); 7854ceac45SDavid Howells if (!root) { 7954ceac45SDavid Howells iput(iroot); 8054ceac45SDavid Howells return ERR_PTR(-ENOMEM); 8154ceac45SDavid Howells } 8254ceac45SDavid Howells 8354ceac45SDavid Howells sb->s_root = root; 8454ceac45SDavid Howells } 8554ceac45SDavid Howells 8654ceac45SDavid Howells /* get the actual root for this mount */ 8754ceac45SDavid Howells fsinfo.fattr = &fattr; 8854ceac45SDavid Howells 8954ceac45SDavid Howells error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 9054ceac45SDavid Howells if (error < 0) { 9154ceac45SDavid Howells dprintk("nfs_get_root: getattr error = %d\n", -error); 9254ceac45SDavid Howells return ERR_PTR(error); 9354ceac45SDavid Howells } 9454ceac45SDavid Howells 9554ceac45SDavid Howells inode = nfs_fhget(sb, mntfh, fsinfo.fattr); 9654ceac45SDavid Howells if (IS_ERR(inode)) { 9754ceac45SDavid Howells dprintk("nfs_get_root: get root inode failed\n"); 9854ceac45SDavid Howells return ERR_PTR(PTR_ERR(inode)); 9954ceac45SDavid Howells } 10054ceac45SDavid Howells 10154ceac45SDavid Howells /* root dentries normally start off anonymous and get spliced in later 10254ceac45SDavid Howells * if the dentry tree reaches them; however if the dentry already 10354ceac45SDavid Howells * exists, we'll pick it up at this point and use it as the root 10454ceac45SDavid Howells */ 10554ceac45SDavid Howells mntroot = d_alloc_anon(inode); 10654ceac45SDavid Howells if (!mntroot) { 10754ceac45SDavid Howells iput(inode); 10854ceac45SDavid Howells dprintk("nfs_get_root: get root dentry failed\n"); 10954ceac45SDavid Howells return ERR_PTR(-ENOMEM); 11054ceac45SDavid Howells } 11154ceac45SDavid Howells 112738a3519SDavid Howells security_d_instantiate(mntroot, inode); 113738a3519SDavid Howells 11454ceac45SDavid Howells if (!mntroot->d_op) 11554ceac45SDavid Howells mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; 11654ceac45SDavid Howells 11754ceac45SDavid Howells return mntroot; 11854ceac45SDavid Howells } 11954ceac45SDavid Howells 12054ceac45SDavid Howells #ifdef CONFIG_NFS_V4 12154ceac45SDavid Howells 12254ceac45SDavid Howells /* 12354ceac45SDavid Howells * Do a simple pathwalk from the root FH of the server to the nominated target 12454ceac45SDavid Howells * of the mountpoint 12554ceac45SDavid Howells * - give error on symlinks 12654ceac45SDavid Howells * - give error on ".." occurring in the path 12754ceac45SDavid Howells * - follow traversals 12854ceac45SDavid Howells */ 12954ceac45SDavid Howells int nfs4_path_walk(struct nfs_server *server, 13054ceac45SDavid Howells struct nfs_fh *mntfh, 13154ceac45SDavid Howells const char *path) 13254ceac45SDavid Howells { 13354ceac45SDavid Howells struct nfs_fsinfo fsinfo; 13454ceac45SDavid Howells struct nfs_fattr fattr; 13554ceac45SDavid Howells struct nfs_fh lastfh; 13654ceac45SDavid Howells struct qstr name; 13754ceac45SDavid Howells int ret; 13854ceac45SDavid Howells 13954ceac45SDavid Howells dprintk("--> nfs4_path_walk(,,%s)\n", path); 14054ceac45SDavid Howells 14154ceac45SDavid Howells fsinfo.fattr = &fattr; 14254ceac45SDavid Howells nfs_fattr_init(&fattr); 14354ceac45SDavid Howells 144faebf4e2STrond Myklebust /* Eat leading slashes */ 145faebf4e2STrond Myklebust while (*path == '/') 146faebf4e2STrond Myklebust path++; 14754ceac45SDavid Howells 14854ceac45SDavid Howells /* Start by getting the root filehandle from the server */ 14954ceac45SDavid Howells ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 15054ceac45SDavid Howells if (ret < 0) { 15154ceac45SDavid Howells dprintk("nfs4_get_root: getroot error = %d\n", -ret); 15254ceac45SDavid Howells return ret; 15354ceac45SDavid Howells } 15454ceac45SDavid Howells 15554ceac45SDavid Howells if (fattr.type != NFDIR) { 15654ceac45SDavid Howells printk(KERN_ERR "nfs4_get_root:" 15754ceac45SDavid Howells " getroot encountered non-directory\n"); 15854ceac45SDavid Howells return -ENOTDIR; 15954ceac45SDavid Howells } 16054ceac45SDavid Howells 161faebf4e2STrond Myklebust /* FIXME: It is quite valid for the server to return a referral here */ 16254ceac45SDavid Howells if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { 16354ceac45SDavid Howells printk(KERN_ERR "nfs4_get_root:" 16454ceac45SDavid Howells " getroot obtained referral\n"); 16554ceac45SDavid Howells return -EREMOTE; 16654ceac45SDavid Howells } 16754ceac45SDavid Howells 16854ceac45SDavid Howells next_component: 16954ceac45SDavid Howells dprintk("Next: %s\n", path); 17054ceac45SDavid Howells 17154ceac45SDavid Howells /* extract the next bit of the path */ 17254ceac45SDavid Howells if (!*path) 17354ceac45SDavid Howells goto path_walk_complete; 17454ceac45SDavid Howells 17554ceac45SDavid Howells name.name = path; 17654ceac45SDavid Howells while (*path && *path != '/') 17754ceac45SDavid Howells path++; 17854ceac45SDavid Howells name.len = path - (const char *) name.name; 17954ceac45SDavid Howells 18054ceac45SDavid Howells eat_dot_dir: 18154ceac45SDavid Howells while (*path == '/') 18254ceac45SDavid Howells path++; 18354ceac45SDavid Howells 18454ceac45SDavid Howells if (path[0] == '.' && (path[1] == '/' || !path[1])) { 18554ceac45SDavid Howells path += 2; 18654ceac45SDavid Howells goto eat_dot_dir; 18754ceac45SDavid Howells } 18854ceac45SDavid Howells 189faebf4e2STrond Myklebust /* FIXME: Why shouldn't the user be able to use ".." in the path? */ 19054ceac45SDavid Howells if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) 19154ceac45SDavid Howells ) { 19254ceac45SDavid Howells printk(KERN_ERR "nfs4_get_root:" 19354ceac45SDavid Howells " Mount path contains reference to \"..\"\n"); 19454ceac45SDavid Howells return -EINVAL; 19554ceac45SDavid Howells } 19654ceac45SDavid Howells 19754ceac45SDavid Howells /* lookup the next FH in the sequence */ 19854ceac45SDavid Howells memcpy(&lastfh, mntfh, sizeof(lastfh)); 19954ceac45SDavid Howells 20054ceac45SDavid Howells dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path); 20154ceac45SDavid Howells 20254ceac45SDavid Howells ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name, 20354ceac45SDavid Howells mntfh, &fattr); 20454ceac45SDavid Howells if (ret < 0) { 20554ceac45SDavid Howells dprintk("nfs4_get_root: getroot error = %d\n", -ret); 20654ceac45SDavid Howells return ret; 20754ceac45SDavid Howells } 20854ceac45SDavid Howells 20954ceac45SDavid Howells if (fattr.type != NFDIR) { 21054ceac45SDavid Howells printk(KERN_ERR "nfs4_get_root:" 21154ceac45SDavid Howells " lookupfh encountered non-directory\n"); 21254ceac45SDavid Howells return -ENOTDIR; 21354ceac45SDavid Howells } 21454ceac45SDavid Howells 215faebf4e2STrond Myklebust /* FIXME: Referrals are quite valid here too */ 21654ceac45SDavid Howells if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { 21754ceac45SDavid Howells printk(KERN_ERR "nfs4_get_root:" 21854ceac45SDavid Howells " lookupfh obtained referral\n"); 21954ceac45SDavid Howells return -EREMOTE; 22054ceac45SDavid Howells } 22154ceac45SDavid Howells 22254ceac45SDavid Howells goto next_component; 22354ceac45SDavid Howells 22454ceac45SDavid Howells path_walk_complete: 22554ceac45SDavid Howells memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); 22654ceac45SDavid Howells dprintk("<-- nfs4_path_walk() = 0\n"); 22754ceac45SDavid Howells return 0; 22854ceac45SDavid Howells } 22954ceac45SDavid Howells 23054ceac45SDavid Howells /* 23154ceac45SDavid Howells * get an NFS4 root dentry from the root filehandle 23254ceac45SDavid Howells */ 23354ceac45SDavid Howells struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) 23454ceac45SDavid Howells { 23554ceac45SDavid Howells struct nfs_server *server = NFS_SB(sb); 23654ceac45SDavid Howells struct nfs_fattr fattr; 23754ceac45SDavid Howells struct dentry *mntroot; 23854ceac45SDavid Howells struct inode *inode; 23954ceac45SDavid Howells int error; 24054ceac45SDavid Howells 24154ceac45SDavid Howells dprintk("--> nfs4_get_root()\n"); 24254ceac45SDavid Howells 24354ceac45SDavid Howells /* create a dummy root dentry with dummy inode for this superblock */ 24454ceac45SDavid Howells if (!sb->s_root) { 24554ceac45SDavid Howells struct nfs_fh dummyfh; 24654ceac45SDavid Howells struct dentry *root; 24754ceac45SDavid Howells struct inode *iroot; 24854ceac45SDavid Howells 24954ceac45SDavid Howells memset(&dummyfh, 0, sizeof(dummyfh)); 25054ceac45SDavid Howells memset(&fattr, 0, sizeof(fattr)); 25154ceac45SDavid Howells nfs_fattr_init(&fattr); 25254ceac45SDavid Howells fattr.valid = NFS_ATTR_FATTR; 25354ceac45SDavid Howells fattr.type = NFDIR; 25454ceac45SDavid Howells fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; 25554ceac45SDavid Howells fattr.nlink = 2; 25654ceac45SDavid Howells 25754ceac45SDavid Howells iroot = nfs_fhget(sb, &dummyfh, &fattr); 25854ceac45SDavid Howells if (IS_ERR(iroot)) 25954ceac45SDavid Howells return ERR_PTR(PTR_ERR(iroot)); 26054ceac45SDavid Howells 26154ceac45SDavid Howells root = d_alloc_root(iroot); 26254ceac45SDavid Howells if (!root) { 26354ceac45SDavid Howells iput(iroot); 26454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 26554ceac45SDavid Howells } 26654ceac45SDavid Howells 26754ceac45SDavid Howells sb->s_root = root; 26854ceac45SDavid Howells } 26954ceac45SDavid Howells 27054ceac45SDavid Howells /* get the info about the server and filesystem */ 27154ceac45SDavid Howells error = nfs4_server_capabilities(server, mntfh); 27254ceac45SDavid Howells if (error < 0) { 27354ceac45SDavid Howells dprintk("nfs_get_root: getcaps error = %d\n", 27454ceac45SDavid Howells -error); 27554ceac45SDavid Howells return ERR_PTR(error); 27654ceac45SDavid Howells } 27754ceac45SDavid Howells 27854ceac45SDavid Howells /* get the actual root for this mount */ 27954ceac45SDavid Howells error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); 28054ceac45SDavid Howells if (error < 0) { 28154ceac45SDavid Howells dprintk("nfs_get_root: getattr error = %d\n", -error); 28254ceac45SDavid Howells return ERR_PTR(error); 28354ceac45SDavid Howells } 28454ceac45SDavid Howells 28554ceac45SDavid Howells inode = nfs_fhget(sb, mntfh, &fattr); 28654ceac45SDavid Howells if (IS_ERR(inode)) { 28754ceac45SDavid Howells dprintk("nfs_get_root: get root inode failed\n"); 28854ceac45SDavid Howells return ERR_PTR(PTR_ERR(inode)); 28954ceac45SDavid Howells } 29054ceac45SDavid Howells 29154ceac45SDavid Howells /* root dentries normally start off anonymous and get spliced in later 29254ceac45SDavid Howells * if the dentry tree reaches them; however if the dentry already 29354ceac45SDavid Howells * exists, we'll pick it up at this point and use it as the root 29454ceac45SDavid Howells */ 29554ceac45SDavid Howells mntroot = d_alloc_anon(inode); 29654ceac45SDavid Howells if (!mntroot) { 29754ceac45SDavid Howells iput(inode); 29854ceac45SDavid Howells dprintk("nfs_get_root: get root dentry failed\n"); 29954ceac45SDavid Howells return ERR_PTR(-ENOMEM); 30054ceac45SDavid Howells } 30154ceac45SDavid Howells 302738a3519SDavid Howells security_d_instantiate(mntroot, inode); 303738a3519SDavid Howells 30454ceac45SDavid Howells if (!mntroot->d_op) 30554ceac45SDavid Howells mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; 30654ceac45SDavid Howells 30754ceac45SDavid Howells dprintk("<-- nfs4_get_root()\n"); 30854ceac45SDavid Howells return mntroot; 30954ceac45SDavid Howells } 31054ceac45SDavid Howells 31154ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 312