summaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfsxdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd/nfsxdr.c')
-rw-r--r--fs/nfsd/nfsxdr.c470
1 files changed, 470 insertions, 0 deletions
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
new file mode 100644
index 000000000..34b9ff3f2
--- /dev/null
+++ b/fs/nfsd/nfsxdr.c
@@ -0,0 +1,470 @@
+/*
+ * linux/fs/nfsd/xdr.c
+ *
+ * XDR support for nfsd
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/xdr.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_XDR
+
+u32 nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
+ nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
+ nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
+ nfserr_nametoolong, nfserr_dquot, nfserr_stale;
+
+#ifdef NFSD_OPTIMIZE_SPACE
+# define inline
+#endif
+
+/*
+ * Mapping of S_IF* types to NFS file types
+ */
+static u32 nfs_ftypes[] = {
+ NFNON, NFFIFO, NFCHR, NFBAD,
+ NFDIR, NFBAD, NFBLK, NFBAD,
+ NFREG, NFBAD, NFLNK, NFBAD,
+ NFSOCK, NFBAD, NFLNK, NFBAD,
+};
+
+/*
+ * Initialization of NFS status variables
+ */
+void
+nfsd_xdr_init(void)
+{
+ static int inited = 0;
+
+ if (inited)
+ return;
+
+ nfs_ok = htonl(NFS_OK);
+ nfserr_perm = htonl(NFSERR_PERM);
+ nfserr_noent = htonl(NFSERR_NOENT);
+ nfserr_io = htonl(NFSERR_IO);
+ nfserr_nxio = htonl(NFSERR_NXIO);
+ nfserr_acces = htonl(NFSERR_ACCES);
+ nfserr_exist = htonl(NFSERR_EXIST);
+ nfserr_nodev = htonl(NFSERR_NODEV);
+ nfserr_notdir = htonl(NFSERR_NOTDIR);
+ nfserr_isdir = htonl(NFSERR_ISDIR);
+ nfserr_fbig = htonl(NFSERR_FBIG);
+ nfserr_nospc = htonl(NFSERR_NOSPC);
+ nfserr_rofs = htonl(NFSERR_ROFS);
+ nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
+ nfserr_dquot = htonl(NFSERR_DQUOT);
+ nfserr_stale = htonl(NFSERR_STALE);
+
+ inited = 1;
+}
+
+/*
+ * XDR functions for basic NFS types
+ */
+static inline u32 *
+decode_fh(u32 *p, struct svc_fh *fhp)
+{
+ fh_init(fhp);
+ memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh));
+
+ /* FIXME: Look up export pointer here and verify
+ * Sun Secure RPC if requested */
+ return p + (sizeof(struct knfs_fh) >> 2);
+}
+
+static inline u32 *
+encode_fh(u32 *p, struct svc_fh *fhp)
+{
+ memcpy(p, &fhp->fh_handle, sizeof(struct knfs_fh));
+ return p + (sizeof(struct knfs_fh) >> 2);
+}
+
+/*
+ * Decode a file name and make sure that the path contains
+ * no slashes or null bytes.
+ */
+static inline u32 *
+decode_filename(u32 *p, char **namp, int *lenp)
+{
+ char *name;
+ int i;
+
+ if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) {
+ for (i = 0, name = *namp; i < *lenp; i++, name++) {
+ if (*name == '\0' || *name == '/')
+ return NULL;
+ }
+ *name = '\0';
+ }
+
+ return p;
+}
+
+static inline u32 *
+decode_pathname(u32 *p, char **namp, int *lenp)
+{
+ char *name;
+ int i;
+
+ if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
+ for (i = 0, name = *namp; i < *lenp; i++, name++) {
+ if (*name == '\0')
+ return NULL;
+ }
+ *name = '\0';
+ }
+
+ return p;
+}
+
+static inline u32 *
+decode_sattr(u32 *p, struct iattr *iap)
+{
+ u32 tmp, tmp1;
+
+ iap->ia_valid = 0;
+
+ /* Sun client bug compatibility check: some sun clients seem to
+ * put 0xffff in the mode field when they mean 0xffffffff.
+ * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah.
+ */
+ if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) {
+ iap->ia_valid |= ATTR_MODE;
+ iap->ia_mode = tmp;
+ }
+ if ((tmp = ntohl(*p++)) != (u32)-1) {
+ iap->ia_valid |= ATTR_UID;
+ iap->ia_uid = tmp;
+ }
+ if ((tmp = ntohl(*p++)) != (u32)-1) {
+ iap->ia_valid |= ATTR_GID;
+ iap->ia_gid = tmp;
+ }
+ if ((tmp = ntohl(*p++)) != (u32)-1) {
+ iap->ia_valid |= ATTR_SIZE;
+ iap->ia_size = tmp;
+ }
+ tmp = ntohl(*p++); tmp1 = ntohl(*p++);
+ if (tmp != (u32)-1 && tmp1 != (u32)-1) {
+ iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
+ iap->ia_atime = tmp;
+ }
+ tmp = ntohl(*p++); tmp1 = ntohl(*p++);
+ if (tmp != (u32)-1 && tmp1 != (u32)-1) {
+ iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
+ iap->ia_mtime = tmp;
+ }
+ return p;
+}
+
+static inline u32 *
+encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+{
+ if (!inode)
+ return 0;
+ *p++ = htonl(nfs_ftypes[(inode->i_mode & S_IFMT) >> 12]);
+ *p++ = htonl((u32) inode->i_mode);
+ *p++ = htonl((u32) inode->i_nlink);
+ *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
+ *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
+ if (S_ISLNK(inode->i_mode) && inode->i_size > NFS_MAXPATHLEN) {
+ *p++ = htonl(NFS_MAXPATHLEN);
+ } else {
+ *p++ = htonl((u32) inode->i_size);
+ }
+ *p++ = htonl((u32) inode->i_blksize);
+ *p++ = htonl((u32) inode->i_rdev);
+ *p++ = htonl((u32) inode->i_blocks);
+ *p++ = htonl((u32) inode->i_dev);
+ *p++ = htonl((u32) inode->i_ino);
+ *p++ = htonl((u32) inode->i_atime);
+ *p++ = 0;
+ *p++ = htonl((u32) inode->i_mtime);
+ *p++ = 0;
+ *p++ = htonl((u32) inode->i_ctime);
+ *p++ = 0;
+
+ return p;
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_argbuf;
+
+ return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_resbuf;
+
+ buf->len = p - buf->base;
+ dprintk("nfsd: ressize_check p %p base %p len %d\n",
+ p, buf->base, buf->buflen);
+ return (buf->len <= buf->buflen);
+}
+
+/*
+ * XDR decode functions
+ */
+int
+nfssvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ if (!(p = decode_fh(p, fhp)))
+ return 0;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_sattrargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_sattr(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_diropargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+
+ args->offset = ntohl(*p++);
+ args->count = ntohl(*p++);
+ args->totalsize = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_writeargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+
+ p++; /* beginoffset */
+ args->offset = ntohl(*p++); /* offset */
+ p++; /* totalcount */
+ args->len = ntohl(*p++);
+ args->data = (char *) p;
+ p += XDR_QUADLEN(args->len);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_createargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len))
+ || !(p = decode_sattr(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_renameargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_filename(p, &args->fname, &args->flen))
+ || !(p = decode_fh(p, &args->tfh))
+ || !(p = decode_filename(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_linkargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_fh(p, &args->tfh))
+ || !(p = decode_filename(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_symlinkargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_filename(p, &args->fname, &args->flen))
+ || !(p = decode_pathname(p, &args->tname, &args->tlen))
+ || !(p = decode_sattr(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readdirargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+ args->cookie = ntohl(*p++);
+ args->count = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+int
+nfssvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_attrstat *resp)
+{
+ if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+ return 0;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_diropres *resp)
+{
+ if (!(p = encode_fh(p, &resp->fh))
+ || !(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+ return 0;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readlinkres *resp)
+{
+ *p++ = htonl(resp->len);
+ p += XDR_QUADLEN(resp->len);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readres *resp)
+{
+ if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+ return 0;
+ *p++ = htonl(resp->count);
+ p += XDR_QUADLEN(resp->count);
+
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readdirres *resp)
+{
+ p += XDR_QUADLEN(resp->count);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_statfsres *resp)
+{
+ struct statfs *stat = &resp->stats;
+
+ *p++ = htonl(8 * 1024); /* max transfer size */
+ *p++ = htonl(stat->f_bsize);
+ *p++ = htonl(stat->f_blocks);
+ *p++ = htonl(stat->f_bfree);
+ *p++ = htonl(stat->f_bavail);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_entry(struct readdir_cd *cd, const char *name,
+ int namlen, off_t offset, ino_t ino)
+{
+ u32 *p = cd->buffer;
+ int buflen, slen;
+
+ /*
+ dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
+ namlen, name, offset, ino);
+ */
+
+ if (offset > ~((u32) 0))
+ return -EINVAL;
+ if (cd->offset)
+ *cd->offset = htonl(offset);
+ if (namlen > NFS2_MAXNAMLEN)
+ namlen = NFS2_MAXNAMLEN;/* truncate filename */
+
+ slen = XDR_QUADLEN(namlen);
+ if ((buflen = cd->buflen - slen - 4) < 0) {
+ cd->eob = 1;
+ return -EINVAL;
+ }
+ *p++ = xdr_one; /* mark entry present */
+ *p++ = htonl((u32) ino); /* file id */
+ *p++ = htonl((u32) namlen); /* name length & name */
+ memcpy(p, name, namlen);
+ p += slen;
+ cd->offset = p; /* remember pointer */
+ *p++ = ~(u32) 0; /* offset of next entry */
+
+ cd->buflen = buflen;
+ cd->buffer = p;
+ return 0;
+}
+
+/*
+ * XDR release functions
+ */
+int
+nfssvc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_fhandle *resp)
+{
+ fh_put(&resp->fh);
+ return 1;
+}