[PATCH] knfsd: allow sockets to be passed to nfsd via 'portlist'
authorNeilBrown <neilb@suse.de>
Mon, 2 Oct 2006 09:17:48 +0000 (02:17 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 2 Oct 2006 14:57:18 +0000 (07:57 -0700)
Userspace should create and bind a socket (but not connectted) and write the
'fd' to portlist.  This will cause the nfs server to listen on that socket.

To close a socket, the name of the socket - as read from 'portlist' can be
written to 'portlist' with a preceding '-'.

Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/nfsd/nfsctl.c
fs/nfsd/nfssvc.c
include/linux/nfsd/nfsd.h
include/linux/sunrpc/svcsock.h
net/sunrpc/svcsock.c

index d4041a0..80e97a5 100644 (file)
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/smp_lock.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>
@@ -426,16 +428,55 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
 
 static ssize_t write_ports(struct file *file, char *buf, size_t size)
 {
-       /* for now, ignore what was written and just
-        * return known ports
-        * AF proto address port
+       if (size == 0) {
+               int len = 0;
+               lock_kernel();
+               if (nfsd_serv)
+                       len = svc_sock_names(buf, nfsd_serv, NULL);
+               unlock_kernel();
+               return len;
+       }
+       /* Either a single 'fd' number is written, in which
+        * case it must be for a socket of a supported family/protocol,
+        * and we use it as an nfsd socket, or
+        * A '-' followed by the 'name' of a socket in which case
+        * we close the socket.
         */
-       int len = 0;
-       lock_kernel();
-       if (nfsd_serv)
-               len = svc_sock_names(buf, nfsd_serv);
-       unlock_kernel();
-       return len;
+       if (isdigit(buf[0])) {
+               char *mesg = buf;
+               int fd;
+               int err;
+               err = get_int(&mesg, &fd);
+               if (err)
+                       return -EINVAL;
+               if (fd < 0)
+                       return -EINVAL;
+               err = nfsd_create_serv();
+               if (!err) {
+                       int proto = 0;
+                       err = svc_addsock(nfsd_serv, fd, buf, &proto);
+                       /* Decrease the count, but don't shutdown the
+                        * the service
+                        */
+                       if (err >= 0)
+                               lockd_up(proto);
+                       nfsd_serv->sv_nrthreads--;
+               }
+               return err;
+       }
+       if (buf[0] == '-') {
+               char *toclose = kstrdup(buf+1, GFP_KERNEL);
+               int len = 0;
+               if (!toclose)
+                       return -ENOMEM;
+               lock_kernel();
+               if (nfsd_serv)
+                       len = svc_sock_names(buf, nfsd_serv, toclose);
+               unlock_kernel();
+               kfree(toclose);
+               return len;
+       }
+       return -EINVAL;
 }
 
 #ifdef CONFIG_NFSD_V4
index 5d473d8..784f94f 100644 (file)
@@ -195,7 +195,7 @@ void nfsd_reset_versions(void)
        }
 }
 
-static int nfsd_create_serv(void)
+int nfsd_create_serv(void)
 {
        int err = 0;
        lock_kernel();
@@ -210,8 +210,6 @@ static int nfsd_create_serv(void)
                               nfsd_last_thread);
        if (nfsd_serv == NULL)
                err = -ENOMEM;
-       else
-               nfsd_serv->sv_nrthreads++;
        unlock_kernel();
        do_gettimeofday(&nfssvc_boot);          /* record boot time */
        return err;
index 46f1dc5..e1dbc86 100644 (file)
@@ -143,6 +143,7 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
 enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
 int nfsd_vers(int vers, enum vers_op change);
 void nfsd_reset_versions(void);
+int nfsd_create_serv(void);
 
 
 /* 
index 3caf92d..b8a9652 100644 (file)
@@ -61,6 +61,10 @@ int          svc_recv(struct svc_serv *, struct svc_rqst *, long);
 int            svc_send(struct svc_rqst *);
 void           svc_drop(struct svc_rqst *);
 void           svc_sock_update_bufs(struct svc_serv *serv);
-int            svc_sock_names(char *buf, struct svc_serv *serv);
+int            svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
+int            svc_addsock(struct svc_serv *serv,
+                           int fd,
+                           char *name_return,
+                           int *proto);
 
 #endif /* SUNRPC_SVCSOCK_H */
index 3ee4b78..c6be67a 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/slab.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/file.h>
 #include <net/sock.h>
 #include <net/checksum.h>
 #include <net/ip.h>
@@ -451,9 +452,9 @@ static int one_sock_name(char *buf, struct svc_sock *svsk)
 }
 
 int
-svc_sock_names(char *buf, struct svc_serv *serv)
+svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
 {
-       struct svc_sock *svsk;
+       struct svc_sock *svsk, *closesk = NULL;
        int len = 0;
 
        if (!serv)
@@ -461,9 +462,14 @@ svc_sock_names(char *buf, struct svc_serv *serv)
        spin_lock(&serv->sv_lock);
        list_for_each_entry(svsk, &serv->sv_permsocks, sk_list) {
                int onelen = one_sock_name(buf+len, svsk);
-               len += onelen;
+               if (toclose && strcmp(toclose, buf+len) == 0)
+                       closesk = svsk;
+               else
+                       len += onelen;
        }
        spin_unlock(&serv->sv_lock);
+       if (closesk)
+               svc_delete_socket(closesk);
        return len;
 }
 EXPORT_SYMBOL(svc_sock_names);
@@ -1407,6 +1413,38 @@ svc_setup_socket(struct svc_serv *serv, struct socket *sock,
        return svsk;
 }
 
+int svc_addsock(struct svc_serv *serv,
+               int fd,
+               char *name_return,
+               int *proto)
+{
+       int err = 0;
+       struct socket *so = sockfd_lookup(fd, &err);
+       struct svc_sock *svsk = NULL;
+
+       if (!so)
+               return err;
+       if (so->sk->sk_family != AF_INET)
+               err =  -EAFNOSUPPORT;
+       else if (so->sk->sk_protocol != IPPROTO_TCP &&
+           so->sk->sk_protocol != IPPROTO_UDP)
+               err =  -EPROTONOSUPPORT;
+       else if (so->state > SS_UNCONNECTED)
+               err = -EISCONN;
+       else {
+               svsk = svc_setup_socket(serv, so, &err, 1);
+               if (svsk)
+                       err = 0;
+       }
+       if (err) {
+               sockfd_put(so);
+               return err;
+       }
+       if (proto) *proto = so->sk->sk_protocol;
+       return one_sock_name(name_return, svsk);
+}
+EXPORT_SYMBOL_GPL(svc_addsock);
+
 /*
  * Create socket for RPC service.
  */
@@ -1482,7 +1520,10 @@ svc_delete_socket(struct svc_sock *svsk)
 
        if (!svsk->sk_inuse) {
                spin_unlock_bh(&serv->sv_lock);
-               sock_release(svsk->sk_sock);
+               if (svsk->sk_sock->file)
+                       sockfd_put(svsk->sk_sock);
+               else
+                       sock_release(svsk->sk_sock);
                kfree(svsk);
        } else {
                spin_unlock_bh(&serv->sv_lock);