#if 0
ifdef notdef
*
* POrtable Dodgy Filesystems in userland (hacK!)
*                alias podfuk (v2)
* pretend you are venus cache manager, and export virtual files to kernel
*
* Copyright 1999 Pavel Machek, pavel@ucw.cz
*
* You can get more info at:
* http://atrey.karlin.mff.cuni.cz/~pavel/podfuk/podfuk.html
*
* Distribute under GPL version 2 or later.
*
* Version 2.0 - it works, modulo strange things in tar archives.
* Version 2.1 - tar archives + ftp are now fixed.
* Version 2.2 - made it Makefile
* Version 2.3 - attempt to make it deadlock free, no more complains for strange fids
* Version 2.4 - make a bit more demonic
* Version 2.5 - roll in changes by qrczak@knm.org.pl - handle uids right
* Version 2.6 - add url, fix mknod, do not use ^#, fix paths
* Version 2.7 - check return value for select + small fixes by Michael Hart <mavrik@ihug.co.nz>
*
* Notice this file is *evil*. It is both valid c source and working
* Makefile. You might want to ln -s podfuk.c Makefile. 
*
* Edit MIDNIGHT= to point at root of mc sources, you need sources of
* mc-4.5.5 or newer for this to work. (Youll miss file callback.h if 
* you use 4.5.30 or older - get it from homepage.)
*
* Edit KERNEL= to point include/ directory of your 2.2 kernel source
*
endif

DEBUG_FLAG = -DDEBUG

MIDNIGHT=/home/patb/projects/mc-4.5.39

KERNEL=/usr/src/linux/include

VFS=$(MIDNIGHT)/vfs

LINUX=/usr/src/linux

all:	podfuk

podfuk:	$(VFS)/libvfs.so podfuk.c
	gcc podfuk.c `glib-config --libs --cflags` -I$(MIDNIGHT) -I$(KERNEL) -g -L $(VFS) -lvfs -o podfuk  $(DEBUG_FLAG)

podfuk-static: podfuk.c
	gcc podfuk.c `glib-config --libs` -DNODAEMON -I$(MIDNIGHT) -I$(KERNEL) -g $(VFS)/tcputil.so $(VFS)/fish.so $(VFS)/ftpfs.so $(VFS)/mcfs.so $(VFS)/utilvfs.so $(VFS)/local.so $(VFS)/vfs.so $(VFS)/tar.so $(VFS)/sfs.so $(VFS)/names.so $(VFS)/container.so $(VFS)/extfs.so $(VFS)/util-alone.so $(VFS)/util.sor $(VFS)/utilunix.sor $(VFS)/direntry.so -o podfuk-static

/dev/cfs0:
	mknod /dev/cfs0 c 67 5

/overlay:
	mkdir /overlay

run: /dev/cfs0 /overlay podfuk
	{ cd $(LINUX)/fs/coda; insmod coda.o; }
	sleep 1
	{ ./podfuk; } &
	sleep 1
	mount /dev/cfs0 /overlay -tcoda

clean:
	rm podfuk podfuk-static

$(MIDNIGHT)/vfs/libvfs.so:
	echo 'Attempting to make libvfs.so from midnight'
	cd $(MIDNIGHT)/vfs
	make libvfs.so

ifdef donotdefine
#endif

/* TTD:
 add timeouts for non-operative ftp sites
 a lookup request precipitates a login & read;
               is this neccessary for the root?
 check uid and gid are coming through the attributes
 merge common code for the creation cases (creat, mkdir) and deletion (rmdir, unlink)
 finish writing CODA_ACCESS
 keep a trail of temp files and BLOW THEM AWAY !!!
 is there another way to do create? yes, but I need to modify MC to give me
               the local filename of a file; also, I need to be able to set the
               has_changed bit in vfs
 */

            /* Coda/Venus in a nutshell:
             * Coda is a filesystem, lives in the kernel
             * Venus is an app; podfuk is a replacement for venus
             * When a coda mount needs service, it sends a message
             *   up to podfuk through a pipe
             * Coda/Podfuk both open /dev/cfs0 and make ioctl calls
             * Message includes ViceFid (coda file/dir handle)
             *   which is: u32 volume-id, u32 node-id, u32 unique-num
             * Each file has an attribute struct: very similar to inode
             *
             * Typ sequence of calls to podfuk from coda:
             * file read: access(), open(), close()
             *    (the read() is done through the local file)
             * dir listing: access(), open(), access(), close()
	     * file write: lookup(file), access(parent), create(file), getattr(), open(),
             *    close(), setattr()
             *
* we have to hide the awful path names from the user
*   many apps cannot handle the #, : and @
* how about
* /ftpsites/site_alias -> /ftpsites/.handle/site_name
* and mount /dev/cfs0 /ftpsites/.handle -t coda
* and the file ~/.podfukrc contains sections called
* [site_name]
* user=patb
* pass=patb
* path=chroot_path
*
* or, we could have a file /ftpsites/.config/site_name
*
* finally:
*       /mnt/ftp/aliasname
*               has placeholders such as
*               /mnt/ftp/corel -> /mnt/.mcvfs/ftp.corel.com
*       /mnt/.mcvfs is mounted as -t coda
*       /mnt/.mcvfsinf has config files, such as /mnt/.mcvfsinf/ftp.corel.com
*/
            
             
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <glib.h>

