Merge commit 'v2.6.34-rc6'
[safe/jmp/linux-2.6] / fs / nfsd / nfsctl.c
index b64a7fb..bc3194e 100644 (file)
@@ -1,46 +1,21 @@
 /*
- * linux/fs/nfsd/nfsctl.c
- *
  * Syscall interface to knfsd.
  *
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  */
 
-#include <linux/module.h>
-
-#include <linux/linkage.h>
-#include <linux/time.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/namei.h>
-#include <linux/fcntl.h>
-#include <linux/net.h>
-#include <linux/in.h>
-#include <linux/syscalls.h>
-#include <linux/unistd.h>
 #include <linux/slab.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/pagemap.h>
-#include <linux/init.h>
-#include <linux/inet.h>
-#include <linux/string.h>
-#include <linux/smp_lock.h>
+#include <linux/namei.h>
 #include <linux/ctype.h>
 
-#include <linux/nfs.h>
 #include <linux/nfsd_idmap.h>
-#include <linux/lockd/bind.h>
-#include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/svcsock.h>
-#include <linux/nfsd/nfsd.h>
-#include <linux/nfsd/cache.h>
-#include <linux/nfsd/xdr.h>
 #include <linux/nfsd/syscall.h>
 #include <linux/lockd/lockd.h>
+#include <linux/sunrpc/clnt.h>
 
-#include <asm/uaccess.h>
-#include <net/ipv6.h>
+#include "nfsd.h"
+#include "cache.h"
 
 /*
  *     We have a single directory with 9 nodes in it.
@@ -55,6 +30,7 @@ enum {
        NFSD_Getfd,
        NFSD_Getfs,
        NFSD_List,
+       NFSD_Export_features,
        NFSD_Fh,
        NFSD_FO_UnlockIP,
        NFSD_FO_UnlockFS,
@@ -70,6 +46,7 @@ enum {
         */
 #ifdef CONFIG_NFSD_V4
        NFSD_Leasetime,
+       NFSD_Gracetime,
        NFSD_RecoveryDir,
 #endif
 };
@@ -94,6 +71,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size);
 static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
 #ifdef CONFIG_NFSD_V4
 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
+static ssize_t write_gracetime(struct file *file, char *buf, size_t size);
 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
 #endif
 
@@ -115,6 +93,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
        [NFSD_MaxBlkSize] = write_maxblksize,
 #ifdef CONFIG_NFSD_V4
        [NFSD_Leasetime] = write_leasetime,
+       [NFSD_Gracetime] = write_gracetime,
        [NFSD_RecoveryDir] = write_recoverydir,
 #endif
 };
@@ -173,13 +152,32 @@ static const struct file_operations exports_operations = {
        .owner          = THIS_MODULE,
 };
 
