#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)
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);
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)
{
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);
+
+ *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|ATTR_ATIME_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
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++)))
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;
{
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 + 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;
}
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);
}
+ 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;
}