Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / nfs / nfs3xdr.c
index 98de023..442d8c4 100644 (file)
 #include <linux/nfs.h>
 #include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfsacl.h>
+#include <linux/vserver/xid.h>
+#include "internal.h"
 
 #define NFSDBG_FACILITY                NFSDBG_XDR
 
 /* Mapping from NFS error code to "errno" error code. */
 #define errno_NFSERR_IO                EIO
 
-extern int                     nfs_stat_to_errno(int);
-
 /*
  * Declare the space requirements for NFS arguments and replies as
  * number of 32bit-words
@@ -67,7 +68,7 @@ extern int                    nfs_stat_to_errno(int);
 #define NFS3_wccstat_sz                (1+NFS3_wcc_data_sz)
 #define NFS3_lookupres_sz      (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
 #define NFS3_accessres_sz      (1+NFS3_post_op_attr_sz+1)
-#define NFS3_readlinkres_sz    (1+NFS3_post_op_attr_sz)
+#define NFS3_readlinkres_sz    (1+NFS3_post_op_attr_sz+1)
 #define NFS3_readres_sz                (1+NFS3_post_op_attr_sz+3)
 #define NFS3_writeres_sz       (1+NFS3_wcc_data_sz+4)
 #define NFS3_createres_sz      (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
@@ -79,6 +80,11 @@ extern int                   nfs_stat_to_errno(int);
 #define NFS3_pathconfres_sz    (1+NFS3_post_op_attr_sz+6)
 #define NFS3_commitres_sz      (1+NFS3_wcc_data_sz+2)
 
+#define ACL3_getaclargs_sz     (NFS3_fh_sz+1)
+#define ACL3_setaclargs_sz     (NFS3_fh_sz+1+2*(2+5*3))
+#define ACL3_getaclres_sz      (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
+#define ACL3_setaclres_sz      (1+NFS3_post_op_attr_sz)
+
 /*
  * Map file type to S_IFMT bits
  */
@@ -109,10 +115,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
 static inline u32 *
 xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
 {
-       /*
-        * Zero all nonused bytes
-        */
-       memset((u8 *)fh, 0, sizeof(*fh));
        if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
                memcpy(fh->data, p, fh->size);
                return p + XDR_QUADLEN(fh->size);
@@ -164,7 +166,8 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
        if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
                fattr->rdev = 0;
 
-       p = xdr_decode_hyper(p, &fattr->fsid_u.nfs3);
+       p = xdr_decode_hyper(p, &fattr->fsid.major);
+       fattr->fsid.minor = 0;
        p = xdr_decode_hyper(p, &fattr->fileid);
        p = xdr_decode_time3(p, &fattr->atime);
        p = xdr_decode_time3(p, &fattr->mtime);
@@ -172,28 +175,29 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
 
        /* Update the mode bits */
        fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
-       fattr->timestamp = jiffies;
        return p;
 }
 
 static inline u32 *
