This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / fs / nfsd / nfs3xdr.c
index 8291752..75491e3 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/xdr3.h>
+#include <linux/vserver/xid.h>
 
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
@@ -71,10 +72,16 @@ decode_fh(u32 *p, struct svc_fh *fhp)
        return p + XDR_QUADLEN(size);
 }
 
+/* Helper function for NFSv3 ACL code */
+u32 *nfs3svc_decode_fh(u32 *p, struct svc_fh *fhp)
+{
+       return decode_fh(p, fhp);
+}
+
 static inline u32 *
 encode_fh(u32 *p, struct svc_fh *fhp)
 {
-       int size = fhp->fh_handle.fh_size;
+       unsigned int size = fhp->fh_handle.fh_size;
        *p++ = htonl(size);
        if (size) p[XDR_QUADLEN(size)-1]=0;
        memcpy(p, &fhp->fh_handle.fh_base, size);
@@ -101,26 +108,12 @@ decode_filename(u32 *p, char **namp, int *lenp)
        return p;
 }
 
-static inline u32 *
-decode_pathname(u32 *p, char **namp, int *lenp)
-{
-       char            *name;
-       int             i;
-
-       if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXPATHLEN)) != NULL) {
-               for (i = 0, name = *namp; i < *lenp; i++, name++) {
-                       if (*name == '\0')
-                               return NULL;
-               }
-       }
-
-       return p;
-}
-
 static inline u32 *
 decode_sattr3(u32 *p, struct iattr *iap)
 {
        u32     tmp;
+       uid_t   uid = 0;
+       gid_t   gid = 0;
 
        iap->ia_valid = 0;
 
@@ -130,12 +123,15 @@ decode_sattr3(u32 *p, struct iattr *iap)
        }
        if (*p++) {
                iap->ia_valid |= ATTR_UID;
-               iap->ia_uid = ntohl(*p++);
+               uid = ntohl(*p++);
        }
        if (*p++) {
                iap->ia_valid |= ATTR_GID;
-               iap->ia_gid = ntohl(*p++);
+               gid = ntohl(*p++);
        }
+       iap->ia_uid = INOXID_UID(XID_TAG_NFSD, uid, gid);
+       iap->ia_gid = INOXID_GID(XID_TAG_NFSD, uid, gid);
+       iap->ia_xid = INOXID_XID(XID_TAG_NFSD, uid, gid, 0);
        if (*p++) {
                u64     newsize;
 
@@ -164,37 +160,36 @@ decode_sattr3(u32 *p, struct iattr *iap)
 }
 
 static inline u32 *
-encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp,
+             struct kstat *stat)
 {
-       struct vfsmount *mnt = fhp->fh_export->ex_mnt;
        struct dentry   *dentry = fhp->fh_dentry;
-       struct kstat stat;
        struct timespec time;
 
-       vfs_getattr(mnt, dentry, &stat);
-
-       *p++ = htonl(nfs3_ftypes[(stat.mode & S_IFMT) >> 12]);
-       *p++ = htonl((u32) stat.mode);
-       *p++ = htonl((u32) stat.nlink);
-       *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid));
-       *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid));
-       if (S_ISLNK(stat.mode) && stat.size > NFS3_MAXPATHLEN) {
+       *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
+       *p++ = htonl((u32) stat->mode);
+       *p++ = htonl((u32) stat->nlink);
+       *p++ = htonl((u32) nfsd_ruid(rqstp,
+               XIDINO_UID(XID_TAG(dentry->d_inode), stat->uid, stat->xid)));
+       *p++ = htonl((u32) nfsd_rgid(rqstp,
+               XIDINO_GID(XID_TAG(dentry->d_inode), stat->gid, stat->xid)));
+       if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
                p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
        } else {
-               p = xdr_encode_hyper(p, (u64) stat.size);
+               p = xdr_encode_hyper(p, (u64) stat->size);
        }
-       p = xdr_encode_hyper(p, ((u64)stat.blocks) << 9);
-       *p++ = htonl((u32) MAJOR(stat.rdev));
-       *p++ = htonl((u32) MINOR(stat.rdev));
+       p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
+       *p++ = htonl((u32) MAJOR(stat->rdev));
+       *p++ = htonl((u32) MINOR(stat->rdev));
        if (is_fsid(fhp, rqstp->rq_reffh))
                p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
        else
