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
  23static LIST_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        if (*end != '/') {
  69                if (--buflen < 0)
  70                        goto Elong;
  71                *--end = '/';
  72        }
  73        namelen = strlen(base);
  74        /* Strip off excess slashes in base string */
  75        while (namelen > 0 && base[namelen - 1] == '/')
  76                namelen--;
  77        buflen -= namelen;
  78        if (buflen < 0)
  79                goto Elong;
  80        end -= namelen;
  81        memcpy(end, base, namelen);
  82        return end;
  83Elong_unlock:
  84        spin_unlock(&dcache_lock);
  85Elong:
  86        return ERR_PTR(-ENAMETOOLONG);
  87}
  88
  89/*
  90 * nfs_follow_mountpoint - handle crossing a mountpoint on the server
  91 * @dentry - dentry of mountpoint
  92 * @nd - nameidata info
  93 *
  94 * When we encounter a mountpoint on the server, we want to set up
  95 * a mountpoint on the client too, to prevent inode numbers from
  96 * colliding, and to allow "df" to work properly.
  97 * On NFSv4, we also want to allow for the fact that different
  98 * filesystems may be migrated to different servers in a failover
  99 * situation, and that different filesystems may want to use
 100 * different security flavours.
 101 */
 102static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
 103{
 104        struct vfsmount *mnt;
 105        struct nfs_server *server = NFS_SERVER(dentry->d_inode);
 106        struct dentry *parent;
 107        struct nfs_fh fh;
 108        struct nfs_fattr fattr;
 109        int err;
 110
 111        dprintk("--> nfs_follow_mountpoint()\n");
 112
 113        err = -ESTALE;
 114        if (IS_ROOT(dentry))
 115                goto out_err;
 116
 117        dprintk("%s: enter\n", __func__);
 118        dput(nd->path.dentry);
 119        nd->path.dentry = dget(dentry);
 120
 121        /* Look it up again */
 122        parent = dget_parent(nd->path.dentry);
 123        err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
 124                                                  &nd->path.dentry->d_name,
 125                                                  &fh, &fattr);
 126        dput(parent);
 127        if (err != 0)
 128                goto out_err;
 129
 130        if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL)
 131                mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry);
 132        else
 133                mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, &fh,
 134                                      &fattr);
 135        err = PTR_ERR(mnt);
 136        if (IS_ERR(mnt))
 137                goto out_err;
 138
 139        mntget(mnt);
 140        err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE,
 141                           &nfs_automount_list);
 142        if (err < 0) {
 143                mntput(mnt);
 144                if (err == -EBUSY)
 145                        goto out_follow;
 146                goto out_err;
 147        }
 148        path_put(&nd->path);
 149        nd->path.mnt = mnt;
 150        nd->path.dentry = dget(mnt->mnt_root);
 151        schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
 152out:
 153        dprintk("%s: done, returned %d\n", __func__, err);
 154
 155        dprintk("<-- nfs_follow_mountpoint() = %d\n", err);
 156        return ERR_PTR(err);
 157out_err:
 158        path_put(&nd->path);
 159        goto out;
 160out_follow:
 161        while (d_mountpoint(nd->path.dentry) &&
 162               follow_down(&nd->path))
 163                ;
 164        err = 0;
 165        goto out;
 166}
 167
 168const struct inode_operations nfs_mountpoint_inode_operations = {
 169        .follow_link    = nfs_follow_mountpoint,
 170        .getattr        = nfs_getattr,
 171};
 172
 173const struct inode_operations nfs_referral_inode_operations = {
 174        .follow_link    = nfs_follow_mountpoint,
 175};
 176
 177static void nfs_expire_automounts(struct work_struct *work)
 178{
 179        struct list_head *list = &nfs_automount_list;
 180
 181        mark_mounts_for_expiry(list);
 182        if (!list_empty(list))
 183                schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
 184}
 185
 186void nfs_release_automount_timer(void)
 187{
 188        if (list_empty(&nfs_automount_list))
 189                cancel_delayed_work(&nfs_automount_task);
 190}
 191
 192/*
 193 * Clone a mountpoint of the appropriate type
 194 */
 195static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
 196                                           const char *devname,
 197                                           struct nfs_clone_mount *mountdata)
 198{
 199#ifdef CONFIG_NFS_V4
 200        struct vfsmount *mnt = ERR_PTR(-EINVAL);
 201        switch (server->nfs_client->rpc_ops->version) {
 202                case 2:
 203                case 3:
 204                        mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
 205                        break;
 206                case 4:
 207                        mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata);
 208        }
 209        return mnt;
 210#else
 211        return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
 212#endif
 213}
 214
 215/**
 216 * nfs_do_submount - set up mountpoint when crossing a filesystem boundary
 217 * @mnt_parent - mountpoint of parent directory
 218 * @dentry - parent directory
 219 * @fh - filehandle for new root dentry
 220 * @fattr - attributes for new root inode
 221 *
 222 */
 223static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
 224                                        const struct dentry *dentry,
 225                                        struct nfs_fh *fh,
 226                                        struct nfs_fattr *fattr)
 227{
 228        struct nfs_clone_mount mountdata = {
 229                .sb = mnt_parent->mnt_sb,
 230                .dentry = dentry,
 231                .fh = fh,
 232                .fattr = fattr,
 233        };
 234        struct vfsmount *mnt = ERR_PTR(-ENOMEM);
 235        char *page = (char *) __get_free_page(GFP_USER);
 236        char *devname;
 237
 238        dprintk("--> nfs_do_submount()\n");
 239
 240        dprintk("%s: submounting on %s/%s\n", __func__,
 241                        dentry->d_parent->d_name.name,
 242                        dentry->d_name.name);
 243        if (page == NULL)
 244                goto out;
 245        devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE);
 246        mnt = (struct vfsmount *)devname;
 247        if (IS_ERR(devname))
 248                goto free_page;
 249        mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata);
 250free_page:
 251        free_page((unsigned long)page);
 252out:
 253        dprintk("%s: done\n", __func__);
 254
 255        dprintk("<-- nfs_do_submount() = %p\n", mnt);
 256        return mnt;
 257}
 258