/*
 * Copyright 1993 by Ulrich Khn. All rights reserved.
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
 * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
 * RISK.
 */

/*
 * File : nfs_xdr.c
 *        some utility functions for dealing with the nfs xdr types
 */

 
#include <string.h>
#include "types.h"
#include "nfs.h"
#include "xdr.h"




bool_t
xdr_nfsstat(xdrs *x, nfsstat *sp)
{
	return xdr_enum(x, (enum_t*)sp);
}

long
xdr_size_nfsstat(nfsstat *sp)
{
	return sizeof(u_long);
}


bool_t
xdr_ftype(xdrs *x, ftype *fp)
{
	return xdr_enum(x, (int*)fp);
}


bool_t
xdr_nfscookie(xdrs *x, nfscookie cookie)
{
	return xdr_fixedopaq(x, cookie, COOKIESIZE);
}


bool_t
xdr_nfsfh(xdrs *x, nfs_fh *fp)
{
	return xdr_fixedopaq(x, fp->data, FHSIZE);
}


bool_t
xdr_nfstime(xdrs *x, nfstime *tp)
{
	if (!xdr_ulong(x, &tp->seconds))
		return FALSE;
	return xdr_ulong(x, &tp->useconds);
}


bool_t
xdr_fattr(xdrs *x, fattr *fp)
{
	long *buf;

	if (XDR_FREE == x->op)
		return TRUE;
	xdr_ftype(x, &fp->type);
#ifndef NO_INLINE
	buf = xdr_inline(x, 10*BYTES_PER_XDR_UNIT);
#else
	buf = NULL;
#endif
	if (NULL != buf)
	{
		if (XDR_DECODE == x->op)
		{
			fp->mode = IXDR_GET_ULONG(buf);
			fp->nlink = IXDR_GET_ULONG(buf);
			fp->uid = IXDR_GET_ULONG(buf);
			fp->gid = IXDR_GET_ULONG(buf);
			fp->size = IXDR_GET_ULONG(buf);
			fp->blocksize = IXDR_GET_ULONG(buf);
			fp->rdev = IXDR_GET_ULONG(buf);
			fp->blocks = IXDR_GET_ULONG(buf);
			fp->fsid = IXDR_GET_ULONG(buf);
			fp->fileid = IXDR_GET_ULONG(buf);
		}
		else if (XDR_ENCODE == x->op)
		{
			IXDR_PUT_ULONG(buf, fp->mode);
			IXDR_PUT_ULONG(buf, fp->nlink);
			IXDR_PUT_ULONG(buf, fp->uid);
			IXDR_PUT_ULONG(buf, fp->gid);
			IXDR_PUT_ULONG(buf, fp->size);
			IXDR_PUT_ULONG(buf, fp->blocksize);
			IXDR_PUT_ULONG(buf, fp->rdev);
			IXDR_PUT_ULONG(buf, fp->blocks);
			IXDR_PUT_ULONG(buf, fp->fsid);
			IXDR_PUT_ULONG(buf, fp->fileid);
		}
	}
	else
	{
		if (!xdr_ulong(x, &fp->mode))
			return FALSE;
		if (!xdr_ulong(x, &fp->nlink))
			return FALSE;
		if (!xdr_ulong(x, &fp->uid))
			return FALSE;
		if (!xdr_ulong(x, &fp->gid))
			return FALSE;
		if (!xdr_ulong(x, &fp->size))
			return FALSE;
		if (!xdr_ulong(x, &fp->blocksize))
			return FALSE;
		if (!xdr_ulong(x, &fp->rdev))
			return FALSE;
		if (!xdr_ulong(x, &fp->blocks))
			return FALSE;
		if (!xdr_ulong(x, &fp->fsid))
			return FALSE;
		if (!xdr_ulong(x, &fp->fileid))
			return FALSE;
	}
	if (!xdr_nfstime(x, &fp->atime))
			return FALSE;
	if (!xdr_nfstime(x, &fp->mtime))
			return FALSE;
	return xdr_nfstime(x, &fp->ctime);
}


bool_t
xdr_sattr(xdrs *x, sattr *sp)
{
	long *buf;

	if (XDR_FREE == x->op)
		return TRUE;
#ifndef NO_INLINE
	buf = xdr_inline(x, 4*BYTES_PER_XDR_UNIT);
#else
	buf = NULL;
#endif
	if (buf)
	{
		if (XDR_DECODE == x->op)
		{
			sp->mode = IXDR_GET_ULONG(buf);
			sp->uid = IXDR_GET_ULONG(buf);
			sp->gid = IXDR_GET_ULONG(buf);
			sp->size = IXDR_GET_ULONG(buf);
		}
		else if (XDR_ENCODE == x->op)
		{
			IXDR_PUT_ULONG(buf, sp->mode);
			IXDR_PUT_ULONG(buf, sp->uid);
			IXDR_PUT_ULONG(buf, sp->gid);
			IXDR_PUT_ULONG(buf, sp->size);
		}
	}
	else
	{
		if (!xdr_ulong(x, &sp->mode))
			return FALSE;
		if (!xdr_ulong(x, &sp->uid))
			return FALSE;
		if (!xdr_ulong(x, &sp->gid))
			return FALSE;
		if (!xdr_ulong(x, &sp->size))
			return FALSE;
	}
	if (!xdr_nfstime(x, &sp->atime))
			return FALSE;
	return xdr_nfstime(x, &sp->mtime);
}



