linux/fs/nfsd/nfsctl.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfsd/nfsctl.c
   3 *
   4 * Syscall interface to knfsd.
   5 *
   6 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
   7 */
   8
   9#include <linux/module.h>
  10
  11#include <linux/linkage.h>
  12#include <linux/time.h>
  13#include <linux/errno.h>
  14#include <linux/fs.h>
  15#include <linux/fcntl.h>
  16#include <linux/net.h>
  17#include <linux/in.h>
  18#include <linux/syscalls.h>
  19#include <linux/unistd.h>
  20#include <linux/slab.h>
  21#include <linux/proc_fs.h>
  22#include <linux/seq_file.h>
  23#include <linux/pagemap.h>
  24#include <linux/init.h>
  25#include <linux/string.h>
  26#include <linux/smp_lock.h>
  27#include <linux/ctype.h>
  28
  29#include <linux/nfs.h>
  30#include <linux/nfsd_idmap.h>
  31#include <linux/lockd/bind.h>
  32#include <linux/sunrpc/svc.h>
  33#include <linux/sunrpc/svcsock.h>
  34#include <linux/nfsd/nfsd.h>
  35#include <linux/nfsd/cache.h>
  36#include <linux/nfsd/xdr.h>
  37#include <linux/nfsd/syscall.h>
  38
  39#include <asm/uaccess.h>
  40
  41/*
  42 *      We have a single directory with 9 nodes in it.
  43 */
  44enum {
  45        NFSD_Root = 1,
  46        NFSD_Svc,
  47        NFSD_Add,
  48        NFSD_Del,
  49        NFSD_Export,
  50        NFSD_Unexport,
  51        NFSD_Getfd,
  52        NFSD_Getfs,
  53        NFSD_List,
  54        NFSD_Fh,
  55        NFSD_Threads,
  56        NFSD_Pool_Threads,
  57        NFSD_Versions,
  58        NFSD_Ports,
  59        NFSD_MaxBlkSize,
  60        /*
  61         * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
  62         * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
  63         */
  64#ifdef CONFIG_NFSD_V4
  65        NFSD_Leasetime,
  66        NFSD_RecoveryDir,
  67#endif
  68};
  69
  70/*
  71 * write() for these nodes.
  72 */
  73static ssize_t write_svc(struct file *file, char *buf, size_t size);
  74static ssize_t write_add(struct file *file, char *buf, size_t size);
  75static ssize_t write_del(struct file *file, char *buf, size_t size);
  76static ssize_t write_export(struct file *file, char *buf, size_t size);
  77static ssize_t write_unexport(struct file *file, char *buf, size_t size);
  78static ssize_t write_getfd(struct file *file, char *buf, size_t size);
  79static ssize_t write_getfs(struct file *file, char *buf, size_t size);
  80static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
  81static ssize_t write_threads(struct file *file, char *buf, size_t size);
  82static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
  83static ssize_t write_versions(struct file *file, char *buf, size_t size);
  84static ssize_t write_ports(struct file *file, char *buf, size_t size);
  85static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
  86#ifdef CONFIG_NFSD_V4
  87static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
  88static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
  89#endif
  90
  91static ssize_t (*write_op[])(struct file *, char *, size_t) = {
  92        [NFSD_Svc] = write_svc,
  93        [NFSD_Add] = write_add,
  94        [NFSD_Del] = write_del,
  95        [NFSD_Export] = write_export,
  96        [NFSD_Unexport] = write_unexport,
  97        [NFSD_Getfd] = write_getfd,
  98        [NFSD_Getfs] = write_getfs,
  99        [NFSD_Fh] = write_filehandle,
 100        [NFSD_Threads] = write_threads,
 101        [NFSD_Pool_Threads] = write_pool_threads,
 102        [NFSD_Versions] = write_versions,
 103        [NFSD_Ports] = write_ports,
 104        [NFSD_MaxBlkSize] = write_maxblksize,
 105#ifdef CONFIG_NFSD_V4
 106        [NFSD_Leasetime] = write_leasetime,
 107        [NFSD_RecoveryDir] = write_recoverydir,
 108#endif
 109};
 110
 111static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
 112{
 113        ino_t ino =  file->f_path.dentry->d_inode->i_ino;
 114        char *data;
 115        ssize_t rv;
 116
 117        if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
 118                return -EINVAL;
 119
 120        data = simple_transaction_get(file, buf, size);
 121        if (IS_ERR(data))
 122                return PTR_ERR(data);
 123
 124        rv =  write_op[ino](file, data, size);
 125        if (rv >= 0) {
 126                simple_transaction_set(file, rv);
 127                rv = size;
 128        }
 129        return rv;
 130}
 131
 132static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
 133{
 134        if (! file->private_data) {
 135                /* An attempt to read a transaction file without writing
 136                 * causes a 0-byte write so that the file can return
 137                 * state information
 138                 */
 139                ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos);
 140                if (rv < 0)
 141                        return rv;
 142        }
 143        return simple_transaction_read(file, buf, size, pos);
 144}
 145
 146static const struct file_operations transaction_ops = {
 147        .write          = nfsctl_transaction_write,
 148        .read           = nfsctl_transaction_read,
 149        .release        = simple_transaction_release,
 150};
 151
 152extern struct seq_operations nfs_exports_op;
 153static int exports_open(struct inode *inode, struct file *file)
 154{
 155        return seq_open(file, &nfs_exports_op);
 156}
 157
 158static const struct file_operations exports_operations = {
 159        .open           = exports_open,
 160        .read           = seq_read,
 161        .llseek         = seq_lseek,
 162        .release        = seq_release,
 163};
 164
 165/*----------------------------------------------------------------------------*/
 166/*
 167 * payload - write methods
 168 * If the method has a response, the response should be put in buf,
 169 * and the length returned.  Otherwise return 0 or and -error.
 170 */
 171
 172static ssize_t write_svc(struct file *file, char *buf, size_t size)
 173{
 174        struct nfsctl_svc *data;
 175        if (size < sizeof(*data))
 176                return -EINVAL;
 177        data = (struct nfsctl_svc*) buf;
 178        return nfsd_svc(data->svc_port, data->svc_nthreads);
 179}
 180
 181static ssize_t write_add(struct file *file, char *buf, size_t size)
 182{
 183        struct nfsctl_client *data;
 184        if (size < sizeof(*data))
 185                return -EINVAL;
 186        data = (struct nfsctl_client *)buf;
 187        return exp_addclient(data);
 188}
 189
 190static ssize_t write_del(struct file *file, char *buf, size_t size)
 191{
 192        struct nfsctl_client *data;
 193        if (size < sizeof(*data))
 194                return -EINVAL;
 195        data = (struct nfsctl_client *)buf;
 196        return exp_delclient(data);
 197}
 198
 199static ssize_t write_export(struct file *file, char *buf, size_t size)
 200{
 201        struct nfsctl_export *data;
 202        if (size < sizeof(*data))
 203                return -EINVAL;
 204        data = (struct nfsctl_export*)buf;
 205        return exp_export(data);
 206}
 207
 208static ssize_t write_unexport(struct file *file, char *buf, size_t size)
 209{
 210        struct nfsctl_export *data;
 211
 212        if (size < sizeof(*data))
 213                return -EINVAL;
 214        data = (struct nfsctl_export*)buf;
 215        return exp_unexport(data);
 216}
 217
 218static ssize_t write_getfs(struct file *file, char *buf, size_t size)
 219{
 220        struct nfsctl_fsparm *data;
 221        struct sockaddr_in *sin;
 222        struct auth_domain *clp;
 223        int err = 0;
 224        struct knfsd_fh *res;
 225
 226        if (size < sizeof(*data))
 227                return -EINVAL;
 228        data = (struct nfsctl_fsparm*)buf;
 229        err = -EPROTONOSUPPORT;
 230        if (data->gd_addr.sa_family != AF_INET)
 231                goto out;
 232        sin = (struct sockaddr_in *)&data->gd_addr;
 233        if (data->gd_maxlen > NFS3_FHSIZE)
 234                data->gd_maxlen = NFS3_FHSIZE;
 235
 236        res = (struct knfsd_fh*)buf;
 237
 238        exp_readlock();
 239        if (!(clp = auth_unix_lookup(sin->sin_addr)))
 240                err = -EPERM;
 241        else {
 242                err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
 243                auth_domain_put(clp);
 244        }
 245        exp_readunlock();
 246        if (err == 0)
 247                err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
 248 out:
 249        return err;
 250}
 251
 252static ssize_t write_getfd(struct file *file, char *buf, size_t size)
 253{
 254        struct nfsctl_fdparm *data;
 255        struct sockaddr_in *sin;
 256        struct auth_domain *clp;
 257        int err = 0;
 258        struct knfsd_fh fh;
 259        char *res;
 260
 261        if (size < sizeof(*data))
 262                return -EINVAL;
 263        data = (struct nfsctl_fdparm*)buf;
 264        err = -EPROTONOSUPPORT;
 265        if (data->gd_addr.sa_family != AF_INET)
 266                goto out;
 267        err = -EINVAL;
 268        if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
 269                goto out;
 270
 271        res = buf;
 272        sin = (struct sockaddr_in *)&data->gd_addr;
 273        exp_readlock();
 274        if (!(clp = auth_unix_lookup(sin->sin_addr)))
 275                err = -EPERM;
 276        else {
 277                err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
 278                auth_domain_put(clp);
 279        }
 280        exp_readunlock();
 281
 282        if (err == 0) {
 283                memset(res,0, NFS_FHSIZE);
 284                memcpy(res, &fh.fh_base, fh.fh_size);
 285                err = NFS_FHSIZE;
 286        }
 287 out:
 288        return err;
 289}
 290
 291static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
 292{
 293        /* request is:
 294         *   domain path maxsize
 295         * response is
 296         *   filehandle
 297         *
 298         * qword quoting is used, so filehandle will be \x....
 299         */
 300        char *dname, *path;
 301        int uninitialized_var(maxsize);
 302        char *mesg = buf;
 303        int len;
 304        struct auth_domain *dom;
 305        struct knfsd_fh fh;
 306
 307        if (buf[size-1] != '\n')
 308                return -EINVAL;
 309        buf[size-1] = 0;
 310
 311        dname = mesg;
 312        len = qword_get(&mesg, dname, size);
 313        if (len <= 0) return -EINVAL;
 314        
 315        path = dname+len+1;
 316        len = qword_get(&mesg, path, size);
 317        if (len <= 0) return -EINVAL;
 318
 319        len = get_int(&mesg, &maxsize);
 320        if (len)
 321                return len;
 322
 323        if (maxsize < NFS_FHSIZE)
 324                return -EINVAL;
 325        if (maxsize > NFS3_FHSIZE)
 326                maxsize = NFS3_FHSIZE;
 327
 328        if (qword_get(&mesg, mesg, size)>0)
 329                return -EINVAL;
 330
 331        /* we have all the words, they are in buf.. */
 332        dom = unix_domain_find(dname);
 333        if (!dom)
 334                return -ENOMEM;
 335
 336        len = exp_rootfh(dom, path, &fh,  maxsize);
 337        auth_domain_put(dom);
 338        if (len)
 339                return len;
 340        
 341        mesg = buf; len = SIMPLE_TRANSACTION_LIMIT;
 342        qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
 343        mesg[-1] = '\n';
 344        return mesg - buf;      
 345}
 346
 347extern int nfsd_nrthreads(void);
 348
 349static ssize_t write_threads(struct file *file, char *buf, size_t size)
 350{
 351        /* if size > 0, look for a number of threads and call nfsd_svc
 352         * then write out number of threads as reply
 353         */
 354        char *mesg = buf;
 355        int rv;
 356        if (size > 0) {
 357                int newthreads;
 358                rv = get_int(&mesg, &newthreads);
 359                if (rv)
 360                        return rv;
 361                if (newthreads <0)
 362                        return -EINVAL;
 363                rv = nfsd_svc(2049, newthreads);
 364                if (rv)
 365                        return rv;
 366        }
 367        sprintf(buf, "%d\n", nfsd_nrthreads());
 368        return strlen(buf);
 369}
 370
 371extern int nfsd_nrpools(void);
 372extern int nfsd_get_nrthreads(int n, int *);
 373extern int nfsd_set_nrthreads(int n, int *);
 374
 375static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
 376{
 377        /* if size > 0, look for an array of number of threads per node
 378         * and apply them  then write out number of threads per node as reply
 379         */
 380        char *mesg = buf;
 381        int i;
 382        int rv;
 383        int len;
 384        int npools = nfsd_nrpools();
 385        int *nthreads;
 386
 387        if (npools == 0) {
 388                /*
 389                 * NFS is shut down.  The admin can start it by
 390                 * writing to the threads file but NOT the pool_threads
 391                 * file, sorry.  Report zero threads.
 392                 */
 393                strcpy(buf, "0\n");
 394                return strlen(buf);
 395        }
 396
 397        nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
 398        if (nthreads == NULL)
 399                return -ENOMEM;
 400
 401        if (size > 0) {
 402                for (i = 0; i < npools; i++) {
 403                        rv = get_int(&mesg, &nthreads[i]);
 404                        if (rv == -ENOENT)
 405                                break;          /* fewer numbers than pools */
 406                        if (rv)
 407                                goto out_free;  /* syntax error */
 408                        rv = -EINVAL;
 409                        if (nthreads[i] < 0)
 410                                goto out_free;
 411                }
 412                rv = nfsd_set_nrthreads(i, nthreads);
 413                if (rv)
 414                        goto out_free;
 415        }
 416
 417        rv = nfsd_get_nrthreads(npools, nthreads);
 418        if (rv)
 419                goto out_free;
 420
 421        mesg = buf;
 422        size = SIMPLE_TRANSACTION_LIMIT;
 423        for (i = 0; i < npools && size > 0; i++) {
 424                snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
 425                len = strlen(mesg);
 426                size -= len;
 427                mesg += len;
 428        }
 429
 430        return (mesg-buf);
 431
 432out_free:
 433        kfree(nthreads);
 434        return rv;
 435}
 436
 437static ssize_t write_versions(struct file *file, char *buf, size_t size)
 438{
 439        /*
 440         * Format:
 441         *   [-/+]vers [-/+]vers ...
 442         */
 443        char *mesg = buf;
 444        char *vers, sign;
 445        int len, num;
 446        ssize_t tlen = 0;
 447        char *sep;
 448
 449        if (size>0) {
 450                if (nfsd_serv)
 451                        /* Cannot change versions without updating
 452                         * nfsd_serv->sv_xdrsize, and reallocing
 453                         * rq_argp and rq_resp
 454                         */
 455                        return -EBUSY;
 456                if (buf[size-1] != '\n')
 457                        return -EINVAL;
 458                buf[size-1] = 0;
 459
 460                vers = mesg;
 461                len = qword_get(&mesg, vers, size);
 462                if (len <= 0) return -EINVAL;
 463                do {
 464                        sign = *vers;
 465                        if (sign == '+' || sign == '-')
 466                                num = simple_strtol((vers+1), NULL, 0);
 467                        else
 468                                num = simple_strtol(vers, NULL, 0);
 469                        switch(num) {
 470                        case 2:
 471                        case 3:
 472                        case 4:
 473                                nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
 474                                break;
 475                        default:
 476                                return -EINVAL;
 477                        }
 478                        vers += len + 1;
 479                        tlen += len;
 480                } while ((len = qword_get(&mesg, vers, size)) > 0);
 481                /* If all get turned off, turn them back on, as
 482                 * having no versions is BAD
 483                 */
 484                nfsd_reset_versions();
 485        }
 486        /* Now write current state into reply buffer */
 487        len = 0;
 488        sep = "";
 489        for (num=2 ; num <= 4 ; num++)
 490                if (nfsd_vers(num, NFSD_AVAIL)) {
 491                        len += sprintf(buf+len, "%s%c%d", sep,
 492                                       nfsd_vers(num, NFSD_TEST)?'+':'-',
 493                                       num);
 494                        sep = " ";
 495                }
 496        len += sprintf(buf+len, "\n");
 497        return len;
 498}
 499
 500static ssize_t write_ports(struct file *file, char *buf, size_t size)
 501{
 502        if (size == 0) {
 503                int len = 0;
 504                lock_kernel();
 505                if (nfsd_serv)
 506                        len = svc_sock_names(buf, nfsd_serv, NULL);
 507                unlock_kernel();
 508                return len;
 509        }
 510        /* Either a single 'fd' number is written, in which
 511         * case it must be for a socket of a supported family/protocol,
 512         * and we use it as an nfsd socket, or
 513         * A '-' followed by the 'name' of a socket in which case
 514         * we close the socket.
 515         */
 516        if (isdigit(buf[0])) {
 517                char *mesg = buf;
 518                int fd;
 519                int err;
 520                err = get_int(&mesg, &fd);
 521                if (err)
 522                        return -EINVAL;
 523                if (fd < 0)
 524                        return -EINVAL;
 525                err = nfsd_create_serv();
 526                if (!err) {
 527                        int proto = 0;
 528                        err = svc_addsock(nfsd_serv, fd, buf, &proto);
 529                        if (err >= 0) {
 530                                err = lockd_up(proto);
 531                                if (err < 0)
 532                                        svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
 533                        }
 534                        /* Decrease the count, but don't shutdown the
 535                         * the service
 536                         */
 537                        lock_kernel();
 538                        nfsd_serv->sv_nrthreads--;
 539                        unlock_kernel();
 540                }
 541                return err < 0 ? err : 0;
 542        }
 543        if (buf[0] == '-') {
 544                char *toclose = kstrdup(buf+1, GFP_KERNEL);
 545                int len = 0;
 546                if (!toclose)
 547                        return -ENOMEM;
 548                lock_kernel();
 549                if (nfsd_serv)
 550                        len = svc_sock_names(buf, nfsd_serv, toclose);
 551                unlock_kernel();
 552                if (len >= 0)
 553                        lockd_down();
 554                kfree(toclose);
 555                return len;
 556        }
 557        return -EINVAL;
 558}
 559
 560int nfsd_max_blksize;
 561
 562static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
 563{
 564        char *mesg = buf;
 565        if (size > 0) {
 566                int bsize;
 567                int rv = get_int(&mesg, &bsize);
 568                if (rv)
 569                        return rv;
 570                /* force bsize into allowed range and
 571                 * required alignment.
 572                 */
 573                if (bsize < 1024)
 574                        bsize = 1024;
 575                if (bsize > NFSSVC_MAXBLKSIZE)
 576                        bsize = NFSSVC_MAXBLKSIZE;
 577                bsize &= ~(1024-1);
 578                lock_kernel();
 579                if (nfsd_serv && nfsd_serv->sv_nrthreads) {
 580                        unlock_kernel();
 581                        return -EBUSY;
 582                }
 583                nfsd_max_blksize = bsize;
 584                unlock_kernel();
 585        }
 586        return sprintf(buf, "%d\n", nfsd_max_blksize);
 587}
 588
 589#ifdef CONFIG_NFSD_V4
 590extern time_t nfs4_leasetime(void);
 591
 592static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
 593{
 594        /* if size > 10 seconds, call
 595         * nfs4_reset_lease() then write out the new lease (seconds) as reply
 596         */
 597        char *mesg = buf;
 598        int rv;
 599
 600        if (size > 0) {
 601                int lease;
 602                rv = get_int(&mesg, &lease);
 603                if (rv)
 604                        return rv;
 605                if (lease < 10 || lease > 3600)
 606                        return -EINVAL;
 607                nfs4_reset_lease(lease);
 608        }
 609        sprintf(buf, "%ld\n", nfs4_lease_time());
 610        return strlen(buf);
 611}
 612
 613static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
 614{
 615        char *mesg = buf;
 616        char *recdir;
 617        int len, status;
 618
 619        if (size > PATH_MAX || buf[size-1] != '\n')
 620                return -EINVAL;
 621        buf[size-1] = 0;
 622
 623        recdir = mesg;
 624        len = qword_get(&mesg, recdir, size);
 625        if (len <= 0)
 626                return -EINVAL;
 627
 628        status = nfs4_reset_recoverydir(recdir);
 629        return strlen(buf);
 630}
 631#endif
 632
 633/*----------------------------------------------------------------------------*/
 634/*
 635 *      populating the filesystem.
 636 */
 637
 638static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
 639{
 640        static struct tree_descr nfsd_files[] = {
 641                [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
 642                [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
 643                [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
 644                [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
 645                [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
 646                [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
 647                [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
 648                [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
 649                [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
 650                [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
 651                [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
 652                [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
 653                [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
 654                [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
 655#ifdef CONFIG_NFSD_V4
 656                [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
 657                [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
 658#endif
 659                /* last one */ {""}
 660        };
 661        return simple_fill_super(sb, 0x6e667364, nfsd_files);
 662}
 663
 664static int nfsd_get_sb(struct file_system_type *fs_type,
 665        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 666{
 667        return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
 668}
 669
 670static struct file_system_type nfsd_fs_type = {
 671        .owner          = THIS_MODULE,
 672        .name           = "nfsd",
 673        .get_sb         = nfsd_get_sb,
 674        .kill_sb        = kill_litter_super,
 675};
 676
 677static int __init init_nfsd(void)
 678{
 679        int retval;
 680        printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
 681
 682        retval = nfs4_state_init(); /* nfs4 locking state */
 683        if (retval)
 684                return retval;
 685        nfsd_stat_init();       /* Statistics */
 686        nfsd_cache_init();      /* RPC reply cache */
 687        nfsd_export_init();     /* Exports table */
 688        nfsd_lockd_init();      /* lockd->nfsd callbacks */
 689        nfsd_idmap_init();      /* Name to ID mapping */
 690        if (proc_mkdir("fs/nfs", NULL)) {
 691                struct proc_dir_entry *entry;
 692                entry = create_proc_entry("fs/nfs/exports", 0, NULL);
 693                if (entry)
 694                        entry->proc_fops =  &exports_operations;
 695        }
 696        retval = register_filesystem(&nfsd_fs_type);
 697        if (retval) {
 698                nfsd_export_shutdown();
 699                nfsd_cache_shutdown();
 700                remove_proc_entry("fs/nfs/exports", NULL);
 701                remove_proc_entry("fs/nfs", NULL);
 702                nfsd_stat_shutdown();
 703                nfsd_lockd_shutdown();
 704        }
 705        return retval;
 706}
 707
 708static void __exit exit_nfsd(void)
 709{
 710        nfsd_export_shutdown();
 711        nfsd_cache_shutdown();
 712        remove_proc_entry("fs/nfs/exports", NULL);
 713        remove_proc_entry("fs/nfs", NULL);
 714        nfsd_stat_shutdown();
 715        nfsd_lockd_shutdown();
 716        nfsd_idmap_shutdown();
 717        nfsd4_free_slabs();
 718        unregister_filesystem(&nfsd_fs_type);
 719}
 720
 721MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
 722MODULE_LICENSE("GPL");
 723module_init(init_nfsd)
 724module_exit(exit_nfsd)
 725