linux/fs/nfsd/nfs4recover.c
<<
>>
Prefs
   1/*
   2*  Copyright (c) 2004 The Regents of the University of Michigan.
   3*  Copyright (c) 2012 Jeff Layton <jlayton@redhat.com>
   4*  All rights reserved.
   5*
   6*  Andy Adamson <andros@citi.umich.edu>
   7*
   8*  Redistribution and use in source and binary forms, with or without
   9*  modification, are permitted provided that the following conditions
  10*  are met:
  11*
  12*  1. Redistributions of source code must retain the above copyright
  13*     notice, this list of conditions and the following disclaimer.
  14*  2. Redistributions in binary form must reproduce the above copyright
  15*     notice, this list of conditions and the following disclaimer in the
  16*     documentation and/or other materials provided with the distribution.
  17*  3. Neither the name of the University nor the names of its
  18*     contributors may be used to endorse or promote products derived
  19*     from this software without specific prior written permission.
  20*
  21*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  22*  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  23*  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  24*  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25*  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  28*  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29*  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30*  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32*
  33*/
  34
  35#include <linux/file.h>
  36#include <linux/slab.h>
  37#include <linux/namei.h>
  38#include <linux/crypto.h>
  39#include <linux/sched.h>
  40#include <linux/fs.h>
  41#include <linux/module.h>
  42#include <net/net_namespace.h>
  43#include <linux/sunrpc/rpc_pipe_fs.h>
  44#include <linux/sunrpc/clnt.h>
  45#include <linux/nfsd/cld.h>
  46
  47#include "nfsd.h"
  48#include "state.h"
  49#include "vfs.h"
  50#include "netns.h"
  51
  52#define NFSDDBG_FACILITY                NFSDDBG_PROC
  53
  54/* Declarations */
  55struct nfsd4_client_tracking_ops {
  56        int (*init)(struct net *);
  57        void (*exit)(struct net *);
  58        void (*create)(struct nfs4_client *);
  59        void (*remove)(struct nfs4_client *);
  60        int (*check)(struct nfs4_client *);
  61        void (*grace_done)(struct net *, time_t);
  62};
  63
  64/* Globals */
  65static struct file *rec_file;
  66static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
  67static struct nfsd4_client_tracking_ops *client_tracking_ops;
  68
  69static int
  70nfs4_save_creds(const struct cred **original_creds)
  71{
  72        struct cred *new;
  73
  74        new = prepare_creds();
  75        if (!new)
  76                return -ENOMEM;
  77
  78        new->fsuid = 0;
  79        new->fsgid = 0;
  80        *original_creds = override_creds(new);
  81        put_cred(new);
  82        return 0;
  83}
  84
  85static void
  86nfs4_reset_creds(const struct cred *original)
  87{
  88        revert_creds(original);
  89}
  90
  91static void
  92md5_to_hex(char *out, char *md5)
  93{
  94        int i;
  95
  96        for (i=0; i<16; i++) {
  97                unsigned char c = md5[i];
  98
  99                *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
 100                *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
 101        }
 102        *out = '\0';
 103}
 104
 105__be32
 106nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
 107{
 108        struct xdr_netobj cksum;
 109        struct hash_desc desc;
 110        struct scatterlist sg;
 111        __be32 status = nfserr_jukebox;
 112
 113        dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
 114                        clname->len, clname->data);
 115        desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 116        desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
 117        if (IS_ERR(desc.tfm))
 118                goto out_no_tfm;
 119        cksum.len = crypto_hash_digestsize(desc.tfm);
 120        cksum.data = kmalloc(cksum.len, GFP_KERNEL);
 121        if (cksum.data == NULL)
 122                goto out;
 123
 124        sg_init_one(&sg, clname->data, clname->len);
 125
 126        if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data))
 127                goto out;
 128
 129        md5_to_hex(dname, cksum.data);
 130
 131        status = nfs_ok;
 132out:
 133        kfree(cksum.data);
 134        crypto_free_hash(desc.tfm);
 135out_no_tfm:
 136        return status;
 137}
 138
 139static void
 140nfsd4_create_clid_dir(struct nfs4_client *clp)
 141{
 142        const struct cred *original_cred;
 143        char *dname = clp->cl_recdir;
 144        struct dentry *dir, *dentry;
 145        int status;
 146
 147        dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
 148
 149        if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
 150                return;
 151        if (!rec_file)
 152                return;
 153        status = nfs4_save_creds(&original_cred);
 154        if (status < 0)
 155                return;
 156
 157        status = mnt_want_write_file(rec_file);
 158        if (status)
 159                return;
 160
 161        dir = rec_file->f_path.dentry;
 162        /* lock the parent */
 163        mutex_lock(&dir->d_inode->i_mutex);
 164
 165        dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
 166        if (IS_ERR(dentry)) {
 167                status = PTR_ERR(dentry);
 168                goto out_unlock;
 169        }
 170        if (dentry->d_inode)
 171                /*
 172                 * In the 4.1 case, where we're called from
 173                 * reclaim_complete(), records from the previous reboot
 174                 * may still be left, so this is OK.
 175                 *
 176                 * In the 4.0 case, we should never get here; but we may
 177                 * as well be forgiving and just succeed silently.
 178                 */
 179                goto out_put;
 180        status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
 181out_put:
 182        dput(dentry);
 183out_unlock:
 184        mutex_unlock(&dir->d_inode->i_mutex);
 185        if (status == 0)
 186                vfs_fsync(rec_file, 0);
 187        else
 188                printk(KERN_ERR "NFSD: failed to write recovery record"
 189                                " (err %d); please check that %s exists"
 190                                " and is writeable", status,
 191                                user_recovery_dirname);
 192        mnt_drop_write_file(rec_file);
 193        nfs4_reset_creds(original_cred);
 194}
 195
 196typedef int (recdir_func)(struct dentry *, struct dentry *);
 197
 198struct name_list {
 199        char name[HEXDIR_LEN];
 200        struct list_head list;
 201};
 202
 203static int
 204nfsd4_build_namelist(void *arg, const char *name, int namlen,
 205                loff_t offset, u64 ino, unsigned int d_type)
 206{
 207        struct list_head *names = arg;
 208        struct name_list *entry;
 209
 210        if (namlen != HEXDIR_LEN - 1)
 211                return 0;
 212        entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
 213        if (entry == NULL)
 214                return -ENOMEM;
 215        memcpy(entry->name, name, HEXDIR_LEN - 1);
 216        entry->name[HEXDIR_LEN - 1] = '\0';
 217        list_add(&entry->list, names);
 218        return 0;
 219}
 220
 221static int
 222nfsd4_list_rec_dir(recdir_func *f)
 223{
 224        const struct cred *original_cred;
 225        struct dentry *dir = rec_file->f_path.dentry;
 226        LIST_HEAD(names);
 227        int status;
 228
 229        status = nfs4_save_creds(&original_cred);
 230        if (status < 0)
 231                return status;
 232
 233        status = vfs_llseek(rec_file, 0, SEEK_SET);
 234        if (status < 0) {
 235                nfs4_reset_creds(original_cred);
 236                return status;
 237        }
 238
 239        status = vfs_readdir(rec_file, nfsd4_build_namelist, &names);
 240        mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
 241        while (!list_empty(&names)) {
 242                struct name_list *entry;
 243                entry = list_entry(names.next, struct name_list, list);
 244                if (!status) {
 245                        struct dentry *dentry;
 246                        dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
 247                        if (IS_ERR(dentry)) {
 248                                status = PTR_ERR(dentry);
 249                                break;
 250                        }
 251                        status = f(dir, dentry);
 252                        dput(dentry);
 253                }
 254                list_del(&entry->list);
 255                kfree(entry);
 256        }
 257        mutex_unlock(&dir->d_inode->i_mutex);
 258        nfs4_reset_creds(original_cred);
 259        return status;
 260}
 261
 262static int
 263nfsd4_unlink_clid_dir(char *name, int namlen)
 264{
 265        struct dentry *dir, *dentry;
 266        int status;
 267
 268        dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
 269
 270        dir = rec_file->f_path.dentry;
 271        mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
 272        dentry = lookup_one_len(name, dir, namlen);
 273        if (IS_ERR(dentry)) {
 274                status = PTR_ERR(dentry);
 275                goto out_unlock;
 276        }
 277        status = -ENOENT;
 278        if (!dentry->d_inode)
 279                goto out;
 280        status = vfs_rmdir(dir->d_inode, dentry);
 281out:
 282        dput(dentry);
 283out_unlock:
 284        mutex_unlock(&dir->d_inode->i_mutex);
 285        return status;
 286}
 287
 288static void
 289nfsd4_remove_clid_dir(struct nfs4_client *clp)
 290{
 291        const struct cred *original_cred;
 292        int status;
 293
 294        if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
 295                return;
 296
 297        status = mnt_want_write_file(rec_file);
 298        if (status)
 299                goto out;
 300        clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
 301
 302        status = nfs4_save_creds(&original_cred);
 303        if (status < 0)
 304                goto out;
 305
 306        status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
 307        nfs4_reset_creds(original_cred);
 308        if (status == 0)
 309                vfs_fsync(rec_file, 0);
 310        mnt_drop_write_file(rec_file);
 311out:
 312        if (status)
 313                printk("NFSD: Failed to remove expired client state directory"
 314                                " %.*s\n", HEXDIR_LEN, clp->cl_recdir);
 315}
 316
 317static int
 318purge_old(struct dentry *parent, struct dentry *child)
 319{
 320        int status;
 321
 322        if (nfs4_has_reclaimed_state(child->d_name.name, false))
 323                return 0;
 324
 325        status = vfs_rmdir(parent->d_inode, child);
 326        if (status)
 327                printk("failed to remove client recovery directory %s\n",
 328                                child->d_name.name);
 329        /* Keep trying, success or failure: */
 330        return 0;
 331}
 332
 333static void
 334nfsd4_recdir_purge_old(struct net *net, time_t boot_time)
 335{
 336        int status;
 337
 338        if (!rec_file)
 339                return;
 340        status = mnt_want_write_file(rec_file);
 341        if (status)
 342                goto out;
 343        status = nfsd4_list_rec_dir(purge_old);
 344        if (status == 0)
 345                vfs_fsync(rec_file, 0);
 346        mnt_drop_write_file(rec_file);
 347out:
 348        if (status)
 349                printk("nfsd4: failed to purge old clients from recovery"
 350                        " directory %s\n", rec_file->f_path.dentry->d_name.name);
 351}
 352
 353static int
 354load_recdir(struct dentry *parent, struct dentry *child)
 355{
 356        if (child->d_name.len != HEXDIR_LEN - 1) {
 357                printk("nfsd4: illegal name %s in recovery directory\n",
 358                                child->d_name.name);
 359                /* Keep trying; maybe the others are OK: */
 360                return 0;
 361        }
 362        nfs4_client_to_reclaim(child->d_name.name);
 363        return 0;
 364}
 365
 366static int
 367nfsd4_recdir_load(void) {
 368        int status;
 369
 370        if (!rec_file)
 371                return 0;
 372
 373        status = nfsd4_list_rec_dir(load_recdir);
 374        if (status)
 375                printk("nfsd4: failed loading clients from recovery"
 376                        " directory %s\n", rec_file->f_path.dentry->d_name.name);
 377        return status;
 378}
 379
 380/*
 381 * Hold reference to the recovery directory.
 382 */
 383
 384static int
 385nfsd4_init_recdir(void)
 386{
 387        const struct cred *original_cred;
 388        int status;
 389
 390        printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
 391                        user_recovery_dirname);
 392
 393        BUG_ON(rec_file);
 394
 395        status = nfs4_save_creds(&original_cred);
 396        if (status < 0) {
 397                printk("NFSD: Unable to change credentials to find recovery"
 398                       " directory: error %d\n",
 399                       status);
 400                return status;
 401        }
 402
 403        rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
 404        if (IS_ERR(rec_file)) {
 405                printk("NFSD: unable to find recovery directory %s\n",
 406                                user_recovery_dirname);
 407                status = PTR_ERR(rec_file);
 408                rec_file = NULL;
 409        }
 410
 411        nfs4_reset_creds(original_cred);
 412        return status;
 413}
 414
 415static int
 416nfsd4_load_reboot_recovery_data(struct net *net)
 417{
 418        int status;
 419
 420        /* XXX: The legacy code won't work in a container */
 421        if (net != &init_net) {
 422                WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client "
 423                        "tracking in a container!\n");
 424                return -EINVAL;
 425        }
 426
 427        nfs4_lock_state();
 428        status = nfsd4_init_recdir();
 429        if (!status)
 430                status = nfsd4_recdir_load();
 431        nfs4_unlock_state();
 432        if (status)
 433                printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n");
 434        return status;
 435}
 436
 437static void
 438nfsd4_shutdown_recdir(void)
 439{
 440        if (!rec_file)
 441                return;
 442        fput(rec_file);
 443        rec_file = NULL;
 444}
 445
 446static void
 447nfsd4_legacy_tracking_exit(struct net *net)
 448{
 449        nfs4_release_reclaim();
 450        nfsd4_shutdown_recdir();
 451}
 452
 453/*
 454 * Change the NFSv4 recovery directory to recdir.
 455 */
 456int
 457nfs4_reset_recoverydir(char *recdir)
 458{
 459        int status;
 460        struct path path;
 461
 462        status = kern_path(recdir, LOOKUP_FOLLOW, &path);
 463        if (status)
 464                return status;
 465        status = -ENOTDIR;
 466        if (S_ISDIR(path.dentry->d_inode->i_mode)) {
 467                strcpy(user_recovery_dirname, recdir);
 468                status = 0;
 469        }
 470        path_put(&path);
 471        return status;
 472}
 473
 474char *
 475nfs4_recoverydir(void)
 476{
 477        return user_recovery_dirname;
 478}
 479
 480static int
 481nfsd4_check_legacy_client(struct nfs4_client *clp)
 482{
 483        /* did we already find that this client is stable? */
 484        if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
 485                return 0;
 486
 487        /* look for it in the reclaim hashtable otherwise */
 488        if (nfsd4_find_reclaim_client(clp)) {
 489                set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
 490                return 0;
 491        }
 492
 493        return -ENOENT;
 494}
 495
 496static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
 497        .init           = nfsd4_load_reboot_recovery_data,
 498        .exit           = nfsd4_legacy_tracking_exit,
 499        .create         = nfsd4_create_clid_dir,
 500        .remove         = nfsd4_remove_clid_dir,
 501        .check          = nfsd4_check_legacy_client,
 502        .grace_done     = nfsd4_recdir_purge_old,
 503};
 504
 505/* Globals */
 506#define NFSD_PIPE_DIR           "nfsd"
 507#define NFSD_CLD_PIPE           "cld"
 508
 509/* per-net-ns structure for holding cld upcall info */
 510struct cld_net {
 511        struct rpc_pipe         *cn_pipe;
 512        spinlock_t               cn_lock;
 513        struct list_head         cn_list;
 514        unsigned int             cn_xid;
 515};
 516
 517struct cld_upcall {
 518        struct list_head         cu_list;
 519        struct cld_net          *cu_net;
 520        struct task_struct      *cu_task;
 521        struct cld_msg           cu_msg;
 522};
 523
 524static int
 525__cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
 526{
 527        int ret;
 528        struct rpc_pipe_msg msg;
 529
 530        memset(&msg, 0, sizeof(msg));
 531        msg.data = cmsg;
 532        msg.len = sizeof(*cmsg);
 533
 534        /*
 535         * Set task state before we queue the upcall. That prevents
 536         * wake_up_process in the downcall from racing with schedule.
 537         */
 538        set_current_state(TASK_UNINTERRUPTIBLE);
 539        ret = rpc_queue_upcall(pipe, &msg);
 540        if (ret < 0) {
 541                set_current_state(TASK_RUNNING);
 542                goto out;
 543        }
 544
 545        schedule();
 546        set_current_state(TASK_RUNNING);
 547
 548        if (msg.errno < 0)
 549                ret = msg.errno;
 550out:
 551        return ret;
 552}
 553
 554static int
 555cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
 556{
 557        int ret;
 558
 559        /*
 560         * -EAGAIN occurs when pipe is closed and reopened while there are
 561         *  upcalls queued.
 562         */
 563        do {
 564                ret = __cld_pipe_upcall(pipe, cmsg);
 565        } while (ret == -EAGAIN);
 566
 567        return ret;
 568}
 569
 570static ssize_t
 571cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
 572{
 573        struct cld_upcall *tmp, *cup;
 574        struct cld_msg __user *cmsg = (struct cld_msg __user *)src;
 575        uint32_t xid;
 576        struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
 577                                                nfsd_net_id);
 578        struct cld_net *cn = nn->cld_net;
 579
 580        if (mlen != sizeof(*cmsg)) {
 581                dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen,
 582                        sizeof(*cmsg));
 583                return -EINVAL;
 584        }
 585
 586        /* copy just the xid so we can try to find that */
 587        if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) {
 588                dprintk("%s: error when copying xid from userspace", __func__);
 589                return -EFAULT;
 590        }
 591
 592        /* walk the list and find corresponding xid */
 593        cup = NULL;
 594        spin_lock(&cn->cn_lock);
 595        list_for_each_entry(tmp, &cn->cn_list, cu_list) {
 596                if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) {
 597                        cup = tmp;
 598                        list_del_init(&cup->cu_list);
 599                        break;
 600                }
 601        }
 602        spin_unlock(&cn->cn_lock);
 603
 604        /* couldn't find upcall? */
 605        if (!cup) {
 606                dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid);
 607                return -EINVAL;
 608        }
 609
 610        if (copy_from_user(&cup->cu_msg, src, mlen) != 0)
 611                return -EFAULT;
 612
 613        wake_up_process(cup->cu_task);
 614        return mlen;
 615}
 616
 617static void
 618cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
 619{
 620        struct cld_msg *cmsg = msg->data;
 621        struct cld_upcall *cup = container_of(cmsg, struct cld_upcall,
 622                                                 cu_msg);
 623
 624        /* errno >= 0 means we got a downcall */
 625        if (msg->errno >= 0)
 626                return;
 627
 628        wake_up_process(cup->cu_task);
 629}
 630
 631static const struct rpc_pipe_ops cld_upcall_ops = {
 632        .upcall         = rpc_pipe_generic_upcall,
 633        .downcall       = cld_pipe_downcall,
 634        .destroy_msg    = cld_pipe_destroy_msg,
 635};
 636
 637static struct dentry *
 638nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe)
 639{
 640        struct dentry *dir, *dentry;
 641
 642        dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR);
 643        if (dir == NULL)
 644                return ERR_PTR(-ENOENT);
 645        dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
 646        dput(dir);
 647        return dentry;
 648}
 649
 650static void
 651nfsd4_cld_unregister_sb(struct rpc_pipe *pipe)
 652{
 653        if (pipe->dentry)
 654                rpc_unlink(pipe->dentry);
 655}
 656
 657static struct dentry *
 658nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe)
 659{
 660        struct super_block *sb;
 661        struct dentry *dentry;
 662
 663        sb = rpc_get_sb_net(net);
 664        if (!sb)
 665                return NULL;
 666        dentry = nfsd4_cld_register_sb(sb, pipe);
 667        rpc_put_sb_net(net);
 668        return dentry;
 669}
 670
 671static void
 672nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe)
 673{
 674        struct super_block *sb;
 675
 676        sb = rpc_get_sb_net(net);
 677        if (sb) {
 678                nfsd4_cld_unregister_sb(pipe);
 679                rpc_put_sb_net(net);
 680        }
 681}
 682
 683/* Initialize rpc_pipefs pipe for communication with client tracking daemon */
 684static int
 685nfsd4_init_cld_pipe(struct net *net)
 686{
 687        int ret;
 688        struct dentry *dentry;
 689        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 690        struct cld_net *cn;
 691
 692        if (nn->cld_net)
 693                return 0;
 694
 695        cn = kzalloc(sizeof(*cn), GFP_KERNEL);
 696        if (!cn) {
 697                ret = -ENOMEM;
 698                goto err;
 699        }
 700
 701        cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
 702        if (IS_ERR(cn->cn_pipe)) {
 703                ret = PTR_ERR(cn->cn_pipe);
 704                goto err;
 705        }
 706        spin_lock_init(&cn->cn_lock);
 707        INIT_LIST_HEAD(&cn->cn_list);
 708
 709        dentry = nfsd4_cld_register_net(net, cn->cn_pipe);
 710        if (IS_ERR(dentry)) {
 711                ret = PTR_ERR(dentry);
 712                goto err_destroy_data;
 713        }
 714
 715        cn->cn_pipe->dentry = dentry;
 716        nn->cld_net = cn;
 717        return 0;
 718
 719err_destroy_data:
 720        rpc_destroy_pipe_data(cn->cn_pipe);
 721err:
 722        kfree(cn);
 723        printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n",
 724                        ret);
 725        return ret;
 726}
 727
 728static void
 729nfsd4_remove_cld_pipe(struct net *net)
 730{
 731        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 732        struct cld_net *cn = nn->cld_net;
 733
 734        nfsd4_cld_unregister_net(net, cn->cn_pipe);
 735        rpc_destroy_pipe_data(cn->cn_pipe);
 736        kfree(nn->cld_net);
 737        nn->cld_net = NULL;
 738}
 739
 740static struct cld_upcall *
 741alloc_cld_upcall(struct cld_net *cn)
 742{
 743        struct cld_upcall *new, *tmp;
 744
 745        new = kzalloc(sizeof(*new), GFP_KERNEL);
 746        if (!new)
 747                return new;
 748
 749        /* FIXME: hard cap on number in flight? */
 750restart_search:
 751        spin_lock(&cn->cn_lock);
 752        list_for_each_entry(tmp, &cn->cn_list, cu_list) {
 753                if (tmp->cu_msg.cm_xid == cn->cn_xid) {
 754                        cn->cn_xid++;
 755                        spin_unlock(&cn->cn_lock);
 756                        goto restart_search;
 757                }
 758        }
 759        new->cu_task = current;
 760        new->cu_msg.cm_vers = CLD_UPCALL_VERSION;
 761        put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid);
 762        new->cu_net = cn;
 763        list_add(&new->cu_list, &cn->cn_list);
 764        spin_unlock(&cn->cn_lock);
 765
 766        dprintk("%s: allocated xid %u\n", __func__, new->cu_msg.cm_xid);
 767
 768        return new;
 769}
 770
 771static void
 772free_cld_upcall(struct cld_upcall *victim)
 773{
 774        struct cld_net *cn = victim->cu_net;
 775
 776        spin_lock(&cn->cn_lock);
 777        list_del(&victim->cu_list);
 778        spin_unlock(&cn->cn_lock);
 779        kfree(victim);
 780}
 781
 782/* Ask daemon to create a new record */
 783static void
 784nfsd4_cld_create(struct nfs4_client *clp)
 785{
 786        int ret;
 787        struct cld_upcall *cup;
 788        /* FIXME: determine net from clp */
 789        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 790        struct cld_net *cn = nn->cld_net;
 791
 792        /* Don't upcall if it's already stored */
 793        if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
 794                return;
 795
 796        cup = alloc_cld_upcall(cn);
 797        if (!cup) {
 798                ret = -ENOMEM;
 799                goto out_err;
 800        }
 801
 802        cup->cu_msg.cm_cmd = Cld_Create;
 803        cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
 804        memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
 805                        clp->cl_name.len);
 806
 807        ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
 808        if (!ret) {
 809                ret = cup->cu_msg.cm_status;
 810                set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
 811        }
 812
 813        free_cld_upcall(cup);
 814out_err:
 815        if (ret)
 816                printk(KERN_ERR "NFSD: Unable to create client "
 817                                "record on stable storage: %d\n", ret);
 818}
 819
 820/* Ask daemon to create a new record */
 821static void
 822nfsd4_cld_remove(struct nfs4_client *clp)
 823{
 824        int ret;
 825        struct cld_upcall *cup;
 826        /* FIXME: determine net from clp */
 827        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 828        struct cld_net *cn = nn->cld_net;
 829
 830        /* Don't upcall if it's already removed */
 831        if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
 832                return;
 833
 834        cup = alloc_cld_upcall(cn);
 835        if (!cup) {
 836                ret = -ENOMEM;
 837                goto out_err;
 838        }
 839
 840        cup->cu_msg.cm_cmd = Cld_Remove;
 841        cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
 842        memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
 843                        clp->cl_name.len);
 844
 845        ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
 846        if (!ret) {
 847                ret = cup->cu_msg.cm_status;
 848                clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
 849        }
 850
 851        free_cld_upcall(cup);
 852out_err:
 853        if (ret)
 854                printk(KERN_ERR "NFSD: Unable to remove client "
 855                                "record from stable storage: %d\n", ret);
 856}
 857
 858/* Check for presence of a record, and update its timestamp */
 859static int
 860nfsd4_cld_check(struct nfs4_client *clp)
 861{
 862        int ret;
 863        struct cld_upcall *cup;
 864        /* FIXME: determine net from clp */
 865        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 866        struct cld_net *cn = nn->cld_net;
 867
 868        /* Don't upcall if one was already stored during this grace pd */
 869        if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
 870                return 0;
 871
 872        cup = alloc_cld_upcall(cn);
 873        if (!cup) {
 874                printk(KERN_ERR "NFSD: Unable to check client record on "
 875                                "stable storage: %d\n", -ENOMEM);
 876                return -ENOMEM;
 877        }
 878
 879        cup->cu_msg.cm_cmd = Cld_Check;
 880        cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
 881        memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
 882                        clp->cl_name.len);
 883
 884        ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
 885        if (!ret) {
 886                ret = cup->cu_msg.cm_status;
 887                set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
 888        }
 889
 890        free_cld_upcall(cup);
 891        return ret;
 892}
 893
 894static void
 895nfsd4_cld_grace_done(struct net *net, time_t boot_time)
 896{
 897        int ret;
 898        struct cld_upcall *cup;
 899        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 900        struct cld_net *cn = nn->cld_net;
 901
 902        cup = alloc_cld_upcall(cn);
 903        if (!cup) {
 904                ret = -ENOMEM;
 905                goto out_err;
 906        }
 907
 908        cup->cu_msg.cm_cmd = Cld_GraceDone;
 909        cup->cu_msg.cm_u.cm_gracetime = (int64_t)boot_time;
 910        ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
 911        if (!ret)
 912                ret = cup->cu_msg.cm_status;
 913
 914        free_cld_upcall(cup);
 915out_err:
 916        if (ret)
 917                printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
 918}
 919
 920static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
 921        .init           = nfsd4_init_cld_pipe,
 922        .exit           = nfsd4_remove_cld_pipe,
 923        .create         = nfsd4_cld_create,
 924        .remove         = nfsd4_cld_remove,
 925        .check          = nfsd4_cld_check,
 926        .grace_done     = nfsd4_cld_grace_done,
 927};
 928
 929int
 930nfsd4_client_tracking_init(struct net *net)
 931{
 932        int status;
 933        struct path path;
 934
 935        if (!client_tracking_ops) {
 936                client_tracking_ops = &nfsd4_cld_tracking_ops;
 937                status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
 938                if (!status) {
 939                        if (S_ISDIR(path.dentry->d_inode->i_mode))
 940                                client_tracking_ops =
 941                                                &nfsd4_legacy_tracking_ops;
 942                        path_put(&path);
 943                }
 944        }
 945
 946        status = client_tracking_ops->init(net);
 947        if (status) {
 948                printk(KERN_WARNING "NFSD: Unable to initialize client "
 949                                    "recovery tracking! (%d)\n", status);
 950                client_tracking_ops = NULL;
 951        }
 952        return status;
 953}
 954
 955void
 956nfsd4_client_tracking_exit(struct net *net)
 957{
 958        if (client_tracking_ops) {
 959                client_tracking_ops->exit(net);
 960                client_tracking_ops = NULL;
 961        }
 962}
 963
 964void
 965nfsd4_client_record_create(struct nfs4_client *clp)
 966{
 967        if (client_tracking_ops)
 968                client_tracking_ops->create(clp);
 969}
 970
 971void
 972nfsd4_client_record_remove(struct nfs4_client *clp)
 973{
 974        if (client_tracking_ops)
 975                client_tracking_ops->remove(clp);
 976}
 977
 978int
 979nfsd4_client_record_check(struct nfs4_client *clp)
 980{
 981        if (client_tracking_ops)
 982                return client_tracking_ops->check(clp);
 983
 984        return -EOPNOTSUPP;
 985}
 986
 987void
 988nfsd4_record_grace_done(struct net *net, time_t boot_time)
 989{
 990        if (client_tracking_ops)
 991                client_tracking_ops->grace_done(net, boot_time);
 992}
 993
 994static int
 995rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
 996{
 997        struct super_block *sb = ptr;
 998        struct net *net = sb->s_fs_info;
 999        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1000        struct cld_net *cn = nn->cld_net;
1001        struct dentry *dentry;
1002        int ret = 0;
1003
1004        if (!try_module_get(THIS_MODULE))
1005                return 0;
1006
1007        if (!cn) {
1008                module_put(THIS_MODULE);
1009                return 0;
1010        }
1011
1012        switch (event) {
1013        case RPC_PIPEFS_MOUNT:
1014                dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe);
1015                if (IS_ERR(dentry)) {
1016                        ret = PTR_ERR(dentry);
1017                        break;
1018                }
1019                cn->cn_pipe->dentry = dentry;
1020                break;
1021        case RPC_PIPEFS_UMOUNT:
1022                if (cn->cn_pipe->dentry)
1023                        nfsd4_cld_unregister_sb(cn->cn_pipe);
1024                break;
1025        default:
1026                ret = -ENOTSUPP;
1027                break;
1028        }
1029        module_put(THIS_MODULE);
1030        return ret;
1031}
1032
1033static struct notifier_block nfsd4_cld_block = {
1034        .notifier_call = rpc_pipefs_event,
1035};
1036
1037int
1038register_cld_notifier(void)
1039{
1040        return rpc_pipefs_notifier_register(&nfsd4_cld_block);
1041}
1042
1043void
1044unregister_cld_notifier(void)
1045{
1046        rpc_pipefs_notifier_unregister(&nfsd4_cld_block);
1047}
1048
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.