--- /dev/null
+--- linux-2.6.7/Documentation/filesystems/00-INDEX.lsec 2004-06-15 23:20:26.000000000 -0600
++++ linux-2.6.7/Documentation/filesystems/00-INDEX 2005-03-23 14:28:24.576313528 -0700
+@@ -28,6 +28,8 @@ jfs.txt
+ - info and mount options for the JFS filesystem.
+ ncpfs.txt
+ - info on Novell Netware(tm) filesystem using NCP protocol.
++nfs4.txt
++ - info and mount options for the nfs4 filesystem.
+ ntfs.txt
+ - info and mount options for the NTFS filesystem (Windows NT).
+ proc.txt
+--- linux-2.6.7/Documentation/filesystems/nfs4.txt.lsec 2005-03-23 14:28:24.576313528 -0700
++++ linux-2.6.7/Documentation/filesystems/nfs4.txt 2005-03-23 14:28:24.576313528 -0700
+@@ -0,0 +1,20 @@
++NFS version 4
++=============
++
++NFS version 4 is specified by RFC3530. Compared to earlier NFS versions,
++it provides enhanced security and better client caching, among other features.
++
++In addition to basic file operations, the NFS client supports locking, kerberos
++(basic authentication and integrity), and reboot recovery.
++
++As this writing (July 2004), patches to nfs-utils and util-linux are required
++for NFSv4 support; see http://www.citi.umich.edu/projects/nfsv4/linux/ for
++patches and instructions.
++
++The kernel treats NFS version 4 as a separate filesystem type, nfs4, so it is
++mounted using "mount -tnfs4 server:/path /mntpoint", not by mounting the nfs
++filesystem with -onfsver=4.
++
++Mount options:
++
++XXX?
+--- linux-2.6.7/fs/locks.c.lsec 2004-06-15 23:20:03.000000000 -0600
++++ linux-2.6.7/fs/locks.c 2005-03-23 14:28:22.425640480 -0700
+@@ -317,7 +317,7 @@ static int flock_to_posix_lock(struct fi
+ if (l->l_len == 0)
+ fl->fl_end = OFFSET_MAX;
+
+- fl->fl_owner = current->files;
++ fl->fl_owner = 0;
+ fl->fl_pid = current->tgid;
+ fl->fl_file = filp;
+ fl->fl_flags = FL_POSIX;
+@@ -357,7 +357,7 @@ static int flock64_to_posix_lock(struct
+ if (l->l_len == 0)
+ fl->fl_end = OFFSET_MAX;
+
+- fl->fl_owner = current->files;
++ fl->fl_owner = 0;
+ fl->fl_pid = current->tgid;
+ fl->fl_file = filp;
+ fl->fl_flags = FL_POSIX;
+@@ -920,7 +920,7 @@ int posix_lock_file(struct file *filp, s
+ */
+ int locks_mandatory_locked(struct inode *inode)
+ {
+- fl_owner_t owner = current->files;
++ unsigned int pid = current->tgid;
+ struct file_lock *fl;
+
+ /*
+@@ -930,7 +930,9 @@ int locks_mandatory_locked(struct inode
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (!IS_POSIX(fl))
+ continue;
+- if (fl->fl_owner != owner)
++ if (fl->fl_owner != 0)
++ break;
++ if (fl->fl_pid != pid)
+ break;
+ }
+ unlock_kernel();
+@@ -958,7 +960,7 @@ int locks_mandatory_area(int read_write,
+ int error;
+
+ locks_init_lock(&fl);
+- fl.fl_owner = current->files;
++ fl.fl_owner = 0;
+ fl.fl_pid = current->tgid;
+ fl.fl_file = filp;
+ fl.fl_flags = FL_POSIX | FL_ACCESS;
+@@ -1684,7 +1686,7 @@ void locks_remove_posix(struct file *fil
+ lock_kernel();
+ while (*before != NULL) {
+ struct file_lock *fl = *before;
+- if (IS_POSIX(fl) && (fl->fl_owner == owner)) {
++ if (IS_POSIX(fl) && posix_same_owner(fl, &lock)) {
+ locks_delete_lock(before);
+ continue;
+ }
+@@ -1982,18 +1984,6 @@ int lock_may_write(struct inode *inode,
+
+ EXPORT_SYMBOL(lock_may_write);
+
+-static inline void __steal_locks(struct file *file, fl_owner_t from)
+-{
+- struct inode *inode = file->f_dentry->d_inode;
+- struct file_lock *fl = inode->i_flock;
+-
+- while (fl) {
+- if (fl->fl_file == file && fl->fl_owner == from)
+- fl->fl_owner = current->files;
+- fl = fl->fl_next;
+- }
+-}
+-
+ /* When getting ready for executing a binary, we make sure that current
+ * has a files_struct on its own. Before dropping the old files_struct,
+ * we take over ownership of all locks for all file descriptors we own.
+@@ -2002,31 +1992,6 @@ static inline void __steal_locks(struct
+ */
+ void steal_locks(fl_owner_t from)
+ {
+- struct files_struct *files = current->files;
+- int i, j;
+-
+- if (from == files)
+- return;
+-
+- lock_kernel();
+- j = 0;
+- for (;;) {
+- unsigned long set;
+- i = j * __NFDBITS;
+- if (i >= files->max_fdset || i >= files->max_fds)
+- break;
+- set = files->open_fds->fds_bits[j++];
+- while (set) {
+- if (set & 1) {
+- struct file *file = files->fd[i];
+- if (file)
+- __steal_locks(file, from);
+- }
+- i++;
+- set >>= 1;
+- }
+- }
+- unlock_kernel();
+ }
+ EXPORT_SYMBOL(steal_locks);
+
+--- linux-2.6.7/fs/hostfs/hostfs_kern.c.lsec 2005-03-23 14:25:58.982447160 -0700
++++ linux-2.6.7/fs/hostfs/hostfs_kern.c 2005-03-23 14:33:11.946626600 -0700
+@@ -290,7 +290,6 @@ static void hostfs_delete_inode(struct i
+ {
+ if(HOSTFS_I(inode)->fd != -1) {
+ close_file(&HOSTFS_I(inode)->fd);
+- printk("Closing host fd in .delete_inode\n");
+ HOSTFS_I(inode)->fd = -1;
+ }
+ clear_inode(inode);
+@@ -303,7 +302,6 @@ static void hostfs_destroy_inode(struct
+
+ if(HOSTFS_I(inode)->fd != -1) {
+ close_file(&HOSTFS_I(inode)->fd);
+- printk("Closing host fd in .destroy_inode\n");
+ }
+
+ kfree(HOSTFS_I(inode));
+--- linux-2.6.7/fs/open.c.lsec 2005-03-23 14:26:01.774022776 -0700
++++ linux-2.6.7/fs/open.c 2005-03-23 14:28:23.226518728 -0700
+@@ -1025,7 +1025,7 @@ int filp_close(struct file *filp, fl_own
+ }
+
+ dnotify_flush(filp, id);
+- locks_remove_posix(filp, id);
++ locks_remove_posix(filp, 0);
+ fput(filp);
+ return retval;
+ }
+--- linux-2.6.7/fs/nfsd/export.c.lsec 2004-06-15 23:19:36.000000000 -0600
++++ linux-2.6.7/fs/nfsd/export.c 2005-03-23 14:28:24.686296808 -0700
+@@ -255,7 +255,7 @@ static inline void svc_expkey_update(str
+ new->ek_export = item->ek_export;
+ }
+
+-static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */
++static DefineSimpleCacheLookup(svc_expkey)
+
+ #define EXPORT_HASHBITS 8
+ #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS)
+@@ -487,8 +487,72 @@ static inline void svc_export_update(str
+ new->ex_fsid = item->ex_fsid;
+ }
+
+-static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */
++struct svc_export *
++svc_export_lookup(struct svc_export *item, int set)
++{
++ struct svc_export *tmp, *new = NULL;
++ struct cache_head **hp, **head;
+
++ head = &svc_export_cache.hash_table[svc_export_hash(item)];
++retry:
++ if (set||new)
++ write_lock(&svc_export_cache.hash_lock);
++ else
++ read_lock(&svc_export_cache.hash_lock);
++ for(hp=head; *hp != NULL; hp = &tmp->h.next) {
++ tmp = container_of(*hp, struct svc_export, h);
++ if (svc_export_match(item, tmp)) { /* found a match */
++ cache_get(&tmp->h);
++ if (set) {
++ if (test_bit(CACHE_NEGATIVE, &item->h.flags))
++ set_bit(CACHE_NEGATIVE, &tmp->h.flags);
++ else {
++ clear_bit(CACHE_NEGATIVE, &tmp->h.flags);
++ svc_export_update(tmp, item);
++ }
++ }
++ if (set||new)
++ write_unlock(&svc_export_cache.hash_lock);
++ else
++ read_unlock(&svc_export_cache.hash_lock);
++ if (set)
++ cache_fresh(&svc_export_cache, &tmp->h,
++ item->h.expiry_time);
++ if (new)
++ svc_export_put(&new->h, &svc_export_cache);
++ return tmp;
++ }
++ }
++ /* Didn't find anything */
++ if (new) {
++ svc_export_init(new, item);
++ new->h.next = *head;
++ *head = &new->h;
++ set_bit(CACHE_HASHED, &new->h.flags);
++ svc_export_cache.entries++;
++ if (set) {
++ tmp = new;
++ if (test_bit(CACHE_NEGATIVE, &item->h.flags))
++ set_bit(CACHE_NEGATIVE, &tmp->h.flags);
++ else
++ svc_export_update(tmp, item);
++ }
++ }
++ if (set||new)
++ write_unlock(&svc_export_cache.hash_lock);
++ else
++ read_unlock(&svc_export_cache.hash_lock);
++ if (new && set)
++ cache_fresh(&svc_export_cache, &new->h, item->h.expiry_time);
++ if (new)
++ return new;
++ new = kmalloc(sizeof(*new), GFP_KERNEL);
++ if (new) {
++ cache_init(&new->h);
++ goto retry;
++ }
++ return NULL;
++}
+
+ struct svc_expkey *
+ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
+--- linux-2.6.7/fs/nfsd/nfs4callback.c.lsec 2005-03-23 14:28:24.578313224 -0700
++++ linux-2.6.7/fs/nfsd/nfs4callback.c 2005-03-23 14:28:24.578313224 -0700
+@@ -0,0 +1,631 @@
++/*
++ * linux/fs/nfsd/nfs4callback.c
++ *
++ * Copyright (c) 2001 The Regents of the University of Michigan.
++ * All rights reserved.
++ *
++ * Kendrick Smith <kmsmith@umich.edu>
++ * Andy Adamson <andros@umich.edu>
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. Neither the name of the University nor the names of its
++ * contributors may be used to endorse or promote products derived
++ * from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/inet.h>
++#include <linux/errno.h>
++#include <linux/sunrpc/xdr.h>
++#include <linux/sunrpc/svc.h>
++#include <linux/sunrpc/clnt.h>
++#include <linux/nfsd/nfsd.h>
++#include <linux/nfsd/state.h>
++#include <linux/sunrpc/sched.h>
++#include <linux/nfs4.h>
++
++#define NFSDDBG_FACILITY NFSDDBG_PROC
++
++#define NFSPROC4_CB_NULL 0
++#define NFSPROC4_CB_COMPOUND 1
++
++/* forward declarations */
++static void nfs4_cb_null(struct rpc_task *task);
++
++/* Index of predefined Linux callback client operations */
++
++enum {
++ NFSPROC4_CLNT_CB_NULL = 0,
++ NFSPROC4_CLNT_CB_GETATTR,
++ NFSPROC4_CLNT_CB_RECALL,
++};
++
++enum nfs_cb_opnum4 {
++ OP_CB_GETATTR = 3,
++ OP_CB_RECALL = 4,
++ OP_CB_ILLEGAL = 10044
++};
++
++
++#define NFS4_MAXTAGLEN 20
++
++#define cb_compound_enc_hdr_sz 4
++#define cb_compound_dec_hdr_sz (3 + (NFS4_MAXTAGLEN >> 2))
++#define op_enc_sz 1
++#define op_dec_sz 2
++#define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2))
++#define enc_stateid_sz 16
++
++#define NFS4_enc_cb_getattr_sz (cb_compound_enc_hdr_sz + \
++ op_enc_sz + \
++ enc_nfs4_fh_sz + 4)
++
++#define NFS4_dec_cb_getattr_sz (cb_compound_dec_hdr_sz + \
++ op_dec_sz + \
++ 11)
++
++#define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \
++ 1 + enc_stateid_sz + \
++ enc_nfs4_fh_sz)
++
++#define NFS4_dec_cb_recall_sz (cb_compound_dec_hdr_sz + \
++ op_dec_sz)
++
++/*
++* Generic encode routines from fs/nfs/nfs4xdr.c
++*/
++static inline u32 *
++xdr_writemem(u32 *p, const void *ptr, int nbytes)
++{
++ int tmp = XDR_QUADLEN(nbytes);
++ if (!tmp)
++ return p;
++ p[tmp-1] = 0;
++ memcpy(p, ptr, nbytes);
++ return p + tmp;
++}
++
++#define WRITE32(n) *p++ = htonl(n)
++#define WRITEMEM(ptr,nbytes) do { \
++ p = xdr_writemem(p, ptr, nbytes); \
++} while (0)
++#define RESERVE_SPACE(nbytes) do { \
++ p = xdr_reserve_space(xdr, nbytes); \
++ if (!p) dprintk("NFSD: RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __FUNCTION__); \
++ BUG_ON(!p); \
++} while (0)
++
++/*
++ * Generic decode routines from fs/nfs/nfs4xdr.c
++ */
++#define DECODE_TAIL \
++ status = 0; \
++out: \
++ return status; \
++xdr_error: \
++ dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \
++ status = -EIO; \
++ goto out
++
++#define READ32(x) (x) = ntohl(*p++)
++#define READ64(x) do { \
++ (x) = (u64)ntohl(*p++) << 32; \
++ (x) |= ntohl(*p++); \
++} while (0)
++#define READTIME(x) do { \
++ p++; \
++ (x.tv_sec) = ntohl(*p++); \
++ (x.tv_nsec) = ntohl(*p++); \
++} while (0)
++#define READ_BUF(nbytes) do { \
++ p = xdr_inline_decode(xdr, nbytes); \
++ if (!p) { \
++ dprintk("NFSD: %s: reply buffer overflowed in line %d.", \
++ __FUNCTION__, __LINE__); \
++ return -EIO; \
++ } \
++} while (0)
++
++struct nfs4_cb_compound_hdr {
++ int status;
++ u32 ident;
++ u32 nops;
++ u32 taglen;
++ char * tag;
++};
++
++struct nfs4_cb_getattr {
++ struct nfs_fh fh;
++ u32 bm0;
++ u32 bm1;
++ __u64 change_attr;
++ __u64 size;
++ struct timespec mtime;
++};
++
++struct nfs4_cb_recall {
++ nfs4_stateid stateid;
++ int trunc;
++ struct nfs_fh fh;
++};
++
++static struct {
++ int stat;
++ int errno;
++} nfs_cb_errtbl[] = {
++ { NFS4_OK, 0 },
++ { NFS4ERR_PERM, EPERM },
++ { NFS4ERR_NOENT, ENOENT },
++ { NFS4ERR_IO, EIO },
++ { NFS4ERR_NXIO, ENXIO },
++ { NFS4ERR_ACCESS, EACCES },
++ { NFS4ERR_EXIST, EEXIST },
++ { NFS4ERR_XDEV, EXDEV },
++ { NFS4ERR_NOTDIR, ENOTDIR },
++ { NFS4ERR_ISDIR, EISDIR },
++ { NFS4ERR_INVAL, EINVAL },
++ { NFS4ERR_FBIG, EFBIG },
++ { NFS4ERR_NOSPC, ENOSPC },
++ { NFS4ERR_ROFS, EROFS },
++ { NFS4ERR_MLINK, EMLINK },
++ { NFS4ERR_NAMETOOLONG, ENAMETOOLONG },
++ { NFS4ERR_NOTEMPTY, ENOTEMPTY },
++ { NFS4ERR_DQUOT, EDQUOT },
++ { NFS4ERR_STALE, ESTALE },
++ { NFS4ERR_BADHANDLE, EBADHANDLE },
++ { NFS4ERR_BAD_COOKIE, EBADCOOKIE },
++ { NFS4ERR_NOTSUPP, ENOTSUPP },
++ { NFS4ERR_TOOSMALL, ETOOSMALL },
++ { NFS4ERR_SERVERFAULT, ESERVERFAULT },
++ { NFS4ERR_BADTYPE, EBADTYPE },
++ { NFS4ERR_LOCKED, EAGAIN },
++ { NFS4ERR_RESOURCE, EREMOTEIO },
++ { NFS4ERR_SYMLINK, ELOOP },
++ { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP },
++ { NFS4ERR_DEADLOCK, EDEADLK },
++ { -1, EIO }
++};
++
++static int
++nfs_cb_stat_to_errno(int stat)
++{
++ int i;
++ for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
++ if (nfs_cb_errtbl[i].stat == stat)
++ return nfs_cb_errtbl[i].errno;
++ }
++ /* If we cannot translate the error, the recovery routines should
++ * handle it.
++ * Note: remaining NFSv4 error codes have values > 10000, so should
++ * not conflict with native Linux error codes.
++ */
++ return stat;
++}
++
++/*
++ * XDR encode
++ */
++
++static int
++encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
++{
++ u32 * p;
++
++ RESERVE_SPACE(16);
++ WRITE32(0); /* tag length is always 0 */
++ WRITE32(NFS4_MINOR_VERSION);
++ WRITE32(hdr->ident);
++ WRITE32(hdr->nops);
++ return 0;
++}
++
++static int
++encode_cb_getattr(struct xdr_stream *xdr, struct nfs4_cb_getattr *cb_get)
++{
++ u32 *p;
++ int len = cb_get->fh.size;
++
++ RESERVE_SPACE(20 + len);
++ WRITE32(OP_CB_GETATTR);
++ WRITE32(len);
++ WRITEMEM(cb_get->fh.data, len);
++ WRITE32(2);
++ WRITE32(cb_get->bm0);
++ WRITE32(cb_get->bm1);
++ return 0;
++}
++
++static int
++encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
++{
++ u32 *p;
++ int len = cb_rec->fh.size;
++
++ RESERVE_SPACE(8+sizeof(cb_rec->stateid.data));
++ WRITE32(OP_CB_RECALL);
++ WRITEMEM(cb_rec->stateid.data, sizeof(cb_rec->stateid.data));
++ WRITE32(cb_rec->trunc);
++ WRITE32(len);
++ WRITEMEM(cb_rec->fh.data, len);
++ return 0;
++}
++
++static int
++nfs4_xdr_enc_cb_getattr(struct rpc_rqst *req, u32 *p, struct nfs4_cb_getattr *args)
++{
++ struct xdr_stream xdr;
++ struct nfs4_cb_compound_hdr hdr = {
++ .nops = 1,
++ };
++
++ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++ encode_cb_compound_hdr(&xdr, &hdr);
++ return (encode_cb_getattr(&xdr, args));
++}
++
++static int
++nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, u32 *p, struct nfs4_cb_recall *args)
++{
++ struct xdr_stream xdr;
++ struct nfs4_cb_compound_hdr hdr = {
++ .nops = 1,
++ };
++
++ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++ encode_cb_compound_hdr(&xdr, &hdr);
++ return (encode_cb_recall(&xdr, args));
++}
++
++
++static int
++decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
++ u32 *p;
++
++ READ_BUF(8);
++ READ32(hdr->status);
++ READ32(hdr->taglen);
++ READ_BUF(hdr->taglen + 4);
++ hdr->tag = (char *)p;
++ p += XDR_QUADLEN(hdr->taglen);
++ READ32(hdr->nops);
++ return 0;
++}
++
++static int
++decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
++{
++ u32 *p;
++ u32 op;
++ int32_t nfserr;
++
++ READ_BUF(8);
++ READ32(op);
++ if (op != expected) {
++ dprintk("NFSD: decode_cb_op_hdr: Callback server returned operation"
++ " %d but we issued a request for %d\n",
++ op, expected);
++ return -EIO;
++ }
++ READ32(nfserr);
++ if (nfserr != NFS_OK)
++ return -nfs_cb_stat_to_errno(nfserr);
++ return 0;
++}
++
++static int
++decode_cb_getattr(struct xdr_stream *xdr, struct nfs4_cb_getattr *cb_get)
++{
++ int status;
++ u32 bmlen,
++ attrlen =0,
++ bmval0 =0,
++ bmval1 =0,
++ len = 0;
++ u32 *p;
++
++ status = decode_cb_op_hdr(xdr, OP_CB_GETATTR);
++ if (status)
++ return status;
++ READ_BUF(4);
++ READ32(bmlen);
++ if( (bmlen < 1) || (bmlen > 2))
++ goto xdr_error;
++ READ_BUF((bmlen << 2) + 4);
++ READ32(bmval0);
++ if (bmval0 & ~(FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE))
++ goto out_bad_bitmap;
++ if (bmlen == 2) {
++ READ32(bmval1);
++ if (bmval1 & ~ FATTR4_WORD1_TIME_MODIFY)
++ goto out_bad_bitmap;
++ }
++ READ32(attrlen);
++ if (bmval0 & FATTR4_WORD0_CHANGE) {
++ READ_BUF(8);
++ len += 8;
++ READ64(cb_get->change_attr);
++ dprintk("decode_cb_getattr: changeid=%Ld\n",
++ (long long)cb_get->change_attr);
++ }
++ if (bmval0 & FATTR4_WORD0_SIZE) {
++ READ_BUF(8);
++ len += 8;
++ READ64(cb_get->size);
++ dprintk("decode_cb_getattr: size=%Ld\n",
++ (long long)cb_get->size);
++ }
++ if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
++ READ_BUF(12);
++ len += 12;
++ READTIME(cb_get->mtime);
++ dprintk("decode_cb_gatattr: mtime=%ld\n",
++ (long)cb_get->mtime.tv_sec);
++ }
++ if (len != attrlen)
++ goto xdr_error;
++
++ DECODE_TAIL;
++
++out_bad_bitmap:
++ dprintk("NFSD: %s Callback server returned bad attribute bitmap\n",
++ __FUNCTION__);
++ return -EIO;
++
++}
++
++static int
++nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp, u32 *p, struct nfs4_cb_getattr *res)
++{
++ struct xdr_stream xdr;
++ struct nfs4_cb_compound_hdr hdr;
++ int status;
++
++ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
++ status = decode_cb_compound_hdr(&xdr, &hdr);
++ if (status)
++ goto out;
++ status = decode_cb_getattr(&xdr, res);
++out:
++ return status;
++}
++
++static int
++nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, u32 *p)
++{
++ struct xdr_stream xdr;
++ struct nfs4_cb_compound_hdr hdr;
++ int status;
++
++ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
++ status = decode_cb_compound_hdr(&xdr, &hdr);
++ if (status)
++ goto out;
++ status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
++out:
++ return status;
++}
++
++static int
++nfs4_xdr_enc_null(struct rpc_rqst *req, u32 *p)
++{
++ struct xdr_stream xdrs, *xdr = &xdrs;
++
++ xdr_init_encode(&xdrs, &req->rq_snd_buf, p);
++ RESERVE_SPACE(0);
++ return 0;
++}
++
++static int
++nfs4_xdr_dec_null(struct rpc_rqst *req, u32 *p)
++{
++ return 0;
++}
++
++/*
++ * RPC procedure tables
++ */
++#ifndef MAX
++# define MAX(a, b) (((a) > (b))? (a) : (b))
++#endif
++
++#define PROC(proc, argtype, restype) \
++[NFSPROC4_CLNT_##proc] = { \
++ .p_proc = NFSPROC4_CB_COMPOUND, \
++ .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
++ .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
++ .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \
++}
++
++struct rpc_procinfo nfs4_cb_procedures[] = {
++ PROC(CB_GETATTR, enc_cb_getattr, dec_cb_getattr),
++ PROC(CB_RECALL, enc_cb_recall, dec_cb_recall),
++};
++
++struct rpc_version nfs_cb_version4 = {
++ .number = 1,
++ .nrprocs = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]),
++ .procs = nfs4_cb_procedures
++};
++
++static struct rpc_version * nfs_cb_version[] = {
++ NULL,
++ &nfs_cb_version4,
++};
++
++struct rpc_procinfo nfs4_cb_null_proc= {
++ .p_proc = NFSPROC4_CB_NULL,
++ .p_encode = (kxdrproc_t)nfs4_xdr_enc_null,
++ .p_decode = (kxdrproc_t) nfs4_xdr_dec_null,
++ .p_bufsiz = 0,
++};
++
++/*
++ * Use the SETCLIENTID credential
++ */
++struct rpc_cred *
++nfsd4_lookupcred(struct nfs4_client *clp, int taskflags)
++{
++ struct auth_cred acred;
++ struct rpc_clnt *clnt = clp->cl_callback.cb_client;
++ struct rpc_cred *ret = NULL;
++
++ if (!clnt)
++ goto out;
++ get_group_info(clp->cl_cred.cr_group_info);
++ acred.uid = clp->cl_cred.cr_uid;
++ acred.gid = clp->cl_cred.cr_gid;
++ acred.group_info = clp->cl_cred.cr_group_info;
++
++ dprintk("NFSD: looking up %s cred\n",
++ clnt->cl_auth->au_ops->au_name);
++ ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags);
++ put_group_info(clp->cl_cred.cr_group_info);
++out:
++ return ret;
++}
++
++/*
++ * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
++ */
++void
++nfsd4_probe_callback(struct nfs4_client *clp)
++{
++ struct sockaddr_in addr;
++ struct nfs4_callback *cb = &clp->cl_callback;
++ struct rpc_timeout timeparms;
++ struct rpc_xprt * xprt;
++ struct rpc_program * program = &cb->cb_program;
++ struct rpc_stat * stat = &cb->cb_stat;
++ struct rpc_clnt * clnt;
++ struct rpc_message msg = {
++ .rpc_proc = &nfs4_cb_null_proc,
++ .rpc_argp = clp,
++ };
++ char hostname[32];
++ int status;
++
++ dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d 1\n",
++ cb->cb_parsed, cb->cb_set);
++ if (!cb->cb_parsed || cb->cb_set)
++ goto out_err;
++
++ /* Currently, we only support tcp for the callback channel */
++ if (cb->cb_netid.len !=3 || memcmp((char *)cb->cb_netid.data, "tcp", 3))
++ goto out_err;
++
++ /* Initialize address */
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_port = htons(cb->cb_port);
++ addr.sin_addr.s_addr = htonl(cb->cb_addr);
++
++ /* Initialize timeout */
++ timeparms.to_initval = HZ;
++ timeparms.to_retries = 5;
++ timeparms.to_maxval = NFSD_LEASE_TIME*HZ;
++ timeparms.to_exponential = 1;
++
++ /* Create RPC transport */
++ if (!(xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms))) {
++ dprintk("NFSD: couldn't create callback transport!\n");
++ goto out_err;
++ }
++
++ /* Initialize rpc_program */
++ program->name = "nfs4_cb";
++ program->number = cb->cb_prog;
++ program->nrvers = sizeof(nfs_cb_version)/sizeof(nfs_cb_version[0]);
++ program->version = nfs_cb_version;
++ program->stats = stat;
++
++ /* Initialize rpc_stat */
++ memset(stat, 0, sizeof(struct rpc_stat));
++ stat->program = program;
++
++ /* Create RPC client
++ *
++ * XXX AUTH_UNIX only - need AUTH_GSS....
++ */
++ sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr));
++ if (!(clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX))) {
++ dprintk("NFSD: couldn't create callback client\n");
++ goto out_xprt;
++ }
++ clnt->cl_intr = 1;
++ clnt->cl_softrtry = 1;
++ clnt->cl_chatty = 1;
++ cb->cb_client = clnt;
++
++ /* Kick rpciod, put the call on the wire. */
++
++ if (rpciod_up() != 0) {
++ dprintk("nfsd: couldn't start rpciod for callbacks!\n");
++ goto out_clnt;
++ }
++
++ /* the task holds a reference to the nfs4_client struct */
++ atomic_inc(&clp->cl_count);
++
++ msg.rpc_cred = nfsd4_lookupcred(clp,0);
++ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, nfs4_cb_null, 0);
++
++ if (status != 0) {
++ dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n");
++ goto out_rpciod;
++ }
++ return;
++
++out_rpciod:
++ rpciod_down();
++out_clnt:
++ rpc_shutdown_client(clnt);
++ goto out_err;
++out_xprt:
++ xprt_destroy(xprt);
++out_err:
++ dprintk("NFSD: warning: no callback path to client %.*s\n",
++ clp->cl_name.len, clp->cl_name.data);
++ cb->cb_client = NULL;
++}
++
++static void
++nfs4_cb_null(struct rpc_task *task)
++{
++ struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
++ struct nfs4_callback *cb = &clp->cl_callback;
++ u32 addr = htonl(cb->cb_addr);
++
++ dprintk("NFSD: nfs4_cb_null task->tk_status %d\n", task->tk_status);
++
++ if (task->tk_status < 0) {
++ dprintk("NFSD: callback establishment to client %.*s failed\n",
++ clp->cl_name.len, clp->cl_name.data);
++ goto out;
++ }
++ cb->cb_set = 1;
++ dprintk("NFSD: callback set to client %u.%u.%u.%u\n", NIPQUAD(addr));
++out:
++ put_nfs4_client(clp);
++}
+--- linux-2.6.7/fs/nfsd/nfs4xdr.c.lsec 2004-06-15 23:19:52.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4xdr.c 2005-03-23 14:28:23.924412632 -0700
+@@ -55,6 +55,8 @@
+ #include <linux/nfsd/state.h>
+ #include <linux/nfsd/xdr4.h>
+ #include <linux/nfsd_idmap.h>
++#include <linux/nfs4.h>
++#include <linux/nfs4_acl.h>
+
+ #define NFSDDBG_FACILITY NFSDDBG_XDR
+
+@@ -287,27 +289,40 @@ u32 *read_buf(struct nfsd4_compoundargs
+ return p;
+ }
+
+-char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
++static int
++defer_free(struct nfsd4_compoundargs *argp,
++ void (*release)(const void *), void *p)
+ {
+ struct tmpbuf *tb;
++
++ tb = kmalloc(sizeof(*tb), GFP_KERNEL);
++ if (!tb)
++ return -ENOMEM;
++ tb->buf = p;
++ tb->release = release;
++ tb->next = argp->to_free;
++ argp->to_free = tb;
++ return 0;
++}
++
++char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
++{
++ void *new = NULL;
+ if (p == argp->tmp) {
+- p = kmalloc(nbytes, GFP_KERNEL);
+- if (!p) return NULL;
++ new = kmalloc(nbytes, GFP_KERNEL);
++ if (!new) return NULL;
++ p = new;
+ memcpy(p, argp->tmp, nbytes);
+ } else {
+ if (p != argp->tmpp)
+ BUG();
+ argp->tmpp = NULL;
+ }
+- tb = kmalloc(sizeof(*tb), GFP_KERNEL);
+- if (!tb) {
+- kfree(p);
++ if (defer_free(argp, kfree, p)) {
++ kfree(new);
+ return NULL;
+- }
+- tb->buf = p;
+- tb->next = argp->to_free;
+- argp->to_free = tb;
+- return (char*)p;
++ } else
++ return (char *)p;
+ }
+
+
+@@ -335,7 +350,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoun
+ }
+
+ static int
+-nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr)
++nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,
++ struct nfs4_acl **acl)
+ {
+ int expected_len, len = 0;
+ u32 dummy32;
+@@ -364,6 +380,51 @@ nfsd4_decode_fattr(struct nfsd4_compound
+ READ64(iattr->ia_size);
+ iattr->ia_valid |= ATTR_SIZE;
+ }
++ if (bmval[0] & FATTR4_WORD0_ACL) {
++ int nace, i;
++ struct nfs4_ace ace;
++
++ READ_BUF(4); len += 4;
++ READ32(nace);
++
++ *acl = nfs4_acl_new();
++ if (*acl == NULL) {
++ status = -ENOMEM;
++ goto out_nfserr;
++ }
++ defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);
++
++ for (i = 0; i < nace; i++) {
++ READ_BUF(16); len += 16;
++ READ32(ace.type);
++ READ32(ace.flag);
++ READ32(ace.access_mask);
++ READ32(dummy32);
++ READ_BUF(dummy32);
++ len += XDR_QUADLEN(dummy32) << 2;
++ READMEM(buf, dummy32);
++ if (check_utf8(buf, dummy32))
++ return nfserr_inval;
++ ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
++ status = 0;
++ if (ace.whotype != NFS4_ACL_WHO_NAMED)
++ ace.who = 0;
++ else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP)
++ status = nfsd_map_name_to_gid(argp->rqstp,
++ buf, dummy32, &ace.who);
++ else
++ status = nfsd_map_name_to_uid(argp->rqstp,
++ buf, dummy32, &ace.who);
++ if (status)
++ goto out_nfserr;
++ if (nfs4_acl_add_ace(*acl, ace.type, ace.flag,
++ ace.access_mask, ace.whotype, ace.who) != 0) {
++ status = -ENOMEM;
++ goto out_nfserr;
++ }
++ }
++ } else
++ *acl = NULL;
+ if (bmval[1] & FATTR4_WORD1_MODE) {
+ READ_BUF(4);
+ len += 4;
+@@ -549,7 +610,7 @@ nfsd4_decode_create(struct nfsd4_compoun
+ if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
+ return status;
+
+- if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr)))
++ if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl)))
+ goto out;
+
+ DECODE_TAIL;
+@@ -698,7 +759,7 @@ nfsd4_decode_open(struct nfsd4_compounda
+ switch (open->op_createmode) {
+ case NFS4_CREATE_UNCHECKED:
+ case NFS4_CREATE_GUARDED:
+- if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr)))
++ if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl)))
+ goto out;
+ break;
+ case NFS4_CREATE_EXCLUSIVE:
+@@ -875,7 +936,7 @@ nfsd4_decode_setattr(struct nfsd4_compou
+ READ_BUF(sizeof(stateid_t));
+ READ32(setattr->sa_stateid.si_generation);
+ COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t));
+- if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr)))
++ if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl)))
+ goto out;
+
+ DECODE_TAIL;
+@@ -1288,32 +1349,24 @@ static u32 nfs4_ftypes[16] = {
+ NF4SOCK, NF4BAD, NF4LNK, NF4BAD,
+ };
+
+-static inline int
+-xdr_padding(int l)
+-{
+- return 3 - ((l - 1) & 3); /* smallest i>=0 such that (l+i)%4 = 0 */
+-}
+-
+ static int
+-nfsd4_encode_name(struct svc_rqst *rqstp, int group, uid_t id,
++nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
+ u32 **p, int *buflen)
+ {
+ int status;
+- u32 len;
+
+ if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
+ return nfserr_resource;
+- if (group)
++ if (whotype != NFS4_ACL_WHO_NAMED)
++ status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
++ else if (group)
+ status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
+ else
+ status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
+ if (status < 0)
+ return nfserrno(status);
+- len = (unsigned)status;
+- *(*p)++ = htonl(len);
+- memset((u8 *)*p + len, 0, xdr_padding(len));
+- *p += XDR_QUADLEN(len);
+- *buflen -= (XDR_QUADLEN(len) << 2) + 4;
++ *p = xdr_encode_opaque(*p, NULL, status);
++ *buflen -= (XDR_QUADLEN(status) << 2) + 4;
+ BUG_ON(*buflen < 0);
+ return 0;
+ }
+@@ -1321,13 +1374,20 @@ nfsd4_encode_name(struct svc_rqst *rqstp
+ static inline int
+ nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen)
+ {
+- return nfsd4_encode_name(rqstp, uid, 0, p, buflen);
++ return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
+ }
+
+ static inline int
+ nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen)
+ {
+- return nfsd4_encode_name(rqstp, gid, 1, p, buflen);
++ return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
++}
++
++static inline int
++nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
++ u32 **p, int *buflen)
++{
++ return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
+ }
+
+
+@@ -1354,6 +1414,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+ u64 dummy64;
+ u32 *p = buffer;
+ int status;
++ int aclsupport = 0;
++ struct nfs4_acl *acl = NULL;
+
+ BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
+ BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
+@@ -1376,6 +1438,17 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+ goto out;
+ fhp = &tempfh;
+ }
++ if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
++ | FATTR4_WORD0_SUPPORTED_ATTRS)) {
++ status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
++ aclsupport = (status == 0);
++ if (bmval0 & FATTR4_WORD0_ACL) {
++ if (status == -EOPNOTSUPP)
++ bmval0 &= ~FATTR4_WORD0_ACL;
++ else if (status != 0)
++ goto out_nfserr;
++ }
++ }
+ if ((buflen -= 16) < 0)
+ goto out_resource;
+
+@@ -1388,7 +1461,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+ if ((buflen -= 12) < 0)
+ goto out_resource;
+ WRITE32(2);
+- WRITE32(NFSD_SUPPORTED_ATTRS_WORD0);
++ WRITE32(aclsupport ?
++ NFSD_SUPPORTED_ATTRS_WORD0 :
++ NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
+ WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
+ }
+ if (bmval0 & FATTR4_WORD0_TYPE) {
+@@ -1459,10 +1534,44 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+ goto out_resource;
+ WRITE32(0);
+ }
++ if (bmval0 & FATTR4_WORD0_ACL) {
++ struct nfs4_ace *ace;
++ struct list_head *h;
++
++ if (acl == NULL) {
++ if ((buflen -= 4) < 0)
++ goto out_resource;
++
++ WRITE32(0);
++ goto out_acl;
++ }
++ if ((buflen -= 4) < 0)
++ goto out_resource;
++ WRITE32(acl->naces);
++
++ list_for_each(h, &acl->ace_head) {
++ ace = list_entry(h, struct nfs4_ace, l_ace);
++
++ if ((buflen -= 4*3) < 0)
++ goto out_resource;
++ WRITE32(ace->type);
++ WRITE32(ace->flag);
++ WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
++ status = nfsd4_encode_aclname(rqstp, ace->whotype,
++ ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
++ &p, &buflen);
++ if (status == nfserr_resource)
++ goto out_resource;
++ if (status)
++ goto out;
++ }
++ }
++out_acl:
+ if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
+ if ((buflen -= 4) < 0)
+ goto out_resource;
+- WRITE32(0);
++ WRITE32(aclsupport ?
++ ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
+ }
+ if (bmval0 & FATTR4_WORD0_CANSETTIME) {
+ if ((buflen -= 4) < 0)
+@@ -1645,6 +1754,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+ status = nfs_ok;
+
+ out:
++ nfs4_acl_free(acl);
+ if (fhp == &tempfh)
+ fh_put(&tempfh);
+ return status;
+@@ -2471,6 +2581,24 @@ nfs4svc_encode_voidres(struct svc_rqst *
+ return xdr_ressize_check(rqstp, p);
+ }
+
++void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
++{
++ if (args->ops != args->iops) {
++ kfree(args->ops);
++ args->ops = args->iops;
++ }
++ if (args->tmpp) {
++ kfree(args->tmpp);
++ args->tmpp = NULL;
++ }
++ while (args->to_free) {
++ struct tmpbuf *tb = args->to_free;
++ args->to_free = tb->next;
++ tb->release(tb->buf);
++ kfree(tb);
++ }
++}
++
+ int
+ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args)
+ {
+@@ -2487,20 +2615,7 @@ nfs4svc_decode_compoundargs(struct svc_r
+
+ status = nfsd4_decode_compound(args);
+ if (status) {
+- if (args->ops != args->iops) {
+- kfree(args->ops);
+- args->ops = args->iops;
+- }
+- if (args->tmpp) {
+- kfree(args->tmpp);
+- args->tmpp = NULL;
+- }
+- while (args->to_free) {
+- struct tmpbuf *tb = args->to_free;
+- args->to_free = tb->next;
+- kfree(tb->buf);
+- kfree(tb);
+- }
++ nfsd4_release_compoundargs(args);
+ }
+ return !status;
+ }
+--- linux-2.6.7/fs/nfsd/nfs4proc.c.lsec 2004-06-15 23:20:26.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4proc.c 2005-03-23 14:28:24.080388920 -0700
+@@ -52,6 +52,7 @@
+ #include <linux/nfs4.h>
+ #include <linux/nfsd/state.h>
+ #include <linux/nfsd/xdr4.h>
++#include <linux/nfs4_acl.h>
+
+ #define NFSDDBG_FACILITY NFSDDBG_PROC
+
+@@ -135,9 +136,11 @@ do_open_fhandle(struct svc_rqst *rqstp,
+ {
+ int status;
+
+- dprintk("NFSD: do_open_fhandle\n");
++ /* Only reclaims from previously confirmed clients are valid */
++ if ((status = nfs4_check_open_reclaim(&open->op_clientid)))
++ return status;
+
+- /* we don't know the target directory, and therefore can not
++ /* We don't know the target directory, and therefore can not
+ * set the change info
+ */
+
+@@ -172,8 +175,7 @@ nfsd4_open(struct svc_rqst *rqstp, struc
+ if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+ return nfserr_grace;
+
+- if (nfs4_in_no_grace() &&
+- open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
++ if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+ return nfserr_no_grace;
+
+ /* This check required by spec. */
+@@ -318,7 +320,7 @@ nfsd4_commit(struct svc_rqst *rqstp, str
+ return status;
+ }
+
+-static inline int
++static int
+ nfsd4_create(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_create *create)
+ {
+ struct svc_fh resfh;
+@@ -435,7 +437,7 @@ nfsd4_link(struct svc_rqst *rqstp, struc
+ return status;
+ }
+
+-static inline int
++static int
+ nfsd4_lookupp(struct svc_rqst *rqstp, struct svc_fh *current_fh)
+ {
+ struct svc_fh tmp_fh;
+@@ -619,7 +621,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, st
+ status = nfserr_bad_stateid;
+ if (ZERO_STATEID(&setattr->sa_stateid) || ONE_STATEID(&setattr->sa_stateid)) {
+ dprintk("NFSD: nfsd4_setattr: magic stateid!\n");
+- return status;
++ goto out;
+ }
+
+ nfs4_lock_state();
+@@ -627,17 +629,25 @@ nfsd4_setattr(struct svc_rqst *rqstp, st
+ &setattr->sa_stateid,
+ CHECK_FH | RDWR_STATE, &stp))) {
+ dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
+- goto out;
++ goto out_unlock;
+ }
+ status = nfserr_openmode;
+ if (!access_bits_permit_write(stp->st_access_bmap)) {
+ dprintk("NFSD: nfsd4_setattr: not opened for write!\n");
+- goto out;
++ goto out_unlock;
+ }
+ nfs4_unlock_state();
+ }
+- return (nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr, 0, (time_t)0));
++ status = nfs_ok;
++ if (setattr->sa_acl != NULL)
++ status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl);
++ if (status)
++ goto out;
++ status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr,
++ 0, (time_t)0);
+ out:
++ return status;
++out_unlock:
+ nfs4_unlock_state();
+ return status;
+ }
+@@ -773,13 +783,20 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+ struct nfsd4_compoundres *resp)
+ {
+ struct nfsd4_op *op;
+- struct svc_fh current_fh;
+- struct svc_fh save_fh;
++ struct svc_fh *current_fh = NULL;
++ struct svc_fh *save_fh = NULL;
+ int slack_space; /* in words, not bytes! */
+ int status;
+
+- fh_init(¤t_fh, NFS4_FHSIZE);
+- fh_init(&save_fh, NFS4_FHSIZE);
++ status = nfserr_resource;
++ current_fh = kmalloc(sizeof(*current_fh), GFP_KERNEL);
++ if (current_fh == NULL)
++ goto out;
++ fh_init(current_fh, NFS4_FHSIZE);
++ save_fh = kmalloc(sizeof(*save_fh), GFP_KERNEL);
++ if (save_fh == NULL)
++ goto out;
++ fh_init(save_fh, NFS4_FHSIZE);
+
+ resp->xbuf = &rqstp->rq_res;
+ resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len;
+@@ -831,7 +848,7 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+ * SETATTR NOFILEHANDLE error handled in nfsd4_setattr
+ * due to required returned bitmap argument
+ */
+- if ((!current_fh.fh_dentry) &&
++ if ((!current_fh->fh_dentry) &&
+ !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) ||
+ (op->opnum == OP_SETCLIENTID) ||
+ (op->opnum == OP_SETCLIENTID_CONFIRM) ||
+@@ -843,105 +860,105 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+ }
+ switch (op->opnum) {
+ case OP_ACCESS:
+- op->status = nfsd4_access(rqstp, ¤t_fh, &op->u.access);
++ op->status = nfsd4_access(rqstp, current_fh, &op->u.access);
+ break;
+ case OP_CLOSE:
+- op->status = nfsd4_close(rqstp, ¤t_fh, &op->u.close);
++ op->status = nfsd4_close(rqstp, current_fh, &op->u.close);
+ if (op->u.close.cl_stateowner)
+ op->replay =
+ &op->u.close.cl_stateowner->so_replay;
+ break;
+ case OP_COMMIT:
+- op->status = nfsd4_commit(rqstp, ¤t_fh, &op->u.commit);
++ op->status = nfsd4_commit(rqstp, current_fh, &op->u.commit);
+ break;
+ case OP_CREATE:
+- op->status = nfsd4_create(rqstp, ¤t_fh, &op->u.create);
++ op->status = nfsd4_create(rqstp, current_fh, &op->u.create);
+ break;
+ case OP_GETATTR:
+- op->status = nfsd4_getattr(rqstp, ¤t_fh, &op->u.getattr);
++ op->status = nfsd4_getattr(rqstp, current_fh, &op->u.getattr);
+ break;
+ case OP_GETFH:
+- op->status = nfsd4_getfh(¤t_fh, &op->u.getfh);
++ op->status = nfsd4_getfh(current_fh, &op->u.getfh);
+ break;
+ case OP_LINK:
+- op->status = nfsd4_link(rqstp, ¤t_fh, &save_fh, &op->u.link);
++ op->status = nfsd4_link(rqstp, current_fh, save_fh, &op->u.link);
+ break;
+ case OP_LOCK:
+- op->status = nfsd4_lock(rqstp, ¤t_fh, &op->u.lock);
++ op->status = nfsd4_lock(rqstp, current_fh, &op->u.lock);
+ if (op->u.lock.lk_stateowner)
+ op->replay =
+ &op->u.lock.lk_stateowner->so_replay;
+ break;
+ case OP_LOCKT:
+- op->status = nfsd4_lockt(rqstp, ¤t_fh, &op->u.lockt);
++ op->status = nfsd4_lockt(rqstp, current_fh, &op->u.lockt);
+ break;
+ case OP_LOCKU:
+- op->status = nfsd4_locku(rqstp, ¤t_fh, &op->u.locku);
++ op->status = nfsd4_locku(rqstp, current_fh, &op->u.locku);
+ if (op->u.locku.lu_stateowner)
+ op->replay =
+ &op->u.locku.lu_stateowner->so_replay;
+ break;
+ case OP_LOOKUP:
+- op->status = nfsd4_lookup(rqstp, ¤t_fh, &op->u.lookup);
++ op->status = nfsd4_lookup(rqstp, current_fh, &op->u.lookup);
+ break;
+ case OP_LOOKUPP:
+- op->status = nfsd4_lookupp(rqstp, ¤t_fh);
++ op->status = nfsd4_lookupp(rqstp, current_fh);
+ break;
+ case OP_NVERIFY:
+- op->status = nfsd4_verify(rqstp, ¤t_fh, &op->u.nverify);
++ op->status = nfsd4_verify(rqstp, current_fh, &op->u.nverify);
+ if (op->status == nfserr_not_same)
+ op->status = nfs_ok;
+ break;
+ case OP_OPEN:
+- op->status = nfsd4_open(rqstp, ¤t_fh, &op->u.open);
++ op->status = nfsd4_open(rqstp, current_fh, &op->u.open);
+ if (op->u.open.op_stateowner)
+ op->replay =
+ &op->u.open.op_stateowner->so_replay;
+ break;
+ case OP_OPEN_CONFIRM:
+- op->status = nfsd4_open_confirm(rqstp, ¤t_fh, &op->u.open_confirm);
++ op->status = nfsd4_open_confirm(rqstp, current_fh, &op->u.open_confirm);
+ if (op->u.open_confirm.oc_stateowner)
+ op->replay =
+ &op->u.open_confirm.oc_stateowner->so_replay;
+ break;
+ case OP_OPEN_DOWNGRADE:
+- op->status = nfsd4_open_downgrade(rqstp, ¤t_fh, &op->u.open_downgrade);
++ op->status = nfsd4_open_downgrade(rqstp, current_fh, &op->u.open_downgrade);
+ if (op->u.open_downgrade.od_stateowner)
+ op->replay =
+ &op->u.open_downgrade.od_stateowner->so_replay;
+ break;
+ case OP_PUTFH:
+- op->status = nfsd4_putfh(rqstp, ¤t_fh, &op->u.putfh);
++ op->status = nfsd4_putfh(rqstp, current_fh, &op->u.putfh);
+ break;
+ case OP_PUTROOTFH:
+- op->status = nfsd4_putrootfh(rqstp, ¤t_fh);
++ op->status = nfsd4_putrootfh(rqstp, current_fh);
+ break;
+ case OP_READ:
+- op->status = nfsd4_read(rqstp, ¤t_fh, &op->u.read);
++ op->status = nfsd4_read(rqstp, current_fh, &op->u.read);
+ break;
+ case OP_READDIR:
+- op->status = nfsd4_readdir(rqstp, ¤t_fh, &op->u.readdir);
++ op->status = nfsd4_readdir(rqstp, current_fh, &op->u.readdir);
+ break;
+ case OP_READLINK:
+- op->status = nfsd4_readlink(rqstp, ¤t_fh, &op->u.readlink);
++ op->status = nfsd4_readlink(rqstp, current_fh, &op->u.readlink);
+ break;
+ case OP_REMOVE:
+- op->status = nfsd4_remove(rqstp, ¤t_fh, &op->u.remove);
++ op->status = nfsd4_remove(rqstp, current_fh, &op->u.remove);
+ break;
+ case OP_RENAME:
+- op->status = nfsd4_rename(rqstp, ¤t_fh, &save_fh, &op->u.rename);
++ op->status = nfsd4_rename(rqstp, current_fh, save_fh, &op->u.rename);
+ break;
+ case OP_RENEW:
+ op->status = nfsd4_renew(&op->u.renew);
+ break;
+ case OP_RESTOREFH:
+- op->status = nfsd4_restorefh(¤t_fh, &save_fh);
++ op->status = nfsd4_restorefh(current_fh, save_fh);
+ break;
+ case OP_SAVEFH:
+- op->status = nfsd4_savefh(¤t_fh, &save_fh);
++ op->status = nfsd4_savefh(current_fh, save_fh);
+ break;
+ case OP_SETATTR:
+- op->status = nfsd4_setattr(rqstp, ¤t_fh, &op->u.setattr);
++ op->status = nfsd4_setattr(rqstp, current_fh, &op->u.setattr);
+ break;
+ case OP_SETCLIENTID:
+ op->status = nfsd4_setclientid(rqstp, &op->u.setclientid);
+@@ -950,12 +967,12 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+ op->status = nfsd4_setclientid_confirm(rqstp, &op->u.setclientid_confirm);
+ break;
+ case OP_VERIFY:
+- op->status = nfsd4_verify(rqstp, ¤t_fh, &op->u.verify);
++ op->status = nfsd4_verify(rqstp, current_fh, &op->u.verify);
+ if (op->status == nfserr_same)
+ op->status = nfs_ok;
+ break;
+ case OP_WRITE:
+- op->status = nfsd4_write(rqstp, ¤t_fh, &op->u.write);
++ op->status = nfsd4_write(rqstp, current_fh, &op->u.write);
+ break;
+ case OP_RELEASE_LOCKOWNER:
+ op->status = nfsd4_release_lockowner(rqstp, &op->u.release_lockowner);
+@@ -976,22 +993,13 @@ encode_op:
+ }
+
+ out:
+- if (args->ops != args->iops) {
+- kfree(args->ops);
+- args->ops = args->iops;
+- }
+- if (args->tmpp) {
+- kfree(args->tmpp);
+- args->tmpp = NULL;
+- }
+- while (args->to_free) {
+- struct tmpbuf *tb = args->to_free;
+- args->to_free = tb->next;
+- kfree(tb->buf);
+- kfree(tb);
+- }
+- fh_put(¤t_fh);
+- fh_put(&save_fh);
++ nfsd4_release_compoundargs(args);
++ if (current_fh)
++ fh_put(current_fh);
++ kfree(current_fh);
++ if (save_fh)
++ fh_put(save_fh);
++ kfree(save_fh);
+ return status;
+ }
+
+--- linux-2.6.7/fs/nfsd/nfs4state.c.lsec 2004-06-15 23:19:43.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4state.c 2005-03-23 14:28:24.028396824 -0700
+@@ -51,6 +51,9 @@
+ #define NFSDDBG_FACILITY NFSDDBG_PROC
+
+ /* Globals */
++static time_t lease_time = 90; /* default lease time */
++static time_t old_lease_time = 90; /* past incarnation lease time */
++static u32 nfs4_reclaim_init = 0;
+ time_t boot_time;
+ static time_t grace_end = 0;
+ static u32 current_clientid = 1;
+@@ -82,7 +85,7 @@ struct nfs4_stateid * find_stateid(state
+ * protects clientid_hashtbl[], clientstr_hashtbl[],
+ * unconfstr_hashtbl[], uncofid_hashtbl[].
+ */
+-static struct semaphore client_sema;
++static DECLARE_MUTEX(client_sema);
+
+ void
+ nfs4_lock_state(void)
+@@ -131,8 +134,11 @@ static void release_file(struct nfs4_fil
+ ((id) & CLIENT_HASH_MASK)
+ #define clientstr_hashval(name, namelen) \
+ (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK)
+-
+-/* conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed
++/*
++ * reclaim_str_hashtbl[] holds known client info from previous reset/reboot
++ * used in reboot/reset lease grace period processing
++ *
++ * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed
+ * setclientid_confirmed info.
+ *
+ * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed
+@@ -144,6 +150,8 @@ static void release_file(struct nfs4_fil
+ * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
+ * for last close replay.
+ */
++static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE];
++static int reclaim_str_hashtbl_size;
+ static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE];
+ static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE];
+ static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE];
+@@ -208,12 +216,20 @@ free_client(struct nfs4_client *clp)
+ kfree(clp);
+ }
+
+-static void
++void
++put_nfs4_client(struct nfs4_client *clp)
++{
++ if (atomic_dec_and_test(&clp->cl_count))
++ free_client(clp);
++}
++
++void
+ expire_client(struct nfs4_client *clp)
+ {
+ struct nfs4_stateowner *sop;
+
+- dprintk("NFSD: expire_client\n");
++ dprintk("NFSD: expire_client cl_count %d\n",
++ atomic_read(&clp->cl_count));
+ list_del(&clp->cl_idhash);
+ list_del(&clp->cl_strhash);
+ list_del(&clp->cl_lru);
+@@ -221,7 +237,7 @@ expire_client(struct nfs4_client *clp)
+ sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient);
+ release_stateowner(sop);
+ }
+- free_client(clp);
++ put_nfs4_client(clp);
+ }
+
+ static struct nfs4_client *
+@@ -230,6 +246,7 @@ create_client(struct xdr_netobj name) {
+
+ if(!(clp = alloc_client(name)))
+ goto out;
++ atomic_set(&clp->cl_count, 1);
+ INIT_LIST_HEAD(&clp->cl_idhash);
+ INIT_LIST_HEAD(&clp->cl_strhash);
+ INIT_LIST_HEAD(&clp->cl_perclient);
+@@ -339,6 +356,99 @@ move_to_confirmed(struct nfs4_client *cl
+ renew_client(clp);
+ }
+
++
++/* a helper function for parse_callback */
++static int
++parse_octet(unsigned int *lenp, char **addrp)
++{
++ unsigned int len = *lenp;
++ char *p = *addrp;
++ int n = -1;
++ char c;
++
++ for (;;) {
++ if (!len)
++ break;
++ len--;
++ c = *p++;
++ if (c == '.')
++ break;
++ if ((c < '0') || (c > '9')) {
++ n = -1;
++ break;
++ }
++ if (n < 0)
++ n = 0;
++ n = (n * 10) + (c - '0');
++ if (n > 255) {
++ n = -1;
++ break;
++ }
++ }
++ *lenp = len;
++ *addrp = p;
++ return n;
++}
++
++/* parse and set the setclientid ipv4 callback address */
++int
++parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp)
++{
++ int temp = 0;
++ u32 cbaddr = 0;
++ u16 cbport = 0;
++ u32 addrlen = addr_len;
++ char *addr = addr_val;
++ int i, shift;
++
++ /* ipaddress */
++ shift = 24;
++ for(i = 4; i > 0 ; i--) {
++ if ((temp = parse_octet(&addrlen, &addr)) < 0) {
++ return 0;
++ }
++ cbaddr |= (temp << shift);
++ if(shift > 0)
++ shift -= 8;
++ }
++ *cbaddrp = cbaddr;
++
++ /* port */
++ shift = 8;
++ for(i = 2; i > 0 ; i--) {
++ if ((temp = parse_octet(&addrlen, &addr)) < 0) {
++ return 0;
++ }
++ cbport |= (temp << shift);
++ if(shift > 0)
++ shift -= 8;
++ }
++ *cbportp = cbport;
++ return 1;
++}
++
++void
++gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
++{
++ struct nfs4_callback *cb = &clp->cl_callback;
++
++ if( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val,
++ &cb->cb_addr, &cb->cb_port))) {
++ printk(KERN_INFO "NFSD: BAD callback address. client will not receive delegations\n");
++ printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
++ "will not receive delegations\n",
++ clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
++
++ cb->cb_parsed = 0;
++ return;
++ }
++ cb->cb_netid.len = se->se_callback_netid_len;
++ cb->cb_netid.data = se->se_callback_netid_val;
++ cb->cb_prog = se->se_callback_prog;
++ cb->cb_ident = se->se_callback_ident;
++ cb->cb_parsed = 1;
++}
++
+ /*
+ * RFC 3010 has a complex implmentation description of processing a
+ * SETCLIENTID request consisting of 5 bullets, labeled as
+@@ -450,6 +560,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+ copy_cred(&new->cl_cred,&rqstp->rq_cred);
+ gen_clid(new);
+ gen_confirm(new);
++ gen_callback(new, setclid);
+ add_to_unconfirmed(new, strhashval);
+ } else if (cmp_verf(&conf->cl_verifier, &clverifier)) {
+ /*
+@@ -477,6 +588,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+ copy_cred(&new->cl_cred,&rqstp->rq_cred);
+ copy_clid(new, conf);
+ gen_confirm(new);
++ gen_callback(new, setclid);
+ add_to_unconfirmed(new,strhashval);
+ } else if (!unconf) {
+ /*
+@@ -494,6 +606,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+ copy_cred(&new->cl_cred,&rqstp->rq_cred);
+ gen_clid(new);
+ gen_confirm(new);
++ gen_callback(new, setclid);
+ add_to_unconfirmed(new, strhashval);
+ } else if (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm)) {
+ /*
+@@ -519,6 +632,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+ copy_cred(&new->cl_cred,&rqstp->rq_cred);
+ gen_clid(new);
+ gen_confirm(new);
++ gen_callback(new, setclid);
+ add_to_unconfirmed(new, strhashval);
+ } else {
+ /* No cases hit !!! */
+@@ -529,7 +643,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+ setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
+ setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
+ memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data));
+- printk(KERN_INFO "NFSD: this client will not receive delegations\n");
+ status = nfs_ok;
+ out:
+ nfs4_unlock_state();
+@@ -575,7 +688,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+ * not been found.
+ */
+ if (clp->cl_addr != ip_addr) {
+- printk("NFSD: setclientid: string in use by client"
++ dprintk("NFSD: setclientid: string in use by client"
+ "(clientid %08x/%08x)\n",
+ clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
+ goto out;
+@@ -588,7 +701,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+ continue;
+ status = nfserr_inval;
+ if (clp->cl_addr != ip_addr) {
+- printk("NFSD: setclientid: string in use by client"
++ dprintk("NFSD: setclientid: string in use by client"
+ "(clientid %08x/%08x)\n",
+ clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
+ goto out;
+@@ -610,6 +723,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+ status = nfserr_clid_inuse;
+ else {
+ expire_client(conf);
++ clp = unconf;
+ move_to_confirmed(unconf, idhashval);
+ status = nfs_ok;
+ }
+@@ -627,6 +741,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+ if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) {
+ status = nfserr_clid_inuse;
+ } else {
++ clp = conf;
+ status = nfs_ok;
+ }
+ goto out;
+@@ -641,6 +756,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+ status = nfserr_clid_inuse;
+ } else {
+ status = nfs_ok;
++ clp = unconf;
+ move_to_confirmed(unconf, idhashval);
+ }
+ goto out;
+@@ -660,7 +776,9 @@ nfsd4_setclientid_confirm(struct svc_rqs
+ status = nfserr_inval;
+ goto out;
+ out:
+- /* XXX if status == nfs_ok, probe callback path */
++ if (!status)
++ nfsd4_probe_callback(clp);
++
+ nfs4_unlock_state();
+ return status;
+ }
+@@ -1510,10 +1628,12 @@ nfs4_preprocess_seqid_op(struct svc_fh *
+
+ status = nfserr_bad_stateid;
+
+- /* for new lock stateowners, check that the lock->v.new.open_stateid
+- * refers to an open stateowner, and that the lockclid
+- * (nfs4_lock->v.new.clientid) is the same as the
+- * open_stateid->st_stateowner->so_client->clientid
++ /* for new lock stateowners:
++ * check that the lock->v.new.open_stateid
++ * refers to an open stateowner
++ *
++ * check that the lockclid (nfs4_lock->v.new.clientid) is the same
++ * as the open_stateid->st_stateowner->so_client->clientid
+ */
+ if (lockclid) {
+ struct nfs4_stateowner *sop = stp->st_stateowner;
+@@ -1599,6 +1719,17 @@ check_replay:
+ }
+
+ /*
++ * eventually, this will perform an upcall to the 'state daemon' as well as
++ * set the cl_first_state field.
++ */
++void
++first_state(struct nfs4_client *clp)
++{
++ if (!clp->cl_first_state)
++ clp->cl_first_state = get_seconds();
++}
++
++/*
+ * nfs4_unlock_state(); called in encode
+ */
+ int
+@@ -1635,6 +1766,7 @@ nfsd4_open_confirm(struct svc_rqst *rqst
+ stp->st_stateid.si_fileid,
+ stp->st_stateid.si_generation);
+ status = nfs_ok;
++ first_state(sop->so_client);
+ out:
+ return status;
+ }
+@@ -1850,6 +1982,21 @@ nfs4_set_lock_denied(struct file_lock *f
+ deny->ld_type = NFS4_WRITE_LT;
+ }
+
++static struct nfs4_stateowner *
++find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid)
++{
++ struct nfs4_stateowner *local = NULL;
++ int i;
++
++ for (i = 0; i < LOCK_HASH_SIZE; i++) {
++ list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) {
++ if(!cmp_owner_str(local, owner, clid))
++ continue;
++ return local;
++ }
++ }
++ return NULL;
++}
+
+ static int
+ find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) {
+@@ -1969,7 +2116,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+
+ if (nfs4_in_grace() && !lock->lk_reclaim)
+ return nfserr_grace;
+- if (nfs4_in_no_grace() && lock->lk_reclaim)
++ if (!nfs4_in_grace() && lock->lk_reclaim)
+ return nfserr_no_grace;
+
+ if (check_lock_length(lock->lk_offset, lock->lk_length))
+@@ -1992,7 +2139,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+ printk("NFSD: nfsd4_lock: clientid is stale!\n");
+ goto out;
+ }
+- /* does the clientid in the lock owner own the open stateid? */
++
++ /* is the new lock seqid presented by the client zero? */
++ status = nfserr_bad_seqid;
++ if (lock->v.new.lock_seqid != 0)
++ goto out;
+
+ /* validate and update open stateid and open seqid */
+ status = nfs4_preprocess_seqid_op(current_fh,
+@@ -2011,15 +2162,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+ strhashval = lock_ownerstr_hashval(fp->fi_inode,
+ open_sop->so_client->cl_clientid.cl_id,
+ lock->v.new.owner);
+-
+ /*
+ * If we already have this lock owner, the client is in
+ * error (or our bookeeping is wrong!)
+ * for asking for a 'new lock'.
+ */
+ status = nfserr_bad_stateid;
+- if (find_lockstateowner_str(strhashval, &lock->v.new.owner,
+- &lock->v.new.clientid, &lock_sop))
++ lock_sop = find_lockstateowner(&lock->v.new.owner,
++ &lock->v.new.clientid);
++ if (lock_sop)
+ goto out;
+ status = nfserr_resource;
+ if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock)))
+@@ -2315,7 +2466,7 @@ nfsd4_release_lockowner(struct svc_rqst
+ clientid_t *clid = &rlockowner->rl_clientid;
+ struct nfs4_stateowner *local = NULL;
+ struct xdr_netobj *owner = &rlockowner->rl_owner;
+- int status, i;
++ int status;
+
+ dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
+ clid->cl_boot, clid->cl_id);
+@@ -2330,34 +2481,136 @@ nfsd4_release_lockowner(struct svc_rqst
+
+ nfs4_lock_state();
+
+- /* find the lockowner */
+ status = nfs_ok;
+- for (i=0; i < LOCK_HASH_SIZE; i++)
+- list_for_each_entry(local, &lock_ownerstr_hashtbl[i], so_strhash)
+- if(cmp_owner_str(local, owner, clid)) {
+- struct nfs4_stateid *stp;
+-
+- /* check for any locks held by any stateid
+- * associated with the (lock) stateowner */
+- status = nfserr_locks_held;
+- list_for_each_entry(stp, &local->so_perfilestate,
+- st_perfilestate) {
+- if(stp->st_vfs_set) {
+- if (check_for_locks(&stp->st_vfs_file,
+- local))
+- goto out;
+- }
+- }
+- /* no locks held by (lock) stateowner */
+- status = nfs_ok;
+- release_stateowner(local);
+- goto out;
++ local = find_lockstateowner(owner, clid);
++ if (local) {
++ struct nfs4_stateid *stp;
++
++ /* check for any locks held by any stateid
++ * associated with the (lock) stateowner */
++ status = nfserr_locks_held;
++ list_for_each_entry(stp, &local->so_perfilestate,
++ st_perfilestate) {
++ if(stp->st_vfs_set) {
++ if (check_for_locks(&stp->st_vfs_file, local))
++ goto out;
+ }
++ }
++ /* no locks held by (lock) stateowner */
++ status = nfs_ok;
++ release_stateowner(local);
++ }
+ out:
+ nfs4_unlock_state();
+ return status;
+ }
+
++static inline struct nfs4_client_reclaim *
++alloc_reclaim(int namelen)
++{
++ struct nfs4_client_reclaim *crp = NULL;
++
++ crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
++ if (!crp)
++ return NULL;
++ crp->cr_name.data = kmalloc(namelen, GFP_KERNEL);
++ if (!crp->cr_name.data) {
++ kfree(crp);
++ return NULL;
++ }
++ return crp;
++}
++
++/*
++ * failure => all reset bets are off, nfserr_no_grace...
++ */
++static int
++nfs4_client_to_reclaim(struct nfs4_client *clp)
++{
++ unsigned int strhashval;
++ struct nfs4_client_reclaim *crp = NULL;
++
++ crp = alloc_reclaim(clp->cl_name.len);
++ if (!crp)
++ return 0;
++ strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len);
++ INIT_LIST_HEAD(&crp->cr_strhash);
++ list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]);
++ memcpy(crp->cr_name.data, clp->cl_name.data, clp->cl_name.len);
++ crp->cr_name.len = clp->cl_name.len;
++ crp->cr_first_state = clp->cl_first_state;
++ crp->cr_expired = 0;
++ return 1;
++}
++
++static void
++nfs4_release_reclaim(void)
++{
++ struct nfs4_client_reclaim *crp = NULL;
++ int i;
++
++ BUG_ON(!nfs4_reclaim_init);
++ for (i = 0; i < CLIENT_HASH_SIZE; i++) {
++ while (!list_empty(&reclaim_str_hashtbl[i])) {
++ crp = list_entry(reclaim_str_hashtbl[i].next,
++ struct nfs4_client_reclaim, cr_strhash);
++ list_del(&crp->cr_strhash);
++ kfree(crp->cr_name.data);
++ kfree(crp);
++ reclaim_str_hashtbl_size--;
++ }
++ }
++ BUG_ON(reclaim_str_hashtbl_size);
++}
++
++/*
++ * called from OPEN, CLAIM_PREVIOUS with a new clientid. */
++struct nfs4_client_reclaim *
++nfs4_find_reclaim_client(clientid_t *clid)
++{
++ unsigned int idhashval = clientid_hashval(clid->cl_id);
++ unsigned int strhashval;
++ struct nfs4_client *clp, *client = NULL;
++ struct nfs4_client_reclaim *crp = NULL;
++
++
++ /* find clientid in conf_id_hashtbl */
++ list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
++ if (cmp_clid(&clp->cl_clientid, clid)) {
++ client = clp;
++ break;
++ }
++ }
++ if (!client)
++ return NULL;
++
++ /* find clp->cl_name in reclaim_str_hashtbl */
++ strhashval = clientstr_hashval(client->cl_name.data,
++ client->cl_name.len);
++ list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) {
++ if(cmp_name(&crp->cr_name, &client->cl_name)) {
++ return crp;
++ }
++ }
++ return NULL;
++}
++
++/*
++* Called from OPEN. Look for clientid in reclaim list.
++*/
++int
++nfs4_check_open_reclaim(clientid_t *clid)
++{
++ struct nfs4_client_reclaim *crp;
++
++ if ((crp = nfs4_find_reclaim_client(clid)) == NULL)
++ return nfserr_reclaim_bad;
++ if (crp->cr_expired)
++ return nfserr_no_grace;
++ return nfs_ok;
++}
++
++
+ /*
+ * Start and stop routines
+ */
+@@ -2366,10 +2619,16 @@ void
+ nfs4_state_init(void)
+ {
+ int i;
+- time_t start = get_seconds();
++ time_t grace_time;
+
+ if (nfs4_init)
+ return;
++ if (!nfs4_reclaim_init) {
++ for (i = 0; i < CLIENT_HASH_SIZE; i++)
++ INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
++ reclaim_str_hashtbl_size = 0;
++ nfs4_reclaim_init = 1;
++ }
+ for (i = 0; i < CLIENT_HASH_SIZE; i++) {
+ INIT_LIST_HEAD(&conf_id_hashtbl[i]);
+ INIT_LIST_HEAD(&conf_str_hashtbl[i]);
+@@ -2396,27 +2655,36 @@ nfs4_state_init(void)
+
+ INIT_LIST_HEAD(&close_lru);
+ INIT_LIST_HEAD(&client_lru);
+- init_MUTEX(&client_sema);
+- boot_time = start;
+- grace_end = start + NFSD_LEASE_TIME;
++ boot_time = get_seconds();
++ grace_time = max(old_lease_time, lease_time);
++ if (reclaim_str_hashtbl_size == 0)
++ grace_time = 0;
++ if (grace_time)
++ printk("NFSD: starting %ld-second grace period\n", grace_time);
++ grace_end = boot_time + grace_time;
+ INIT_WORK(&laundromat_work,laundromat_main, NULL);
+ schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
+ nfs4_init = 1;
+-
+ }
+
+ int
+ nfs4_in_grace(void)
+ {
+- return time_before(get_seconds(), (unsigned long)grace_end);
++ return get_seconds() < grace_end;
+ }
+
+-int
+-nfs4_in_no_grace(void)
++void
++set_no_grace(void)
+ {
+- return (grace_end < get_seconds());
++ printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n");
++ grace_end = get_seconds();
+ }
+
++time_t
++nfs4_lease_time(void)
++{
++ return lease_time;
++}
+
+ static void
+ __nfs4_state_shutdown(void)
+@@ -2454,6 +2722,61 @@ void
+ nfs4_state_shutdown(void)
+ {
+ nfs4_lock_state();
++ nfs4_release_reclaim();
+ __nfs4_state_shutdown();
+ nfs4_unlock_state();
+ }
++
++/*
++ * Called when leasetime is changed.
++ *
++ * if nfsd is not started, simply set the global lease.
++ *
++ * if nfsd(s) are running, lease change requires nfsv4 state to be reset.
++ * e.g: boot_time is reset, existing nfs4_client structs are
++ * used to fill reclaim_str_hashtbl, then all state (except for the
++ * reclaim_str_hashtbl) is re-initialized.
++ *
++ * if the old lease time is greater than the new lease time, the grace
++ * period needs to be set to the old lease time to allow clients to reclaim
++ * their state. XXX - we may want to set the grace period == lease time
++ * after an initial grace period == old lease time
++ *
++ * if an error occurs in this process, the new lease is set, but the server
++ * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace
++ * which means OPEN/LOCK/READ/WRITE will fail during grace period.
++ *
++ * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and
++ * OPEN and LOCK reclaims.
++ */
++void
++nfs4_reset_lease(time_t leasetime)
++{
++ struct nfs4_client *clp;
++ int i;
++
++ printk("NFSD: New leasetime %ld\n",leasetime);
++ if (!nfs4_init)
++ return;
++ nfs4_lock_state();
++ old_lease_time = lease_time;
++ lease_time = leasetime;
++
++ nfs4_release_reclaim();
++
++ /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */
++ for (i = 0; i < CLIENT_HASH_SIZE; i++) {
++ list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) {
++ if (!nfs4_client_to_reclaim(clp)) {
++ nfs4_release_reclaim();
++ goto init_state;
++ }
++ reclaim_str_hashtbl_size++;
++ }
++ }
++init_state:
++ __nfs4_state_shutdown();
++ nfs4_state_init();
++ nfs4_unlock_state();
++}
++
+--- linux-2.6.7/fs/nfsd/vfs.c.lsec 2004-06-15 23:19:13.000000000 -0600
++++ linux-2.6.7/fs/nfsd/vfs.c 2005-03-23 14:28:24.520322040 -0700
+@@ -44,6 +44,16 @@
+ #include <linux/nfsd/nfsfh.h>
+ #include <linux/quotaops.h>
+ #include <linux/dnotify.h>
++#ifdef CONFIG_NFSD_V4
++#include <linux/posix_acl.h>
++#include <linux/posix_acl_xattr.h>
++#include <linux/xattr_acl.h>
++#include <linux/xattr.h>
++#include <linux/nfs4.h>
++#include <linux/nfs4_acl.h>
++#include <linux/nfsd_idmap.h>
++#include <linux/security.h>
++#endif /* CONFIG_NFSD_V4 */
+
+ #include <asm/uaccess.h>
+
+@@ -344,6 +354,177 @@ out_nfserr:
+ goto out;
+ }
+
++#if defined(CONFIG_NFSD_V4)
++
++static int
++set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
++{
++ int len;
++ size_t buflen;
++ char *buf = NULL;
++ int error = 0;
++ struct inode *inode = dentry->d_inode;
++
++ buflen = posix_acl_xattr_size(pacl->a_count);
++ buf = kmalloc(buflen, GFP_KERNEL);
++ error = -ENOMEM;
++ if (buf == NULL)
++ goto out;
++
++ len = posix_acl_to_xattr(pacl, buf, buflen);
++ if (len < 0) {
++ error = len;
++ goto out;
++ }
++
++ error = -EOPNOTSUPP;
++ if (inode->i_op && inode->i_op->setxattr) {
++ down(&inode->i_sem);
++ security_inode_setxattr(dentry, key, buf, len, 0);
++ error = inode->i_op->setxattr(dentry, key, buf, len, 0);
++ if (!error)
++ security_inode_post_setxattr(dentry, key, buf, len, 0);
++ up(&inode->i_sem);
++ }
++out:
++ kfree(buf);
++ return (error);
++}
++
++int
++nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
++ struct nfs4_acl *acl)
++{
++ int error;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct posix_acl *pacl = NULL, *dpacl = NULL;
++ unsigned int flags = 0;
++
++ /* Get inode */
++ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR);
++ if (error)
++ goto out;
++
++ dentry = fhp->fh_dentry;
++ inode = dentry->d_inode;
++ if (S_ISDIR(inode->i_mode))
++ flags = NFS4_ACL_DIR;
++
++ error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
++ if (error < 0)
++ goto out_nfserr;
++
++ if (pacl) {
++ error = set_nfsv4_acl_one(dentry, pacl, XATTR_NAME_ACL_ACCESS);
++ if (error < 0)
++ goto out_nfserr;
++ }
++
++ if (dpacl) {
++ error = set_nfsv4_acl_one(dentry, dpacl, XATTR_NAME_ACL_DEFAULT);
++ if (error < 0)
++ goto out_nfserr;
++ }
++
++ error = nfs_ok;
++
++out:
++ posix_acl_release(pacl);
++ posix_acl_release(dpacl);
++ return (error);
++out_nfserr:
++ error = nfserrno(error);
++ goto out;
++}
++
++static struct posix_acl *
++_get_posix_acl(struct dentry *dentry, char *key)
++{
++ struct inode *inode = dentry->d_inode;
++ char *buf = NULL;
++ int buflen, error = 0;
++ struct posix_acl *pacl = NULL;
++
++ down(&inode->i_sem);
++
++ buflen = inode->i_op->getxattr(dentry, key, NULL, 0);
++ if (buflen <= 0) {
++ error = buflen < 0 ? buflen : -ENODATA;
++ goto out_sem;
++ }
++
++ buf = kmalloc(buflen, GFP_KERNEL);
++ if (buf == NULL) {
++ error = -ENOMEM;
++ goto out_sem;
++ }
++
++ error = -EOPNOTSUPP;
++ if (inode->i_op && inode->i_op->getxattr) {
++ error = security_inode_getxattr(dentry, key);
++ if (error)
++ goto out_sem;
++ error = inode->i_op->getxattr(dentry, key, buf, buflen);
++ }
++ if (error < 0)
++ goto out_sem;
++
++ error = 0;
++ up(&inode->i_sem);
++
++ pacl = posix_acl_from_xattr(buf, buflen);
++ out:
++ kfree(buf);
++ return pacl;
++ out_sem:
++ up(&inode->i_sem);
++ pacl = ERR_PTR(error);
++ goto out;
++}
++
++int
++nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
++{
++ struct inode *inode = dentry->d_inode;
++ int error = 0;
++ struct posix_acl *pacl = NULL, *dpacl = NULL;
++ unsigned int flags = 0;
++
++ pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS);
++ if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
++ pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
++ if (IS_ERR(pacl)) {
++ error = PTR_ERR(pacl);
++ pacl = NULL;
++ goto out;
++ }
++
++ if (S_ISDIR(inode->i_mode)) {
++ dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT);
++ if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
++ dpacl = NULL;
++ else if (IS_ERR(dpacl)) {
++ error = PTR_ERR(dpacl);
++ dpacl = NULL;
++ goto out;
++ }
++ flags = NFS4_ACL_DIR;
++ }
++
++ *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
++ if (IS_ERR(*acl)) {
++ error = PTR_ERR(*acl);
++ *acl = NULL;
++ }
++ out:
++ posix_acl_release(pacl);
++ posix_acl_release(dpacl);
++ return error;
++}
++
++#endif /* defined(CONFIG_NFS_V4) */
++
+ #ifdef CONFIG_NFSD_V3
+ /*
+ * Check server access rights to a file system object
+--- linux-2.6.7/fs/nfsd/nfs4idmap.c.lsec 2004-06-15 23:19:43.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4idmap.c 2005-03-23 14:28:24.687296656 -0700
+@@ -78,9 +78,9 @@ struct ent {
+
+ #define DefineSimpleCacheLookupMap(STRUCT, FUNC) \
+ DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \
+- (struct STRUCT *item, int set), /*no setup */, \
++ (struct STRUCT *item, int set), \
+ & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \
+- STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0)
++ STRUCT##_init(new, item), STRUCT##_update(tmp, item))
+
+ /* Common entry handling */
+
+--- linux-2.6.7/fs/nfsd/nfs4acl.c.lsec 2005-03-23 14:28:24.463330704 -0700
++++ linux-2.6.7/fs/nfsd/nfs4acl.c 2005-03-23 14:28:24.463330704 -0700
+@@ -0,0 +1,974 @@
++/*
++ * fs/nfs4acl/acl.c
++ *
++ * Common NFSv4 ACL handling code.
++ *
++ * Copyright (c) 2002, 2003 The Regents of the University of Michigan.
++ * All rights reserved.
++ *
++ * Marius Aamodt Eriksen <marius@umich.edu>
++ * Jeff Sedlak <jsedlak@umich.edu>
++ * J. Bruce Fields <bfields@umich.edu>
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. Neither the name of the University nor the names of its
++ * contributors may be used to endorse or promote products derived
++ * from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/module.h>
++#include <linux/nfs_fs.h>
++#include <linux/posix_acl.h>
++#include <linux/nfs4.h>
++#include <linux/nfs4_acl.h>
++
++
++/* mode bit translations: */
++#define NFS4_READ_MODE (NFS4_ACE_READ_DATA | NFS4_ACE_READ_NAMED_ATTRS)
++#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_WRITE_NAMED_ATTRS | NFS4_ACE_APPEND_DATA)
++#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
++#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)
++#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)
++
++/* flags used to simulate posix default ACLs */
++#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
++ | NFS4_ACE_DIRECTORY_INHERIT_ACE | NFS4_ACE_INHERIT_ONLY_ACE)
++
++#define MASK_EQUAL(mask1, mask2) \
++ ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) )
++
++static u32
++mask_from_posix(unsigned short perm, unsigned int flags)
++{
++ int mask = NFS4_ANYONE_MODE;
++
++ if (flags & NFS4_ACL_OWNER)
++ mask |= NFS4_OWNER_MODE;
++ if (perm & ACL_READ)
++ mask |= NFS4_READ_MODE;
++ if (perm & ACL_WRITE)
++ mask |= NFS4_WRITE_MODE;
++ if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
++ mask |= NFS4_ACE_DELETE_CHILD;
++ if (perm & ACL_EXECUTE)
++ mask |= NFS4_EXECUTE_MODE;
++ return mask;
++}
++
++static u32
++deny_mask(u32 allow_mask, unsigned int flags)
++{
++ u32 ret = ~allow_mask & ~NFS4_ACE_DELETE;
++ if (!(flags & NFS4_ACL_DIR))
++ ret &= ~NFS4_ACE_DELETE_CHILD;
++ return ret;
++}
++
++static int
++mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
++{
++ u32 ignore = 0;
++
++ if (!(flags & NFS4_ACL_DIR))
++ ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */
++ perm |= ignore;
++ *mode = 0;
++ if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
++ *mode |= ACL_READ;
++ if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE)
++ *mode |= ACL_WRITE;
++ if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
++ *mode |= ACL_EXECUTE;
++ if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags)))
++ return -EINVAL;
++ return 0;
++}
++
++struct ace_container {
++ struct nfs4_ace *ace;
++ struct list_head ace_l;
++};
++
++static short ace2type(struct nfs4_ace *);
++static int _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int);
++static struct posix_acl *_nfsv4_to_posix_one(struct nfs4_acl *, unsigned int);
++int nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t);
++int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *);
++
++struct nfs4_acl *
++nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl,
++ unsigned int flags)
++{
++ struct nfs4_acl *acl;
++ int error = -EINVAL;
++
++ if ((pacl != NULL &&
++ (posix_acl_valid(pacl) < 0 || pacl->a_count == 0)) ||
++ (dpacl != NULL &&
++ (posix_acl_valid(dpacl) < 0 || dpacl->a_count == 0)))
++ goto out_err;
++
++ acl = nfs4_acl_new();
++ if (acl == NULL) {
++ error = -ENOMEM;
++ goto out_err;
++ }
++
++ if (pacl != NULL) {
++ error = _posix_to_nfsv4_one(pacl, acl,
++ flags & ~NFS4_ACL_TYPE_DEFAULT);
++ if (error < 0)
++ goto out_acl;
++ }
++
++ if (dpacl != NULL) {
++ error = _posix_to_nfsv4_one(dpacl, acl,
++ flags | NFS4_ACL_TYPE_DEFAULT);
++ if (error < 0)
++ goto out_acl;
++ }
++
++ return acl;
++
++out_acl:
++ nfs4_acl_free(acl);
++out_err:
++ acl = ERR_PTR(error);
++
++ return acl;
++}
++
++static int
++nfs4_acl_add_pair(struct nfs4_acl *acl, int eflag, u32 mask, int whotype,
++ uid_t owner, unsigned int flags)
++{
++ int error;
++
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE,
++ eflag, mask, whotype, owner);
++ if (error < 0)
++ return error;
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++ eflag, deny_mask(mask, flags), whotype, owner);
++ return error;
++}
++
++/* We assume the acl has been verified with posix_acl_valid. */
++static int
++_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
++ unsigned int flags)
++{
++ struct posix_acl_entry *pa, *pe, *group_owner_entry;
++ int error = -EINVAL;
++ u32 mask, mask_mask;
++ int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
++ NFS4_INHERITANCE_FLAGS : 0);
++
++ BUG_ON(pacl->a_count < 3);
++ pe = pacl->a_entries + pacl->a_count;
++ pa = pe - 2; /* if mask entry exists, it's second from the last. */
++ if (pa->e_tag == ACL_MASK)
++ mask_mask = deny_mask(mask_from_posix(pa->e_perm, flags), flags);
++ else
++ mask_mask = 0;
++
++ pa = pacl->a_entries;
++ BUG_ON(pa->e_tag != ACL_USER_OBJ);
++ mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
++ error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_OWNER, 0, flags);
++ if (error < 0)
++ goto out;
++ pa++;
++
++ while (pa->e_tag == ACL_USER) {
++ mask = mask_from_posix(pa->e_perm, flags);
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++ eflag, mask_mask, NFS4_ACL_WHO_NAMED, pa->e_id);
++ if (error < 0)
++ goto out;
++
++
++ error = nfs4_acl_add_pair(acl, eflag, mask,
++ NFS4_ACL_WHO_NAMED, pa->e_id, flags);
++ if (error < 0)
++ goto out;
++ pa++;
++ }
++
++ /* In the case of groups, we apply allow ACEs first, then deny ACEs,
++ * since a user can be in more than one group. */
++
++ /* allow ACEs */
++
++ if (pacl->a_count > 3) {
++ BUG_ON(pa->e_tag != ACL_GROUP_OBJ);
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++ NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask,
++ NFS4_ACL_WHO_GROUP, 0);
++ if (error < 0)
++ goto out;
++ }
++ group_owner_entry = pa;
++ mask = mask_from_posix(pa->e_perm, flags);
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE,
++ NFS4_ACE_IDENTIFIER_GROUP | eflag, mask,
++ NFS4_ACL_WHO_GROUP, 0);
++ if (error < 0)
++ goto out;
++ pa++;
++
++ while (pa->e_tag == ACL_GROUP) {
++ mask = mask_from_posix(pa->e_perm, flags);
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++ NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask,
++ NFS4_ACL_WHO_NAMED, pa->e_id);
++ if (error < 0)
++ goto out;
++
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE,
++ NFS4_ACE_IDENTIFIER_GROUP | eflag, mask,
++ NFS4_ACL_WHO_NAMED, pa->e_id);
++ if (error < 0)
++ goto out;
++ pa++;
++ }
++
++ /* deny ACEs */
++
++ pa = group_owner_entry;
++ mask = mask_from_posix(pa->e_perm, flags);
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++ NFS4_ACE_IDENTIFIER_GROUP | eflag,
++ deny_mask(mask, flags), NFS4_ACL_WHO_GROUP, 0);
++ if (error < 0)
++ goto out;
++ pa++;
++ while (pa->e_tag == ACL_GROUP) {
++ mask = mask_from_posix(pa->e_perm, flags);
++ error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++ NFS4_ACE_IDENTIFIER_GROUP | eflag,
++ deny_mask(mask, flags), NFS4_ACL_WHO_NAMED, pa->e_id);
++ if (error < 0)
++ goto out;
++ pa++;
++ }
++
++ if (pa->e_tag == ACL_MASK)
++ pa++;
++ BUG_ON(pa->e_tag != ACL_OTHER);
++ mask = mask_from_posix(pa->e_perm, flags);
++ error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_EVERYONE, 0, flags);
++
++out:
++ return error;
++}
++
++static void
++sort_pacl_range(struct posix_acl *pacl, int start, int end) {
++ int sorted = 0, i;
++ struct posix_acl_entry tmp;
++
++ /* We just do a bubble sort; easy to do in place, and we're not
++ * expecting acl's to be long enough to justify anything more. */
++ while (!sorted) {
++ sorted = 1;
++ for (i = start; i < end; i++) {
++ if (pacl->a_entries[i].e_id
++ > pacl->a_entries[i+1].e_id) {
++ sorted = 0;
++ tmp = pacl->a_entries[i];
++ pacl->a_entries[i] = pacl->a_entries[i+1];
++ pacl->a_entries[i+1] = tmp;
++ }
++ }
++ }
++}
++
++static void
++sort_pacl(struct posix_acl *pacl)
++{
++ /* posix_acl_valid requires that users and groups be in order
++ * by uid/gid. */
++ int i, j;
++
++ if (pacl->a_count <= 4)
++ return; /* no users or groups */
++ i = 1;
++ while (pacl->a_entries[i].e_tag == ACL_USER)
++ i++;
++ sort_pacl_range(pacl, 1, i-1);
++
++ BUG_ON(pacl->a_entries[i].e_tag != ACL_GROUP_OBJ);
++ j = i++;
++ while (pacl->a_entries[j].e_tag == ACL_GROUP)
++ j++;
++ sort_pacl_range(pacl, i, j-1);
++ return;
++}
++
++static int
++write_pace(struct nfs4_ace *ace, struct posix_acl *pacl,
++ struct posix_acl_entry **pace, short tag, unsigned int flags)
++{
++ struct posix_acl_entry *this = *pace;
++
++ if (*pace == pacl->a_entries + pacl->a_count)
++ return -EINVAL; /* fell off the end */
++ (*pace)++;
++ this->e_tag = tag;
++ if (tag == ACL_USER_OBJ)
++ flags |= NFS4_ACL_OWNER;
++ if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags))
++ return -EINVAL;
++ this->e_id = (tag == ACL_USER || tag == ACL_GROUP ?
++ ace->who : ACL_UNDEFINED_ID);
++ return 0;
++}
++
++static struct nfs4_ace *
++get_next_v4_ace(struct list_head **p, struct list_head *head)
++{
++ struct nfs4_ace *ace;
++
++ *p = (*p)->next;
++ if (*p == head)
++ return NULL;
++ ace = list_entry(*p, struct nfs4_ace, l_ace);
++
++ return ace;
++}
++
++int
++nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl,
++ struct posix_acl **dpacl, unsigned int flags)
++{
++ struct nfs4_acl *dacl;
++ int error = -ENOMEM;
++
++ *pacl = NULL;
++ *dpacl = NULL;
++
++ dacl = nfs4_acl_new();
++ if (dacl == NULL)
++ goto out;
++
++ error = nfs4_acl_split(acl, dacl);
++ if (error < 0)
++ goto out_acl;
++
++ if (pacl != NULL) {
++ if (acl->naces == 0) {
++ error = -ENODATA;
++ goto try_dpacl;
++ }
++
++ *pacl = _nfsv4_to_posix_one(acl, flags);
++ if (IS_ERR(*pacl)) {
++ error = PTR_ERR(*pacl);
++ *pacl = NULL;
++ goto out_acl;
++ }
++ }
++
++try_dpacl:
++ if (dpacl != NULL) {
++ if (dacl->naces == 0) {
++ if (pacl == NULL || *pacl == NULL)
++ error = -ENODATA;
++ goto out_acl;
++ }
++
++ error = 0;
++ *dpacl = _nfsv4_to_posix_one(dacl, flags);
++ if (IS_ERR(*dpacl)) {
++ error = PTR_ERR(*dpacl);
++ *dpacl = NULL;
++ goto out_acl;
++ }
++ }
++
++out_acl:
++ if (error && pacl) {
++ posix_acl_release(*pacl);
++ *pacl = NULL;
++ }
++ nfs4_acl_free(dacl);
++out:
++ return error;
++}
++
++static int
++same_who(struct nfs4_ace *a, struct nfs4_ace *b)
++{
++ return a->whotype == b->whotype &&
++ (a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who);
++}
++
++static int
++complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny,
++ unsigned int flags)
++{
++ int ignore = 0;
++ if (!(flags & NFS4_ACL_DIR))
++ ignore |= NFS4_ACE_DELETE_CHILD;
++ return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags),
++ ignore|deny->access_mask) &&
++ allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
++ deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE &&
++ allow->flag == deny->flag &&
++ same_who(allow, deny);
++}
++
++static inline int
++user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++ struct posix_acl *pacl, struct posix_acl_entry **pace,
++ unsigned int flags)
++{
++ int error = -EINVAL;
++ struct nfs4_ace *ace, *ace2;
++
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ if (ace2type(ace) != ACL_USER_OBJ)
++ goto out;
++ error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags);
++ if (error < 0)
++ goto out;
++ error = -EINVAL;
++ ace2 = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace2 == NULL)
++ goto out;
++ if (!complementary_ace_pair(ace, ace2, flags))
++ goto out;
++ error = 0;
++out:
++ return error;
++}
++
++static inline int
++users_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++ struct nfs4_ace **mask_ace,
++ struct posix_acl *pacl, struct posix_acl_entry **pace,
++ unsigned int flags)
++{
++ int error = -EINVAL;
++ struct nfs4_ace *ace, *ace2;
++
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ while (ace2type(ace) == ACL_USER) {
++ if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
++ goto out;
++ if (*mask_ace &&
++ !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
++ goto out;
++ *mask_ace = ace;
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
++ goto out;
++ error = write_pace(ace, pacl, pace, ACL_USER, flags);
++ if (error < 0)
++ goto out;
++ error = -EINVAL;
++ ace2 = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace2 == NULL)
++ goto out;
++ if (!complementary_ace_pair(ace, ace2, flags))
++ goto out;
++ if ((*mask_ace)->flag != ace2->flag ||
++ !same_who(*mask_ace, ace2))
++ goto out;
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ }
++ error = 0;
++out:
++ return error;
++}
++
++static inline int
++group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++ struct nfs4_ace **mask_ace,
++ struct posix_acl *pacl, struct posix_acl_entry **pace,
++ unsigned int flags)
++{
++ int error = -EINVAL;
++ struct nfs4_ace *ace, *ace2;
++ struct ace_container *ac;
++ struct list_head group_l;
++
++ INIT_LIST_HEAD(&group_l);
++ ace = list_entry(*p, struct nfs4_ace, l_ace);
++
++ /* group owner (mask and allow aces) */
++
++ if (pacl->a_count != 3) {
++ /* then the group owner should be preceded by mask */
++ if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
++ goto out;
++ if (*mask_ace &&
++ !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
++ goto out;
++ *mask_ace = ace;
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++
++ if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace))
++ goto out;
++ }
++
++ if (ace2type(ace) != ACL_GROUP_OBJ)
++ goto out;
++
++ ac = kmalloc(sizeof(*ac), GFP_KERNEL);
++ error = -ENOMEM;
++ if (ac == NULL)
++ goto out;
++ ac->ace = ace;
++ list_add_tail(&ac->ace_l, &group_l);
++
++ error = -EINVAL;
++ if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
++ goto out;
++
++ error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags);
++ if (error < 0)
++ goto out;
++
++ error = -EINVAL;
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++
++ /* groups (mask and allow aces) */
++
++ while (ace2type(ace) == ACL_GROUP) {
++ if (*mask_ace == NULL)
++ goto out;
++
++ if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE ||
++ !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
++ goto out;
++ *mask_ace = ace;
++
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ ac = kmalloc(sizeof(*ac), GFP_KERNEL);
++ error = -ENOMEM;
++ if (ac == NULL)
++ goto out;
++ error = -EINVAL;
++ if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE ||
++ !same_who(ace, *mask_ace))
++ goto out;
++
++ ac->ace = ace;
++ list_add_tail(&ac->ace_l, &group_l);
++
++ error = write_pace(ace, pacl, pace, ACL_GROUP, flags);
++ if (error < 0)
++ goto out;
++ error = -EINVAL;
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ }
++
++ /* group owner (deny ace) */
++
++ if (ace2type(ace) != ACL_GROUP_OBJ)
++ goto out;
++ ac = list_entry(group_l.next, struct ace_container, ace_l);
++ ace2 = ac->ace;
++ if (!complementary_ace_pair(ace2, ace, flags))
++ goto out;
++ list_del(group_l.next);
++ kfree(ac);
++
++ /* groups (deny aces) */
++
++ while (!list_empty(&group_l)) {
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ if (ace2type(ace) != ACL_GROUP)
++ goto out;
++ ac = list_entry(group_l.next, struct ace_container, ace_l);
++ ace2 = ac->ace;
++ if (!complementary_ace_pair(ace2, ace, flags))
++ goto out;
++ list_del(group_l.next);
++ kfree(ac);
++ }
++
++ ace = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace == NULL)
++ goto out;
++ if (ace2type(ace) != ACL_OTHER)
++ goto out;
++ error = 0;
++out:
++ while (!list_empty(&group_l)) {
++ ac = list_entry(group_l.next, struct ace_container, ace_l);
++ list_del(group_l.next);
++ kfree(ac);
++ }
++ return error;
++}
++
++static inline int
++mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++ struct nfs4_ace **mask_ace,
++ struct posix_acl *pacl, struct posix_acl_entry **pace,
++ unsigned int flags)
++{
++ int error = -EINVAL;
++ struct nfs4_ace *ace;
++
++ ace = list_entry(*p, struct nfs4_ace, l_ace);
++ if (pacl->a_count != 3) {
++ if (*mask_ace == NULL)
++ goto out;
++ (*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags);
++ write_pace(*mask_ace, pacl, pace, ACL_MASK, flags);
++ }
++ error = 0;
++out:
++ return error;
++}
++
++static inline int
++other_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++ struct posix_acl *pacl, struct posix_acl_entry **pace,
++ unsigned int flags)
++{
++ int error = -EINVAL;
++ struct nfs4_ace *ace, *ace2;
++
++ ace = list_entry(*p, struct nfs4_ace, l_ace);
++ if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
++ goto out;
++ error = write_pace(ace, pacl, pace, ACL_OTHER, flags);
++ if (error < 0)
++ goto out;
++ error = -EINVAL;
++ ace2 = get_next_v4_ace(p, &n4acl->ace_head);
++ if (ace2 == NULL)
++ goto out;
++ if (!complementary_ace_pair(ace, ace2, flags))
++ goto out;
++ error = 0;
++out:
++ return error;
++}
++
++static int
++calculate_posix_ace_count(struct nfs4_acl *n4acl)
++{
++ if (n4acl->naces == 6) /* owner, owner group, and other only */
++ return 3;
++ else { /* Otherwise there must be a mask entry. */
++ /* Also, the remaining entries are for named users and
++ * groups, and come in threes (mask, allow, deny): */
++ if (n4acl->naces < 7)
++ return -1;
++ if ((n4acl->naces - 7) % 3)
++ return -1;
++ return 4 + (n4acl->naces - 7)/3;
++ }
++}
++
++
++static struct posix_acl *
++_nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags)
++{
++ struct posix_acl *pacl;
++ int error = -EINVAL, nace = 0;
++ struct list_head *p;
++ struct nfs4_ace *mask_ace = NULL;
++ struct posix_acl_entry *pace;
++
++ nace = calculate_posix_ace_count(n4acl);
++ if (nace < 0)
++ goto out_err;
++
++ pacl = posix_acl_alloc(nace, GFP_KERNEL);
++ error = -ENOMEM;
++ if (pacl == NULL)
++ goto out_err;
++
++ pace = &pacl->a_entries[0];
++ p = &n4acl->ace_head;
++
++ error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags);
++ if (error)
++ goto out_acl;
++
++ error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags);
++ if (error)
++ goto out_acl;
++
++ error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace,
++ flags);
++ if (error)
++ goto out_acl;
++
++ error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags);
++ if (error)
++ goto out_acl;
++ error = other_from_v4(n4acl, &p, pacl, &pace, flags);
++ if (error)
++ goto out_acl;
++
++ error = -EINVAL;
++ if (p->next != &n4acl->ace_head)
++ goto out_acl;
++ if (pace != pacl->a_entries + pacl->a_count)
++ goto out_acl;
++
++ sort_pacl(pacl);
++
++ return pacl;
++out_acl:
++ posix_acl_release(pacl);
++out_err:
++ pacl = ERR_PTR(error);
++ return pacl;
++}
++
++int
++nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl)
++{
++ struct list_head *h, *n;
++ struct nfs4_ace *ace;
++ int error = 0;
++
++ list_for_each_safe(h, n, &acl->ace_head) {
++ ace = list_entry(h, struct nfs4_ace, l_ace);
++
++ if ((ace->flag & NFS4_INHERITANCE_FLAGS)
++ != NFS4_INHERITANCE_FLAGS)
++ continue;
++
++ error = nfs4_acl_add_ace(dacl, ace->type, ace->flag,
++ ace->access_mask, ace->whotype, ace->who) == -1;
++ if (error < 0)
++ goto out;
++
++ list_del(h);
++ kfree(ace);
++ acl->naces--;
++ }
++
++out:
++ return error;
++}
++
++static short
++ace2type(struct nfs4_ace *ace)
++{
++ switch (ace->whotype) {
++ case NFS4_ACL_WHO_NAMED:
++ return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
++ ACL_GROUP : ACL_USER);
++ case NFS4_ACL_WHO_OWNER:
++ return ACL_USER_OBJ;
++ case NFS4_ACL_WHO_GROUP:
++ return ACL_GROUP_OBJ;
++ case NFS4_ACL_WHO_EVERYONE:
++ return ACL_OTHER;
++ }
++ BUG();
++ return -1;
++}
++
++EXPORT_SYMBOL(nfs4_acl_posix_to_nfsv4);
++EXPORT_SYMBOL(nfs4_acl_nfsv4_to_posix);
++
++struct nfs4_acl *
++nfs4_acl_new(void)
++{
++ struct nfs4_acl *acl;
++
++ if ((acl = kmalloc(sizeof(*acl), GFP_KERNEL)) == NULL)
++ return NULL;
++
++ acl->naces = 0;
++ INIT_LIST_HEAD(&acl->ace_head);
++
++ return acl;
++}
++
++void
++nfs4_acl_free(struct nfs4_acl *acl)
++{
++ struct list_head *h;
++ struct nfs4_ace *ace;
++
++ if (!acl)
++ return;
++
++ while (!list_empty(&acl->ace_head)) {
++ h = acl->ace_head.next;
++ list_del(h);
++ ace = list_entry(h, struct nfs4_ace, l_ace);
++ kfree(ace);
++ }
++
++ kfree(acl);
++
++ return;
++}
++
++int
++nfs4_acl_add_ace(struct nfs4_acl *acl, u32 type, u32 flag, u32 access_mask,
++ int whotype, uid_t who)
++{
++ struct nfs4_ace *ace;
++
++ if ((ace = kmalloc(sizeof(*ace), GFP_KERNEL)) == NULL)
++ return -1;
++
++ ace->type = type;
++ ace->flag = flag;
++ ace->access_mask = access_mask;
++ ace->whotype = whotype;
++ ace->who = who;
++
++ list_add_tail(&ace->l_ace, &acl->ace_head);
++ acl->naces++;
++
++ return 0;
++}
++
++static struct {
++ char *string;
++ int stringlen;
++ int type;
++} s2t_map[] = {
++ {
++ .string = "OWNER@",
++ .stringlen = sizeof("OWNER@") - 1,
++ .type = NFS4_ACL_WHO_OWNER,
++ },
++ {
++ .string = "GROUP@",
++ .stringlen = sizeof("GROUP@") - 1,
++ .type = NFS4_ACL_WHO_GROUP,
++ },
++ {
++ .string = "EVERYONE@",
++ .stringlen = sizeof("EVERYONE@") - 1,
++ .type = NFS4_ACL_WHO_EVERYONE,
++ },
++};
++
++int
++nfs4_acl_get_whotype(char *p, u32 len)
++{
++ int i;
++
++ for (i=0; i < sizeof(s2t_map) / sizeof(*s2t_map); i++) {
++ if (s2t_map[i].stringlen == len &&
++ 0 == memcmp(s2t_map[i].string, p, len))
++ return s2t_map[i].type;
++ }
++ return NFS4_ACL_WHO_NAMED;
++}
++
++int
++nfs4_acl_write_who(int who, char *p)
++{
++ int i;
++
++ for (i=0; i < sizeof(s2t_map) / sizeof(*s2t_map); i++) {
++ if (s2t_map[i].type == who) {
++ memcpy(p, s2t_map[i].string, s2t_map[i].stringlen);
++ return s2t_map[i].stringlen;
++ }
++ }
++ BUG();
++ return -1;
++}
++
++static inline int
++match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who)
++{
++ switch (ace->whotype) {
++ case NFS4_ACL_WHO_NAMED:
++ return who == ace->who;
++ case NFS4_ACL_WHO_OWNER:
++ return who == owner;
++ case NFS4_ACL_WHO_GROUP:
++ return who == group;
++ case NFS4_ACL_WHO_EVERYONE:
++ return 1;
++ default:
++ return 0;
++ }
++}
++
++/* 0 = granted, -EACCES = denied; mask is an nfsv4 mask, not mode bits */
++int
++nfs4_acl_permission(struct nfs4_acl *acl, uid_t owner, gid_t group,
++ uid_t who, u32 mask)
++{
++ struct nfs4_ace *ace;
++ u32 allowed = 0;
++
++ list_for_each_entry(ace, &acl->ace_head, l_ace) {
++ if (!match_who(ace, group, owner, who))
++ continue;
++ switch (ace->type) {
++ case NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE:
++ allowed |= ace->access_mask;
++ if ((allowed & mask) == mask)
++ return 0;
++ break;
++ case NFS4_ACE_ACCESS_DENIED_ACE_TYPE:
++ if (ace->access_mask & mask)
++ return -EACCES;
++ break;
++ }
++ }
++ return -EACCES;
++}
++
++EXPORT_SYMBOL(nfs4_acl_new);
++EXPORT_SYMBOL(nfs4_acl_free);
++EXPORT_SYMBOL(nfs4_acl_add_ace);
++EXPORT_SYMBOL(nfs4_acl_get_whotype);
++EXPORT_SYMBOL(nfs4_acl_write_who);
++EXPORT_SYMBOL(nfs4_acl_permission);
+--- linux-2.6.7/fs/nfsd/Makefile.lsec 2004-06-15 23:19:13.000000000 -0600
++++ linux-2.6.7/fs/nfsd/Makefile 2005-03-23 14:28:24.461331008 -0700
+@@ -7,5 +7,6 @@ obj-$(CONFIG_NFSD) += nfsd.o
+ nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
+ export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
+ nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
+-nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o
++nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
++ nfs4acl.o nfs4callback.o
+ nfsd-objs := $(nfsd-y)
+--- linux-2.6.7/fs/nfsd/nfsctl.c.lsec 2004-06-15 23:19:01.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfsctl.c 2005-03-23 14:28:24.132381016 -0700
+@@ -36,7 +36,7 @@
+ #include <asm/uaccess.h>
+
+ /*
+- * We have a single directory with 8 nodes in it.
++ * We have a single directory with 9 nodes in it.
+ */
+ enum {
+ NFSD_Root = 1,
+@@ -50,6 +50,7 @@ enum {
+ NFSD_List,
+ NFSD_Fh,
+ NFSD_Threads,
++ NFSD_Leasetime,
+ };
+
+ /*
+@@ -64,6 +65,7 @@ static ssize_t write_getfd(struct file *
+ static ssize_t write_getfs(struct file *file, char *buf, size_t size);
+ static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
+ static ssize_t write_threads(struct file *file, char *buf, size_t size);
++static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
+
+ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
+ [NFSD_Svc] = write_svc,
+@@ -75,6 +77,7 @@ static ssize_t (*write_op[])(struct file
+ [NFSD_Getfs] = write_getfs,
+ [NFSD_Fh] = write_filehandle,
+ [NFSD_Threads] = write_threads,
++ [NFSD_Leasetime] = write_leasetime,
+ };
+
+ /* an argresp is stored in an allocated page and holds the
+@@ -393,6 +396,29 @@ static ssize_t write_threads(struct file
+ return strlen(buf);
+ }
+
++extern time_t nfs4_leasetime(void);
++
++static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
++{
++ /* if size > 10 seconds, call
++ * nfs4_reset_lease() then write out the new lease (seconds) as reply
++ */
++ char *mesg = buf;
++ int rv;
++
++ if (size > 0) {
++ int lease;
++ rv = get_int(&mesg, &lease);
++ if (rv)
++ return rv;
++ if (lease < 10 || lease > 3600)
++ return -EINVAL;
++ nfs4_reset_lease(lease);
++ }
++ sprintf(buf, "%ld\n", nfs4_lease_time());
++ return strlen(buf);
++}
++
+ /*----------------------------------------------------------------------------*/
+ /*
+ * populating the filesystem.
+@@ -411,6 +437,7 @@ static int nfsd_fill_super(struct super_
+ [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
+ [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
+ [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
++ [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
+ /* last one */ {""}
+ };
+ return simple_fill_super(sb, 0x6e667364, nfsd_files);
+--- linux-2.6.7/fs/nfs/callback_proc.c.lsec 2005-03-23 14:28:22.485631360 -0700
++++ linux-2.6.7/fs/nfs/callback_proc.c 2005-03-23 14:28:22.485631360 -0700
+@@ -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;
++}
+--- linux-2.6.7/fs/nfs/delegation.c.lsec 2005-03-23 14:28:22.546622088 -0700
++++ linux-2.6.7/fs/nfs/delegation.c 2005-03-23 14:28:22.545622240 -0700
+@@ -0,0 +1,320 @@
++/*
++ * linux/fs/nfs/delegation.c
++ *
++ * Copyright (C) 2004 Trond Myklebust
++ *
++ * 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 "delegation.h"
++
++static struct nfs_delegation *nfs_alloc_delegation(void)
++{
++ return (struct nfs_delegation *)kmalloc(sizeof(struct nfs_delegation), GFP_KERNEL);
++}
++
++static void nfs_free_delegation(struct nfs_delegation *delegation)
++{
++ if (delegation->cred)
++ put_rpccred(delegation->cred);
++ kfree(delegation);
++}
++
++static void nfs_delegation_claim_opens(struct inode *inode)
++{
++ struct nfs_inode *nfsi = NFS_I(inode);
++ struct nfs_open_context *ctx;
++ struct nfs4_state *state;
++
++again:
++ spin_lock(&inode->i_lock);
++ list_for_each_entry(ctx, &nfsi->open_files, list) {
++ state = ctx->state;
++ if (state == NULL)
++ continue;
++ if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
++ continue;
++ get_nfs_open_context(ctx);
++ spin_unlock(&inode->i_lock);
++ if (nfs4_open_delegation_recall(ctx->dentry, state) < 0)
++ return;
++ put_nfs_open_context(ctx);
++ goto again;
++ }
++ spin_unlock(&inode->i_lock);
++}
++
++/*
++ * Set up a delegation on an inode
++ */
++void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
++{
++ struct nfs_delegation *delegation = NFS_I(inode)->delegation;
++
++ if (delegation == NULL)
++ return;
++ memcpy(delegation->stateid.data, res->delegation.data,
++ sizeof(delegation->stateid.data));
++ delegation->type = res->delegation_type;
++ delegation->maxsize = res->maxsize;
++ put_rpccred(cred);
++ delegation->cred = get_rpccred(cred);
++ delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
++ NFS_I(inode)->delegation_state = delegation->type;
++ wmb();
++}
++
++/*
++ * Set up a delegation on an inode
++ */
++int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
++{
++ struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
++ struct nfs_inode *nfsi = NFS_I(inode);
++ struct nfs_delegation *delegation;
++ int status = 0;
++
++ delegation = nfs_alloc_delegation();
++ if (delegation == NULL)
++ return -ENOMEM;
++ memcpy(delegation->stateid.data, res->delegation.data,
++ sizeof(delegation->stateid.data));
++ delegation->type = res->delegation_type;
++ delegation->maxsize = res->maxsize;
++ delegation->cred = get_rpccred(cred);
++ delegation->inode = inode;
++
++ spin_lock(&clp->cl_lock);
++ if (nfsi->delegation == NULL) {
++ list_add(&delegation->super_list, &clp->cl_delegations);
++ nfsi->delegation = delegation;
++ nfsi->delegation_state = delegation->type;
++ delegation = NULL;
++ } else {
++ if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
++ sizeof(delegation->stateid)) != 0 ||
++ delegation->type != nfsi->delegation->type) {
++ printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
++ __FUNCTION__, NIPQUAD(clp->cl_addr));
++ status = -EIO;
++ }
++ }
++ spin_unlock(&clp->cl_lock);
++ if (delegation != NULL)
++ kfree(delegation);
++ return status;
++}
++
++static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
++{
++ int res = 0;
++
++ __nfs_revalidate_inode(NFS_SERVER(inode), inode);
++
++ res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
++ nfs_free_delegation(delegation);
++ return res;
++}
++
++/* Sync all data to disk upon delegation return */
++static void nfs_msync_inode(struct inode *inode)
++{
++ down(&inode->i_sem);
++ filemap_fdatawrite(inode->i_mapping);
++ nfs_wb_all(inode);
++ filemap_fdatawait(inode->i_mapping);
++ up(&inode->i_sem);
++}
++
++/*
++ * Basic procedure for returning a delegation to the server
++ */
++int nfs_inode_return_delegation(struct inode *inode)
++{
++ struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
++ struct nfs_inode *nfsi = NFS_I(inode);
++ struct nfs_delegation *delegation;
++ int res = 0;
++
++ nfs_msync_inode(inode);
++ down_read(&clp->cl_sem);
++ /* Guard against new delegated open calls */
++ down_write(&nfsi->rwsem);
++ spin_lock(&clp->cl_lock);
++ delegation = nfsi->delegation;
++ if (delegation != NULL) {
++ list_del_init(&delegation->super_list);
++ nfsi->delegation = NULL;
++ nfsi->delegation_state = 0;
++ }
++ spin_unlock(&clp->cl_lock);
++ nfs_delegation_claim_opens(inode);
++ up_write(&nfsi->rwsem);
++ up_read(&clp->cl_sem);
++ nfs_msync_inode(inode);
++
++ if (delegation != NULL)
++ res = nfs_do_return_delegation(inode, delegation);
++ return res;
++}
++
++/*
++ * Return all delegations associated to a super block
++ */
++void nfs_return_all_delegations(struct super_block *sb)
++{
++ struct nfs4_client *clp = NFS_SB(sb)->nfs4_state;
++ struct nfs_delegation *delegation;
++ struct inode *inode;
++
++ if (clp == NULL)
++ return;
++restart:
++ spin_lock(&clp->cl_lock);
++ list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
++ if (delegation->inode->i_sb != sb)
++ continue;
++ inode = igrab(delegation->inode);
++ if (inode == NULL)
++ continue;
++ spin_unlock(&clp->cl_lock);
++ nfs_inode_return_delegation(inode);
++ iput(inode);
++ goto 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;
++ nfsi->delegation_state = 0;
++ args->result = 0;
++ } else {
++ delegation = NULL;
++ args->result = -ENOENT;
++ }
++ spin_unlock(&clp->cl_lock);
++ complete(&args->started);
++ nfs_delegation_claim_opens(inode);
++ 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
++ */
++struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle)
++{
++ struct nfs_delegation *delegation;
++ struct inode *res = NULL;
++ spin_lock(&clp->cl_lock);
++ list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
++ if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
++ res = igrab(delegation->inode);
++ break;
++ }
++ }
++ spin_unlock(&clp->cl_lock);
++ return res;
++}
++
++/*
++ * Mark all delegations as needing to be reclaimed
++ */
++void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
++{
++ struct nfs_delegation *delegation;
++ spin_lock(&clp->cl_lock);
++ list_for_each_entry(delegation, &clp->cl_delegations, super_list)
++ delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
++ spin_unlock(&clp->cl_lock);
++}
++
++/*
++ * Reap all unclaimed delegations after reboot recovery is done
++ */
++void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
++{
++ struct nfs_delegation *delegation, *n;
++ LIST_HEAD(head);
++ spin_lock(&clp->cl_lock);
++ list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) {
++ if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
++ continue;
++ list_move(&delegation->super_list, &head);
++ NFS_I(delegation->inode)->delegation = NULL;
++ NFS_I(delegation->inode)->delegation_state = 0;
++ }
++ spin_unlock(&clp->cl_lock);
++ while(!list_empty(&head)) {
++ delegation = list_entry(head.next, struct nfs_delegation, super_list);
++ list_del(&delegation->super_list);
++ nfs_free_delegation(delegation);
++ }
++}
+--- linux-2.6.7/fs/nfs/delegation.h.lsec 2005-03-23 14:28:22.546622088 -0700
++++ linux-2.6.7/fs/nfs/delegation.h 2005-03-23 14:28:22.546622088 -0700
+@@ -0,0 +1,56 @@
++/*
++ * linux/fs/nfs/delegation.h
++ *
++ * Copyright (c) Trond Myklebust
++ *
++ * Definitions pertaining to NFS delegated files
++ */
++#ifndef FS_NFS_DELEGATION_H
++#define FS_NFS_DELEGATION_H
++
++#if defined(CONFIG_NFS_V4)
++/*
++ * NFSv4 delegation
++ */
++struct nfs_delegation {
++ struct list_head super_list;
++ struct rpc_cred *cred;
++ struct inode *inode;
++ nfs4_stateid stateid;
++ int type;
++#define NFS_DELEGATION_NEED_RECLAIM 1
++ long flags;
++ loff_t maxsize;
++};
++
++int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
++void nfs_inode_reclaim_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);
++
++void nfs_delegation_mark_reclaim(struct nfs4_client *clp);
++void nfs_delegation_reap_unclaimed(struct nfs4_client *clp);
++
++/* NFSv4 delegation-related procedures */
++int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);
++int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state);
++
++static inline int nfs_have_delegation(struct inode *inode, int flags)
++{
++ flags &= FMODE_READ|FMODE_WRITE;
++ rmb();
++ if ((NFS_I(inode)->delegation_state & flags) == flags)
++ return 1;
++ return 0;
++}
++#else
++static inline int nfs_have_delegation(struct inode *inode, int flags)
++{
++ return 0;
++}
++#endif
++
++#endif
+--- linux-2.6.7/fs/nfs/nfs3proc.c.lsec 2004-06-15 23:19:23.000000000 -0600
++++ linux-2.6.7/fs/nfs/nfs3proc.c 2005-03-23 14:28:22.820580440 -0700
+@@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_tas
+ return 1;
+ }
+
+-static struct rpc_cred *
+-nfs_cred(struct inode *inode, struct file *filp)
+-{
+- struct rpc_cred *cred = NULL;
+-
+- if (filp)
+- cred = (struct rpc_cred *)filp->private_data;
+- if (!cred)
+- cred = NFS_I(inode)->mm_cred;
+- return cred;
+-}
+-
+ /*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+@@ -164,8 +152,7 @@ nfs3_proc_lookup(struct inode *dir, stru
+ return status;
+ }
+
+-static int
+-nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
++static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
+ {
+ struct nfs_fattr fattr;
+ struct nfs3_accessargs arg = {
+@@ -178,9 +165,10 @@ nfs3_proc_access(struct inode *inode, st
+ .rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS],
+ .rpc_argp = &arg,
+ .rpc_resp = &res,
+- .rpc_cred = cred
++ .rpc_cred = entry->cred
+ };
+- int status;
++ int mode = entry->mask;
++ int status;
+
+ dprintk("NFS call access\n");
+ fattr.valid = 0;
+@@ -200,10 +188,16 @@ nfs3_proc_access(struct inode *inode, st
+ }
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ nfs_refresh_inode(inode, &fattr);
+- dprintk("NFS reply access\n");
+-
+- if (status == 0 && (arg.access & res.access) != arg.access)
+- status = -EACCES;
++ if (status == 0) {
++ entry->mask = 0;
++ if (res.access & NFS3_ACCESS_READ)
++ entry->mask |= MAY_READ;
++ if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
++ entry->mask |= MAY_WRITE;
++ if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
++ entry->mask |= MAY_EXEC;
++ }
++ dprintk("NFS reply access, status = %d\n", status);
+ return status;
+ }
+
+@@ -227,8 +221,7 @@ nfs3_proc_readlink(struct inode *inode,
+ return status;
+ }
+
+-static int
+-nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
++static int nfs3_proc_read(struct nfs_read_data *rdata)
+ {
+ int flags = rdata->flags;
+ struct inode * inode = rdata->inode;
+@@ -237,13 +230,13 @@ nfs3_proc_read(struct nfs_read_data *rda
+ .rpc_proc = &nfs3_procedures[NFS3PROC_READ],
+ .rpc_argp = &rdata->args,
+ .rpc_resp = &rdata->res,
++ .rpc_cred = rdata->cred,
+ };
+ int status;
+
+ dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
+ (long long) rdata->args.offset);
+ fattr->valid = 0;
+- msg.rpc_cred = nfs_cred(inode, filp);
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+ if (status >= 0)
+ nfs_refresh_inode(inode, fattr);
+@@ -251,8 +244,7 @@ nfs3_proc_read(struct nfs_read_data *rda
+ return status;
+ }
+
+-static int
+-nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
++static int nfs3_proc_write(struct nfs_write_data *wdata)
+ {
+ int rpcflags = wdata->flags;
+ struct inode * inode = wdata->inode;
+@@ -261,13 +253,13 @@ nfs3_proc_write(struct nfs_write_data *w
+ .rpc_proc = &nfs3_procedures[NFS3PROC_WRITE],
+ .rpc_argp = &wdata->args,
+ .rpc_resp = &wdata->res,
++ .rpc_cred = wdata->cred,
+ };
+ int status;
+
+ dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
+ (long long) wdata->args.offset);
+ fattr->valid = 0;
+- msg.rpc_cred = nfs_cred(inode, filp);
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
+ if (status >= 0)
+ nfs_refresh_inode(inode, fattr);
+@@ -275,8 +267,7 @@ nfs3_proc_write(struct nfs_write_data *w
+ return status < 0? status : wdata->res.count;
+ }
+
+-static int
+-nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
++static int nfs3_proc_commit(struct nfs_write_data *cdata)
+ {
+ struct inode * inode = cdata->inode;
+ struct nfs_fattr * fattr = cdata->res.fattr;
+@@ -284,13 +275,13 @@ nfs3_proc_commit(struct nfs_write_data *
+ .rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT],
+ .rpc_argp = &cdata->args,
+ .rpc_resp = &cdata->res,
++ .rpc_cred = cdata->cred,
+ };
+ int status;
+
+ dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
+ (long long) cdata->args.offset);
+ fattr->valid = 0;
+- msg.rpc_cred = nfs_cred(inode, filp);
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ if (status >= 0)
+ nfs_refresh_inode(inode, fattr);
+@@ -534,6 +525,8 @@ nfs3_proc_symlink(struct inode *dir, str
+ };
+ int status;
+
++ if (path->len > NFS3_MAXPATHLEN)
++ return -ENAMETOOLONG;
+ dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+@@ -832,27 +825,6 @@ nfs3_proc_commit_setup(struct nfs_write_
+ rpc_call_setup(task, &msg, 0);
+ }
+
+-/*
+- * Set up the nfspage struct with the right credentials
+- */
+-void
+-nfs3_request_init(struct nfs_page *req, struct file *filp)
+-{
+- req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
+-}
+-
+-static int
+-nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
+-{
+- if (req->wb_file != filp)
+- return 0;
+- if (req->wb_page != page)
+- return 0;
+- if (req->wb_cred != nfs_file_cred(filp))
+- return 0;
+- return 1;
+-}
+-
+ static int
+ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
+ {
+@@ -863,6 +835,7 @@ struct nfs_rpc_ops nfs_v3_clientops = {
+ .version = 3, /* protocol version */
+ .dentry_ops = &nfs_dentry_operations,
+ .dir_inode_ops = &nfs_dir_inode_operations,
++ .file_inode_ops = &nfs_file_inode_operations,
+ .getroot = nfs3_proc_get_root,
+ .getattr = nfs3_proc_getattr,
+ .setattr = nfs3_proc_setattr,
+@@ -892,7 +865,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
+ .commit_setup = nfs3_proc_commit_setup,
+ .file_open = nfs_open,
+ .file_release = nfs_release,
+- .request_init = nfs3_request_init,
+- .request_compatible = nfs3_request_compatible,
+ .lock = nfs3_proc_lock,
+ };
+--- linux-2.6.7/fs/nfs/proc.c.lsec 2004-06-15 23:20:03.000000000 -0600
++++ linux-2.6.7/fs/nfs/proc.c 2005-03-23 14:28:23.058544264 -0700
+@@ -49,18 +49,6 @@
+
+ extern struct rpc_procinfo nfs_procedures[];
+
+-static struct rpc_cred *
+-nfs_cred(struct inode *inode, struct file *filp)
+-{
+- struct rpc_cred *cred = NULL;
+-
+- if (filp)
+- cred = (struct rpc_cred *)filp->private_data;
+- if (!cred)
+- cred = NFS_I(inode)->mm_cred;
+- return cred;
+-}
+-
+ /*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+@@ -167,8 +155,7 @@ nfs_proc_readlink(struct inode *inode, s
+ return status;
+ }
+
+-static int
+-nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
++static int nfs_proc_read(struct nfs_read_data *rdata)
+ {
+ int flags = rdata->flags;
+ struct inode * inode = rdata->inode;
+@@ -177,15 +164,14 @@ nfs_proc_read(struct nfs_read_data *rdat
+ .rpc_proc = &nfs_procedures[NFSPROC_READ],
+ .rpc_argp = &rdata->args,
+ .rpc_resp = &rdata->res,
++ .rpc_resp = rdata->cred,
+ };
+ int status;
+
+ dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
+ (long long) rdata->args.offset);
+ fattr->valid = 0;
+- msg.rpc_cred = nfs_cred(inode, filp);
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+-
+ if (status >= 0) {
+ nfs_refresh_inode(inode, fattr);
+ /* Emulate the eof flag, which isn't normally needed in NFSv2
+@@ -198,8 +184,7 @@ nfs_proc_read(struct nfs_read_data *rdat
+ return status;
+ }
+
+-static int
+-nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
++static int nfs_proc_write(struct nfs_write_data *wdata)
+ {
+ int flags = wdata->flags;
+ struct inode * inode = wdata->inode;
+@@ -208,13 +193,13 @@ nfs_proc_write(struct nfs_write_data *wd
+ .rpc_proc = &nfs_procedures[NFSPROC_WRITE],
+ .rpc_argp = &wdata->args,
+ .rpc_resp = &wdata->res,
++ .rpc_resp = wdata->cred,
+ };
+ int status;
+
+ dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
+ (long long) wdata->args.offset);
+ fattr->valid = 0;
+- msg.rpc_cred = nfs_cred(inode, filp);
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+ if (status >= 0) {
+ nfs_refresh_inode(inode, fattr);
+@@ -400,6 +385,8 @@ nfs_proc_symlink(struct inode *dir, stru
+ };
+ int status;
+
++ if (path->len > NFS2_MAXPATHLEN)
++ return -ENAMETOOLONG;
+ dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
+@@ -619,27 +606,6 @@ nfs_proc_commit_setup(struct nfs_write_d
+ BUG();
+ }
+
+-/*
+- * Set up the nfspage struct with the right credentials
+- */
+-static void
+-nfs_request_init(struct nfs_page *req, struct file *filp)
+-{
+- req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
+-}
+-
+-static int
+-nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
+-{
+- if (req->wb_file != filp)
+- return 0;
+- if (req->wb_page != page)
+- return 0;
+- if (req->wb_cred != nfs_file_cred(filp))
+- return 0;
+- return 1;
+-}
+-
+ static int
+ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
+ {
+@@ -651,6 +617,7 @@ struct nfs_rpc_ops nfs_v2_clientops = {
+ .version = 2, /* protocol version */
+ .dentry_ops = &nfs_dentry_operations,
+ .dir_inode_ops = &nfs_dir_inode_operations,
++ .file_inode_ops = &nfs_file_inode_operations,
+ .getroot = nfs_proc_get_root,
+ .getattr = nfs_proc_getattr,
+ .setattr = nfs_proc_setattr,
+@@ -680,7 +647,5 @@ struct nfs_rpc_ops nfs_v2_clientops = {
+ .commit_setup = nfs_proc_commit_setup,
+ .file_open = nfs_open,
+ .file_release = nfs_release,
+- .request_init = nfs_request_init,
+- .request_compatible = nfs_request_compatible,
+ .lock = nfs_proc_lock,
+ };
+--- linux-2.6.7/fs/nfs/file.c.lsec 2004-06-15 23:19:37.000000000 -0600
++++ linux-2.6.7/fs/nfs/file.c 2005-03-23 14:28:22.760589560 -0700
+@@ -31,6 +31,8 @@
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+
++#include "delegation.h"
++
+ #define NFSDBG_FACILITY NFSDBG_FILE
+
+ static long nfs_file_fcntl(int fd, unsigned int cmd,
+@@ -66,6 +68,19 @@ struct inode_operations nfs_file_inode_o
+ .setattr = nfs_setattr,
+ };
+
++#ifdef CONFIG_NFS_V4
++
++struct inode_operations nfs4_file_inode_operations = {
++ .permission = nfs_permission,
++ .getattr = nfs_getattr,
++ .setattr = nfs_setattr,
++ .getxattr = nfs_getxattr,
++ .setxattr = nfs_setxattr,
++ .listxattr = nfs_listxattr,
++};
++
++#endif /* CONFIG_NFS_V4 */
++
+ /* Hack for future NFS swap support */
+ #ifndef IS_SWAPFILE
+ # define IS_SWAPFILE(inode) (0)
+@@ -127,6 +142,7 @@ nfs_file_release(struct inode *inode, st
+ static int
+ nfs_file_flush(struct file *file)
+ {
++ struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+ struct inode *inode = file->f_dentry->d_inode;
+ int status;
+
+@@ -138,9 +154,9 @@ nfs_file_flush(struct file *file)
+ /* Ensure that data+attribute caches are up to date after close() */
+ status = nfs_wb_all(inode);
+ if (!status) {
+- status = file->f_error;
+- file->f_error = 0;
+- if (!status)
++ status = ctx->error;
++ ctx->error = 0;
++ if (!status && !nfs_have_delegation(inode, FMODE_READ))
+ __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ }
+ unlock_kernel();
+@@ -211,6 +227,7 @@ nfs_file_mmap(struct file * file, struct
+ static int
+ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+ {
++ struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+ struct inode *inode = dentry->d_inode;
+ int status;
+
+@@ -219,8 +236,8 @@ nfs_fsync(struct file *file, struct dent
+ lock_kernel();
+ status = nfs_wb_all(inode);
+ if (!status) {
+- status = file->f_error;
+- file->f_error = 0;
++ status = ctx->error;
++ ctx->error = 0;
+ }
+ unlock_kernel();
+ return status;
+@@ -302,6 +319,90 @@ out_swapfile:
+ goto out;
+ }
+
++static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
++{
++ struct inode *inode = filp->f_mapping->host;
++ int status;
++
++ lock_kernel();
++ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
++ unlock_kernel();
++ return status;
++}
++
++static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
++{
++ struct inode *inode = filp->f_mapping->host;
++ sigset_t oldset;
++ int status;
++
++ rpc_clnt_sigmask(NFS_CLIENT(inode), &oldset);
++ /*
++ * Flush all pending writes before doing anything
++ * with locks..
++ */
++ filemap_fdatawrite(filp->f_mapping);
++ down(&inode->i_sem);
++ nfs_wb_all(inode);
++ up(&inode->i_sem);
++ filemap_fdatawait(filp->f_mapping);
++
++ /* NOTE: special case
++ * If we're signalled while cleaning up locks on process exit, we
++ * still need to complete the unlock.
++ */
++ lock_kernel();
++ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
++ rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
++ return status;
++}
++
++static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
++{
++ struct inode *inode = filp->f_mapping->host;
++ int status;
++
++ /*
++ * Flush all pending writes before doing anything
++ * with locks..
++ */
++ status = filemap_fdatawrite(filp->f_mapping);
++ if (status == 0) {
++ down(&inode->i_sem);
++ status = nfs_wb_all(inode);
++ up(&inode->i_sem);
++ if (status == 0)
++ status = filemap_fdatawait(filp->f_mapping);
++ }
++ if (status < 0)
++ return status;
++
++ lock_kernel();
++ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
++ /* If we were signalled we still need to ensure that
++ * we clean up any state on the server. We therefore
++ * record the lock call as having succeeded in order to
++ * ensure that locks_remove_posix() cleans it out when
++ * the process exits.
++ */
++ if (status == -EINTR || status == -ERESTARTSYS)
++ posix_lock_file(filp, fl);
++ unlock_kernel();
++ if (status < 0)
++ return status;
++ /*
++ * Make sure we clear the cache whenever we try to get the lock.
++ * This makes locking act as a cache coherency point.
++ */
++ filemap_fdatawrite(filp->f_mapping);
++ down(&inode->i_sem);
++ nfs_wb_all(inode); /* we may have slept */
++ up(&inode->i_sem);
++ filemap_fdatawait(filp->f_mapping);
++ nfs_zap_caches(inode);
++ return 0;
++}
++
+ /*
+ * Lock a (portion of) a file
+ */
+@@ -309,8 +410,6 @@ int
+ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+ {
+ struct inode * inode = filp->f_mapping->host;
+- int status = 0;
+- int status2;
+
+ dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
+ inode->i_sb->s_id, inode->i_ino,
+@@ -328,8 +427,8 @@ nfs_lock(struct file *filp, int cmd, str
+ /* Fake OK code if mounted without NLM support */
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
+ if (IS_GETLK(cmd))
+- status = LOCK_USE_CLNT;
+- goto out_ok;
++ return LOCK_USE_CLNT;
++ return 0;
+ }
+ }
+
+@@ -340,45 +439,12 @@ nfs_lock(struct file *filp, int cmd, str
+ * Not sure whether that would be unique, though, or whether
+ * that would break in other places.
+ */
+- if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX))
++ if (!(fl->fl_flags & FL_POSIX))
+ return -ENOLCK;
+
+- /*
+- * Flush all pending writes before doing anything
+- * with locks..
+- */
+- status = filemap_fdatawrite(filp->f_mapping);
+- down(&inode->i_sem);
+- status2 = nfs_wb_all(inode);
+- if (!status)
+- status = status2;
+- up(&inode->i_sem);
+- status2 = filemap_fdatawait(filp->f_mapping);
+- if (!status)
+- status = status2;
+- if (status < 0)
+- return status;
+-
+- lock_kernel();
+- status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+- unlock_kernel();
+- if (status < 0)
+- return status;
+-
+- status = 0;
+-
+- /*
+- * Make sure we clear the cache whenever we try to get the lock.
+- * This makes locking act as a cache coherency point.
+- */
+- out_ok:
+- if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
+- filemap_fdatawrite(filp->f_mapping);
+- down(&inode->i_sem);
+- nfs_wb_all(inode); /* we may have slept */
+- up(&inode->i_sem);
+- filemap_fdatawait(filp->f_mapping);
+- nfs_zap_caches(inode);
+- }
+- return status;
++ if (IS_GETLK(cmd))
++ return do_getlk(filp, cmd, fl);
++ if (fl->fl_type == F_UNLCK)
++ return do_unlk(filp, cmd, fl);
++ return do_setlk(filp, cmd, fl);
+ }
+--- linux-2.6.7/fs/nfs/write.c.lsec 2004-06-15 23:19:43.000000000 -0600
++++ linux-2.6.7/fs/nfs/write.c 2005-03-23 14:28:23.225518880 -0700
+@@ -63,6 +63,8 @@
+ #include <linux/smp_lock.h>
+ #include <linux/mempool.h>
+
++#include "delegation.h"
++
+ #define NFSDBG_FACILITY NFSDBG_PAGECACHE
+
+ #define MIN_POOL_WRITE (32)
+@@ -71,7 +73,8 @@
+ /*
+ * Local function declarations
+ */
+-static struct nfs_page * nfs_update_request(struct file*, struct inode *,
++static struct nfs_page * nfs_update_request(struct nfs_open_context*,
++ struct inode *,
+ struct page *,
+ unsigned int, unsigned int);
+ static void nfs_writeback_done_partial(struct nfs_write_data *, int);
+@@ -173,7 +176,7 @@ static void nfs_mark_uptodate(struct pag
+ * Write a page synchronously.
+ * Offset is the data offset within the page.
+ */
+-static int nfs_writepage_sync(struct file *file, struct inode *inode,
++static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
+ struct page *page, unsigned int offset, unsigned int count,
+ int how)
+ {
+@@ -187,9 +190,10 @@ static int nfs_writepage_sync(struct fil
+
+ memset(wdata, 0, sizeof(*wdata));
+ wdata->flags = how;
++ wdata->cred = ctx->cred;
+ wdata->inode = inode;
+ wdata->args.fh = NFS_FH(inode);
+- wdata->args.lockowner = current->files;
++ wdata->args.context = ctx;
+ wdata->args.pages = &page;
+ wdata->args.stable = NFS_FILE_SYNC;
+ wdata->args.pgbase = offset;
+@@ -208,7 +212,7 @@ static int nfs_writepage_sync(struct fil
+ wdata->args.count = count;
+ wdata->args.offset = page_offset(page) + wdata->args.pgbase;
+
+- result = NFS_PROTO(inode)->write(wdata, file);
++ result = NFS_PROTO(inode)->write(wdata);
+
+ if (result < 0) {
+ /* Must mark the page invalid after I/O error */
+@@ -241,13 +245,14 @@ io_error:
+ return written ? written : result;
+ }
+
+-static int nfs_writepage_async(struct file *file, struct inode *inode,
+- struct page *page, unsigned int offset, unsigned int count)
++static int nfs_writepage_async(struct nfs_open_context *ctx,
++ struct inode *inode, struct page *page,
++ unsigned int offset, unsigned int count)
+ {
+ struct nfs_page *req;
+ int status;
+
+- req = nfs_update_request(file, inode, page, offset, count);
++ req = nfs_update_request(ctx, inode, page, offset, count);
+ status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+ if (status < 0)
+ goto out;
+@@ -274,6 +279,7 @@ static int wb_priority(struct writeback_
+ */
+ int nfs_writepage(struct page *page, struct writeback_control *wbc)
+ {
++ struct nfs_open_context *ctx;
+ struct inode *inode = page->mapping->host;
+ unsigned long end_index;
+ unsigned offset = PAGE_CACHE_SIZE;
+@@ -308,16 +314,21 @@ int nfs_writepage(struct page *page, str
+ if (page->index >= end_index+1 || !offset)
+ goto out;
+ do_it:
++ ctx = nfs_find_open_context(inode, FMODE_WRITE);
++ if (ctx == NULL) {
++ err = -EBADF;
++ goto out;
++ }
+ lock_kernel();
+ if (!IS_SYNC(inode) && inode_referenced) {
+- err = nfs_writepage_async(NULL, inode, page, 0, offset);
++ err = nfs_writepage_async(ctx, inode, page, 0, offset);
+ if (err >= 0) {
+ err = 0;
+ if (wbc->for_reclaim)
+ nfs_flush_inode(inode, 0, 0, FLUSH_STABLE);
+ }
+ } else {
+- err = nfs_writepage_sync(NULL, inode, page, 0,
++ err = nfs_writepage_sync(ctx, inode, page, 0,
+ offset, priority);
+ if (err >= 0) {
+ if (err != offset)
+@@ -326,6 +337,7 @@ do_it:
+ }
+ }
+ unlock_kernel();
++ put_nfs_open_context(ctx);
+ out:
+ unlock_page(page);
+ if (inode_referenced)
+@@ -374,8 +386,7 @@ out:
+ /*
+ * Insert a write request into an inode
+ */
+-static inline int
+-nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
++static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
+ {
+ struct nfs_inode *nfsi = NFS_I(inode);
+ int error;
+@@ -387,6 +398,8 @@ nfs_inode_add_request(struct inode *inod
+ if (!nfsi->npages) {
+ igrab(inode);
+ nfs_begin_data_update(inode);
++ if (nfs_have_delegation(inode, FMODE_WRITE))
++ nfsi->change_attr++;
+ }
+ nfsi->npages++;
+ req->wb_count++;
+@@ -404,7 +417,7 @@ nfs_inode_remove_request(struct nfs_page
+
+ BUG_ON (!NFS_WBACK_BUSY(req));
+ spin_lock(&nfs_wreq_lock);
+- inode = req->wb_inode;
++ inode = req->wb_context->dentry->d_inode;
+ nfsi = NFS_I(inode);
+ radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
+ nfsi->npages--;
+@@ -450,7 +463,7 @@ nfs_find_request(struct inode *inode, un
+ static void
+ nfs_mark_request_dirty(struct nfs_page *req)
+ {
+- struct inode *inode = req->wb_inode;
++ struct inode *inode = req->wb_context->dentry->d_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ spin_lock(&nfs_wreq_lock);
+@@ -467,7 +480,7 @@ nfs_mark_request_dirty(struct nfs_page *
+ static inline int
+ nfs_dirty_request(struct nfs_page *req)
+ {
+- struct nfs_inode *nfsi = NFS_I(req->wb_inode);
++ struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode);
+ return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty;
+ }
+
+@@ -478,7 +491,7 @@ nfs_dirty_request(struct nfs_page *req)
+ static void
+ nfs_mark_request_commit(struct nfs_page *req)
+ {
+- struct inode *inode = req->wb_inode;
++ struct inode *inode = req->wb_context->dentry->d_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ spin_lock(&nfs_wreq_lock);
+@@ -619,9 +632,9 @@ static int nfs_wait_on_write_congestion(
+ *
+ * Note: Should always be called with the Page Lock held!
+ */
+-static struct nfs_page *
+-nfs_update_request(struct file* file, struct inode *inode, struct page *page,
+- unsigned int offset, unsigned int bytes)
++static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
++ struct inode *inode, struct page *page,
++ unsigned int offset, unsigned int bytes)
+ {
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_page *req, *new = NULL;
+@@ -668,13 +681,9 @@ nfs_update_request(struct file* file, st
+ }
+ spin_unlock(&nfs_wreq_lock);
+
+- new = nfs_create_request(file, inode, page, offset, bytes);
++ new = nfs_create_request(ctx, inode, page, offset, bytes);
+ if (IS_ERR(new))
+ return new;
+- if (file) {
+- new->wb_file = file;
+- get_file(file);
+- }
+ }
+
+ /* We have a request for our page.
+@@ -684,7 +693,7 @@ nfs_update_request(struct file* file, st
+ * request.
+ */
+ rqend = req->wb_offset + req->wb_bytes;
+- if (req->wb_file != file
++ if (req->wb_context != ctx
+ || req->wb_page != page
+ || !nfs_dirty_request(req)
+ || offset > rqend || end < req->wb_offset) {
+@@ -705,9 +714,9 @@ nfs_update_request(struct file* file, st
+ return req;
+ }
+
+-int
+-nfs_flush_incompatible(struct file *file, struct page *page)
++int nfs_flush_incompatible(struct file *file, struct page *page)
+ {
++ struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+ struct inode *inode = page->mapping->host;
+ struct nfs_page *req;
+ int status = 0;
+@@ -721,7 +730,7 @@ nfs_flush_incompatible(struct file *file
+ */
+ req = nfs_find_request(inode, page->index);
+ if (req) {
+- if (!NFS_PROTO(inode)->request_compatible(req, file, page))
++ if (req->wb_page != page || ctx != req->wb_context)
+ status = nfs_wb_page(inode, page);
+ nfs_release_request(req);
+ }
+@@ -737,6 +746,7 @@ nfs_flush_incompatible(struct file *file
+ int nfs_updatepage(struct file *file, struct page *page,
+ unsigned int offset, unsigned int count)
+ {
++ struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = page->mapping->host;
+ struct nfs_page *req;
+@@ -747,7 +757,7 @@ int nfs_updatepage(struct file *file, st
+ count, (long long)(page_offset(page) +offset));
+
+ if (IS_SYNC(inode)) {
+- status = nfs_writepage_sync(file, inode, page, offset, count, 0);
++ status = nfs_writepage_sync(ctx, inode, page, offset, count, 0);
+ if (status > 0) {
+ if (offset == 0 && status == PAGE_CACHE_SIZE)
+ SetPageUptodate(page);
+@@ -784,7 +794,7 @@ int nfs_updatepage(struct file *file, st
+ * it out now.
+ */
+ do {
+- req = nfs_update_request(file, inode, page, offset, count);
++ req = nfs_update_request(ctx, inode, page, offset, count);
+ status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+ if (status != -EBUSY)
+ break;
+@@ -860,16 +870,15 @@ static void nfs_write_rpcsetup(struct nf
+ * NB: take care not to mess about with data->commit et al. */
+
+ data->req = req;
+- data->inode = inode = req->wb_inode;
+- data->cred = req->wb_cred;
++ data->inode = inode = req->wb_context->dentry->d_inode;
++ data->cred = req->wb_context->cred;
+
+ data->args.fh = NFS_FH(inode);
+ data->args.offset = req_offset(req) + offset;
+ data->args.pgbase = req->wb_pgbase + offset;
+ data->args.pages = data->pagevec;
+ data->args.count = count;
+- data->args.lockowner = req->wb_lockowner;
+- data->args.state = req->wb_state;
++ data->args.context = req->wb_context;
+
+ data->res.fattr = &data->fattr;
+ data->res.count = count;
+@@ -1029,7 +1038,7 @@ nfs_flush_list(struct list_head *head, i
+ while (!list_empty(head)) {
+ pages += nfs_coalesce_requests(head, &one_request, wpages);
+ req = nfs_list_entry(one_request.next);
+- error = nfs_flush_one(&one_request, req->wb_inode, how);
++ error = nfs_flush_one(&one_request, req->wb_context->dentry->d_inode, how);
+ if (error < 0)
+ break;
+ }
+@@ -1054,16 +1063,15 @@ static void nfs_writeback_done_partial(s
+ struct page *page = req->wb_page;
+
+ dprintk("NFS: write (%s/%Ld %d@%Ld)",
+- req->wb_inode->i_sb->s_id,
+- (long long)NFS_FILEID(req->wb_inode),
++ req->wb_context->dentry->d_inode->i_sb->s_id,
++ (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+ req->wb_bytes,
+ (long long)req_offset(req));
+
+ if (status < 0) {
+ ClearPageUptodate(page);
+ SetPageError(page);
+- if (req->wb_file)
+- req->wb_file->f_error = status;
++ req->wb_context->error = status;
+ dprintk(", error = %d\n", status);
+ } else {
+ #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+@@ -1104,16 +1112,15 @@ static void nfs_writeback_done_full(stru
+ page = req->wb_page;
+
+ dprintk("NFS: write (%s/%Ld %d@%Ld)",
+- req->wb_inode->i_sb->s_id,
+- (long long)NFS_FILEID(req->wb_inode),
++ req->wb_context->dentry->d_inode->i_sb->s_id,
++ (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+ req->wb_bytes,
+ (long long)req_offset(req));
+
+ if (status < 0) {
+ ClearPageUptodate(page);
+ SetPageError(page);
+- if (req->wb_file)
+- req->wb_file->f_error = status;
++ req->wb_context->error = status;
+ end_page_writeback(page);
+ nfs_inode_remove_request(req);
+ dprintk(", error = %d\n", status);
+@@ -1232,7 +1239,7 @@ static void nfs_commit_rpcsetup(struct l
+ list_splice_init(head, &data->pages);
+ first = nfs_list_entry(data->pages.next);
+ last = nfs_list_entry(data->pages.prev);
+- inode = first->wb_inode;
++ inode = first->wb_context->dentry->d_inode;
+
+ /*
+ * Determine the offset range of requests in the COMMIT call.
+@@ -1246,7 +1253,7 @@ static void nfs_commit_rpcsetup(struct l
+ len = 0;
+
+ data->inode = inode;
+- data->cred = first->wb_cred;
++ data->cred = first->wb_context->cred;
+
+ data->args.fh = NFS_FH(data->inode);
+ data->args.offset = start;
+@@ -1313,13 +1320,12 @@ nfs_commit_done(struct rpc_task *task)
+ nfs_list_remove_request(req);
+
+ dprintk("NFS: commit (%s/%Ld %d@%Ld)",
+- req->wb_inode->i_sb->s_id,
+- (long long)NFS_FILEID(req->wb_inode),
++ req->wb_context->dentry->d_inode->i_sb->s_id,
++ (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+ req->wb_bytes,
+ (long long)req_offset(req));
+ if (task->tk_status < 0) {
+- if (req->wb_file)
+- req->wb_file->f_error = task->tk_status;
++ req->wb_context->error = task->tk_status;
+ nfs_inode_remove_request(req);
+ dprintk(", error = %d\n", task->tk_status);
+ goto next;
+--- linux-2.6.7/fs/nfs/nfs4xdr.c.lsec 2004-06-15 23:20:26.000000000 -0600
++++ linux-2.6.7/fs/nfs/nfs4xdr.c 2005-03-23 14:28:23.056544568 -0700
+@@ -84,9 +84,13 @@ static int nfs_stat_to_errno(int);
+ ((3+NFS4_FHSIZE) >> 2))
+ #define encode_getattr_maxsz (op_encode_hdr_maxsz + 3)
+ #define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
++#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
+ #define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz)
+ #define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \
+ nfs4_fattr_bitmap_maxsz)
++#define encode_setattr_maxsz (op_decode_hdr_maxsz + 4 + \
++ nfs4_fattr_bitmap_maxsz)
++#define decode_setattr_maxsz (op_decode_hdr_maxsz + 3)
+ #define encode_savefh_maxsz (op_encode_hdr_maxsz)
+ #define decode_savefh_maxsz (op_decode_hdr_maxsz)
+ #define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2)
+@@ -118,10 +122,17 @@ static int nfs_stat_to_errno(int);
+ #define encode_link_maxsz (op_encode_hdr_maxsz + \
+ nfs4_name_maxsz)
+ #define decode_link_maxsz (op_decode_hdr_maxsz + 5)
++#define encode_symlink_maxsz (op_encode_hdr_maxsz + \
++ 1 + nfs4_name_maxsz + \
++ nfs4_path_maxsz + \
++ nfs4_fattr_bitmap_maxsz)
++#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8)
+ #define encode_create_maxsz (op_encode_hdr_maxsz + \
+- 2 + 2 * nfs4_name_maxsz + \
++ 2 + nfs4_name_maxsz + \
+ nfs4_fattr_bitmap_maxsz)
+ #define decode_create_maxsz (op_decode_hdr_maxsz + 8)
++#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4)
++#define decode_delegreturn_maxsz (op_decode_hdr_maxsz)
+ #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */
+ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */
+ #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \
+@@ -172,16 +183,14 @@ static int nfs_stat_to_errno(int);
+ #define NFS4_dec_open_confirm_sz (compound_decode_hdr_maxsz + \
+ decode_putfh_maxsz + \
+ op_decode_hdr_maxsz + 4)
+-#define NFS4_enc_open_reclaim_sz (compound_encode_hdr_maxsz + \
++#define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+ op_encode_hdr_maxsz + \
+- 11 + \
+- encode_getattr_maxsz)
+-#define NFS4_dec_open_reclaim_sz (compound_decode_hdr_maxsz + \
++ 11)
++#define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \
+ decode_putfh_maxsz + \
+ op_decode_hdr_maxsz + \
+- 4 + 5 + 2 + 3 + \
+- decode_getattr_maxsz)
++ 4 + 5 + 2 + 3)
+ #define NFS4_enc_open_downgrade_sz \
+ (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+@@ -313,6 +322,16 @@ static int nfs_stat_to_errno(int);
+ decode_savefh_maxsz + \
+ decode_putfh_maxsz + \
+ decode_link_maxsz)
++#define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \
++ encode_putfh_maxsz + \
++ encode_symlink_maxsz + \
++ encode_getattr_maxsz + \
++ encode_getfh_maxsz)
++#define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \
++ decode_putfh_maxsz + \
++ decode_symlink_maxsz + \
++ decode_getattr_maxsz + \
++ decode_getfh_maxsz)
+ #define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+ encode_create_maxsz + \
+@@ -339,6 +358,33 @@ static int nfs_stat_to_errno(int);
+ encode_getattr_maxsz)
+ #define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \
+ decode_getattr_maxsz)
++#define NFS4_enc_delegreturn_sz (compound_encode_hdr_maxsz + \
++ encode_putfh_maxsz + \
++ encode_delegreturn_maxsz)
++#define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
++ decode_delegreturn_maxsz)
++#define username_maxsz (1 + ((IDMAP_NAMESZ + 3) >> 2))
++/* XXX: fix ACL bounds */
++#define ace_maxsz (3 + username_maxsz)
++#define NFS_ACL_MAX_ENTRIES 32
++#define acl_maxentries ((NFS_ACL_MAX_ENTRIES - 3) * 3 + 6)
++#define acl_maxsz (1 + acl_maxentries * ace_maxsz)
++#define NFS4_enc_getacl_sz compound_encode_hdr_maxsz + \
++ encode_putfh_maxsz + \
++ encode_getattr_maxsz
++#define username_maxsz (1 + ((IDMAP_NAMESZ + 3) >> 2))
++#define ace_maxsz (3 + username_maxsz)
++#define acl_maxentries ((NFS_ACL_MAX_ENTRIES - 3) * 3 + 6)
++#define acl_maxsz (1 + acl_maxentries * ace_maxsz)
++#define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \
++ decode_putfh_maxsz + \
++ op_decode_hdr_maxsz + 3 + 1 + acl_maxsz)
++#define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \
++ encode_putfh_maxsz + \
++ op_encode_hdr_maxsz + 4 + 1 + acl_maxsz)
++#define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \
++ decode_putfh_maxsz + \
++ decode_setattr_maxsz)
+
+ static struct {
+ unsigned int mode;
+@@ -388,6 +434,15 @@ struct compound_hdr {
+ BUG_ON(!p); \
+ } while (0)
+
++static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
++{
++ uint32_t *p;
++
++ p = xdr_reserve_space(xdr, 4 + len);
++ BUG_ON(p == NULL);
++ xdr_encode_opaque(p, str, len);
++}
++
+ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+ {
+ uint32_t *p;
+@@ -402,6 +457,15 @@ static int encode_compound_hdr(struct xd
+ return 0;
+ }
+
++static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
++{
++ uint32_t *p;
++
++ p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
++ BUG_ON(p == NULL);
++ xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
++}
++
+ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+ {
+ char owner_name[IDMAP_NAMESZ];
+@@ -420,7 +484,7 @@ static int encode_attrs(struct xdr_strea
+ * In the worst-case, this would be
+ * 12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
+ * = 36 bytes, plus any contribution from variable-length fields
+- * such as owner/group/acl's.
++ * such as owner/group.
+ */
+ len = 16;
+
+@@ -742,19 +806,12 @@ static int encode_lookup(struct xdr_stre
+ return 0;
+ }
+
+-static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++static void encode_share_access(struct xdr_stream *xdr, int open_flags)
+ {
+- int status;
+ uint32_t *p;
+
+- /*
+- * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
+- * owner 4, opentype 4 = 36
+- */
+- RESERVE_SPACE(36);
+- WRITE32(OP_OPEN);
+- WRITE32(arg->seqid);
+- switch (arg->share_access) {
++ RESERVE_SPACE(8);
++ switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
+ case FMODE_READ:
+ WRITE32(NFS4_SHARE_ACCESS_READ);
+ break;
+@@ -767,84 +824,135 @@ static int encode_open(struct xdr_stream
+ default:
+ BUG();
+ }
+- WRITE32(0); /* for linux, share_deny = 0 always */
++ WRITE32(0); /* for linux, share_deny = 0 always */
++}
++
++static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++ uint32_t *p;
++ /*
++ * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
++ * owner 4 = 32
++ */
++ RESERVE_SPACE(8);
++ WRITE32(OP_OPEN);
++ WRITE32(arg->seqid);
++ encode_share_access(xdr, arg->open_flags);
++ RESERVE_SPACE(16);
+ WRITE64(arg->clientid);
+ WRITE32(4);
+ WRITE32(arg->id);
+- WRITE32(arg->opentype);
++}
+
+- if (arg->opentype == NFS4_OPEN_CREATE) {
+- if (arg->createmode == NFS4_CREATE_EXCLUSIVE) {
+- RESERVE_SPACE(12);
+- WRITE32(arg->createmode);
+- WRITEMEM(arg->u.verifier.data, sizeof(arg->u.verifier.data));
+- }
+- else if (arg->u.attrs) {
+- RESERVE_SPACE(4);
+- WRITE32(arg->createmode);
+- if ((status = encode_attrs(xdr, arg->u.attrs, arg->server)))
+- return status;
+- }
+- else {
+- RESERVE_SPACE(12);
+- WRITE32(arg->createmode);
+- WRITE32(0);
+- WRITE32(0);
+- }
++static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++ uint32_t *p;
++
++ RESERVE_SPACE(4);
++ switch(arg->open_flags & O_EXCL) {
++ case 0:
++ WRITE32(NFS4_CREATE_UNCHECKED);
++ encode_attrs(xdr, arg->u.attrs, arg->server);
++ break;
++ default:
++ WRITE32(NFS4_CREATE_EXCLUSIVE);
++ encode_nfs4_verifier(xdr, &arg->u.verifier);
+ }
++}
+
+- RESERVE_SPACE(8 + arg->name->len);
+- WRITE32(NFS4_OPEN_CLAIM_NULL);
+- WRITE32(arg->name->len);
+- WRITEMEM(arg->name->name, arg->name->len);
++static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++ uint32_t *p;
+
+- return 0;
++ RESERVE_SPACE(4);
++ switch (arg->open_flags & O_CREAT) {
++ case 0:
++ WRITE32(NFS4_OPEN_NOCREATE);
++ break;
++ default:
++ BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
++ WRITE32(NFS4_OPEN_CREATE);
++ encode_createmode(xdr, arg);
++ }
+ }
+
+-static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
++static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type)
+ {
+ uint32_t *p;
+
+- RESERVE_SPACE(8+sizeof(arg->stateid.data));
+- WRITE32(OP_OPEN_CONFIRM);
+- WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
+- WRITE32(arg->seqid);
++ RESERVE_SPACE(4);
++ switch (delegation_type) {
++ case 0:
++ WRITE32(NFS4_OPEN_DELEGATE_NONE);
++ break;
++ case FMODE_READ:
++ WRITE32(NFS4_OPEN_DELEGATE_READ);
++ break;
++ case FMODE_WRITE|FMODE_READ:
++ WRITE32(NFS4_OPEN_DELEGATE_WRITE);
++ break;
++ default:
++ BUG();
++ }
++}
+
+- return 0;
++static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *name)
++{
++ uint32_t *p;
++
++ RESERVE_SPACE(4);
++ WRITE32(NFS4_OPEN_CLAIM_NULL);
++ encode_string(xdr, name->len, name->name);
+ }
+
++static inline void encode_claim_previous(struct xdr_stream *xdr, int type)
++{
++ uint32_t *p;
++
++ RESERVE_SPACE(4);
++ WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
++ encode_delegation_type(xdr, type);
++}
+
+-static int encode_open_reclaim(struct xdr_stream *xdr, const struct nfs_open_reclaimargs *arg)
++static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struct qstr *name, const nfs4_stateid *stateid)
+ {
+ uint32_t *p;
+
+- /*
+- * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
+- * owner 4, opentype 4, claim 4, delegation_type 4 = 44
+- */
+- RESERVE_SPACE(44);
+- WRITE32(OP_OPEN);
+- WRITE32(arg->seqid);
+- switch (arg->share_access) {
+- case FMODE_READ:
+- WRITE32(NFS4_SHARE_ACCESS_READ);
++ RESERVE_SPACE(4+sizeof(stateid->data));
++ WRITE32(NFS4_OPEN_CLAIM_DELEGATE_CUR);
++ WRITEMEM(stateid->data, sizeof(stateid->data));
++ encode_string(xdr, name->len, name->name);
++}
++
++static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++ encode_openhdr(xdr, arg);
++ encode_opentype(xdr, arg);
++ switch (arg->claim) {
++ case NFS4_OPEN_CLAIM_NULL:
++ encode_claim_null(xdr, arg->name);
+ break;
+- case FMODE_WRITE:
+- WRITE32(NFS4_SHARE_ACCESS_WRITE);
++ case NFS4_OPEN_CLAIM_PREVIOUS:
++ encode_claim_previous(xdr, arg->u.delegation_type);
+ break;
+- case FMODE_READ|FMODE_WRITE:
+- WRITE32(NFS4_SHARE_ACCESS_BOTH);
++ case NFS4_OPEN_CLAIM_DELEGATE_CUR:
++ encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
+ break;
+ default:
+ BUG();
+ }
+- WRITE32(0); /* for linux, share_deny = 0 always */
+- WRITE64(arg->clientid);
+- WRITE32(4);
+- WRITE32(arg->id);
+- WRITE32(NFS4_OPEN_NOCREATE);
+- WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
+- WRITE32(NFS4_OPEN_DELEGATE_NONE);
++ return 0;
++}
++
++static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
++{
++ uint32_t *p;
++
++ RESERVE_SPACE(8+sizeof(arg->stateid.data));
++ WRITE32(OP_OPEN_CONFIRM);
++ WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
++ WRITE32(arg->seqid);
++
+ return 0;
+ }
+
+@@ -852,14 +960,11 @@ static int encode_open_downgrade(struct
+ {
+ uint32_t *p;
+
+- RESERVE_SPACE(16+sizeof(arg->stateid.data));
++ RESERVE_SPACE(8+sizeof(arg->stateid.data));
+ WRITE32(OP_OPEN_DOWNGRADE);
+ WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
+ WRITE32(arg->seqid);
+- WRITE32(arg->share_access);
+- /* No deny modes */
+- WRITE32(0);
+-
++ encode_share_access(xdr, arg->open_flags);
+ return 0;
+ }
+
+@@ -887,15 +992,15 @@ static int encode_putrootfh(struct xdr_s
+ return 0;
+ }
+
+-static void encode_stateid(struct xdr_stream *xdr, struct nfs4_state *state, fl_owner_t lockowner)
++static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
+ {
+ extern nfs4_stateid zero_stateid;
+ nfs4_stateid stateid;
+ uint32_t *p;
+
+ RESERVE_SPACE(16);
+- if (state != NULL) {
+- nfs4_copy_stateid(&stateid, state, lockowner);
++ if (ctx->state != NULL) {
++ nfs4_copy_stateid(&stateid, ctx->state, ctx->pid);
+ WRITEMEM(stateid.data, sizeof(stateid.data));
+ } else
+ WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data));
+@@ -908,7 +1013,7 @@ static int encode_read(struct xdr_stream
+ RESERVE_SPACE(4);
+ WRITE32(OP_READ);
+
+- encode_stateid(xdr, args->state, args->lockowner);
++ encode_stateid(xdr, args->context);
+
+ RESERVE_SPACE(12);
+ WRITE64(args->offset);
+@@ -1003,6 +1108,45 @@ static int encode_renew(struct xdr_strea
+ return 0;
+ }
+
++extern nfs4_stateid zero_stateid;
++
++static int
++encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
++{
++ uint32_t *p;
++ uint32_t *q = (uint32_t *)arg->acl;
++ uint32_t *end = (uint32_t *)(arg->acl + arg->acl_len);
++ uint32_t tmp;
++ int naces, i;
++
++ RESERVE_SPACE(4+sizeof(zero_stateid.data));
++ WRITE32(OP_SETATTR);
++ WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data));
++ RESERVE_SPACE(4*4);
++ WRITE32(1);
++ WRITE32(FATTR4_WORD0_ACL);
++ WRITE32(arg->acl_len);
++ if (q + 1 > end)
++ return -EINVAL;
++ naces = ntohl(*q++);
++ WRITE32(naces);
++ for (i = 0; i < naces; i++) {
++ if (q + 4 > end)
++ return -EINVAL;
++ RESERVE_SPACE(3*4);
++ memcpy(p, q, 3*4); /* type, flag, access_mask, length */
++ q += 3;
++ tmp = ntohl(*q++); /* length */
++ if (tmp > XDR_MAX_NETOBJ)
++ return -EINVAL;
++ if (q + XDR_QUADLEN(tmp) > end)
++ return -EINVAL;
++ RESERVE_SPACE((XDR_QUADLEN(tmp) << 2) + 4);
++ p = xdr_encode_opaque(p, q, tmp);
++ }
++ return 0;
++}
++
+ static int
+ encode_savefh(struct xdr_stream *xdr)
+ {
+@@ -1031,26 +1175,18 @@ static int encode_setattr(struct xdr_str
+
+ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid)
+ {
+- uint32_t total_len;
+- uint32_t len1, len2, len3;
+ uint32_t *p;
+
+- len1 = strlen(setclientid->sc_name);
+- len2 = strlen(setclientid->sc_netid);
+- len3 = strlen(setclientid->sc_uaddr);
+- total_len = XDR_QUADLEN(len1) + XDR_QUADLEN(len2) + XDR_QUADLEN(len3);
+- total_len = (total_len << 2) + 24 + sizeof(setclientid->sc_verifier.data);
+-
+- RESERVE_SPACE(total_len);
++ RESERVE_SPACE(4 + sizeof(setclientid->sc_verifier->data));
+ WRITE32(OP_SETCLIENTID);
+- WRITEMEM(setclientid->sc_verifier.data, sizeof(setclientid->sc_verifier.data));
+- WRITE32(len1);
+- WRITEMEM(setclientid->sc_name, len1);
++ WRITEMEM(setclientid->sc_verifier->data, sizeof(setclientid->sc_verifier->data));
++
++ encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name);
++ RESERVE_SPACE(4);
+ WRITE32(setclientid->sc_prog);
+- WRITE32(len2);
+- WRITEMEM(setclientid->sc_netid, len2);
+- WRITE32(len3);
+- WRITEMEM(setclientid->sc_uaddr, len3);
++ encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid);
++ encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
++ RESERVE_SPACE(4);
+ WRITE32(setclientid->sc_cb_ident);
+
+ return 0;
+@@ -1075,7 +1211,7 @@ static int encode_write(struct xdr_strea
+ RESERVE_SPACE(4);
+ WRITE32(OP_WRITE);
+
+- encode_stateid(xdr, args->state, args->lockowner);
++ encode_stateid(xdr, args->context);
+
+ RESERVE_SPACE(16);
+ WRITE64(args->offset);
+@@ -1086,6 +1222,18 @@ static int encode_write(struct xdr_strea
+
+ return 0;
+ }
++
++static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid)
++{
++ uint32_t *p;
++
++ RESERVE_SPACE(20);
++
++ WRITE32(OP_DELEGRETURN);
++ WRITEMEM(stateid->data, sizeof(stateid->data));
++ return 0;
++
++}
+ /*
+ * END OF "GENERIC" ENCODE ROUTINES.
+ */
+@@ -1244,6 +1392,14 @@ out:
+ }
+
+ /*
++ * Encode SYMLINK request
++ */
++static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, uint32_t *p, const struct nfs4_create_arg *args)
++{
++ return nfs4_xdr_enc_create(req, p, args);
++}
++
++/*
+ * Encode GETATTR request
+ */
+ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, uint32_t *p, const struct nfs4_getattr_arg *args)
+@@ -1331,13 +1487,13 @@ out:
+ }
+
+ /*
+- * Encode an OPEN request
++ * Encode an OPEN request with no attributes.
+ */
+-static int nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, struct nfs_open_reclaimargs *args)
++static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, uint32_t *p, struct nfs_openargs *args)
+ {
+ struct xdr_stream xdr;
+ struct compound_hdr hdr = {
+- .nops = 3,
++ .nops = 2,
+ };
+ int status;
+
+@@ -1346,10 +1502,7 @@ static int nfs4_xdr_enc_open_reclaim(str
+ status = encode_putfh(&xdr, args->fh);
+ if (status)
+ goto out;
+- status = encode_open_reclaim(&xdr, args);
+- if (status)
+- goto out;
+- status = encode_getfattr(&xdr, args->bitmask);
++ status = encode_open(&xdr, args);
+ out:
+ return status;
+ }
+@@ -1538,6 +1691,52 @@ out:
+ }
+
+ /*
++ * Encode an SETACL request
++ */
++static int
++nfs4_xdr_enc_setacl(struct rpc_rqst *req, uint32_t *p, struct nfs_setaclargs *args)
++
++{
++ struct xdr_stream xdr;
++ struct compound_hdr hdr = {
++ .nops = 2,
++ };
++ int status;
++
++ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++ encode_compound_hdr(&xdr, &hdr);
++ status = encode_putfh(&xdr, args->fh);
++ if(status)
++ goto out;
++ status = encode_setacl(&xdr, args);
++out:
++ return status;
++}
++
++/*
++ * Encode a GETACL request
++ */
++static int
++nfs4_xdr_enc_getacl(struct rpc_rqst *req, uint32_t *p,struct nfs_fh *fhandle)
++{
++ struct xdr_stream xdr;
++ struct compound_hdr hdr = {
++ .nops = 2,
++ };
++ int status;
++
++ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++ encode_compound_hdr(&xdr, &hdr);
++ status = encode_putfh(&xdr, fhandle);
++ if (status)
++ goto out;
++ status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0);
++out:
++ return status;
++
++}
++
++/*
+ * Encode a WRITE request
+ */
+ static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args)
+@@ -1716,6 +1915,24 @@ static int nfs4_xdr_enc_setclientid_conf
+ }
+
+ /*
++ * DELEGRETURN request
++ */
++static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, uint32_t *p, const struct nfs4_delegreturnargs *args)
++{
++ struct xdr_stream xdr;
++ struct compound_hdr hdr = {
++ .nops = 2,
++ };
++ int status;
++
++ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++ encode_compound_hdr(&xdr, &hdr);
++ if ((status = encode_putfh(&xdr, args->fhandle)) == 0)
++ status = encode_delegreturn(&xdr, args->stateid);
++ return status;
++}
++
++/*
+ * START OF "GENERIC" DECODE ROUTINES.
+ * These may look a little ugly since they are imported from a "generic"
+ * set of XDR encode/decode routines which are intended to be shared by
+@@ -1749,6 +1966,17 @@ static int nfs4_xdr_enc_setclientid_conf
+ } \
+ } while (0)
+
++static int decode_opaque_inline(struct xdr_stream *xdr, uint32_t *len, char **string)
++{
++ uint32_t *p;
++
++ READ_BUF(4);
++ READ32(*len);
++ READ_BUF(*len);
++ *string = (char *)p;
++ return 0;
++}
++
+ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+ {
+ uint32_t *p;
+@@ -1785,6 +2013,17 @@ static int decode_op_hdr(struct xdr_stre
+ return 0;
+ }
+
++/* Dummy routine */
++static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs4_client *clp)
++{
++ uint32_t *p;
++ uint32_t strlen;
++ char *str;
++
++ READ_BUF(12);
++ return decode_opaque_inline(xdr, &strlen, &str);
++}
++
+ static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
+ {
+ uint32_t bmlen, *p;
+@@ -2717,10 +2956,56 @@ static int decode_lookup(struct xdr_stre
+ return decode_op_hdr(xdr, OP_LOOKUP);
+ }
+
++/* This is too sick! */
++static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
++{
++ uint32_t *p;
++ uint32_t limit_type, nblocks, blocksize;
++
++ READ_BUF(12);
++ READ32(limit_type);
++ switch (limit_type) {
++ case 1:
++ READ64(*maxsize);
++ break;
++ case 2:
++ READ32(nblocks);
++ READ32(blocksize);
++ *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
++ }
++ return 0;
++}
++
++static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
++{
++ uint32_t *p;
++ uint32_t delegation_type;
++
++ READ_BUF(4);
++ READ32(delegation_type);
++ if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
++ res->delegation_type = 0;
++ return 0;
++ }
++ READ_BUF(20);
++ COPYMEM(res->delegation.data, sizeof(res->delegation.data));
++ READ32(res->do_recall);
++ switch (delegation_type) {
++ case NFS4_OPEN_DELEGATE_READ:
++ res->delegation_type = FMODE_READ;
++ break;
++ case NFS4_OPEN_DELEGATE_WRITE:
++ res->delegation_type = FMODE_WRITE|FMODE_READ;
++ if (decode_space_limit(xdr, &res->maxsize) < 0)
++ return -EIO;
++ }
++ return decode_ace(xdr, NULL, res->server->nfs4_state);
++}
++
+ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
+ {
+ uint32_t *p;
+- uint32_t bmlen, delegation_type;
++ uint32_t bmlen;
+ int status;
+
+ status = decode_op_hdr(xdr, OP_OPEN);
+@@ -2737,11 +3022,9 @@ static int decode_open(struct xdr_stream
+ if (bmlen > 10)
+ goto xdr_error;
+
+- READ_BUF((bmlen << 2) + 4);
++ READ_BUF(bmlen << 2);
+ p += bmlen;
+- READ32(delegation_type);
+- if (delegation_type == NFS4_OPEN_DELEGATE_NONE)
+- return 0;
++ return decode_delegation(xdr, res);
+ xdr_error:
+ printk(KERN_NOTICE "%s: xdr error!\n", __FUNCTION__);
+ return -EIO;
+@@ -2967,6 +3250,72 @@ static int decode_renew(struct xdr_strea
+ return decode_op_hdr(xdr, OP_RENEW);
+ }
+
++static int decode_attr_acl(struct xdr_stream *xdr, uint32_t *bitmap,
++ struct nfs_getaclres *res)
++{
++ uint32_t *p;
++
++ if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
++ return -EIO;
++ if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
++ ssize_t size = res->acl_len;
++ uint32_t nace, tmp;
++ u32 *start;
++ int i;
++
++ res->acl_len = 0;
++ READ_BUF(4);
++ start = p;
++ READ32(nace);
++ res->acl_len += 4;
++
++ for (i = 0; i < nace; i++) {
++ READ_BUF(4*4);
++ res->acl_len += 4*4;
++ p += 3;
++ READ32(tmp); /* namelen */
++ READ_BUF(tmp);
++ if (tmp > XDR_MAX_NETOBJ) {
++ printk(KERN_WARNING "%s: name too long (%u)!\n",
++ __FUNCTION__, tmp);
++ return -EIO;
++ }
++ res->acl_len += XDR_QUADLEN(tmp) << 2;
++ }
++ if (size && res->acl_len > size)
++ return -ERANGE;
++ if (size == 0 && res->acl_len <= XATTR_SIZE_MAX)
++ res->acl = kmalloc(res->acl_len, GFP_KERNEL);
++ if (res->acl)
++ memcpy(res->acl, start, res->acl_len);
++ }
++ return 0;
++}
++
++static int decode_getacl(struct xdr_stream *xdr, struct nfs_getaclres *res)
++{
++ uint32_t *savep;
++ uint32_t attrlen,
++ bitmap[2] = {0};
++ int status;
++
++ if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
++ goto xdr_error;
++ if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
++ goto xdr_error;
++ if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
++ goto xdr_error;
++
++ if ((status = decode_attr_acl(xdr, bitmap, res)) != 0)
++ goto xdr_error;
++
++ status = verify_attr_len(xdr, savep, attrlen);
++xdr_error:
++ if (status != 0)
++ printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
++ return status;
++}
++
+ static int
+ decode_savefh(struct xdr_stream *xdr)
+ {
+@@ -3048,6 +3397,11 @@ static int decode_write(struct xdr_strea
+ return 0;
+ }
+
++static int decode_delegreturn(struct xdr_stream *xdr)
++{
++ return decode_op_hdr(xdr, OP_DELEGRETURN);
++}
++
+ /*
+ * Decode OPEN_DOWNGRADE response
+ */
+@@ -3222,6 +3576,14 @@ out:
+ }
+
+ /*
++ * Decode SYMLINK response
++ */
++static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
++{
++ return nfs4_xdr_dec_create(rqstp, p, res);
++}
++
++/*
+ * Decode GETATTR response
+ */
+ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_getattr_res *res)
+@@ -3243,6 +3605,50 @@ out:
+
+ }
+
++/*
++ * Decode SETACL response
++ */
++static int
++nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, uint32_t *p, void *res)
++{
++ struct xdr_stream xdr;
++ struct compound_hdr hdr