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