fs/nfs/Makefile | 3 fs/nfs/callback.c | 158 ++++++++++++++ fs/nfs/callback.h | 70 ++++++ fs/nfs/callback_proc.c | 107 +++++++++ fs/nfs/callback_xdr.c | 457 +++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4state.c | 12 - fs/nfsd/export.c | 2 fs/nfsd/nfsctl.c | 4 include/linux/nfs_fs.h | 1 include/linux/sunrpc/svcauth.h | 4 net/sunrpc/svcauth_unix.c | 11 11 files changed, 815 insertions(+), 14 deletions(-) diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfs/Makefile linux-2.6.6-09-delegation2/fs/nfs/Makefile --- linux-2.6.6-08-delegation1/fs/nfs/Makefile 2004-04-19 12:29:31.000000000 -0400 +++ linux-2.6.6-09-delegation2/fs/nfs/Makefile 2004-04-19 12:29:36.000000000 -0400 @@ -9,6 +9,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr 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 -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfs/callback.c linux-2.6.6-09-delegation2/fs/nfs/callback.c --- linux-2.6.6-08-delegation1/fs/nfs/callback.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-09-delegation2/fs/nfs/callback.c 2004-04-19 12:29:36.000000000 -0400 @@ -0,0 +1,158 @@ +/* + * linux/fs/nfs/callback.c + * + * Copyright (C) 2004 Trond Myklebust + * + * NFSv4 callback handling + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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); + } + 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; +} + +/* + * 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, +}; diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfs/callback.h linux-2.6.6-09-delegation2/fs/nfs/callback.h --- linux-2.6.6-08-delegation1/fs/nfs/callback.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-09-delegation2/fs/nfs/callback.h 2004-04-19 12:29:36.000000000 -0400 @@ -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 *res); + +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 -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfs/callback_proc.c linux-2.6.6-09-delegation2/fs/nfs/callback_proc.c --- linux-2.6.6-08-delegation1/fs/nfs/callback_proc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-09-delegation2/fs/nfs/callback_proc.c 2004-04-19 12:29:36.000000000 -0400 @@ -0,0 +1,107 @@ +/* + * linux/fs/nfs/callback_proc.c + * + * Copyright (C) 2004 Trond Myklebust + * + * NFSv4 callback procedures + */ +#include +#include +#include +#include + +#include +#include +#include "callback.h" +#include "delegation.h" + +unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res) +{ + struct nfs4_client *clp; + struct inode *inode; + + res->bitmap[0] = res->bitmap[1] = 0; + res->status = htonl(NFS4ERR_BADHANDLE); + clp = nfs4_get_client(&args->addr->sin_addr); + if (clp == NULL) + goto out; + inode = nfs_delegation_find_inode(clp, &args->fh); + if (inode == NULL) + goto out_putclient; + if ((NFS_I(inode)->delegation.type & FMODE_WRITE) != 0) { + res->status = 0; + 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]; + } + iput(inode); +out_putclient: + nfs4_put_client(clp); +out: + return res->status; +} + +struct recall_threadargs { + struct inode *inode; + struct nfs4_client *clp; + + struct completion started; +}; + +static int recall_thread(void *data) +{ + struct recall_threadargs *args = (struct recall_threadargs *)data; + struct inode *inode = args->inode; + struct nfs4_client *clp = args->clp; + + complete(&args->started); + nfs_inode_return_delegation(inode); + iput(inode); + nfs4_put_client(clp); + module_put_and_exit(0); +} + +unsigned nfs4_callback_recall(struct cb_recallargs *args, void *res) +{ + struct nfs4_client *clp; + struct inode *inode; + struct nfs_delegation *delegation; + struct recall_threadargs data; + unsigned status; + + status = htonl(NFS4ERR_BADHANDLE); + clp = nfs4_get_client(&args->addr->sin_addr); + if (clp == NULL) + goto out; + inode = nfs_delegation_find_inode(clp, &args->fh); + if (inode == NULL) + goto out_putclient; + delegation = &NFS_I(inode)->delegation; + if (delegation->type == 0) + goto out_iput; + if (memcmp(delegation->stateid.data, args->stateid.data, sizeof(delegation->stateid.data)) != 0) + goto out_iput; + /* Set up a helper thread to actually return the delegation */ + data.inode = inode; + data.clp = clp; + init_completion(&data.started); + status = htonl(NFS4ERR_RESOURCE); + __module_get(THIS_MODULE); + if (kernel_thread(recall_thread, &data, CLONE_KERNEL | SIGCHLD) < 0) + goto out_module_put; + wait_for_completion(&data.started); + return 0; +out_module_put: + module_put(THIS_MODULE); +out_iput: + iput(inode); +out_putclient: + nfs4_put_client(clp); +out: + return status; +} diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfs/callback_xdr.c linux-2.6.6-09-delegation2/fs/nfs/callback_xdr.c --- linux-2.6.6-08-delegation1/fs/nfs/callback_xdr.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-09-delegation2/fs/nfs/callback_xdr.c 2004-04-19 12:29:36.000000000 -0400 @@ -0,0 +1,457 @@ +/* + * linux/fs/nfs/callback_xdr.c + * + * Copyright (C) 2004 Trond Myklebust + * + * NFSv4 callback encode/decode procedures + */ +#include +#include +#include +#include +#include +#include "callback.h" + +#define CB_OP_HDR_RES_MAXSZ (2) +#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) + +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); + + p = read_buf(xdr, *len); + if (unlikely(p == NULL)) + return htonl(NFS4ERR_RESOURCE); + *str = (const char *)p; + + 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; + int status; + + status = decode_string(xdr, &hdr->taglen, &hdr->tag); + if (unlikely(status != 0)) + return status; + p = read_buf(xdr, 12); + if (unlikely(p == NULL)) + return htonl(NFS4ERR_RESOURCE); + hdr->callback_ident = ntohl(*p++); + 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->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) +{ + int status; + + status = decode_fh(xdr, &args->fh); + if (unlikely(status != 0)) + return status; + args->addr = &rqstp->rq_addr; + status = decode_bitmap(xdr, args->bitmap); + return status; +} + +static unsigned decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_recallargs *args) +{ + uint32_t *p; + int status; + + args->addr = &rqstp->rq_addr; + status = decode_stateid(xdr, &args->stateid); + if (unlikely(status != 0)) + return status; + p = read_buf(xdr, 4); + if (unlikely(p == NULL)) + return htonl(NFS4ERR_RESOURCE); + args->truncate = ntohl(*p); + status = decode_fh(xdr, &args->fh); + 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_ATTR0); + 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) +{ + int 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 = htonl(res); + return 0; +} + +static unsigned encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_getattrres *res) +{ + uint32_t *savep; + int status; + + 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: + return status; +} + +static unsigned encode_recall_res(struct xdr_stream *xdr, const uint32_t *res) +{ + return encode_op_hdr(xdr, OP_CB_RECALL, *res); +} + +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; + int res; + + 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 < op->res_maxsize) { + 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 (res == 0 && status == 0 && op->encode_res != NULL) + res = op->encode_res(rqstp, xdr_out, resp); + return res; +} + +/* + * 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; + unsigned int status; + unsigned int nops = 1; + + xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); + xdr_init_encode(&xdr_out, &rqstp->rq_res, + (uint32_t*)((char *)rqstp->rq_res.head[0].iov_base + + rqstp->rq_res.head[0].iov_len)); + 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_in, &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); + 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, + .encode_res = (callback_encode_res_t)encode_recall_res, + .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_decode = NULL, /* COMPOUNDs don't really fit into */ + .pc_encode = NULL, /* SunRPC's decode+func+encode model */ + .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, +}; + diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfs/nfs4state.c linux-2.6.6-09-delegation2/fs/nfs/nfs4state.c --- linux-2.6.6-08-delegation1/fs/nfs/nfs4state.c 2004-04-19 12:29:31.000000000 -0400 +++ linux-2.6.6-09-delegation2/fs/nfs/nfs4state.c 2004-04-19 12:29:36.000000000 -0400 @@ -45,6 +45,8 @@ #include #include +#include "callback.h" + #define OPENOWNER_POOL_SIZE 8 static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED; @@ -144,9 +146,12 @@ nfs4_get_client(struct in_addr *addr) if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) goto found; } - if (new) + if (new) { list_add(&new->cl_servers, &nfs4_clientid_list); - spin_unlock(&state_spinlock); + spin_unlock(&state_spinlock); + nfs_callback_up(); + } else + spin_unlock(&state_spinlock); return new; found: atomic_inc(&clp->cl_count); @@ -168,6 +173,7 @@ nfs4_put_client(struct nfs4_client *clp) rpc_wake_up(&clp->cl_rpcwaitq); nfs4_kill_renewd(clp); nfs4_free_client(clp); + nfs_callback_down(); } u32 @@ -785,7 +791,7 @@ reclaimer(void *ptr) goto out; } } - status = nfs4_proc_setclientid(clp, 0, 0); + status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport); if (status) goto out_error; status = nfs4_proc_setclientid_confirm(clp); diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfsd/export.c linux-2.6.6-09-delegation2/fs/nfsd/export.c --- linux-2.6.6-08-delegation1/fs/nfsd/export.c 2004-04-19 12:27:37.000000000 -0400 +++ linux-2.6.6-09-delegation2/fs/nfsd/export.c 2004-04-19 12:29:36.000000000 -0400 @@ -1094,7 +1094,7 @@ exp_addclient(struct nfsctl_client *ncp) /* Insert client into hashtable. */ for (i = 0; i < ncp->cl_naddr; i++) - auth_unix_add_addr(ncp->cl_addrlist[i], dom); + auth_unix_add_addr(ncp->cl_addrlist[i], "nfsd", dom); auth_unix_forget_old(dom); auth_domain_put(dom); diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/fs/nfsd/nfsctl.c linux-2.6.6-09-delegation2/fs/nfsd/nfsctl.c --- linux-2.6.6-08-delegation1/fs/nfsd/nfsctl.c 2004-04-19 12:27:31.000000000 -0400 +++ linux-2.6.6-09-delegation2/fs/nfsd/nfsctl.c 2004-04-19 12:29:36.000000000 -0400 @@ -261,7 +261,7 @@ static ssize_t write_getfs(struct file * res = (struct knfsd_fh*)buf; exp_readlock(); - if (!(clp = auth_unix_lookup(sin->sin_addr))) + if (!(clp = auth_unix_lookup(sin->sin_addr, "nfsd"))) err = -EPERM; else { err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); @@ -296,7 +296,7 @@ static ssize_t write_getfd(struct file * res = buf; sin = (struct sockaddr_in *)&data->gd_addr; exp_readlock(); - if (!(clp = auth_unix_lookup(sin->sin_addr))) + if (!(clp = auth_unix_lookup(sin->sin_addr, "nfsd"))) err = -EPERM; else { err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/include/linux/nfs_fs.h linux-2.6.6-09-delegation2/include/linux/nfs_fs.h --- linux-2.6.6-08-delegation1/include/linux/nfs_fs.h 2004-04-19 12:29:31.000000000 -0400 +++ linux-2.6.6-09-delegation2/include/linux/nfs_fs.h 2004-04-19 12:29:36.000000000 -0400 @@ -729,6 +729,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__ diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/include/linux/sunrpc/svcauth.h linux-2.6.6-09-delegation2/include/linux/sunrpc/svcauth.h --- linux-2.6.6-08-delegation1/include/linux/sunrpc/svcauth.h 2004-04-19 12:27:34.000000000 -0400 +++ linux-2.6.6-09-delegation2/include/linux/sunrpc/svcauth.h 2004-04-19 12:29:36.000000000 -0400 @@ -112,10 +112,10 @@ extern void svc_auth_unregister(rpc_auth extern struct auth_domain *unix_domain_find(char *name); extern void auth_domain_put(struct auth_domain *item); -extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom); +extern int auth_unix_add_addr(struct in_addr addr, const char *mclass, struct auth_domain *dom); extern struct auth_domain *auth_domain_lookup(struct auth_domain *item, int set); extern struct auth_domain *auth_domain_find(char *name); -extern struct auth_domain *auth_unix_lookup(struct in_addr addr); +extern struct auth_domain *auth_unix_lookup(struct in_addr addr, char *mclass); extern int auth_unix_forget_old(struct auth_domain *dom); extern void svcauth_unix_purge(void); diff -u --recursive --new-file --show-c-function linux-2.6.6-08-delegation1/net/sunrpc/svcauth_unix.c linux-2.6.6-09-delegation2/net/sunrpc/svcauth_unix.c --- linux-2.6.6-08-delegation1/net/sunrpc/svcauth_unix.c 2004-04-19 12:27:38.000000000 -0400 +++ linux-2.6.6-09-delegation2/net/sunrpc/svcauth_unix.c 2004-04-19 13:26:20.000000000 -0400 @@ -19,7 +19,7 @@ */ -static char *strdup(char *s) +static char *strdup(const char *s) { char *rv = kmalloc(strlen(s)+1, GFP_KERNEL); if (rv) @@ -262,7 +262,8 @@ struct cache_detail ip_map_cache = { static DefineSimpleCacheLookup(ip_map, 0) -int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) +int auth_unix_add_addr(struct in_addr addr, const char *mclass, + struct auth_domain *dom) { struct unix_domain *udom; struct ip_map ip, *ipmp; @@ -270,7 +271,7 @@ int auth_unix_add_addr(struct in_addr ad if (dom->flavour != RPC_AUTH_UNIX) return -EINVAL; udom = container_of(dom, struct unix_domain, h); - ip.m_class = strdup("nfsd"); + ip.m_class = strdup(mclass); if (!ip.m_class) return -ENOMEM; ip.m_addr = addr; @@ -299,12 +300,12 @@ int auth_unix_forget_old(struct auth_dom return 0; } -struct auth_domain *auth_unix_lookup(struct in_addr addr) +struct auth_domain *auth_unix_lookup(struct in_addr addr, char *mclass) { struct ip_map key, *ipm; struct auth_domain *rv; - key.m_class = "nfsd"; + key.m_class = mclass; key.m_addr = addr; ipm = ip_map_lookup(&key, 0);