#if 0
ifdef notdef
*
* POrtable Dodgy Filesystems in Userland (this used to be hacK!)
*                alias podfuk
* pretend you are venus cache manager, and export virtual files to kernel
* (plus, you can get kernel patch there; with it you will not have to cd
* /overlay first. Very handy.)
*
* Copyright 1999/2000 Pavel Machek <pavel@ucw.cz>
* Copyright 2000 Pat Beirne <patb@corel.ca>
* Copyright 2000 Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
*
* 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>
* Version 2.8 - merged small fixes from Pat Beirne <patb@corel.ca>, preparation for rw support
* Version 2.9 - added rw support, progress bar possibility: Pat Beirne <patb@corel.ca>
* Version 3.0 - flush_Vfids() added - free memory in long term: Pat Beirne <patb@corel.ca>
* Version 3.1 - fixes to make ftpfs work
* Version 3.2 - added support for Solaris 7/8 SPARC (32bit kernel, 64bit untested yet) + 
*               minor code cleanup by
*               Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
* Version 3.3 - more cleanup
*             - fixed silly issue with select() on Solaris
*             - fixed minor flaws for Solaris 64bit kernel support
*             - Solaris version now handels mount (nosuid,ro)/umount automagically
*             - Solaris version now automagically uses /var/tmp instead of /tmp (/tmp is tmpfs 
*               and CODA kernel module does not like tmpfs)
*             - Fixed Solaris version to set euid/egid correctly (but still may fail if 
*               kerb5/secrpc is used. Requires more investigations...) to allow access to files 
*               not readable by "root" (like NFS, AFS, DFS etc.).
*             - fixed crash in CODA_RENAME as temp. buffer was too small (was: 256, now: PATH_MAX)
*             - dummy function message_1s1d no longer required when using >= mc-4.5.51
*             - Makefile now handels different OS (Linux,SunOS(=Solaris)) and developers 
*               (pavel,gisburn) automagically.
*             - 64bit Solaris is working now :-)
*             - portability: replaced printf( "%m" ) with perror()
* Version 3.4 - fixed problem that browsing ftp://sunsolve.sun.de/pub/patches/ did not list all 
*               files (31 of ~~3563) by adding missing "padding" of venus_dirent entries (see 
*               coda-5.3.8/coda-src/dir/dirbody.c DIR_Convert())
*             - removed stupid debugging code left over from debugging g_free()-related crashes.
*
* ToDo/known issues:
* - you may not use apostrophe in comments.
* - ftpfs: ftpfs should support partial reads from files. Otherwise content-based type typing 
*   (like done with /usr/bin/file) will download each complete file (which is painfull on sites 
*   with _large_ files)
* - ftpfs: browsing of large directories _painfull_ slow
* - solaris/coda: getcwd(3c) does not work, confusing _many_ applications
* - Makefile does not work with POSIX make
* - podfuk can only handle one request at the same time (which may result in deadlocks)
* - podfuk should use pathconf(2)/_PC_PATH_MAX to calculate the proper buffer size for path buffers
* - free() frees memory from glib functions (g_*) on platforms where glibc!=std-libc (all 
*   non-glibc/Linux platforms). midnights mc_def_ungetlocalcopy can be hacked to avoid that podfuk crashes 
*   too often.
* - rw-support does not work (on Solaris, status of Linux unknown [Roland]).
* - Solaris: Locking of /etc/mnttab is not as describe in getmntent(3C)
*		
* Solaris 7/SPARC/32bit+64bit kernel:
* - Building binaries:
*   - mc _must_ be build with "-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64", e.g.
*     export CFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64" before running 
*     "configure".
* - Usage: Start podfuk
*   % podfuk&
*   or
*   % podfuk /mountpoint&
*  - Warning: 
*   unmounting filesystem during activity on the filesystem _may_ PANIC the machine.
*   64bit kernel support on the way
*
* The following patch is quite usefull to avoid that podfuk crashes too often:
* 
* -- snip --
* % diff -w -c vfs.c.original vfs.c
* *** vfs.c.original      Fri Oct 13 16:44:20 2000
* --- vfs.c       Fri Oct 13 16:58:05 2000
* ***************
* *** 1132,1138 ****
* --- 1132,1141 ----
*         }
*       }
*       unlink (local);
* + #if 0    
* +     /* disabled by gisburn 20001013 */
*       g_free (local);
* + #endif    
*       return 0;
*   
*    failed:
* ***************
* *** 1140,1146 ****
* --- 1143,1152 ----
*       if (fdout) mc_close(fdout);
*       if (fdin) close(fdin);
*       unlink (local);
* + #if 0    
* +     /* disabled by gisburn 20001013 */
*       g_free (local);
* + #endif    
*       return -1;
*   }
* -- snip --
*   
* !!This version has a problem, buried in VFS. A user copies two files,
*   A and B, both on the VFS. The sequence: open(A,O_RDONLY), open(B,WR_ONLY),
*   read(A), write(B), close(B), close(A). The first open() causes the
*   VFS to read to a local copy. The second open() makes a local empty temp file.
*   The first close() will write the temp file to the VFS AND FLUSH THE
*   VFS DIRECTORY CACHE!. When the second close() is called, it re-reads the
*   file from the VFS. Wasteful but not fatal. Bad.!!
*
*   This version does not clean up the v_list linked list. Bad. !!
*
* 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. (You will 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

SHELL=/bin/sh
OS=$(shell uname -s)
LOGNAME=$(shell logname)

# DEBUG turns on diagnostic printing
# USE_MONITOR makes calls to an X progress bar
ifeq ($(OS),SunOS)
# Solaris Workshop 6 (Sun Forte)
CC = /opt/SUNWspro/bin/cc
CFLAGS = -DSOLARIS -Dsun -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DNODAEMON -DSOLARIS_UPDATE_MNTTAB -I/usr/include -g -Xa -xCC -fast -xarch=v7 -lsocket -lnsl
#CFLAGS = -DSOLARIS -Dsun -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DNODEBUG -DSOLARIS_UPDATE_MNTTAB -I/usr/include -g -Xa -xCC -fast -xarch=v7 -lsocket -lnsl
else
CFLAGS = -DNODAEMON -Wall -g
CC = gcc
endif
# Corel definitions
#CFLAGS = -DDEBUG -DUSE_MONITOR -DCOREL_DEF -Wall -g
#CFLAGS = -DDEBUG -Wall -DNODAEMON

ifeq ($(LOGNAME),pavel)
MIDNIGHT=/home/pavel/gnome/mc
else 
ifeq ($(LOGNAME),gisburn)
MIDNIGHT=/home/gisburn/cwork/coda_podfuk/mc-4.5.51
endif
endif

KERNEL=/usr/src/linux/include

VFS=$(MIDNIGHT)/vfs
MC_SRC=$(MIDNIGHT)/src
VFSOBJS=$(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 $(VFS)/cpio.so 	

LINUX=/lib/modules/2.2.14

all:	podfuk podfuk-static

podfuk:	$(VFS)/libvfs.so podfuk.c
	$(CC) podfuk.c `glib-config --libs --cflags` -I$(MIDNIGHT) -I$(KERNEL) $(VFS)/libvfs.so -o $@  $(CFLAGS)
	chmod a+rx $@

podfuk-static: $(VFSOBJS) podfuk.c
	$(CC) podfuk.c `glib-config --libs --cflags` -I$(MIDNIGHT) -I$(KERNEL) $(VFSOBJS) -o $@ $(CFLAGS) 
	chmod a+rx $@

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

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

$(VFS)/direntry.so: $(VFS)/direntry.c
	echo 'Attempting to make direntry.so from midnight'
	cd $(MIDNIGHT)/vfs; make direntry.so 

clean:
	-rm -f podfuk podfuk-static *.o $(VFSOBJS) $(VFS)/libvfs.so
		
ifeq ($(OS),Linux)
# linux-only. This will cause havoc on Solaris machines
/dev/cfs0:
	mknod /dev/cfs0 c 67 5

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

# linux only: stop podfuk
stop:
	-killall -HUP podfuk
	sleep 1
	-killall podfuk
	-umount /overlay	
endif # OS = Linux

ifeq ($(OS),SunOS)	
# Solaris-only: test ftpfs+automounter	
test-ftp:
	# this should not return a failure (like: "pwd: cannot determine current directory")
	-(cd /ftp/ftp.uni-giessen.de/pub; /usr/bin/pwd)
	# this should print "." and ".."
	-ls -la /ftp/ftp.uni-giessen.de/ | grep " .*\.$$"
	# this should not give "permission denied"
	-cat /ftp/ftp.uni-giessen.de/pub/_INDEX >/dev/null
endif # OS = SunOS

			
ifdef donotdefine
#endif

/* TTD:
 add timeouts for non-operative ftp sites
 a lookup request precipitates a login & read;
               is this neccessary for the root?
 how are we going to clean up the vfid chain?
 	there could be an "infinite" number of entries
 */

   /* 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(), lookup(), access(), close()
    * file write: lookup(file), access(parent), create(file), getattr(),
    *    open(), close(), setattr()
    */

/* When we get error return, we _must_ return error. We should log error, because someone forgot to set return code */

#define ERRNO (errno?errno:ENOCSI)

#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include <sys/types.h>
#ifdef SOLARIS
#include <sys/types32.h>
#include <sys/fcntl.h>
#include <sys/mount.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#endif
#include <sys/stat.h>
#include <sys/time.h>

#ifdef SOLARIS
/* use local copy of includes (be sure that -Dsun is set. 
 * CODA includes should look for "__sun", but unfortunately 
 * they use "sun"... ;-( ) 
 */
#include "coda.h"
#else
#include <sys/fsuid.h>
#include <linux/coda.h>
#endif

#define VFS_STANDALONE 1
#include <vfs/vfs.h>
#include <vfs/callback.h>

#ifndef USE_VFS
#error Midnight was not configured to use vfs. Please configure it to use it. 
#endif

#define PARANOIA 1

#define VERSION_STRING "3.4"

/* this is mainly for creating binaries for curious people... =:-) */
#ifndef PODFUK
#define PODFUK "podfuk"
#endif

#define DEBUG
#ifdef DEBUG
# define DEBUG_PRINT printf
# define FLUSH_TIME 15
#else
# define DEBUG_PRINT if (0) printf
# define FLUSH_TIME 300
#endif

#ifdef SOLARIS
# define MOUNT_POINT_STRING "/mnt"
#elif COREL_DEF
# define MOUNT_POINT_STRING "/mnt/.mcvfs"
#else
# define MOUNT_POINT_STRING "/overlay"
#endif
static char *server_mountpoint = MOUNT_POINT_STRING;

#define CODA_DEVICE "/dev/cfs0"

/* There is nothing magic about these constants. I just selected them
 * so that kernel does not complain too loudly.  */
#define MY_VOL   0x01234567
#define MY_VNODE 0xffffffff
#define MY_INODE 0x12348765
#define MK_FID(f,v,n,u) {f.Volume=v;f.Vnode=n;f.Unique=u;}

/* missing from vfs.h */
vfs *vfs_rosplit (char *path);
extern int ftpfs_use_passive_connections;
extern int ftpfs_retry_seconds;

/* we need some padding with zeros */
static char zeropad[2048];

/* our handle to the pipe */
static int cfs;

/* our local object: NOTE: we try to do as little as possible
 * and trust that vfs will handle caching for us */
struct v_list {
  char *real_name;
  char *local_name;
  short usage;
  char  has_changed; // dirty bit
  char  is_dir;
  char  is_created; // marker for files which do not yet exist at the other end
  char  is_trunc;   // marker for files which are to be sent to the other end
  /* timestamp ?? */
};
// accessors
#define v_usage(x) ((struct v_list*)x.Unique)->usage
#define v_created(x) ((struct v_list*)x.Unique)->is_created

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

static int count = 0;

struct stat generic_stat = { 0 /* dev */, 0 /* pad */,
	0 /* inode */, S_IFREG | 0666 /* mode */,
	0, 0, /* uid, gid */ 0 /* device */, 0 /* pad */,
	0 /* size */, 1024 /* blksize */ }; /* rest are 0 */

#ifdef USE_MONITOR
static int  monitor_handle;
static char monitor_buf[300];
#endif


/******************** mount on Solaris **********************************/
#ifdef SOLARIS
#define eprint printf /* BUG: should be stderr */

static
int coda_mount( char *venusRoot )
{
    /* New mount */
    if( mount(CODA_DEVICE, venusRoot, (MS_DATA|MS_NOSUID|MS_RDONLY), "coda", NULL, 0) != 0 ) {
      return( EXIT_FAILURE );
    } else {
#ifdef SOLARIS_UPDATE_MNTTAB    
      /* Success - Update the /etc mount table entry
       * this may not be required on Solaris 8 as /etc/mnttab 
       * is updated by the kernel automagically
       */
      int           lfd, 
                    mfd;
      int           lck;
      FILE         *mnttab;
      char          tm[25];
      struct mnttab mt;

      lfd = open ("/etc/.mnttab.lock", O_WRONLY|O_CREAT, 0600);
      if (lfd >= 0) {

        lck = lockf(lfd, F_LOCK, 0);
        if (lck == 0) {
        
          mnttab = fopen(MNTTAB, "a+");
          if (mnttab != NULL) {
            mt.mnt_special = "CODA";
            mt.mnt_mountp  = venusRoot;
            mt.mnt_fstype  = "CODA";
            mt.mnt_mntopts = "ro";
            mt.mnt_time    = tm;
            sprintf (tm, "%ld", (long)time(0));
            
            putmntent(mnttab,&mt);
            
            fclose(mnttab);
          } else
            eprint ("Could not update /etc/mnttab.");
          (void) lockf(lfd, F_ULOCK, 0);
        } else
          eprint ("Could not update /etc/mnttab.");
        close(lfd);
      } else
        eprint ("Could not update /etc/mnttab.");
#endif /* SOLARIS_UPDATE_MNTTAB */
      ;	
    }
    
    return( EXIT_SUCCESS );
}   


static int
coda_umount( const char *mountpoint )
{
#ifndef SOLARIS_UPDATE_MNTTAB  
  if( umount( server_mountpoint ) != 0 )
  {
    perror( "umount failed" );
    return( EXIT_FAILURE );
  }
#else
  /* BUG: This is _evil_ - but it handels upading of /etc/mnttab, too.
   * On Solaris 2.8 /etc/mnttab is managed by the kernel, then the umount() 
   * above can handle this, too.
   */
   {
     char cmd[1024];
     sprintf( cmd, "/usr/sbin/umount %s", mountpoint );
     system( cmd );
   }
#endif /* !SOLARIS_UPDATE_MNTTAB */
    /* BUG. should return failure on failure */
    return( EXIT_SUCCESS );
}
#endif

/******************** misc **********************************/

/* allocate a tmp. name and be sure that the resulting string can be safely
 * free'ed with g_free()
 * This is required on all platforms where free()!=g_free()
 */
static char *
mytempnam( char *prefix )
{
    char *tmp = tempnam( NULL, prefix );
    char *name;
    
    name = g_strdup( tmp );
    free( tmp );
    return( name );
}

#if 0
/* dummy function missing in some mc builds */
void message_1s1d (int flags, char *title, char *str, int d)
{
    /* NOP */
}
#endif

/******************** shutdown and timeout **********************************/
static void 
signal_handler(int signo)
{
  mc_vfs_done();

#ifdef SOLARIS
  printf("umounting...\n");
  coda_umount( server_mountpoint );
#endif  
  exit(EXIT_SUCCESS);
}


#ifdef FLUSH_VFIDS
static void flush_VFids(void)
{
  GList *pWalk, *pPrev;

  struct coda_purgefid_out cmd;

  DEBUG_PRINT( "cleaning the local VFid cache\n" );
  /* note: we are deleting elements, so we have to walk carefully */
  /* walk backward so that we purge children, then parents */
  for ( pWalk = g_list_last(pList), pPrev = g_list_previous(pWalk);
        pPrev;
        pWalk = pPrev, pPrev = g_list_previous(pPrev)) {
    if (((struct v_list*)pWalk->data)->usage == 0) {
      struct v_list* v = (struct v_list*) pWalk->data;
      DEBUG_PRINT( "purging list element <%s>\n", v->real_name );

      /* !!! OOPS !!! if a shell has a cwd on our mount point, then this
       purge will not work, leaving broken VFids !!! */
      cmd.oh.opcode = CODA_PURGEFID;
      cmd.oh.unique = 0;
      MK_FID(cmd.CodaFid,MY_VOL,MY_VNODE,(long)v);
      write(cfs, &cmd, sizeof(cmd));
      
      g_free( v->real_name );
      if (v->local_name) g_free( v->local_name);
      g_free(pWalk->data);
      pList = g_list_remove_link(pList, pWalk);
      g_list_free(pWalk);
    }
  }
}
#endif /* FLUSH_VFIDS */


/******************** config ************************************************/
static 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( (char *)val ); printf("logfile set to %s\n",val); }
  if ( !strcmp( var, "ftpfs_login_timeout") )
    ftpfs_retry_seconds = atoi(val);
}