-xdr_encode_sattr(u32 *p, struct iattr *attr)
+xdr_encode_sattr(u32 *p, struct iattr *attr, int tagxid)
 {
        if (attr->ia_valid & ATTR_MODE) {
                *p++ = xdr_one;
-               *p++ = htonl(attr->ia_mode);
+               *p++ = htonl(attr->ia_mode & S_IALLUGO);
        } else {
                *p++ = xdr_zero;
        }
-       if (attr->ia_valid & ATTR_UID) {
+       if (attr->ia_valid & ATTR_UID ||
+               (tagxid && (attr->ia_valid & ATTR_XID))) {
                *p++ = xdr_one;
-               *p++ = htonl(attr->ia_uid);
+               *p++ = htonl(XIDINO_UID(tagxid, attr->ia_uid, attr->ia_xid));
        } else {
                *p++ = xdr_zero;
        }
-       if (attr->ia_valid & ATTR_GID) {
+       if (attr->ia_valid & ATTR_GID ||
+               (tagxid && (attr->ia_valid & ATTR_XID))) {
                *p++ = xdr_one;
-               *p++ = htonl(attr->ia_gid);
+               *p++ = htonl(XIDINO_GID(tagxid, attr->ia_gid, attr->ia_xid));
        } else {
                *p++ = xdr_zero;
        }
@@ -278,7 +282,8 @@ static int
 nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_sattr(p, args->sattr);
+       p = xdr_encode_sattr(p, args->sattr,
+               req->rq_task->tk_client->cl_tagxid);
        *p++ = htonl(args->guard);
        if (args->guard)
                p = xdr_encode_time3(p, &args->guardtime);
@@ -369,7 +374,8 @@ nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
                *p++ = args->verifier[0];
                *p++ = args->verifier[1];
        } else
-               p = xdr_encode_sattr(p, args->sattr);
+               p = xdr_encode_sattr(p, args->sattr,
+                       req->rq_task->tk_client->cl_tagxid);
 
        req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
        return 0;
@@ -383,7 +389,8 @@ nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_array(p, args->name, args->len);
-       p = xdr_encode_sattr(p, args->sattr);
+       p = xdr_encode_sattr(p, args->sattr,
+               req->rq_task->tk_client->cl_tagxid);
        req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
        return 0;
 }
@@ -396,7 +403,8 @@ nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args
 {
        p = xdr_encode_fhandle(p, args->fromfh);
        p = xdr_encode_array(p, args->fromname, args->fromlen);
-       p = xdr_encode_sattr(p, args->sattr);
+       p = xdr_encode_sattr(p, args->sattr,
+               req->rq_task->tk_client->cl_tagxid);
        p = xdr_encode_array(p, args->topath, args->tolen);
        req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
        return 0;
@@ -411,7 +419,8 @@ nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_array(p, args->name, args->len);
        *p++ = htonl(args->type);
-       p = xdr_encode_sattr(p, args->sattr);
+       p = xdr_encode_sattr(p, args->sattr,
+               req->rq_task->tk_client->cl_tagxid);
        if (args->type == NF3CHR || args->type == NF3BLK) {
                *p++ = htonl(MAJOR(args->rdev));
                *p++ = htonl(MINOR(args->rdev));
@@ -484,7 +493,7 @@ static int
 nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
 {
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct iovec *iov = rcvbuf->head;
+       struct kvec *iov = rcvbuf->head;
        struct page **page;
        int hdrlen, recvd;
        int status, nr;
@@ -631,6 +640,74 @@ nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
        return 0;
 }
 
+#ifdef CONFIG_NFS_V3_ACL
+/*
+ * Encode GETACL arguments
+ */
+static int
+nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p,
+                   struct nfs3_getaclargs *args)
+{
+       struct rpc_auth *auth = req->rq_task->tk_auth;
+       unsigned int replen;
+
+       p = xdr_encode_fhandle(p, args->fh);
+       *p++ = htonl(args->mask);
+       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+       if (args->mask & (NFS_ACL | NFS_DFACL)) {
+               /* Inline the page array */
+               replen = (RPC_REPHDRSIZE + auth->au_rslack +
+                         ACL3_getaclres_sz) << 2;
+               xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
+                                NFSACL_MAXPAGES << PAGE_SHIFT);
+       }
+       return 0;
+}
+
+/*
+ * Encode SETACL arguments
+ */
+static int
+nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p,
+                   struct nfs3_setaclargs *args)
+{
+       struct xdr_buf *buf = &req->rq_snd_buf;
+       unsigned int base, len_in_head, len = nfsacl_size(
+               (args->mask & NFS_ACL)   ? args->acl_access  : NULL,
+               (args->mask & NFS_DFACL) ? args->acl_default : NULL);
+       int count, err;
+
+       p = xdr_encode_fhandle(p, NFS_FH(args->inode));
+       *p++ = htonl(args->mask);
+       base = (char *)p - (char *)buf->head->iov_base;
+       /* put as much of the acls into head as possible. */
+       len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
+       len -= len_in_head;
+       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
+
+       for (count = 0; (count << PAGE_SHIFT) < len; count++) {
+               args->pages[count] = alloc_page(GFP_KERNEL);
+               if (!args->pages[count]) {
+                       while (count)
+                               __free_page(args->pages[--count]);
+                       return -ENOMEM;
+               }
+       }
+       xdr_encode_pages(buf, args->pages, 0, len);
+
+       err = nfsacl_encode(buf, base, args->inode,
+                           (args->mask & NFS_ACL) ?
+                           args->acl_access : NULL, 1, 0);
+       if (err > 0)
+               err = nfsacl_encode(buf, base + err, args->inode,
+                                   (args->mask & NFS_DFACL) ?
+                                   args->acl_default : NULL, 1,
+                                   NFS_ACL_DEFAULT);
+       return (err > 0) ? 0 : err;
+}
+#endif  /* CONFIG_NFS_V3_ACL */
+
 /*
  * NFS XDR decode functions
  */
@@ -703,14 +780,13 @@ nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *ar
 {
        struct rpc_auth *auth = req->rq_task->tk_auth;
        unsigned int replen;
-       u32 count = args->count - 4;
 
        p = xdr_encode_fhandle(p, args->fh);
        req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
        /* Inline the page array */
        replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
+       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
        return 0;
 }
 
@@ -721,10 +797,9 @@ static int
 nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct iovec *iov = rcvbuf->head;
-       unsigned int hdrlen;
-       u32     *strlen, len;
-       char    *string;
+       struct kvec *iov = rcvbuf->head;
+       int hdrlen, len, recvd;
+       char    *kaddr;
        int     status;
 
        status = ntohl(*p++);
