]> git.hungrycats.org Git - linux/commitdiff
NFSv4: Add support for a delegation callback server.
authorTrond Myklebust <trond.myklebust@fys.uio.no>
Mon, 23 Aug 2004 15:54:38 +0000 (11:54 -0400)
committerTrond Myklebust <trond.myklebust@fys.uio.no>
Mon, 23 Aug 2004 15:54:38 +0000 (11:54 -0400)
Signed-off-by: Trond Myklebust <trond.myklebust@fys.uio.no>
12 files changed:
fs/nfs/Makefile
fs/nfs/callback.c [new file with mode: 0644]
fs/nfs/callback.h [new file with mode: 0644]
fs/nfs/callback_proc.c [new file with mode: 0644]
fs/nfs/callback_xdr.c [new file with mode: 0644]
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4state.c
include/linux/nfs_fs.h
include/linux/sunrpc/svc.h
net/sunrpc/sunrpc_syms.c
net/sunrpc/svc.c

index 98ade147a4dee3fbbbee1d0ced5cf4d71d370d34..b4baa031edf4ddcbf1dbfad15bd3b24b82cbfd58 100644 (file)
@@ -9,6 +9,7 @@ nfs-y                   := dir.o file.o inode.o nfs2xdr.o pagelist.o \
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o      
 nfs-$(CONFIG_NFS_V3)   += nfs3proc.o nfs3xdr.o
 nfs-$(CONFIG_NFS_V4)   += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