+static int export_features_show(struct seq_file *m, void *v)
+{
+       seq_printf(m, "0x%x 0x%x\n", NFSEXP_ALLFLAGS, NFSEXP_SECINFO_FLAGS);
+       return 0;
+}
+
+static int export_features_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, export_features_show, NULL);
+}
+
+static struct file_operations export_features_operations = {
+       .open           = export_features_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 extern int nfsd_pool_stats_open(struct inode *inode, struct file *file);
+extern int nfsd_pool_stats_release(struct inode *inode, struct file *file);
 
-static struct file_operations pool_stats_operations = {
+static const struct file_operations pool_stats_operations = {
        .open           = nfsd_pool_stats_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release,
+       .release        = nfsd_pool_stats_release,
        .owner          = THIS_MODULE,
 };
 
@@ -207,10 +205,14 @@ static struct file_operations pool_stats_operations = {
 static ssize_t write_svc(struct file *file, char *buf, size_t size)
 {
        struct nfsctl_svc *data;
+       int err;
        if (size < sizeof(*data))
                return -EINVAL;
        data = (struct nfsctl_svc*) buf;
-       return nfsd_svc(data->svc_port, data->svc_nthreads);
+       err = nfsd_svc(data->svc_port, data->svc_nthreads);
+       if (err < 0)
+               return err;
+       return 0;
 }
 
 /**
@@ -487,22 +489,18 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
  *
  * Input:
  *                     buf:    '\n'-terminated C string containing a
- *                             presentation format IPv4 address
+ *                             presentation format IP address
  *                     size:   length of C string in @buf
  * Output:
  *     On success:     returns zero if all specified locks were released;
  *                     returns one if one or more locks were not released
  *     On error:       return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in
  */
 static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
 {
-       struct sockaddr_in sin = {
-               .sin_family     = AF_INET,
-       };
-       int b1, b2, b3, b4;
-       char c;
+       struct sockaddr_storage address;
+       struct sockaddr *sap = (struct sockaddr *)&address;
+       size_t salen = sizeof(address);
        char *fo_path;
 
        /* sanity check */
@@ -516,14 +514,10 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
        if (qword_get(&buf, fo_path, size) < 0)
                return -EINVAL;
 
-       /* get ipv4 address */
-       if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
-               return -EINVAL;
-       if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
+       if (rpc_pton(fo_path, size, sap, salen) == 0)
                return -EINVAL;
-       sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
 
-       return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
+       return nlmsvc_unlock_all_by_ip(sap);
 }
 
 /**
@@ -692,11 +686,12 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
                if (newthreads < 0)
                        return -EINVAL;
                rv = nfsd_svc(NFS_PORT, newthreads);
-               if (rv)
+               if (rv < 0)
                        return rv;
-       }
-       sprintf(buf, "%d\n", nfsd_nrthreads());
-       return strlen(buf);
+       } else
+               rv = nfsd_nrthreads();
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv);
 }
 
 /**
@@ -779,10 +774,7 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
                size -= len;
                mesg += len;
        }
-
-       mutex_unlock(&nfsd_mutex);
-       return (mesg-buf);
-
+       rv = mesg - buf;
 out_free:
        kfree(nthreads);
        mutex_unlock(&nfsd_mutex);
@@ -793,7 +785,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
 {
        char *mesg = buf;
        char *vers, *minorp, sign;
-       int len, num;
+       int len, num, remaining;
        unsigned minor;
        ssize_t tlen = 0;
        char *sep;
@@ -840,32 +832,50 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
                        }
                next:
                        vers += len + 1;
-                       tlen += len;
                } while ((len = qword_get(&mesg, vers, size)) > 0);
                /* If all get turned off, turn them back on, as
                 * having no versions is BAD
                 */
                nfsd_reset_versions();
        }
+
        /* Now write current state into reply buffer */
        len = 0;
        sep = "";
+       remaining = SIMPLE_TRANSACTION_LIMIT;
        for (num=2 ; num <= 4 ; num++)
                if (nfsd_vers(num, NFSD_AVAIL)) {
-                       len += sprintf(buf+len, "%s%c%d", sep,
+                       len = snprintf(buf, remaining, "%s%c%d", sep,
                                       nfsd_vers(num, NFSD_TEST)?'+':'-',
                                       num);
                        sep = " ";
+
+                       if (len > remaining)
+                               break;
+                       remaining -= len;
+                       buf += len;
+                       tlen += len;
                }
        if (nfsd_vers(4, NFSD_AVAIL))
-               for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION; minor++)
-                       len += sprintf(buf+len, " %c4.%u",
+               for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
+                    minor++) {
+                       len = snprintf(buf, remaining, " %c4.%u",
                                        (nfsd_vers(4, NFSD_TEST) &&
                                         nfsd_minorversion(minor, NFSD_TEST)) ?
                                                '+' : '-',
                                        minor);
-       len += sprintf(buf+len, "\n");
-       return len;
+
+                       if (len > remaining)
+                               break;
+                       remaining -= len;
+                       buf += len;
+                       tlen += len;
+               }
+
+       len = snprintf(buf, remaining, "\n");
+       if (len > remaining)
+               return -EINVAL;
+       return tlen + len;
 }
 
 /**
@@ -982,6 +992,7 @@ static ssize_t __write_ports_delfd(char *buf)
 static ssize_t __write_ports_addxprt(char *buf)
 {
        char transport[16];
+       struct svc_xprt *xprt;
        int port, err;
 
        if (sscanf(buf, "%15s %4u", transport, &port) != 2)
@@ -996,13 +1007,24 @@ static ssize_t __write_ports_addxprt(char *buf)
 
        err = svc_create_xprt(nfsd_serv, transport,
                                PF_INET, port, SVC_SOCK_ANONYMOUS);
-       if (err < 0) {
-               /* Give a reasonable perror msg for bad transport string */
-               if (err == -ENOENT)
-                       err = -EPROTONOSUPPORT;
-               return err;
-       }
+       if (err < 0)
+               goto out_err;
+
+       err = svc_create_xprt(nfsd_serv, transport,
+                               PF_INET6, port, SVC_SOCK_ANONYMOUS);
+       if (err < 0 && err != -EAFNOSUPPORT)
+               goto out_close;
        return 0;
