Merge branch 'bugfixes' into nfs-for-2.6.38
Trond Myklebust [Mon, 10 Jan 2011 19:48:02 +0000 (14:48 -0500)]
Conflicts:
fs/nfs/nfs2xdr.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4xdr.c

1  2 
fs/nfs/dir.c
fs/nfs/nfs2xdr.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4xdr.c
include/linux/sunrpc/xdr.h

diff --cc fs/nfs/dir.c
@@@ -33,9 -33,7 +33,8 @@@
  #include <linux/namei.h>
  #include <linux/mount.h>
  #include <linux/sched.h>
- #include <linux/vmalloc.h>
  #include <linux/kmemleak.h>
 +#include <linux/xattr.h>
  
  #include "delegation.h"
  #include "iostat.h"
@@@ -943,12 -487,7 +943,7 @@@ int nfs2_decode_dirent(struct xdr_strea
  
        entry->d_type = DT_UNKNOWN;
  
-       /* Peek at the next entry to see if we're at EOD */
-       p = xdr_inline_peek(xdr, 4 + 4);
-       entry->eof = 0;
-       if (p != NULL)
-               entry->eof = (p[0] == xdr_zero) && (p[1] != xdr_zero);
 -      return p;
 +      return 0;
  
  out_overflow:
        print_overflow_msg(__func__, xdr);
@@@ -1707,302 -862,105 +1707,297 @@@ out_status
  }
  
  /*
 - * Decode READ reply
 + * 3.3.8  CREATE3res
 + *
 + *    struct CREATE3resok {
 + *            post_op_fh3     obj;
 + *            post_op_attr    obj_attributes;
 + *            wcc_data        dir_wcc;
 + *    };
 + *
 + *    struct CREATE3resfail {
 + *            wcc_data        dir_wcc;
 + *    };
 + *
 + *    union CREATE3res switch (nfsstat3 status) {
 + *    case NFS3_OK:
 + *            CREATE3resok    resok;
 + *    default:
 + *            CREATE3resfail  resfail;
 + *    };
   */
 -static int
 -nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
 +static int decode_create3resok(struct xdr_stream *xdr,
 +                             struct nfs3_diropres *result)
  {
 -      struct kvec *iov = req->rq_rcv_buf.head;
 -      size_t hdrlen;
 -      u32 count, ocount, recvd;
 -      int status;
 +      int error;
 +
 +      error = decode_post_op_fh3(xdr, result->fh);
 +      if (unlikely(error))
 +              goto out;
 +      error = decode_post_op_attr(xdr, result->fattr);
 +      if (unlikely(error))
 +              goto out;
 +      /* The server isn't required to return a file handle.
 +       * If it didn't, force the client to perform a LOOKUP
 +       * to determine the correct file handle and attribute
 +       * values for the new object. */
 +      if (result->fh->size == 0)
 +              result->fattr->valid = 0;
 +      error = decode_wcc_data(xdr, result->dir_attr);
 +out:
 +      return error;
 +}
  
 -      status = ntohl(*p++);
 -      p = xdr_decode_post_op_attr(p, res->fattr);
 +static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
 +                                 struct xdr_stream *xdr,
 +                                 struct nfs3_diropres *result)
 +{
 +      enum nfs_stat status;
 +      int error;
 +
 +      error = decode_nfsstat3(xdr, &status);
 +      if (unlikely(error))
 +              goto out;
 +      if (status != NFS3_OK)
 +              goto out_default;
 +      error = decode_create3resok(xdr, result);
 +out:
 +      return error;
 +out_default:
 +      error = decode_wcc_data(xdr, result->dir_attr);
 +      if (unlikely(error))
 +              goto out;
 +      return nfs_stat_to_errno(status);
 +}
  
 -      if (status != 0)
 -              return nfs_stat_to_errno(status);
 +/*
 + * 3.3.12  REMOVE3res
 + *
 + *    struct REMOVE3resok {
 + *            wcc_data    dir_wcc;
 + *    };
 + *
 + *    struct REMOVE3resfail {
 + *            wcc_data    dir_wcc;
 + *    };
 + *
 + *    union REMOVE3res switch (nfsstat3 status) {
 + *    case NFS3_OK:
 + *            REMOVE3resok   resok;
 + *    default:
 + *            REMOVE3resfail resfail;
 + *    };
 + */
 +static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
 +                                 struct xdr_stream *xdr,
 +                                 struct nfs_removeres *result)
 +{
 +      enum nfs_stat status;
 +      int error;
 +
 +      error = decode_nfsstat3(xdr, &status);
 +      if (unlikely(error))
 +              goto out;
 +      error = decode_wcc_data(xdr, result->dir_attr);
 +      if (unlikely(error))
 +              goto out;
 +      if (status != NFS3_OK)
 +              goto out_status;
 +out:
 +      return error;
 +out_status:
 +      return nfs_stat_to_errno(status);
 +}
  
 -      /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
 -       * in that it puts the count both in the res struct and in the
 -       * opaque data count. */
 -      count    = ntohl(*p++);
 -      res->eof = ntohl(*p++);
 -      ocount   = ntohl(*p++);
 +/*
 + * 3.3.14  RENAME3res
 + *
 + *    struct RENAME3resok {
 + *            wcc_data        fromdir_wcc;
 + *            wcc_data        todir_wcc;
 + *    };
 + *
 + *    struct RENAME3resfail {
 + *            wcc_data        fromdir_wcc;
 + *            wcc_data        todir_wcc;
 + *    };
 + *
 + *    union RENAME3res switch (nfsstat3 status) {
 + *    case NFS3_OK:
 + *            RENAME3resok   resok;
 + *    default:
 + *            RENAME3resfail resfail;
 + *    };
 + */
 +static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
 +                                 struct xdr_stream *xdr,
 +                                 struct nfs_renameres *result)
 +{
 +      enum nfs_stat status;
 +      int error;
 +
 +      error = decode_nfsstat3(xdr, &status);
 +      if (unlikely(error))
 +              goto out;
 +      error = decode_wcc_data(xdr, result->old_fattr);
 +      if (unlikely(error))
 +              goto out;
 +      error = decode_wcc_data(xdr, result->new_fattr);
 +      if (unlikely(error))
 +              goto out;
 +      if (status != NFS3_OK)
 +              goto out_status;
 +out:
 +      return error;
 +out_status:
 +      return nfs_stat_to_errno(status);
 +}
  
 -      if (ocount != count) {
 -              dprintk("NFS: READ count doesn't match RPC opaque count.\n");
 -              return -errno_NFSERR_IO;
 -      }
 +/*
 + * 3.3.15  LINK3res
 + *
 + *    struct LINK3resok {
 + *            post_op_attr    file_attributes;
 + *            wcc_data        linkdir_wcc;
 + *    };
 + *
 + *    struct LINK3resfail {
 + *            post_op_attr    file_attributes;
 + *            wcc_data        linkdir_wcc;
 + *    };
 + *
 + *    union LINK3res switch (nfsstat3 status) {
 + *    case NFS3_OK:
 + *            LINK3resok      resok;
 + *    default:
 + *            LINK3resfail    resfail;
 + *    };
 + */
 +static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
 +                               struct nfs3_linkres *result)
 +{
 +      enum nfs_stat status;
 +      int error;
 +
 +      error = decode_nfsstat3(xdr, &status);
 +      if (unlikely(error))
 +              goto out;
 +      error = decode_post_op_attr(xdr, result->fattr);
 +      if (unlikely(error))
 +              goto out;
 +      error = decode_wcc_data(xdr, result->dir_attr);
 +      if (unlikely(error))
 +              goto out;
 +      if (status != NFS3_OK)
 +              goto out_status;
 +out:
 +      return error;
 +out_status:
 +      return nfs_stat_to_errno(status);
 +}
  
 -      hdrlen = (u8 *) p - (u8 *) iov->iov_base;
 -      if (iov->iov_len < hdrlen) {
 -              dprintk("NFS: READ reply header overflowed:"
 -                              "length %Zu > %Zu\n", hdrlen, iov->iov_len);
 -                      return -errno_NFSERR_IO;
 -      } else if (iov->iov_len != hdrlen) {
 -              dprintk("NFS: READ header is short. iovec will be shifted.\n");
 -              xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
 -      }
 +/**
 + * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
 + *                    the local page cache
 + * @xdr: XDR stream where entry resides
 + * @entry: buffer to fill in with entry data
 + * @plus: boolean indicating whether this should be a readdirplus entry
 + *
 + * Returns zero if successful, otherwise a negative errno value is
 + * returned.
 + *
 + * This function is not invoked during READDIR reply decoding, but
 + * rather whenever an application invokes the getdents(2) system call
 + * on a directory already in our cache.
 + *
 + * 3.3.16  entry3
 + *
 + *    struct entry3 {
 + *            fileid3         fileid;
 + *            filename3       name;
 + *            cookie3         cookie;
 + *            fhandle3        filehandle;
 + *            post_op_attr3   attributes;
 + *            entry3          *nextentry;
 + *    };
 + *
 + * 3.3.17  entryplus3
 + *    struct entryplus3 {
 + *            fileid3         fileid;
 + *            filename3       name;
 + *            cookie3         cookie;
 + *            post_op_attr    name_attributes;
 + *            post_op_fh3     name_handle;
 + *            entryplus3      *nextentry;
 + *    };
 + */
 +int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
 +                     int plus)
 +{
 +      struct nfs_entry old = *entry;
 +      __be32 *p;
 +      int error;
  
 -      recvd = req->rq_rcv_buf.len - hdrlen;
 -      if (count > recvd) {
 -              dprintk("NFS: server cheating in read reply: "
 -                      "count %u > recvd %u\n", count, recvd);
 -              count = recvd;
 -              res->eof = 0;
 +      p = xdr_inline_decode(xdr, 4);
 +      if (unlikely(p == NULL))
 +              goto out_overflow;
 +      if (*p == xdr_zero) {
 +              p = xdr_inline_decode(xdr, 4);
 +              if (unlikely(p == NULL))
 +                      goto out_overflow;
 +              if (*p == xdr_zero)
 +                      return -EAGAIN;
 +              entry->eof = 1;
 +              return -EBADCOOKIE;
        }
  
 -      if (count < res->count)
 -              res->count = count;
 +      error = decode_fileid3(xdr, &entry->ino);
 +      if (unlikely(error))
 +              return error;
  
 -      return count;
 -}
 +      error = decode_inline_filename3(xdr, &entry->name, &entry->len);
 +      if (unlikely(error))
 +              return error;
  
 -/*
 - * Decode WRITE response
 - */
 -static int
 -nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
 -{
 -      int     status;
 +      entry->prev_cookie = entry->cookie;
 +      error = decode_cookie3(xdr, &entry->cookie);
 +      if (unlikely(error))
 +              return error;
  
 -      status = ntohl(*p++);
 -      p = xdr_decode_wcc_data(p, res->fattr);
 +      entry->d_type = DT_UNKNOWN;
  
 -      if (status != 0)
 -              return nfs_stat_to_errno(status);
 +      if (plus) {
 +              entry->fattr->valid = 0;
 +              error = decode_post_op_attr(xdr, entry->fattr);
 +              if (unlikely(error))
 +                      return error;
 +              if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
 +                      entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
  
 -      res->count = ntohl(*p++);
 -      res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
 -      res->verf->verifier[0] = *p++;
 -      res->verf->verifier[1] = *p++;
 +              /* In fact, a post_op_fh3: */
 +              p = xdr_inline_decode(xdr, 4);
 +              if (unlikely(p == NULL))
 +                      goto out_overflow;
 +              if (*p != xdr_zero) {
 +                      error = decode_nfs_fh3(xdr, entry->fh);
 +                      if (unlikely(error)) {
 +                              if (error == -E2BIG)
 +                                      goto out_truncated;
 +                              return error;
 +                      }
 +              } else
 +                      zero_nfs_fh3(entry->fh);
 +      }
  
-       /* Peek at the next entry to see if we're at EOD */
-       p = xdr_inline_peek(xdr, 4 + 4);
-       entry->eof = 0;
-       if (p != NULL)
-               entry->eof = (p[0] == xdr_zero) && (p[1] != xdr_zero);
 -      return res->count;
 -}
 +      return 0;
  
 -/*
 - * Decode a CREATE response
 - */
 -static int
 -nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
 -{
 -      int     status;
 -
 -      status = ntohl(*p++);
 -      if (status == 0) {
 -              if (*p++) {
 -                      if (!(p = xdr_decode_fhandle(p, res->fh)))
 -                              return -errno_NFSERR_IO;
 -                      p = xdr_decode_post_op_attr(p, res->fattr);
 -              } else {
 -                      memset(res->fh, 0, sizeof(*res->fh));
 -                      /* Do decode post_op_attr but set it to NULL */
 -                      p = xdr_decode_post_op_attr(p, res->fattr);
 -                      res->fattr->valid = 0;
 -              }
 -      } else {
 -              status = nfs_stat_to_errno(status);
 -      }
 -      p = xdr_decode_wcc_data(p, res->dir_attr);
 -      return status;
 +out_overflow:
 +      print_overflow_msg(__func__, xdr);
 +      return -EAGAIN;
 +out_truncated:
 +      dprintk("NFS: directory entry contains invalid file handle\n");
 +      *entry = old;
 +      return -EAGAIN;
  }
  
  /*
@@@ -6135,13 -6215,7 +6135,7 @@@ int nfs4_decode_dirent(struct xdr_strea
        if (verify_attr_len(xdr, p, len) < 0)
                goto out_overflow;
  
-       p = xdr_inline_peek(xdr, 8);
-       if (p != NULL)
-               entry->eof = !p[0] && p[1];
-       else
-               entry->eof = 0;
 -      return p;
 +      return 0;
  
  out_overflow:
        print_overflow_msg(__func__, xdr);
@@@ -201,14 -201,10 +201,16 @@@ struct xdr_stream 
  
        __be32 *end;            /* end of available buffer space */
        struct kvec *iov;       /* pointer to the current kvec */
+       struct kvec scratch;    /* Scratch buffer */
+       struct page **page_ptr; /* pointer to the current page */
  };
  
 +/*
 + * These are the xdr_stream style generic XDR encode and decode functions.
 + */
 +typedef void  (*kxdreproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
 +typedef int   (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
 +
  extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
  extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
  extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,