
/* $Header:rmt_remote.c 12.0$ */
/* $ACIS:rmt_remote.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/sys/RCS/rmt_remote.c,v $ */


#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:rmt_remote.c 12.0$";
#endif

/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

#ifdef VICE

/*
 * rmt_remote.c
 *
 * Miscellaneous routines for dealing with remote files.
 */


/* VICEPS can be defined if it is desired for ps c to work correctly.
 * This will cost an extra path scan per path name lookup.
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/kernel.h"
#include "../h/namei.h"
#include "../h/proc.h"
#include "../h/buf.h"
#include "../h/mount.h"
#include "../h/inode.h"
#include "../h/file.h"
#include "../h/stat.h"
#include "../h/remote.h"
#include "rfs.h"
#include "../h/ioctl.h"
#include "../h/vice.h"


extern int ino_rw(), rmt_ioctl(), ino_select(), rmt_close();
static struct fileops rmt_ops = { ino_rw, rmt_ioctl, ino_select, rmt_close };

/* rmt_copen -- remote version of open/creat system calls */
rmt_copen(dev, uflags, umode, fp)/* no return (except u.u_error) */
    dev_t dev;		/* remote device */
    int uflags;	        /* flags argument copied from original system call */
    int umode;	        /* mode argument copied from original system call */
    struct file *fp;
{
    struct inode   *ip;
    int     kflags = uflags - FOPEN; /* the kernel version of uflags,
    					with the r/w flags normalized */
    rmt_getfile(dev, &ip, uflags, umode, &fp->f_rmt_fileid, 0);
    if (ip) {
	iunlock(ip);
	fp->f_flag = kflags & FMASK;
	fp->f_type = DTYPE_REMOTE;
	fp->f_ops = &rmt_ops;
	fp->f_data = (caddr_t) ip;
    }
    else {
	if (!u.u_error)
	    panic("rmt_copen");
    }
}

/* rmt_close -- close an open remote file (system call support) */
rmt_close(fp)
    struct file *fp;
{
    extern  rmt_close1 ();
    register struct inode  *ip = (struct inode *) fp->f_data;
    rmt_syscall(ip->i_rmt_dev, rmt_close1, fp->f_rmt_fileid);
    ino_close(fp);
}

/* rmt_close1 -- support for close of remote inode */
static
rmt_close1(dev, fileid)
    dev_t dev;
    int fileid;
{
    devhack(dev);
    rmt.rt_imr.im_arg[0].im_aval = fileid;
    rmt_send(dev, RT_close);
}

/* rmt_fstat -- get status of open remote file */
rmt_fstat(dev,fp,sb)
    dev_t dev;			/* The remote device */
    register struct file *fp;	/* The open file */
    register struct stat *sb;	/* Where to dump the status */
{
    devhack(dev);
    rmt.rt_imr.im_arg[0].im_aval = fp->f_rmt_fileid;
    rmt_send(dev, RT_fstat);
    if (!u.u_error)
        *sb = *(struct stat *) & rmt.rt_imw.im_data[0];
}

/* rmt_stat -- get status of named remote file */
rmt_stat(dev, sb, follow)
    dev_t dev;			/* the remote device */
    register struct stat *sb;	/* Where to dump the status */
    int follow;			/* 0-->don't chase symbolic links; 1-->do */
{
    devhack(dev);
    rmt_store_path_arg(dev, 0);
    rmt_send(dev, RT_stat);
    if (!u.u_error)
        *sb = *(struct stat *) & rmt.rt_imw.im_data[0];
}

/* Check that file is in the name space of a remote file system.
   The file is in the name space unless u.u_error == EABSPATH1.
   Used by rename, link */
rmt_checkPathName(dev, path, follow)
    dev_t dev;
    char *path;
    int follow;
{
    rmt_storeNamedPathArg(dev, path, 0);
    rmt_send(dev, RT_stat);
}