#if 0
inline char* g_strstrip(char*c) {
	char *p;
	for (p=c; *p && isspace(*p); p++);
	memmove(c,p,strlen(p)+1);
	for (p=c+strlen(c); p>c && isspace(p[-1]); p--);
	*p = 0;
	return c;}

#endif

/* load and parse an ini file: handles sections, comments and params */
static 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);
	  g_strdelimit(p+1,"]",'\0');
          section = g_strdup(p+1);
          //printf("got section header [%s]\n", section);
    } else if (p[0] == '#') {
          //printf("got a comment <%s>\n",p+1);
    } else {
          char *pp;
          pp = strchr(buf,'=');
	  if (pp>buf && pp<(buf+strlen(buf)-1)) {
	     *pp=0;
             (*setter)( section, g_strstrip(buf), g_strstrip(pp+1) );
          //printf("got strings <%s> and <%s>\n",buf,pp+1);
	  }
    }
  }
  g_free(section);
  fclose(f);
}

/*************************** debugging callback ****************************/
#ifdef USE_MONITOR
static void progress_func( char* m)
{
  if (monitor_handle==-1) return;
  /* Send the progress string to the monitor.
   * Add an extra zero to delimit the string. */
  write(monitor_handle,m,strlen(m)+1);
}
#endif /* USE_MONITOR */