+out_close:
+       xprt = svc_find_xprt(nfsd_serv, transport, PF_INET, port);
+       if (xprt != NULL) {
+               svc_close_xprt(xprt);
+               svc_xprt_put(xprt);
+       }
+out_err:
+       /* Decrease the count, but don't shut down the service */
+       nfsd_serv->sv_nrthreads--;
+       return err;
 }
 
 /*
@@ -1179,32 +1201,51 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
                nfsd_max_blksize = bsize;
                mutex_unlock(&nfsd_mutex);
        }
-       return sprintf(buf, "%d\n", nfsd_max_blksize);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
+                                                       nfsd_max_blksize);
 }
 
 #ifdef CONFIG_NFSD_V4
-extern time_t nfs4_leasetime(void);
-
-static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
+static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time)
 {
-       /* if size > 10 seconds, call
-        * nfs4_reset_lease() then write out the new lease (seconds) as reply
-        */
        char *mesg = buf;
-       int rv, lease;
+       int rv, i;
 
        if (size > 0) {
                if (nfsd_serv)
                        return -EBUSY;
-               rv = get_int(&mesg, &lease);
+               rv = get_int(&mesg, &i);
                if (rv)
                        return rv;
-               if (lease < 10 || lease > 3600)
+               /*
+                * Some sanity checking.  We don't have a reason for
+                * these particular numbers, but problems with the
+                * extremes are:
+                *      - Too short: the briefest network outage may
+                *        cause clients to lose all their locks.  Also,
+                *        the frequent polling may be wasteful.
+                *      - Too long: do you really want reboot recovery
+                *        to take more than an hour?  Or to make other
+                *        clients wait an hour before being able to
+                *        revoke a dead client's locks?
+                */
+               if (i < 10 || i > 3600)
                        return -EINVAL;
-               nfs4_reset_lease(lease);
+               *time = i;
        }
-       sprintf(buf, "%ld\n", nfs4_lease_time());
-       return strlen(buf);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time);
+}
+
+static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time)
+{
+       ssize_t rv;
+
+       mutex_lock(&nfsd_mutex);
+       rv = __nfsd4_write_time(file, buf, size, time);
+       mutex_unlock(&nfsd_mutex);
+       return rv;
 }
 
 /**
@@ -1230,12 +1271,22 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
  */
 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
 {
-       ssize_t rv;
+       return nfsd4_write_time(file, buf, size, &nfsd4_lease);
+}
 
-       mutex_lock(&nfsd_mutex);
-       rv = __write_leasetime(file, buf, size);
-       mutex_unlock(&nfsd_mutex);
-       return rv;
+/**
+ * write_gracetime - Set or report current NFSv4 grace period time
+ *
+ * As above, but sets the time of the NFSv4 grace period.
+ *
+ * Note this should never be set to less than the *previous*
+ * lease-period time, but we don't try to enforce this.  (In the common
+ * case (a new boot), we don't know what the previous lease time was
+ * anyway.)
+ */
+static ssize_t write_gracetime(struct file *file, char *buf, size_t size)
+{
+       return nfsd4_write_time(file, buf, size, &nfsd4_grace);
 }
 
 extern char *nfs4_recoverydir(void);
@@ -1260,8 +1311,9 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
 
                status = nfs4_reset_recoverydir(recdir);
        }
-       sprintf(buf, "%s\n", nfs4_recoverydir());
-       return strlen(buf);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n",
+                                                       nfs4_recoverydir());
 }
 
 /**
@@ -1313,6 +1365,8 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
                [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
+               [NFSD_Export_features] = {"export_features",
+                                       &export_features_operations, S_IRUGO},
                [NFSD_FO_UnlockIP] = {"unlock_ip",
                                        &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_FO_UnlockFS] = {"unlock_filesystem",
@@ -1326,6 +1380,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
                [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
 #ifdef CONFIG_NFSD_V4
                [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
+               [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
 #endif
                /* last one */ {""}