/* rmt_simple_path -- implement several remote file system calls taking a pathname + integer args */
/* VARARGS1 */
rmt_simple_path(dev, rmt_syscall_type, arg1, arg2)
    dev_t dev;			/* the remote device */
    int rmt_syscall_type;	/* the type of the system call, as defined in remote.h */
    int arg1;			/* the first integer argument */
    int arg2;			/* the second integer argument */
{
    devhack(dev);
    rmt_store_path_arg(dev, 0);
    rmt.rt_imr.im_arg[1].im_aval = arg1;
    rmt.rt_imr.im_arg[2].im_aval = arg2;
    rmt_send(dev, rmt_syscall_type);
    return;
}

/* rmt_simple_file -- implement several remote file system calls taking a file descriptor + integer args */
/* VARARGS1 */
rmt_simple_file(dev, rmt_syscall_type, fp, arg1, arg2)
    dev_t dev;			/* the remote device */
    int rmt_syscall_type;	/* the type of the system call, as defined in remote.h */
    register struct file *fp;
    int arg1;			/* the first integer argument */
    int arg2;			/* the second integer argument */
{
    devhack(dev);
    rmt.rt_imr.im_arg[0].im_aval = fp->f_rmt_fileid;
    rmt.rt_imr.im_arg[1].im_aval = arg1;
    rmt.rt_imr.im_arg[2].im_aval = arg2;
    rmt_send(dev, rmt_syscall_type);
}

/* rmt_symlink -- remote symbolic link */
rmt_symlink(dev,tp,tl)
    dev_t dev;			/* The remote device */
    register char *tp;		/* Pointer to symbolic link string value */
    register int tl;		/* length of link information */
{
    rmt_store_user_arg(dev, 0, tp, tl);
    rmt_store_path_arg(dev, 1);
    rmt_send(dev, RT_symlink);
}

/* rmt_rename_or_link -- rename or link to a remote file */
rmt_rename_or_link(dev,type,old,new)
    dev_t dev;	       /* The remote device */
    char *old,*new;    /* The new and old path names */
    int type;	       /* RT_rename or RT_link */
{
    rmt_storeNamedPathArg(dev, old, 0);
    rmt_storeNamedPathArg(dev, new, 1);
    rmt_send(dev, type);
}

/* rmt_readlink -- read the value of a remote symbolic link */
rmt_readlink(dev, buf, maxsize)
    dev_t	dev;		/* The remote device */
    char	*buf;		/* buffer to store link value (in user space) */
    int		maxsize;	/* size of user buffer */
{
    devhack(dev);
    rmt_store_path_arg(dev, 0);
    rmt_send(dev, RT_readlink);
    if (u.u_error)
	return;
    if (rmt_check_arg(dev, 1) != 0 || rmt.rt_imw.im_arg[1].im_asize > maxsize) {
	u.u_error = ENOENT;
	return;
    }
    u.u_error = copyout(&rmt.rt_imw.im_data[0], buf, rmt.rt_imw.im_arg[1].im_asize);
    u.u_r.r_val1 = rmt.rt_imw.im_arg[1].im_asize;
}
	
/* rmt_chdirec -- change current working directory to a remote directory */
rmt_chdirec(dev,ipp,fidp)
    dev_t dev;			/* The remote device */
    struct inode **ipp;
    struct rmtWd *fidp;
{
    devhack(dev);

    if (fidp == NULL) {
	u.u_error = EINVAL;	/* Chroot not supported */
    }
    rmt_store_path_arg(dev, 0);
    rmt_send(dev, RT_chdir);
    if (u.u_error)
	return;
    /* Vice II: a fid is returned; VICE I interface no longer supported */
    fidp->dev = dev;
    bcopy(rmt.rt_imw.im_wdfid, fidp->fid, sizeof(fidp->fid));
    if (*ipp) {
    	irele(*ipp);
	*ipp = NULL;
    }
}

/* rmt_utimes -- set remote file times */
rmt_utimes(dev, tptr)
    dev_t dev;			/* The remote device */
    struct timeval (*tptr)[2];	/* Pointer to time structures */
{
    rmt_store_path_arg(dev, 0);
    rmt_store_user_arg(dev, 1,(caddr_t) tptr, sizeof(*tptr));
    rmt_send(dev, RT_utimes);
}