/************************** file name maintenance **************************/

static char *
look_name( ViceFid id )
{
  if ((id.Volume != MY_VOL) || (id.Vnode != MY_VNODE)) {
    printf( "Bad handle passed %lx/%lx/%lx\n", id.Volume, id.Vnode, id.Unique );
    exit(EXIT_FAILURE);
  }
  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 %lx/%lx/%lx\n", id.Volume, id.Vnode, id.Unique );
      return NULL; //exit(EXIT_FAILURE);
    }
  } /* paranoia */
  return ((struct v_list*)id.Unique)->real_name;
}


static 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 = g_strdup(name);
  count++;

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

#if 0
static void
dump_vfid_list(void) 
{
  GList* pWalk;
  for ( pWalk = pList; pWalk; pWalk = g_list_next(pWalk) ) {
  	struct v_list* v = (struct v_list*) pWalk->data;
  	printf("list element <%s>\n",v->real_name);
  }
}
#endif

static 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)) {
      struct v_list* v = (struct v_list*) (pWalk->data);
      g_free(v->real_name);
      if (v->local_name) g_free(v->local_name);
      pList = g_list_remove( pList, pWalk->data );
      g_free(pWalk->data);
      count--;
      return;
    }
  }
  printf( "failure trying to delete the file %s\n", real_name );
}