#include <linux/coda.h>

#include <vfs/vfs.h>
#ifndef USE_VFS
#error Midnight was not configured to use vfs. Please configure it to use it. 
#endif

#define PARANOIA 1

#ifdef DEBUG
# define DEBUG_PRINT printf
#else
# define DEBUG_PRINT if (0) printf
#endif
            
#define MY_VOL   0x01234567
#define MY_VNODE 0xffffffff
#define MY_INODE 0x12345678

extern int ftpfs_use_passive_connections;
       int ftpfs_login_timeout = 10;
int cfs;

struct v_list {
  char *real_name;
  char *local_name;
  char  has_changed;
  char  is_dir;
  /* timestamp ?? */
};

/* a linked list of the above objects */
GList *pList;

int count = 0;

/******************** shutdown and timeout **********************************/
void signal_handler(int signo)
{
  mc_vfs_done();
  exit(0);
}

/******************** config ************************************************/
void
set_global_var( const char* section, const char* var, const char* val )
{
  if ( !strcmp( var, "ftpfs_use_passive_connections" ) )
    ftpfs_use_passive_connections = ( val[0] == '1' );
  if ( !strcmp( var, "logfile" ) && val[0] ) {
      ftpfs_set_debug( val ); printf("logfile set to %s\n",val); }
  if ( !strcmp( var, "ftpfs_login_timeout") )
    ftpfs_login_timeout = atoi(val);
}

/* load and parse an ini file: handles sections, comments and params */
void
load_profile( const char* config_file, void (*setter) (const char*, const char*, const char*) )
{
  char* section = 0;
  FILE* f = fopen( config_file, "r" );
  if ( !f ) return;
  while (!feof(f)) {
    char buf[250];
    char *p = buf;
    fgets(buf,250,f);
    p = g_strstrip(buf);
    if (p[0] == '[') {
          g_free(section);
          section = g_strdup(g_strdelimit(p+1,"]",'\0'));
          //printf("got section header [%s]\n", section);
    } else if (p[0] == '#') {
          //printf("got a comment <%s>\n",p+1);
    } else {
          char **pp;
          pp = g_strsplit(buf,"=",1);
          if (pp[0] && pp[1]) (*setter)( section, pp[0], pp[1] );
          //printf("got strings <%s> and <%s>\n",g_strstrip(pp[0]),pp[1] ? (g_strstrip(pp[1])) : "");
          g_strfreev(pp);
    }
  }
  g_free(section);
  fclose(f);
}

char *
look_name( ViceFid id )
{
  if ((id.Volume != MY_VOL) || (id.Vnode != MY_VNODE)) {
    printf( "Bad handle passed %x/%x/%x\n", id.Volume, id.Vnode, id.Unique );
    exit(1);
  }
  if (PARANOIA) {
    GList *pWalk;
    for( pWalk = pList; pWalk; pWalk = g_list_next(pWalk) ) {
        if (pWalk->data == (gpointer) id.Unique)
            break;
    }
    if (!pWalk) {
      printf( "Bad handle passed %x/%x/%x\n", id.Volume, id.Vnode, id.Unique );
      exit(1);
    }
  } /* paranoia */
  return ((struct v_list*)id.Unique)->real_name;
}

