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