static inline int
CodaToVfsFlags( int flags )
{
  return ((flags & (C_O_READ | C_O_WRITE)) == (C_O_READ | C_O_WRITE)) ? O_RDWR :
	   (((flags & (C_O_READ | C_O_WRITE)) == C_O_WRITE) ? O_WRONLY : O_RDONLY) |
	((flags & C_O_TRUNC) ? O_TRUNC : 0) |
	((flags & C_O_CREAT) ? O_CREAT : 0) |
	((flags & C_O_EXCL) ? O_EXCL : 0);
}


/*********************local copy management ********************************/
static 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 files do not need an ftp read first; this code cluster handles it */
  v->is_created = 0;
  if (flags & C_O_WRITE)
    v->has_changed = 1;
  if (flags & C_O_TRUNC) {
    v->is_trunc = 1;
    v->local_name = mytempnam( PODFUK );
    close(open(v->local_name, CodaToVfsFlags(flags),0666)); /* touch the file */
    return v->local_name;
  } else  {
    char *name = mc_getlocalcopy(v->real_name); 
    if( !name ) printf( "\nget_local_copy: NULL for '%s'\n", v->real_name );
    return v->local_name = name;
  }
}

static void
write_dir_entry( int handle, char *name, int *offset )
{
  struct venus_dirent ent;
  static unsigned long ino = MY_INODE; /* this must be ulong */
  
  ent.d_fileno = ino++; /* FIXME: there should be a "better" way to get unique filenos...*/
#ifndef SOLARIS  
  ent.d_type = CDT_REG; /* the kernel does not use this */
#endif
  strncpy(ent.d_name, name, CODA_MAXNAMLEN);
  ent.d_name[CODA_MAXNAMLEN] = '\0';
  ent.d_namlen = strlen(ent.d_name);
  
  ent.d_reclen = DIRSIZ(&ent); /* carefull: DIRSIZ() uses ent.d_namlen 
                                * to calculate record size 
			        */
  /* DIRSIZ() does some padding, therefore... 
   * be sure that this structure doesn't contain zeros (two zero bytes 
   * at the end are the end-of-dir-marker for this IO command !!)
   */
  if( (ent.d_namlen+1) <= CODA_MAXNAMLEN ) ent.d_name[ent.d_namlen+1] = 0xFF;
  if( (ent.d_namlen+2) <= CODA_MAXNAMLEN ) ent.d_name[ent.d_namlen+2] = 0xFF;
  if( (ent.d_namlen+3) <= CODA_MAXNAMLEN ) ent.d_name[ent.d_namlen+3] = 0xFF;

#if 0
#define DIRBLKSIZ 0x200    
  /* if what we just wrote crosses a boundary: */
  if (((*offset + ent.d_reclen) ^ *offset) & ~(DIRBLKSIZ - 1)) {
    /* Calculate how much we have to add to its d_reclen 
     * and offset for the padding: 
     */
    int pad = ((*offset + (DIRBLKSIZ - 1)) & ~(DIRBLKSIZ - 1)) - *offset;

    *offset += pad;
    printf( " (writing direntry pad %d) ", pad );
    if( write(handle, zeropad, pad ) != pad )
      perror( "\aerror writing dirent pad" );
  }
#endif

  *offset += ent.d_reclen;
  
  if (write(handle, &ent, ent.d_reclen) != ent.d_reclen)
    perror( "\aerror writing dirent" );   /* FIXME: we should propagate error */
  DEBUG_PRINT( "['%s', handle=%x, ino=%lx]", name, handle, ent.d_fileno );
}

