summaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs2xdr.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
commit59223edaa18759982db0a8aced0e77457d10c68e (patch)
tree89354903b01fa0a447bffeefe00df3044495db2e /fs/nfs/nfs2xdr.c
parentdb7d4daea91e105e3859cf461d7e53b9b77454b2 (diff)
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'fs/nfs/nfs2xdr.c')
-rw-r--r--fs/nfs/nfs2xdr.c202
1 files changed, 104 insertions, 98 deletions
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index a4c4e86d5..1bc7d3d37 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -56,11 +56,12 @@ static int nfs_stat_to_errno(int stat);
#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
#define NFS_readdirargs_sz NFS_fhandle_sz+2
+#define NFS_readlinkargs_sz NFS_fhandle_sz
#define NFS_dec_void_sz 0
#define NFS_attrstat_sz 1+NFS_fattr_sz
#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz 1+NFS_path_sz
+#define NFS_readlinkres_sz 1
#define NFS_readres_sz 1+NFS_fattr_sz+1
#define NFS_stat_sz 1
#define NFS_readdirres_sz 1
@@ -198,7 +199,6 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
*p++ = htonl(args->count);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-#if 1
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
buflen = req->rq_rvec[0].iov_len;
@@ -209,10 +209,6 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
req->rq_rvec[2].iov_len = buflen - replen;
req->rq_rlen = args->count + buflen;
req->rq_rnr = 3;
-#else
- replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
- req->rq_rvec[0].iov_len = replen;
-#endif
return 0;
}
@@ -359,133 +355,107 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
{
struct rpc_task *task = req->rq_task;
struct rpc_auth *auth = task->tk_auth;
- u32 bufsiz = args->bufsiz;
+ int bufsiz = args->bufsiz;
int replen;
- /*
- * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(args->cookie);
+
+ /* Some servers (e.g. HP OS 9.5) seem to expect the buffer size
* to be in longwords ... check whether to convert the size.
*/
if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
- bufsiz = bufsiz >> 2;
+ *p++ = htonl(bufsiz >> 2);
+ else
+ *p++ = htonl(bufsiz);
- p = xdr_encode_fhandle(p, args->fh);
- *p++ = htonl(args->cookie);
- *p++ = htonl(bufsiz); /* see above */
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
- /*
- dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
- RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
- */
req->rq_rvec[0].iov_len = replen;
req->rq_rvec[1].iov_base = args->buffer;
- req->rq_rvec[1].iov_len = args->bufsiz;
- req->rq_rlen = replen + args->bufsiz;
+ req->rq_rvec[1].iov_len = bufsiz;
+ req->rq_rlen = replen + bufsiz;
req->rq_rnr = 2;
- /*
- dprintk("RPC: readdirargs set up reply vec:\n");
- dprintk(" rvec[0] = %p/%d\n",
- req->rq_rvec[0].iov_base,
- req->rq_rvec[0].iov_len);
- dprintk(" rvec[1] = %p/%d\n",
- req->rq_rvec[1].iov_base,
- req->rq_rvec[1].iov_len);
- */
-
return 0;
}
/*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- * entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
+ * Decode the result of a readdir call.
*/
+#define NFS_DIRENT_MAXLEN (5 * sizeof(u32) + (NFS_MAXNAMLEN + 1))
static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{
struct iovec *iov = req->rq_rvec;
int status, nr;
- char *string, *start;
- u32 *end, *entry, len, fileid, cookie;
+ u32 *end;
+ u32 last_cookie = res->cookie;
- if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ status = ntohl(*p++);
+ if (status) {
+ nr = -nfs_stat_to_errno(status);
+ goto error;
+ }
if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
/* Unexpected reply header size. Punt. */
printk("NFS: Odd RPC header size in readdirres reply\n");
- return -errno_NFSERR_IO;
+ nr = -errno_NFSERR_IO;
+ goto error;
}
- /* Get start and end address of XDR data */
+ /* Get start and end address of XDR readdir response. */
p = (u32 *) iov[1].iov_base;
end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
- /* Get start and end of dirent buffer */
- entry = (u32 *) res->buffer;
- start = (char *) res->buffer;
- string = (char *) res->buffer + res->bufsiz;
for (nr = 0; *p++; nr++) {
- fileid = ntohl(*p++);
+ __u32 len;
+
+ /* Convert fileid. */
+ *p = ntohl(*p);
+ p++;
+
+ /* Convert and capture len */
+ len = *p = ntohl(*p);
+ p++;
- len = ntohl(*p++);
- /*
- * Check whether the server has exceeded our reply buffer,
- * and set a flag to convert the size to longwords.
- */
if ((p + QUADLEN(len) + 3) > end) {
struct rpc_clnt *clnt = req->rq_task->tk_client;
- printk(KERN_WARNING
- "NFS: server %s, readdir reply truncated\n",
- clnt->cl_server);
- printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
- nr, (end - p), len);
+
clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
+ p -= 2;
+ p[-1] = 0;
+ p[0] = 0;
break;
}
if (len > NFS_MAXNAMLEN) {
- printk("NFS: giant filename in readdir (len %x)!\n",
- len);
- return -errno_NFSERR_IO;
+ nr = -errno_NFSERR_IO;
+ goto error;
}
- string -= len;
- if ((void *) (entry+3) > (void *) string) {
- /*
- * This error is impossible as long as the temp
- * buffer is no larger than the user buffer. The
- * current packing algorithm uses the same amount
- * of space in the user buffer as in the XDR data,
- * so it's guaranteed to fit.
- */
- printk("NFS: incorrect buffer size in %s!\n",
- __FUNCTION__);
- break;
- }
-
- memmove(string, p, len);
p += QUADLEN(len);
- cookie = ntohl(*p++);
- /*
- * To make everything fit, we encode the length, offset,
- * and eof flag into 32 bits. This works for filenames
- * up to 32K and PAGE_SIZE up to 64K.
- */
- status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
- *entry++ = fileid;
- *entry++ = cookie;
- *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
+
+ /* Convert and capture cookie. */
+ last_cookie = *p = ntohl(*p);
+ p++;
}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
+ p -= 1;
+ status = ((end - p) << 2);
+ if (!p[1] && (status >= NFS_DIRENT_MAXLEN)) {
+ status = ((__u8 *)p - (__u8 *)iov[1].iov_base);
+ res->buffer += status;
+ res->bufsiz -= status;
+ } else if (p[1]) {
+ status = (int)((long)p & ~PAGE_CACHE_MASK);
+ res->bufsiz = -status;
+ } else {
+ res->bufsiz = 0;
+ }
+ res->cookie = last_cookie;
+ return nr;
+
+error:
+ res->bufsiz = 0;
return nr;
}
@@ -553,20 +523,56 @@ nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
}
/*
+ * Encode arguments to readlink call
+ */
+static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+{
+ struct rpc_task *task = req->rq_task;
+ struct rpc_auth *auth = task->tk_auth;
+ int bufsiz = NFS_MAXPATHLEN;
+ int replen;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
+ req->rq_rvec[0].iov_len = replen;
+ req->rq_rvec[1].iov_base = (void *) args->buffer;
+ req->rq_rvec[1].iov_len = bufsiz;
+ req->rq_rlen = replen + bufsiz;
+ req->rq_rnr = 2;
+
+ return 0;
+}
+
+/*
* Decode READLINK reply
*/
static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
{
- int status;
+ struct iovec *iov = req->rq_rvec;
+ int status, len;
+ char *name;
- if ((status = ntohl(*p++)))
+ /* Verify OK status. */
+ if ((status = ntohl(*p++)) != 0)
return -nfs_stat_to_errno(status);
- xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
- /* Caller takes over the buffer here to avoid extra copy */
- res->buffer = req->rq_task->tk_buffer;
- req->rq_task->tk_buffer = NULL;
+ /* Verify OK response length. */
+ if ((__u8 *)p != ((u8 *) iov->iov_base + iov->iov_len))
+ return -errno_NFSERR_IO;
+
+ /* Convert and verify that string length is in range. */
+ p = iov[1].iov_base;
+ len = *p = ntohl(*p);
+ p++;
+ if (len > iov[1].iov_len)
+ return -errno_NFSERR_IO;
+
+ /* NULL terminate the string we got. */
+ name = (char *) p;
+ name[len] = 0;
+
return 0;
}
@@ -653,7 +659,7 @@ static struct rpc_procinfo nfs_procedures[18] = {
PROC(setattr, sattrargs, attrstat),
PROC(root, enc_void, dec_void),
PROC(lookup, diropargs, diropres),
- PROC(readlink, fhandle, readlinkres),
+ PROC(readlink, readlinkargs, readlinkres),
PROC(read, readargs, readres),
PROC(writecache, enc_void, dec_void),
PROC(write, writeargs, attrstat),