-               p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat.dev));
-       p = xdr_encode_hyper(p, (u64) stat.ino);
-       p = encode_time3(p, &stat.atime);
+               p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat->dev));
+       p = xdr_encode_hyper(p, (u64) stat->ino);
+       p = encode_time3(p, &stat->atime);
        lease_get_mtime(dentry->d_inode, &time); 
        p = encode_time3(p, &time);
-       p = encode_time3(p, &stat.ctime);
+       p = encode_time3(p, &stat->ctime);
 
        return p;
 }
@@ -242,13 +237,26 @@ encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
        struct dentry *dentry = fhp->fh_dentry;
        if (dentry && dentry->d_inode != NULL) {
-               *p++ = xdr_one;         /* attributes follow */
-               return encode_fattr3(rqstp, p, fhp);
+               int err;
+               struct kstat stat;
+
+               err = vfs_getattr(fhp->fh_export->ex_mnt, dentry, &stat);
+               if (!err) {
+                       *p++ = xdr_one;         /* attributes follow */
+                       return encode_fattr3(rqstp, p, fhp, &stat);
+               }
        }
        *p++ = xdr_zero;
        return p;
 }
 
+/* Helper for NFSv3 ACLs */
+u32 *
+nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+       return encode_post_op_attr(rqstp, p, fhp);
+}
+
 /*
  * Enocde weak cache consistency data
  */