/* rmt_mount -- "mount" a remote file system on a special remote mount device */
rmt_mount(dev, ip, parent)
    dev_t dev;			/* The communications channel with the
				   agent process */
    struct inode *ip;		/* The indirect inode */
    struct inode *parent;	/* The indirect inode's parent */
{
    register struct mount  *mp;
    extern struct mount *getmountstruct ();
    if (minor(dev) >= NRFS)
	u.u_error = ENXIO;
    mp = getmountstruct(dev);
    if (mp == 0)
	u.u_error = EBUSY;	/* This seems like the wrong error code!!
				   But this is the way it's documented,
				   sigh */
    if (u.u_error) {
	iput(ip);
	irele(parent);
	return;
    }
    mp->m_dev = dev;
    mp->m_inodp = ip;
    mp->m_remote = 1;
    mp->m_parent = parent;
    ip->i_flag |= IMOUNT;
    iunlock(ip);
}

/* rmt_unmount -- "unmount" a remote file system. */
rmt_unmount(dev)
    dev_t dev;			/* The communications channel with
				   the agent process */
{
    register struct mount  *mp;
    register struct inode  *ip;
    for (mp = &mount[0]; mp < &mount[NMOUNT]; mp++)
	if (mp->m_remote && dev == mp->m_dev)
	    goto found;
    return EINVAL;
found: 
    ip = mp->m_inodp;
    ip->i_flag &= ~IMOUNT;
    irele(ip);
    irele(mp->m_parent);
    mp->m_dev = 0;
    mp->m_remote = 0;
/*DBUG*/
    if (mp->m_bufp)
	panic("remote unmount:  buffer pointer not zero!");
    return 0;
}

/* rmt_ioctl -- ioctl to an open remote file (system call support) */
rmt_ioctl(fp, com, data)
    struct file *fp;
    unsigned int com;
    caddr_t data;
{
    extern  rmt_ioctl1 ();
    register struct inode  *ip = (struct inode *) fp->f_data;
    if (!_VALIDVICEIOCTL(com)) {
	u.u_error = ino_ioctl(fp, com, data);
	return u.u_error;
    }
    rmt_syscall(ip->i_rmt_dev, rmt_ioctl1, 0, fp->f_rmt_fileid, com,(struct ViceIoctl  *) data);
    return u.u_error;
}

/* pioctl -- pathname ioctl (system call support--for remote pathnames only) */
pioctl() {
    extern rmt_ioctl1();
    struct a {
	char	*path;
	int	cmd;
	caddr_t cmarg;
	int	follow;
    } *uap;
    struct ViceIoctl data;
    int com;
    struct inode *ip;
    int remote;
    register struct nameidata *ndp = &u.u_nd;

    uap = (struct a *) u.u_ap;
    com = uap->cmd;
    if (! _VALIDVICEIOCTL(com)) {
	u.u_error = EINVAL;
	return;
    }
    u.u_error = copyin(uap->cmarg, (caddr_t) &data, sizeof (data));
    if (u.u_error)
    	return;
    ndp->ni_nameiop = LOOKUP;
    if (uap->follow) ndp->ni_nameiop |= FOLLOW;
    ndp->ni_segflg = UIO_USERSPACE;
    ndp->ni_dirp = uap->path;
    ip = RemoteNamei(&remote, ndp, rmt_ioctl1, 1, 0, com, &data);
    if (remote)
    	return;
    if (ip) {
	iput(ip);
	u.u_error = EINVAL;	/* Not supported on non-remote files */
    }
}


/* rmt_ioctl1 -- support for ioctl of remote file */
static
rmt_ioctl1(dev, ioctl_type, fileid, com, data)
    dev_t dev;
    int ioctl_type;	/* 0 -> ioctl, 1 -> pioctl */
    int fileid;		
    int com;
    struct ViceIoctl *data;
{
    devhack(dev);
    if (ioctl_type == 0)
	rmt.rt_imr.im_arg[0].im_aval = fileid;
    else
        rmt_store_path_arg(dev, 0);
    rmt.rt_imr.im_arg[1].im_aval = com;
    if (data->in && data->in_size) {
	rmt_store_user_arg(dev, 2, data->in, data->in_size);
	if (u.u_error)
	    return;
    }
    rmt_send(dev, ioctl_type==0?RT_ioctl:RT_pioctl);
    if (u.u_error)
	return;
    if (data->out && data->out_size) {
	if (rmt_check_arg(dev, 0) == -1 || rmt.rt_imw.im_arg[0].im_asize > data->out_size) {
	    u.u_error = EINVAL;	/* This is a lousy error code */
	    return;
	}
	u.u_error = copyout(&rmt.rt_imw.im_data[0], data->out, rmt.rt_imw.im_arg[0].im_asize);
	return;
    }
}