-                          delegation.o idmap.o
+                          delegation.o idmap.o \
+                          callback.o callback_xdr.o callback_proc.o
 nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
 nfs-objs               := $(nfs-y)
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
new file mode 100644 (file)
index 0000000..ad7677b
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * linux/fs/nfs/callback.c
+ *
+ * Copyright (C) 2004 Trond Myklebust
+ *
+ * NFSv4 callback handling
+ */
+
+#include <linux/config.h>
+#include <linux/completion.h>
+#include <linux/ip.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/nfs_fs.h>
+#include "callback.h"
+
+#define NFSDBG_FACILITY NFSDBG_CALLBACK
+
+struct nfs_callback_data {
+       unsigned int users;
+       struct svc_serv *serv;
+       pid_t pid;
+       struct completion started;
+       struct completion stopped;
+};
+
+static struct nfs_callback_data nfs_callback_info;
+static DECLARE_MUTEX(nfs_callback_sema);
+static struct svc_program nfs4_callback_program;
+
+unsigned short nfs_callback_tcpport;
+
+/*
+ * This is the callback kernel thread.
+ */
+static void nfs_callback_svc(struct svc_rqst *rqstp)
+{
+       struct svc_serv *serv = rqstp->rq_server;
+       int err;
+
+       __module_get(THIS_MODULE);
+       lock_kernel();
+
+       nfs_callback_info.pid = current->pid;
+       daemonize("nfsv4-svc");
+       /* Process request with signals blocked, but allow SIGKILL.  */
+       allow_signal(SIGKILL);
+
+       complete(&nfs_callback_info.started);
+
+       while (nfs_callback_info.users != 0 || !signalled()) {
+               /*
+                * Listen for a request on the socket
+                */
+               err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT);
+               if (err == -EAGAIN || err == -EINTR)
+                       continue;
+               if (err < 0) {
+                       printk(KERN_WARNING
+                                       "%s: terminating on error %d\n",
+                                       __FUNCTION__, -err);
+                       break;
+               }
+               dprintk("%s: request from %u.%u.%u.%u\n", __FUNCTION__,
+                               NIPQUAD(rqstp->rq_addr.sin_addr.s_addr));
+               svc_process(serv, rqstp);
+       }
+
+       nfs_callback_info.pid = 0;
+       complete(&nfs_callback_info.stopped);
+       unlock_kernel();
+       module_put_and_exit(0);
+}
+
+/*
+ * Bring up the server process if it is not already up.
+ */
+int nfs_callback_up(void)
+{
+       struct svc_serv *serv;
+       struct svc_sock *svsk;
+       int ret = 0;
+
+       lock_kernel();
+       down(&nfs_callback_sema);
+       if (nfs_callback_info.users++ || nfs_callback_info.pid != 0)
+               goto out;
+       init_completion(&nfs_callback_info.started);
+       init_completion(&nfs_callback_info.stopped);
+       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE);
+       ret = -ENOMEM;
+       if (!serv)
+               goto out_err;
+       /* FIXME: We don't want to register this socket with the portmapper */
+       ret = svc_makesock(serv, IPPROTO_TCP, 0);
+       if (ret < 0)
+               goto out_destroy;
+       if (!list_empty(&serv->sv_permsocks)) {
+               svsk = list_entry(serv->sv_permsocks.next,
+                               struct svc_sock, sk_list);
+               nfs_callback_tcpport = ntohs(inet_sk(svsk->sk_sk)->sport);
+               dprintk ("Callback port = 0x%x\n", nfs_callback_tcpport);
+       } else
+               BUG();
+       ret = svc_create_thread(nfs_callback_svc, serv);
+       if (ret < 0)
+               goto out_destroy;
+       nfs_callback_info.serv = serv;
+       wait_for_completion(&nfs_callback_info.started);
+out:
+       up(&nfs_callback_sema);
+       unlock_kernel();
+       return ret;
+out_destroy:
+       svc_destroy(serv);
+out_err:
+       nfs_callback_info.users--;
+       goto out;
+}
+
+/*
+ * Kill the server process if it is not already up.
+ */
+int nfs_callback_down(void)
+{
+       int ret = 0;
+
+       lock_kernel();
+       down(&nfs_callback_sema);
+       if (--nfs_callback_info.users || nfs_callback_info.pid == 0)
+               goto out;
+       kill_proc(nfs_callback_info.pid, SIGKILL, 1);
+       wait_for_completion(&nfs_callback_info.stopped);
+out:
+       up(&nfs_callback_sema);
+       unlock_kernel();
+       return ret;
+}
+
+/*
+ * AUTH_NULL authentication
+ */
+static int nfs_callback_null_accept(struct svc_rqst *rqstp, u32 *authp)
+{
+       struct kvec    *argv = &rqstp->rq_arg.head[0];
+       struct kvec    *resv = &rqstp->rq_res.head[0];
+
+       if (argv->iov_len < 3*4)
+               return SVC_GARBAGE;
+
+       if (svc_getu32(argv) != 0) {
+               dprintk("svc: bad null cred\n");
+               *authp = rpc_autherr_badcred;
+               return SVC_DENIED;
+       }
+       if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
+               dprintk("svc: bad null verf\n");
+                *authp = rpc_autherr_badverf;
+                return SVC_DENIED;
+       }
+
+       /* Signal that mapping to nobody uid/gid is required */
+       rqstp->rq_cred.cr_uid = (uid_t) -1;
+       rqstp->rq_cred.cr_gid = (gid_t) -1;
+       rqstp->rq_cred.cr_group_info = groups_alloc(0);
+       if (rqstp->rq_cred.cr_group_info == NULL)
+               return SVC_DROP; /* kmalloc failure - client must retry */
+
+       /* Put NULL verifier */
+       svc_putu32(resv, RPC_AUTH_NULL);
+       svc_putu32(resv, 0);
+       dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
+       return SVC_OK;
+}
+
+static int nfs_callback_null_release(struct svc_rqst *rqstp)
+{
+       if (rqstp->rq_cred.cr_group_info)
+               put_group_info(rqstp->rq_cred.cr_group_info);
+       rqstp->rq_cred.cr_group_info = NULL;
+       return 0; /* don't drop */
+}
+
+static struct auth_ops nfs_callback_auth_null = {
+       .name = "null",
+       .flavour = RPC_AUTH_NULL,
+       .accept = nfs_callback_null_accept,
+       .release = nfs_callback_null_release,
+};
+
+/*
+ * AUTH_SYS authentication
+ */
+static int nfs_callback_unix_accept(struct svc_rqst *rqstp, u32 *authp)
+{
+       struct kvec    *argv = &rqstp->rq_arg.head[0];
+       struct kvec    *resv = &rqstp->rq_res.head[0];
+       struct svc_cred *cred = &rqstp->rq_cred;
+       u32 slen, i;
+       int len = argv->iov_len;
+
+       dprintk("%s: start\n", __FUNCTION__);
+       cred->cr_group_info = NULL;
+       rqstp->rq_client = NULL;
+       if ((len -= 3*4) < 0)
+               return SVC_GARBAGE;
+
+       /* Get length, time stamp and machine name */
+       svc_getu32(argv);
+       svc_getu32(argv);
+       slen = XDR_QUADLEN(ntohl(svc_getu32(argv)));
+       if (slen > 64 || (len -= (slen + 3)*4) < 0)
+               goto badcred;
+       argv->iov_base = (void*)((u32*)argv->iov_base + slen);
+       argv->iov_len -= slen*4;
+
+       cred->cr_uid = ntohl(svc_getu32(argv));
+       cred->cr_gid = ntohl(svc_getu32(argv));
+       slen = ntohl(svc_getu32(argv));
+       if (slen > 16 || (len -= (slen + 2)*4) < 0)
+               goto badcred;
+       cred->cr_group_info = groups_alloc(slen);
+       if (cred->cr_group_info == NULL)
+               return SVC_DROP;
+       for (i = 0; i < slen; i++)
+               GROUP_AT(cred->cr_group_info, i) = ntohl(svc_getu32(argv));
+
+       if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
+               *authp = rpc_autherr_badverf;
+               return SVC_DENIED;
+       }
+       /* Put NULL verifier */
+       svc_putu32(resv, RPC_AUTH_NULL);
+       svc_putu32(resv, 0);
+       dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
+       return SVC_OK;
+badcred:
+       *authp = rpc_autherr_badcred;
+       return SVC_DENIED;
+}
+
+static int nfs_callback_unix_release(struct svc_rqst *rqstp)
+{
+       if (rqstp->rq_cred.cr_group_info)
+               put_group_info(rqstp->rq_cred.cr_group_info);
+       rqstp->rq_cred.cr_group_info = NULL;
+       return 0;
+}
+
+static struct auth_ops nfs_callback_auth_unix = {
+       .name = "unix",
+       .flavour = RPC_AUTH_UNIX,
+       .accept = nfs_callback_unix_accept,
+       .release = nfs_callback_unix_release,
+};
+
+/*
+ * Hook the authentication protocol
+ */
+static int nfs_callback_auth(struct svc_rqst *rqstp, u32 *authp)
+{
+       struct in_addr *addr = &rqstp->rq_addr.sin_addr;
+       struct nfs4_client *clp;
+       struct kvec *argv = &rqstp->rq_arg.head[0];
+       int flavour;
+       int retval;
+
+       /* Don't talk to strangers */
+       clp = nfs4_find_client(addr);
+       if (clp == NULL)
+               return SVC_DROP;
+       dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
+       nfs4_put_client(clp);
+       flavour = ntohl(svc_getu32(argv));
+       switch(flavour) {
+               case RPC_AUTH_NULL:
+                       if (rqstp->rq_proc != CB_NULL) {
+                               *authp = rpc_autherr_tooweak;
+                               retval = SVC_DENIED;
+                               break;
+                       }
+                       rqstp->rq_authop = &nfs_callback_auth_null;
+                       retval = nfs_callback_null_accept(rqstp, authp);
+                       break;
+               case RPC_AUTH_UNIX:
+                       /* Eat the authentication flavour */
+                       rqstp->rq_authop = &nfs_callback_auth_unix;
+                       retval = nfs_callback_unix_accept(rqstp, authp);
+                       break;
+               default:
+                       /* FIXME: need to add RPCSEC_GSS upcalls */
+#if 0
+                       svc_ungetu32(argv);
+                       retval = svc_authenticate(rqstp, authp);
+#else
+                       *authp = rpc_autherr_rejectedcred;
+                       retval = SVC_DENIED;
+#endif
+       }
+       dprintk("%s: flavour %d returning error %d\n", __FUNCTION__, flavour, retval);
+       return retval;
+}
+
+/*
+ * Define NFS4 callback program
+ */
+extern struct svc_version nfs4_callback_version1;
+
+static struct svc_version *nfs4_callback_version[] = {
+       [1] = &nfs4_callback_version1,
+};
+
+static struct svc_stat nfs4_callback_stats;
+
+static struct svc_program nfs4_callback_program = {
+       .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
+       .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
+       .pg_vers = nfs4_callback_version,               /* version table */
+       .pg_name = "NFSv4 callback",                    /* service name */
+       .pg_class = "nfs",                              /* authentication class */
+       .pg_stats = &nfs4_callback_stats,
+       .pg_authenticate = nfs_callback_auth,
+};
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
new file mode 100644 (file)
index 0000000..a0db2d4
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * linux/fs/nfs/callback.h
+ *
+ * Copyright (C) 2004 Trond Myklebust
+ *
+ * NFSv4 callback definitions
+ */
+#ifndef __LINUX_FS_NFS_CALLBACK_H
+#define __LINUX_FS_NFS_CALLBACK_H
+
+#define NFS4_CALLBACK 0x40000000
+#define NFS4_CALLBACK_XDRSIZE 2048
+#define NFS4_CALLBACK_BUFSIZE (1024 + NFS4_CALLBACK_XDRSIZE)
+
+enum nfs4_callback_procnum {
+       CB_NULL = 0,
+       CB_COMPOUND = 1,
+};
+
+enum nfs4_callback_opnum {
+       OP_CB_GETATTR = 3,
+       OP_CB_RECALL  = 4,
+       OP_CB_ILLEGAL = 10044,
+};
+
+struct cb_compound_hdr_arg {
+       int taglen;
+       const char *tag;
+       unsigned int callback_ident;
+       unsigned nops;
+};
+
+struct cb_compound_hdr_res {
+       uint32_t *status;
+       int taglen;
+       const char *tag;
+       uint32_t *nops;
+};
+
+struct cb_getattrargs {
+       struct sockaddr_in *addr;
+       struct nfs_fh fh;
+       uint32_t bitmap[2];
+};
+
+struct cb_getattrres {
+       uint32_t status;
+       uint32_t bitmap[2];
+       uint64_t size;
+       uint64_t change_attr;
+       struct timespec ctime;
+       struct timespec mtime;
+};
+
+struct cb_recallargs {
+       struct sockaddr_in *addr;
+       struct nfs_fh fh;
+       nfs4_stateid stateid;
+       uint32_t truncate;
+};
+
+extern unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
+extern unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
+
+extern int nfs_callback_up(void);
+extern int nfs_callback_down(void);
+
+extern unsigned short nfs_callback_tcpport;
+
+#endif /* __LINUX_FS_NFS_CALLBACK_H */
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
new file mode 100644 (file)
index 0000000..ece27e4
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * linux/fs/nfs/callback_proc.c
+ *
+ * Copyright (C) 2004 Trond Myklebust
+ *
+ * NFSv4 callback procedures
+ */
+#include <linux/config.h>
+#include <linux/nfs4.h>
+#include <linux/nfs_fs.h>
+#include "callback.h"
+#include "delegation.h"
+
+#define NFSDBG_FACILITY NFSDBG_CALLBACK
+unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
+{
+       struct nfs4_client *clp;
+       struct nfs_delegation *delegation;
+       struct nfs_inode *nfsi;
+       struct inode *inode;
+       
+       res->bitmap[0] = res->bitmap[1] = 0;
+       res->status = htonl(NFS4ERR_BADHANDLE);
+       clp = nfs4_find_client(&args->addr->sin_addr);
+       if (clp == NULL)
+               goto out;
+       inode = nfs_delegation_find_inode(clp, &args->fh);
+       if (inode == NULL)
+               goto out_putclient;
+       nfsi = NFS_I(inode);
+       down_read(&nfsi->rwsem);
+       delegation = nfsi->delegation;
+       if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0)
+               goto out_iput;
+       res->size = i_size_read(inode);
+       res->change_attr = NFS_CHANGE_ATTR(inode);
+       res->ctime = inode->i_ctime;
+       res->mtime = inode->i_mtime;
+       res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) &
+               args->bitmap[0];
+       res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) &
+               args->bitmap[1];
+       res->status = 0;
+out_iput:
+       up_read(&nfsi->rwsem);
+       iput(inode);
+out_putclient:
+       nfs4_put_client(clp);
+out:
+       dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
+       return res->status;
+}
+
+unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
+{
+       struct nfs4_client *clp;
+       struct inode *inode;
+       unsigned res;
+       
+       res = htonl(NFS4ERR_BADHANDLE);
+       clp = nfs4_find_client(&args->addr->sin_addr);
+       if (clp == NULL)
+               goto out;
+       inode = nfs_delegation_find_inode(clp, &args->fh);
+       if (inode == NULL)
+               goto out_putclient;
+       /* Set up a helper thread to actually return the delegation */
+       switch(nfs_async_inode_return_delegation(inode, &args->stateid)) {
+               case 0:
+                       res = 0;
+                       break;
+               case -ENOENT:
+                       res = htonl(NFS4ERR_BAD_STATEID);
+                       break;
+               default:
+                       res = htonl(NFS4ERR_RESOURCE);
+       }
+       iput(inode);
+out_putclient:
+       nfs4_put_client(clp);
+out:
+       dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
+       return res;
+}
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
new file mode 100644 (file)
index 0000000..d271df9
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * linux/fs/nfs/callback_xdr.c
+ *
+ * Copyright (C) 2004 Trond Myklebust
+ *
+ * NFSv4 callback encode/decode procedures
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfs4.h>
+#include <linux/nfs_fs.h>
+#include "callback.h"
+
+#define CB_OP_TAGLEN_MAXSZ     (512)
+#define CB_OP_HDR_RES_MAXSZ    (2 + CB_OP_TAGLEN_MAXSZ)
+#define CB_OP_GETATTR_BITMAP_MAXSZ     (4)
+#define CB_OP_GETATTR_RES_MAXSZ        (CB_OP_HDR_RES_MAXSZ + \
+                               CB_OP_GETATTR_BITMAP_MAXSZ + \
+                               2 + 2 + 3 + 3)
+#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
+
+#define NFSDBG_FACILITY NFSDBG_CALLBACK
+
+typedef unsigned (*callback_process_op_t)(void *, void *);
+typedef unsigned (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
+typedef unsigned (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
+
+
+struct callback_op {
+       callback_process_op_t process_op;
+       callback_decode_arg_t decode_args;
+       callback_encode_res_t encode_res;
+       long res_maxsize;
+};
+
+static struct callback_op callback_ops[];
+
+static int nfs4_callback_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+       return htonl(NFS4_OK);
+}
+
+static int nfs4_decode_void(struct svc_rqst *rqstp, uint32_t *p, void *dummy)
+{
+       return xdr_argsize_check(rqstp, p);
+}
+
+static int nfs4_encode_void(struct svc_rqst *rqstp, uint32_t *p, void *dummy)
+{
+       return xdr_ressize_check(rqstp, p);
+}
+
+static uint32_t *read_buf(struct xdr_stream *xdr, int nbytes)
+{
+       uint32_t *p;
+
+       p = xdr_inline_decode(xdr, nbytes);
+       if (unlikely(p == NULL))
+               printk(KERN_WARNING "NFSv4 callback reply buffer overflowed!\n");
+       return p;
+}
+
+static unsigned decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str)
+{
+       uint32_t *p;
+
+       p = read_buf(xdr, 4);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       *len = ntohl(*p);
+
+       if (*len != 0) {
+               p = read_buf(xdr, *len);
+               if (unlikely(p == NULL))
+                       return htonl(NFS4ERR_RESOURCE);
+               *str = (const char *)p;
+       } else
+               *str = NULL;
+
+       return 0;
+}
+
+static unsigned decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh)
+{
+       uint32_t *p;
+
+       p = read_buf(xdr, 4);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       fh->size = ntohl(*p);
+       if (fh->size > NFS4_FHSIZE)
+               return htonl(NFS4ERR_BADHANDLE);
+       p = read_buf(xdr, fh->size);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       memcpy(&fh->data[0], p, fh->size);
+       memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size);
+       return 0;
+}
+
+static unsigned decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
+{
+       uint32_t *p;
+       unsigned int attrlen;
+
+       p = read_buf(xdr, 4);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       attrlen = ntohl(*p);
+       p = read_buf(xdr, attrlen << 2);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       if (likely(attrlen > 0))
+               bitmap[0] = ntohl(*p++);
+       if (attrlen > 1)
+               bitmap[1] = ntohl(*p);
+       return 0;
+}
+
+static unsigned decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
+{
+       uint32_t *p;
+
+       p = read_buf(xdr, 16);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       memcpy(stateid->data, p, 16);
+       return 0;
+}
+
+static unsigned decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
+{
+       uint32_t *p;
+       unsigned int minor_version;
+       unsigned status;
+
+       status = decode_string(xdr, &hdr->taglen, &hdr->tag);
+       if (unlikely(status != 0))
+               return status;
+       /* We do not like overly long tags! */
+       if (hdr->taglen > CB_OP_TAGLEN_MAXSZ-12 || hdr->taglen < 0) {
+               printk("NFSv4 CALLBACK %s: client sent tag of length %u\n",
+                               __FUNCTION__, hdr->taglen);
+               return htonl(NFS4ERR_RESOURCE);
+       }
+       p = read_buf(xdr, 12);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       minor_version = ntohl(*p++);
+       /* Check minor version is zero. */
+       if (minor_version != 0) {
+               printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n",
+                               __FUNCTION__, minor_version);
+               return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
+       }
+       hdr->callback_ident = ntohl(*p++);
+       hdr->nops = ntohl(*p);
+       return 0;
+}
+
+static unsigned decode_op_hdr(struct xdr_stream *xdr, unsigned int *op)
+{
+       uint32_t *p;
+       p = read_buf(xdr, 4);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       *op = ntohl(*p);
+       return 0;
+}
+
+static unsigned decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_getattrargs *args)
+{
+       unsigned status;
+
+       status = decode_fh(xdr, &args->fh);
+       if (unlikely(status != 0))
+               goto out;
+       args->addr = &rqstp->rq_addr;
+       status = decode_bitmap(xdr, args->bitmap);
+out:
+       dprintk("%s: exit with status = %d\n", __FUNCTION__, status);
+       return status;
+}
+
+static unsigned decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_recallargs *args)
+{
+       uint32_t *p;
+       unsigned status;
+
+       args->addr = &rqstp->rq_addr;
+       status = decode_stateid(xdr, &args->stateid);
+       if (unlikely(status != 0))
+               goto out;
+       p = read_buf(xdr, 4);
+       if (unlikely(p == NULL)) {
+               status = htonl(NFS4ERR_RESOURCE);
+               goto out;
+       }
+       args->truncate = ntohl(*p);
+       status = decode_fh(xdr, &args->fh);
+out:
+       dprintk("%s: exit with status = %d\n", __FUNCTION__, status);
+       return 0;
+}
+
+static unsigned encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
+{
+       uint32_t *p;
+
+       p = xdr_reserve_space(xdr, 4 + len);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       xdr_encode_opaque(p, str, len);
+       return 0;
+}
+
+#define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE)
+#define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY)
+static unsigned encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, uint32_t **savep)
+{
+       uint32_t bm[2];
+       uint32_t *p;
+
+       bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0);
+       bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1);
+       if (bm[1] != 0) {
+               p = xdr_reserve_space(xdr, 16);
+               if (unlikely(p == NULL))
+                       return htonl(NFS4ERR_RESOURCE);
+               *p++ = htonl(2);
+               *p++ = bm[0];
+               *p++ = bm[1];
+       } else if (bm[0] != 0) {
+               p = xdr_reserve_space(xdr, 12);
+               if (unlikely(p == NULL))
+                       return htonl(NFS4ERR_RESOURCE);
+               *p++ = htonl(1);
+               *p++ = bm[0];
+       } else {
+               p = xdr_reserve_space(xdr, 8);
+               if (unlikely(p == NULL))
+                       return htonl(NFS4ERR_RESOURCE);
+               *p++ = htonl(0);
+       }
+       *savep = p;
+       return 0;
+}
+
+static unsigned encode_attr_change(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t change)
+{
+       uint32_t *p;
+
+       if (!(bitmap[0] & FATTR4_WORD0_CHANGE))
+               return 0;
+       p = xdr_reserve_space(xdr, 8);
+       if (unlikely(p == 0))
+               return htonl(NFS4ERR_RESOURCE);
+       p = xdr_encode_hyper(p, change);
+       return 0;
+}
+
+static unsigned encode_attr_size(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t size)
+{
+       uint32_t *p;
+
+       if (!(bitmap[0] & FATTR4_WORD0_SIZE))
+               return 0;
+       p = xdr_reserve_space(xdr, 8);
+       if (unlikely(p == 0))
+               return htonl(NFS4ERR_RESOURCE);
+       p = xdr_encode_hyper(p, size);
+       return 0;
+}
+
+static unsigned encode_attr_time(struct xdr_stream *xdr, const struct timespec *time)
+{
+       uint32_t *p;
+
+       p = xdr_reserve_space(xdr, 12);
+       if (unlikely(p == 0))
+               return htonl(NFS4ERR_RESOURCE);
+       p = xdr_encode_hyper(p, time->tv_sec);
+       *p = htonl(time->tv_nsec);
+       return 0;
+}
+
+static unsigned encode_attr_ctime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
+{
+       if (!(bitmap[1] & FATTR4_WORD1_TIME_METADATA))
+               return 0;
+       return encode_attr_time(xdr,time);
+}
+
+static unsigned encode_attr_mtime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
+{
+       if (!(bitmap[1] & FATTR4_WORD1_TIME_MODIFY))
+               return 0;
+       return encode_attr_time(xdr,time);
+}
+
+static unsigned encode_compound_hdr_res(struct xdr_stream *xdr, struct cb_compound_hdr_res *hdr)
+{
+       unsigned status;
+
+       hdr->status = xdr_reserve_space(xdr, 4);
+       if (unlikely(hdr->status == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       status = encode_string(xdr, hdr->taglen, hdr->tag);
+       if (unlikely(status != 0))
+               return status;
+       hdr->nops = xdr_reserve_space(xdr, 4);
+       if (unlikely(hdr->nops == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       return 0;
+}
+
+static unsigned encode_op_hdr(struct xdr_stream *xdr, uint32_t op, uint32_t res)
+{
+       uint32_t *p;
+       
+       p = xdr_reserve_space(xdr, 8);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+       *p++ = htonl(op);
+       *p = res;
+       return 0;
+}
+
+static unsigned encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_getattrres *res)
+{
+       uint32_t *savep;
+       unsigned status = res->status;
+       
+       if (unlikely(status != 0))
+               goto out;
+       status = encode_attr_bitmap(xdr, res->bitmap, &savep);
+       if (unlikely(status != 0))
+               goto out;
+       status = encode_attr_change(xdr, res->bitmap, res->change_attr);
+       if (unlikely(status != 0))
+               goto out;
+       status = encode_attr_size(xdr, res->bitmap, res->size);
+       if (unlikely(status != 0))
+               goto out;
+       status = encode_attr_ctime(xdr, res->bitmap, &res->ctime);
+       if (unlikely(status != 0))
+               goto out;
+       status = encode_attr_mtime(xdr, res->bitmap, &res->mtime);
+       *savep = htonl((unsigned int)((char *)xdr->p - (char *)(savep+1)));
+out:
+       dprintk("%s: exit with status = %d\n", __FUNCTION__, status);
+       return status;
+}
+
+static unsigned process_op(struct svc_rqst *rqstp,
+               struct xdr_stream *xdr_in, void *argp,
+               struct xdr_stream *xdr_out, void *resp)
+{
+       struct callback_op *op;
+       unsigned int op_nr;
+       unsigned int status = 0;
+       long maxlen;
+       unsigned res;
+
+       dprintk("%s: start\n", __FUNCTION__);
+       status = decode_op_hdr(xdr_in, &op_nr);
+       if (unlikely(status != 0)) {
+               op_nr = OP_CB_ILLEGAL;
+               op = &callback_ops[0];
+       } else if (unlikely(op_nr != OP_CB_GETATTR && op_nr != OP_CB_RECALL)) {
+               op_nr = OP_CB_ILLEGAL;
+               op = &callback_ops[0];
+               status = htonl(NFS4ERR_OP_ILLEGAL);
+       } else
+               op = &callback_ops[op_nr];
+
+       maxlen = xdr_out->end - xdr_out->p;
+       if (maxlen > 0 && maxlen < PAGE_SIZE) {
+               if (likely(status == 0 && op->decode_args != NULL))
+                       status = op->decode_args(rqstp, xdr_in, argp);
+               if (likely(status == 0 && op->process_op != NULL))
+                       status = op->process_op(argp, resp);
+       } else
+               status = htonl(NFS4ERR_RESOURCE);
+
+       res = encode_op_hdr(xdr_out, op_nr, status);
+       if (status == 0)
+               status = res;
+       if (op->encode_res != NULL && status == 0)
+               status = op->encode_res(rqstp, xdr_out, resp);
+       dprintk("%s: done, status = %d\n", __FUNCTION__, status);
+       return status;
+}
+
+/*
+ * Decode, process and encode a COMPOUND
+ */
+static int nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+       struct cb_compound_hdr_arg hdr_arg;
+       struct cb_compound_hdr_res hdr_res;
+       struct xdr_stream xdr_in, xdr_out;
+       uint32_t *p;
+       unsigned int status;
+       unsigned int nops = 1;
+
+       dprintk("%s: start\n", __FUNCTION__);
+
+       xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base);
+
+       p = (uint32_t*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
+       rqstp->rq_res.head[0].iov_len = PAGE_SIZE;
+       xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
+
+       decode_compound_hdr_arg(&xdr_in, &hdr_arg);
+       hdr_res.taglen = hdr_arg.taglen;
+       hdr_res.tag = hdr_arg.tag;
+       encode_compound_hdr_res(&xdr_out, &hdr_res);
+
+       for (;;) {
+               status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp);
+               if (status != 0)
+                       break;
+               if (nops == hdr_arg.nops)
+                       break;
+               nops++;
+       }
+       *hdr_res.status = status;
+       *hdr_res.nops = htonl(nops);
+       dprintk("%s: done, status = %u\n", __FUNCTION__, status);
+       return rpc_success;
+}
+
+/*
+ * Define NFS4 callback COMPOUND ops.
+ */
+static struct callback_op callback_ops[] = {
+       [0] = {
+               .res_maxsize = CB_OP_HDR_RES_MAXSZ,
+       },
+       [OP_CB_GETATTR] = {
+               .process_op = (callback_process_op_t)nfs4_callback_getattr,
+               .decode_args = (callback_decode_arg_t)decode_getattr_args,
+               .encode_res = (callback_encode_res_t)encode_getattr_res,
+               .res_maxsize = CB_OP_GETATTR_RES_MAXSZ,
+       },
+       [OP_CB_RECALL] = {
+               .process_op = (callback_process_op_t)nfs4_callback_recall,
+               .decode_args = (callback_decode_arg_t)decode_recall_args,
+               .res_maxsize = CB_OP_RECALL_RES_MAXSZ,
+       }
+};
+
+/*
+ * Define NFS4 callback procedures
+ */
+static struct svc_procedure nfs4_callback_procedures1[] = {
+       [CB_NULL] = {
+               .pc_func = nfs4_callback_null,
+               .pc_decode = (kxdrproc_t)nfs4_decode_void,
+               .pc_encode = (kxdrproc_t)nfs4_encode_void,
+               .pc_xdrressize = 1,
+       },
+       [CB_COMPOUND] = {
+               .pc_func = nfs4_callback_compound,
+               .pc_encode = (kxdrproc_t)nfs4_encode_void,
+               .pc_argsize = 256,
+               .pc_ressize = 256,
+               .pc_xdrressize = NFS4_CALLBACK_BUFSIZE,
+       }
+};
+
+struct svc_version nfs4_callback_version1 = {
+       .vs_vers = 1,
+       .vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
+       .vs_proc = nfs4_callback_procedures1,
+       .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
+       .vs_dispatch = NULL,
+};
+
index d736eb3a7a80a91f8215aa690c24acbba7d5489c..a004db25e5f2a96317f9d034b05c4281ca743ab0 100644 (file)
@@ -6,10 +6,15 @@
  * NFS file delegation management
  *
  */
+#include <linux/config.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_xdr.h>
-#include <linux/spinlock.h>
 
 #include "delegation.h"
 
@@ -141,6 +146,75 @@ restart:
        spin_unlock(&clp->cl_lock);
 }
 