@@ -733,22 +808,33 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
        if (status != 0)
                return -nfs_stat_to_errno(status);
 
+       /* Convert length of symlink */
+       len = ntohl(*p++);
+       if (len >= rcvbuf->page_len || len <= 0) {
+               dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
+               return -ENAMETOOLONG;
+       }
+
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len > hdrlen) {
+       if (iov->iov_len < hdrlen) {
+               printk(KERN_WARNING "NFS: READLINK reply header overflowed:"
+                               "length %d > %Zu\n", hdrlen, iov->iov_len);
+               return -errno_NFSERR_IO;
+       } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
                xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
        }
+       recvd = req->rq_rcv_buf.len - hdrlen;
+       if (recvd < len) {
+               printk(KERN_WARNING "NFS: server cheating in readlink reply: "
+                               "count %u > recvd %u\n", len, recvd);
+               return -EIO;
+       }
 
-       strlen = (u32*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
-       /* Convert length of symlink */
-       len = ntohl(*strlen);
-       if (len > rcvbuf->page_len)
-               len = rcvbuf->page_len;
-       *strlen = len;
        /* NULL terminate the string we got */
-       string = (char *)(strlen + 1);
-       string[len] = 0;
-       kunmap_atomic(strlen, KM_USER0);
+       kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
+       kaddr[len+rcvbuf->page_base] = '\0';
+       kunmap_atomic(kaddr, KM_USER0);
        return 0;
 }
 
@@ -758,7 +844,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 static int
 nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
-       struct iovec *iov = req->rq_rcv_buf.head;
+       struct kvec *iov = req->rq_rcv_buf.head;
        int     status, count, ocount, recvd, hdrlen;
 
        status = ntohl(*p++);
@@ -973,6 +1059,54 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
        return 0;
 }
 
+#ifdef CONFIG_NFS_V3_ACL
+/*
+ * Decode GETACL reply
+ */
+static int
+nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
+                  struct nfs3_getaclres *res)
+{
+       struct xdr_buf *buf = &req->rq_rcv_buf;
+       int status = ntohl(*p++);
+       struct posix_acl **acl;
+       unsigned int *aclcnt;
+       int err, base;
+
+       if (status != 0)
+               return -nfs_stat_to_errno(status);
+       p = xdr_decode_post_op_attr(p, res->fattr);
+       res->mask = ntohl(*p++);
+       if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+               return -EINVAL;
+       base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
+
+       acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
+       aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
+       err = nfsacl_decode(buf, base, aclcnt, acl);
+
+       acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
+       aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
+       if (err > 0)
+               err = nfsacl_decode(buf, base + err, aclcnt, acl);
+       return (err > 0) ? 0 : err;
+}
+
+/*
+ * Decode setacl reply.
+ */
+static int
+nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+       int status = ntohl(*p++);
+
+       if (status)
+               return -nfs_stat_to_errno(status);
+       xdr_decode_post_op_attr(p, fattr);
+       return 0;
+}
+#endif  /* CONFIG_NFS_V3_ACL */
+
 #ifndef MAX
 # define MAX(a, b)     (((a) > (b))? (a) : (b))
 #endif
@@ -983,7 +1117,9 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
        .p_encode    = (kxdrproc_t) nfs3_xdr_##argtype,                 \
        .p_decode    = (kxdrproc_t) nfs3_xdr_##restype,                 \
        .p_bufsiz    = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2,       \
-       .p_timer     = timer                                            \
+       .p_timer     = timer,                                           \
+       .p_statidx   = NFS3PROC_##proc,                                 \
+       .p_name      = #proc,                                           \
        }
 
 struct rpc_procinfo    nfs3_procedures[] = {
@@ -1012,7 +1148,34 @@ struct rpc_procinfo      nfs3_procedures[] = {
 
 struct rpc_version             nfs_version3 = {
        .number                 = 3,
-       .nrprocs                = sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
+       .nrprocs                = ARRAY_SIZE(nfs3_procedures),
        .procs                  = nfs3_procedures
 };
 
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_procinfo     nfs3_acl_procedures[] = {
+       [ACLPROC3_GETACL] = {
+               .p_proc = ACLPROC3_GETACL,
+               .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
+               .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
+               .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2,
+               .p_timer = 1,
+               .p_name = "GETACL",
+       },
+       [ACLPROC3_SETACL] = {
+               .p_proc = ACLPROC3_SETACL,
+               .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
+               .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
+               .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2,
+               .p_timer = 0,
+               .p_name = "SETACL",
+       },
+};
+
+struct rpc_version             nfsacl_version3 = {
+       .number                 = 3,
+       .nrprocs                = sizeof(nfs3_acl_procedures)/
+                                 sizeof(nfs3_acl_procedures[0]),
+       .procs                  = nfs3_acl_procedures,
+};
+#endif  /* CONFIG_NFS_V3_ACL */