static 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;
  int num = 0;
  int offset = 0;
  struct venus_dirent final_dirent;
  
  if (!dir)
    return NULL;
  v->is_dir = 1;
  DEBUG_PRINT( "converting dir to file " );
  cache = mytempnam ("dir");
  handle = open (cache, O_WRONLY | O_CREAT | O_EXCL, 0666);
  if (handle == -1) {
    perror("\acan not open temp file for directory list" );
    mc_closedir(dir);
    g_free(cache);
    return NULL;
  }
  DEBUG_PRINT( "%s:", cache );

  /* copy a list file names out to a file */
  while ((ent1 = mc_readdir(dir)))
  {
     write_dir_entry(handle, ent1->d_name, &offset);
     num++;
  }   
  
  /* write last venus_dirent with fileno==0. Be sure to include padding, too. */
  final_dirent.d_fileno = 0;
#if 0
  final_dirent.d_reclen = ((offset + (DIRBLKSIZ - 1)) & ~(DIRBLKSIZ - 1)) - offset;
#else
  final_dirent.d_reclen = offset;
#endif
#ifndef SOLARIS
  final_dirent.d_type = 0;
#endif  
  final_dirent.d_namlen = 0;
  
#define minEntry 12 /* FIXME: is this true for all platforms ?? */
  write(handle, &final_dirent, (final_dirent.d_reclen>minEntry)?(final_dirent.d_reclen):(minEntry) );
		  
  DEBUG_PRINT( " (wrote %d entries) ", num );
  if( close(handle) != 0 )
    perror( "\n\aerror closing temp file dir directory list" );
    
  mc_closedir(dir);
  return v->local_name = cache;
}