+struct recall_threadargs {
+       struct inode *inode;
+       struct nfs4_client *clp;
+       const nfs4_stateid *stateid;
+
+       struct completion started;
+       int result;
+};
+
+static int recall_thread(void *data)
+{
+       struct recall_threadargs *args = (struct recall_threadargs *)data;
+       struct inode *inode = igrab(args->inode);
+       struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_delegation *delegation;
+
+       daemonize("nfsv4-delegreturn");
+
+       nfs_msync_inode(inode);
+       down_read(&clp->cl_sem);
+       down_write(&nfsi->rwsem);
+       spin_lock(&clp->cl_lock);
+       delegation = nfsi->delegation;
+       if (delegation != NULL && memcmp(delegation->stateid.data,
+                               args->stateid->data,
+                               sizeof(delegation->stateid.data)) == 0) {
+               list_del_init(&delegation->super_list);
+               nfsi->delegation = NULL;
+               args->result = 0;
+       } else {
+               delegation = NULL;
+               args->result = -ENOENT;
+       }
+       spin_unlock(&clp->cl_lock);
+       complete(&args->started);
+       up_write(&nfsi->rwsem);
+       up_read(&clp->cl_sem);
+       nfs_msync_inode(inode);
+
+       if (delegation != NULL)
+               nfs_do_return_delegation(inode, delegation);
+       iput(inode);
+       module_put_and_exit(0);
+}
+
+/*
+ * Asynchronous delegation recall!
+ */
+int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
+{
+       struct recall_threadargs data = {
+               .inode = inode,
+               .stateid = stateid,
+       };
+       int status;
+
+       init_completion(&data.started);
+       __module_get(THIS_MODULE);
+       status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
+       if (status < 0)
+               goto out_module_put;
+       wait_for_completion(&data.started);
+       return data.result;
+out_module_put:
+       module_put(THIS_MODULE);
+       return status;
+}
+
 /*
  * Retrieve the inode associated with a delegation
  */