/* System call:  set process authentication group */
setpag() 
{
    static int extra = 0;
    u.u_rmt_pag = time.tv_sec + ++extra;
}

/* fnamei -- Like namei, but will open a remote file.  RETURNS FILE PTR */
/* VARARGS3 */
struct file *
fnamei(ndp, creation_mode, exec)
    register struct nameidata *ndp;
    int creation_mode;	/* create mode, if flag&IWRITE, not required otherwise */
    int exec;           /* should be 1, if the file is to be executed */
{
 /* This function is intended for simple applications within the kernel,
    like core dumps, exec, etc.  It will not open directories. If returned
    file pointer is not NULL, then it will have a valid f_data pointer to
    an inode structure.  The inode will be locked */

    extern struct fileops   inodeops;
    struct inode  *ip, *rip, *RemoteNamei();
    register struct file   *fp;
    int     uflags, kflags;
    int flags;
    int remote;

    flags = ndp->ni_nameiop & ~(LOCKPARENT|NOCACHE|FOLLOW);
    uflags = (flags == CREATE) ? (O_WRONLY | O_TRUNC | O_CREAT) : (O_RDONLY);
    kflags = uflags - FOPEN;
    fp = file_alloc();		/* allocate file w/o user file descriptor */
    if (fp == NULL)
	return NULL;
    fp->f_flag = kflags & FMASK;
    ip = RemoteNamei(&remote, ndp, rmt_getfile, &rip, uflags, creation_mode, &fp->f_rmt_fileid, exec);
    if (!remote) {
	fp->f_type = DTYPE_INODE;
	fp->f_ops = &inodeops;
	if (flags == CREATE && ip == NULL && !u.u_error)
	    ip = maknode(creation_mode, ndp);
	if (ip != NULL) {
	    if (u.u_error) {
		iput(ip);
		ip = NULL;
	    }
	}
    }
    else {
    /* Remote file */
	ip = rip;
	fp->f_type = DTYPE_REMOTE;
	fp->f_ops = &rmt_ops;
    }
    if (ip != NULL)
	fp->f_data = (caddr_t) ip;
    else {
	fp->f_count = 0;
	fp = NULL;
    }
    return fp;
}

/* This is used by rename, link to decide if the system call should be
   handled remotely.  Returns 1 if it was handled remotely.  Leaves
   behind pointers to expanded names if there were remote symbolic
   links */