ViceFid
alloc_vfid( char *name )
{
  ViceFid res;
  struct v_list *h = g_new0(struct v_list,1);
  pList = g_list_append(pList, h);
  h->real_name = strdup(name);
  count++;

  res.Volume = MY_VOL;
  res.Vnode =  MY_VNODE;
  res.Unique = (long) h;
  DEBUG_PRINT( "new vfid created at %p (file <%s>)\n",h,h->real_name );
  return res;
}

void
delete_name( const char * real_name ) {
  GList *pWalk;
  /* at this point, the paranoid would check for a dirty file and write it out */
  for( pWalk = pList; pWalk; pWalk = g_list_next(pWalk) ) {
    if (!strcmp( ((struct v_list*)pWalk->data)->real_name, real_name)) {
      pList = g_list_remove( pList, pWalk->data );
      return;
    }
  }
  printf( "failure trying to delete the file %s\n", real_name );
}

inline char *
get_local_copy_new( ViceFid id)
{
  int h;
  struct v_list* v = (struct v_list*)id.Unique;
  v->local_name = tempnam( NULL, "podfu" );
  h = open(v->local_name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0666);
  if (h<0)
    return v->local_name = 0;
  write(h, &h, 0);
  close(h);
  v->has_changed = 1;
  return v->local_name;
}
            
inline char *
get_local_copy( ViceFid id, int flags )
{
  struct v_list* v = (struct v_list*)id.Unique;
  /* I would normally put an assert here, but look_name() was called already */
  /* truncated on writeonly files do not need an ftp read first */
  if ((flags & C_O_READ) || !(flags & C_O_TRUNC)) {
    if (flags & C_O_WRITE)
      v->has_changed = 1;
    return v->local_name = mc_getlocalcopy(v->real_name);
  } else
    return get_local_copy_new(id);;
}

void
write_dir_entry( int handle, char *name )
{
  struct venus_dirent ent;
  int len;
  static int ino = MY_INODE;
  
  ent.d_fileno = ino++;
  ent.d_type = CDT_REG; /* should we report dirs & symlinks now ?? */
  
  ent.d_namlen = len = strlen(name);
  strcpy(ent.d_name, name);
  len += ((char *) &ent.d_name - (char *) &ent);
  ent.d_reclen = len;
  if (write(handle, &ent, len)<0)
    DEBUG_PRINT( "error writing dirent (%m)" );
  DEBUG_PRINT( "[%s]", name );
}

