Commit b41b66d6 authored by NeilBrown's avatar NeilBrown Committed by Linus Torvalds

[PATCH] knfsd: allow sockets to be passed to nfsd via 'portlist'

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: default avatarNeil Brown <neilb@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 80212d59
...@@ -24,9 +24,11 @@ ...@@ -24,9 +24,11 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/ctype.h>
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/nfsd_idmap.h> #include <linux/nfsd_idmap.h>
#include <linux/lockd/bind.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
...@@ -426,16 +428,55 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size) ...@@ -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) static ssize_t write_ports(struct file *file, char *buf, size_t size)
{ {
/* for now, ignore what was written and just if (size == 0) {
* return known ports int len = 0;
* AF proto address port 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.
*/
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; int len = 0;
if (!toclose)
return -ENOMEM;
lock_kernel(); lock_kernel();
if (nfsd_serv) if (nfsd_serv)
len = svc_sock_names(buf, nfsd_serv); len = svc_sock_names(buf, nfsd_serv, toclose);
unlock_kernel(); unlock_kernel();
kfree(toclose);
return len; return len;
}
return -EINVAL;
} }
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
......
...@@ -195,7 +195,7 @@ void nfsd_reset_versions(void) ...@@ -195,7 +195,7 @@ void nfsd_reset_versions(void)
} }
} }
static int nfsd_create_serv(void) int nfsd_create_serv(void)
{ {
int err = 0; int err = 0;
lock_kernel(); lock_kernel();
...@@ -210,8 +210,6 @@ static int nfsd_create_serv(void) ...@@ -210,8 +210,6 @@ static int nfsd_create_serv(void)
nfsd_last_thread); nfsd_last_thread);
if (nfsd_serv == NULL) if (nfsd_serv == NULL)
err = -ENOMEM; err = -ENOMEM;
else
nfsd_serv->sv_nrthreads++;
unlock_kernel(); unlock_kernel();
do_gettimeofday(&nfssvc_boot); /* record boot time */ do_gettimeofday(&nfssvc_boot); /* record boot time */
return err; return err;
......
...@@ -143,6 +143,7 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *); ...@@ -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 }; enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
int nfsd_vers(int vers, enum vers_op change); int nfsd_vers(int vers, enum vers_op change);
void nfsd_reset_versions(void); void nfsd_reset_versions(void);
int nfsd_create_serv(void);
/* /*
......
...@@ -61,6 +61,10 @@ int svc_recv(struct svc_serv *, struct svc_rqst *, long); ...@@ -61,6 +61,10 @@ int svc_recv(struct svc_serv *, struct svc_rqst *, long);
int svc_send(struct svc_rqst *); int svc_send(struct svc_rqst *);
void svc_drop(struct svc_rqst *); void svc_drop(struct svc_rqst *);
void svc_sock_update_bufs(struct svc_serv *serv); 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 */ #endif /* SUNRPC_SVCSOCK_H */
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/file.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <net/ip.h> #include <net/ip.h>
...@@ -451,9 +452,9 @@ static int one_sock_name(char *buf, struct svc_sock *svsk) ...@@ -451,9 +452,9 @@ static int one_sock_name(char *buf, struct svc_sock *svsk)
} }
int 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; int len = 0;
if (!serv) if (!serv)
...@@ -461,9 +462,14 @@ svc_sock_names(char *buf, struct svc_serv *serv) ...@@ -461,9 +462,14 @@ svc_sock_names(char *buf, struct svc_serv *serv)
spin_lock(&serv->sv_lock); spin_lock(&serv->sv_lock);
list_for_each_entry(svsk, &serv->sv_permsocks, sk_list) { list_for_each_entry(svsk, &serv->sv_permsocks, sk_list) {
int onelen = one_sock_name(buf+len, svsk); int onelen = one_sock_name(buf+len, svsk);
if (toclose && strcmp(toclose, buf+len) == 0)
closesk = svsk;
else
len += onelen; len += onelen;
} }
spin_unlock(&serv->sv_lock); spin_unlock(&serv->sv_lock);
if (closesk)
svc_delete_socket(closesk);
return len; return len;
} }
EXPORT_SYMBOL(svc_sock_names); EXPORT_SYMBOL(svc_sock_names);
...@@ -1407,6 +1413,38 @@ svc_setup_socket(struct svc_serv *serv, struct socket *sock, ...@@ -1407,6 +1413,38 @@ svc_setup_socket(struct svc_serv *serv, struct socket *sock,
return svsk; 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. * Create socket for RPC service.
*/ */
...@@ -1482,6 +1520,9 @@ svc_delete_socket(struct svc_sock *svsk) ...@@ -1482,6 +1520,9 @@ svc_delete_socket(struct svc_sock *svsk)
if (!svsk->sk_inuse) { if (!svsk->sk_inuse) {
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
if (svsk->sk_sock->file)
sockfd_put(svsk->sk_sock);
else
sock_release(svsk->sk_sock); sock_release(svsk->sk_sock);
kfree(svsk); kfree(svsk);
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment