X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fnfs%2Fnamespace.c;h=40c76678289168ed0cbbe3e653f58485a56003eb;hb=e608e79f1bf4b967afcf57777e63b5f0939b00e8;hp=a155505c36f13112ef32c34f04bac4a98f1e2379;hpb=55a975937d40cac582e981ddc8ed783b3dcc043c;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index a155505..40c7667 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -2,12 +2,11 @@ * linux/fs/nfs/namespace.c * * Copyright (C) 2005 Trond Myklebust + * - Modified by David Howells * * NFS namespace */ -#include - #include #include #include @@ -15,9 +14,78 @@ #include #include #include +#include "internal.h" #define NFSDBG_FACILITY NFSDBG_VFS +static void nfs_expire_automounts(struct work_struct *work); + +static LIST_HEAD(nfs_automount_list); +static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts); +int nfs_mountpoint_expiry_timeout = 500 * HZ; + +static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, + const struct dentry *dentry, + struct nfs_fh *fh, + struct nfs_fattr *fattr); + +/* + * nfs_path - reconstruct the path given an arbitrary dentry + * @base - arbitrary string to prepend to the path + * @droot - pointer to root dentry for mountpoint + * @dentry - pointer to dentry + * @buffer - result buffer + * @buflen - length of buffer + * + * Helper function for constructing the path from the + * root dentry to an arbitrary hashed dentry. + * + * This is mainly for use in figuring out the path on the + * server side when automounting on top of an existing partition. + */ +char *nfs_path(const char *base, + const struct dentry *droot, + const struct dentry *dentry, + char *buffer, ssize_t buflen) +{ + char *end = buffer+buflen; + int namelen; + + *--end = '\0'; + buflen--; + spin_lock(&dcache_lock); + while (!IS_ROOT(dentry) && dentry != droot) { + namelen = dentry->d_name.len; + buflen -= namelen + 1; + if (buflen < 0) + goto Elong_unlock; + end -= namelen; + memcpy(end, dentry->d_name.name, namelen); + *--end = '/'; + dentry = dentry->d_parent; + } + spin_unlock(&dcache_lock); + if (*end != '/') { + if (--buflen < 0) + goto Elong; + *--end = '/'; + } + namelen = strlen(base); + /* Strip off excess slashes in base string */ + while (namelen > 0 && base[namelen - 1] == '/') + namelen--; + buflen -= namelen; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, base, namelen); + return end; +Elong_unlock: + spin_unlock(&dcache_lock); +Elong: + return ERR_PTR(-ENAMETOOLONG); +} + /* * nfs_follow_mountpoint - handle crossing a mountpoint on the server * @dentry - dentry of mountpoint @@ -40,50 +108,150 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) struct nfs_fattr fattr; int err; - BUG_ON(IS_ROOT(dentry)); - dprintk("%s: enter\n", __FUNCTION__); - dput(nd->dentry); - nd->dentry = dget(dentry); - if (d_mountpoint(nd->dentry)) - goto out_follow; + dprintk("--> nfs_follow_mountpoint()\n"); + + err = -ESTALE; + if (IS_ROOT(dentry)) + goto out_err; + + dprintk("%s: enter\n", __func__); + dput(nd->path.dentry); + nd->path.dentry = dget(dentry); + /* Look it up again */ - parent = dget_parent(nd->dentry); - err = server->rpc_ops->lookup(parent->d_inode, &nd->dentry->d_name, &fh, &fattr); + parent = dget_parent(nd->path.dentry); + err = server->nfs_client->rpc_ops->lookup(parent->d_inode, + &nd->path.dentry->d_name, + &fh, &fattr); dput(parent); if (err != 0) goto out_err; - mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); + if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) + mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry); + else + mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, &fh, + &fattr); err = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; mntget(mnt); - err = do_add_mount(mnt, nd, nd->mnt->mnt_flags, NULL); + err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, + &nfs_automount_list); if (err < 0) { mntput(mnt); if (err == -EBUSY) goto out_follow; goto out_err; } - mntput(nd->mnt); - dput(nd->dentry); - nd->mnt = mnt; - nd->dentry = dget(mnt->mnt_root); + path_put(&nd->path); + nd->path.mnt = mnt; + nd->path.dentry = dget(mnt->mnt_root); + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); out: - dprintk("%s: done, returned %d\n", __FUNCTION__, err); + dprintk("%s: done, returned %d\n", __func__, err); + + dprintk("<-- nfs_follow_mountpoint() = %d\n", err); return ERR_PTR(err); out_err: - path_release(nd); + path_put(&nd->path); goto out; out_follow: - while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) + while (d_mountpoint(nd->path.dentry) && + follow_down(&nd->path)) ; err = 0; goto out; } -struct inode_operations nfs_mountpoint_inode_operations = { +const struct inode_operations nfs_mountpoint_inode_operations = { .follow_link = nfs_follow_mountpoint, .getattr = nfs_getattr, }; + +const struct inode_operations nfs_referral_inode_operations = { + .follow_link = nfs_follow_mountpoint, +}; + +static void nfs_expire_automounts(struct work_struct *work) +{ + struct list_head *list = &nfs_automount_list; + + mark_mounts_for_expiry(list); + if (!list_empty(list)) + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); +} + +void nfs_release_automount_timer(void) +{ + if (list_empty(&nfs_automount_list)) + cancel_delayed_work(&nfs_automount_task); +} + +/* + * Clone a mountpoint of the appropriate type + */ +static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, + const char *devname, + struct nfs_clone_mount *mountdata) +{ +#ifdef CONFIG_NFS_V4 + struct vfsmount *mnt = ERR_PTR(-EINVAL); + switch (server->nfs_client->rpc_ops->version) { + case 2: + case 3: + mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); + break; + case 4: + mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata); + } + return mnt; +#else + return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); +#endif +} + +/** + * nfs_do_submount - set up mountpoint when crossing a filesystem boundary + * @mnt_parent - mountpoint of parent directory + * @dentry - parent directory + * @fh - filehandle for new root dentry + * @fattr - attributes for new root inode + * + */ +static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, + const struct dentry *dentry, + struct nfs_fh *fh, + struct nfs_fattr *fattr) +{ + struct nfs_clone_mount mountdata = { + .sb = mnt_parent->mnt_sb, + .dentry = dentry, + .fh = fh, + .fattr = fattr, + }; + struct vfsmount *mnt = ERR_PTR(-ENOMEM); + char *page = (char *) __get_free_page(GFP_USER); + char *devname; + + dprintk("--> nfs_do_submount()\n"); + + dprintk("%s: submounting on %s/%s\n", __func__, + dentry->d_parent->d_name.name, + dentry->d_name.name); + if (page == NULL) + goto out; + devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); + mnt = (struct vfsmount *)devname; + if (IS_ERR(devname)) + goto free_page; + mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata); +free_page: + free_page((unsigned long)page); +out: + dprintk("%s: done\n", __func__); + + dprintk("<-- nfs_do_submount() = %p\n", mnt); + return mnt; +}