index 58ed5946bf1c8d220d4fc1d2d9d4e050d6973712..fdc66f858951c5308e0bbe6bc77af52ce8425a84 100644 (file)
@@ -22,6 +22,7 @@ struct nfs_delegation {
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
 int nfs_inode_return_delegation(struct inode *inode);
+int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
 
 struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle);
 void nfs_return_all_delegations(struct super_block *sb);
index 7ef58d5eb4820fce2d1d5f48b66ba961a45de19a..b62078d7f8110659f5bf1d9b2f7b336879249751 100644 (file)
@@ -46,6 +46,8 @@
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
 
+#include "callback.h"
+
 #define OPENOWNER_POOL_SIZE    8
 
 static spinlock_t              state_spinlock = SPIN_LOCK_UNLOCKED;
@@ -94,22 +96,26 @@ nfs4_alloc_client(struct in_addr *addr)
 {
        struct nfs4_client *clp;
 
-       if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL))) {
-               memset(clp, 0, sizeof(*clp));
-               memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
-               init_rwsem(&clp->cl_sem);
-               INIT_LIST_HEAD(&clp->cl_delegations);
-               INIT_LIST_HEAD(&clp->cl_state_owners);
-               INIT_LIST_HEAD(&clp->cl_unused);
-               spin_lock_init(&clp->cl_lock);
-               atomic_set(&clp->cl_count, 1);
-               INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp);
-               INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
-               INIT_LIST_HEAD(&clp->cl_superblocks);
-               init_waitqueue_head(&clp->cl_waitq);
-               rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
-               clp->cl_state = 1 << NFS4CLNT_OK;
+       if (nfs_callback_up() < 0)
+               return NULL;
+       if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)) == NULL) {
+               nfs_callback_down();
+               return NULL;
        }
