Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / fs / nfs / nfs2xdr.c
index 6a32483..f0015fa 100644 (file)
@@ -57,7 +57,7 @@ extern int                    nfs_stat_to_errno(int stat);
 
 #define NFS_attrstat_sz                (1+NFS_fattr_sz)
 #define NFS_diropres_sz                (1+NFS_fhandle_sz+NFS_fattr_sz)
-#define NFS_readlinkres_sz     (1)
+#define NFS_readlinkres_sz     (2)
 #define NFS_readres_sz         (1+NFS_fattr_sz+1)
 #define NFS_writeres_sz         (NFS_attrstat_sz)
 #define NFS_stat_sz            (1)
@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
 static inline u32 *
 xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
 {
-       /* Zero handle first to allow comparisons */
-       memset(fhandle, 0, sizeof(*fhandle));
        /* NFSv2 handles have a fixed length */
        fhandle->size = NFS2_FHSIZE;
        memcpy(fhandle->data, p, NFS2_FHSIZE);
@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep)
        return p;
 }
 
+static inline u32*
+xdr_encode_current_server_time(u32 *p, struct timespec *timep)
+{
+       /*
+        * Passing the invalid value useconds=1000000 is a
+        * Sun convention for "set to current server time".
+        * It's needed to make permissions checks for the
+        * "touch" program across v2 mounts to Solaris and
+        * Irix boxes work correctly. See description of
+        * sattr in section 6.1 of "NFS Illustrated" by
+        * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
+        */
+       *p++ = htonl(timep->tv_sec);
+       *p++ = htonl(1000000);
+       return p;
+}
+
 static inline u32*
 xdr_decode_time(u32 *p, struct timespec *timep)
 {
@@ -128,36 +143,38 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
                fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
                fattr->rdev = 0;
        }
-       fattr->timestamp = jiffies;
        return p;
 }
 
-#define SATTR(p, attr, flag, field) \
-        *p++ = (attr->ia_valid & flag) ? htonl(attr->field) : ~(u32) 0
 static inline u32 *
 xdr_encode_sattr(u32 *p, struct iattr *attr)
 {
-       SATTR(p, attr, ATTR_MODE, ia_mode);
-       SATTR(p, attr, ATTR_UID, ia_uid);
-       SATTR(p, attr, ATTR_GID, ia_gid);
-       SATTR(p, attr, ATTR_SIZE, ia_size);
+       const u32 not_set = __constant_htonl(0xFFFFFFFF);
 
-       if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
+       *p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set;
+       *p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set;
+       *p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set;
+       *p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set;
+
+       if (attr->ia_valid & ATTR_ATIME_SET) {
                p = xdr_encode_time(p, &attr->ia_atime);
+       } else if (attr->ia_valid & ATTR_ATIME) {
+               p = xdr_encode_current_server_time(p, &attr->ia_atime);
        } else {
-               *p++ = ~(u32) 0;
-               *p++ = ~(u32) 0;
+               *p++ = not_set;
+               *p++ = not_set;
        }
 
-       if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
+       if (attr->ia_valid & ATTR_MTIME_SET) {
                p = xdr_encode_time(p, &attr->ia_mtime);
+       } else if (attr->ia_valid & ATTR_MTIME) {
+               p = xdr_encode_current_server_time(p, &attr->ia_mtime);
        } else {
-               *p++ = ~(u32) 0;        
-               *p++ = ~(u32) 0;
+               *p++ = not_set; 
+               *p++ = not_set;
        }
        return p;
 }
-#undef SATTR
 
 /*
  * NFS encode functions
@@ -231,7 +248,7 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 static int
 nfs_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, recvd, hdrlen;
 
        if ((status = ntohl(*p++)))
@@ -375,7 +392,7 @@ static int
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, void *dummy)
 {
        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;
@@ -511,7 +528,6 @@ static int
 nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
 {
        struct rpc_auth *auth = req->rq_task->tk_auth;
-       unsigned int count = args->count - 5;
        unsigned int replen;
 
        p = xdr_encode_fhandle(p, args->fh);
@@ -519,7 +535,7 @@ nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args
 
        /* Inline the page array */
        replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_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;
 }
 
@@ -530,33 +546,39 @@ static int
 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
 {
        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;
 
        if ((status = ntohl(*p++)))
                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);
        }
-
-       strlen = (u32*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
-       /* Convert length of symlink */
-       len = ntohl(*strlen);
-       if (len > rcvbuf->page_len) {
-               dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
-               kunmap_atomic(strlen, KM_USER0);
-               return -ENAMETOOLONG;
+       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 = 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;
 }
 
@@ -660,7 +682,9 @@ nfs_stat_to_errno(int stat)
        .p_encode   =  (kxdrproc_t) nfs_xdr_##argtype,                  \
        .p_decode   =  (kxdrproc_t) nfs_xdr_##restype,                  \
        .p_bufsiz   =  MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \
-       .p_timer    =  timer                                            \
+       .p_timer    =  timer,                                           \
+       .p_statidx  =  NFSPROC_##proc,                                  \
+       .p_name     =  #proc,                                           \
        }
 struct rpc_procinfo    nfs_procedures[] = {
     PROC(GETATTR,      fhandle,        attrstat, 1),
@@ -682,6 +706,6 @@ struct rpc_procinfo nfs_procedures[] = {
 
 struct rpc_version             nfs_version2 = {
        .number                 = 2,
-       .nrprocs                = sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
+       .nrprocs                = ARRAY_SIZE(nfs_procedures),
        .procs                  = nfs_procedures
 };