/* rfaobj */
#ifdef DOCUMENTATION

title	rfaobj	block mode remote file access object
index	rfaobj	block mode remote file access object

synopsis

	  ins rfaobj/task=rfa$$$
	  ncp set object 128 name rfa$$$ copies 4

description

	Remote file access server for block mode file access.
	Must be used with the special AP nfar routines provided
	in the CN library. See fopnxn(), rgetb(), rputb().

bugs

	There is a race condition possible in the rclose() exchange
	where a nt_abo will be received when the task using the
	rfa exits before the close completes. Note that rclose()
	does not retrun the real close status.

edit history
	.s.nf
	01 20-jul-85 hjj fixed up handling of network disconnect
			and abort handling. Rebuilt with fixed
			fgetb and fputb. Now returns proper
			error messages. Now linked against qwrap()
			to kill excess lf on stderr on exit.
	02 13-mar-86 hjj added more error info in diagnostic msgs.
#endif

#include <stdio.h>
#include <cx.h>
#include <stdnet.h>

#define IE_DAO	0363	/* transfer rejected */
#define IE_REJ	0250	/* transfer rejected */
#define IE_DSQ	0246	/* disk quota exceed, tried to put too much */
#define IE_EOF	0366	/* end of file */

#define C_OPEN	1
#define C_CLOSE	2
#define C_GET	3
#define C_PUT	4

struct r_fopnp {	/* remote fopen packet */
	int code;		/* function code */
	char filnam[32];
	char mode[16];
	int inisiz;
	int recsiz;
};

static struct r_opsts ropnsts;
static struct r_opsts *rp = &ropnsts;

struct r_xfrp {		/* remote put packet */
	int code;		/* function code */
	int siz;
  	int lbn;
};

extern int $$nerr;
static NIOV *np;
char buf[132];

FILE *fp;	/* fp for file openned by remote */
int hiblk;	/* highest file block allocated */
int efblk;	/* end of file block */
int curblk;	/* current lbn */


int $$nargs = 1;

main()
{
  int netmsg();
  int len;
  int *ip;

  if(!opn_net(netmsg)) {
    tmsg('F', "open net failed nerr %d\n", $$nerr);
    exits(4);
  }
  fp = NULL;	/* initially, no file open */

  wtse(10);	/* wait for attach from other task */

  while ((len = get_net(buf, sizeof buf, np)) != NULL) {
    dsar();		/* let code finish */
    ip = buf;
    switch(*ip) {	/* whats the message code */

	case C_OPEN:	ropen(ip);
			break;
	case C_CLOSE:	rclos();
			finish(1);
	case C_GET:	rget(ip);
			break;
	case C_PUT:	rput(ip);
			break;
	default:	badmsg(ip);
    }
  enar();		/* let asts in */
  }
  finish(1);
}

ropen(popn)
struct r_fopnp *popn;
{

  fp = fopenx(popn->filnam, popn->mode, popn->inisiz, popn->recsiz);

  zero(rp, (sizeof (struct r_opsts)));

  rp->$$ferr = $$ferr;
  if ($$ferr == 1) {
    fgetname(fp, rp->filnam);	/* full file name */
    copy(&(rp->att), fp->io_fdb, (sizeof (struct fatrb$)));
  }

  hiblk = rp->att.f_hibk[1];	/* highest file block allocated */
  efblk = rp->att.f_efbk[1];	/* end of file block */

  if (!put_net(rp, (sizeof (struct r_opsts)), np)) {
    tmsg('F', "can't reply to reqester - opn, err %o, %o\n", $$nerr,
	np->iosb[1]);
    exits(4);
  }
}

rclos()
{
  if (!fp) return(NULL);
  fclose(fp);
  fp = NULL;
}

rget(rqp)
struct r_xfrp *rqp;		/* remote xfr packet */
{
  char *bp;	/* transfer buffer pointer */
  int xfrlen;

  if (fp == NULL) return(xfrply(IE_NLN));
  if (rqp->lbn >= efblk) return(xfrply(IE_EOF));

  if ((bp = malloc(rqp->siz)) == NULL) return(xfrply(IE_NBF));

  /* we can accept the transfer, tell requestor and get data */

  xfrply(1);

  if ((xfrlen = fgetb(bp, rqp->siz, rqp->lbn, fp)) <= 0) 
  	return(xfrply($$ferr));

  if (!put_net(bp, xfrlen, np)) {
    tmsg('E', "get - put data to requestor failed err %o, %o\n", $$nerr,
	np->iosb[1]);
    mfree(bp);
    return(xfrply(IE_REJ));
  }

  mfree(bp);
  return;
}


rput(rqp)
struct r_xfrp *rqp;		/* remote put packet */
{
  char *bp;	/* transfer buffer pointer */
  int xfrlen;

  if (fp == NULL) return(xfrply(IE_NLN));
  if ((((rqp->siz+511)/512)+rqp->lbn) >= hiblk) {
    return(xfrply(IE_DSQ));
  }

  if ((bp = malloc(rqp->siz)) == NULL) return(xfrply(IE_NBF));

  /* we can accept the transfer, tell requestor and get data */

  xfrply(1);
  if ((xfrlen = get_net(bp, rqp->siz, np)) == NULL) {
    tmsg('E', "rput-get data failed in put err %o, %o  xfrlen %o \n", 
  	$$nerr, np->iosb[1], xfrlen);
    mfree(bp);
    return(xfrply(IE_REJ));
  }

  if (xfrlen != rqp->siz) {
    tmsg('E', "put byte transfer(%d) != bytes expected(%d)\n",
  	xfrlen, rqp->siz);
    mfree(bp);
    return(xfrply(IE_DAO));
  }

  fputb(bp, rqp->siz, rqp->lbn, fp);
  mfree(bp);
  return(xfrply($$ferr));
}

xfrply(errcod)
int errcod;
{
  if (np == NULL) return(NULL);
  if (!put_net(&errcod, 2, np)) {
    tmsg('F', "can't reply to reqester - xfrply, err %o, %o \n", $$nerr,
	np->iosb[1]);
    exits(4);
  }
}

badmsg(p)
int *p;
{
  tmsg('M', "bad message code %o \n", *p);
}

netmsg(code, acp)
char code[4];		/* message type code */
struct a_conblk *acp;	/* pointer to received message */
{
  switch (code[1]) { 
      case NT_CON:	{
		accept(acp);
		setf(10);
	      break;
	      }    
      case NT_INT:	{
	      break;
	      }    
      case NT_DSC:	{
	      rclos(0);
	      finish(4);
	      }    
      case NT_ABT:	{
	      rclos(0);
	      finish(4);
	      }    
      case NT_ABO:	{
	      rclos(0);
	      finish(4);
	      }    
    
      default: {
	      tmsg('W', "unexpected msg %d msglen %d lun %d\n",
			code[1], code[2], code[3]);
	      }
      }

  return(1);
}

accept(ap)
struct a_conblk *ap;	/* pointer to received message */
{
  NIOV *acc_net();

  if ((np = acc_net(ap)) == NULL) {
    tmsg('E', "accnt failed, err %o, %o\n", $$nerr,
	np->iosb[1]);
    return(0);
  }
}

finish(stat)	/* close everything and exit with status = stat */
int stat;
{
  dsc_net(np);
  cls_net();
/*  tmsg('D', "standard exit\n");
*/  exits(stat);
}
