/* $Id: server.C,v 1.73 2000/10/09 04:50:40 dm Exp $ */

/*
 *
 * Copyright (C) 1998-2000 David Mazieres (dm@uun.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "sfsrwcd.h"
#include "axprt_crypt.h"

/* #define NO_ACACHE 1 */

void
server::getreply (time_t rqtime, nfscall *nc, void *res, clnt_stat err)
{
  auto_xdr_delete axd (ex_nfs_program_3.tbl[nc->proc ()].xdr_res, res);
  if (err) {
    if (err == RPC_CANTSEND || err == RPC_CANTRECV)
      getnfscall (nc);
    else
      nc->reject (SYSTEM_ERR);
    return;
  }
#ifndef NO_ACACHE
  xattrvec xv;
  nfs3_getxattr (&xv, nc->proc (), nc->getvoidarg (), res);
  for (xattr *x = xv.base (); x < xv.lim (); x++) {
    if (x->fattr)
      x->fattr->expire += rqtime;
    ac.attr_enter (*x->fh, x->fattr, x->wattr);

    if (nc->proc () == NFSPROC3_ACCESS) {
      ex_access3res *ares = static_cast<ex_access3res *> (res);
      access3args *a = nc->template getarg<access3args> ();
      if (ares->status)
	ac.flush_access (a->object, nc->getaid ());
      else
	ac.access_enter (a->object, nc->getaid (),
			 a->access, ares->resok->access);
    }
  }
#endif /* !NO_ACACHE */
  nfs3_exp_disable (nc->proc (), res);
  nc->reply (res);
}

void
server::cbdispatch (svccb *sbp)
{
  if (!sbp)
    return;

  switch (sbp->proc ()) {
  case ex_NFSCBPROC3_NULL:
    sbp->reply (NULL);
    break;
  case ex_NFSCBPROC3_INVALIDATE:
    {
      ex_invalidate3args *xa = sbp->template getarg<ex_invalidate3args> ();
      ex_fattr3 *a = NULL;
      if (xa->attributes.present && xa->attributes.attributes->expire) {
	a = xa->attributes.attributes.addr ();
	a->expire += timenow;
      }
      ac.attr_enter (xa->handle, a, NULL);
      sbp->reply (NULL);
      break;
    }
  default:
    sbp->reject (PROC_UNAVAIL);
    break;
  }
}

void
server::flushstate ()
{
  ac.flush_attr ();
  nfsc = NULL;
  nfscbs = NULL;
  super::flushstate ();
}

void
server::authclear (sfs_aid aid)
{
  ac.flush_access (aid);
  super::authclear (aid);
}

bool
server::setrootfh (const sfs_fsinfo *fsi)
{
  if (fsi->prog != ex_NFS_PROGRAM || fsi->nfs->vers != ex_NFS_V3)
    return false;
  nfs_fh3 fh (fsi->nfs->v3->root);
  if (fsinfo && rootfh.data != fh.data)
    return false;

  rootfh = fh;
  nfsc = aclnt::alloc (x, ex_nfs_program_3);
  nfscbs = asrv::alloc (x, ex_nfscb_program_3,
			wrap (this, &server::cbdispatch));
  return true;
}

void
server::dispatch (nfscall *nc)
{
#if 0
  if (sfsctl_intercept (this, srvno, nc))
    return;
#endif

#if 0
  if (nc->proc () == NFSPROC_CLOSE) {
    nfs_fh3 *fhp = nc->getfh3arg ();
    warn << "close 0x" << hexdump (fhp->data.base (), fhp->data.size ())
	 << "\n";
    nfsstat3 ok (NFS3_OK);
    nc->reply (&ok);
    return;
  }
#endif

#ifndef NO_ACACHE
  if (nc->proc () == NFSPROC3_GETATTR) {
    const ex_fattr3 *f = ac.attr_lookup (*nc->template getarg<nfs_fh3> ());
    if (f) {
      getattr3res res (NFS3_OK);
      *res.attributes = *reinterpret_cast<const fattr3 *> (f);
      nc->reply (&res);
      return;
    }
  }
  else if (nc->proc () == NFSPROC3_ACCESS) {
    access3args *a = nc->template getarg<access3args> ();
    int32_t perm = ac.access_lookup (a->object, nc->getaid (), a->access);
    if (perm > 0) {
      access3res res (NFS3_OK);
      res.resok->obj_attributes.set_present (true);
      *res.resok->obj_attributes.attributes
	= *reinterpret_cast<const fattr3 *> (ac.attr_lookup (a->object));
      res.resok->access = perm;
      nc->reply (&res);
      return;
    }
  }
#endif /* !NO_ACACHE */

  void *res = ex_nfs_program_3.tbl[nc->proc ()].alloc_res ();
  nfsc->call (nc->proc (), nc->getvoidarg (), res,
	      wrap (mkref (this), &server::getreply, timenow, nc, res),
	      authof (nc->getaid ()));
}