char *
get_dir_copy( ViceFid id )
{
  struct v_list* v = (struct v_list*) id.Unique;
  DIR *dir = mc_opendir(v->real_name);
  char *cache;
  int handle;
  struct dirent *ent1;
  char zeros[128] = {0,0, };

  if (!dir)
    return NULL;
  DEBUG_PRINT( "converting dir to file " );
  cache = tempnam (NULL, "dir");
  handle = open (cache, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (handle == -1) {
    DEBUG_PRINT("can not open temp file\n" );
    closedir(dir);  /* ?? should this be mc_closedir() ?? */
    free(cache); /* possible memory leak fixed */
    return NULL;
  }
  DEBUG_PRINT( "%s:", cache );

  while (ent1 = mc_readdir(dir)) 
     write_dir_entry(handle, ent1->d_name);
  write(handle, zeros, 2);
  DEBUG_PRINT(" ");
  close(handle);
  mc_closedir(dir);
  v->is_dir = 1;
  return v->local_name = cache;
}

void
unget_local_copy( ViceFid id )
{
  struct v_list* v = (struct v_list*) id.Unique;
  int ret_val;
  if (v->local_name) {
    if (v->has_changed) DEBUG_PRINT( "saving the file %s ", v->local_name);
        /* NOTE: if we give mc_ungetlocalcopy() the exact correct localfile name,
         it will not free() the 2nd parameter string */
    if (v->is_dir) {
      unlink( v->local_name );
    } else
      mc_ungetlocalcopy(v->real_name, v->local_name, v->has_changed);
    v->has_changed = 0;
    /* re-enable this line once we fix mc_ungetlocalcopy */ //g_free(v->local_name);
    v->local_name = 0;
  } else {
    DEBUG_PRINT( "<empty file name on a close>" );
  }
}

/* convert mc vfs type/attr to coda type/attr */
int
st2type(struct stat *s)
{
  if (S_ISDIR(s->st_mode))
    return C_VDIR;
  if (S_ISREG(s->st_mode))
    return C_VREG;
  if (S_ISBLK(s->st_mode))
    return C_VBLK;
  if (S_ISCHR(s->st_mode))
    return C_VCHR;
  if (S_ISLNK(s->st_mode))
    return C_VLNK;
  if (S_ISSOCK(s->st_mode))
    return C_VSOCK;
  if (S_ISFIFO(s->st_mode))
    return C_VFIFO;
  DEBUG_PRINT( "Unknown type\n" );
  return C_VNON;
}

void
st2attr(struct stat *s, struct coda_vattr *a)
{
  static int id = 0;

  bzero(a, sizeof(struct coda_vattr));
  a->va_type = st2type(s);
#define COPY(x) a->va_##x = s->st_##x;
  COPY(mode);
  DEBUG_PRINT( "(mode = %o)", s->st_mode );
  COPY(nlink);
  COPY(uid);
  COPY(gid);
  a->va_fileid = id++;
  COPY(size);
  a->va_blocksize = 1024; /* s->st_blksize; */
  a->va_atime.tv_nsec = a->va_ctime.tv_nsec = a->va_mtime.tv_nsec = 0;
  a->va_atime.tv_sec = s->st_atime;
  a->va_mtime.tv_sec = s->st_mtime;
  a->va_ctime.tv_sec = s->st_ctime;
  a->va_gen = 0;
  a->va_flags = 0;

  a->va_bytes = s->st_blocks * s->st_blksize;
  a->va_filerev = 0;

}

/* Poor protection against deadlocks when podfuk is mounted on
   /overlay (right protection is VFS_NO_LOCALHASH, which is
   unfortunately in new midnights, only). 

   In fact, "new midnight" currently means my own tree at my home
   machine :-) */
int 
legal_name(char *name)
{
  if (!strncmp (name, "/mnt/.mcvfs", 11)
        || (strchr(name, '#') && !vfs_rosplit(name)))
    return 0;
  return 1;
}

int
main(void)
{
  char buf[2048];

  setvbuf(stdout, NULL, _IONBF, 0);
  cfs = open( "/dev/cfs0", O_RDWR );
  if (cfs == -1) {
    DEBUG_PRINT( "Error opening cfs0: %m\n" );
    exit(1);
  } 
#ifndef NODAEMON
  chdir("/");
  if (fork()) {
    exit(0);
  }
  setsid(); /* to guarantee effect, the man page says this should be performed after
	       a fork, as the child... */
  DEBUG_PRINT( "podfuk spawned as %d, ", getpid() );
#endif
  DEBUG_PRINT( "Opened cfs0\n" );
  load_profile( "/etc/.podfukrc", set_global_var );
  mc_vfs_init();
  signal(SIGTERM, signal_handler);
  signal(SIGINT,  signal_handler);
  vfs_flags |= FL_ALWAYS_MAGIC;
//#ifdef FL_NO_LOCALHASH
//  vfs_flags |= FL_NO_LOCALHASH;	/* Avoid local deadlock when someone accesses /#ahoj#utar */
//#else
//#warning Sorry, your midnight is old. It may deadlock under some uses.
//#endif

  while(1) {
    char in_buf[sizeof(union inputArgs) + 256];
    union inputArgs *req = (union inputArgs*) &in_buf[0];
    union outputArgs rep;
    struct stat st;
    int msg;
    int size;
    char *name;
    fd_set rfds;
    struct timeval tv;
    int retval;

    /* Watch stdin (fd 0) to see when it has input. */
    FD_ZERO(&rfds);
    FD_SET(cfs, &rfds);

    /* Wait up to five seconds. */
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    retval = select(cfs+1, &rfds, NULL, NULL, &tv);
    if (retval == -1) {
      DEBUG_PRINT( "select failed: %m\n" );
      exit(1);
    }
    vfs_timeout_handler();
    /* Don't rely on the value of tv now! */
    
    msg = read(cfs, req, sizeof(*req) + 256);
    if (msg == -1)
      continue;
    DEBUG_PRINT( "got %3.d byte command: opcode = %2.d ", msg, req->ih.opcode );
    //    printf( " (un=%d,pid=%d,pgid=%d) ", req.ih.unique, req.ih.pid, req.ih.pgid );
    //    printf( " (uid=%d,euid=%d,suid=%d,fsuid=%d) ",req.ih.cred.cr_uid,req.ih.cred.cr_euid,req.ih.cred.cr_suid,req.ih.cred.cr_fsuid );
    setfsgid (req->ih.cred.cr_fsgid); vfs_gid = req->ih.cred.cr_fsgid;
    setfsuid (req->ih.cred.cr_fsuid); vfs_uid = req->ih.cred.cr_fsuid;
    rep.oh.opcode = req->ih.opcode;
    rep.oh.unique = req->ih.unique;
    rep.oh.result = ENOSYS;
    size = sizeof(rep.oh);

#define CMD(x) rep.oh.result = 0; size = sizeof(rep.coda_##x); DEBUG_PRINT( "%.10s",#x );
#define CMD_NOREP(x) rep.oh.result = 0; DEBUG_PRINT( "%.10s",#x );
//#define DUMP(x) DEBUG_PRINT( "(%x/%x/%x:%s)", req->coda_##x.VFid.Volume, req->coda_##x.VFid.Vnode, req->coda_##x.VFid.Unique, look_name(req->coda_##x.VFid));
#define DUMP(x) DEBUG_PRINT( " (base:%s)", look_name(req->coda_##x.VFid));
#define DUMP_NAME(x) DEBUG_PRINT( " (base:%s\tname:%s)", look_name(req->coda_##x.VFid), (char*)req + req->coda_##x.name);
#define STAT(x) if (!legal_name(x)) {rep.oh.result = ENOENT; break; } else if (mc_stat(x, &st) == -1) { rep.oh.result = errno; DEBUG_PRINT( "file %s probably does not exist.",x ); break; }

    switch (req->ih.opcode) {
    case CODA_ROOT:
      CMD(root);
      DEBUG_PRINT( ": " );
      rep.coda_root.VFid = alloc_vfid( "/" );
      break;

      /********************* file props & access *******************/
    case CODA_GETATTR:
      CMD(getattr);
      DUMP(getattr); DEBUG_PRINT( ": " );
      name = look_name(req->coda_getattr.VFid);
      STAT(name); /* fills in st */
      st2attr( &st, &rep.coda_getattr.attr );
      break;
    case CODA_SETATTR:
      CMD_NOREP(setattr);
      name = look_name(req->coda_setattr.VFid);
      DEBUG_PRINT( " try to set mode to %o, mdate to %x and owner to %d: ", req->coda_setattr.attr.va_mode, req->coda_setattr.attr.va_mtime.tv_sec, req->coda_setattr.attr.va_uid );
      if ( req->coda_setattr.attr.va_mode != (u_short) -1)
        mc_chmod( name, req->coda_setattr.attr.va_mode );
      /* cannot change owner, group or dates .......*/
      rep.oh.result = 0; /* lie to the caller !! */
      break;
    case CODA_ACCESS:
        /* this needs to be beefed up !! */
      rep.oh.result = 0; DEBUG_PRINT( "access" );
      DUMP(access); DEBUG_PRINT( "flags:%x ", req->coda_access.flags );
      break;
    case CODA_LOOKUP: /* !! ttd: search for existing VFid's instead of making many new ones */
      CMD(lookup);
      DUMP_NAME(lookup);
      name = ((char *) req) + req->coda_lookup.name;
      DEBUG_PRINT( "flags=%d: ", req->coda_lookup.flags );
      sprintf( buf, "%s/%s", look_name(req->coda_lookup.VFid), name );
      DEBUG_PRINT( "(stat: %s)", buf );
      STAT(buf); /* fills in st */
      rep.coda_lookup.VFid = alloc_vfid(buf);
      rep.coda_lookup.vtype = st2type(&st);
      break;

      /************************ open, close, create & mkdir ******************/
    case CODA_OPEN:
      CMD(open);
      DUMP(open); DEBUG_PRINT( "flags:%#x ", req->coda_open.flags );
      name = look_name(req->coda_open.VFid);
      STAT(name);
      if (S_ISDIR(st.st_mode)) {
	DEBUG_PRINT( "[open request on a directory]" );
	name = get_dir_copy(req->coda_open.VFid);
      } else name = get_local_copy(req->coda_open.VFid,req->coda_open.flags);
      if (!name || (stat(name, &st)==-1)) {
	rep.oh.result = errno;
	break;
      }
      rep.coda_open.dev = st.st_dev;
      rep.coda_open.inode = st.st_ino;
      break;
    case CODA_CLOSE:
      CMD_NOREP(close);
      DUMP(close);
      name = look_name(req->coda_open.VFid);/*!!*/
      unget_local_copy(req->coda_open.VFid);
      /* !! how do we get error codes back to caller ?? */
      DEBUG_PRINT( " the current VFid list has %d elements\n", g_list_length( pList ) );
      break;
    case CODA_CREATE:
      CMD(create);
      DUMP_NAME(create);
      name = ((char *) req) + req->coda_create.name;
      sprintf( buf, "%s/%s", look_name(req->coda_mkdir.VFid), name );
      DEBUG_PRINT( "(create_name: %s)", buf );
      /* ?? try a 0 byte write ??*/
#if 0
      name = get_local_copy_new(rep.coda_create.VFid);
      {int i; i = open(name, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
          write(i,&i,1); close(i);
	  unget_local_copy(rep.coda_create.VFid); }
      ((struct v_list*)(rep.coda_create.VFid.Unique))->has_changed = 1;
#endif
      {
        int i;
        i = mc_open(buf, O_WRONLY | O_CREAT, 0600);
        if (i>=0) {
          mc_write(i,(void*)&i,1);
          mc_close(i);
          rep.coda_create.VFid = alloc_vfid(buf);
        } else
          rep.oh.result = errno;
      }
      rep.coda_create.attr = req->coda_create.attr;
      break;
    case CODA_MKDIR:
      CMD(mkdir);
      DUMP_NAME(mkdir);
      /* note: we only get the entry name; must concat with VFid */
      name = ((char *) req) + req->coda_mkdir.name;
      DEBUG_PRINT( "(mkdir: %s)", buf );
      if ( mc_mkdir( buf, req->coda_mkdir.attr.va_mode ) == 0) {
        rep.coda_mkdir.VFid = alloc_vfid(buf);
        rep.coda_mkdir.attr = req->coda_mkdir.attr;
      } else
        rep.oh.result = errno;
      break;

      /********************* deletions & rename ******************/
    case CODA_RMDIR: /* !! there is common code in rmdir, remove and mkdir */
      CMD_NOREP(rmdir);
      DUMP_NAME(rmdir);
      /* note: we only get the entry name; must concat with VFid */
      name = ((char *) req) + req->coda_rmdir.name;
      sprintf( buf, "%s/%s", look_name(req->coda_rmdir.VFid), name );
      DEBUG_PRINT( "(rmdir: %s)", buf );
      if ( mc_rmdir( buf ) == 0) {
        rep.oh.result = 0;
        delete_name( buf );
      } else
        rep.oh.result = errno;
      break;
    case CODA_REMOVE:
      CMD_NOREP(remove);
      DUMP_NAME(mkdir);
      /* note: we only get the entry name; must concat with VFid */
      name = ((char *) req) + req->coda_remove.name;
      DEBUG_PRINT( "name=%s, mode=%#o: ", name );
      sprintf( buf, "%s/%s", look_name(req->coda_remove.VFid), name );
      DEBUG_PRINT( "(remove: %s)", buf );
      if ( mc_unlink( buf ) == 0) {
        rep.oh.result = 0;
        delete_name( buf );
      } else
        rep.oh.result = errno;
      break;
    case CODA_RENAME:
      CMD_NOREP(rename);
      {
        char buf2[256];
        name = ((char *) req) + req->coda_rename.srcname;
        sprintf( buf, "%s/%s", look_name(req->coda_rename.sourceFid), name );
        name = ((char *) req) + req->coda_rename.destname;
        sprintf( buf2, "%s/%s", look_name(req->coda_rename.destFid), name );
        DEBUG_PRINT( "src name=%s, dest name %s: ", buf, buf2 );
        if ( mc_rename( buf, buf2 ) == 0) {
          rep.oh.result = 0;
          delete_name( buf );
        } else
          rep.oh.result = errno;
      }
      break;

    default:
      DEBUG_PRINT("unimplemented coda call");
      break;
    }

    DEBUG_PRINT( "returning %d, %d bytes\n", rep.oh.result, size );
    msg = write(cfs, &rep, size);
    fflush(stdout);

    setegid (0); vfs_gid = 0;
    seteuid (0); vfs_uid = 0;
  } /* while */
}

/* Leave this in place - this is here so that code still is valid Makefile. */
#if 0
endif
#endif

