/*
 * Name: nfs_funcs.c
 * Description: This module contains the dispatcher for the nfs server
 *     functions and stubs for these functions. They provide their service
 *     based on the functions of 'fo_nfs.c'. The common interface is
 *     defined in 'my_defines.h'.
 * Author: Christian Starkjohann <cs@hal.kph.tuwien.ac.at>
 * Date: 1996-12-14
 * Copyright: GNU-GPL
 * Tabsize: 4
 */
#define NFSCLIENT
#include "syshdr.h"
#include <rpc/rpc.h>
#include "my_defines.h"
#include "nfs_prot.h"

#define	DPRINTF(arg)	if(debug_mode &	DEBUG_NFS)			debprintf arg
#define	NDPRINTF(arg)	if(debug_mode &	DEBUG_NFS_NOISY)	debprintf arg

#define	NFS_MAX_GROUPS	50

int		nfs_uid;
int		nfs_gid;
short	nfs_gids[NFS_MAX_GROUPS];
int		nfs_gidslen;	/* gids in nfs_gids */

/* ------------------------------------------------------------------------- */

#define	FH(fh)	((fh)->data[0])
#define	FHP(fh)	(&(fh)->data[0])

/* ------------------------------------------------------------------------- */

static inline void	set_ids(struct svc_req *rqstp)
{
#if 0	/* currently no authentication used */
int		i;

	if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
		struct authunix_parms *unix_cred;
		unix_cred = (struct authunix_parms *) rqstp->rq_clntcred;
		nfs_uid = unix_cred->aup_uid;
		nfs_gid = unix_cred->aup_gid;
		nfs_gidslen = unix_cred->aup_len > NFS_MAX_GROUPS ?
										NFS_MAX_GROUPS : unix_cred->aup_len;
		for(i=0;i<nfs_gidslen;i++)
			nfs_gids[i] = unix_cred->aup_gids[i];
	} else {
		nfs_uid = -1;
		nfs_gid = -1;
		/* Construct a list of one gid. */
		nfs_gidslen = 1;
		nfs_gids[0] = -1;
	}
/*	set_current_ids(nfs_uid, nfs_gid);*/
#endif
}

/* ------------------------------------------------------------------------- */

static void	input_attr(my_attr_t *ma, sattr *sa)
{
	ma->mode = sa->mode;
	ma->nlink = -1;
	ma->uid = sa->uid;
	ma->gid = sa->gid;
	ma->size = sa->size;
	ma->blocksize = -1;
	ma->blocks = -1;
	ma->fileid = -1;
	ma->atime = sa->atime.seconds;	/* ignored in lower level */
	ma->mtime = sa->mtime.seconds;
	ma->ctime = -1;
}

/* ------------------------------------------------------------------------- */

static void	output_attr(my_attr_t *ma, fattr *fa)
{
	bzero(fa, sizeof(*fa));
	fa->type = NFNON;
	if((ma->mode & NFSMODE_FMT) == NFSMODE_DIR)
		fa->type = NFDIR;
	else if((ma->mode & NFSMODE_FMT) == NFSMODE_REG)
		fa->type = NFREG;
	else if((ma->mode & NFSMODE_FMT) == NFSMODE_LNK)
		fa->type = NFLNK;
	else if((ma->mode & NFSMODE_FMT) == NFSMODE_CHR)
		fa->type = NFCHR;
	else if((ma->mode & NFSMODE_FMT) == NFSMODE_BLK)
		fa->type = NFBLK;
	else if((ma->mode & NFSMODE_FMT) == NFSMODE_SOCK)
		fa->type = NFSOCK;
	else if((ma->mode & NFSMODE_FMT) == NFSMODE_FIFO)
		fa->type = NFFIFO;
	else{
		eprintf("** output_attr(): file type unknown: mode=0%o\n", ma->mode);
	}
	fa->mode = ma->mode;
	fa->nlink = ma->nlink;
	fa->uid = ma->uid;
	fa->gid = ma->gid;
	fa->size = ma->size;
	fa->blocksize = ma->blocksize;
	fa->blocks = ma->blocks;
	fa->fsid = 562654;	/* dummy non zero value */
	fa->fileid = ma->fileid;
#ifdef SOLARIS
	fa->atime.seconds = ma->atime & 0x7fffffff;
	fa->mtime.seconds = ma->mtime & 0x7fffffff;
	fa->ctime.seconds = ma->ctime & 0x7fffffff;
#else
	fa->atime.seconds = ma->atime;
	fa->mtime.seconds = ma->mtime;
	fa->ctime.seconds = ma->ctime;
#endif
	fa->rdev = ma->rdev;
	NDPRINTF(("output_attr(): attributes are:\n"));
	NDPRINTF(("  type      = %d\n", fa->type));
	NDPRINTF(("  mode      = 0%o\n", fa->mode));
	NDPRINTF(("  nlink     = %d\n", fa->nlink));
	NDPRINTF(("  uid       = %d\n", fa->uid));
	NDPRINTF(("  gid       = %d\n", fa->gid));
	NDPRINTF(("  size      = %d\n", fa->size));
	NDPRINTF(("  blocksize = %d\n", fa->blocksize));
	NDPRINTF(("  blocks    = %d\n", fa->blocks));
	NDPRINTF(("  fileid    = %d\n", fa->fileid));
}

