1 /* 2 * linux/fs/nfs/namespace.c 3 * 4 * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> 5 * - Modified by David Howells <dhowells@redhat.com> 6 * 7 * NFS namespace 8 */ 9 10 #include <linux/dcache.h> 11 #include <linux/gfp.h> 12 #include <linux/mount.h> 13 #include <linux/namei.h> 14 #include <linux/nfs_fs.h> 15 #include <linux/string.h> 16 #include <linux/sunrpc/clnt.h> 17 #include <linux/vfs.h> 18 #include "internal.h" 19 20 #define NFSDBG_FACILITY NFSDBG_VFS 21 22 static void nfs_expire_automounts(struct work_struct *work); 23 24 static LIST_HEAD(nfs_automount_list); 25 static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts); 26 int nfs_mountpoint_expiry_timeout = 500 * HZ; 27 28 static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, 29 const struct dentry *dentry, 30 struct nfs_fh *fh, 31 struct nfs_fattr *fattr); 32 33 /* 34 * nfs_path - reconstruct the path given an arbitrary dentry 35 * @base - arbitrary string to prepend to the path 36 * @droot - pointer to root dentry for mountpoint 37 * @dentry - pointer to dentry 38 * @buffer - result buffer 39 * @buflen - length of buffer 40 * 41 * Helper function for constructing the path from the 42 * root dentry to an arbitrary hashed dentry. 43 * 44 * This is mainly for use in figuring out the path on the 45 * server side when automounting on top of an existing partition. 46 */ 47 char *nfs_path(const char *base, 48 const struct dentry *droot, 49 const struct dentry *dentry, 50 char *buffer, ssize_t buflen) 51 { 52 char *end = buffer+buflen; 53 int namelen; 54 55 *--end = '\0'; 56 buflen--; 57 spin_lock(&dcache_lock); 58 while (!IS_ROOT(dentry) && dentry != droot) { 59 namelen = dentry->d_name.len; 60 buflen -= namelen + 1; 61 if (buflen < 0) 62 goto Elong_unlock; 63 end -= namelen; 64 memcpy(end, dentry->d_name.name, namelen); 65 *--end = '/'; 66 dentry = dentry->d_parent; 67 } 68 spin_unlock(&dcache_lock); 69 if (*end != '/') { 70 if (--buflen < 0) 71 goto Elong; 72 *--end = '/'; 73 } 74 namelen = strlen(base); 75 /* Strip off excess slashes in base string */ 76 while (namelen > 0 && base[namelen - 1] == '/') 77 namelen--; 78 buflen -= namelen; 79 if (buflen < 0) 80 goto Elong; 81 end -= namelen; 82 memcpy(end, base, namelen); 83 return end; 84 Elong_unlock: 85 spin_unlock(&dcache_lock); 86 Elong: 87 return ERR_PTR(-ENAMETOOLONG); 88 } 89 90 /* 91 * nfs_follow_mountpoint - handle crossing a mountpoint on the server 92 * @dentry - dentry of mountpoint 93 * @nd - nameidata info 94 * 95 * When we encounter a mountpoint on the server, we want to set up 96 * a mountpoint on the client too, to prevent inode numbers from 97 * colliding, and to allow "df" to work properly. 98 * On NFSv4, we also want to allow for the fact that different 99 * filesystems may be migrated to different servers in a failover 100 * situation, and that different filesystems may want to use 101 * different security flavours. 102 */ 103 static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) 104 { 105 struct vfsmount *mnt; 106 struct nfs_server *server = NFS_SERVER(dentry->d_inode); 107 struct dentry *parent; 108 struct nfs_fh *fh = NULL; 109 struct nfs_fattr *fattr = NULL; 110 int err; 111 112 dprintk("--> nfs_follow_mountpoint()\n"); 113 114 err = -ESTALE; 115 if (IS_ROOT(dentry)) 116 goto out_err; 117 118 err = -ENOMEM; 119 fh = nfs_alloc_fhandle(); 120 fattr = nfs_alloc_fattr(); 121 if (fh == NULL || fattr == NULL) 122 goto out_err; 123 124 dprintk("%s: enter\n", __func__); 125 dput(nd->path.dentry); 126 nd->path.dentry = dget(dentry); 127 128 /* Look it up again */ 129 parent = dget_parent(nd->path.dentry); 130 err = server->nfs_client->rpc_ops->lookup(parent->d_inode, 131 &nd->path.dentry->d_name, 132 fh, fattr); 133 dput(parent); 134 if (err != 0) 135 goto out_err; 136 137 if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) 138 mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry); 139 else 140 mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh, 141 fattr); 142 err = PTR_ERR(mnt); 143 if (IS_ERR(mnt)) 144 goto out_err; 145 146 mntget(mnt); 147 err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, 148 &nfs_automount_list); 149 if (err < 0) { 150 mntput(mnt); 151 if (err == -EBUSY) 152 goto out_follow; 153 goto out_err; 154 } 155 path_put(&nd->path); 156 nd->path.mnt = mnt; 157 nd->path.dentry = dget(mnt->mnt_root); 158 schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); 159 out: 160 nfs_free_fattr(fattr); 161 nfs_free_fhandle(fh); 162 dprintk("%s: done, returned %d\n", __func__, err); 163 164 dprintk("<-- nfs_follow_mountpoint() = %d\n", err); 165 return ERR_PTR(err); 166 out_err: 167 path_put(&nd->path); 168 goto out; 169 out_follow: 170 while (d_mountpoint(nd->path.dentry) && 171 follow_down(&nd->path)) 172 ; 173 err = 0; 174 goto out; 175 } 176 177 const struct inode_operations nfs_mountpoint_inode_operations = { 178 .follow_link = nfs_follow_mountpoint, 179 .getattr = nfs_getattr, 180 }; 181 182 const struct inode_operations nfs_referral_inode_operations = { 183 .follow_link = nfs_follow_mountpoint, 184 }; 185 186 static void nfs_expire_automounts(struct work_struct *work) 187 { 188 struct list_head *list = &nfs_automount_list; 189 190 mark_mounts_for_expiry(list); 191 if (!list_empty(list)) 192 schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); 193 } 194 195 void nfs_release_automount_timer(void) 196 { 197 if (list_empty(&nfs_automount_list)) 198 cancel_delayed_work(&nfs_automount_task); 199 } 200 201 /* 202 * Clone a mountpoint of the appropriate type 203 */ 204 static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, 205 const char *devname, 206 struct nfs_clone_mount *mountdata) 207 { 208 #ifdef CONFIG_NFS_V4 209 struct vfsmount *mnt = ERR_PTR(-EINVAL); 210 switch (server->nfs_client->rpc_ops->version) { 211 case 2: 212 case 3: 213 mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); 214 break; 215 case 4: 216 mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata); 217 } 218 return mnt; 219 #else 220 return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); 221 #endif 222 } 223 224 /** 225 * nfs_do_submount - set up mountpoint when crossing a filesystem boundary 226 * @mnt_parent - mountpoint of parent directory 227 * @dentry - parent directory 228 * @fh - filehandle for new root dentry 229 * @fattr - attributes for new root inode 230 * 231 */ 232 static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, 233 const struct dentry *dentry, 234 struct nfs_fh *fh, 235 struct nfs_fattr *fattr) 236 { 237 struct nfs_clone_mount mountdata = { 238 .sb = mnt_parent->mnt_sb, 239 .dentry = dentry, 240 .fh = fh, 241 .fattr = fattr, 242 }; 243 struct vfsmount *mnt = ERR_PTR(-ENOMEM); 244 char *page = (char *) __get_free_page(GFP_USER); 245 char *devname; 246 247 dprintk("--> nfs_do_submount()\n"); 248 249 dprintk("%s: submounting on %s/%s\n", __func__, 250 dentry->d_parent->d_name.name, 251 dentry->d_name.name); 252 if (page == NULL) 253 goto out; 254 devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); 255 mnt = (struct vfsmount *)devname; 256 if (IS_ERR(devname)) 257 goto free_page; 258 mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata); 259 free_page: 260 free_page((unsigned long)page); 261 out: 262 dprintk("%s: done\n", __func__); 263 264 dprintk("<-- nfs_do_submount() = %p\n", mnt); 265 return mnt; 266 } 267