#include "fs.h"
#include "fsfile.h"


nfs::nfs(ref<FS> fs)
  : fs(fs)
{
}

//NFS Commands from dispatch::
void
nfs::dispatch(nfscall *nc)
{
  switch(nc->proc()){
  case NFSPROC3_NULL:
    nc->reply(nc->getvoidres());
    break;
  case NFSPROC3_GETATTR:
    nfs3_getattr(nc);
    break;
  case NFSPROC3_SETATTR:
    nfs3_setattr(nc);
    break;
  case NFSPROC3_LOOKUP:
    nfs3_lookup(nc);
    break;
  case NFSPROC3_ACCESS:
    nfs3_access(nc);
    break;
  case NFSPROC3_READ:
    nfs3_read(nc);
    break;
  case NFSPROC3_WRITE:
    nfs3_write(nc);
    break;
  case NFSPROC3_CREATE:
    nfs3_create(nc);
    break;
  case NFSPROC3_MKDIR:
    nfs3_mkdir(nc);
    break;
  case NFSPROC3_REMOVE:
    nfs3_remove(nc);
    break;
  case NFSPROC3_READDIR:
    nfs3_readdir(nc);
    break;
  case NFSPROC3_FSSTAT:
    nfs3_fsstat(nc);
    break;
  case NFSPROC3_FSINFO:
    nfs3_fsinfo(nc);
    break;
  default:
    fprintf(stderr, "nfs:dispatch unknown proc %d\n", nc->proc());
    nc->reject(SYSTEM_ERR);
  }
}

void
nfs::nfs3_getattr(nfscall *nc){
  nfs_fh3 *fh = nc->template getarg<nfs_fh3> ();
  
  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));

  cbe error = fatalCBMsg("get_attr error.");

  fsfile::load(fs, timestamp, num, fsfile::L_READ,
	       wrap(this, &nfs::nfs3_getattr_on_load, nc, error),
	       error);
}

void
nfs::nfs3_getattr_on_load(nfscall *nc, cbe error, ref<fsfile> file){
  nfs_fh3 *fh = nc->template getarg<nfs_fh3> ();
	       
  file->getAttr(wrap(this, &nfs::nfs3_getattr_on_getAttr,
		     nc, file),
		error);
}

void
nfs::nfs3_getattr_on_getAttr(nfscall *nc, ref<fsfile> file, fattr3 fa){
  getattr3res *res = nc->template getres<getattr3res> ();

  res->set_status(NFS3_OK);
  *(res->attributes) = fa;

  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_getattr_error(nfscall *nc){
  nc->error(NFS3ERR_IO);
}

void
nfs::nfs3_setattr(nfscall *nc){
  setattr3args *a = nc->template getarg<setattr3args> ();
  nfs_fh3 fh = a->object;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));

  cbe error = fatalCBMsg("set_attr error.");

  fsfile::load(fs, ts, num, fsfile::L_WRITE,
	       wrap(this, &nfs::nfs3_setattr_on_load, nc, error),
	       error);
}

void
nfs::nfs3_setattr_on_load(nfscall *nc, cbe error, ref<fsfile> file){
  setattr3args *a = nc->template getarg<setattr3args> ();

  file->getAttr(wrap(this, &nfs::nfs3_setattr_on_getAttr,
		     nc, file, error)
		error);
}

void
nfs::nfs3_setattr_on_getAttr(nfscall *nc, ref<fsfile> file, cbe error,
			     fattr3 oldfa){
  setattr3args *a = nc->template getarg<setattr3args> ();
  fattr3 newfa = oldfa;

  if (a->new_attributes.mode.set)
    newfa.mode = *a->new_attributes.mode.val;
  if (a->new_attributes.uid.set)
    newfa.uid = *a->new_attributes.uid.val;
  if (a->new_attributes.gid.set)
    newfa.gid = *a->new_attributes.gid.val;
  if (a->new_attributes.size.set)
  {
    //    warn << "setting size " << *a->new_attributes.size.val << "\n";
    newfa.size = *a->new_attributes.size.val;
  }
  
  if (a->new_attributes.mtime.set == SET_TO_CLIENT_TIME) 
    newfa.mtime = *a->new_attributes.mtime.time;
  if (a->new_attributes.atime.set == SET_TO_CLIENT_TIME) 
    newfa.atime = *a->new_attributes.atime.time;
  
  file->setAttr(newfa, 
		wrap(this, &nfs::nfs3_setattr_on_setAttr, nc, file, oldfa, error),
		error);
}