@@ -328,7 +336,7 @@ int
 nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd3_readargs *args)
 {
-       int len;
+       unsigned int len;
        int v,pn;
 
        if (!(p = decode_fh(p, &args->fh))
@@ -340,15 +348,15 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
        if (len > NFSSVC_MAXBLKSIZE)
                len = NFSSVC_MAXBLKSIZE;
 
-       /* set up the iovec */
+       /* set up the kvec */
        v=0;
        while (len > 0) {
                pn = rqstp->rq_resused;
                svc_take_page(rqstp);
                args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
                args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE;
+               len -= args->vec[v].iov_len;
                v++;
-               len -= PAGE_SIZE;
        }
        args->vlen = v;
        return xdr_argsize_check(rqstp, p);
@@ -358,7 +366,7 @@ int
 nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd3_writeargs *args)
 {
-       int len, v;
+       unsigned int len, v, hdr;
 
        if (!(p = decode_fh(p, &args->fh))
         || !(p = xdr_decode_hyper(p, &args->offset)))
@@ -368,9 +376,13 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
        args->stable = ntohl(*p++);
        len = args->len = ntohl(*p++);
 
+       hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
+       if (rqstp->rq_arg.len < hdr ||
+           rqstp->rq_arg.len - hdr < len)
+               return 0;
+
        args->vec[0].iov_base = (void*)p;
-       args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len -
-               (((void*)p) - rqstp->rq_arg.head[0].iov_base);
+       args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
 
        if (len > NFSSVC_MAXBLKSIZE)
                len = NFSSVC_MAXBLKSIZE;
@@ -427,10 +439,10 @@ int
 nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd3_symlinkargs *args)
 {
-       int len;
+       unsigned int len;
        int avail;
        char *old, *new;
-       struct iovec *vec;
+       struct kvec *vec;
 
        if (!(p = decode_fh(p, &args->ffh))
         || !(p = decode_filename(p, &args->fname, &args->flen))
@@ -444,7 +456,7 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
         */
        svc_take_page(rqstp);
        len = ntohl(*p++);
-       if (len <= 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
+       if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
                return 0;
        args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
        args->tlen = len;
@@ -615,7 +627,7 @@ nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd3_attrstat *resp)
 {
        if (resp->status == 0)
-               p = encode_fattr3(rqstp, p, &resp->fh);
+               p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
        return xdr_ressize_check(rqstp, p);
 }
 
@@ -759,10 +771,16 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
                /* stupid readdir cookie */
                memcpy(p, resp->verf, 8); p += 2;
                xdr_ressize_check(rqstp, p);
-               p = resp->buffer;
+               if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE)
+                       return 1; /*No room for trailer */
+               rqstp->rq_res.page_len = (resp->count) << 2;
+
+               /* add the 'tail' to the end of the 'head' page - page 0. */
+               rqstp->rq_restailpage = 0;
+               rqstp->rq_res.tail[0].iov_base = p;
                *p++ = 0;               /* no more entries */
                *p++ = htonl(resp->common.err == nfserr_eof);
-               rqstp->rq_res.page_len = (resp->count + 2) << 2;
+               rqstp->rq_res.tail[0].iov_len = 2<<2;
                return 1;
        } else
                return xdr_ressize_check(rqstp, p);
@@ -799,6 +817,7 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
 {
        struct svc_export       *exp;
        struct dentry           *dparent, *dchild;
+       int rv = 0;
 
        dparent = cd->fh.fh_dentry;
        exp  = cd->fh.fh_export;
@@ -807,17 +826,23 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
        if (isdotent(name, namlen)) {
                if (namlen == 2) {
                        dchild = dget_parent(dparent);
+                       if (dchild == dparent) {
+                               /* filesystem root - cannot return filehandle for ".." */
+                               dput(dchild);
+                               return 1;
+                       }
                } else
                        dchild = dget(dparent);
        } else
                dchild = lookup_one_len(name, dparent, namlen);
        if (IS_ERR(dchild))
                return 1;
-       if (d_mountpoint(dchild))
-               return 1;
-       if (fh_compose(fhp, exp, dchild, &cd->fh) != 0 || !dchild->d_inode)
-               return 1;
-       return 0;
+       if (d_mountpoint(dchild) ||
+           fh_compose(fhp, exp, dchild, &cd->fh) != 0 ||
+           !dchild->d_inode)
+               rv = 1;
+       dput(dchild);
+       return rv;
 }
 
 /*
@@ -845,8 +870,18 @@ encode_entry(struct readdir_cd *ccd, const char *name,
        int             elen;           /* estimated entry length in words */
        int             num_entry_words = 0;    /* actual number of words */
 
-       if (cd->offset)
-               xdr_encode_hyper(cd->offset, (u64) offset);
+       if (cd->offset) {
+               u64 offset64 = offset;
+
+               if (unlikely(cd->offset1)) {
+                       /* we ended up with offset on a page boundary */
+                       *cd->offset = htonl(offset64 >> 32);
+                       *cd->offset1 = htonl(offset64 & 0xffffffff);
+                       cd->offset1 = NULL;
+               } else {
+                       xdr_encode_hyper(cd->offset, (u64) offset);
+               }
+       }
 
        /*
        dprintk("encode_entry(%.*s @%ld%s)\n",
@@ -927,17 +962,32 @@ encode_entry(struct readdir_cd *ccd, const char *name,
                        /* update offset */
                        cd->offset = cd->buffer + (cd->offset - tmp);
                } else {
+                       unsigned int offset_r = (cd->offset - tmp) << 2;
+
+                       /* update pointer to offset location.
+                        * This is a 64bit quantity, so we need to
+                        * deal with 3 cases:
+                        *  -   entirely in first page
+                        *  -   entirely in second page
+                        *  -   4 bytes in each page
+                        */
+                       if (offset_r + 8 <= len1) {
+                               cd->offset = p + (cd->offset - tmp);
+                       } else if (offset_r >= len1) {
+                               cd->offset -= len1 >> 2;
+                       } else {
+                               /* sitting on the fence */
+                               BUG_ON(offset_r != len1 - 4);
+                               cd->offset = p + (cd->offset - tmp);
+                               cd->offset1 = tmp;
+                       }
+
                        len2 = (num_entry_words << 2) - len1;
 
                        /* move from temp page to current and next pages */
                        memmove(p, tmp, len1);
                        memmove(tmp, (caddr_t)tmp+len1, len2);
 
-                       /* update offset */
-                       if (((cd->offset - tmp) << 2) <= len1)
-                               cd->offset = p + (cd->offset - tmp);
-                       else
-                               cd->offset -= len1 >> 2;
                        p = tmp + (len2 >> 2);
                }
        }