linux/fs/nfs/nfs4super.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com>
   3 */
   4#include <linux/init.h>
   5#include <linux/module.h>
   6#include <linux/nfs4_mount.h>
   7#include <linux/nfs_fs.h>
   8#include "delegation.h"
   9#include "internal.h"
  10#include "nfs4_fs.h"
  11#include "nfs4idmap.h"
  12#include "dns_resolve.h"
  13#include "pnfs.h"
  14#include "nfs.h"
  15
  16#define NFSDBG_FACILITY         NFSDBG_VFS
  17
  18static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
  19static void nfs4_evict_inode(struct inode *inode);
  20static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
  21        int flags, const char *dev_name, void *raw_data);
  22static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
  23        int flags, const char *dev_name, void *raw_data);
  24static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
  25        int flags, const char *dev_name, void *raw_data);
  26
  27static struct file_system_type nfs4_remote_fs_type = {
  28        .owner          = THIS_MODULE,
  29        .name           = "nfs4",
  30        .mount          = nfs4_remote_mount,
  31        .kill_sb        = nfs_kill_super,
  32        .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
  33};
  34
  35static struct file_system_type nfs4_remote_referral_fs_type = {
  36        .owner          = THIS_MODULE,
  37        .name           = "nfs4",
  38        .mount          = nfs4_remote_referral_mount,
  39        .kill_sb        = nfs_kill_super,
  40        .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
  41};
  42
  43struct file_system_type nfs4_referral_fs_type = {
  44        .owner          = THIS_MODULE,
  45        .name           = "nfs4",
  46        .mount          = nfs4_referral_mount,
  47        .kill_sb        = nfs_kill_super,
  48        .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
  49};
  50
  51static const struct super_operations nfs4_sops = {
  52        .alloc_inode    = nfs_alloc_inode,
  53        .destroy_inode  = nfs_destroy_inode,
  54        .write_inode    = nfs4_write_inode,
  55        .drop_inode     = nfs_drop_inode,
  56        .statfs         = nfs_statfs,
  57        .evict_inode    = nfs4_evict_inode,
  58        .umount_begin   = nfs_umount_begin,
  59        .show_options   = nfs_show_options,
  60        .show_devname   = nfs_show_devname,
  61        .show_path      = nfs_show_path,
  62        .show_stats     = nfs_show_stats,
  63        .remount_fs     = nfs_remount,
  64};
  65
  66struct nfs_subversion nfs_v4 = {
  67        .owner = THIS_MODULE,
  68        .nfs_fs   = &nfs4_fs_type,
  69        .rpc_vers = &nfs_version4,
  70        .rpc_ops  = &nfs_v4_clientops,
  71        .sops     = &nfs4_sops,
  72        .xattr    = nfs4_xattr_handlers,
  73};
  74
  75static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
  76{
  77        int ret = nfs_write_inode(inode, wbc);
  78
  79        if (ret == 0)
  80                ret = pnfs_layoutcommit_inode(inode,
  81                                wbc->sync_mode == WB_SYNC_ALL);
  82        return ret;
  83}
  84
  85/*
  86 * Clean out any remaining NFSv4 state that might be left over due
  87 * to open() calls that passed nfs_atomic_lookup, but failed to call
  88 * nfs_open().
  89 */
  90static void nfs4_evict_inode(struct inode *inode)
  91{
  92        truncate_inode_pages_final(&inode->i_data);
  93        clear_inode(inode);
  94        /* If we are holding a delegation, return it! */
  95        nfs_inode_return_delegation_noreclaim(inode);
  96        /* Note that above delegreturn would trigger pnfs return-on-close */
  97        pnfs_return_layout(inode);
  98        pnfs_destroy_layout(NFS_I(inode));
  99        /* First call standard NFS clear_inode() code */
 100        nfs_clear_inode(inode);
 101}
 102
 103/*
 104 * Get the superblock for the NFS4 root partition
 105 */
 106static struct dentry *
 107nfs4_remote_mount(struct file_system_type *fs_type, int flags,
 108                  const char *dev_name, void *info)
 109{
 110        struct nfs_mount_info *mount_info = info;
 111        struct nfs_server *server;
 112        struct dentry *mntroot = ERR_PTR(-ENOMEM);
 113
 114        mount_info->set_security = nfs_set_sb_security;
 115
 116        /* Get a volume representation */
 117        server = nfs4_create_server(mount_info, &nfs_v4);
 118        if (IS_ERR(server)) {
 119                mntroot = ERR_CAST(server);
 120                goto out;
 121        }
 122
 123        mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4);
 124
 125out:
 126        return mntroot;
 127}
 128
 129static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
 130                int flags, void *data, const char *hostname)
 131{
 132        struct vfsmount *root_mnt;
 133        char *root_devname;
 134        size_t len;
 135
 136        len = strlen(hostname) + 5;
 137        root_devname = kmalloc(len, GFP_KERNEL);
 138        if (root_devname == NULL)
 139                return ERR_PTR(-ENOMEM);
 140        /* Does hostname needs to be enclosed in brackets? */
 141        if (strchr(hostname, ':'))
 142                snprintf(root_devname, len, "[%s]:/", hostname);
 143        else
 144                snprintf(root_devname, len, "%s:/", hostname);
 145        root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
 146        kfree(root_devname);
 147        return root_mnt;
 148}
 149
 150struct nfs_referral_count {
 151        struct list_head list;
 152        const struct task_struct *task;
 153        unsigned int referral_count;
 154};
 155
 156static LIST_HEAD(nfs_referral_count_list);
 157static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
 158
 159static struct nfs_referral_count *nfs_find_referral_count(void)
 160{
 161        struct nfs_referral_count *p;
 162
 163        list_for_each_entry(p, &nfs_referral_count_list, list) {
 164                if (p->task == current)
 165                        return p;
 166        }
 167        return NULL;
 168}
 169
 170#define NFS_MAX_NESTED_REFERRALS 2
 171
 172static int nfs_referral_loop_protect(void)
 173{
 174        struct nfs_referral_count *p, *new;
 175        int ret = -ENOMEM;
 176
 177        new = kmalloc(sizeof(*new), GFP_KERNEL);
 178        if (!new)
 179                goto out;
 180        new->task = current;
 181        new->referral_count = 1;
 182
 183        ret = 0;
 184        spin_lock(&nfs_referral_count_list_lock);
 185        p = nfs_find_referral_count();
 186        if (p != NULL) {
 187                if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
 188                        ret = -ELOOP;
 189                else
 190                        p->referral_count++;
 191        } else {
 192                list_add(&new->list, &nfs_referral_count_list);
 193                new = NULL;
 194        }
 195        spin_unlock(&nfs_referral_count_list_lock);
 196        kfree(new);
 197out:
 198        return ret;
 199}
 200
 201static void nfs_referral_loop_unprotect(void)
 202{
 203        struct nfs_referral_count *p;
 204
 205        spin_lock(&nfs_referral_count_list_lock);
 206        p = nfs_find_referral_count();
 207        p->referral_count--;
 208        if (p->referral_count == 0)
 209                list_del(&p->list);
 210        else
 211                p = NULL;
 212        spin_unlock(&nfs_referral_count_list_lock);
 213        kfree(p);
 214}
 215
 216static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
 217                const char *export_path)
 218{
 219        struct dentry *dentry;
 220        int err;
 221
 222        if (IS_ERR(root_mnt))
 223                return ERR_CAST(root_mnt);
 224
 225        err = nfs_referral_loop_protect();
 226        if (err) {
 227                mntput(root_mnt);
 228                return ERR_PTR(err);
 229        }
 230
 231        dentry = mount_subtree(root_mnt, export_path);
 232        nfs_referral_loop_unprotect();
 233
 234        return dentry;
 235}
 236
 237struct dentry *nfs4_try_mount(int flags, const char *dev_name,
 238                              struct nfs_mount_info *mount_info,
 239                              struct nfs_subversion *nfs_mod)
 240{
 241        char *export_path;
 242        struct vfsmount *root_mnt;
 243        struct dentry *res;
 244        struct nfs_parsed_mount_data *data = mount_info->parsed;
 245
 246        dfprintk(MOUNT, "--> nfs4_try_mount()\n");
 247
 248        export_path = data->nfs_server.export_path;
 249        data->nfs_server.export_path = "/";
 250        root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
 251                        data->nfs_server.hostname);
 252        data->nfs_server.export_path = export_path;
 253
 254        res = nfs_follow_remote_path(root_mnt, export_path);
 255
 256        dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
 257                 PTR_ERR_OR_ZERO(res),
 258                 IS_ERR(res) ? " [error]" : "");
 259        return res;
 260}
 261
 262static struct dentry *
 263nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
 264                           const char *dev_name, void *raw_data)
 265{
 266        struct nfs_mount_info mount_info = {
 267                .fill_super = nfs_fill_super,
 268                .set_security = nfs_clone_sb_security,
 269                .cloned = raw_data,
 270        };
 271        struct nfs_server *server;
 272        struct dentry *mntroot = ERR_PTR(-ENOMEM);
 273
 274        dprintk("--> nfs4_referral_get_sb()\n");
 275
 276        mount_info.mntfh = nfs_alloc_fhandle();
 277        if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
 278                goto out;
 279
 280        /* create a new volume representation */
 281        server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
 282        if (IS_ERR(server)) {
 283                mntroot = ERR_CAST(server);
 284                goto out;
 285        }
 286
 287        mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4);
 288out:
 289        nfs_free_fhandle(mount_info.mntfh);
 290        return mntroot;
 291}
 292
 293/*
 294 * Create an NFS4 server record on referral traversal
 295 */
 296static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
 297                int flags, const char *dev_name, void *raw_data)
 298{
 299        struct nfs_clone_mount *data = raw_data;
 300        char *export_path;
 301        struct vfsmount *root_mnt;
 302        struct dentry *res;
 303
 304        dprintk("--> nfs4_referral_mount()\n");
 305
 306        export_path = data->mnt_path;
 307        data->mnt_path = "/";
 308
 309        root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
 310                        flags, data, data->hostname);
 311        data->mnt_path = export_path;
 312
 313        res = nfs_follow_remote_path(root_mnt, export_path);
 314        dprintk("<-- nfs4_referral_mount() = %d%s\n",
 315                PTR_ERR_OR_ZERO(res),
 316                IS_ERR(res) ? " [error]" : "");
 317        return res;
 318}
 319
 320
 321static int __init init_nfs_v4(void)
 322{
 323        int err;
 324
 325        err = nfs_dns_resolver_init();
 326        if (err)
 327                goto out;
 328
 329        err = nfs_idmap_init();
 330        if (err)
 331                goto out1;
 332
 333        err = nfs4_register_sysctl();
 334        if (err)
 335                goto out2;
 336
 337        register_nfs_version(&nfs_v4);
 338        return 0;
 339out2:
 340        nfs_idmap_quit();
 341out1:
 342        nfs_dns_resolver_destroy();
 343out:
 344        return err;
 345}
 346
 347static void __exit exit_nfs_v4(void)
 348{
 349        /* Not called in the _init(), conditionally loaded */
 350        nfs4_pnfs_v3_ds_connect_unload();
 351
 352        unregister_nfs_version(&nfs_v4);
 353        nfs4_unregister_sysctl();
 354        nfs_idmap_quit();
 355        nfs_dns_resolver_destroy();
 356}
 357
 358MODULE_LICENSE("GPL");
 359
 360module_init(init_nfs_v4);
 361module_exit(exit_nfs_v4);
 362