void
nfs::nfs3_setattr_on_setAttr(nfscall *nc, ref<fsfile> file, fattr3 oldfa, cbe error,
			     fattr3 newfa){
  wccstat3 *res = nc->template getres<wccstat3>();
  res->set_status(NFS3_OK);
  *res->wcc = make_wcc(oldfa, newfa);
  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_setattr_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_LOOKUP{load directory; lookup in dir; send reply;}
void
nfs::nfs3_lookup(nfscall *nc)
{
  diropargs3 *a = nc->template getarg<diropargs3> ();
  nfs_fh3 fh = a->dir;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));

  cbe error = fatalCBMsg("lookup error.");
  fsfile::load(fs, ts, num, fsfile::L_READ,
	       wrap(this, &nfs::nfs3_lookup_on_load, nc, error),
	       error);
}

void
nfs::nfs3_lookup_on_load(nfscall *nc, ref<fsfile> file, cbe error)
{
  fsdir::load(file,
	      wrap(this, &nfs::nfs3_lookup_on_dirLoad,
		   nc, file, error),
	      error);
}

void
nfs::nfs3_lookup_on_dirLoad(nfscall *nc, ref<fsfile> file, cbe error,
			    ref<fsdir> dir)
{
  diropargs3 *a = nc->template getarg<diropargs3> ();

  dir->lookup(a->name,
	      wrap(this, &nfs3_lookup_on_lookup, nc, error),
	      error);
}

void
nfs::nfs3_lookup_on_lookup(nfscall *nc, cbe error,
			   ref<dirEntry> ent)
{
  if (!ent){
    nc->error(NFS3ERROR_NOENT);
  }else{
    lookup3res *res = nc->template getres<lookup3res> ();
    res->set_status(NFS3_OK);
    res->resok->object = ent->getInumber();
    res->resok->obj_attributes.set_present(false);
    res->resok->dir_attributes.set_present(false);
    nc->reply(nc->getvoidres());
  }
}

void
nfs::nfs3_lookup_error(nfscall *nc, int code){
  nc->error(NFS3ERR_IO);
  
}

//NFSPROC3_ACCESS{get file; get attr; do something;}
void
nfs::nfs3_access(nfscall *nc){
  access3args *aa = nc->template getarg<access3args. ();
  nfs_fh3 fh = aa->object;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));
  cbe error = fatalCBMsg("access error.");

  fsfile::load(fs, ts, num, fsfile::L_READ,
	       wrap(this, &nfs::nfs3_access_on_load, nc, error),
	       error);
}

void
nfs::nfs3_access_on_load(nfscall *nc, cbe error,
			 ref<fsfile> file)
{
  file->getAttr(wrap(this, &nfs::nfs3_access_on_getAttr,
		     nc, file, error),
		error);
}

void
nfs::nfs3_access_on_getAttr(nfscall *nc, ref<fsfile> file, cbe error,
			    fattr3 fa){
  access3args *aa = nc->template getarg<access3args> ();
  access3res *res = nc->template getres<access3res> ();
  res->set_status(NFS3_OK);
  res->resok->access = aa->access; // XXX
  res->resok->obj_attributes.set_present(true);
  *res->resok->obj_attributes.attributes = fa;
  
  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_access_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_READ
void
nfs::nfs3_read(nfscall *nc){
  read3args *a = nc->template getarg<read3args> ();
  nfs_fh3 fh = a->file;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));
  cbe error = fatalCBMsg("read error.");

  fsfile::load(fs, ts, num, fsfile::L_READ,
	       wrap(this, &nfs::nfs3_read_on_load, nc, error),
	       error);
}