int RemoteMaybe(p1, p2, ndp, rmtFunc)
    register struct Name *p1, *p2;
    register struct nameidata *ndp;
    int (*rmtFunc)();
{
    register struct Name *p;
    int rmt_checkPathName(), rmt_rename_or_link();
    int i;
    p1->buf = p2->buf = NULL;
    p1->dev = p2->dev = NODEV;
    p1->state = p2->state = havePath;
    p1->func = p2->func = UIO_USERSPACE;

    for (i = 0; i<MAXSYMLINKS*2;) {
	if ((p = p1)->state == havePath || (p = p2)->state == havePath) {
	    struct inode *ip;
	    u.u_rmt_requested = 1;
	    ndp->ni_segflg = p->func;
	    ndp->ni_nameiop = LOOKUP | p->follow;
	    ndp->ni_dirp = p->name;
	    ip = namei(ndp);
	    if (ip)
	        iput(ip); /*bletch*/
	    u.u_error = 0;
	    if (u.u_rmt_dev != NODEV) {
	    	p->state = haveDev;
		p->dev = u.u_rmt_dev;
	        p->buf = u.u_rmt_path_buf;
		p->name = u.u_rmt_path;
		u.u_rmt_dev = NODEV;
		u.u_rmt_path_buf = NULL;
	    }
	    else
	        p->state = isLocal;
	    continue;
	}
	if (p1->state == isLocal && p2->state == isLocal)
	    return 0;
        if (p1->dev != NODEV && p1->dev == p2->dev) {
	    u.u_rmt_follow1 = p1->follow;
	    u.u_rmt_follow2 = p2->follow;
	    rmt_syscall(p1->dev, rmt_rename_or_link, rmtFunc,
	    	p1->name, p2->name);
	    if (u.u_error == EABSPATH1)
		p = p1;
	    else {
		p1->state = isRemote;
		if (u.u_error == EABSPATH2)
		    p = p2;
		else /* p2->state = isRemote; */
		    break;
	    }
	}
	else if ((p = p1)->state == haveDev || (p = p2)->state == haveDev) {
	    u.u_rmt_follow1 = p->follow;
	    rmt_syscall(p->dev, rmt_checkPathName, p->name, p->follow);
	    if (u.u_error != EABSPATH1) {
	        if (u.u_error == EINTR)
	    	    break;
		p->state = isRemote;
		continue;
	    }
	}
	else {
	    u.u_error = EXDEV;
	    break;
	}
	if (p->buf)
	    brelse(p->buf);
	p->dev = NODEV;
	p->buf = u.u_rmt_path_buf;
        p->name = u.u_rmt_path;
	u.u_rmt_path_buf = NULL;
	p->func = UIO_SYSSPACE;
	p->state = havePath;
	u.u_error = 0;
	i++;
    }
    if (i >= MAXSYMLINKS*2)
	u.u_error = ELOOP;
    if (p1->buf)
    	brelse(p1->buf);
    if (p2->buf)
        brelse(p2->buf);
    return 1;
}

/*
 * Remote Namei:  like namei, but may perform the indicated function remotely,
 * if the file is remote.  Resolves remote symbolic links.
 * Returns:  
 *    function result is an inode pointer if the operation was performed
 *	locally (possibly after remote symbolic link expansion).  This
 *	pointer may, of course, be NULL if the local operation failed.
 *    *remote is set to 1 if the operation was performed remotely; 0 otherwise
 */
struct inode *RemoteNamei(remote, ndp, rmtFunc, a1, a2, a3, a4, a5)
    int *remote;
    register struct nameidata *ndp;
    int (*rmtFunc)();
{
    int i;
    struct buf *buf = NULL;
    struct inode *ip;
    *remote = 0;
    for (i = 0; i < MAXSYMLINKS; i++) {
        u.u_rmt_requested = 1;
        ip = namei(ndp);
	if (buf) {
	    brelse(buf);
	    buf = NULL;
	}
        if (u.u_rmt_dev == NODEV)
	    break;
#ifndef NOVICEPS
        CopyDirent(ndp, u.u_rmt_path);
#endif NOVICEPS
	u.u_rmt_follow1 = ((ndp->ni_nameiop & FOLLOW) != 0);
	rmt_syscall(u.u_rmt_dev, rmtFunc, a1, a2, a3, a4, a5);
	/* Note:  if VENUS returns EINTR, then loop will stop */
	if (!u.u_rmt_path_buf) {
	    *remote = 1;
	    break;
	}
	buf = u.u_rmt_path_buf;
	ndp->ni_segflg = UIO_SYSSPACE;
	ndp->ni_dirp = u.u_rmt_path;
	u.u_rmt_path_buf = NULL;
	u.u_error = 0;
    }
    if (i >= MAXSYMLINKS)
	u.u_error = ELOOP;
    if (buf)
	brelse(buf);
    return ip;
}

#ifndef NOVICEPS
CopyDirent(ndp, path)
    register struct nameidata *ndp;
    register char *path;
{
    register char *de = path;
    register len;
    for (; *path; path++)
	if (*path == '/')
	    de = path+1;
    len = path - de;
    if (len > MAXNAMLEN)
	len = MAXNAMLEN;
    ndp->ni_dent.d_namlen = len;
    path = (char *)&ndp->ni_dent.d_name;
    while (len--)
	*path++ = *de++;
    *path++ = '\0';
}
#endif NOVICEPS