bool_t
xdr_attrstat(xdrs *x, attrstat *ap)
{
	if (!xdr_enum(x, &ap->status))
		return FALSE;
	if (NFS_OK == ap->status)
		return xdr_fattr(x, &ap->attrstat_u.attributes);
	return TRUE;
}

long
xdr_size_attrstat(attrstat *ap)
{
	u_long r = sizeof(u_long);

	if (NFS_OK == ap->status)
		r += sizeof(fattr);
	return r;
}


bool_t
xdr_sattrargs(xdrs *x, sattrargs *argp)
{
	if (!xdr_nfsfh(x, &argp->file))
		return FALSE;
	return xdr_sattr(x, &argp->attributes);
}

long
xdr_size_sattrargs(sattrargs *ap)
{
	return xdr_size_nfsfh(&ap->file)+xdr_size_sattr(&ap->attributes);
}


bool_t
xdr_diropres(xdrs *x, diropres *rp)
{
	if (!xdr_enum(x, &rp->status))
		return FALSE;
	if (NFS_OK == rp->status)
	{
		if (!xdr_nfsfh(x, &rp->diropres_u.diropok.file))
			return FALSE;
		return xdr_fattr(x, &rp->diropres_u.diropok.attributes);
	}
	return TRUE;
}

long
xdr_size_diropres(diropres *rp)
{
	long r = sizeof(u_long);

	if (NFS_OK == rp->status)
	{
		r += xdr_size_nfsfh(&rp->diropres_u.diropok.file);
		r += xdr_size_fattr(&rp->diropres_u.diropok.attributes);
	}
	return r;
}


bool_t
xdr_diropargs(xdrs *x, diropargs *ap)
{
	if (!xdr_nfsfh(x, &ap->dir))
		return FALSE;
	return xdr_string(x, &ap->name, MAXNAMLEN);
}

long
xdr_size_diropargs(diropargs *ap)
{
	long r = xdr_size_nfsfh(&ap->dir);

	r += sizeof(u_long);
	r += (strlen(ap->name)+3)&(~3L);  /* round up to four bytes */
	return r;
}


bool_t
xdr_readlinkres(xdrs *x, readlinkres *rp)
{
	if (!xdr_enum(x, &rp->status))
		return FALSE;
	if (NFS_OK == rp->status)
		return xdr_string(x, &rp->readlinkres_u.data, MAXPATHLEN);
	return TRUE;
}

long
xdr_size_readlinkres(readlinkres *rp)
{
	long r = sizeof(u_long);

	if (NFS_OK == rp->status)
	{
		r += sizeof(u_long);
		r += (strlen(rp->readlinkres_u.data)+3)&(~3L);
	}
	return r;
}


bool_t
xdr_readres(xdrs *x, readres *rp)
{
	if (!xdr_enum(x, &rp->status))
		return FALSE;
	if (NFS_OK == rp->status)
	{
		if (!xdr_fattr(x, &rp->readres_u.read_ok.attributes))
			return FALSE;
		if (!xdr_opaque(x, &rp->readres_u.read_ok.data_val,
		                   &rp->readres_u.read_ok.data_len, MAXDATA))
			return FALSE;
	}
	return TRUE;
}

long
xdr_size_readres(readres *rp)
{
	long r = sizeof(u_long);

	if (NFS_OK == rp->status)
	{
		r += xdr_size_fattr(&rp->readres_u.read_ok.attributes);
		r += sizeof(u_long);
		r += (rp->readres_u.read_ok.data_len+3) & (~3L);
	}
	return r;
}


bool_t
xdr_readargs(xdrs *x, readargs *ap)
{
	if (!xdr_nfsfh(x, &ap->file))
		return FALSE;
	xdr_ulong(x, &ap->offset);
	xdr_ulong(x, &ap->count);
	return xdr_ulong(x, &ap->totalcount);
}

long
xdr_size_readargs(readargs *ap)
{
	return xdr_size_nfsfh(&ap->file)+3*sizeof(u_long);
}


bool_t
xdr_writeargs(xdrs *x, writeargs *ap)
{
	if (!xdr_nfsfh(x, &ap->file))
		return FALSE;
	xdr_ulong(x, &ap->beginoffset);
	xdr_ulong(x, &ap->offset);
	xdr_ulong(x, &ap->totalcount);
	return xdr_opaque(x, &ap->data_val, &ap->data_len, MAXDATA);
}

long
xdr_size_writeargs(writeargs *ap)
{
	long r = sizeof(nfs_fh);

	r += 4*sizeof(u_long);
	r += (ap->totalcount+3) & (~3L);
	return r;
}


