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/mount.h>
   8#include <linux/nfs4_mount.h>
   9#include <linux/nfs_fs.h>
  10#include <linux/nfs_ssc.h>
  11#include "delegation.h"
  12#include "internal.h"
  13#include "nfs4_fs.h"
  14#include "nfs4idmap.h"
  15#include "dns_resolve.h"
  16#include "pnfs.h"
  17#include "nfs.h"
  18
  19#define NFSDBG_FACILITY         NFSDBG_VFS
  20
  21static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
  22static void nfs4_evict_inode(struct inode *inode);
  23
  24static const struct super_operations nfs4_sops = {
  25        .alloc_inode    = nfs_alloc_inode,
  26        .free_inode     = nfs_free_inode,
  27        .write_inode    = nfs4_write_inode,
  28        .drop_inode     = nfs_drop_inode,
  29        .statfs         = nfs_statfs,
  30        .evict_inode    = nfs4_evict_inode,
  31        .umount_begin   = nfs_umount_begin,
  32        .show_options   = nfs_show_options,
  33        .show_devname   = nfs_show_devname,
  34        .show_path      = nfs_show_path,
  35        .show_stats     = nfs_show_stats,
  36};
  37
  38struct nfs_subversion nfs_v4 = {
  39        .owner          = THIS_MODULE,
  40        .nfs_fs         = &nfs4_fs_type,
  41        .rpc_vers       = &nfs_version4,
  42        .rpc_ops        = &nfs_v4_clientops,
  43        .sops           = &nfs4_sops,
  44        .xattr          = nfs4_xattr_handlers,
  45};
  46
  47static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
  48{
  49        int ret = nfs_write_inode(inode, wbc);
  50
  51        if (ret == 0)
  52                ret = pnfs_layoutcommit_inode(inode,
  53                                wbc->sync_mode == WB_SYNC_ALL);
  54        return ret;
  55}
  56
  57/*
  58 * Clean out any remaining NFSv4 state that might be left over due
  59 * to open() calls that passed nfs_atomic_lookup, but failed to call
  60 * nfs_open().
  61 */
  62static void nfs4_evict_inode(struct inode *inode)
  63{
  64        truncate_inode_pages_final(&inode->i_data);
  65        clear_inode(inode);
  66        /* If we are holding a delegation, return and free it */
  67        nfs_inode_evict_delegation(inode);
  68        /* Note that above delegreturn would trigger pnfs return-on-close */
  69        pnfs_return_layout(inode);
  70        pnfs_destroy_layout(NFS_I(inode));
  71        /* First call standard NFS clear_inode() code */
  72        nfs_clear_inode(inode);
  73        nfs4_xattr_cache_zap(inode);
  74}
  75
  76struct nfs_referral_count {
  77        struct list_head list;
  78        const struct task_struct *task;
  79        unsigned int referral_count;
  80};
  81
  82static LIST_HEAD(nfs_referral_count_list);
  83static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
  84
  85static struct nfs_referral_count *nfs_find_referral_count(void)
  86{
  87        struct nfs_referral_count *p;
  88
  89        list_for_each_entry(p, &nfs_referral_count_list, list) {
  90                if (p->task == current)
  91                        return p;
  92        }
  93        return NULL;
  94}
  95
  96#define NFS_MAX_NESTED_REFERRALS 2
  97
  98static int nfs_referral_loop_protect(void)
  99{
 100        struct nfs_referral_count *p, *new;
 101        int ret = -ENOMEM;
 102
 103        new = kmalloc(sizeof(*new), GFP_KERNEL);
 104        if (!new)
 105                goto out;
 106        new->task = current;
 107        new->referral_count = 1;
 108
 109        ret = 0;
 110        spin_lock(&nfs_referral_count_list_lock);
 111        p = nfs_find_referral_count();
 112        if (p != NULL) {
 113                if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
 114                        ret = -ELOOP;
 115                else
 116                        p->referral_count++;
 117        } else {
 118                list_add(&new->list, &nfs_referral_count_list);
 119                new = NULL;
 120        }
 121        spin_unlock(&nfs_referral_count_list_lock);
 122        kfree(new);
 123out:
 124        return ret;
 125}
 126
 127static void nfs_referral_loop_unprotect(void)
 128{
 129        struct nfs_referral_count *p;
 130
 131        spin_lock(&nfs_referral_count_list_lock);
 132        p = nfs_find_referral_count();
 133        p->referral_count--;
 134        if (p->referral_count == 0)
 135                list_del(&p->list);
 136        else
 137                p = NULL;
 138        spin_unlock(&nfs_referral_count_list_lock);
 139        kfree(p);
 140}
 141
 142static int do_nfs4_mount(struct nfs_server *server,
 143                         struct fs_context *fc,
 144                         const char *hostname,
 145                         const char *export_path)
 146{
 147        struct nfs_fs_context *root_ctx;
 148        struct fs_context *root_fc;
 149        struct vfsmount *root_mnt;
 150        struct dentry *dentry;
 151        size_t len;
 152        int ret;
 153
 154        struct fs_parameter param = {
 155                .key    = "source",
 156                .type   = fs_value_is_string,
 157                .dirfd  = -1,
 158        };
 159
 160        if (IS_ERR(server))
 161                return PTR_ERR(server);
 162
 163        root_fc = vfs_dup_fs_context(fc);
 164        if (IS_ERR(root_fc)) {
 165                nfs_free_server(server);
 166                return PTR_ERR(root_fc);
 167        }
 168        kfree(root_fc->source);
 169        root_fc->source = NULL;
 170
 171        root_ctx = nfs_fc2context(root_fc);
 172        root_ctx->internal = true;
 173        root_ctx->server = server;
 174        /* We leave export_path unset as it's not used to find the root. */
 175
 176        len = strlen(hostname) + 5;
 177        param.string = kmalloc(len, GFP_KERNEL);
 178        if (param.string == NULL) {
 179                put_fs_context(root_fc);
 180                return -ENOMEM;
 181        }
 182
 183        /* Does hostname needs to be enclosed in brackets? */
 184        if (strchr(hostname, ':'))
 185                param.size = snprintf(param.string, len, "[%s]:/", hostname);
 186        else
 187                param.size = snprintf(param.string, len, "%s:/", hostname);
 188        ret = vfs_parse_fs_param(root_fc, &param);
 189        kfree(param.string);
 190        if (ret < 0) {
 191                put_fs_context(root_fc);
 192                return ret;
 193        }
 194        root_mnt = fc_mount(root_fc);
 195        put_fs_context(root_fc);
 196
 197        if (IS_ERR(root_mnt))
 198                return PTR_ERR(root_mnt);
 199
 200        ret = nfs_referral_loop_protect();
 201        if (ret) {
 202                mntput(root_mnt);
 203                return ret;
 204        }
 205
 206        dentry = mount_subtree(root_mnt, export_path);
 207        nfs_referral_loop_unprotect();
 208
 209        if (IS_ERR(dentry))
 210                return PTR_ERR(dentry);
 211
 212        fc->root = dentry;
 213        return 0;
 214}
 215
 216int nfs4_try_get_tree(struct fs_context *fc)
 217{
 218        struct nfs_fs_context *ctx = nfs_fc2context(fc);
 219        int err;
 220
 221        dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
 222
 223        /* We create a mount for the server's root, walk to the requested
 224         * location and then create another mount for that.
 225         */
 226        err= do_nfs4_mount(nfs4_create_server(fc),
 227                           fc, ctx->nfs_server.hostname,
 228                           ctx->nfs_server.export_path);
 229        if (err) {
 230                nfs_errorf(fc, "NFS4: Couldn't follow remote path");
 231                dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
 232        } else {
 233                dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
 234        }
 235        return err;
 236}
 237
 238/*
 239 * Create an NFS4 server record on referral traversal
 240 */
 241int nfs4_get_referral_tree(struct fs_context *fc)
 242{
 243        struct nfs_fs_context *ctx = nfs_fc2context(fc);
 244        int err;
 245
 246        dprintk("--> nfs4_referral_mount()\n");
 247
 248        /* create a new volume representation */
 249        err = do_nfs4_mount(nfs4_create_referral_server(fc),
 250                            fc, ctx->nfs_server.hostname,
 251                            ctx->nfs_server.export_path);
 252        if (err) {
 253                nfs_errorf(fc, "NFS4: Couldn't follow remote path");
 254                dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
 255        } else {
 256                dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
 257        }
 258        return err;
 259}
 260
 261static int __init init_nfs_v4(void)
 262{
 263        int err;
 264
 265        err = nfs_dns_resolver_init();
 266        if (err)
 267                goto out;
 268
 269        err = nfs_idmap_init();
 270        if (err)
 271                goto out1;
 272
 273#ifdef CONFIG_NFS_V4_2
 274        err = nfs4_xattr_cache_init();
 275        if (err)
 276                goto out2;
 277#endif
 278
 279        err = nfs4_register_sysctl();
 280        if (err)
 281                goto out2;
 282
 283#ifdef CONFIG_NFS_V4_2
 284        nfs42_ssc_register_ops();
 285#endif
 286        register_nfs_version(&nfs_v4);
 287        return 0;
 288out2:
 289        nfs_idmap_quit();
 290out1:
 291        nfs_dns_resolver_destroy();
 292out:
 293        return err;
 294}
 295
 296static void __exit exit_nfs_v4(void)
 297{
 298        /* Not called in the _init(), conditionally loaded */
 299        nfs4_pnfs_v3_ds_connect_unload();
 300
 301        unregister_nfs_version(&nfs_v4);
 302#ifdef CONFIG_NFS_V4_2
 303        nfs4_xattr_cache_exit();
 304        nfs42_ssc_unregister_ops();
 305#endif
 306        nfs4_unregister_sysctl();
 307        nfs_idmap_quit();
 308        nfs_dns_resolver_destroy();
 309}
 310
 311MODULE_LICENSE("GPL");
 312
 313module_init(init_nfs_v4);
 314module_exit(exit_nfs_v4);
 315