/* rmt_syscall -- ask an agent process to handle a system call for us */
/* VARARGS1 */
rmt_syscall(dev,func,a1,a2,a3,a4,a5)
    int (*func)();	/* kernel support function for the remote call */
    dev_t dev;		/* remote file system device */
    int	a1,a2,a3,a4,a5;	/* arbitrary arguments to func */
{
    devhack(dev);
    int     result;

 /* Disable processing of STOP signal during the system call, so that
    the buffer won't get tied up */
    u.u_procp->p_flag |= SRMT;

    rmt_attach_rbuf(dev,1);
    result = (*func)(dev, a1, a2, a3, a4, a5);
    if (u.u_rmt_path_buf != NULL) {
	brelse(u.u_rmt_path_buf);
	u.u_rmt_path_buf = NULL;
    }
    if (u.u_error == EABSPATH1 || u.u_error == EABSPATH2) {
	/* DEBUG */ if ((rmt.rt_flags&RT_WBUF)==0 || rmt.rt_imw.im_client != u.u_procp->p_pid)
		printf("rmt_remote.c: screw up!\n");
	else {
	    if (rmt_check_arg(dev, 0) != 0)
		u.u_error = EINVAL;	/* Lousy error code... */
	    else {
		u.u_rmt_path_buf = geteblk(MAXPATHLEN);
		u.u_rmt_path = u.u_rmt_path_buf->b_un.b_addr;
		bcopy(rmt.rt_imw.im_data, u.u_rmt_path, rmt.rt_imw.im_arg[0].im_asize);
	    }
	}
    }
    rmtcleanup(dev);
    u.u_procp->p_flag &= ~SRMT;
    u.u_rmt_requested = 0;
    u.u_rmt_dev = NODEV;
    return result;
}

/* rmt_store_path_arg -- store a pathname in a message to be sent to an agent */
static
rmt_store_path_arg(dev, arg_num)
	dev_t   dev;		/* Remote device */
	int	arg_num;	/* The number of the argument to store (0, 1, 2...) */
{
/* The pathname argument in the buffer given by u.u_rmt_path_buf is stored in
   rmt.rt_imr (the structure that will be read later by the agent process).
   rmt.rt_imr.im_dsize is used to get the first available position in rmt.rt_imr.im_data
   and is updated accordingly.
   NB:  the rmt path buffer is then released (after all, it s been copied)
 */
    devhack(dev);
    register char  *pathp;
    register struct im_arg *argp;
    register char  *datap;
    if (u.u_rmt_path_buf == NULL)
	panic("rmt_store_path_arg:  no path!");
    pathp = u.u_rmt_path;
    argp = &rmt.rt_imr.im_arg[arg_num];
    datap = &rmt.rt_imr.im_data[argp->im_aval = rmt.rt_imr.im_dsize];
    do {
    } while (*datap++ = *pathp++);
    rmt.rt_imr.im_dsize += (argp->im_asize = pathp - u.u_rmt_path);
    brelse(u.u_rmt_path_buf), u.u_rmt_path_buf = NULL;
}

rmt_storeNamedPathArg(dev, pathname, argNum)
    dev_t	dev;
    char 	*pathname;
    int		argNum;
{
    devhack(dev);
    register struct im_arg *argp = &rmt.rt_imr.im_arg[argNum];
    register char *pathp = pathname;
    register char *datap = &rmt.rt_imr.im_data[argp->im_aval = rmt.rt_imr.im_dsize];
    do {
    } while (*datap++ = *pathp++);
    rmt.rt_imr.im_dsize += (argp->im_asize = pathp - pathname);
}

/* rmt_store_user_arg -- store a multi-byte argument from user space into a message for the agent */
static
rmt_store_user_arg(dev, arg_num, uaddr, size)
        dev_t	dev;		/* Remote device */
	int	arg_num;	/* The number of the argument to store (0, 1, 2...) */
	caddr_t	uaddr;		/* Address in user space of the data structure */
	int	size;		/* number of bytes */
{
    devhack(dev);
    register struct im_arg *argp;
    if (size > MAXPATHLEN || size <= 0) {
	u.u_error = EINVAL;
	return;
    }
    argp = &rmt.rt_imr.im_arg[arg_num];
    u.u_error = copyin(uaddr, &rmt.rt_imr.im_data[argp->im_aval = rmt.rt_imr.im_dsize], size);
    rmt.rt_imr.im_dsize += (argp->im_asize = size);
}