/* ------------------------------------------------------------------------- */

static int	nfs_errno(int sys_errno)
{
	switch(sys_errno){
	case 0:
		return NFS_OK;
	case EPERM:
		return NFSERR_PERM;
	case ENOENT:
		return NFSERR_NOENT;
	case ENXIO:
		return NFSERR_NXIO;
	case ETXTBSY:
	case EWOULDBLOCK:
	case EACCES:
		return NFSERR_ACCES;
	case EEXIST:
		return NFSERR_EXIST;
	case ENODEV:
		return NFSERR_NODEV;
	case ENOTDIR:
		return NFSERR_NOTDIR;
	case EISDIR:
		return NFSERR_ISDIR;
	case E2BIG:
	case EFBIG:
		return NFSERR_FBIG;
	case ENOSPC:
		return NFSERR_NOSPC;
	case EROFS:
		return NFSERR_ROFS;
	case ENAMETOOLONG:
		return NFSERR_NAMETOOLONG;
	case ENOTEMPTY:
		return NFSERR_NOTEMPTY;
	case EDQUOT:
		return NFSERR_DQUOT;
	case MY_NFSERR_STALE:
		return NFSERR_STALE;
	default:	/* this is general enough for a reasonable error default */
		return NFSERR_IO;
	}
}

/* ------------------------------------------------------------------------- */

static struct diropres	*nfsproc_create(createargs *ca)
{
static diropres res;
my_attr_t	fa, sa;

