Home | History | Annotate | Line # | Download | only in coda
coda_psdev.c revision 1.11
      1  1.11  matt /*	$NetBSD: coda_psdev.c,v 1.11 1999/09/18 05:31:42 matt Exp $	*/
      2   1.2   rvb 
      3   1.1   rvb /*
      4   1.2   rvb  *
      5   1.2   rvb  *             Coda: an Experimental Distributed File System
      6   1.2   rvb  *                              Release 3.1
      7   1.2   rvb  *
      8   1.2   rvb  *           Copyright (c) 1987-1998 Carnegie Mellon University
      9   1.2   rvb  *                          All Rights Reserved
     10   1.2   rvb  *
     11   1.2   rvb  * Permission  to  use, copy, modify and distribute this software and its
     12   1.2   rvb  * documentation is hereby granted,  provided  that  both  the  copyright
     13   1.2   rvb  * notice  and  this  permission  notice  appear  in  all  copies  of the
     14   1.2   rvb  * software, derivative works or  modified  versions,  and  any  portions
     15   1.2   rvb  * thereof, and that both notices appear in supporting documentation, and
     16   1.2   rvb  * that credit is given to Carnegie Mellon University  in  all  documents
     17   1.2   rvb  * and publicity pertaining to direct or indirect use of this code or its
     18   1.2   rvb  * derivatives.
     19   1.2   rvb  *
     20   1.2   rvb  * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
     21   1.2   rvb  * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
     22   1.2   rvb  * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
     23   1.2   rvb  * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
     24   1.2   rvb  * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
     25   1.2   rvb  * ANY DERIVATIVE WORK.
     26   1.2   rvb  *
     27   1.2   rvb  * Carnegie  Mellon  encourages  users  of  this  software  to return any
     28   1.2   rvb  * improvements or extensions that  they  make,  and  to  grant  Carnegie
     29   1.2   rvb  * Mellon the rights to redistribute these changes without encumbrance.
     30   1.2   rvb  *
     31   1.4   rvb  * 	@(#) coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $
     32   1.2   rvb  */
     33   1.1   rvb 
     34   1.1   rvb /*
     35   1.1   rvb  * Mach Operating System
     36   1.1   rvb  * Copyright (c) 1989 Carnegie-Mellon University
     37   1.1   rvb  * All rights reserved.  The CMU software License Agreement specifies
     38   1.1   rvb  * the terms and conditions for use and redistribution.
     39   1.1   rvb  */
     40   1.1   rvb 
     41   1.1   rvb /*
     42   1.1   rvb  * This code was written for the Coda file system at Carnegie Mellon
     43   1.1   rvb  * University.  Contributers include David Steere, James Kistler, and
     44   1.1   rvb  * M. Satyanarayanan.  */
     45   1.1   rvb 
     46   1.1   rvb /* These routines define the psuedo device for communication between
     47   1.1   rvb  * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c,
     48   1.1   rvb  * but I moved them to make it easier to port the Minicache without
     49   1.1   rvb  * porting coda. -- DCS 10/12/94
     50   1.1   rvb  */
     51   1.1   rvb 
     52   1.1   rvb /*
     53   1.1   rvb  * HISTORY
     54   1.4   rvb  * $Log: coda_psdev.c,v $
     55  1.11  matt  * Revision 1.11  1999/09/18 05:31:42  matt
     56  1.11  matt  * Make this compile on alpha again.
     57  1.11  matt  *
     58  1.10   rvb  * Revision 1.10  1999/04/29 22:20:42  rvb
     59  1.10   rvb  * Typo/braino?
     60  1.10   rvb  *
     61  1.10   rvb  * Revision 1.9  1998/11/11 19:22:08  rvb
     62  1.10   rvb  * Lookup now passes up an extra flag.  But old veni will
     63  1.10   rvb  * be ok; new veni will check /dev/cfs0 to make sure that a new
     64  1.10   rvb  * kernel is running.
     65   1.9   rvb  * Also, a bug in vc_nb_close iff CODA_SIGNAL's were seen has been
     66   1.9   rvb  * fixed.
     67   1.9   rvb  *
     68   1.9   rvb  * Revision 1.8  1998/11/09 16:36:16  rvb
     69   1.9   rvb  * Change the way unmounting happens to guarantee that the
     70   1.9   rvb  * client programs are allowed to finish up (coda_call is
     71   1.9   rvb  * forced to complete) and release their locks.  Thus there
     72   1.8   rvb  * is a reasonable chance that the vflush implicit in the
     73   1.8   rvb  * unmount will not get hung on held locks.
     74   1.8   rvb  *
     75   1.8   rvb  * Revision 1.7  1998/09/28 17:55:22  rvb
     76   1.8   rvb  * I want to distinguish from DEBUG printouts and CODA_VERBOSE printouts.
     77   1.7   rvb  * The latter are normal informational messages that are sometimes
     78   1.7   rvb  * interesting to view.
     79   1.7   rvb  *
     80   1.7   rvb  * Revision 1.6  1998/09/26 15:24:46  tv
     81   1.7   rvb  * DIAGNOSTIC -> DEBUG for all non-panic messages.  DIAGNOSTIC is only for
     82   1.6    tv  * sanity checks and should not turn on any messages not already printed
     83   1.6    tv  * without it.
     84   1.6    tv  *
     85   1.6    tv  * Revision 1.5  1998/09/25 15:01:13  rvb
     86   1.6    tv  * Conditionalize "stray" printouts under DIAGNOSTIC and DEBUG.
     87   1.5   rvb  * Make files compile if DEBUG is on (from  Alan Barrett).  Finally,
     88   1.5   rvb  * make coda an lkm.
     89   1.5   rvb  *
     90   1.4   rvb  * Revision 1.4  1998/09/15 02:02:59  rvb
     91   1.4   rvb  * Final piece of rename cfs->coda
     92   1.4   rvb  *
     93   1.4   rvb  * Revision 1.3  1998/09/12 15:05:48  rvb
     94   1.3   rvb  * Change cfs/CFS in symbols, strings and constants to coda/CODA
     95   1.3   rvb  * to avoid fs conflicts.
     96   1.3   rvb  *
     97   1.2   rvb  * Revision 1.2  1998/09/08 17:12:47  rvb
     98   1.2   rvb  * Pass2 complete
     99   1.2   rvb  *
    100   1.1   rvb  * Revision 1.1.1.1  1998/08/29 21:26:45  rvb
    101   1.1   rvb  * Very Preliminary Coda
    102   1.1   rvb  *
    103   1.1   rvb  * Revision 1.9  1998/08/28 18:12:17  rvb
    104   1.1   rvb  * Now it also works on FreeBSD -current.  This code will be
    105   1.1   rvb  * committed to the FreeBSD -current and NetBSD -current
    106   1.1   rvb  * trees.  It will then be tailored to the particular platform
    107   1.1   rvb  * by flushing conditional code.
    108   1.1   rvb  *
    109   1.1   rvb  * Revision 1.8  1998/08/18 17:05:15  rvb
    110   1.1   rvb  * Don't use __RCSID now
    111   1.1   rvb  *
    112   1.1   rvb  * Revision 1.7  1998/08/18 16:31:41  rvb
    113   1.1   rvb  * Sync the code for NetBSD -current; test on 1.3 later
    114   1.1   rvb  *
    115   1.1   rvb  * Revision 1.8  1998/06/09 23:30:42  rvb
    116   1.1   rvb  * Try to allow ^C -- take 1
    117   1.1   rvb  *
    118   1.1   rvb  * Revision 1.5.2.8  98/01/23  11:21:04  rvb
    119   1.1   rvb  * Sync with 2.2.5
    120   1.1   rvb  *
    121   1.1   rvb  * Revision 1.5.2.7  98/01/22  22:22:21  rvb
    122   1.3   rvb  * sync 1.2 and 1.3
    123   1.1   rvb  *
    124   1.1   rvb  * Revision 1.5.2.6  98/01/22  13:11:24  rvb
    125   1.1   rvb  * Move make_coda_node ctlfid later so vfsp is known; work on ^c and ^z
    126   1.1   rvb  *
    127   1.1   rvb  * Revision 1.5.2.5  97/12/16  22:01:27  rvb
    128   1.1   rvb  * Oops add cfs_subr.h cfs_venus.h; sync with peter
    129   1.1   rvb  *
    130   1.1   rvb  * Revision 1.5.2.4  97/12/16  12:40:05  rvb
    131   1.3   rvb  * Sync with 1.3
    132   1.1   rvb  *
    133   1.1   rvb  * Revision 1.5.2.3  97/12/10  14:08:24  rvb
    134   1.1   rvb  * Fix O_ flags; check result in coda_call
    135   1.1   rvb  *
    136   1.1   rvb  * Revision 1.5.2.2  97/12/10  11:40:24  rvb
    137   1.1   rvb  * No more ody
    138   1.1   rvb  *
    139   1.1   rvb  * Revision 1.5.2.1  97/12/06  17:41:20  rvb
    140   1.1   rvb  * Sync with peters coda.h
    141   1.1   rvb  *
    142   1.1   rvb  * Revision 1.5  97/12/05  10:39:16  rvb
    143   1.1   rvb  * Read CHANGES
    144   1.1   rvb  *
    145   1.1   rvb  * Revision 1.4.18.9  97/12/05  08:58:07  rvb
    146   1.1   rvb  * peter found this one
    147   1.1   rvb  *
    148   1.1   rvb  * Revision 1.4.18.8  97/11/26  15:28:57  rvb
    149   1.1   rvb  * Cant make downcall pbuf == union cfs_downcalls yet
    150   1.1   rvb  *
    151   1.1   rvb  * Revision 1.4.18.7  97/11/25  09:40:49  rvb
    152   1.1   rvb  * Final cfs_venus.c w/o macros, but one locking bug
    153   1.1   rvb  *
    154   1.1   rvb  * Revision 1.4.18.6  97/11/20  11:46:41  rvb
    155   1.1   rvb  * Capture current cfs_venus
    156   1.1   rvb  *
    157   1.1   rvb  * Revision 1.4.18.5  97/11/18  10:27:15  rvb
    158   1.1   rvb  * cfs_nbsd.c is DEAD!!!; integrated into cfs_vf/vnops.c
    159   1.1   rvb  * cfs_nb_foo and cfs_foo are joined
    160   1.1   rvb  *
    161   1.1   rvb  * Revision 1.4.18.4  97/11/13  22:02:59  rvb
    162   1.1   rvb  * pass2 cfs_NetBSD.h mt
    163   1.1   rvb  *
    164   1.1   rvb  * Revision 1.4.18.3  97/11/12  12:09:38  rvb
    165   1.1   rvb  * reorg pass1
    166   1.1   rvb  *
    167   1.1   rvb  * Revision 1.4.18.2  97/10/29  16:06:09  rvb
    168   1.1   rvb  * Kill DYING
    169   1.1   rvb  *
    170   1.1   rvb  * Revision 1.4.18.1  1997/10/28 23:10:15  rvb
    171   1.1   rvb  * >64Meg; venus can be killed!
    172   1.1   rvb  *
    173   1.1   rvb  * Revision 1.4  1996/12/12 22:10:58  bnoble
    174   1.1   rvb  * Fixed the "downcall invokes venus operation" deadlock in all known cases.
    175   1.1   rvb  * There may be more
    176   1.1   rvb  *
    177   1.1   rvb  * Revision 1.3  1996/11/13 04:14:20  bnoble
    178   1.1   rvb  * Merging BNOBLE_WORK_6_20_96 into main line
    179   1.1   rvb  *
    180   1.1   rvb  * Revision 1.2.8.1  1996/08/22 14:25:04  bnoble
    181   1.1   rvb  * Added a return code from vc_nb_close
    182   1.1   rvb  *
    183   1.1   rvb  * Revision 1.2  1996/01/02 16:56:58  bnoble
    184   1.3   rvb  * Added support for Coda MiniCache and raw inode calls (final commit)
    185   1.1   rvb  *
    186   1.1   rvb  * Revision 1.1.2.1  1995/12/20 01:57:24  bnoble
    187   1.1   rvb  * Added CODA-specific files
    188   1.1   rvb  *
    189   1.1   rvb  * Revision 1.1  1995/03/14  20:52:15  bnoble
    190   1.1   rvb  * Initial revision
    191   1.1   rvb  *
    192   1.1   rvb  */
    193   1.3   rvb 
    194   1.1   rvb /* These routines are the device entry points for Venus. */
    195   1.5   rvb 
    196   1.5   rvb extern int coda_nc_initialized;    /* Set if cache has been initialized */
    197   1.5   rvb 
    198   1.3   rvb #ifdef	_LKM
    199   1.5   rvb #define	NVCODA 4
    200   1.5   rvb #else
    201   1.1   rvb #include <vcoda.h>
    202   1.1   rvb #endif
    203   1.1   rvb 
    204   1.1   rvb #include <sys/param.h>
    205   1.1   rvb #include <sys/systm.h>
    206   1.1   rvb #include <sys/kernel.h>
    207   1.1   rvb #include <sys/malloc.h>
    208   1.1   rvb #include <sys/proc.h>
    209   1.1   rvb #include <sys/mount.h>
    210   1.1   rvb #include <sys/file.h>
    211   1.1   rvb #include <sys/ioctl.h>
    212   1.4   rvb #include <sys/poll.h>
    213   1.4   rvb #include <sys/select.h>
    214   1.4   rvb 
    215   1.4   rvb #include <coda/coda.h>
    216   1.5   rvb #include <coda/cnode.h>
    217   1.1   rvb #include <coda/coda_namecache.h>
    218   1.2   rvb #include <coda/coda_io.h>
    219   1.2   rvb #include <coda/coda_psdev.h>
    220   1.3   rvb 
    221   1.8   rvb #define CTL_C
    222   1.8   rvb 
    223   1.8   rvb int coda_psdev_print_entry = 0;
    224   1.8   rvb static
    225   1.8   rvb int outstanding_upcalls = 0;
    226   1.8   rvb int coda_call_sleep = PZERO - 1;
    227   1.8   rvb #ifdef	CTL_C
    228   1.3   rvb int coda_pcatch = PCATCH;
    229   1.3   rvb #else
    230   1.1   rvb #endif
    231   1.3   rvb 
    232   1.1   rvb #define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__FUNCTION__))
    233   1.1   rvb 
    234   1.1   rvb void vcodaattach(int n);
    235   1.1   rvb 
    236   1.1   rvb struct vmsg {
    237   1.1   rvb     struct queue vm_chain;
    238   1.1   rvb     caddr_t	 vm_data;
    239   1.1   rvb     u_short	 vm_flags;
    240   1.1   rvb     u_short      vm_inSize;	/* Size is at most 5000 bytes */
    241   1.1   rvb     u_short	 vm_outSize;
    242   1.1   rvb     u_short	 vm_opcode; 	/* copied from data to save ptr lookup */
    243   1.1   rvb     int		 vm_unique;
    244   1.1   rvb     caddr_t	 vm_sleep;	/* Not used by Mach. */
    245   1.1   rvb };
    246   1.1   rvb 
    247   1.1   rvb #define	VM_READ	    1
    248   1.3   rvb #define	VM_WRITE    2
    249   1.1   rvb #define	VM_INTR	    4
    250   1.3   rvb 
    251   1.1   rvb /* vcodaattach: do nothing */
    252   1.1   rvb void
    253   1.1   rvb vcodaattach(n)
    254   1.1   rvb     int n;
    255   1.1   rvb {
    256   1.1   rvb }
    257   1.1   rvb 
    258   1.1   rvb /*
    259   1.1   rvb  * These functions are written for NetBSD.
    260   1.1   rvb  */
    261   1.1   rvb int
    262   1.1   rvb vc_nb_open(dev, flag, mode, p)
    263   1.1   rvb     dev_t        dev;
    264   1.1   rvb     int          flag;
    265   1.1   rvb     int          mode;
    266   1.1   rvb     struct proc *p;             /* NetBSD only */
    267   1.1   rvb {
    268   1.1   rvb     register struct vcomm *vcp;
    269   1.3   rvb 
    270   1.1   rvb     ENTRY;
    271   1.1   rvb 
    272   1.3   rvb     if (minor(dev) >= NVCODA || minor(dev) < 0)
    273   1.3   rvb 	return(ENXIO);
    274   1.1   rvb 
    275   1.3   rvb     if (!coda_nc_initialized)
    276   1.1   rvb 	coda_nc_init();
    277   1.1   rvb 
    278   1.1   rvb     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
    279   1.1   rvb     if (VC_OPEN(vcp))
    280   1.1   rvb 	return(EBUSY);
    281   1.1   rvb 
    282   1.1   rvb     bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
    283   1.1   rvb     INIT_QUEUE(vcp->vc_requests);
    284   1.3   rvb     INIT_QUEUE(vcp->vc_replys);
    285   1.3   rvb     MARK_VC_OPEN(vcp);
    286   1.1   rvb 
    287   1.1   rvb     coda_mnttbl[minor(dev)].mi_vfsp = NULL;
    288   1.1   rvb     coda_mnttbl[minor(dev)].mi_rootvp = NULL;
    289   1.1   rvb 
    290   1.1   rvb     return(0);
    291   1.1   rvb }
    292   1.1   rvb 
    293   1.1   rvb int
    294   1.1   rvb vc_nb_close (dev, flag, mode, p)
    295   1.1   rvb     dev_t        dev;
    296   1.1   rvb     int          flag;
    297   1.1   rvb     int          mode;
    298   1.9   rvb     struct proc *p;
    299   1.3   rvb {
    300   1.1   rvb     register struct vcomm *vcp;
    301   1.1   rvb     register struct vmsg *vmp, *nvmp = NULL;
    302   1.1   rvb     struct coda_mntinfo *mi;
    303   1.1   rvb     int                 err;
    304   1.3   rvb 
    305   1.1   rvb     ENTRY;
    306   1.1   rvb 
    307   1.3   rvb     if (minor(dev) >= NVCODA || minor(dev) < 0)
    308   1.1   rvb 	return(ENXIO);
    309   1.1   rvb 
    310   1.1   rvb     mi = &coda_mnttbl[minor(dev)];
    311   1.1   rvb     vcp = &(mi->mi_vcomm);
    312   1.1   rvb 
    313   1.1   rvb     if (!VC_OPEN(vcp))
    314   1.1   rvb 	panic("vcclose: not open");
    315   1.1   rvb 
    316   1.1   rvb     /* prevent future operations on this vfs from succeeding by auto-
    317   1.1   rvb      * unmounting any vfs mounted via this device. This frees user or
    318   1.1   rvb      * sysadm from having to remember where all mount points are located.
    319   1.8   rvb      * Put this before WAKEUPs to avoid queuing new messages between
    320   1.8   rvb      * the WAKEUP and the unmount (which can happen if we're unlucky)
    321   1.8   rvb      */
    322   1.8   rvb     if (!mi->mi_rootvp) {
    323   1.1   rvb 	/* just a simple open/close w no mount */
    324   1.8   rvb 	MARK_VC_CLOSED(vcp);
    325   1.8   rvb 	return 0;
    326   1.8   rvb     }
    327   1.8   rvb 
    328   1.8   rvb     /* Let unmount know this is for real */
    329   1.8   rvb     VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
    330   1.1   rvb     if (vfs_busy(mi->mi_vfsp, 0, 0))
    331   1.1   rvb 	return (EBUSY);
    332   1.1   rvb     coda_unmounting(mi->mi_vfsp);
    333   1.1   rvb 
    334  1.10   rvb     /* Wakeup clients so they can return. */
    335   1.1   rvb     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
    336   1.9   rvb 	 !EOQ(vmp, vcp->vc_requests);
    337   1.1   rvb 	 vmp = nvmp)
    338   1.1   rvb     {
    339   1.3   rvb     	nvmp = (struct vmsg *)GETNEXT(vmp->vm_chain);
    340   1.3   rvb 	/* Free signal request messages and don't wakeup cause
    341   1.3   rvb 	   no one is waiting. */
    342   1.1   rvb 	if (vmp->vm_opcode == CODA_SIGNAL) {
    343   1.1   rvb 	    CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
    344   1.8   rvb 	    CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
    345   1.1   rvb 	    continue;
    346   1.1   rvb 	}
    347   1.8   rvb 	outstanding_upcalls++;
    348   1.1   rvb 	wakeup(&vmp->vm_sleep);
    349   1.1   rvb     }
    350   1.1   rvb 
    351   1.1   rvb     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
    352   1.8   rvb 	 !EOQ(vmp, vcp->vc_replys);
    353   1.1   rvb 	 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
    354   1.1   rvb     {
    355   1.8   rvb 	outstanding_upcalls++;
    356   1.1   rvb 	wakeup(&vmp->vm_sleep);
    357   1.8   rvb     }
    358   1.8   rvb 
    359   1.8   rvb     MARK_VC_CLOSED(vcp);
    360   1.8   rvb 
    361   1.8   rvb     if (outstanding_upcalls) {
    362   1.8   rvb #ifdef	CODA_VERBOSE
    363   1.8   rvb 	printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
    364   1.8   rvb     	(void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
    365   1.8   rvb 	printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
    366   1.8   rvb #else
    367   1.8   rvb     	(void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
    368   1.8   rvb #endif
    369   1.8   rvb     }
    370   1.8   rvb 
    371   1.8   rvb     err = dounmount(mi->mi_vfsp, flag, p);
    372   1.1   rvb     if (err)
    373   1.1   rvb 	myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
    374   1.1   rvb 	           err, minor(dev)));
    375   1.1   rvb     return 0;
    376   1.1   rvb }
    377   1.1   rvb 
    378   1.1   rvb int
    379   1.1   rvb vc_nb_read(dev, uiop, flag)
    380   1.1   rvb     dev_t        dev;
    381   1.1   rvb     struct uio  *uiop;
    382   1.1   rvb     int          flag;
    383   1.1   rvb {
    384   1.1   rvb     register struct vcomm *	vcp;
    385   1.1   rvb     register struct vmsg *vmp;
    386   1.1   rvb     int error = 0;
    387   1.3   rvb 
    388   1.1   rvb     ENTRY;
    389   1.1   rvb 
    390   1.3   rvb     if (minor(dev) >= NVCODA || minor(dev) < 0)
    391   1.1   rvb 	return(ENXIO);
    392   1.1   rvb 
    393   1.1   rvb     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
    394   1.1   rvb     /* Get message at head of request queue. */
    395   1.1   rvb     if (EMPTY(vcp->vc_requests))
    396   1.1   rvb 	return(0);	/* Nothing to read */
    397   1.1   rvb 
    398   1.1   rvb     vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
    399   1.1   rvb 
    400   1.1   rvb     /* Move the input args into userspace */
    401   1.1   rvb     uiop->uio_rw = UIO_READ;
    402   1.1   rvb     error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
    403   1.1   rvb     if (error) {
    404   1.1   rvb 	myprintf(("vcread: error (%d) on uiomove\n", error));
    405   1.5   rvb 	error = EINVAL;
    406   1.1   rvb     }
    407   1.1   rvb 
    408   1.1   rvb #ifdef OLD_DIAGNOSTIC
    409   1.1   rvb     if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
    410   1.1   rvb 	panic("vc_nb_read: bad chain");
    411   1.1   rvb #endif
    412   1.1   rvb 
    413   1.1   rvb     REMQUE(vmp->vm_chain);
    414   1.3   rvb 
    415   1.3   rvb     /* If request was a signal, free up the message and don't
    416   1.1   rvb        enqueue it in the reply queue. */
    417   1.1   rvb     if (vmp->vm_opcode == CODA_SIGNAL) {
    418   1.3   rvb 	if (codadebug)
    419   1.3   rvb 	    myprintf(("vcread: signal msg (%d, %d)\n",
    420   1.1   rvb 		      vmp->vm_opcode, vmp->vm_unique));
    421   1.1   rvb 	CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
    422   1.1   rvb 	CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
    423   1.1   rvb 	return(error);
    424   1.1   rvb     }
    425   1.1   rvb 
    426   1.1   rvb     vmp->vm_flags |= VM_READ;
    427   1.1   rvb     INSQUE(vmp->vm_chain, vcp->vc_replys);
    428   1.1   rvb 
    429   1.1   rvb     return(error);
    430   1.1   rvb }
    431   1.1   rvb 
    432   1.1   rvb int
    433   1.1   rvb vc_nb_write(dev, uiop, flag)
    434   1.1   rvb     dev_t        dev;
    435   1.1   rvb     struct uio  *uiop;
    436   1.1   rvb     int          flag;
    437   1.3   rvb {
    438   1.1   rvb     register struct vcomm *	vcp;
    439   1.1   rvb     register struct vmsg *vmp;
    440   1.1   rvb     struct coda_out_hdr *out;
    441   1.1   rvb     u_long seq;
    442   1.1   rvb     u_long opcode;
    443   1.1   rvb     int buf[2];
    444   1.1   rvb     int error = 0;
    445   1.3   rvb 
    446   1.1   rvb     ENTRY;
    447   1.1   rvb 
    448   1.3   rvb     if (minor(dev) >= NVCODA || minor(dev) < 0)
    449   1.1   rvb 	return(ENXIO);
    450   1.1   rvb 
    451   1.1   rvb     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
    452   1.1   rvb 
    453   1.1   rvb     /* Peek at the opcode, unique without transfering the data. */
    454   1.1   rvb     uiop->uio_rw = UIO_WRITE;
    455   1.1   rvb     error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
    456   1.1   rvb     if (error) {
    457   1.1   rvb 	myprintf(("vcwrite: error (%d) on uiomove\n", error));
    458   1.1   rvb 	return(EINVAL);
    459   1.1   rvb     }
    460   1.1   rvb 
    461   1.3   rvb     opcode = buf[0];
    462   1.1   rvb     seq = buf[1];
    463   1.1   rvb 
    464   1.1   rvb     if (codadebug)
    465   1.1   rvb 	myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
    466   1.1   rvb 
    467   1.1   rvb     if (DOWNCALL(opcode)) {
    468   1.1   rvb 	union outputArgs pbuf;
    469   1.3   rvb 
    470   1.1   rvb 	/* get the rest of the data. */
    471   1.1   rvb 	uiop->uio_rw = UIO_WRITE;
    472   1.1   rvb 	error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
    473   1.1   rvb 	if (error) {
    474   1.1   rvb 	    myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
    475   1.1   rvb 		      error, opcode, seq));
    476   1.1   rvb 	    return(EINVAL);
    477   1.1   rvb 	    }
    478   1.1   rvb 
    479   1.1   rvb 	return handleDownCall(opcode, &pbuf);
    480   1.1   rvb     }
    481   1.1   rvb 
    482   1.1   rvb     /* Look for the message on the (waiting for) reply queue. */
    483   1.1   rvb     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
    484   1.1   rvb 	 !EOQ(vmp, vcp->vc_replys);
    485   1.1   rvb 	 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
    486   1.1   rvb     {
    487   1.1   rvb 	if (vmp->vm_unique == seq) break;
    488   1.3   rvb     }
    489   1.1   rvb 
    490   1.1   rvb     if (EOQ(vmp, vcp->vc_replys)) {
    491   1.1   rvb 	if (codadebug)
    492   1.1   rvb 	    myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
    493   1.1   rvb 
    494   1.1   rvb 	return(ESRCH);
    495   1.1   rvb 	}
    496   1.1   rvb 
    497   1.1   rvb     /* Remove the message from the reply queue */
    498   1.3   rvb     REMQUE(vmp->vm_chain);
    499   1.1   rvb 
    500   1.1   rvb     /* move data into response buffer. */
    501   1.1   rvb     out = (struct coda_out_hdr *)vmp->vm_data;
    502   1.1   rvb     /* Don't need to copy opcode and uniquifier. */
    503  1.11  matt 
    504  1.11  matt     /* get the rest of the data. */
    505   1.1   rvb     if (vmp->vm_outSize < uiop->uio_resid) {
    506   1.1   rvb 	myprintf(("vcwrite: more data than asked for (%d < %lu)\n",
    507   1.1   rvb 		  vmp->vm_outSize, (unsigned long) uiop->uio_resid));
    508   1.1   rvb 	wakeup(&vmp->vm_sleep); 	/* Notify caller of the error. */
    509   1.1   rvb 	return(EINVAL);
    510   1.1   rvb     }
    511   1.1   rvb 
    512   1.1   rvb     buf[0] = uiop->uio_resid; 	/* Save this value. */
    513   1.1   rvb     uiop->uio_rw = UIO_WRITE;
    514   1.1   rvb     error = uiomove((caddr_t) &out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
    515   1.1   rvb     if (error) {
    516   1.1   rvb 	myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
    517   1.1   rvb 		  error, opcode, seq));
    518   1.1   rvb 	return(EINVAL);
    519   1.1   rvb     }
    520   1.1   rvb 
    521   1.1   rvb     /* I don't think these are used, but just in case. */
    522   1.1   rvb     /* XXX - aren't these two already correct? -bnoble */
    523   1.1   rvb     out->opcode = opcode;
    524   1.1   rvb     out->unique = seq;
    525   1.1   rvb     vmp->vm_outSize	= buf[0];	/* Amount of data transferred? */
    526   1.1   rvb     vmp->vm_flags |= VM_WRITE;
    527   1.1   rvb     wakeup(&vmp->vm_sleep);
    528   1.1   rvb 
    529   1.1   rvb     return(0);
    530   1.1   rvb }
    531   1.1   rvb 
    532   1.5   rvb int
    533   1.1   rvb vc_nb_ioctl(dev, cmd, addr, flag, p)
    534   1.1   rvb     dev_t         dev;
    535   1.1   rvb     u_long        cmd;
    536   1.1   rvb     caddr_t       addr;
    537   1.1   rvb     int           flag;
    538   1.1   rvb     struct proc  *p;
    539   1.1   rvb {
    540   1.3   rvb     ENTRY;
    541   1.3   rvb 
    542   1.3   rvb     switch(cmd) {
    543   1.1   rvb     case CODARESIZE: {
    544   1.1   rvb 	struct coda_resize *data = (struct coda_resize *)addr;
    545   1.3   rvb 	return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
    546   1.3   rvb 	break;
    547   1.3   rvb     }
    548   1.1   rvb     case CODASTATS:
    549   1.1   rvb 	if (coda_nc_use) {
    550   1.1   rvb 	    coda_nc_gather_stats();
    551   1.1   rvb 	    return(0);
    552   1.1   rvb 	} else {
    553   1.3   rvb 	    return(ENODEV);
    554   1.3   rvb 	}
    555   1.3   rvb 	break;
    556   1.1   rvb     case CODAPRINT:
    557   1.1   rvb 	if (coda_nc_use) {
    558   1.1   rvb 	    print_coda_nc();
    559   1.1   rvb 	    return(0);
    560   1.1   rvb 	} else {
    561   1.9   rvb 	    return(ENODEV);
    562   1.9   rvb 	}
    563   1.9   rvb 	break;
    564   1.9   rvb     case CIOC_KERNEL_VERSION:
    565   1.9   rvb 	switch (*(u_int *)addr) {
    566   1.9   rvb 	case 0:
    567   1.9   rvb 		*(u_int *)addr = coda_kernel_version;
    568   1.9   rvb 		return 0;
    569   1.9   rvb 		break;
    570   1.9   rvb 	case 1:
    571   1.9   rvb 	case 2:
    572   1.9   rvb 		if (coda_kernel_version != *(u_int *)addr)
    573   1.9   rvb 		    return ENOENT;
    574   1.9   rvb 		else
    575   1.9   rvb 		    return 0;
    576   1.9   rvb 	default:
    577   1.1   rvb 		return ENOENT;
    578   1.1   rvb 	}
    579   1.1   rvb     	break;
    580   1.1   rvb     default :
    581   1.1   rvb 	return(EINVAL);
    582   1.1   rvb 	break;
    583   1.1   rvb     }
    584   1.1   rvb }
    585   1.1   rvb 
    586   1.1   rvb int
    587   1.1   rvb vc_nb_poll(dev, events, p)
    588   1.1   rvb     dev_t         dev;
    589   1.1   rvb     int           events;
    590   1.1   rvb     struct proc  *p;
    591   1.1   rvb {
    592   1.1   rvb     register struct vcomm *vcp;
    593   1.1   rvb     int event_msk = 0;
    594   1.3   rvb 
    595   1.1   rvb     ENTRY;
    596   1.1   rvb 
    597   1.3   rvb     if (minor(dev) >= NVCODA || minor(dev) < 0)
    598   1.1   rvb 	return(ENXIO);
    599   1.1   rvb 
    600   1.1   rvb     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
    601   1.1   rvb 
    602   1.1   rvb     event_msk = events & (POLLIN|POLLRDNORM);
    603   1.1   rvb     if (!event_msk)
    604   1.1   rvb 	return(0);
    605   1.1   rvb 
    606   1.1   rvb     if (!EMPTY(vcp->vc_requests))
    607   1.1   rvb 	return(events & (POLLIN|POLLRDNORM));
    608   1.1   rvb 
    609   1.1   rvb     selrecord(p, &(vcp->vc_selproc));
    610   1.1   rvb 
    611   1.1   rvb     return(0);
    612   1.1   rvb }
    613   1.1   rvb 
    614   1.3   rvb /*
    615   1.1   rvb  * Statistics
    616   1.1   rvb  */
    617   1.1   rvb struct coda_clstat coda_clstat;
    618   1.1   rvb 
    619   1.1   rvb /*
    620   1.1   rvb  * Key question: whether to sleep interuptably or uninteruptably when
    621   1.1   rvb  * waiting for Venus.  The former seems better (cause you can ^C a
    622   1.1   rvb  * job), but then GNU-EMACS completion breaks. Use tsleep with no
    623   1.1   rvb  * timeout, and no longjmp happens. But, when sleeping
    624   1.1   rvb  * "uninterruptibly", we don't get told if it returns abnormally
    625   1.1   rvb  * (e.g. kill -9).
    626   1.3   rvb  */
    627   1.3   rvb 
    628   1.1   rvb int
    629   1.1   rvb coda_call(mntinfo, inSize, outSize, buffer)
    630   1.1   rvb      struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
    631   1.1   rvb {
    632   1.1   rvb 	struct vcomm *vcp;
    633   1.1   rvb 	struct vmsg *vmp;
    634   1.4   rvb 	int error;
    635   1.1   rvb #ifdef	CTL_C
    636   1.4   rvb 	struct proc *p = curproc;
    637   1.1   rvb 	sigset_t psig_omask;
    638   1.1   rvb 	int i;
    639   1.1   rvb 	psig_omask = p->p_siglist;	/* array assignment */
    640   1.1   rvb #endif
    641   1.1   rvb 	if (mntinfo == NULL) {
    642   1.1   rvb 	    /* Unlikely, but could be a race condition with a dying warden */
    643   1.1   rvb 	    return ENODEV;
    644   1.1   rvb 	}
    645   1.3   rvb 
    646   1.3   rvb 	vcp = &(mntinfo->mi_vcomm);
    647   1.1   rvb 
    648   1.1   rvb 	coda_clstat.ncalls++;
    649   1.1   rvb 	coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
    650   1.1   rvb 
    651   1.3   rvb 	if (!VC_OPEN(vcp))
    652   1.1   rvb 	    return(ENODEV);
    653   1.1   rvb 
    654   1.1   rvb 	CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
    655   1.1   rvb 	/* Format the request message. */
    656   1.1   rvb 	vmp->vm_data = buffer;
    657   1.1   rvb 	vmp->vm_flags = 0;
    658   1.3   rvb 	vmp->vm_inSize = inSize;
    659   1.1   rvb 	vmp->vm_outSize
    660   1.3   rvb 	    = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
    661   1.1   rvb 	vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
    662   1.1   rvb 	vmp->vm_unique = ++vcp->vc_seq;
    663   1.1   rvb 	if (codadebug)
    664   1.1   rvb 	    myprintf(("Doing a call for %d.%d\n",
    665   1.3   rvb 		      vmp->vm_opcode, vmp->vm_unique));
    666   1.1   rvb 
    667   1.1   rvb 	/* Fill in the common input args. */
    668   1.1   rvb 	((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
    669   1.1   rvb 
    670   1.1   rvb 	/* Append msg to request queue and poke Venus. */
    671   1.1   rvb 	INSQUE(vmp->vm_chain, vcp->vc_requests);
    672   1.1   rvb 	selwakeup(&(vcp->vc_selproc));
    673   1.1   rvb 
    674   1.1   rvb 	/* We can be interrupted while we wait for Venus to process
    675   1.1   rvb 	 * our request.  If the interrupt occurs before Venus has read
    676   1.1   rvb 	 * the request, we dequeue and return. If it occurs after the
    677   1.1   rvb 	 * read but before the reply, we dequeue, send a signal
    678   1.1   rvb 	 * message, and return. If it occurs after the reply we ignore
    679   1.1   rvb 	 * it. In no case do we want to restart the syscall.  If it
    680   1.1   rvb 	 * was interrupted by a venus shutdown (vcclose), return
    681   1.1   rvb 	 * ENODEV.  */
    682   1.3   rvb 
    683   1.1   rvb 	/* Ignore return, We have to check anyway */
    684   1.1   rvb #ifdef	CTL_C
    685   1.1   rvb 	/* This is work in progress.  Setting coda_pcatch lets tsleep reawaken
    686   1.1   rvb 	   on a ^c or ^z.  The problem is that emacs sets certain interrupts
    687   1.1   rvb 	   as SA_RESTART.  This means that we should exit sleep handle the
    688   1.1   rvb 	   "signal" and then go to sleep again.  Mostly this is done by letting
    689   1.1   rvb 	   the syscall complete and be restarted.  We are not idempotent and
    690   1.1   rvb 	   can not do this.  A better solution is necessary.
    691   1.3   rvb 	 */
    692   1.1   rvb 	i = 0;
    693   1.1   rvb 	do {
    694   1.1   rvb 	    error = tsleep(&vmp->vm_sleep, (coda_call_sleep|coda_pcatch), "coda_call", hz*2);
    695   1.7   rvb 	    if (error == 0)
    696   1.3   rvb 	    	break;
    697   1.5   rvb 	    else if (error == EWOULDBLOCK) {
    698   1.4   rvb #ifdef	CODA_VERBOSE
    699   1.4   rvb 		    printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
    700   1.7   rvb #endif
    701   1.3   rvb     	    } else if (sigismember(&p->p_siglist, SIGIO)) {
    702   1.5   rvb 		    sigaddset(&p->p_sigmask, SIGIO);
    703   1.8   rvb #ifdef	CODA_VERBOSE
    704   1.8   rvb 		    printf("coda_call: tsleep returns %d SIGIO, cnt %d\n", error, i);
    705   1.8   rvb #endif
    706   1.8   rvb     	    } else if (sigismember(&p->p_siglist, SIGALRM)) {
    707   1.8   rvb 		    sigaddset(&p->p_sigmask, SIGALRM);
    708   1.1   rvb #ifdef	CODA_VERBOSE
    709   1.4   rvb 		    printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n", error, i);
    710   1.4   rvb #endif
    711   1.4   rvb 	    } else {
    712   1.4   rvb 		    sigset_t tmp;
    713   1.7   rvb 		    tmp = p->p_siglist;		/* array assignment */
    714   1.3   rvb 		    sigminusset(&p->p_sigmask, &tmp);
    715   1.4   rvb 
    716   1.4   rvb #ifdef	CODA_VERBOSE
    717   1.4   rvb 		    printf("coda_call: tsleep returns %d, cnt %d\n", error, i);
    718   1.4   rvb 		    printf("coda_call: siglist = %x.%x.%x.%x, sigmask = %x.%x.%x.%x, mask %x.%x.%x.%x\n",
    719   1.4   rvb 			    p->p_siglist.__bits[0], p->p_siglist.__bits[1],
    720   1.4   rvb 			    p->p_siglist.__bits[2], p->p_siglist.__bits[3],
    721   1.5   rvb 			    p->p_sigmask.__bits[0], p->p_sigmask.__bits[1],
    722   1.1   rvb 			    p->p_sigmask.__bits[2], p->p_sigmask.__bits[3],
    723   1.5   rvb 			    tmp.__bits[0], tmp.__bits[1], tmp.__bits[2], tmp.__bits[3]);
    724   1.4   rvb #endif
    725   1.4   rvb 		    break;
    726   1.4   rvb #ifdef	notyet
    727   1.4   rvb 		    sigminusset(&p->p_sigmask, &p->p_siglist);
    728   1.4   rvb 		    printf("coda_call: siglist = %x.%x.%x.%x, sigmask = %x.%x.%x.%x\n",
    729   1.4   rvb 			    p->p_siglist.__bits[0], p->p_siglist.__bits[1],
    730   1.5   rvb 			    p->p_siglist.__bits[2], p->p_siglist.__bits[3],
    731   1.1   rvb 			    p->p_sigmask.__bits[0], p->p_sigmask.__bits[1],
    732   1.8   rvb 			    p->p_sigmask.__bits[2], p->p_sigmask.__bits[3]);
    733   1.4   rvb #endif
    734   1.1   rvb 	    }
    735   1.3   rvb 	} while (error && i++ < 128 && VC_OPEN(vcp));
    736   1.1   rvb 	p->p_siglist = psig_omask;	/* array assignment */
    737   1.1   rvb #else
    738   1.1   rvb 	(void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
    739   1.1   rvb #endif
    740   1.1   rvb 	if (VC_OPEN(vcp)) {	/* Venus is still alive */
    741   1.1   rvb  	/* Op went through, interrupt or not... */
    742   1.1   rvb 	    if (vmp->vm_flags & VM_WRITE) {
    743   1.1   rvb 		error = 0;
    744   1.1   rvb 		*outSize = vmp->vm_outSize;
    745   1.1   rvb 	    }
    746   1.7   rvb 
    747   1.7   rvb 	    else if (!(vmp->vm_flags & VM_READ)) {
    748   1.7   rvb 		/* Interrupted before venus read it. */
    749   1.5   rvb #ifdef	CODA_VERBOSE
    750   1.5   rvb 		if (1)
    751   1.1   rvb #else
    752   1.1   rvb 		if (codadebug)
    753   1.1   rvb #endif
    754   1.1   rvb 		    myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
    755   1.1   rvb 			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
    756   1.1   rvb 		REMQUE(vmp->vm_chain);
    757   1.1   rvb 		error = EINTR;
    758   1.1   rvb 	    }
    759   1.1   rvb 
    760   1.1   rvb 	    else {
    761   1.3   rvb 		/* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
    762   1.1   rvb                    upcall started */
    763   1.1   rvb 		/* Interrupted after start of upcall, send venus a signal */
    764   1.7   rvb 		struct coda_in_hdr *dog;
    765   1.7   rvb 		struct vmsg *svmp;
    766   1.7   rvb 
    767   1.5   rvb #ifdef	CODA_VERBOSE
    768   1.5   rvb 		if (1)
    769   1.1   rvb #else
    770   1.1   rvb 		if (codadebug)
    771   1.1   rvb #endif
    772   1.1   rvb 		    myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
    773   1.1   rvb 			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
    774   1.1   rvb 
    775   1.3   rvb 		REMQUE(vmp->vm_chain);
    776   1.1   rvb 		error = EINTR;
    777   1.3   rvb 
    778   1.3   rvb 		CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
    779   1.1   rvb 
    780   1.1   rvb 		CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
    781   1.3   rvb 		dog = (struct coda_in_hdr *)svmp->vm_data;
    782   1.1   rvb 
    783   1.3   rvb 		svmp->vm_flags = 0;
    784   1.3   rvb 		dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
    785   1.1   rvb 		dog->unique = svmp->vm_unique = vmp->vm_unique;
    786   1.3   rvb 		svmp->vm_inSize = sizeof (struct coda_in_hdr);
    787   1.3   rvb /*??? rvb */	svmp->vm_outSize = sizeof (struct coda_in_hdr);
    788   1.1   rvb 
    789   1.1   rvb 		if (codadebug)
    790   1.1   rvb 		    myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
    791   1.1   rvb 			   svmp->vm_opcode, svmp->vm_unique));
    792   1.1   rvb 
    793   1.1   rvb 		/* insert at head of queue! */
    794   1.1   rvb 		INSQUE(svmp->vm_chain, vcp->vc_requests);
    795   1.1   rvb 		selwakeup(&(vcp->vc_selproc));
    796   1.1   rvb 	    }
    797   1.3   rvb 	}
    798   1.1   rvb 
    799   1.1   rvb 	else {	/* If venus died (!VC_OPEN(vcp)) */
    800   1.1   rvb 	    if (codadebug)
    801   1.1   rvb 		myprintf(("vcclose woke op %d.%d flags %d\n",
    802   1.1   rvb 		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
    803   1.1   rvb 
    804   1.3   rvb 		error = ENODEV;
    805   1.8   rvb 	}
    806   1.8   rvb 
    807   1.8   rvb 	CODA_FREE(vmp, sizeof(struct vmsg));
    808   1.1   rvb 
    809   1.1   rvb 	if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
    810   1.3   rvb 		wakeup(&outstanding_upcalls);
    811   1.1   rvb 
    812   1.1   rvb 	if (!error)
    813   1.4   rvb 		error = ((struct coda_out_hdr *)buffer)->result;
    814             	return(error);
    815             }
    816             
    817