|
@@ -77,6 +77,16 @@ static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
|
|
xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
|
|
xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Handle decode buffer overflows out-of-line.
|
|
|
|
+ */
|
|
|
|
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
|
|
|
|
+{
|
|
|
|
+ dprintk("NFS: %s prematurely hit the end of our receive buffer. "
|
|
|
|
+ "Remaining buffer length is %tu words.\n",
|
|
|
|
+ func, xdr->end - xdr->p);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
/*
|
|
/*
|
|
* Common NFS XDR functions as inlines
|
|
* Common NFS XDR functions as inlines
|
|
@@ -138,6 +148,74 @@ xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
|
|
* or decoded inline.
|
|
* or decoded inline.
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * typedef opaque nfsdata<>;
|
|
|
|
+ */
|
|
|
|
+static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
|
|
|
|
+{
|
|
|
|
+ u32 recvd, count;
|
|
|
|
+ size_t hdrlen;
|
|
|
|
+ __be32 *p;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, 4);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ count = be32_to_cpup(p);
|
|
|
|
+ hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
|
|
|
+ recvd = xdr->buf->len - hdrlen;
|
|
|
|
+ if (unlikely(count > recvd))
|
|
|
|
+ goto out_cheating;
|
|
|
|
+out:
|
|
|
|
+ xdr_read_pages(xdr, count);
|
|
|
|
+ result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */
|
|
|
|
+ result->count = count;
|
|
|
|
+ return count;
|
|
|
|
+out_cheating:
|
|
|
|
+ dprintk("NFS: server cheating in read result: "
|
|
|
|
+ "count %u > recvd %u\n", count, recvd);
|
|
|
|
+ count = recvd;
|
|
|
|
+ goto out;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * enum stat {
|
|
|
|
+ * NFS_OK = 0,
|
|
|
|
+ * NFSERR_PERM = 1,
|
|
|
|
+ * NFSERR_NOENT = 2,
|
|
|
|
+ * NFSERR_IO = 5,
|
|
|
|
+ * NFSERR_NXIO = 6,
|
|
|
|
+ * NFSERR_ACCES = 13,
|
|
|
|
+ * NFSERR_EXIST = 17,
|
|
|
|
+ * NFSERR_NODEV = 19,
|
|
|
|
+ * NFSERR_NOTDIR = 20,
|
|
|
|
+ * NFSERR_ISDIR = 21,
|
|
|
|
+ * NFSERR_FBIG = 27,
|
|
|
|
+ * NFSERR_NOSPC = 28,
|
|
|
|
+ * NFSERR_ROFS = 30,
|
|
|
|
+ * NFSERR_NAMETOOLONG = 63,
|
|
|
|
+ * NFSERR_NOTEMPTY = 66,
|
|
|
|
+ * NFSERR_DQUOT = 69,
|
|
|
|
+ * NFSERR_STALE = 70,
|
|
|
|
+ * NFSERR_WFLUSH = 99
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
|
|
|
|
+{
|
|
|
|
+ __be32 *p;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, 4);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ *status = be32_to_cpup(p);
|
|
|
|
+ return 0;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* 2.3.3. fhandle
|
|
* 2.3.3. fhandle
|
|
*
|
|
*
|
|
@@ -152,6 +230,21 @@ static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
|
|
memcpy(p, fh->data, NFS2_FHSIZE);
|
|
memcpy(p, fh->data, NFS2_FHSIZE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
|
|
|
|
+{
|
|
|
|
+ __be32 *p;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, NFS2_FHSIZE);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ fh->size = NFS2_FHSIZE;
|
|
|
|
+ memcpy(fh->data, p, NFS2_FHSIZE);
|
|
|
|
+ return 0;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* 2.3.4. timeval
|
|
* 2.3.4. timeval
|
|
*
|
|
*
|
|
@@ -185,6 +278,41 @@ static __be32 *xdr_encode_current_server_time(__be32 *p,
|
|
return p;
|
|
return p;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * 2.3.5. fattr
|
|
|
|
+ *
|
|
|
|
+ * struct fattr {
|
|
|
|
+ * ftype type;
|
|
|
|
+ * unsigned int mode;
|
|
|
|
+ * unsigned int nlink;
|
|
|
|
+ * unsigned int uid;
|
|
|
|
+ * unsigned int gid;
|
|
|
|
+ * unsigned int size;
|
|
|
|
+ * unsigned int blocksize;
|
|
|
|
+ * unsigned int rdev;
|
|
|
|
+ * unsigned int blocks;
|
|
|
|
+ * unsigned int fsid;
|
|
|
|
+ * unsigned int fileid;
|
|
|
|
+ * timeval atime;
|
|
|
|
+ * timeval mtime;
|
|
|
|
+ * timeval ctime;
|
|
|
|
+ * };
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
|
|
|
|
+{
|
|
|
|
+ __be32 *p;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ xdr_decode_fattr(p, fattr);
|
|
|
|
+ return 0;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* 2.3.6. sattr
|
|
* 2.3.6. sattr
|
|
*
|
|
*
|
|
@@ -259,6 +387,32 @@ static void encode_filename(struct xdr_stream *xdr,
|
|
xdr_encode_opaque(p, name, length);
|
|
xdr_encode_opaque(p, name, length);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int decode_filename_inline(struct xdr_stream *xdr,
|
|
|
|
+ const char **name, u32 *length)
|
|
|
|
+{
|
|
|
|
+ __be32 *p;
|
|
|
|
+ u32 count;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, 4);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ count = be32_to_cpup(p);
|
|
|
|
+ if (count > NFS3_MAXNAMLEN)
|
|
|
|
+ goto out_nametoolong;
|
|
|
|
+ p = xdr_inline_decode(xdr, count);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ *name = (const char *)p;
|
|
|
|
+ *length = count;
|
|
|
|
+ return 0;
|
|
|
|
+out_nametoolong:
|
|
|
|
+ dprintk("NFS: returned filename too long: %u\n", count);
|
|
|
|
+ return -ENAMETOOLONG;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* 2.3.8. path
|
|
* 2.3.8. path
|
|
*
|
|
*
|
|
@@ -274,6 +428,65 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
|
|
xdr_write_pages(xdr, pages, 0, length);
|
|
xdr_write_pages(xdr, pages, 0, length);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int decode_path(struct xdr_stream *xdr)
|
|
|
|
+{
|
|
|
|
+ u32 length, recvd;
|
|
|
|
+ size_t hdrlen;
|
|
|
|
+ __be32 *p;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, 4);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ length = be32_to_cpup(p);
|
|
|
|
+ if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
|
|
|
|
+ goto out_size;
|
|
|
|
+ hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
|
|
|
+ recvd = xdr->buf->len - hdrlen;
|
|
|
|
+ if (unlikely(length > recvd))
|
|
|
|
+ goto out_cheating;
|
|
|
|
+
|
|
|
|
+ xdr_read_pages(xdr, length);
|
|
|
|
+ xdr_terminate_string(xdr->buf, length);
|
|
|
|
+ return 0;
|
|
|
|
+out_size:
|
|
|
|
+ dprintk("NFS: returned pathname too long: %u\n", length);
|
|
|
|
+ return -ENAMETOOLONG;
|
|
|
|
+out_cheating:
|
|
|
|
+ dprintk("NFS: server cheating in pathname result: "
|
|
|
|
+ "length %u > received %u\n", length, recvd);
|
|
|
|
+ return -EIO;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 2.3.9. attrstat
|
|
|
|
+ *
|
|
|
|
+ * union attrstat switch (stat status) {
|
|
|
|
+ * case NFS_OK:
|
|
|
|
+ * fattr attributes;
|
|
|
|
+ * default:
|
|
|
|
+ * void;
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result)
|
|
|
|
+{
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ error = decode_stat(xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+ error = decode_fattr(xdr, result);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* 2.3.10. diropargs
|
|
* 2.3.10. diropargs
|
|
*
|
|
*
|
|
@@ -289,6 +502,48 @@ static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
|
|
encode_filename(xdr, name, length);
|
|
encode_filename(xdr, name, length);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * 2.3.11. diropres
|
|
|
|
+ *
|
|
|
|
+ * union diropres switch (stat status) {
|
|
|
|
+ * case NFS_OK:
|
|
|
|
+ * struct {
|
|
|
|
+ * fhandle file;
|
|
|
|
+ * fattr attributes;
|
|
|
|
+ * } diropok;
|
|
|
|
+ * default:
|
|
|
|
+ * void;
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
|
|
|
|
+{
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ error = decode_fhandle(xdr, result->fh);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ error = decode_fattr(xdr, result->fattr);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
|
|
|
|
+{
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ error = decode_stat(xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+ error = decode_diropok(xdr, result);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
/*
|
|
/*
|
|
* NFSv2 XDR encode functions
|
|
* NFSv2 XDR encode functions
|
|
@@ -630,13 +885,6 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
|
|
return pglen;
|
|
return pglen;
|
|
}
|
|
}
|
|
|
|
|
|
-static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
|
|
|
|
-{
|
|
|
|
- dprintk("nfs: %s: prematurely hit end of receive buffer. "
|
|
|
|
- "Remaining buffer length is %tu words.\n",
|
|
|
|
- func, xdr->end - xdr->p);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
__be32 *
|
|
__be32 *
|
|
nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
|
|
nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
|
|
{
|
|
{
|
|
@@ -700,6 +948,25 @@ nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
|
|
return status;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nfs2_xdr_dec_stat(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ void *__unused)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ error = decode_stat(&xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Decode attrstat reply
|
|
* Decode attrstat reply
|
|
* GETATTR, SETATTR, WRITE
|
|
* GETATTR, SETATTR, WRITE
|
|
@@ -715,6 +982,15 @@ nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ struct nfs_fattr *result)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ return decode_attrstat(&xdr, result);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Decode diropres reply
|
|
* Decode diropres reply
|
|
* LOOKUP, CREATE, MKDIR
|
|
* LOOKUP, CREATE, MKDIR
|
|
@@ -731,6 +1007,15 @@ nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ struct nfs_diropok *result)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ return decode_diropres(&xdr, result);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Decode READLINK reply
|
|
* Decode READLINK reply
|
|
*/
|
|
*/
|
|
@@ -771,6 +1056,70 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * 2.2.6. readlinkres
|
|
|
|
+ *
|
|
|
|
+ * union readlinkres switch (stat status) {
|
|
|
|
+ * case NFS_OK:
|
|
|
|
+ * path data;
|
|
|
|
+ * default:
|
|
|
|
+ * void;
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ void *__unused)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ error = decode_stat(&xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+ error = decode_path(&xdr);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 2.2.7. readres
|
|
|
|
+ *
|
|
|
|
+ * union readres switch (stat status) {
|
|
|
|
+ * case NFS_OK:
|
|
|
|
+ * fattr attributes;
|
|
|
|
+ * nfsdata data;
|
|
|
|
+ * default:
|
|
|
|
+ * void;
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+static int nfs2_xdr_dec_readres(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ struct nfs_readres *result)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ error = decode_stat(&xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+ error = decode_fattr(&xdr, result->fattr);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ error = decode_nfsdata(&xdr, result);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Decode WRITE reply
|
|
* Decode WRITE reply
|
|
*/
|
|
*/
|
|
@@ -781,6 +1130,150 @@ nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
|
|
return nfs_xdr_attrstat(req, p, res->fattr);
|
|
return nfs_xdr_attrstat(req, p, res->fattr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ struct nfs_writeres *result)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+
|
|
|
|
+ /* All NFSv2 writes are "file sync" writes */
|
|
|
|
+ result->verf->committed = NFS_FILE_SYNC;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ return decode_attrstat(&xdr, result->fattr);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
|
|
|
|
+ * the local page cache.
|
|
|
|
+ * @xdr: XDR stream where entry resides
|
|
|
|
+ * @entry: buffer to fill in with entry data
|
|
|
|
+ * @server: nfs_server data for this directory
|
|
|
|
+ * @plus: boolean indicating whether this should be a readdirplus entry
|
|
|
|
+ *
|
|
|
|
+ * Returns the position of the next item in the buffer, or an ERR_PTR.
|
|
|
|
+ *
|
|
|
|
+ * 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.
|
|
|
|
+ *
|
|
|
|
+ * 2.2.17. entry
|
|
|
|
+ *
|
|
|
|
+ * struct entry {
|
|
|
|
+ * unsigned fileid;
|
|
|
|
+ * filename name;
|
|
|
|
+ * nfscookie cookie;
|
|
|
|
+ * entry *nextentry;
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+__be32 *nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
|
|
|
|
+ struct nfs_server *server, int plus)
|
|
|
|
+{
|
|
|
|
+ __be32 *p;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ 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 ERR_PTR(-EAGAIN);
|
|
|
|
+ entry->eof = 1;
|
|
|
|
+ return ERR_PTR(-EBADCOOKIE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, 4);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ entry->ino = be32_to_cpup(p);
|
|
|
|
+
|
|
|
|
+ error = decode_filename_inline(xdr, &entry->name, &entry->len);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ return ERR_PTR(error);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The type (size and byte order) of nfscookie isn't defined in
|
|
|
|
+ * RFC 1094. This implementation assumes that it's an XDR uint32.
|
|
|
|
+ */
|
|
|
|
+ entry->prev_cookie = entry->cookie;
|
|
|
|
+ p = xdr_inline_decode(xdr, 4);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ entry->cookie = be32_to_cpup(p);
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return ERR_PTR(-EAGAIN);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 2.2.17. readdirres
|
|
|
|
+ *
|
|
|
|
+ * union readdirres switch (stat status) {
|
|
|
|
+ * case NFS_OK:
|
|
|
|
+ * struct {
|
|
|
|
+ * entry *entries;
|
|
|
|
+ * bool eof;
|
|
|
|
+ * } readdirok;
|
|
|
|
+ * default:
|
|
|
|
+ * void;
|
|
|
|
+ * };
|
|
|
|
+ *
|
|
|
|
+ * Read the directory contents into the page cache, but don't
|
|
|
|
+ * touch them. The actual decoding is done by nfs2_decode_dirent()
|
|
|
|
+ * during subsequent nfs_readdir() calls.
|
|
|
|
+ */
|
|
|
|
+static int decode_readdirok(struct xdr_stream *xdr)
|
|
|
|
+{
|
|
|
|
+ u32 recvd, pglen;
|
|
|
|
+ size_t hdrlen;
|
|
|
|
+
|
|
|
|
+ pglen = xdr->buf->page_len;
|
|
|
|
+ hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
|
|
|
+ recvd = xdr->buf->len - hdrlen;
|
|
|
|
+ if (unlikely(pglen > recvd))
|
|
|
|
+ goto out_cheating;
|
|
|
|
+out:
|
|
|
|
+ xdr_read_pages(xdr, pglen);
|
|
|
|
+ return pglen;
|
|
|
|
+out_cheating:
|
|
|
|
+ dprintk("NFS: server cheating in readdir result: "
|
|
|
|
+ "pglen %u > recvd %u\n", pglen, recvd);
|
|
|
|
+ pglen = recvd;
|
|
|
|
+ goto out;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ void *__unused)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ error = decode_stat(&xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+ error = decode_readdirok(&xdr);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Decode STATFS reply
|
|
* Decode STATFS reply
|
|
*/
|
|
*/
|
|
@@ -800,6 +1293,61 @@ nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * 2.2.18. statfsres
|
|
|
|
+ *
|
|
|
|
+ * union statfsres (stat status) {
|
|
|
|
+ * case NFS_OK:
|
|
|
|
+ * struct {
|
|
|
|
+ * unsigned tsize;
|
|
|
|
+ * unsigned bsize;
|
|
|
|
+ * unsigned blocks;
|
|
|
|
+ * unsigned bfree;
|
|
|
|
+ * unsigned bavail;
|
|
|
|
+ * } info;
|
|
|
|
+ * default:
|
|
|
|
+ * void;
|
|
|
|
+ * };
|
|
|
|
+ */
|
|
|
|
+static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
|
|
|
|
+{
|
|
|
|
+ __be32 *p;
|
|
|
|
+
|
|
|
|
+ p = xdr_inline_decode(xdr, NFS_info_sz << 2);
|
|
|
|
+ if (unlikely(p == NULL))
|
|
|
|
+ goto out_overflow;
|
|
|
|
+ result->tsize = be32_to_cpup(p++);
|
|
|
|
+ result->bsize = be32_to_cpup(p++);
|
|
|
|
+ result->blocks = be32_to_cpup(p++);
|
|
|
|
+ result->bfree = be32_to_cpup(p++);
|
|
|
|
+ result->bavail = be32_to_cpup(p);
|
|
|
|
+ return 0;
|
|
|
|
+out_overflow:
|
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, __be32 *p,
|
|
|
|
+ struct nfs2_fsstat *result)
|
|
|
|
+{
|
|
|
|
+ struct xdr_stream xdr;
|
|
|
|
+ enum nfs_stat status;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
|
|
|
|
+ error = decode_stat(&xdr, &status);
|
|
|
|
+ if (unlikely(error))
|
|
|
|
+ goto out;
|
|
|
|
+ if (status != NFS_OK)
|
|
|
|
+ goto out_default;
|
|
|
|
+ error = decode_info(&xdr, result);
|
|
|
|
+out:
|
|
|
|
+ return error;
|
|
|
|
+out_default:
|
|
|
|
+ return nfs_stat_to_errno(status);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* We need to translate between nfs status return values and
|
|
* We need to translate between nfs status return values and
|
|
* the local errno values which may not be the same.
|
|
* the local errno values which may not be the same.
|
|
@@ -867,7 +1415,7 @@ int nfs_stat_to_errno(enum nfs_stat status)
|
|
[NFSPROC_##proc] = { \
|
|
[NFSPROC_##proc] = { \
|
|
.p_proc = NFSPROC_##proc, \
|
|
.p_proc = NFSPROC_##proc, \
|
|
.p_encode = (kxdrproc_t)nfs2_xdr_enc_##argtype, \
|
|
.p_encode = (kxdrproc_t)nfs2_xdr_enc_##argtype, \
|
|
- .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
|
|
|
|
|
|
+ .p_decode = (kxdrproc_t)nfs2_xdr_dec_##restype, \
|
|
.p_arglen = NFS_##argtype##_sz, \
|
|
.p_arglen = NFS_##argtype##_sz, \
|
|
.p_replen = NFS_##restype##_sz, \
|
|
.p_replen = NFS_##restype##_sz, \
|
|
.p_timer = timer, \
|
|
.p_timer = timer, \
|