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/namei.h>
  16#include <linux/fcntl.h>
  17#include <linux/net.h>
  18#include <linux/in.h>
  19#include <linux/syscalls.h>
  20#include <linux/unistd.h>
  21#include <linux/slab.h>
  22#include <linux/proc_fs.h>
  23#include <linux/seq_file.h>
  24#include <linux/pagemap.h>
  25#include <linux/init.h>
  26#include <linux/inet.h>
  27#include <linux/string.h>
  28#include <linux/ctype.h>
  29
  30#include <linux/nfs.h>
  31#include <linux/nfsd_idmap.h>
  32#include <linux/lockd/bind.h>
  33#include <linux/sunrpc/svc.h>
  34#include <linux/sunrpc/svcsock.h>
  35#include <linux/nfsd/nfsd.h>
  36#include <linux/nfsd/cache.h>
  37#include <linux/nfsd/xdr.h>
  38#include <linux/nfsd/syscall.h>
  39#include <linux/lockd/lockd.h>
  40#include <linux/sunrpc/clnt.h>
  41
  42#include <asm/uaccess.h>
  43#include <net/ipv6.h>
  44
  45/*
  46 *      We have a single directory with 9 nodes in it.
  47 */
  48enum {
  49        NFSD_Root = 1,
  50        NFSD_Svc,
  51        NFSD_Add,
  52        NFSD_Del,
  53        NFSD_Export,
  54        NFSD_Unexport,
  55        NFSD_Getfd,
  56        NFSD_Getfs,
  57        NFSD_List,
  58        NFSD_Fh,
  59        NFSD_FO_UnlockIP,
  60        NFSD_FO_UnlockFS,
  61        NFSD_Threads,
  62        NFSD_Pool_Threads,
  63        NFSD_Pool_Stats,
  64        NFSD_Versions,
  65        NFSD_Ports,
  66        NFSD_MaxBlkSize,
  67        /*
  68         * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
  69         * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
  70         */
  71#ifdef CONFIG_NFSD_V4
  72        NFSD_Leasetime,
  73        NFSD_RecoveryDir,
  74#endif
  75};
  76
  77/*
  78 * write() for these nodes.
  79 */
  80static ssize_t write_svc(struct file *file, char *buf, size_t size);
  81static ssize_t write_add(struct file *file, char *buf, size_t size);
  82static ssize_t write_del(struct file *file, char *buf, size_t size);
  83static ssize_t write_export(struct file *file, char *buf, size_t size);
  84static ssize_t write_unexport(struct file *file, char *buf, size_t size);
  85static ssize_t write_getfd(struct file *file, char *buf, size_t size);
  86static ssize_t write_getfs(struct file *file, char *buf, size_t size);
  87static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
  88static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
  89static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
  90static ssize_t write_threads(struct file *file, char *buf, size_t size);
  91static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
  92static ssize_t write_versions(struct file *file, char *buf, size_t size);
  93static ssize_t write_ports(struct file *file, char *buf, size_t size);
  94static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
  95#ifdef CONFIG_NFSD_V4
  96static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
  97static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
  98#endif
  99
 100static ssize_t (*write_op[])(struct file *, char *, size_t) = {
 101        [NFSD_Svc] = write_svc,
 102        [NFSD_Add] = write_add,
 103        [NFSD_Del] = write_del,
 104        [NFSD_Export] = write_export,
 105        [NFSD_Unexport] = write_unexport,
 106        [NFSD_Getfd] = write_getfd,
 107        [NFSD_Getfs] = write_getfs,
 108        [NFSD_Fh] = write_filehandle,
 109        [NFSD_FO_UnlockIP] = write_unlock_ip,
 110        [NFSD_FO_UnlockFS] = write_unlock_fs,
 111        [NFSD_Threads] = write_threads,
 112        [NFSD_Pool_Threads] = write_pool_threads,
 113        [NFSD_Versions] = write_versions,
 114        [NFSD_Ports] = write_ports,
 115        [NFSD_MaxBlkSize] = write_maxblksize,
 116#ifdef CONFIG_NFSD_V4
 117        [NFSD_Leasetime] = write_leasetime,
 118        [NFSD_RecoveryDir] = write_recoverydir,
 119#endif
 120};
 121
 122static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
 123{
 124        ino_t ino =  file->f_path.dentry->d_inode->i_ino;
 125        char *data;
 126        ssize_t rv;
 127
 128        if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
 129                return -EINVAL;
 130
 131        data = simple_transaction_get(file, buf, size);
 132        if (IS_ERR(data))
 133                return PTR_ERR(data);
 134
 135        rv =  write_op[ino](file, data, size);
 136        if (rv >= 0) {
 137                simple_transaction_set(file, rv);
 138                rv = size;
 139        }
 140        return rv;
 141}
 142
 143static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
 144{
 145        if (! file->private_data) {
 146                /* An attempt to read a transaction file without writing
 147                 * causes a 0-byte write so that the file can return
 148                 * state information
 149                 */
 150                ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos);
 151                if (rv < 0)
 152                        return rv;
 153        }
 154        return simple_transaction_read(file, buf, size, pos);
 155}
 156
 157static const struct file_operations transaction_ops = {
 158        .write          = nfsctl_transaction_write,
 159        .read           = nfsctl_transaction_read,
 160        .release        = simple_transaction_release,
 161};
 162
 163static int exports_open(struct inode *inode, struct file *file)
 164{
 165        return seq_open(file, &nfs_exports_op);
 166}
 167
 168static const struct file_operations exports_operations = {
 169        .open           = exports_open,
 170        .read           = seq_read,
 171        .llseek         = seq_lseek,
 172        .release        = seq_release,
 173        .owner          = THIS_MODULE,
 174};
 175
 176extern int nfsd_pool_stats_open(struct inode *inode, struct file *file);
 177extern int nfsd_pool_stats_release(struct inode *inode, struct file *file);
 178
 179static const struct file_operations pool_stats_operations = {
 180        .open           = nfsd_pool_stats_open,
 181        .read           = seq_read,
 182        .llseek         = seq_lseek,
 183        .release        = nfsd_pool_stats_release,
 184        .owner          = THIS_MODULE,
 185};
 186
 187/*----------------------------------------------------------------------------*/
 188/*
 189 * payload - write methods
 190 */
 191
 192/**
 193 * write_svc - Start kernel's NFSD server
 194 *
 195 * Deprecated.  /proc/fs/nfsd/threads is preferred.
 196 * Function remains to support old versions of nfs-utils.
 197 *
 198 * Input:
 199 *                      buf:    struct nfsctl_svc
 200 *                              svc_port:       port number of this
 201 *                                              server's listener
 202 *                              svc_nthreads:   number of threads to start
 203 *                      size:   size in bytes of passed in nfsctl_svc
 204 * Output:
 205 *      On success:     returns zero
 206 *      On error:       return code is negative errno value
 207 */
 208static ssize_t write_svc(struct file *file, char *buf, size_t size)
 209{
 210        struct nfsctl_svc *data;
 211        int err;
 212        if (size < sizeof(*data))
 213                return -EINVAL;
 214        data = (struct nfsctl_svc*) buf;
 215        err = nfsd_svc(data->svc_port, data->svc_nthreads);
 216        if (err < 0)
 217                return err;
 218        return 0;
 219}
 220
 221/**
 222 * write_add - Add or modify client entry in auth unix cache
 223 *
 224 * Deprecated.  /proc/net/rpc/auth.unix.ip is preferred.
 225 * Function remains to support old versions of nfs-utils.
 226 *
 227 * Input:
 228 *                      buf:    struct nfsctl_client
 229 *                              cl_ident:       '\0'-terminated C string
 230 *                                              containing domain name
 231 *                                              of client
 232 *                              cl_naddr:       no. of items in cl_addrlist
 233 *                              cl_addrlist:    array of client addresses
 234 *                              cl_fhkeytype:   ignored
 235 *                              cl_fhkeylen:    ignored
 236 *                              cl_fhkey:       ignored
 237 *                      size:   size in bytes of passed in nfsctl_client
 238 * Output:
 239 *      On success:     returns zero
 240 *      On error:       return code is negative errno value
 241 *
 242 * Note: Only AF_INET client addresses are passed in, since
 243 * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
 244 */
 245static ssize_t write_add(struct file *file, char *buf, size_t size)
 246{
 247        struct nfsctl_client *data;
 248        if (size < sizeof(*data))
 249                return -EINVAL;
 250        data = (struct nfsctl_client *)buf;
 251        return exp_addclient(data);
 252}
 253
 254/**
 255 * write_del - Remove client from auth unix cache
 256 *
 257 * Deprecated.  /proc/net/rpc/auth.unix.ip is preferred.
 258 * Function remains to support old versions of nfs-utils.
 259 *
 260 * Input:
 261 *                      buf:    struct nfsctl_client
 262 *                              cl_ident:       '\0'-terminated C string
 263 *                                              containing domain name
 264 *                                              of client
 265 *                              cl_naddr:       ignored
 266 *                              cl_addrlist:    ignored
 267 *                              cl_fhkeytype:   ignored
 268 *                              cl_fhkeylen:    ignored
 269 *                              cl_fhkey:       ignored
 270 *                      size:   size in bytes of passed in nfsctl_client
 271 * Output:
 272 *      On success:     returns zero
 273 *      On error:       return code is negative errno value
 274 *
 275 * Note: Only AF_INET client addresses are passed in, since
 276 * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
 277 */
 278static ssize_t write_del(struct file *file, char *buf, size_t size)
 279{
 280        struct nfsctl_client *data;
 281        if (size < sizeof(*data))
 282                return -EINVAL;
 283        data = (struct nfsctl_client *)buf;
 284        return exp_delclient(data);
 285}
 286
 287/**
 288 * write_export - Export part or all of a local file system
 289 *
 290 * Deprecated.  /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
 291 * Function remains to support old versions of nfs-utils.
 292 *
 293 * Input:
 294 *                      buf:    struct nfsctl_export
 295 *                              ex_client:      '\0'-terminated C string
 296 *                                              containing domain name
 297 *                                              of client allowed to access
 298 *                                              this export
 299 *                              ex_path:        '\0'-terminated C string
 300 *                                              containing pathname of
 301 *                                              directory in local file system
 302 *                              ex_dev:         fsid to use for this export
 303 *                              ex_ino:         ignored
 304 *                              ex_flags:       export flags for this export
 305 *                              ex_anon_uid:    UID to use for anonymous
 306 *                                              requests
 307 *                              ex_anon_gid:    GID to use for anonymous
 308 *                                              requests
 309 *                      size:   size in bytes of passed in nfsctl_export
 310 * Output:
 311 *      On success:     returns zero
 312 *      On error:       return code is negative errno value
 313 */
 314static ssize_t write_export(struct file *file, char *buf, size_t size)
 315{
 316        struct nfsctl_export *data;
 317        if (size < sizeof(*data))
 318                return -EINVAL;
 319        data = (struct nfsctl_export*)buf;
 320        return exp_export(data);
 321}
 322
 323/**
 324 * write_unexport - Unexport a previously exported file system
 325 *
 326 * Deprecated.  /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
 327 * Function remains to support old versions of nfs-utils.
 328 *
 329 * Input:
 330 *                      buf:    struct nfsctl_export
 331 *                              ex_client:      '\0'-terminated C string
 332 *                                              containing domain name
 333 *                                              of client no longer allowed
 334 *                                              to access this export
 335 *                              ex_path:        '\0'-terminated C string
 336 *                                              containing pathname of
 337 *                                              directory in local file system
 338 *                              ex_dev:         ignored
 339 *                              ex_ino:         ignored
 340 *                              ex_flags:       ignored
 341 *                              ex_anon_uid:    ignored
 342 *                              ex_anon_gid:    ignored
 343 *                      size:   size in bytes of passed in nfsctl_export
 344 * Output:
 345 *      On success:     returns zero
 346 *      On error:       return code is negative errno value
 347 */
 348static ssize_t write_unexport(struct file *file, char *buf, size_t size)
 349{
 350        struct nfsctl_export *data;
 351
 352        if (size < sizeof(*data))
 353                return -EINVAL;
 354        data = (struct nfsctl_export*)buf;
 355        return exp_unexport(data);
 356}
 357
 358/**
 359 * write_getfs - Get a variable-length NFS file handle by path
 360 *
 361 * Deprecated.  /proc/fs/nfsd/filehandle is preferred.
 362 * Function remains to support old versions of nfs-utils.
 363 *
 364 * Input:
 365 *                      buf:    struct nfsctl_fsparm
 366 *                              gd_addr:        socket address of client
 367 *                              gd_path:        '\0'-terminated C string
 368 *                                              containing pathname of
 369 *                                              directory in local file system
 370 *                              gd_maxlen:      maximum size of returned file
 371 *                                              handle
 372 *                      size:   size in bytes of passed in nfsctl_fsparm
 373 * Output:
 374 *      On success:     passed-in buffer filled with a knfsd_fh structure
 375 *                      (a variable-length raw NFS file handle);
 376 *                      return code is the size in bytes of the file handle
 377 *      On error:       return code is negative errno value
 378 *
 379 * Note: Only AF_INET client addresses are passed in, since gd_addr
 380 * is the same size as a struct sockaddr_in.
 381 */
 382static ssize_t write_getfs(struct file *file, char *buf, size_t size)
 383{
 384        struct nfsctl_fsparm *data;
 385        struct sockaddr_in *sin;
 386        struct auth_domain *clp;
 387        int err = 0;
 388        struct knfsd_fh *res;
 389        struct in6_addr in6;
 390
 391        if (size < sizeof(*data))
 392                return -EINVAL;
 393        data = (struct nfsctl_fsparm*)buf;
 394        err = -EPROTONOSUPPORT;
 395        if (data->gd_addr.sa_family != AF_INET)
 396                goto out;
 397        sin = (struct sockaddr_in *)&data->gd_addr;
 398        if (data->gd_maxlen > NFS3_FHSIZE)
 399                data->gd_maxlen = NFS3_FHSIZE;
 400
 401        res = (struct knfsd_fh*)buf;
 402
 403        exp_readlock();
 404
 405        ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
 406
 407        clp = auth_unix_lookup(&in6);
 408        if (!clp)
 409                err = -EPERM;
 410        else {
 411                err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
 412                auth_domain_put(clp);
 413        }
 414        exp_readunlock();
 415        if (err == 0)
 416                err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
 417 out:
 418        return err;
 419}
 420
 421/**
 422 * write_getfd - Get a fixed-length NFS file handle by path (used by mountd)
 423 *
 424 * Deprecated.  /proc/fs/nfsd/filehandle is preferred.
 425 * Function remains to support old versions of nfs-utils.
 426 *
 427 * Input:
 428 *                      buf:    struct nfsctl_fdparm
 429 *                              gd_addr:        socket address of client
 430 *                              gd_path:        '\0'-terminated C string
 431 *                                              containing pathname of
 432 *                                              directory in local file system
 433 *                              gd_version:     fdparm structure version
 434 *                      size:   size in bytes of passed in nfsctl_fdparm
 435 * Output:
 436 *      On success:     passed-in buffer filled with nfsctl_res
 437 *                      (a fixed-length raw NFS file handle);
 438 *                      return code is the size in bytes of the file handle
 439 *      On error:       return code is negative errno value
 440 *
 441 * Note: Only AF_INET client addresses are passed in, since gd_addr
 442 * is the same size as a struct sockaddr_in.
 443 */
 444static ssize_t write_getfd(struct file *file, char *buf, size_t size)
 445{
 446        struct nfsctl_fdparm *data;
 447        struct sockaddr_in *sin;
 448        struct auth_domain *clp;
 449        int err = 0;
 450        struct knfsd_fh fh;
 451        char *res;
 452        struct in6_addr in6;
 453
 454        if (size < sizeof(*data))
 455                return -EINVAL;
 456        data = (struct nfsctl_fdparm*)buf;
 457        err = -EPROTONOSUPPORT;
 458        if (data->gd_addr.sa_family != AF_INET)
 459                goto out;
 460        err = -EINVAL;
 461        if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
 462                goto out;
 463
 464        res = buf;
 465        sin = (struct sockaddr_in *)&data->gd_addr;
 466        exp_readlock();
 467
 468        ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
 469
 470        clp = auth_unix_lookup(&in6);
 471        if (!clp)
 472                err = -EPERM;
 473        else {
 474                err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
 475                auth_domain_put(clp);
 476        }
 477        exp_readunlock();
 478
 479        if (err == 0) {
 480                memset(res,0, NFS_FHSIZE);
 481                memcpy(res, &fh.fh_base, fh.fh_size);
 482                err = NFS_FHSIZE;
 483        }
 484 out:
 485        return err;
 486}
 487
 488/**
 489 * write_unlock_ip - Release all locks used by a client
 490 *
 491 * Experimental.
 492 *
 493 * Input:
 494 *                      buf:    '\n'-terminated C string containing a
 495 *                              presentation format IP address
 496 *                      size:   length of C string in @buf
 497 * Output:
 498 *      On success:     returns zero if all specified locks were released;
 499 *                      returns one if one or more locks were not released
 500 *      On error:       return code is negative errno value
 501 */
 502static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
 503{
 504        struct sockaddr_storage address;
 505        struct sockaddr *sap = (struct sockaddr *)&address;
 506        size_t salen = sizeof(address);
 507        char *fo_path;
 508
 509        /* sanity check */
 510        if (size == 0)
 511                return -EINVAL;
 512
 513        if (buf[size-1] != '\n')
 514                return -EINVAL;
 515
 516        fo_path = buf;
 517        if (qword_get(&buf, fo_path, size) < 0)
 518                return -EINVAL;
 519
 520        if (rpc_pton(fo_path, size, sap, salen) == 0)
 521                return -EINVAL;
 522
 523        return nlmsvc_unlock_all_by_ip(sap);
 524}
 525
 526/**
 527 * write_unlock_fs - Release all locks on a local file system
 528 *
 529 * Experimental.
 530 *
 531 * Input:
 532 *                      buf:    '\n'-terminated C string containing the
 533 *                              absolute pathname of a local file system
 534 *                      size:   length of C string in @buf
 535 * Output:
 536 *      On success:     returns zero if all specified locks were released;
 537 *                      returns one if one or more locks were not released
 538 *      On error:       return code is negative errno value
 539 */
 540static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
 541{
 542        struct path path;
 543        char *fo_path;
 544        int error;
 545
 546        /* sanity check */
 547        if (size == 0)
 548                return -EINVAL;
 549
 550        if (buf[size-1] != '\n')
 551                return -EINVAL;
 552
 553        fo_path = buf;
 554        if (qword_get(&buf, fo_path, size) < 0)
 555                return -EINVAL;
 556
 557        error = kern_path(fo_path, 0, &path);
 558        if (error)
 559                return error;
 560
 561        /*
 562         * XXX: Needs better sanity checking.  Otherwise we could end up
 563         * releasing locks on the wrong file system.
 564         *
 565         * For example:
 566         * 1.  Does the path refer to a directory?
 567         * 2.  Is that directory a mount point, or
 568         * 3.  Is that directory the root of an exported file system?
 569         */
 570        error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb);
 571
 572        path_put(&path);
 573        return error;
 574}
 575
 576/**
 577 * write_filehandle - Get a variable-length NFS file handle by path
 578 *
 579 * On input, the buffer contains a '\n'-terminated C string comprised of
 580 * three alphanumeric words separated by whitespace.  The string may
 581 * contain escape sequences.
 582 *
 583 * Input:
 584 *                      buf:
 585 *                              domain:         client domain name
 586 *                              path:           export pathname
 587 *                              maxsize:        numeric maximum size of
 588 *                                              @buf
 589 *                      size:   length of C string in @buf
 590 * Output:
 591 *      On success:     passed-in buffer filled with '\n'-terminated C
 592 *                      string containing a ASCII hex text version
 593 *                      of the NFS file handle;
 594 *                      return code is the size in bytes of the string
 595 *      On error:       return code is negative errno value
 596 */
 597static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
 598{
 599        char *dname, *path;
 600        int uninitialized_var(maxsize);
 601        char *mesg = buf;
 602        int len;
 603        struct auth_domain *dom;
 604        struct knfsd_fh fh;
 605
 606        if (size == 0)
 607                return -EINVAL;
 608
 609        if (buf[size-1] != '\n')
 610                return -EINVAL;
 611        buf[size-1] = 0;
 612
 613        dname = mesg;
 614        len = qword_get(&mesg, dname, size);
 615        if (len <= 0)
 616                return -EINVAL;
 617        
 618        path = dname+len+1;
 619        len = qword_get(&mesg, path, size);
 620        if (len <= 0)
 621                return -EINVAL;
 622
 623        len = get_int(&mesg, &maxsize);
 624        if (len)
 625                return len;
 626
 627        if (maxsize < NFS_FHSIZE)
 628                return -EINVAL;
 629        if (maxsize > NFS3_FHSIZE)
 630                maxsize = NFS3_FHSIZE;
 631
 632        if (qword_get(&mesg, mesg, size)>0)
 633                return -EINVAL;
 634
 635        /* we have all the words, they are in buf.. */
 636        dom = unix_domain_find(dname);
 637        if (!dom)
 638                return -ENOMEM;
 639
 640        len = exp_rootfh(dom, path, &fh,  maxsize);
 641        auth_domain_put(dom);
 642        if (len)
 643                return len;
 644        
 645        mesg = buf;
 646        len = SIMPLE_TRANSACTION_LIMIT;
 647        qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
 648        mesg[-1] = '\n';
 649        return mesg - buf;      
 650}
 651
 652/**
 653 * write_threads - Start NFSD, or report the current number of running threads
 654 *
 655 * Input:
 656 *                      buf:            ignored
 657 *                      size:           zero
 658 * Output:
 659 *      On success:     passed-in buffer filled with '\n'-terminated C
 660 *                      string numeric value representing the number of
 661 *                      running NFSD threads;
 662 *                      return code is the size in bytes of the string
 663 *      On error:       return code is zero
 664 *
 665 * OR
 666 *
 667 * Input:
 668 *                      buf:            C string containing an unsigned
 669 *                                      integer value representing the
 670 *                                      number of NFSD threads to start
 671 *                      size:           non-zero length of C string in @buf
 672 * Output:
 673 *      On success:     NFS service is started;
 674 *                      passed-in buffer filled with '\n'-terminated C
 675 *                      string numeric value representing the number of
 676 *                      running NFSD threads;
 677 *                      return code is the size in bytes of the string
 678 *      On error:       return code is zero or a negative errno value
 679 */
 680static ssize_t write_threads(struct file *file, char *buf, size_t size)
 681{
 682        char *mesg = buf;
 683        int rv;
 684        if (size > 0) {
 685                int newthreads;
 686                rv = get_int(&mesg, &newthreads);
 687                if (rv)
 688                        return rv;
 689                if (newthreads < 0)
 690                        return -EINVAL;
 691                rv = nfsd_svc(NFS_PORT, newthreads);
 692                if (rv < 0)
 693                        return rv;
 694        } else
 695                rv = nfsd_nrthreads();
 696
 697        return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv);
 698}
 699
 700/**
 701 * write_pool_threads - Set or report the current number of threads per pool
 702 *
 703 * Input:
 704 *                      buf:            ignored
 705 *                      size:           zero
 706 *
 707 * OR
 708 *
 709 * Input:
 710 *                      buf:            C string containing whitespace-
 711 *                                      separated unsigned integer values
 712 *                                      representing the number of NFSD
 713 *                                      threads to start in each pool
 714 *                      size:           non-zero length of C string in @buf
 715 * Output:
 716 *      On success:     passed-in buffer filled with '\n'-terminated C
 717 *                      string containing integer values representing the
 718 *                      number of NFSD threads in each pool;
 719 *                      return code is the size in bytes of the string
 720 *      On error:       return code is zero or a negative errno value
 721 */
 722static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
 723{
 724        /* if size > 0, look for an array of number of threads per node
 725         * and apply them  then write out number of threads per node as reply
 726         */
 727        char *mesg = buf;
 728        int i;
 729        int rv;
 730        int len;
 731        int npools;
 732        int *nthreads;
 733
 734        mutex_lock(&nfsd_mutex);
 735        npools = nfsd_nrpools();
 736        if (npools == 0) {
 737                /*
 738                 * NFS is shut down.  The admin can start it by
 739                 * writing to the threads file but NOT the pool_threads
 740                 * file, sorry.  Report zero threads.
 741                 */
 742                mutex_unlock(&nfsd_mutex);
 743                strcpy(buf, "0\n");
 744                return strlen(buf);
 745        }
 746
 747        nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
 748        rv = -ENOMEM;
 749        if (nthreads == NULL)
 750                goto out_free;
 751
 752        if (size > 0) {
 753                for (i = 0; i < npools; i++) {
 754                        rv = get_int(&mesg, &nthreads[i]);
 755                        if (rv == -ENOENT)
 756                                break;          /* fewer numbers than pools */
 757                        if (rv)
 758                                goto out_free;  /* syntax error */
 759                        rv = -EINVAL;
 760                        if (nthreads[i] < 0)
 761                                goto out_free;
 762                }
 763                rv = nfsd_set_nrthreads(i, nthreads);
 764                if (rv)
 765                        goto out_free;
 766        }
 767
 768        rv = nfsd_get_nrthreads(npools, nthreads);
 769        if (rv)
 770                goto out_free;
 771
 772        mesg = buf;
 773        size = SIMPLE_TRANSACTION_LIMIT;
 774        for (i = 0; i < npools && size > 0; i++) {
 775                snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
 776                len = strlen(mesg);
 777                size -= len;
 778                mesg += len;
 779        }
 780        rv = mesg - buf;
 781out_free:
 782        kfree(nthreads);
 783        mutex_unlock(&nfsd_mutex);
 784        return rv;
 785}
 786
 787static ssize_t __write_versions(struct file *file, char *buf, size_t size)
 788{
 789        char *mesg = buf;
 790        char *vers, *minorp, sign;
 791        int len, num, remaining;
 792        unsigned minor;
 793        ssize_t tlen = 0;
 794        char *sep;
 795
 796        if (size>0) {
 797                if (nfsd_serv)
 798                        /* Cannot change versions without updating
 799                         * nfsd_serv->sv_xdrsize, and reallocing
 800                         * rq_argp and rq_resp
 801                         */
 802                        return -EBUSY;
 803                if (buf[size-1] != '\n')
 804                        return -EINVAL;
 805                buf[size-1] = 0;
 806
 807                vers = mesg;
 808                len = qword_get(&mesg, vers, size);
 809                if (len <= 0) return -EINVAL;
 810                do {
 811                        sign = *vers;
 812                        if (sign == '+' || sign == '-')
 813                                num = simple_strtol((vers+1), &minorp, 0);
 814                        else
 815                                num = simple_strtol(vers, &minorp, 0);
 816                        if (*minorp == '.') {
 817                                if (num < 4)
 818                                        return -EINVAL;
 819                                minor = simple_strtoul(minorp+1, NULL, 0);
 820                                if (minor == 0)
 821                                        return -EINVAL;
 822                                if (nfsd_minorversion(minor, sign == '-' ?
 823                                                     NFSD_CLEAR : NFSD_SET) < 0)
 824                                        return -EINVAL;
 825                                goto next;
 826                        }
 827                        switch(num) {
 828                        case 2:
 829                        case 3:
 830                        case 4:
 831                                nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
 832                                break;
 833                        default:
 834                                return -EINVAL;
 835                        }
 836                next:
 837                        vers += len + 1;
 838                } while ((len = qword_get(&mesg, vers, size)) > 0);
 839                /* If all get turned off, turn them back on, as
 840                 * having no versions is BAD
 841                 */
 842                nfsd_reset_versions();
 843        }
 844
 845        /* Now write current state into reply buffer */
 846        len = 0;
 847        sep = "";
 848        remaining = SIMPLE_TRANSACTION_LIMIT;
 849        for (num=2 ; num <= 4 ; num++)
 850                if (nfsd_vers(num, NFSD_AVAIL)) {
 851                        len = snprintf(buf, remaining, "%s%c%d", sep,
 852                                       nfsd_vers(num, NFSD_TEST)?'+':'-',
 853                                       num);
 854                        sep = " ";
 855
 856                        if (len > remaining)
 857                                break;
 858                        remaining -= len;
 859                        buf += len;
 860                        tlen += len;
 861                }
 862        if (nfsd_vers(4, NFSD_AVAIL))
 863                for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
 864                     minor++) {
 865                        len = snprintf(buf, remaining, " %c4.%u",
 866                                        (nfsd_vers(4, NFSD_TEST) &&
 867                                         nfsd_minorversion(minor, NFSD_TEST)) ?
 868                                                '+' : '-',
 869                                        minor);
 870
 871                        if (len > remaining)
 872                                break;
 873                        remaining -= len;
 874                        buf += len;
 875                        tlen += len;
 876                }
 877
 878        len = snprintf(buf, remaining, "\n");
 879        if (len > remaining)
 880                return -EINVAL;
 881        return tlen + len;
 882}
 883
 884/**
 885 * write_versions - Set or report the available NFS protocol versions
 886 *
 887 * Input:
 888 *                      buf:            ignored
 889 *                      size:           zero
 890 * Output:
 891 *      On success:     passed-in buffer filled with '\n'-terminated C
 892 *                      string containing positive or negative integer
 893 *                      values representing the current status of each
 894 *                      protocol version;
 895 *                      return code is the size in bytes of the string
 896 *      On error:       return code is zero or a negative errno value
 897 *
 898 * OR
 899 *
 900 * Input:
 901 *                      buf:            C string containing whitespace-
 902 *                                      separated positive or negative
 903 *                                      integer values representing NFS
 904 *                                      protocol versions to enable ("+n")
 905 *                                      or disable ("-n")
 906 *                      size:           non-zero length of C string in @buf
 907 * Output:
 908 *      On success:     status of zero or more protocol versions has
 909 *                      been updated; passed-in buffer filled with
 910 *                      '\n'-terminated C string containing positive
 911 *                      or negative integer values representing the
 912 *                      current status of each protocol version;
 913 *                      return code is the size in bytes of the string
 914 *      On error:       return code is zero or a negative errno value
 915 */
 916static ssize_t write_versions(struct file *file, char *buf, size_t size)
 917{
 918        ssize_t rv;
 919
 920        mutex_lock(&nfsd_mutex);
 921        rv = __write_versions(file, buf, size);
 922        mutex_unlock(&nfsd_mutex);
 923        return rv;
 924}
 925
 926/*
 927 * Zero-length write.  Return a list of NFSD's current listener
 928 * transports.
 929 */
 930static ssize_t __write_ports_names(char *buf)
 931{
 932        if (nfsd_serv == NULL)
 933                return 0;
 934        return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
 935}
 936
 937/*
 938 * A single 'fd' number was written, in which case it must be for
 939 * a socket of a supported family/protocol, and we use it as an
 940 * nfsd listener.
 941 */
 942static ssize_t __write_ports_addfd(char *buf)
 943{
 944        char *mesg = buf;
 945        int fd, err;
 946
 947        err = get_int(&mesg, &fd);
 948        if (err != 0 || fd < 0)
 949                return -EINVAL;
 950
 951        err = nfsd_create_serv();
 952        if (err != 0)
 953                return err;
 954
 955        err = lockd_up();
 956        if (err != 0)
 957                goto out;
 958
 959        err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
 960        if (err < 0)
 961                lockd_down();
 962
 963out:
 964        /* Decrease the count, but don't shut down the service */
 965        nfsd_serv->sv_nrthreads--;
 966        return err;
 967}
 968
 969/*
 970 * A '-' followed by the 'name' of a socket means we close the socket.
 971 */
 972static ssize_t __write_ports_delfd(char *buf)
 973{
 974        char *toclose;
 975        int len = 0;
 976
 977        toclose = kstrdup(buf + 1, GFP_KERNEL);
 978        if (toclose == NULL)
 979                return -ENOMEM;
 980
 981        if (nfsd_serv != NULL)
 982                len = svc_sock_names(nfsd_serv, buf,
 983                                        SIMPLE_TRANSACTION_LIMIT, toclose);
 984        if (len >= 0)
 985                lockd_down();
 986
 987        kfree(toclose);
 988        return len;
 989}
 990
 991/*
 992 * A transport listener is added by writing it's transport name and
 993 * a port number.
 994 */
 995static ssize_t __write_ports_addxprt(char *buf)
 996{
 997        char transport[16];
 998        int port, err;
 999
1000        if (sscanf(buf, "%15s %4u", transport, &port) != 2)
1001                return -EINVAL;
1002
1003        if (port < 1 || port > USHORT_MAX)
1004                return -EINVAL;
1005
1006        err = nfsd_create_serv();
1007        if (err != 0)
1008                return err;
1009
1010        err = svc_create_xprt(nfsd_serv, transport,
1011                                PF_INET, port, SVC_SOCK_ANONYMOUS);
1012        if (err < 0) {
1013                /* Give a reasonable perror msg for bad transport string */
1014                if (err == -ENOENT)
1015                        err = -EPROTONOSUPPORT;
1016                return err;
1017        }
1018        return 0;
1019}
1020
1021/*
1022 * A transport listener is removed by writing a "-", it's transport
1023 * name, and it's port number.
1024 */
1025static ssize_t __write_ports_delxprt(char *buf)
1026{
1027        struct svc_xprt *xprt;
1028        char transport[16];
1029        int port;
1030
1031        if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
1032                return -EINVAL;
1033
1034        if (port < 1 || port > USHORT_MAX || nfsd_serv == NULL)
1035                return -EINVAL;
1036
1037        xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port);
1038        if (xprt == NULL)
1039                return -ENOTCONN;
1040
1041        svc_close_xprt(xprt);
1042        svc_xprt_put(xprt);
1043        return 0;
1044}
1045
1046static ssize_t __write_ports(struct file *file, char *buf, size_t size)
1047{
1048        if (size == 0)
1049                return __write_ports_names(buf);
1050
1051        if (isdigit(buf[0]))
1052                return __write_ports_addfd(buf);
1053
1054        if (buf[0] == '-' && isdigit(buf[1]))
1055                return __write_ports_delfd(buf);
1056
1057        if (isalpha(buf[0]))
1058                return __write_ports_addxprt(buf);
1059
1060        if (buf[0] == '-' && isalpha(buf[1]))
1061                return __write_ports_delxprt(buf);
1062
1063        return -EINVAL;
1064}
1065
1066/**
1067 * write_ports - Pass a socket file descriptor or transport name to listen on
1068 *
1069 * Input:
1070 *                      buf:            ignored
1071 *                      size:           zero
1072 * Output:
1073 *      On success:     passed-in buffer filled with a '\n'-terminated C
1074 *                      string containing a whitespace-separated list of
1075 *                      named NFSD listeners;
1076 *                      return code is the size in bytes of the string
1077 *      On error:       return code is zero or a negative errno value
1078 *
1079 * OR
1080 *
1081 * Input:
1082 *                      buf:            C string containing an unsigned
1083 *                                      integer value representing a bound
1084 *                                      but unconnected socket that is to be
1085 *                                      used as an NFSD listener; listen(3)
1086 *                                      must be called for a SOCK_STREAM
1087 *                                      socket, otherwise it is ignored
1088 *                      size:           non-zero length of C string in @buf
1089 * Output:
1090 *      On success:     NFS service is started;
1091 *                      passed-in buffer filled with a '\n'-terminated C
1092 *                      string containing a unique alphanumeric name of
1093 *                      the listener;
1094 *                      return code is the size in bytes of the string
1095 *      On error:       return code is a negative errno value
1096 *
1097 * OR
1098 *
1099 * Input:
1100 *                      buf:            C string containing a "-" followed
1101 *                                      by an integer value representing a
1102 *                                      previously passed in socket file
1103 *                                      descriptor
1104 *                      size:           non-zero length of C string in @buf
1105 * Output:
1106 *      On success:     NFS service no longer listens on that socket;
1107 *                      passed-in buffer filled with a '\n'-terminated C
1108 *                      string containing a unique name of the listener;
1109 *                      return code is the size in bytes of the string
1110 *      On error:       return code is a negative errno value
1111 *
1112 * OR
1113 *
1114 * Input:
1115 *                      buf:            C string containing a transport
1116 *                                      name and an unsigned integer value
1117 *                                      representing the port to listen on,
1118 *                                      separated by whitespace
1119 *                      size:           non-zero length of C string in @buf
1120 * Output:
1121 *      On success:     returns zero; NFS service is started
1122 *      On error:       return code is a negative errno value
1123 *
1124 * OR
1125 *
1126 * Input:
1127 *                      buf:            C string containing a "-" followed
1128 *                                      by a transport name and an unsigned
1129 *                                      integer value representing the port
1130 *                                      to listen on, separated by whitespace
1131 *                      size:           non-zero length of C string in @buf
1132 * Output:
1133 *      On success:     returns zero; NFS service no longer listens
1134 *                      on that transport
1135 *      On error:       return code is a negative errno value
1136 */
1137static ssize_t write_ports(struct file *file, char *buf, size_t size)
1138{
1139        ssize_t rv;
1140
1141        mutex_lock(&nfsd_mutex);
1142        rv = __write_ports(file, buf, size);
1143        mutex_unlock(&nfsd_mutex);
1144        return rv;
1145}
1146
1147
1148int nfsd_max_blksize;
1149
1150/**
1151 * write_maxblksize - Set or report the current NFS blksize
1152 *
1153 * Input:
1154 *                      buf:            ignored
1155 *                      size:           zero
1156 *
1157 * OR
1158 *
1159 * Input:
1160 *                      buf:            C string containing an unsigned
1161 *                                      integer value representing the new
1162 *                                      NFS blksize
1163 *                      size:           non-zero length of C string in @buf
1164 * Output:
1165 *      On success:     passed-in buffer filled with '\n'-terminated C string
1166 *                      containing numeric value of the current NFS blksize
1167 *                      setting;
1168 *                      return code is the size in bytes of the string
1169 *      On error:       return code is zero or a negative errno value
1170 */
1171static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
1172{
1173        char *mesg = buf;
1174        if (size > 0) {
1175                int bsize;
1176                int rv = get_int(&mesg, &bsize);
1177                if (rv)
1178                        return rv;
1179                /* force bsize into allowed range and
1180                 * required alignment.
1181                 */
1182                if (bsize < 1024)
1183                        bsize = 1024;
1184                if (bsize > NFSSVC_MAXBLKSIZE)
1185                        bsize = NFSSVC_MAXBLKSIZE;
1186                bsize &= ~(1024-1);
1187                mutex_lock(&nfsd_mutex);
1188                if (nfsd_serv && nfsd_serv->sv_nrthreads) {
1189                        mutex_unlock(&nfsd_mutex);
1190                        return -EBUSY;
1191                }
1192                nfsd_max_blksize = bsize;
1193                mutex_unlock(&nfsd_mutex);
1194        }
1195
1196        return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
1197                                                        nfsd_max_blksize);
1198}
1199
1200#ifdef CONFIG_NFSD_V4
1201extern time_t nfs4_leasetime(void);
1202
1203static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
1204{
1205        /* if size > 10 seconds, call
1206         * nfs4_reset_lease() then write out the new lease (seconds) as reply
1207         */
1208        char *mesg = buf;
1209        int rv, lease;
1210
1211        if (size > 0) {
1212                if (nfsd_serv)
1213                        return -EBUSY;
1214                rv = get_int(&mesg, &lease);
1215                if (rv)
1216                        return rv;
1217                if (lease < 10 || lease > 3600)
1218                        return -EINVAL;
1219                nfs4_reset_lease(lease);
1220        }
1221
1222        return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n",
1223                                                        nfs4_lease_time());
1224}
1225
1226/**
1227 * write_leasetime - Set or report the current NFSv4 lease time
1228 *
1229 * Input:
1230 *                      buf:            ignored
1231 *                      size:           zero
1232 *
1233 * OR
1234 *
1235 * Input:
1236 *                      buf:            C string containing an unsigned
1237 *                                      integer value representing the new
1238 *                                      NFSv4 lease expiry time
1239 *                      size:           non-zero length of C string in @buf
1240 * Output:
1241 *      On success:     passed-in buffer filled with '\n'-terminated C
1242 *                      string containing unsigned integer value of the
1243 *                      current lease expiry time;
1244 *                      return code is the size in bytes of the string
1245 *      On error:       return code is zero or a negative errno value
1246 */
1247static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
1248{
1249        ssize_t rv;
1250
1251        mutex_lock(&nfsd_mutex);
1252        rv = __write_leasetime(file, buf, size);
1253        mutex_unlock(&nfsd_mutex);
1254        return rv;
1255}
1256
1257extern char *nfs4_recoverydir(void);
1258
1259static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
1260{
1261        char *mesg = buf;
1262        char *recdir;
1263        int len, status;
1264
1265        if (size > 0) {
1266                if (nfsd_serv)
1267                        return -EBUSY;
1268                if (size > PATH_MAX || buf[size-1] != '\n')
1269                        return -EINVAL;
1270                buf[size-1] = 0;
1271
1272                recdir = mesg;
1273                len = qword_get(&mesg, recdir, size);
1274                if (len <= 0)
1275                        return -EINVAL;
1276
1277                status = nfs4_reset_recoverydir(recdir);
1278        }
1279
1280        return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n",
1281                                                        nfs4_recoverydir());
1282}
1283
1284/**
1285 * write_recoverydir - Set or report the pathname of the recovery directory
1286 *
1287 * Input:
1288 *                      buf:            ignored
1289 *                      size:           zero
1290 *
1291 * OR
1292 *
1293 * Input:
1294 *                      buf:            C string containing the pathname
1295 *                                      of the directory on a local file
1296 *                                      system containing permanent NFSv4
1297 *                                      recovery data
1298 *                      size:           non-zero length of C string in @buf
1299 * Output:
1300 *      On success:     passed-in buffer filled with '\n'-terminated C string
1301 *                      containing the current recovery pathname setting;
1302 *                      return code is the size in bytes of the string
1303 *      On error:       return code is zero or a negative errno value
1304 */
1305static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
1306{
1307        ssize_t rv;
1308
1309        mutex_lock(&nfsd_mutex);
1310        rv = __write_recoverydir(file, buf, size);
1311        mutex_unlock(&nfsd_mutex);
1312        return rv;
1313}
1314
1315#endif
1316
1317/*----------------------------------------------------------------------------*/
1318/*
1319 *      populating the filesystem.
1320 */
1321
1322static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
1323{
1324        static struct tree_descr nfsd_files[] = {
1325                [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
1326                [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
1327                [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
1328                [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
1329                [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
1330                [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
1331                [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
1332                [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
1333                [NFSD_FO_UnlockIP] = {"unlock_ip",
1334                                        &transaction_ops, S_IWUSR|S_IRUSR},
1335                [NFSD_FO_UnlockFS] = {"unlock_filesystem",
1336                                        &transaction_ops, S_IWUSR|S_IRUSR},
1337                [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
1338                [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
1339                [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
1340                [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
1341                [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
1342                [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
1343                [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
1344#ifdef CONFIG_NFSD_V4
1345                [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
1346                [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
1347#endif
1348                /* last one */ {""}
1349        };
1350        return simple_fill_super(sb, 0x6e667364, nfsd_files);
1351}
1352
1353static int nfsd_get_sb(struct file_system_type *fs_type,
1354        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
1355{
1356        return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
1357}
1358
1359static struct file_system_type nfsd_fs_type = {
1360        .owner          = THIS_MODULE,
1361        .name           = "nfsd",
1362        .get_sb         = nfsd_get_sb,
1363        .kill_sb        = kill_litter_super,
1364};
1365
1366#ifdef CONFIG_PROC_FS
1367static int create_proc_exports_entry(void)
1368{
1369        struct proc_dir_entry *entry;
1370
1371        entry = proc_mkdir("fs/nfs", NULL);
1372        if (!entry)
1373                return -ENOMEM;
1374        entry = proc_create("exports", 0, entry, &exports_operations);
1375        if (!entry)
1376                return -ENOMEM;
1377        return 0;
1378}
1379#else /* CONFIG_PROC_FS */
1380static int create_proc_exports_entry(void)
1381{
1382        return 0;
1383}
1384#endif
1385
1386static int __init init_nfsd(void)
1387{
1388        int retval;
1389        printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
1390
1391        retval = nfs4_state_init(); /* nfs4 locking state */
1392        if (retval)
1393                return retval;
1394        nfsd_stat_init();       /* Statistics */
1395        retval = nfsd_reply_cache_init();
1396        if (retval)
1397                goto out_free_stat;
1398        retval = nfsd_export_init();
1399        if (retval)
1400                goto out_free_cache;
1401        nfsd_lockd_init();      /* lockd->nfsd callbacks */
1402        retval = nfsd_idmap_init();
1403        if (retval)
1404                goto out_free_lockd;
1405        retval = create_proc_exports_entry();
1406        if (retval)
1407                goto out_free_idmap;
1408        retval = register_filesystem(&nfsd_fs_type);
1409        if (retval)
1410                goto out_free_all;
1411        return 0;
1412out_free_all:
1413        remove_proc_entry("fs/nfs/exports", NULL);
1414        remove_proc_entry("fs/nfs", NULL);
1415out_free_idmap:
1416        nfsd_idmap_shutdown();
1417out_free_lockd:
1418        nfsd_lockd_shutdown();
1419        nfsd_export_shutdown();
1420out_free_cache:
1421        nfsd_reply_cache_shutdown();
1422out_free_stat:
1423        nfsd_stat_shutdown();
1424        nfsd4_free_slabs();
1425        return retval;
1426}
1427
1428static void __exit exit_nfsd(void)
1429{
1430        nfsd_export_shutdown();
1431        nfsd_reply_cache_shutdown();
1432        remove_proc_entry("fs/nfs/exports", NULL);
1433        remove_proc_entry("fs/nfs", NULL);
1434        nfsd_stat_shutdown();
1435        nfsd_lockd_shutdown();
1436        nfsd_idmap_shutdown();
1437        nfsd4_free_slabs();
1438        unregister_filesystem(&nfsd_fs_type);
1439}
1440
1441MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
1442MODULE_LICENSE("GPL");
1443module_init(init_nfsd)
1444module_exit(exit_nfsd)
1445