+       memset(clp, 0, sizeof(*clp));
+       memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
+       init_rwsem(&clp->cl_sem);
+       INIT_LIST_HEAD(&clp->cl_delegations);
+       INIT_LIST_HEAD(&clp->cl_state_owners);
+       INIT_LIST_HEAD(&clp->cl_unused);
+       spin_lock_init(&clp->cl_lock);
+       atomic_set(&clp->cl_count, 1);
+       INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp);
+       INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
+       INIT_LIST_HEAD(&clp->cl_superblocks);
+       init_waitqueue_head(&clp->cl_waitq);
+       rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
+       clp->cl_state = 1 << NFS4CLNT_OK;
        return clp;
 }
 
@@ -132,25 +138,52 @@ nfs4_free_client(struct nfs4_client *clp)
        if (clp->cl_rpcclient)
                rpc_shutdown_client(clp->cl_rpcclient);
        kfree(clp);
+       nfs_callback_down();
+}
+
+static struct nfs4_client *__nfs4_find_client(struct in_addr *addr)
+{
+       struct nfs4_client *clp;
+       list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
+               if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) {
+                       atomic_inc(&clp->cl_count);
+                       return clp;
+               }
+       }
+       return NULL;
+}
+
+struct nfs4_client *nfs4_find_client(struct in_addr *addr)
+{
+       struct nfs4_client *clp;
+       spin_lock(&state_spinlock);
+       clp = __nfs4_find_client(addr);
+       spin_unlock(&state_spinlock);
+       return clp;
 }
 
 struct nfs4_client *
 nfs4_get_client(struct in_addr *addr)
 {
-       struct nfs4_client *new, *clp = NULL;
+       struct nfs4_client *clp, *new = NULL;
 
-       new = nfs4_alloc_client(addr);
        spin_lock(&state_spinlock);
-       list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
-               if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0)
-                       goto found;
+       for (;;) {
+               clp = __nfs4_find_client(addr);
+               if (clp != NULL)
+                       break;
+               clp = new;
+               if (clp != NULL) {
+                       list_add(&clp->cl_servers, &nfs4_clientid_list);
+                       new = NULL;
+                       break;
+               }
+               spin_unlock(&state_spinlock);
+               new = nfs4_alloc_client(addr);
+               spin_lock(&state_spinlock);
+               if (new == NULL)
+                       break;
        }