static void
unget_local_copy( ViceFid id )
{
  struct v_list* v = (struct v_list*) id.Unique;
  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 if (v->is_trunc) {
              /* mc_filecopy() */
      int hw = mc_open(v->real_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
      int hr = open(v->local_name, O_RDONLY);
      if (hw>=0 && hr>=0) {
        char*p = g_new(char,0x4000);
        int n = 0x4000;
        while (n>0) {
          n = read(hr,p,n);
          if (n>0) n = mc_write(hw,p,n);
        }
        mc_close(hw);
        close(hr);
        unlink(v->local_name); g_free(v->local_name);
        g_free(p);
        v->is_trunc = 0;
      }
    } else
        /* will free the local_name */
      mc_ungetlocalcopy(v->real_name, v->local_name, v->has_changed);
    v->local_name = NULL;
    v->has_changed = 0;
  } else {
    DEBUG_PRINT( "<empty file name on a close>" );
  }
}

/****************** attribute mangling *************************************/
static inline 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;
  printf( "Unknown type in st2type (%o)\n", s->st_mode );
  return C_VNON;
}

static void
st2attr(struct stat *s, struct coda_vattr *a)
{
  static long id = 0; /* must be long */

  memset(a, 0, 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 :-) */
static int 
legal_name(char *name)
{
  if (!strncmp (name, server_mountpoint, strlen(server_mountpoint))
        || (strchr(name, '#') && !vfs_rosplit(name)))
    return 0;
  return 1;
}


int
main(int argc, char** argv)
{
  char   buf[2048]; /* must be at least PATH_MAX+1 !! */
#ifdef FLUSH_VFIDS  
  time_t time_last_msg = (time_t) 0x7fffffff; /* BUG: not 64bit clean */
#endif
  
#define SIZEOF_INBUF (sizeof(union inputArgs) + 1024 + CODA_MAXNAMLEN)  
  union inputArgs  *in_buf  = g_malloc(SIZEOF_INBUF);
#define SIZEOF_OUTBUF (sizeof(union outputArgs) + 1024 + CODA_MAXNAMLEN)  
  union outputArgs *out_buf = g_malloc(SIZEOF_OUTBUF);  
  
  /* be sure that zeopad contains 0... :-)*/
  memset( zeropad, 0, sizeof(zeropad) );
  
  if( (in_buf==NULL) || (out_buf==NULL) )
  {
    perror( "can't allocate message buffers." );
    exit( EXIT_FAILURE );
  }

  switch(argc) 
  {
    case 1:
        break;
    case 2:
        server_mountpoint = argv[1];
	break;	
    default:
    {
        printf("%s: a virtual file system extention to "
#ifdef SOLARIS
           "Solaris"
#else	       
           "Linux"
#endif	   
	   "\n",argv[0]);
        printf("   version " VERSION_STRING "\n");
#ifdef SOLARIS
        /* Solaris is always _much_ easier... :-) */
        printf("   usage: %s [%s]\n",argv[0],server_mountpoint);
#else	
        printf("   usage: modprobe coda; %s %s; mount " CODA_DEVICE " %s -tcoda\n",
	       argv[0],server_mountpoint,server_mountpoint);
#endif
        exit(EXIT_FAILURE);
    }
        break;	
  }
    
  setvbuf(stdout, NULL, _IONBF, 0);

#ifndef NODAEMON
  chdir("/");
  if (fork()) {
    exit(EXIT_SUCCESS);
  }
  setsid(); /* to guarantee effect, the man page says this should be performed after
	       a fork, as the child... */
  DEBUG_PRINT( "%s spawned as %d, ", argv[0], getpid() );
#endif /* NODAEMON */

#ifdef SOLARIS
#define CACHEDIR "/var/tmp/" PODFUK
  /* podfuk should be useable from multiple users at the same time even when 
   * accessing the same files/dirs 
   */
  umask(0);
  if( mkdir( CACHEDIR, (S_IRWXU|S_IRWXG|S_IRWXO) ) != 0 )
  {
    if( errno != EEXIST )
    {
      perror( PODFUK ": can't create dir for temp. files" );
      exit( EXIT_FAILURE );
    }
  }
  
  /* /tmp on Solaris is a ram-disk (tmpfs) - unfortunately CODA kernel module 
   * is "incompatible" to tmpfs - we need traditional UFS on /var/tmp... ;-( 
   */
  putenv( "TMPDIR=" CACHEDIR );  
#endif

  cfs = open( CODA_DEVICE, O_RDWR );	/* We need to go daemon, first, then to open cfs, otherwise coda will break */
  if (cfs == -1) {
    perror( "Error opening " CODA_DEVICE );
    exit(EXIT_FAILURE);
  } 
  DEBUG_PRINT( "Opened " CODA_DEVICE ".\n" );
#ifdef USE_MONITOR
  vfs_set_callback( CALL_INFO, progress_func );
  monitor_handle = open("/var/tmp/" PODFUK ".pipe", O_RDWR|O_NONBLOCK);
  if (monitor_handle==-1)
    DEBUG_PRINT( "failed to open pipe to monitor\n" );
#endif
  load_profile( "/etc/" PODFUK "rc", set_global_var );
  mc_vfs_init();
  
  signal(SIGTERM, signal_handler); /* graceful shutdown */
  signal(SIGINT,  signal_handler);
  signal(SIGPIPE, SIG_IGN);     /* pipe faults are benign */

  vfs_flags |= FL_ALWAYS_MAGIC;
#ifdef FL_NO_LOCALHASH
  vfs_flags |= FL_NO_LOCALHASH;        /* Avoid local deadlock when someone accesses /#ahoj#utar */
#else
#error Sorry, your midnight is old. It may deadlock under some uses. Remove error and retry if you are sure
#endif

#ifdef SOLARIS
  if( coda_mount( server_mountpoint ) != EXIT_SUCCESS )
  {
    perror( PODFUK ": can't mount filesystem" );
    exit( EXIT_FAILURE );
  }
#endif
  
  while(1) {
    union  inputArgs  *req = in_buf;
    union  outputArgs *rep = out_buf;
    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 60 seconds. */
    tv.tv_sec  = 60;
    tv.tv_usec = 0;

    retval = select(cfs+1, &rfds, NULL, NULL, &tv);
    /* onError */
    if (retval == -1) {
      perror( "select failed\n" );
      exit(EXIT_FAILURE);
    }
#ifdef FLUSH_VFIDS
    /* There are some problems, somewhere. Lets take a look */
    if (retval == 0) {
        if (time(NULL)-time_last_msg > FLUSH_TIME) {
          flush_VFids();
          time_last_msg = (time_t) 0x7fffffff; /* only flush once */
        }
    }
#endif /* FLUSH_VFIDS */
    
    vfs_timeout_handler();
    /* Don't rely on the value of tv now! */

    memset(in_buf,  0, SIZEOF_INBUF);
    memset(out_buf, 0, SIZEOF_OUTBUF);
        
    msg = read(cfs, req, (SIZEOF_INBUF-1));
    if ((msg == -1) || (msg == 0) )
        continue;

    DEBUG_PRINT( "got %3.3d byte command: opcode = %2.2ld ", 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 );
    #ifdef SOLARIS
    seteuid (req->ih.cred.cr_euid);    
    setegid (req->ih.cred.cr_egid);
    #else
    setfsuid (req->ih.cred.cr_fsuid);    
    setfsgid (req->ih.cred.cr_fsgid); 
    #endif
    vfs_uid = req->ih.cred.cr_fsuid;
    vfs_gid = req->ih.cred.cr_fsgid;
    
    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( "\tbase:%s\n\t", look_name(req->coda_##x.VFid));
#define DUMP_NAME(x) DEBUG_PRINT( "\tbase:%s\tname:%s\n\t", 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; }
#define BUILD_NAME(x) name = ((char*) req) + req->coda_##x.name; sprintf( buf, "%s/%s", look_name(req->coda_##x.VFid), name);
#define GET_VFID_name(x) if (NULL==(name=look_name(req->coda_##x.VFid))) {rep->oh.result = ENOENT;  break;}

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

      /********************* file props & access *******************/
    case CODA_GETATTR:
      CMD(getattr);
      DUMP(getattr);
      GET_VFID_name(getattr);
      if (!v_created(req->coda_getattr.VFid)) {
        STAT(name); /* fills in st */
      } else
        st = generic_stat;
      st2attr( &st, &rep->coda_getattr.attr );
      break;
    case CODA_SETATTR:
      CMD_NOREP(setattr);
      DUMP(setattr);
      GET_VFID_name(setattr);
      DEBUG_PRINT( " try to set mode to %o, mdate to %lx 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 .......*/
      break;
    case CODA_ACCESS:
        /* this always returns TRUE; this seems to be ok with current CODA design */
      CMD_NOREP(access);
      DUMP(access); DEBUG_PRINT( "flags:%x ", req->coda_access.flags );
      GET_VFID_name(access);
      break;
    case CODA_LOOKUP: /* !! ttd: search for existing VFid's instead of making many new ones */
      CMD(lookup);
      DUMP_NAME(lookup);
      GET_VFID_name(lookup);
      DEBUG_PRINT( "flags=%d: ", req->coda_lookup.flags );
      BUILD_NAME(lookup);
      DEBUG_PRINT( "(stat: %s)", buf );
      STAT(buf); /* fills in st */
      rep->coda_lookup.VFid = alloc_vfid(buf);
      rep->coda_lookup.vtype = st2type(&st);
      break;
    case CODA_READLINK:
      CMD(readlink);
      DUMP(readlink);
      GET_VFID_name(readlink);
      /* put the string at the end of the structure
       * the reply packet must be smaller than the request packet
       */
      retval = mc_readlink(name,
                           (char*)&rep + sizeof(rep->coda_readlink),
                           msg - sizeof(rep->coda_readlink));
      if (retval<=0) {
        rep->oh.result = ENOENT;
      } else {
        rep->coda_readlink.count = retval; /* == strlen(sym_link_string) */
        rep->coda_readlink.data = (void*) sizeof(rep->coda_readlink);
        size += retval; /* the structure is longer by this amount */
      }
      break;

      /************************ open, close, create & mkdir ******************/
    case CODA_OPEN:
      CMD(open);
      DUMP(open); DEBUG_PRINT( "flags:%#x ", req->coda_open.flags );
      GET_VFID_name(open);
      	
      if (!v_created(req->coda_open.VFid)) {
        STAT(name);
      } else
        st = generic_stat;
      if (S_ISDIR(st.st_mode)) {
	DEBUG_PRINT( "[open request on a directory]" );
	name = get_dir_copy(req->coda_open.VFid);
      } else {
#ifdef USE_MONITOR
        if (monitor_handle>=0) {
          char* pName;
          /* feeble attempt to hide the password */
          if (!memcmp(name,"/#ftp:",6))
            pName = strchr( name+1,'/' );
          if (!pName) pName = name;
          sprintf(monitor_buf,"NAME %s",pName);
          write(monitor_handle, monitor_buf, strlen(monitor_buf+1));
        }
#endif
        name = get_local_copy(req->coda_open.VFid,req->coda_open.flags);
	if( !name ) DEBUG_PRINT( "\n --> local copy FAILED(CODA_OPEN)\n" );
      }
      if (!name || (stat(name, &st)==-1)) {
	rep->oh.result = ERRNO;
	break;
      }
      v_usage(req->coda_open.VFid) += 1;
      rep->coda_open.dev   = st.st_dev;
      rep->coda_open.inode = st.st_ino;
      break;

    case CODA_OPEN_BY_PATH:
    {
      char *namebuf;
      
      CMD(open_by_path);
      DUMP(open_by_path); DEBUG_PRINT( "flags:%#x ", req->coda_open_by_path.flags );
      GET_VFID_name(open_by_path);
      	
      if (!v_created(req->coda_open_by_path.VFid)) {
        STAT(name);
      } else
        st = generic_stat;
      if (S_ISDIR(st.st_mode)) {
	DEBUG_PRINT( "[open request on a directory]" );
	name = get_dir_copy(req->coda_open_by_path.VFid);
      } else {
#ifdef USE_MONITOR
        if (monitor_handle>=0) {
          char* pName;
          /* feeble attempt to hide the password */
          if (!memcmp(name,"/#ftp:",6))
            pName = strchr( name+1,'/' );
          if (!pName) pName = name;
          sprintf(monitor_buf,"NAME %s",pName);
          write(monitor_handle, monitor_buf, strlen(monitor_buf+1));
        }
#endif
        name = get_local_copy(req->coda_open_by_path.VFid,req->coda_open_by_path.flags);
	if( !name ) DEBUG_PRINT( "\n --> local copy FAILED(CODA_OPEN_BY_PATH)\n" );
      }
      if (!name || (stat(name, &st)==-1)) {
	rep->oh.result = ERRNO;
	break;
      }
      v_usage(req->coda_open_by_path.VFid) += 1;
      
      namebuf = (char *)((&rep->coda_open_by_path.path) + 1);
      rep->coda_open_by_path.path = namebuf - (char *)rep;
      strncpy( namebuf, name, CODA_MAXNAMLEN ); 
      namebuf[CODA_MAXNAMLEN] = '\0'; /* strncpy() may not add '\0' */
      size = (namebuf + strlen(namebuf) + 1) - (char *)rep;
    }
      break; 
    case CODA_CLOSE:
      CMD_NOREP(close);
      DUMP(close);
      GET_VFID_name(close);
      unget_local_copy(req->coda_close.VFid);
      DEBUG_PRINT( " the current VFid list has %d elements\n", g_list_length( pList ) );
      //	dump_vfid_list();
#ifdef USE_MONITOR
      if (monitor_handle>=0) {
          write(monitor_handle,"DONE",5);
      }
#endif
      v_usage(req->coda_close.VFid) -= 1;
      break;
    case CODA_CREATE:
      CMD(create);
      DUMP_NAME(create);
      GET_VFID_name(create);
      BUILD_NAME(create);
      DEBUG_PRINT( "(create_name: %s)", buf );
      rep->coda_create.VFid = alloc_vfid(buf);
      v_created(rep->coda_create.VFid) = 1;
      rep->coda_create.attr = req->coda_create.attr;
      break;
    case CODA_MKDIR:
      CMD(mkdir);
      DUMP_NAME(mkdir);
      GET_VFID_name(mkdir);
      BUILD_NAME(mkdir);
      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:
      CMD_NOREP(rmdir);
      DUMP_NAME(rmdir);
      GET_VFID_name(rmdir);
      BUILD_NAME(rmdir);
      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(remove);
      GET_VFID_name(remove);
      BUILD_NAME(remove);
      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[PATH_MAX+1];
        name = ((char *) req) + req->coda_rename.srcname;
        if (!name) {
          rep->oh.result = ENOENT;
          break;
        }
        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 %s (%ld), %d bytes\n",
	rep->oh.result ? "FAILURE" : "SUCCESS",
    	rep->oh.result, size );
    msg = write(cfs, rep, size);
    fflush(stdout);
    fflush(stderr);

    seteuid (0);   
    setegid (0); 
    vfs_uid = 0;
    vfs_gid = 0;

#ifdef FLUSH_VFIDS
    time_last_msg = time(NULL);
#endif    
  } /* while */
} 

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