	bzero(&res, sizeof(res));
	input_attr(&sa, &ca->attributes);
	res.status = nfs_errno(-fo_create(FHP(&res.diropres_u.diropres.file),
							&fa,
							FH(&ca->where.dir),
							ca->where.name,
							&sa));
	DPRINTF(("nfsproc_create(dir=%d, name=%s, size=%d)->%d, inode=%d\n",
				(int)FH(&ca->where.dir), ca->where.name, ca->attributes.size,
				res.status, (int)FH(&res.diropres_u.diropres.file)));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.diropres_u.diropres.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct diropres	*nfsproc_mkdir(createargs *ca)
{
static diropres res;
my_attr_t		fa, sa;

	bzero(&res, sizeof(res));
	input_attr(&sa, &ca->attributes);
	res.status = nfs_errno(-fo_mkdir(FHP(&res.diropres_u.diropres.file),
							&fa,
							FH(&(ca->where.dir)),
							ca->where.name,
							&sa));
	DPRINTF(("nfsproc_mkdir(dir=%d, name=%s)->%d, inode=%d\n",
				(int)FH(&ca->where.dir), ca->where.name, res.status,
				(int)FH(&res.diropres_u.diropres.file)));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.diropres_u.diropres.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct attrstat *nfsproc_getattr(struct nfs_fh *fh)
{
static attrstat	res;
my_attr_t		fa;

	bzero(&res, sizeof(res));
	res.status = nfs_errno(-fo_getattr(&fa, FH(fh)));
	DPRINTF(("nfsproc_getattr(fh=%d)->%d\n", (int)FH(fh), res.status));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.attrstat_u.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct diropres *nfsproc_lookup(diropargs *da)
{
static diropres res;
my_attr_t		fa;

	bzero(&res, sizeof(res));
	res.status = nfs_errno(-fo_lookup(FHP(&res.diropres_u.diropres.file),
							&fa,
							FH(&(da->dir)),
							da->name));
	DPRINTF(("nfsproc_lookup(dir=%d, name=%s)->%d, inode=%d\n",
				(int)FH(&da->dir), da->name, res.status,
				(int)FH(&res.diropres_u.diropres.file)));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.diropres_u.diropres.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */


static struct readdirres *nfsproc_readdir(readdirargs *ra)
{
static readdirres		res;
struct entry			*p, *q;
int						eof, rval;

	for(p=res.readdirres_u.reply.entries; p!=NULL; p=q){
		q = p->nextentry;
		free(p->name);
		free(p);
	}
	bzero(&res, sizeof(res));
	rval = fo_readdir((my_direntry_t **)&res.readdirres_u.reply.entries,
							&eof,
							ra->count,
							FH(&(ra->dir)),
							ntohl(*(int *)(ra->cookie)));
	for(p=res.readdirres_u.reply.entries; p!=NULL; p=p->nextentry){
		*(int *)p->cookie = htonl(*(int *)p->cookie);
	}
	res.status = rval < 0 ? nfs_errno(-rval) : NFS_OK;
	res.readdirres_u.reply.eof = eof;
	DPRINTF(("nfsproc_readdir(dir=%d, max=%d)->%d, EOF=%d\n",
							(int)FH(&ra->dir), ra->count, res.status, eof));
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct attrstat *nfsproc_setattr(sattrargs *sa)
{
static attrstat	res;
my_attr_t		msa, fa;

	bzero(&res, sizeof(res));
	input_attr(&msa, &sa->attributes);
	res.status = nfs_errno(-fo_setattr(&fa, FH(&(sa->file)), &msa));
	DPRINTF(("nfsproc_setattr(fh=%d)->%d\n", (int)FH(&sa->file),res.status));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.attrstat_u.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */

static nfsstat *nfsproc_remove(diropargs *da)
{
static nfsstat	res;

	bzero(&res, sizeof(res));
	res = nfs_errno(-fo_remove(FH(&(da->dir)), da->name));
	DPRINTF(("nfsproc_remove(dir=%d, name=%s)->%d\n",
										(int)FH(&da->dir), da->name, res));
	return &res;
}

/* ------------------------------------------------------------------------- */

static nfsstat *nfsproc_rmdir(diropargs *da)
{
static nfsstat	res;

	bzero(&res, sizeof(res));
	res = nfs_errno(-fo_rmdir(FH(&(da->dir)), da->name));
	DPRINTF(("nfsproc_rmdir(dir=%d, name=%s)->%d\n",
										(int)FH(&da->dir), da->name, res));
	return &res;
}

/* ------------------------------------------------------------------------- */

static nfsstat *nfsproc_rename(renameargs *ra)
{
static nfsstat	res;

	bzero(&res, sizeof(res));
	res = nfs_errno(-fo_rename(FH(&(ra->from.dir)), ra->from.name,
					FH(&(ra->to.dir)), ra->to.name));
	DPRINTF(("nfsproc_rename(dir=%d, name=%s -> dir=%d, name=%s)->%d\n",
					(int)FH(&ra->from.dir), ra->from.name,
					(int)FH(&ra->to.dir), ra->to.name, res));
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct statfsres *nfsproc_statfs(struct nfs_fh *fh)
{
static statfsres	res;
my_statfs_t			my_stat;

	bzero(&res, sizeof(res));
	res.status = nfs_errno(-fo_statfs(&my_stat));
	res.statfsres_u.reply.tsize = 32768;
	res.statfsres_u.reply.bsize = my_stat.bsize;
	res.statfsres_u.reply.blocks = my_stat.blocks;
	res.statfsres_u.reply.bfree = my_stat.bfree;
	res.statfsres_u.reply.bavail = my_stat.bavail;
	DPRINTF(("nfsproc_statfs()->%d\n", res.status));
	DPRINTF(("bsize=%ld blocks=%ld free=%ld avail=%ld\n",
		my_stat.bsize, my_stat.blocks, my_stat.bfree, my_stat.bavail));
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct readres *nfsproc_read(struct readargs *ra)
{
static readres	res;
my_attr_t		fa;

	bzero(&res, sizeof(res));
	res.status = nfs_errno(-fo_read(&fa,
						&res.readres_u.reply.data.data_len,
						&res.readres_u.reply.data.data_val,
						FH(&(ra->file)),
						ra->offset,
						ra->count));
	DPRINTF(("nfsproc_read(fh=%d, offs=%d, len=%d)->%d, len=%d\n",
				(int)FH(&ra->file), (int)ra->offset, (int)ra->count,
				res.status, (int)res.readres_u.reply.data.data_len));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.readres_u.reply.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct attrstat *nfsproc_write(writeargs *wa)
{
static attrstat	res;
my_attr_t		fa;

	bzero(&res, sizeof(res));
	res.status = nfs_errno(-fo_write(&fa,
						FH(&(wa->file)),
						wa->offset,
						wa->data.data_len,
						wa->data.data_val));
	DPRINTF(("nfsproc_write(fh=%d, offs=%d, len=%d)->%d, len=%d\n",
				(int)FH(&wa->file), (int)wa->offset, (int)wa->data.data_len,
				res.status, fa.size));
	if(res.status == NFS_OK)
		output_attr(&fa, &res.attrstat_u.attributes);
	return &res;
}

/* ------------------------------------------------------------------------- */

static nfsstat *nfsproc_link(linkargs *la)
{
static nfsstat	res;

	bzero(&res, sizeof(res));
	res = nfs_errno(-fo_link(FH(&(la->from)), FH(&(la->to.dir)), la->to.name));
	DPRINTF(("nfsproc_link(fh=%d, todir=%d, toname=%s)->%d\n",
				(int)FH(&la->from), (int)FH(&la->to.dir), la->to.name, res));
	return &res;
}

/* ------------------------------------------------------------------------- */

static struct readlinkres *nfsproc_readlink(struct nfs_fh *fh)
{
static readlinkres	res;
int					rval;

	bzero(&res, sizeof(res));
	rval = fo_readlink(&res.readlinkres_u.data, FH(fh));
	res.status = rval < 0 ? nfs_errno(-rval) : NFS_OK;
	DPRINTF(("nfsproc_readlink(fh=%d)->%d, path=%s\n",
						(int)FH(fh), res.status, res.readlinkres_u.data));
	return &res;
}

/* ------------------------------------------------------------------------- */

static nfsstat *nfsproc_symlink(symlinkargs *sa)
{
static nfsstat	res;
my_attr_t		msa;

	bzero(&res, sizeof(res));
	input_attr(&msa, &sa->attributes);
	res = nfs_errno(-fo_symlink(FH(&sa->from.dir), sa->from.name,sa->to,&msa));
	DPRINTF(("nfsproc_symlink(dir=%d, name=%s, path=%s)->%d\n",
						(int)FH(&sa->from.dir), sa->from.name, sa->to, res));
	return &res;
}

/* ------------------------------------------------------------------------- */

/* Dummy routines */
static void *nfsproc_writecache()
{
static nfsstat res;

	eprintf("** nfsproc_writecache()\n");
	res = NFSERR_FBIG;
	return &res;
}

/* ------------------------------------------------------------------------- */

static void *nfsproc_null()
{
static nfsstat res;

	eprintf("** nfsproc_null()\n");
	res = NFSERR_FBIG;
	return &res;
}

/* ------------------------------------------------------------------------- */

static void *nfsproc_root()
{
static nfsstat res;

	eprintf("** nfsproc_root()\n");
	res = NFSERR_FBIG;
	return &res;
}

/* ------------------------------------------------------------------------- */

#define ENTRY(res_type, arg_type, funct) {		\
	sizeof(res_type), sizeof(arg_type),			\
	xdr_##res_type, xdr_##arg_type,				\
	(void *(*)())nfsproc_##funct, #funct	\
}

#define nil	char
#define xdr_nil	xdr_void

struct dentry {
	int	res_size, arg_size;	/* sizeof the res/arg structs	*/
	bool_t	(*xdr_result)();
	bool_t	(*xdr_argument)();
	void	*(*function)();		/* function handler		*/
	char	*name;			/* name of function		*/
};

/* ------------------------------------------------------------------------- */

static struct dentry dispatch_table[] = {
	ENTRY(nil, nil, null),					/* NULL */
	ENTRY(attrstat, nfs_fh, getattr),		/* GETATTR */
	ENTRY(attrstat, sattrargs, setattr),	/* SETATTR */
	ENTRY(nil, nil, root),					/* ROOT */
	ENTRY(diropres, diropargs, lookup),		/* LOOKUP */
	ENTRY(readlinkres, nfs_fh, readlink),	/* READLINK */
	ENTRY(readres, readargs, read),			/* READ */
	ENTRY(nil, nil, writecache),			/* WRITECACHE */
	ENTRY(attrstat, writeargs, write),		/* WRITE */
	ENTRY(diropres, createargs, create),	/* CREATE */
	ENTRY(nfsstat, diropargs, remove),		/* REMOVE */
	ENTRY(nfsstat, renameargs, rename),		/* RENAME */
	ENTRY(nfsstat, linkargs, link),			/* LINK */
	ENTRY(nfsstat, symlinkargs, symlink),	/* SYMLINK */
	ENTRY(diropres, createargs, mkdir),		/* MKDIR */
	ENTRY(nfsstat, diropargs, rmdir),		/* RMDIR */
	ENTRY(readdirres, readdirargs, readdir),/* READDIR */
	ENTRY(statfsres, nfs_fh, statfs),		/* STATFS */
};

/* ------------------------------------------------------------------------- */

void nfs_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
{
unsigned int	proc_i = rqstp->rq_proc;
struct dentry	*dent;
void			*result;
union {
	nfs_fh		nfsproc_getattr_2_arg;
	sattrargs	nfsproc_setattr_2_arg;
	diropargs	nfsproc_lookup_2_arg;
	nfs_fh		nfsproc_readlink_2_arg;
	readargs	nfsproc_read_2_arg;
	writeargs	nfsproc_write_2_arg;
	createargs	nfsproc_create_2_arg;
	diropargs	nfsproc_remove_2_arg;
	renameargs	nfsproc_rename_2_arg;
	linkargs	nfsproc_link_2_arg;
	symlinkargs	nfsproc_symlink_2_arg;
	createargs	nfsproc_mkdir_2_arg;
	diropargs	nfsproc_rmdir_2_arg;
	readdirargs	nfsproc_readdir_2_arg;
	nfs_fh		nfsproc_statfs_2_arg;
}				argument;

	if (proc_i >= (sizeof(dispatch_table) / sizeof(struct entry))){
		svcerr_noproc(transp);
		DPRINTF(("nfs_dispatch(): illegal procedure %d\n", proc_i));
		return;
	}
	dent = &dispatch_table[proc_i];
	bzero(&argument, dent->arg_size);
	if (!svc_getargs(transp, dent->xdr_argument, (caddr_t)&argument)){
		svcerr_decode(transp);
		DPRINTF(("nfs_dispatch(): error decoding arguments for %d\n", proc_i));
		return;
	}
	set_ids(rqstp);

	result = (*dent->function) (&argument);

	if (result != NULL && !svc_sendreply(transp, dent->xdr_result, result)){
		svcerr_systemerr(transp);
	}
	if (!svc_freeargs(transp, dent->xdr_argument, (caddr_t)&argument)){
		eprintf("nfs_dispatch: unable to free arguments\n");
		terminate(1);
	}
}

/* ------------------------------------------------------------------------- */