-       if (new)
-               list_add(&new->cl_servers, &nfs4_clientid_list);
-       spin_unlock(&state_spinlock);
-       return new;
-found:
-       atomic_inc(&clp->cl_count);
        spin_unlock(&state_spinlock);
        if (new)
                nfs4_free_client(new);
index 9954f66c0dac8ebfe2b1039e0146b6d6fbbf79da..dccc401fc6190533301d745a49fc96e3c6aa12ef 100644 (file)
@@ -686,6 +686,7 @@ extern void destroy_nfsv4_state(struct nfs_server *);
 extern struct nfs4_client *nfs4_get_client(struct in_addr *);
 extern void nfs4_put_client(struct nfs4_client *clp);
 extern int nfs4_init_client(struct nfs4_client *clp);
+extern struct nfs4_client *nfs4_find_client(struct in_addr *);
 extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *);
 
 extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
@@ -729,6 +730,7 @@ struct nfs4_mount_data;
 #define NFSDBG_XDR             0x0020
 #define NFSDBG_FILE            0x0040
 #define NFSDBG_ROOT            0x0080
+#define NFSDBG_CALLBACK                0x0100
 #define NFSDBG_ALL             0xFFFF
 
 #ifdef __KERNEL__
index 7abde582f9db85aaced04d975cfb81f5e1e02cbe..f464260d6fdbc217ef69db529e4722f3367977da 100644 (file)
@@ -87,6 +87,14 @@ static inline u32 svc_getu32(struct kvec *iov)
        iov->iov_len -= sizeof(u32);
        return val;
 }