void
nfs::nfs3_read_on_load(nfscall *nc, cbe error, 
		       ref<fsfile> file)
{
  read3args *a = nc->template getarg<read3args> ();

  //read(uint pos, uint len, callback<void, str>::ref cb, cbe error);
  file->read(a->pos, a->lent,
	     wrap(this, &nfs::nfs3_read_on_read, nc, error),
	     error);
}

void
nfs::nfs3_read_on_read(nfscall *nc, cbe error,
		       str data)
{
  read3res *res = nc->template getres<read3res> ();
  res->set_status(NFS3_OK);
  res->resok->eof = eof;
  res->resok->file_attributes.set_present(false);
  res->resok->count = data.len();
  res->resok->data.set((char*)data.cstr(), data.len());
  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_read_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_WRITE
void
nfs::nfs3_write(nfscall *nc){
  write3args *a = nc->template getarg<write3args> ();
  nfs_fh3 fh = a->file;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));
  cbe error = fatalCBMsg("write error");

  fsfile::load(fs, ts, num, fsfile::L_WRITE,
	       wrap(this, &nfs::nfs3_write_on_load, nc, error),
	       error);
}

void
nfs::nfs3_write_on_load(nfscall *nc, cbe error,
		   ref<fsfile> file)
{
  file->getAttr(wrap(this, &nfs::nfs3_write_on_getAttr, nc, file, error),
		error);
}

void
nfs::nfs3_write_on_getAttr(nfscall *nc, ref<fsfile> file, cbe error,
			   fattr3 fa)
{
  
  write3args *a = nc->template getarg<write3args> ();
  
  str data(a->data.base(), a->count);
  file->write(a->offset, data,
	      wrap(this, &nfs::nfs3_write_on_write, nc, file, fa, a->count, error),
	      error);
}

void
nfs::nfs3_write_on_write(nfscall *nc, ref<fsfile> file, fattr3 oldfa, uint64 count, cbe error)
{
  file->getAttr(wrap(this, &nfs::nfs3_write_on_final, nc, oldfa, count),
		error);
}

void
nfs::nfs3_write_on_final(nfscall *nc, fattr3 oldfa, uint64 count, 
		    fattr3 newfa)
{
  write3res *res = nc->template getres<write3res> ();

  res->set_status(NFS3_OK);
  //res->resok->file_wcc = fsutil::make_wcc(oldfa, newfa);
  res->resok->count = count;
  res->resok->committed = FILE_SYNC;
  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_write_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_CREATE
void
nfs::nfs3_create(nfscall *nc)
{
  create3args *a = nc->template getarg<create3args>;
  nfs_fh3 = a->where.dir;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));

  cbe error = fatalCBMsg("create error");

  fsfile::load(fs, ts, num, L_WRITE,
	       wrap(this, &nfs::nfs3_create_on_load, nc, error),
	       error);
}

void
nfs::nfs3_create_on_load(nfscall *nc, cbe error,
			 ref<fsfile> file)
{
  file->getAttr(wrap(this, &nfs::nfs3_create_on_getAttr, nc, file, error),
		error);
}

void
nfs::nfs3_create_on_getAttr(nfscall *nc, ref<fsfile> file, cbe error,
			    fattr3 oldfa)
{
  create3args *a = nc->template getarg<create3args>;
  
  fsdir::load(file,
	      wrap(this, &nfs::nfs3_create_on_dirLoad, nc, file, oldfa, error),
	      error);
}

void
nfs::nfs3_create_on_dirLoad(nfscall *nc, ref<fsfile> file, fattr3 oldfa, cbe error,
			    ref<fsdir> dir)
{
  create3args *a = nc->template getarg<create3args>;
  
  dir->lookup(a->name,
	      wrap(this, &nfs::nfs3_create_on_lookup, nc, file, oldfa, dir, error),
	      error);
}