bool_t
xdr_createargs(xdrs *x, createargs *ap)
{
	if (!xdr_diropargs(x, &ap->where))
		return FALSE;
	return xdr_sattr(x, &ap->attributes);
}

long
xdr_size_createargs(createargs *ap)
{
	return xdr_size_diropargs(&ap->where)+xdr_size_sattr(&ap->attributes);
}


bool_t
xdr_renameargs(xdrs *x, renameargs *ap)
{
	if (!xdr_diropargs(x, &ap->from))
		return FALSE;
	return xdr_diropargs(x, &ap->to);
}

long
xdr_size_renameargs(renameargs *ap)
{
	return xdr_size_diropargs(&ap->from)+xdr_size_diropargs(&ap->to);
}


bool_t
xdr_linkargs(xdrs *x, linkargs *ap)
{
	if (!xdr_nfsfh(x, &ap->from))
		return FALSE;
	return xdr_diropargs(x, &ap->to);
}

long
xdr_size_linkargs(linkargs *ap)
{
	return xdr_size_nfsfh(&ap->from)+xdr_size_diropargs(&ap->to);
}


bool_t
xdr_symlinkargs(xdrs *x, symlinkargs *ap)
{
	if (!xdr_diropargs(x, &ap->from))
		return FALSE;
	if (!xdr_string(x, &ap->to, MAXPATHLEN))
		return FALSE;
	return xdr_sattr(x, &ap->attributes);
}

long
xdr_size_symlinkargs(symlinkargs *ap)
{
	long r = xdr_size_diropargs(&ap->from);

	r += sizeof(u_long);
	r += (strlen(ap->to)+3)&(~3L);
	r += xdr_size_sattr(&ap->attributes);
	return r;
}


bool_t
xdr_entry(xdrs *x, entry *ep)
{
	if (!xdr_ulong(x, &ep->fileid))
		return FALSE;
	if (XDR_DECODE == x->op)
		ep->name = (char*)ep + sizeof(entry);
	if (!xdr_string(x, &ep->name, MAXNAMLEN))
		return FALSE;
	if (!xdr_nfscookie(x, ep->cookie))
		return FALSE;
	if (XDR_DECODE == x->op)
	{
		char *p = (char*)ep;

		p += sizeof(entry) + strlen(ep->name)+1;
		p = (char*)(((long)p+1)&(~1L));  /* round up */
		ep->nextentry = (entry*)p;
	}
	return xdr_pointer(x, (char**)&ep->nextentry,
	                          sizeof(entry), (xdrproc_t)xdr_entry);
}

long
xdr_size_entry(entry *ep)
{
	long r = 0;

	while (ep)
	{
		r += sizeof(u_long);
		r += sizeof(u_long);
		r += (strlen(ep->name)+3)&(~3L);
		r += xdr_size_nfscookie(ep->cookie);
		r += sizeof(u_long);
		ep = ep->nextentry;
	}
	return r;
}


bool_t
xdr_readdirres(xdrs *x, readdirres *rp)
{
	if (!xdr_enum(x, &rp->status))
		return FALSE;
	if (NFS_OK == rp->status)
	{
		if (!xdr_pointer(x, (char**)&rp->readdirres_u.readdirok.entries,
		                                sizeof(entry), (xdrproc_t)xdr_entry))
			return FALSE;
		return xdr_bool(x, &rp->readdirres_u.readdirok.eof);
	}
	return TRUE;
}

long
xdr_size_readdirres(readdirres *rp)
{
	long r = sizeof(u_long);

	if (NFS_OK == rp->status)
	{
		r += sizeof(u_long);   /* the first boolean "pointer" */
		r += xdr_size_entry(rp->readdirres_u.readdirok.entries);
		r += sizeof(u_long);
	}
	return r;
}


bool_t
xdr_readdirargs(xdrs *x, readdirargs *ap)
{
	if (!xdr_nfsfh(x, &ap->dir))
		return FALSE;
	if (!xdr_nfscookie(x, ap->cookie))
		return FALSE;
	return xdr_ulong(x, &ap->count);
}

long
xdr_size_readdirargs(readdirargs *ap)
{
	return xdr_size_nfsfh(&ap->dir)+
	              xdr_size_nfscookie(ap->cookie)+sizeof(u_long);
}


bool_t
xdr_statfsres(xdrs *x, statfsres *rp)
{
	if (!xdr_enum(x, &rp->status))
		return FALSE;
	if (NFS_OK == rp->status)
	{
		xdr_ulong(x, &rp->statfsres_u.info.tsize);
		xdr_ulong(x, &rp->statfsres_u.info.bsize);
		xdr_ulong(x, &rp->statfsres_u.info.blocks);
		xdr_ulong(x, &rp->statfsres_u.info.bfree);
		return xdr_ulong(x, &rp->statfsres_u.info.bavail);
	}
	return TRUE;
}

long
xdr_size_statfsres(statfsres *rp)
{
	long r = sizeof(u_long);

	if (NFS_OK == rp->status)
		r += 5*sizeof(u_long);
	return r;
}