+
+static inline void svc_ungetu32(struct kvec *iov)
+{
+       u32 *vp = (u32 *)iov->iov_base;
+       iov->iov_base = (void *)(vp - 1);
+       iov->iov_len += sizeof(*vp);
+}
+
 static inline void svc_putu32(struct kvec *iov, u32 val)
 {
        u32 *vp = iov->iov_base + iov->iov_len;
@@ -243,6 +251,8 @@ struct svc_program {
        char *                  pg_name;        /* service name */
        char *                  pg_class;       /* class name: services sharing authentication */
        struct svc_stat *       pg_stats;       /* rpc statistics */
+       /* Override authentication. NULL means use default */
+       int                     (*pg_authenticate)(struct svc_rqst *, u32 *);
 };
 
 /*
index b6ef7772b3aac95647581aab15e9d9fc2e8fa9c7..51f0392ae53154bb76ec344d0e2c0b16af98f7ed 100644 (file)
@@ -89,6 +89,7 @@ EXPORT_SYMBOL(svc_makesock);
 EXPORT_SYMBOL(svc_reserve);
 EXPORT_SYMBOL(svc_auth_register);
 EXPORT_SYMBOL(auth_domain_lookup);
+EXPORT_SYMBOL(svc_authenticate);
 
 /* RPC statistics */
 #ifdef CONFIG_PROC_FS
index db65a24a165887b6b9332124b59767743a70f3aa..8c5b0517db7fe58dbba43e1de5e510c0a1ef2d89 100644 (file)
@@ -263,6 +263,7 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
        u32                     *statp;
        u32                     dir, prog, vers, proc,
                                auth_stat, rpc_stat;
+       int                     auth_res;
 
        rpc_stat = rpc_success;
 
@@ -304,12 +305,17 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
        rqstp->rq_vers = vers = ntohl(svc_getu32(argv));        /* version number */
        rqstp->rq_proc = proc = ntohl(svc_getu32(argv));        /* procedure number */
 
+       progp = serv->sv_program;
        /*
         * Decode auth data, and add verifier to reply buffer.
         * We do this before anything else in order to get a decent
         * auth verifier.
         */
-       switch (svc_authenticate(rqstp, &auth_stat)) {
+       if (progp->pg_authenticate != NULL)
+               auth_res = progp->pg_authenticate(rqstp, &auth_stat);
+       else
+               auth_res = svc_authenticate(rqstp, &auth_stat);
+       switch (auth_res) {
        case SVC_OK:
                break;
        case SVC_GARBAGE:
@@ -326,7 +332,6 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
                goto sendit;
        }
                
-       progp = serv->sv_program;
        if (prog != progp->pg_prog)
                goto err_bad_prog;