void
nfs::nfs3_create_on_lookup(nfscall *nc, ref<fsfile> file, fattr3 oldfa, ref<fsdir> dir, cbe error,
			   ref<fsdirEntry> ent)
{
  if (ent){
    //file exists, we'll just use it
    create3args *a = nc->template getarg<create3args>;
    nfs_fh3 = a->where.dir;
    
    inumber num;
    timestamp ts;
    bcopy(fh.data.base(), &num, sizeof(inumber));
    bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));
    
    fsfile::load(fs, ts, ent->getInumber(), L_READ,
		 wrap(this, &nfs::nfs3_create_on_create, nc, file, oldfa, dir, error),
		 error);
  }else{
    //doesn't exist.  let's create it.
    
    fattr3 fa;
    bzero(&fa, sizeof(fa));
    fa.type = NF3REG;
    fa.mode = 0777; // rwxrwxrwx
    fa.nlink = 0;   // will be incremented when added to dir
    fa.atime = fa.mtime = fsutil::nfstime();

    fsfile::create(fs, fa,
		   wrap(this, &nfs::nfs3_create_on_create, nc, file, oldfa, dir, error),
		   error);
  }
}

void
nfs::nfs3_create_on_create(nfscall *nc, ref<fsfile> file, fattr3 oldfa, ref<fsdir> dir, cbe error,
			   ref<fsfile> file)
{
  create3args *a = nc->template getarg<create3args>;

  inumber num;
  num = fs->log->newInumber();
    
  dir->add(a->name, num,
	   wrap(this, &nfs::nfs3_create_on_add, nc, file, oldfa, dir, error),
	   error);
}

void
nfs::nfs3_create_on_add(nfscall *nc, ref<fsfile> file, fattr3 oldfa, ref<fsdir> dir, cbe error,
			ref<fsdirEntry> ent)
{
  file->getAttr(wrap(this, &nfs::nfs3_create_final, nc, ent.getInumber(), oldfa),
		error);
}

void
nfs::nfs3_create_final(nfscall *nc, inumber num, fattr3 oldfa,
		       fattr3 newfa)
{
  diropres3 *res = nc->template getres<diropres3> ();
  res->set_status(NFS3_OK);
  res->resok->obj.set_present(true);
  *res->resok->obj.handle = num;
  res->resok->obj_attributes.set_present(false);
  res->resok->dir_wcc = fsutil::make_wcc(dfa_before, dfa_after);
  nc->reply(nc->getvoidres());
  
  // dfa_before and dfa_after are fattr3 structures describing
  // the directory's attributes before and after the create.
  // this is part of the NFS v3 cache consistency machinery.
  
  nc->error(NFS3_OK);
}

void
nfs::nfs3_create_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_MKDIR
void
nfs::nfs3_mkdir(nfscall *nc){
  mkdir3args *a = nc->template getarg<mkdir3args> ();
  // a->where.dir : existing directory
  // a->where.name : name of new directory
  // a->attributes.mode.set : if non-zero, set new dir's fattr3.mode to...
  // *a->attributes.mode.val : the new directory's mode
  nfs_fh3 fh = a->where.dir;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));
  cbe error = fatalCBMsg("mkdir error");

  fsfile::load(fs, ts, num, fsfile::L_WRITE,
	       wrap(this, &nfs::nfs3_mkdir_on_load, nc, error),
	       error);
}

void
nfs::nfs3_mkdir_on_load(nfscall *nc, cbe error,
			ref<fsfile> file)
{
  file->getAttr(wrap(this, &nfs::nfs3_mkdir_on_getAttr, nc, file, error),
		error);
}

void
nfs::nfs3_mkdir_on_load(nfscall *nc, ref<fsfile> pfile, cbe error,
			fattr3 pfa)
  fsdir::load(pfile,
	      wrap(this, &nfs::nfs3_mkdir_on_dirLoad, nc, pfile, pfa, error),
	      error);
}

void
nfs::nfs3_mkdir_on_dirLoad(nfscall *nc, ref<fsfile> pfile, fattr3 fa, cbe error, 
			   ref<fsdir> parent)
{
  mkdir3args *a = nc->template getarg<mkdir3args> ();

  parent->lookup(a->where.name,
		 wrap(this, &nfs::nfs3_mkdir_on_lookup, nc, pfile, parent, fa, error),
		 error);
}