/* rmt_check_arg -- verify validity of a non-integer argument passed from an agent process */
static
rmt_check_arg(dev,arg_num)	/* Returns -1 if there is something wrong, 0 otherwise */
    dev_t dev;			/* Remote device */
    register int arg_num;	/* The number of the argument to check (0, 1, 2...) */
{
    devhack(dev);
    register struct im_arg *argp;
    argp = &rmt.rt_imw.im_arg[arg_num];
    return(
	    argp->im_asize == 0
	    || argp->im_aval + argp->im_asize > rmt.rt_imw.im_dsize
	    || argp->im_asize > MAXPATHLEN
	) ? -1 : 0;
}

/* rmt_getfile -- get an inode pointer for a copy of a file from a remote file system */
static
rmt_getfile(dev, ipp, uflags, umode, fileid, exec)
    dev_t dev;			/* remote device */
    struct inode **ipp;		/* Returned inode pointer */
    int uflags;			/* open flags as in open(2) */
    int umode;			/* creation mode as in open(2) */
    int *fileid;		/* file identifier for remote system -- returned to caller */
    int exec;			/* 1 ==> open for execute */
{
    devhack(dev);
    register struct inode  *ip;
    int     arg1ok, inodePassedBack;
    register struct nameidata *ndp = &u.u_nd;
    *ipp = NULL;
    if (u.u_rmt_path_buf == NULL)
	panic("rmt_getfile:  u.u_rmt_path_buf == NULL");
    rmt_store_path_arg(dev, 0);
    rmt.rt_imr.im_arg[1].im_aval = uflags;
    rmt.rt_imr.im_arg[2].im_aval = umode & ~u.u_cmask;
    rmt.rt_imr.im_arg[3].im_aval = exec;
    rmt_send(dev, RT_open);
    if (u.u_error)
	return;
    *fileid = rmt.rt_imw.im_arg[0].im_aval;
    inodePassedBack = (rmt.rt_imw.im_arg[1].im_asize == 0);
    if (!inodePassedBack)
        arg1ok = rmt_check_arg(dev, 1);
    rmt_detach_wbuf(dev);
    if (inodePassedBack) {
	struct inode *igetinode();
	/* Device/inode passed back in args 1 & 2 */
	ip = igetinode((dev_t)rmt.rt_imw.im_arg[1].im_aval,
	               (ino_t)rmt.rt_imw.im_arg[2].im_aval);
    }
    else {
        if (arg1ok == -1) {
	    rmt_getfile_abort(dev, *fileid, RTE_BADARG);
	    return;
        }
	ndp->ni_nameiop = LOOKUP;
	ndp->ni_segflg = UIO_SYSSPACE;
	ndp->ni_dirp = &rmt.rt_imw.im_data[0]; /* Data offset 0 assumed... */
        ip = namei(ndp);
    }
    if (u.u_error) {
	rmt_getfile_abort(dev, *fileid, RTE_NOFILE);
	return;
    }
    if ((ip->i_mode & IFMT) != IFREG) {
	iput(ip);
	rmt_getfile_abort(dev, *fileid, RTE_NOTREG);
	return;
    }
    /*
    if (text file busy) {
	iput(ip);
	rmt_getfile_abort(dev, *fileid, 0);
	u.u_error = ETXTBSY;
	return;
    }
    */
    ip->i_rmt_dev = dev;
    *ipp = ip;
    return;
}

/* rmt_getfile_abort -- Abort a remote open request */
static
rmt_getfile_abort(dev,fileid,error)
    dev_t dev;		/* device where agent process will receive messages from
    			   kernel for this file */
    int fileid;		/* file identification supplied by agent on open */
    int error;		/* error code to return on close message to agent process.
    			   If this is 0, then the no error is returned to the agent,
			   also, u.u_error is not set (because evidently an error
			   other than a problem with the agent has occured) */
{
/* Only called when a file identifier has already been assigned by an agent process */
    devhack(dev);
    rmt_attach_rbuf(dev,1);
    rmt.rt_imr.im_arg[0].im_aval = fileid;
    if (error) {
	u.u_error = ERFS;
	rmt.rt_imr.im_error = error;
    }
    rmt_send(dev, RT_close);
}

#endif VICE