void
nfs::nfs3_mkdir_on_lookup(nfscall *nc, ref<fsfile> pfile, ref<fsdir> parent, fattr3 oldfa, cbe error,
			  ref<fsdirEntry> ent)
{
  mkdir3args *a = nc->template getarg<mkdir3args> ();

  if (ent){
    //dir already exists, we're done

    pfile->getAttr(wrap(this, &nfs::nfs3_mkdir_on_final, nc, ent.getInumber(), oldfa),
		   error);
  }else{
    fattr3 fa;
    bzero(&fa, sizeof(fa));
    fa.type = NF3DIR;
    fa.mode = 0777; // rwxrwxrwx
    if (a->attributes.mode.set)
      fa.mode = *a->attributes.mode.val;
    fa.nlink = 2;
    fa.fileid = fh2fileid(fh);
    fa.atime = fa.mtime = fsutil::nfstime();

    fsdir::create(parent, fa,
		  wrap(this, &nfs::nfs3_mkdir_on_create, nc, pfile, parent, oldfa, error),
		  error);
  }
}

void
nfs::nfs3_mkdir_on_create(nfscall *nc, ref<fsfile> pfile, ref<fsdir> parent, fattr3 oldfa, cbe error, 
			  ref<fsdir> dir)
{
  mkdir3args *a = nc->template getarg<mkdir3args> ();

  parent->add(a->name, dir,
	      wrap(this, &nfs::nfs3_mkdir_on_add, nc, pfile, parent, oldfa, dir, error),
	      error);
}

void
nfs::nfs3_mkdir_on_add(nfscall *nc, ref<fsfile> pfile, ref<fsdir> parent, fattr3 oldfa, cbe error,
			     ref<fsdirEntry> ent)
{
  mkdir3args *a = nc->template getarg<mkdir3args> ();
  
  pfile->getAttr(wrap(this, &nfs::nfs3_mkdir_on_final, nc, ent.getInumber(), oldfa),
		 error);
}

void
nfs::nfs3_mkdir_on_final(nfscall *nc, inumber num, fattr3 oldfa,
		       fattr3 newpfa)
{
  diropres3 *res = nc->template getres<diropres3> ();
  res->set_status(NFS3_OK);
  res->resok->obj.set_present(true);
  *res->resok->obj.handle = num;
  res->resok->obj_attributes.set_present(false);
  res->resok->dir_wcc = fsutil::make_wcc(oldpfa, newpfa);

  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_mkdir_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_REMOVE
void
nfs::nfs3_remove(nfscall *nc){
  diropargs3 *a = nc->template getarg<diropargs3> ();
  // a->dir : the directory
  // a->name : the file name
  nfs_fh3 fh = a->dir;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));

  cbe error = fatalCBMsg("remove error");

  fsfile::load(fs, ts, num, fsfile::L_WRITE,
	       wrap(this, &nfs::nfs3_remove_on_load, nc, error),
	       error);
}

void
nfs::nfs3_remove_on_load(nfscall *nc, cbe error, 
			 ref<fsfile> file)
{
  file->getAttr(wrap(this, &nfs::nfs3_remove_on_getAttr, nc, file, error),
		error);
}

void
nfs::nfs3_remove_on_getAttr(nfscall *nc, ref<fsfile> file, cbe error,
			    fattr3 oldfa)
{
  fsdir::load(file,
	      wrap(this, &nfs::nfs3_remove_on_dirLoad, nc, file, oldfa, error),
	      error);
}

void
nfs::nfs3_remove_on_dirLoad(nfscall *nc, ref<fsfile> file, fattr3 oldfa, cbe error, 
			    ref<fsdir> dir)
{
  diropargs3 *a = nc->template getarg<diropargs3> ();
  // a->dir : the directory
  // a->name : the file name
  
  dir->remove(a->name,
	      wrap(this, &nfs::nfs3_remove_on_remove, nc, file, oldfa, dir, error),
	      error);
}

void
nfs::nfs3_remove_on_remove(nfscall *nc, ref<fsfile> file, fattr3 oldfa, ref<fsdir> dir, cbe error)
{
  file->getAttr(wrap(this, &nfs::nfs3_remove_final, nc, oldfa),
		error);
}

void
nfs::nfs3_remove_final(nfscall *nc, fattr3 oldfa,
		       fattr3 newfa)
{
  wccstat3 *res = nc->template getres<wccstat3> ();
  res->set_status(NFS3_OK);
  *res->wcc = make_wcc(oldfa, newfa);
  nc->reply(nc->getvoidres());
}

void
nfs::nfs3_remove_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_READDIR
void
nfs::nfs3_readdir(nfscall *nc)
{
  readdir3args *a = nc->template getarg<readdir3args> ();
  // nfs_fh3 a->dir
  // int a->cookie
  // the cookie indicates where the previous READDIR left off,
  // and where this READDIR should start. that is, it tells
  // you how many directory entries to skip.
  // the cookie is just a copy of whatever you put in the reply.
  nfs_fh3 fh = a->dir;

  inumber num;
  timestamp ts;
  bcopy(fh.data.base(), &num, sizeof(inumber));
  bcopy(fh.data.base()+sizeof(inumber), &ts, sizeof(timestamp));

  cbe error = fatalCBMsg("readdir error");

  fsfile::load(fs, ts, num, fsfile::READ,
	       wrap(this, &nfs::nfs3_readdir_on_load, nc, error),
	       error);
}

void
nfs::nfs3_readdir_on_load(nfscall *nc, cbe error,
			  ref<fsfile> file)
{
  readdir3args *a = nc->template getarg<readdir3args> ();
  // nfs_fh3 a->dir
  // int a->cookie
  // the cookie indicates where the previous READDIR left off,
  // and where this READDIR should start. 

  fsdir::load(file,
	      wrap(this, &nfs::nfs3_readdir_on_dirLoad, nc, error),
	      error);
}

void
nfs::nfs3_readdir_on_dirLoad(nfscall *nc, cbe error, 
			     ref<fsdir> dir)
{
  readdir3args *a = nc->template getarg<readdir3args> ();
  // nfs_fh3 a->dir
  // int a->cookie
  // the cookie indicates where the previous READDIR left off,
  // and where this READDIR should start. 

  //now we've got the directory, we need some way to read the contents
}

void
nfs::nfs3_readdir_error(nfscall *nc){
  //throw error
  nc->error(NFS3ERR_IO);
}

//NFSPROC3_FSSTAT
void
nfs::nfs3_fsstat(nfscall *nc){
  fsstat3res *res = nc->template getres<fsstat3res> ();
  res->set_status(NFS3_OK);
  res->resok->tbytes = 0;
  res->resok->fbytes = 0;
  res->resok->abytes = 0;
  res->resok->tfiles = 0;
  res->resok->ffiles = 0;
  res->resok->afiles = 0;
  res->resok->invarsec = 0;
  nc->reply(nc->getvoidres());
}

//NFSPROC3_FSINFO
void
nfs::nfs3_fsinfo(nfscall *nc){
  fsinfo3res *res = nc->template getres<fsinfo3res> ();
  res->set_status(NFS3_OK);
  res->resok->obj_attributes.set_present(false);
  res->resok->rtmax = 8192;  // max read size
  res->resok->rtpref = 8192; // preferred read size
  res->resok->rtmult = 512;  // reads should be multiple of this (?)
  res->resok->wtmax = 8192;
  res->resok->wtpref = 8192;
  res->resok->wtmult = 8192;
  res->resok->dtpref = 512;  // preferred readdir size
  res->resok->maxfilesize = 0x7fffffff;
  res->resok->time_delta.seconds = 0;
  res->resok->time_delta.nseconds = 1;
  res->resok->properties = FSF3_HOMOGENEOUS;
  // res->resok->properties = (FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS);
  nc->reply(nc->getvoidres());
}

