xref: /netbsd-src/sys/coda/coda_vnops.c (revision 69b6d498973bb4d7230c2d3c12bd9a032738ec8e)
1 /*
2 coda_create/vn_open
3 remove/unlink
4 link
5 mkdir
6 rmdir
7 symlink
8 */
9 /*	$NetBSD: coda_vnops.c,v 1.41 2005/05/29 21:05:25 christos Exp $	*/
10 
11 /*
12  *
13  *             Coda: an Experimental Distributed File System
14  *                              Release 3.1
15  *
16  *           Copyright (c) 1987-1998 Carnegie Mellon University
17  *                          All Rights Reserved
18  *
19  * Permission  to  use, copy, modify and distribute this software and its
20  * documentation is hereby granted,  provided  that  both  the  copyright
21  * notice  and  this  permission  notice  appear  in  all  copies  of the
22  * software, derivative works or  modified  versions,  and  any  portions
23  * thereof, and that both notices appear in supporting documentation, and
24  * that credit is given to Carnegie Mellon University  in  all  documents
25  * and publicity pertaining to direct or indirect use of this code or its
26  * derivatives.
27  *
28  * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
29  * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
30  * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
31  * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
32  * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
33  * ANY DERIVATIVE WORK.
34  *
35  * Carnegie  Mellon  encourages  users  of  this  software  to return any
36  * improvements or extensions that  they  make,  and  to  grant  Carnegie
37  * Mellon the rights to redistribute these changes without encumbrance.
38  *
39  * 	@(#) coda/coda_vnops.c,v 1.1.1.1 1998/08/29 21:26:46 rvb Exp $
40  */
41 
42 /*
43  * Mach Operating System
44  * Copyright (c) 1990 Carnegie-Mellon University
45  * Copyright (c) 1989 Carnegie-Mellon University
46  * All rights reserved.  The CMU software License Agreement specifies
47  * the terms and conditions for use and redistribution.
48  */
49 
50 /*
51  * This code was written for the Coda file system at Carnegie Mellon
52  * University.  Contributers include David Steere, James Kistler, and
53  * M. Satyanarayanan.
54  */
55 
56 #include <sys/cdefs.h>
57 __KERNEL_RCSID(0, "$NetBSD: coda_vnops.c,v 1.41 2005/05/29 21:05:25 christos Exp $");
58 
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/malloc.h>
62 #include <sys/errno.h>
63 #include <sys/acct.h>
64 #include <sys/file.h>
65 #include <sys/uio.h>
66 #include <sys/namei.h>
67 #include <sys/ioctl.h>
68 #include <sys/mount.h>
69 #include <sys/proc.h>
70 #include <sys/select.h>
71 #include <sys/user.h>
72 #include <miscfs/genfs/genfs.h>
73 
74 #include <coda/coda.h>
75 #include <coda/cnode.h>
76 #include <coda/coda_vnops.h>
77 #include <coda/coda_venus.h>
78 #include <coda/coda_opstats.h>
79 #include <coda/coda_subr.h>
80 #include <coda/coda_namecache.h>
81 #include <coda/coda_pioctl.h>
82 
83 /*
84  * These flags select various performance enhancements.
85  */
86 int coda_attr_cache  = 1;       /* Set to cache attributes in the kernel */
87 int coda_symlink_cache = 1;     /* Set to cache symbolic link information */
88 int coda_access_cache = 1;      /* Set to handle some access checks directly */
89 
90 /* structure to keep track of vfs calls */
91 
92 struct coda_op_stats coda_vnodeopstats[CODA_VNODEOPS_SIZE];
93 
94 #define MARK_ENTRY(op) (coda_vnodeopstats[op].entries++)
95 #define MARK_INT_SAT(op) (coda_vnodeopstats[op].sat_intrn++)
96 #define MARK_INT_FAIL(op) (coda_vnodeopstats[op].unsat_intrn++)
97 #define MARK_INT_GEN(op) (coda_vnodeopstats[op].gen_intrn++)
98 
99 /* What we are delaying for in printf */
100 int coda_printf_delay = 0;  /* in microseconds */
101 int coda_vnop_print_entry = 0;
102 static int coda_lockdebug = 0;
103 
104 /* Definition of the vfs operation vector */
105 
106 /*
107  * Some NetBSD details:
108  *
109  *   coda_start is called at the end of the mount syscall.
110  *   coda_init is called at boot time.
111  */
112 
113 #define ENTRY if(coda_vnop_print_entry) myprintf(("Entered %s\n",__func__))
114 
115 /* Definition of the vnode operation vector */
116 
117 const struct vnodeopv_entry_desc coda_vnodeop_entries[] = {
118     { &vop_default_desc, coda_vop_error },
119     { &vop_lookup_desc, coda_lookup },		/* lookup */
120     { &vop_create_desc, coda_create },		/* create */
121     { &vop_mknod_desc, coda_vop_error },	/* mknod */
122     { &vop_open_desc, coda_open },		/* open */
123     { &vop_close_desc, coda_close },		/* close */
124     { &vop_access_desc, coda_access },		/* access */
125     { &vop_getattr_desc, coda_getattr },	/* getattr */
126     { &vop_setattr_desc, coda_setattr },	/* setattr */
127     { &vop_read_desc, coda_read },		/* read */
128     { &vop_write_desc, coda_write },		/* write */
129     { &vop_fcntl_desc, genfs_fcntl },		/* fcntl */
130     { &vop_ioctl_desc, coda_ioctl },		/* ioctl */
131 /* 1.3    { &vop_select_desc, coda_select },	select */
132     { &vop_mmap_desc, genfs_mmap },		/* mmap */
133     { &vop_fsync_desc, coda_fsync },		/* fsync */
134     { &vop_remove_desc, coda_remove },		/* remove */
135     { &vop_link_desc, coda_link },		/* link */
136     { &vop_rename_desc, coda_rename },		/* rename */
137     { &vop_mkdir_desc, coda_mkdir },		/* mkdir */
138     { &vop_rmdir_desc, coda_rmdir },		/* rmdir */
139     { &vop_symlink_desc, coda_symlink },	/* symlink */
140     { &vop_readdir_desc, coda_readdir },	/* readdir */
141     { &vop_readlink_desc, coda_readlink },	/* readlink */
142     { &vop_abortop_desc, coda_abortop },	/* abortop */
143     { &vop_inactive_desc, coda_inactive },	/* inactive */
144     { &vop_reclaim_desc, coda_reclaim },	/* reclaim */
145     { &vop_lock_desc, coda_lock },		/* lock */
146     { &vop_unlock_desc, coda_unlock },		/* unlock */
147     { &vop_bmap_desc, coda_bmap },		/* bmap */
148     { &vop_strategy_desc, coda_strategy },	/* strategy */
149     { &vop_print_desc, coda_vop_error },	/* print */
150     { &vop_islocked_desc, coda_islocked },	/* islocked */
151     { &vop_pathconf_desc, coda_vop_error },	/* pathconf */
152     { &vop_advlock_desc, coda_vop_nop },	/* advlock */
153     { &vop_bwrite_desc, coda_vop_error },	/* bwrite */
154     { &vop_lease_desc, coda_vop_nop },		/* lease */
155     { &vop_blkatoff_desc, coda_vop_error },	/* blkatoff */
156     { &vop_valloc_desc, coda_vop_error },	/* valloc */
157     { &vop_vfree_desc, coda_vop_error },	/* vfree */
158     { &vop_truncate_desc, coda_vop_error },	/* truncate */
159     { &vop_update_desc, coda_vop_error },	/* update */
160     { &vop_seek_desc, genfs_seek },		/* seek */
161     { &vop_poll_desc, genfs_poll },		/* poll */
162     { &vop_getpages_desc, coda_getpages },	/* getpages */
163     { &vop_putpages_desc, coda_putpages },	/* putpages */
164     { NULL, NULL }
165 };
166 
167 const struct vnodeopv_desc coda_vnodeop_opv_desc =
168         { &coda_vnodeop_p, coda_vnodeop_entries };
169 
170 /* Definitions of NetBSD vnodeop interfaces */
171 
172 /* A generic panic: we were called with something we didn't define yet */
173 int
174 coda_vop_error(void *anon) {
175     struct vnodeop_desc **desc = (struct vnodeop_desc **)anon;
176 
177     myprintf(("coda_vop_error: Vnode operation %s called, but not defined.\n",
178 	      (*desc)->vdesc_name));
179     /*
180     panic("coda_nbsd_vop_error");
181     return 0;
182     */
183     return EIO;
184 }
185 
186 /* A generic do-nothing.  For lease_check, advlock */
187 int
188 coda_vop_nop(void *anon) {
189     struct vnodeop_desc **desc = (struct vnodeop_desc **)anon;
190 
191     if (codadebug) {
192 	myprintf(("Vnode operation %s called, but unsupported\n",
193 		  (*desc)->vdesc_name));
194     }
195    return (0);
196 }
197 
198 int
199 coda_vnodeopstats_init(void)
200 {
201 	int i;
202 
203 	for(i=0;i<CODA_VNODEOPS_SIZE;i++) {
204 		coda_vnodeopstats[i].opcode = i;
205 		coda_vnodeopstats[i].entries = 0;
206 		coda_vnodeopstats[i].sat_intrn = 0;
207 		coda_vnodeopstats[i].unsat_intrn = 0;
208 		coda_vnodeopstats[i].gen_intrn = 0;
209 	}
210 
211 	return 0;
212 }
213 
214 /*
215  * coda_open calls Venus to return the device, inode pair of the cache
216  * file holding the data. Using iget, coda_open finds the vnode of the
217  * cache file, and then opens it.
218  */
219 int
220 coda_open(v)
221     void *v;
222 {
223     /*
224      * NetBSD can pass the O_EXCL flag in mode, even though the check
225      * has already happened.  Venus defensively assumes that if open
226      * is passed the EXCL, it must be a bug.  We strip the flag here.
227      */
228 /* true args */
229     struct vop_open_args *ap = v;
230     struct vnode **vpp = &(ap->a_vp);
231     struct cnode *cp = VTOC(*vpp);
232     int flag = ap->a_mode & (~O_EXCL);
233     struct ucred *cred = ap->a_cred;
234     struct proc *p = ap->a_p;
235 /* locals */
236     int error;
237     struct vnode *vp;
238     dev_t dev;
239     ino_t inode;
240 
241     MARK_ENTRY(CODA_OPEN_STATS);
242 
243     /* Check for open of control file. */
244     if (IS_CTL_VP(*vpp)) {
245 	/* XXX */
246 	/* if (WRITABLE(flag)) */
247 	if (flag & (FWRITE | O_TRUNC | O_CREAT | O_EXCL)) {
248 	    MARK_INT_FAIL(CODA_OPEN_STATS);
249 	    return(EACCES);
250 	}
251 	MARK_INT_SAT(CODA_OPEN_STATS);
252 	return(0);
253     }
254 
255     error = venus_open(vtomi((*vpp)), &cp->c_fid, flag, cred, p, &dev, &inode);
256     if (error)
257 	return (error);
258     if (!error) {
259 	CODADEBUG( CODA_OPEN,myprintf(("open: dev %d inode %d result %d\n",
260 				  dev, inode, error)); )
261     }
262 
263     /* Translate the <device, inode> pair for the cache file into
264        an inode pointer. */
265     error = coda_grab_vnode(dev, inode, &vp);
266     if (error)
267 	return (error);
268 
269     /* We get the vnode back locked in both Mach and NetBSD.  Needs unlocked */
270     VOP_UNLOCK(vp, 0);
271     /* Keep a reference until the close comes in. */
272     vref(*vpp);
273 
274     /* Save the vnode pointer for the cache file. */
275     if (cp->c_ovp == NULL) {
276 	cp->c_ovp = vp;
277     } else {
278 	if (cp->c_ovp != vp)
279 	    panic("coda_open:  cp->c_ovp != ITOV(ip)");
280     }
281     cp->c_ocount++;
282 
283     /* Flush the attribute cached if writing the file. */
284     if (flag & FWRITE) {
285 	cp->c_owrite++;
286 	cp->c_flags &= ~C_VATTR;
287     }
288 
289     /* Save the <device, inode> pair for the cache file to speed
290        up subsequent page_read's. */
291     cp->c_device = dev;
292     cp->c_inode = inode;
293 
294     /* Open the cache file. */
295     error = VOP_OPEN(vp, flag, cred, p);
296     return(error);
297 }
298 
299 /*
300  * Close the cache file used for I/O and notify Venus.
301  */
302 int
303 coda_close(v)
304     void *v;
305 {
306 /* true args */
307     struct vop_close_args *ap = v;
308     struct vnode *vp = ap->a_vp;
309     struct cnode *cp = VTOC(vp);
310     int flag = ap->a_fflag;
311     struct ucred *cred = ap->a_cred;
312     struct proc *p = ap->a_p;
313 /* locals */
314     int error;
315 
316     MARK_ENTRY(CODA_CLOSE_STATS);
317 
318     /* Check for close of control file. */
319     if (IS_CTL_VP(vp)) {
320 	MARK_INT_SAT(CODA_CLOSE_STATS);
321 	return(0);
322     }
323 
324     if (IS_UNMOUNTING(cp)) {
325 	if (cp->c_ovp) {
326 #ifdef	CODA_VERBOSE
327 	    printf("coda_close: destroying container ref %d, ufs vp %p of vp %p/cp %p\n",
328 		    vp->v_usecount, cp->c_ovp, vp, cp);
329 #endif
330 #ifdef	hmm
331 	    vgone(cp->c_ovp);
332 #else
333 	    vn_lock(cp->c_ovp, LK_EXCLUSIVE | LK_RETRY);
334 	    VOP_CLOSE(cp->c_ovp, flag, cred, p); /* Do errors matter here? */
335 	    vput(cp->c_ovp);
336 #endif
337 	} else {
338 #ifdef	CODA_VERBOSE
339 	    printf("coda_close: NO container vp %p/cp %p\n", vp, cp);
340 #endif
341 	}
342 	return ENODEV;
343     } else {
344 	vn_lock(cp->c_ovp, LK_EXCLUSIVE | LK_RETRY);
345 	VOP_CLOSE(cp->c_ovp, flag, cred, p); /* Do errors matter here? */
346 	vput(cp->c_ovp);
347     }
348 
349     if (--cp->c_ocount == 0)
350 	cp->c_ovp = NULL;
351 
352     if (flag & FWRITE)                    /* file was opened for write */
353 	--cp->c_owrite;
354 
355     error = venus_close(vtomi(vp), &cp->c_fid, flag, cred, p);
356     vrele(CTOV(cp));
357 
358     CODADEBUG(CODA_CLOSE, myprintf(("close: result %d\n",error)); )
359     return(error);
360 }
361 
362 int
363 coda_read(v)
364     void *v;
365 {
366     struct vop_read_args *ap = v;
367 
368     ENTRY;
369     return(coda_rdwr(ap->a_vp, ap->a_uio, UIO_READ,
370 		    ap->a_ioflag, ap->a_cred, ap->a_uio->uio_procp));
371 }
372 
373 int
374 coda_write(v)
375     void *v;
376 {
377     struct vop_write_args *ap = v;
378 
379     ENTRY;
380     return(coda_rdwr(ap->a_vp, ap->a_uio, UIO_WRITE,
381 		    ap->a_ioflag, ap->a_cred, ap->a_uio->uio_procp));
382 }
383 
384 int
385 coda_rdwr(vp, uiop, rw, ioflag, cred, p)
386     struct vnode *vp;
387     struct uio *uiop;
388     enum uio_rw rw;
389     int ioflag;
390     struct ucred *cred;
391     struct proc *p;
392 {
393 /* upcall decl */
394   /* NOTE: container file operation!!! */
395 /* locals */
396     struct cnode *cp = VTOC(vp);
397     struct vnode *cfvp = cp->c_ovp;
398     int opened_internally = 0;
399     int error = 0;
400 
401     MARK_ENTRY(CODA_RDWR_STATS);
402 
403     CODADEBUG(CODA_RDWR, myprintf(("coda_rdwr(%d, %p, %lu, %lld, %d)\n", rw,
404 			      uiop->uio_iov->iov_base,
405 			      (unsigned long) uiop->uio_resid,
406 			      (long long) uiop->uio_offset, uiop->uio_segflg)); )
407 
408     /* Check for rdwr of control object. */
409     if (IS_CTL_VP(vp)) {
410 	MARK_INT_FAIL(CODA_RDWR_STATS);
411 	return(EINVAL);
412     }
413 
414     /* Redirect the request to UFS. */
415 
416     /*
417      * If file is not already open this must be a page
418      * {read,write} request.  Iget the cache file's inode
419      * pointer if we still have its <device, inode> pair.
420      * Otherwise, we must do an internal open to derive the
421      * pair.
422      */
423     if (cfvp == NULL) {
424 	/*
425 	 * If we're dumping core, do the internal open. Otherwise
426 	 * venus won't have the correct size of the core when
427 	 * it's completely written.
428 	 */
429 	if (cp->c_inode != 0 && !(p && (p->p_acflag & ACORE))) {
430 	    error = coda_grab_vnode(cp->c_device, cp->c_inode, &cfvp);
431 	    if (error) {
432 		MARK_INT_FAIL(CODA_RDWR_STATS);
433 		return(error);
434 	    }
435 	    /*
436 	     * We get the vnode back locked in both Mach and
437 	     * NetBSD.  Needs unlocked
438 	     */
439 	    VOP_UNLOCK(cfvp, 0);
440 	}
441 	else {
442 	    opened_internally = 1;
443 	    MARK_INT_GEN(CODA_OPEN_STATS);
444 	    error = VOP_OPEN(vp, (rw == UIO_READ ? FREAD : FWRITE),
445 			     cred, p);
446 #ifdef	CODA_VERBOSE
447 printf("coda_rdwr: Internally Opening %p\n", vp);
448 #endif
449 	    if (error) {
450 		MARK_INT_FAIL(CODA_RDWR_STATS);
451 		return(error);
452 	    }
453 	    cfvp = cp->c_ovp;
454 	}
455     }
456 
457     /* Have UFS handle the call. */
458     CODADEBUG(CODA_RDWR, myprintf(("indirect rdwr: fid = %s, refcnt = %d\n",
459 			coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount)); )
460 
461     if (rw == UIO_READ) {
462 	error = VOP_READ(cfvp, uiop, ioflag, cred);
463     } else {
464 	error = VOP_WRITE(cfvp, uiop, ioflag, cred);
465     }
466 
467     if (error)
468 	MARK_INT_FAIL(CODA_RDWR_STATS);
469     else
470 	MARK_INT_SAT(CODA_RDWR_STATS);
471 
472     /* Do an internal close if necessary. */
473     if (opened_internally) {
474 	MARK_INT_GEN(CODA_CLOSE_STATS);
475 	(void)VOP_CLOSE(vp, (rw == UIO_READ ? FREAD : FWRITE), cred, p);
476     }
477 
478     /* Invalidate cached attributes if writing. */
479     if (rw == UIO_WRITE)
480 	cp->c_flags &= ~C_VATTR;
481     return(error);
482 }
483 
484 int
485 coda_ioctl(v)
486     void *v;
487 {
488 /* true args */
489     struct vop_ioctl_args *ap = v;
490     struct vnode *vp = ap->a_vp;
491     int com = ap->a_command;
492     caddr_t data = ap->a_data;
493     int flag = ap->a_fflag;
494     struct ucred *cred = ap->a_cred;
495     struct proc  *p = ap->a_p;
496 /* locals */
497     int error;
498     struct vnode *tvp;
499     struct nameidata ndp;
500     struct PioctlData *iap = (struct PioctlData *)data;
501 
502     MARK_ENTRY(CODA_IOCTL_STATS);
503 
504     CODADEBUG(CODA_IOCTL, myprintf(("in coda_ioctl on %s\n", iap->path));)
505 
506     /* Don't check for operation on a dying object, for ctlvp it
507        shouldn't matter */
508 
509     /* Must be control object to succeed. */
510     if (!IS_CTL_VP(vp)) {
511 	MARK_INT_FAIL(CODA_IOCTL_STATS);
512 	CODADEBUG(CODA_IOCTL, myprintf(("coda_ioctl error: vp != ctlvp"));)
513 	    return (EOPNOTSUPP);
514     }
515     /* Look up the pathname. */
516 
517     /* Should we use the name cache here? It would get it from
518        lookupname sooner or later anyway, right? */
519 
520     NDINIT(&ndp, LOOKUP, (iap->follow ? FOLLOW : NOFOLLOW), UIO_USERSPACE,
521 	iap->path, p);
522     error = namei(&ndp);
523     tvp = ndp.ni_vp;
524 
525     if (error) {
526 	MARK_INT_FAIL(CODA_IOCTL_STATS);
527 	CODADEBUG(CODA_IOCTL, myprintf(("coda_ioctl error: lookup returns %d\n",
528 				   error));)
529 	return(error);
530     }
531 
532     /*
533      * Make sure this is a coda style cnode, but it may be a
534      * different vfsp
535      */
536     /* XXX: this totally violates the comment about vtagtype in vnode.h */
537     if (tvp->v_tag != VT_CODA) {
538 	vrele(tvp);
539 	MARK_INT_FAIL(CODA_IOCTL_STATS);
540 	CODADEBUG(CODA_IOCTL,
541 		 myprintf(("coda_ioctl error: %s not a coda object\n",
542 			iap->path));)
543 	return(EINVAL);
544     }
545 
546     if (iap->vi.in_size > VC_MAXDATASIZE) {
547 	vrele(tvp);
548 	return(EINVAL);
549     }
550     error = venus_ioctl(vtomi(tvp), &((VTOC(tvp))->c_fid), com, flag, data, cred, p);
551 
552     if (error)
553 	MARK_INT_FAIL(CODA_IOCTL_STATS);
554     else
555 	CODADEBUG(CODA_IOCTL, myprintf(("Ioctl returns %d \n", error)); )
556 
557     vrele(tvp);
558     return(error);
559 }
560 
561 /*
562  * To reduce the cost of a user-level venus;we cache attributes in
563  * the kernel.  Each cnode has storage allocated for an attribute. If
564  * c_vattr is valid, return a reference to it. Otherwise, get the
565  * attributes from venus and store them in the cnode.  There is some
566  * question if this method is a security leak. But I think that in
567  * order to make this call, the user must have done a lookup and
568  * opened the file, and therefore should already have access.
569  */
570 int
571 coda_getattr(v)
572     void *v;
573 {
574 /* true args */
575     struct vop_getattr_args *ap = v;
576     struct vnode *vp = ap->a_vp;
577     struct cnode *cp = VTOC(vp);
578     struct vattr *vap = ap->a_vap;
579     struct ucred *cred = ap->a_cred;
580     struct proc *p = ap->a_p;
581 /* locals */
582     int error;
583 
584     MARK_ENTRY(CODA_GETATTR_STATS);
585 
586     /* Check for getattr of control object. */
587     if (IS_CTL_VP(vp)) {
588 	MARK_INT_FAIL(CODA_GETATTR_STATS);
589 	return(ENOENT);
590     }
591 
592     /* Check to see if the attributes have already been cached */
593     if (VALID_VATTR(cp)) {
594 	CODADEBUG(CODA_GETATTR, { myprintf(("attr cache hit: %s\n",
595 					coda_f2s(&cp->c_fid)));});
596 	CODADEBUG(CODA_GETATTR, if (!(codadebug & ~CODA_GETATTR))
597 		 print_vattr(&cp->c_vattr); );
598 
599 	*vap = cp->c_vattr;
600 	MARK_INT_SAT(CODA_GETATTR_STATS);
601 	return(0);
602     }
603 
604     error = venus_getattr(vtomi(vp), &cp->c_fid, cred, p, vap);
605 
606     if (!error) {
607 	CODADEBUG(CODA_GETATTR, myprintf(("getattr miss %s: result %d\n",
608 				     coda_f2s(&cp->c_fid), error)); )
609 
610 	CODADEBUG(CODA_GETATTR, if (!(codadebug & ~CODA_GETATTR))
611 		 print_vattr(vap);	);
612 
613 	/* If not open for write, store attributes in cnode */
614 	if ((cp->c_owrite == 0) && (coda_attr_cache)) {
615 	    cp->c_vattr = *vap;
616 	    cp->c_flags |= C_VATTR;
617 	}
618 
619     }
620     return(error);
621 }
622 
623 int
624 coda_setattr(v)
625     void *v;
626 {
627 /* true args */
628     struct vop_setattr_args *ap = v;
629     struct vnode *vp = ap->a_vp;
630     struct cnode *cp = VTOC(vp);
631     struct vattr *vap = ap->a_vap;
632     struct ucred *cred = ap->a_cred;
633     struct proc *p = ap->a_p;
634 /* locals */
635     int error;
636 
637     MARK_ENTRY(CODA_SETATTR_STATS);
638 
639     /* Check for setattr of control object. */
640     if (IS_CTL_VP(vp)) {
641 	MARK_INT_FAIL(CODA_SETATTR_STATS);
642 	return(ENOENT);
643     }
644 
645     if (codadebug & CODADBGMSK(CODA_SETATTR)) {
646 	print_vattr(vap);
647     }
648     error = venus_setattr(vtomi(vp), &cp->c_fid, vap, cred, p);
649 
650     if (!error)
651 	cp->c_flags &= ~C_VATTR;
652 
653     CODADEBUG(CODA_SETATTR,	myprintf(("setattr %d\n", error)); )
654     return(error);
655 }
656 
657 int
658 coda_access(v)
659     void *v;
660 {
661 /* true args */
662     struct vop_access_args *ap = v;
663     struct vnode *vp = ap->a_vp;
664     struct cnode *cp = VTOC(vp);
665     int mode = ap->a_mode;
666     struct ucred *cred = ap->a_cred;
667     struct proc *p = ap->a_p;
668 /* locals */
669     int error;
670 
671     MARK_ENTRY(CODA_ACCESS_STATS);
672 
673     /* Check for access of control object.  Only read access is
674        allowed on it. */
675     if (IS_CTL_VP(vp)) {
676 	/* bogus hack - all will be marked as successes */
677 	MARK_INT_SAT(CODA_ACCESS_STATS);
678 	return(((mode & VREAD) && !(mode & (VWRITE | VEXEC)))
679 	       ? 0 : EACCES);
680     }
681 
682     /*
683      * if the file is a directory, and we are checking exec (eg lookup)
684      * access, and the file is in the namecache, then the user must have
685      * lookup access to it.
686      */
687     if (coda_access_cache) {
688 	if ((vp->v_type == VDIR) && (mode & VEXEC)) {
689 	    if (coda_nc_lookup(cp, ".", 1, cred)) {
690 		MARK_INT_SAT(CODA_ACCESS_STATS);
691 		return(0);                     /* it was in the cache */
692 	    }
693 	}
694     }
695 
696     error = venus_access(vtomi(vp), &cp->c_fid, mode, cred, p);
697 
698     return(error);
699 }
700 
701 /*
702  * CODA abort op, called after namei() when a CREATE/DELETE isn't actually
703  * done. If a buffer has been saved in anticipation of a coda_create or
704  * a coda_remove, delete it.
705  */
706 /* ARGSUSED */
707 int
708 coda_abortop(v)
709     void *v;
710 {
711 /* true args */
712     struct vop_abortop_args /* {
713 	struct vnode *a_dvp;
714 	struct componentname *a_cnp;
715     } */ *ap = v;
716 /* upcall decl */
717 /* locals */
718 
719     if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
720 	PNBUF_PUT(ap->a_cnp->cn_pnbuf);
721     return (0);
722 }
723 
724 int
725 coda_readlink(v)
726     void *v;
727 {
728 /* true args */
729     struct vop_readlink_args *ap = v;
730     struct vnode *vp = ap->a_vp;
731     struct cnode *cp = VTOC(vp);
732     struct uio *uiop = ap->a_uio;
733     struct ucred *cred = ap->a_cred;
734     struct proc *p = ap->a_uio->uio_procp;
735 /* locals */
736     int error;
737     char *str;
738     int len;
739 
740     MARK_ENTRY(CODA_READLINK_STATS);
741 
742     /* Check for readlink of control object. */
743     if (IS_CTL_VP(vp)) {
744 	MARK_INT_FAIL(CODA_READLINK_STATS);
745 	return(ENOENT);
746     }
747 
748     if ((coda_symlink_cache) && (VALID_SYMLINK(cp))) { /* symlink was cached */
749 	uiop->uio_rw = UIO_READ;
750 	error = uiomove(cp->c_symlink, (int)cp->c_symlen, uiop);
751 	if (error)
752 	    MARK_INT_FAIL(CODA_READLINK_STATS);
753 	else
754 	    MARK_INT_SAT(CODA_READLINK_STATS);
755 	return(error);
756     }
757 
758     error = venus_readlink(vtomi(vp), &cp->c_fid, cred, p, &str, &len);
759 
760     if (!error) {
761 	uiop->uio_rw = UIO_READ;
762 	error = uiomove(str, len, uiop);
763 
764 	if (coda_symlink_cache) {
765 	    cp->c_symlink = str;
766 	    cp->c_symlen = len;
767 	    cp->c_flags |= C_SYMLINK;
768 	} else
769 	    CODA_FREE(str, len);
770     }
771 
772     CODADEBUG(CODA_READLINK, myprintf(("in readlink result %d\n",error));)
773     return(error);
774 }
775 
776 int
777 coda_fsync(v)
778     void *v;
779 {
780 /* true args */
781     struct vop_fsync_args *ap = v;
782     struct vnode *vp = ap->a_vp;
783     struct cnode *cp = VTOC(vp);
784     struct ucred *cred = ap->a_cred;
785     struct proc *p = ap->a_p;
786 /* locals */
787     struct vnode *convp = cp->c_ovp;
788     int error;
789 
790     MARK_ENTRY(CODA_FSYNC_STATS);
791 
792     /* Check for fsync on an unmounting object */
793     /* The NetBSD kernel, in it's infinite wisdom, can try to fsync
794      * after an unmount has been initiated.  This is a Bad Thing,
795      * which we have to avoid.  Not a legitimate failure for stats.
796      */
797     if (IS_UNMOUNTING(cp)) {
798 	return(ENODEV);
799     }
800 
801     /* Check for fsync of control object. */
802     if (IS_CTL_VP(vp)) {
803 	MARK_INT_SAT(CODA_FSYNC_STATS);
804 	return(0);
805     }
806 
807     if (convp)
808     	VOP_FSYNC(convp, cred, MNT_WAIT, 0, 0, p);
809 
810     /*
811      * We can expect fsync on any vnode at all if venus is pruging it.
812      * Venus can't very well answer the fsync request, now can it?
813      * Hopefully, it won't have to, because hopefully, venus preserves
814      * the (possibly untrue) invariant that it never purges an open
815      * vnode.  Hopefully.
816      */
817     if (cp->c_flags & C_PURGING) {
818 	return(0);
819     }
820 
821     error = venus_fsync(vtomi(vp), &cp->c_fid, cred, p);
822 
823     CODADEBUG(CODA_FSYNC, myprintf(("in fsync result %d\n",error)); );
824     return(error);
825 }
826 
827 int
828 coda_inactive(v)
829     void *v;
830 {
831     /* XXX - at the moment, inactive doesn't look at cred, and doesn't
832        have a proc pointer.  Oops. */
833 /* true args */
834     struct vop_inactive_args *ap = v;
835     struct vnode *vp = ap->a_vp;
836     struct cnode *cp = VTOC(vp);
837     struct ucred *cred __attribute__((unused)) = NULL;
838     struct proc *p __attribute__((unused)) = curproc;
839 /* upcall decl */
840 /* locals */
841 
842     /* We don't need to send inactive to venus - DCS */
843     MARK_ENTRY(CODA_INACTIVE_STATS);
844 
845     if (IS_CTL_VP(vp)) {
846 	MARK_INT_SAT(CODA_INACTIVE_STATS);
847 	return 0;
848     }
849 
850     CODADEBUG(CODA_INACTIVE, myprintf(("in inactive, %s, vfsp %p\n",
851 				  coda_f2s(&cp->c_fid), vp->v_mount));)
852 
853     /* If an array has been allocated to hold the symlink, deallocate it */
854     if ((coda_symlink_cache) && (VALID_SYMLINK(cp))) {
855 	if (cp->c_symlink == NULL)
856 	    panic("coda_inactive: null symlink pointer in cnode");
857 
858 	CODA_FREE(cp->c_symlink, cp->c_symlen);
859 	cp->c_flags &= ~C_SYMLINK;
860 	cp->c_symlen = 0;
861     }
862 
863     /* Remove it from the table so it can't be found. */
864     coda_unsave(cp);
865     if (vp->v_mount->mnt_data == NULL) {
866 	myprintf(("Help! vfsp->vfs_data was NULL, but vnode %p wasn't dying\n", vp));
867 	panic("badness in coda_inactive");
868     }
869 
870     if (IS_UNMOUNTING(cp)) {
871 #ifdef	DEBUG
872 	printf("coda_inactive: IS_UNMOUNTING use %d: vp %p, cp %p\n", vp->v_usecount, vp, cp);
873 	if (cp->c_ovp != NULL)
874 	    printf("coda_inactive: cp->ovp != NULL use %d: vp %p, cp %p\n",
875 	    	   vp->v_usecount, vp, cp);
876 #endif
877 	lockmgr(&vp->v_lock, LK_RELEASE, &vp->v_interlock);
878     } else {
879 #ifdef OLD_DIAGNOSTIC
880 	if (CTOV(cp)->v_usecount) {
881 	    panic("coda_inactive: nonzero reference count");
882 	}
883 	if (cp->c_ovp != NULL) {
884 	    panic("coda_inactive:  cp->ovp != NULL");
885 	}
886 #endif
887 	VOP_UNLOCK(vp, 0);
888 	vgone(vp);
889     }
890 
891     MARK_INT_SAT(CODA_INACTIVE_STATS);
892     return(0);
893 }
894 
895 /*
896  * Remote file system operations having to do with directory manipulation.
897  */
898 
899 /*
900  * It appears that in NetBSD, lookup is supposed to return the vnode locked
901  */
902 int
903 coda_lookup(v)
904     void *v;
905 {
906 /* true args */
907     struct vop_lookup_args *ap = v;
908     struct vnode *dvp = ap->a_dvp;
909     struct cnode *dcp = VTOC(dvp);
910     struct vnode **vpp = ap->a_vpp;
911     /*
912      * It looks as though ap->a_cnp->ni_cnd->cn_nameptr holds the rest
913      * of the string to xlate, and that we must try to get at least
914      * ap->a_cnp->ni_cnd->cn_namelen of those characters to macth.  I
915      * could be wrong.
916      */
917     struct componentname  *cnp = ap->a_cnp;
918     struct ucred *cred = cnp->cn_cred;
919     struct proc *p = cnp->cn_proc;
920 /* locals */
921     struct cnode *cp;
922     const char *nm = cnp->cn_nameptr;
923     int len = cnp->cn_namelen;
924     CodaFid VFid;
925     int	vtype;
926     int error = 0;
927 
928     cnp->cn_flags &= ~PDIRUNLOCK;
929 
930     MARK_ENTRY(CODA_LOOKUP_STATS);
931 
932     CODADEBUG(CODA_LOOKUP, myprintf(("lookup: %s in %s\n",
933 				   nm, coda_f2s(&dcp->c_fid))););
934 
935     /* Check for lookup of control object. */
936     if (IS_CTL_NAME(dvp, nm, len)) {
937 	*vpp = coda_ctlvp;
938 	vref(*vpp);
939 	MARK_INT_SAT(CODA_LOOKUP_STATS);
940 	goto exit;
941     }
942 
943     if (len+1 > CODA_MAXNAMLEN) {
944 	MARK_INT_FAIL(CODA_LOOKUP_STATS);
945 	CODADEBUG(CODA_LOOKUP, myprintf(("name too long: lookup, %s (%s)\n",
946 				    coda_f2s(&dcp->c_fid), nm)););
947 	*vpp = (struct vnode *)0;
948 	error = EINVAL;
949 	goto exit;
950     }
951     /* First try to look the file up in the cfs name cache */
952     /* lock the parent vnode? */
953     cp = coda_nc_lookup(dcp, nm, len, cred);
954     if (cp) {
955 	*vpp = CTOV(cp);
956 	vref(*vpp);
957 	CODADEBUG(CODA_LOOKUP,
958 		 myprintf(("lookup result %d vpp %p\n",error,*vpp));)
959     } else {
960 
961 	/* The name wasn't cached, so we need to contact Venus */
962 	error = venus_lookup(vtomi(dvp), &dcp->c_fid, nm, len, cred, p, &VFid, &vtype);
963 
964 	if (error) {
965 	    MARK_INT_FAIL(CODA_LOOKUP_STATS);
966 	    CODADEBUG(CODA_LOOKUP, myprintf(("lookup error on %s (%s)%d\n",
967 					coda_f2s(&dcp->c_fid), nm, error));)
968 	    *vpp = (struct vnode *)0;
969 	} else {
970 	    MARK_INT_SAT(CODA_LOOKUP_STATS);
971 	    CODADEBUG(CODA_LOOKUP,
972 		     myprintf(("lookup: %s type %o result %d\n",
973 			    coda_f2s(&VFid), vtype, error)); )
974 
975 	    cp = make_coda_node(&VFid, dvp->v_mount, vtype);
976 	    *vpp = CTOV(cp);
977 
978 	    /* enter the new vnode in the Name Cache only if the top bit isn't set */
979 	    /* And don't enter a new vnode for an invalid one! */
980 	    if (!(vtype & CODA_NOCACHE))
981 		coda_nc_enter(VTOC(dvp), nm, len, cred, VTOC(*vpp));
982 	}
983     }
984 
985  exit:
986     /*
987      * If we are creating, and this was the last name to be looked up,
988      * and the error was ENOENT, then there really shouldn't be an
989      * error and we can make the leaf NULL and return success.  Since
990      * this is supposed to work under Mach as well as NetBSD, we're
991      * leaving this fn wrapped.  We also must tell lookup/namei that
992      * we need to save the last component of the name.  (Create will
993      * have to free the name buffer later...lucky us...)
994      */
995     if (((cnp->cn_nameiop == CREATE) || (cnp->cn_nameiop == RENAME))
996 	&& (cnp->cn_flags & ISLASTCN)
997 	&& (error == ENOENT))
998     {
999 	error = EJUSTRETURN;
1000 	cnp->cn_flags |= SAVENAME;
1001 	*ap->a_vpp = NULL;
1002     }
1003 
1004     /*
1005      * If we are removing, and we are at the last element, and we
1006      * found it, then we need to keep the name around so that the
1007      * removal will go ahead as planned.  Unfortunately, this will
1008      * probably also lock the to-be-removed vnode, which may or may
1009      * not be a good idea.  I'll have to look at the bits of
1010      * coda_remove to make sure.  We'll only save the name if we did in
1011      * fact find the name, otherwise coda_remove won't have a chance
1012      * to free the pathname.
1013      */
1014     if ((cnp->cn_nameiop == DELETE)
1015 	&& (cnp->cn_flags & ISLASTCN)
1016 	&& !error)
1017     {
1018 	cnp->cn_flags |= SAVENAME;
1019     }
1020 
1021     /*
1022      * If the lookup went well, we need to (potentially?) unlock the
1023      * parent, and lock the child.  We are only responsible for
1024      * checking to see if the parent is supposed to be unlocked before
1025      * we return.  We must always lock the child (provided there is
1026      * one, and (the parent isn't locked or it isn't the same as the
1027      * parent.)  Simple, huh?  We can never leave the parent locked unless
1028      * we are ISLASTCN
1029      */
1030     if (!error || (error == EJUSTRETURN)) {
1031 	if (!(cnp->cn_flags & LOCKPARENT) || !(cnp->cn_flags & ISLASTCN)) {
1032 	    if ((error = VOP_UNLOCK(dvp, 0))) {
1033 		return error;
1034 	    }
1035 	    cnp->cn_flags |= PDIRUNLOCK;
1036 	    /*
1037 	     * The parent is unlocked.  As long as there is a child,
1038 	     * lock it without bothering to check anything else.
1039 	     */
1040 	    if (*ap->a_vpp) {
1041 		if ((error = vn_lock(*ap->a_vpp, LK_EXCLUSIVE))) {
1042 		    printf("coda_lookup: ");
1043 		    panic("unlocked parent but couldn't lock child");
1044 		}
1045 	    }
1046 	} else {
1047 	    /* The parent is locked, and may be the same as the child */
1048 	    if (*ap->a_vpp && (*ap->a_vpp != dvp)) {
1049 		/* Different, go ahead and lock it. */
1050 		if ((error = vn_lock(*ap->a_vpp, LK_EXCLUSIVE))) {
1051 		    printf("coda_lookup: ");
1052 		    panic("unlocked parent but couldn't lock child");
1053 		}
1054 	    }
1055 	}
1056     } else {
1057 	/* If the lookup failed, we need to ensure that the leaf is NULL */
1058 	/* Don't change any locking? */
1059 	*ap->a_vpp = NULL;
1060     }
1061     return(error);
1062 }
1063 
1064 /*ARGSUSED*/
1065 int
1066 coda_create(v)
1067     void *v;
1068 {
1069 /* true args */
1070     struct vop_create_args *ap = v;
1071     struct vnode *dvp = ap->a_dvp;
1072     struct cnode *dcp = VTOC(dvp);
1073     struct vattr *va = ap->a_vap;
1074     int exclusive = 1;
1075     int mode = ap->a_vap->va_mode;
1076     struct vnode **vpp = ap->a_vpp;
1077     struct componentname  *cnp = ap->a_cnp;
1078     struct ucred *cred = cnp->cn_cred;
1079     struct proc *p = cnp->cn_proc;
1080 /* locals */
1081     int error;
1082     struct cnode *cp;
1083     const char *nm = cnp->cn_nameptr;
1084     int len = cnp->cn_namelen;
1085     CodaFid VFid;
1086     struct vattr attr;
1087 
1088     MARK_ENTRY(CODA_CREATE_STATS);
1089 
1090     /* All creates are exclusive XXX */
1091     /* I'm assuming the 'mode' argument is the file mode bits XXX */
1092 
1093     /* Check for create of control object. */
1094     if (IS_CTL_NAME(dvp, nm, len)) {
1095 	*vpp = (struct vnode *)0;
1096 	MARK_INT_FAIL(CODA_CREATE_STATS);
1097 	return(EACCES);
1098     }
1099 
1100     error = venus_create(vtomi(dvp), &dcp->c_fid, nm, len, exclusive, mode, va, cred, p, &VFid, &attr);
1101 
1102     if (!error) {
1103 
1104 	/* If this is an exclusive create, panic if the file already exists. */
1105 	/* Venus should have detected the file and reported EEXIST. */
1106 
1107 	if ((exclusive == 1) &&
1108 	    (coda_find(&VFid) != NULL))
1109 	    panic("cnode existed for newly created file!");
1110 
1111 	cp = make_coda_node(&VFid, dvp->v_mount, attr.va_type);
1112 	*vpp = CTOV(cp);
1113 
1114 	/* Update va to reflect the new attributes. */
1115 	(*va) = attr;
1116 
1117 	/* Update the attribute cache and mark it as valid */
1118 	if (coda_attr_cache) {
1119 	    VTOC(*vpp)->c_vattr = attr;
1120 	    VTOC(*vpp)->c_flags |= C_VATTR;
1121 	}
1122 
1123 	/* Invalidate the parent's attr cache, the modification time has changed */
1124 	VTOC(dvp)->c_flags &= ~C_VATTR;
1125 
1126 	/* enter the new vnode in the Name Cache */
1127 	coda_nc_enter(VTOC(dvp), nm, len, cred, VTOC(*vpp));
1128 
1129 	CODADEBUG(CODA_CREATE,
1130 		 myprintf(("create: %s, result %d\n",
1131 			coda_f2s(&VFid), error)); )
1132     } else {
1133 	*vpp = (struct vnode *)0;
1134 	CODADEBUG(CODA_CREATE, myprintf(("create error %d\n", error));)
1135     }
1136 
1137     /* Locking strategy. */
1138     /*
1139      * In NetBSD, all creates must explicitly vput their dvp's.  We'll
1140      * go ahead and use the LOCKLEAF flag of the cnp argument.
1141      * However, I'm pretty sure that create must return the leaf
1142      * locked; so there is a DIAGNOSTIC check to ensure that this is
1143      * true.
1144      */
1145     vput(dvp);
1146     if (!error) {
1147 	if (cnp->cn_flags & LOCKLEAF) {
1148 	    if ((error = vn_lock(*ap->a_vpp, LK_EXCLUSIVE))) {
1149 		printf("coda_create: ");
1150 		panic("unlocked parent but couldn't lock child");
1151 	    }
1152 	}
1153 #ifdef OLD_DIAGNOSTIC
1154 	else {
1155 	    printf("coda_create: LOCKLEAF not set!\n");
1156 	}
1157 #endif
1158     }
1159     /* Have to free the previously saved name */
1160     /*
1161      * This condition is stolen from ufs_makeinode.  I have no idea
1162      * why it's here, but what the hey...
1163      */
1164     if ((cnp->cn_flags & SAVESTART) == 0) {
1165 	PNBUF_PUT(cnp->cn_pnbuf);
1166     }
1167     return(error);
1168 }
1169 
1170 int
1171 coda_remove(v)
1172     void *v;
1173 {
1174 /* true args */
1175     struct vop_remove_args *ap = v;
1176     struct vnode *dvp = ap->a_dvp;
1177     struct cnode *cp = VTOC(dvp);
1178     struct componentname  *cnp = ap->a_cnp;
1179     struct ucred *cred = cnp->cn_cred;
1180     struct proc *p = cnp->cn_proc;
1181 /* locals */
1182     int error;
1183     const char *nm = cnp->cn_nameptr;
1184     int len = cnp->cn_namelen;
1185     struct cnode *tp;
1186 
1187     MARK_ENTRY(CODA_REMOVE_STATS);
1188 
1189     CODADEBUG(CODA_REMOVE, myprintf(("remove: %s in %s\n",
1190 				   nm, coda_f2s(&cp->c_fid))););
1191 
1192     /* Remove the file's entry from the CODA Name Cache */
1193     /* We're being conservative here, it might be that this person
1194      * doesn't really have sufficient access to delete the file
1195      * but we feel zapping the entry won't really hurt anyone -- dcs
1196      */
1197     /* I'm gonna go out on a limb here. If a file and a hardlink to it
1198      * exist, and one is removed, the link count on the other will be
1199      * off by 1. We could either invalidate the attrs if cached, or
1200      * fix them. I'll try to fix them. DCS 11/8/94
1201      */
1202     tp = coda_nc_lookup(VTOC(dvp), nm, len, cred);
1203     if (tp) {
1204 	if (VALID_VATTR(tp)) {	/* If attrs are cached */
1205 	    if (tp->c_vattr.va_nlink > 1) {	/* If it's a hard link */
1206 		tp->c_vattr.va_nlink--;
1207 	    }
1208 	}
1209 
1210 	coda_nc_zapfile(VTOC(dvp), nm, len);
1211 	/* No need to flush it if it doesn't exist! */
1212     }
1213     /* Invalidate the parent's attr cache, the modification time has changed */
1214     VTOC(dvp)->c_flags &= ~C_VATTR;
1215 
1216     /* Check for remove of control object. */
1217     if (IS_CTL_NAME(dvp, nm, len)) {
1218 	MARK_INT_FAIL(CODA_REMOVE_STATS);
1219 	return(ENOENT);
1220     }
1221 
1222     error = venus_remove(vtomi(dvp), &cp->c_fid, nm, len, cred, p);
1223 
1224     CODADEBUG(CODA_REMOVE, myprintf(("in remove result %d\n",error)); )
1225 
1226     /*
1227      * Regardless of what happens, we have to unconditionally drop
1228      * locks/refs on parent and child.  (I hope).  This is based on
1229      * what ufs_remove seems to be doing.
1230      */
1231     if (dvp == ap->a_vp) {
1232 	vrele(ap->a_vp);
1233     } else {
1234 	vput(ap->a_vp);
1235     }
1236     vput(dvp);
1237 
1238     if ((cnp->cn_flags & SAVESTART) == 0) {
1239 	PNBUF_PUT(cnp->cn_pnbuf);
1240     }
1241     return(error);
1242 }
1243 
1244 int
1245 coda_link(v)
1246     void *v;
1247 {
1248 /* true args */
1249     struct vop_link_args *ap = v;
1250     struct vnode *vp = ap->a_vp;
1251     struct cnode *cp = VTOC(vp);
1252     struct vnode *tdvp = ap->a_dvp;
1253     struct cnode *tdcp = VTOC(tdvp);
1254     struct componentname *cnp = ap->a_cnp;
1255     struct ucred *cred = cnp->cn_cred;
1256     struct proc *p = cnp->cn_proc;
1257 /* locals */
1258     int error;
1259     const char *nm = cnp->cn_nameptr;
1260     int len = cnp->cn_namelen;
1261 
1262     MARK_ENTRY(CODA_LINK_STATS);
1263 
1264     if (codadebug & CODADBGMSK(CODA_LINK)) {
1265 
1266 	myprintf(("nb_link:   vp fid: %s\n",
1267 		  coda_f2s(&cp->c_fid)));
1268 	myprintf(("nb_link: tdvp fid: %s)\n",
1269 		  coda_f2s(&tdcp->c_fid)));
1270 
1271     }
1272     if (codadebug & CODADBGMSK(CODA_LINK)) {
1273 	myprintf(("link:   vp fid: %s\n",
1274 		  coda_f2s(&cp->c_fid)));
1275 	myprintf(("link: tdvp fid: %s\n",
1276 		  coda_f2s(&tdcp->c_fid)));
1277 
1278     }
1279 
1280     /* Check for link to/from control object. */
1281     if (IS_CTL_NAME(tdvp, nm, len) || IS_CTL_VP(vp)) {
1282 	MARK_INT_FAIL(CODA_LINK_STATS);
1283 	return(EACCES);
1284     }
1285 
1286     /*
1287      * According to the ufs_link operation here's the locking situation:
1288      *     We enter with the thing called "dvp" (the directory) locked.
1289      *     We must unconditionally drop locks on "dvp"
1290      *
1291      *     We enter with the thing called "vp" (the linked-to) unlocked,
1292      *       but ref'd (?)
1293      *     We seem to need to lock it before calling coda_link, and
1294      *       unconditionally unlock it after.
1295      */
1296 
1297     if ((ap->a_vp != tdvp) && (error = vn_lock(ap->a_vp, LK_EXCLUSIVE))) {
1298 	goto exit;
1299     }
1300 
1301     error = venus_link(vtomi(vp), &cp->c_fid, &tdcp->c_fid, nm, len, cred, p);
1302 
1303     /* Invalidate the parent's attr cache, the modification time has changed */
1304     VTOC(tdvp)->c_flags &= ~C_VATTR;
1305     VTOC(vp)->c_flags &= ~C_VATTR;
1306 
1307     CODADEBUG(CODA_LINK,	myprintf(("in link result %d\n",error)); )
1308 
1309 exit:
1310 
1311     if (ap->a_vp != tdvp) {
1312 	VOP_UNLOCK(ap->a_vp, 0);
1313     }
1314     vput(tdvp);
1315 
1316     /* Drop the name buffer if we don't need to SAVESTART */
1317     if ((cnp->cn_flags & SAVESTART) == 0) {
1318 	PNBUF_PUT(cnp->cn_pnbuf);
1319     }
1320     return(error);
1321 }
1322 
1323 int
1324 coda_rename(v)
1325     void *v;
1326 {
1327 /* true args */
1328     struct vop_rename_args *ap = v;
1329     struct vnode *odvp = ap->a_fdvp;
1330     struct cnode *odcp = VTOC(odvp);
1331     struct componentname  *fcnp = ap->a_fcnp;
1332     struct vnode *ndvp = ap->a_tdvp;
1333     struct cnode *ndcp = VTOC(ndvp);
1334     struct componentname  *tcnp = ap->a_tcnp;
1335     struct ucred *cred = fcnp->cn_cred;
1336     struct proc *p = fcnp->cn_proc;
1337 /* true args */
1338     int error;
1339     const char *fnm = fcnp->cn_nameptr;
1340     int flen = fcnp->cn_namelen;
1341     const char *tnm = tcnp->cn_nameptr;
1342     int tlen = tcnp->cn_namelen;
1343 
1344     MARK_ENTRY(CODA_RENAME_STATS);
1345 
1346     /* Hmmm.  The vnodes are already looked up.  Perhaps they are locked?
1347        This could be Bad. XXX */
1348 #ifdef OLD_DIAGNOSTIC
1349     if ((fcnp->cn_cred != tcnp->cn_cred)
1350 	|| (fcnp->cn_proc != tcnp->cn_proc))
1351     {
1352 	panic("coda_rename: component names don't agree");
1353     }
1354 #endif
1355 
1356     /* Check for rename involving control object. */
1357     if (IS_CTL_NAME(odvp, fnm, flen) || IS_CTL_NAME(ndvp, tnm, tlen)) {
1358 	MARK_INT_FAIL(CODA_RENAME_STATS);
1359 	return(EACCES);
1360     }
1361 
1362     /* Problem with moving directories -- need to flush entry for .. */
1363     if (odvp != ndvp) {
1364 	struct cnode *ovcp = coda_nc_lookup(VTOC(odvp), fnm, flen, cred);
1365 	if (ovcp) {
1366 	    struct vnode *ovp = CTOV(ovcp);
1367 	    if ((ovp) &&
1368 		(ovp->v_type == VDIR)) /* If it's a directory */
1369 		coda_nc_zapfile(VTOC(ovp),"..", 2);
1370 	}
1371     }
1372 
1373     /* Remove the entries for both source and target files */
1374     coda_nc_zapfile(VTOC(odvp), fnm, flen);
1375     coda_nc_zapfile(VTOC(ndvp), tnm, tlen);
1376 
1377     /* Invalidate the parent's attr cache, the modification time has changed */
1378     VTOC(odvp)->c_flags &= ~C_VATTR;
1379     VTOC(ndvp)->c_flags &= ~C_VATTR;
1380 
1381     if (flen+1 > CODA_MAXNAMLEN) {
1382 	MARK_INT_FAIL(CODA_RENAME_STATS);
1383 	error = EINVAL;
1384 	goto exit;
1385     }
1386 
1387     if (tlen+1 > CODA_MAXNAMLEN) {
1388 	MARK_INT_FAIL(CODA_RENAME_STATS);
1389 	error = EINVAL;
1390 	goto exit;
1391     }
1392 
1393     error = venus_rename(vtomi(odvp), &odcp->c_fid, &ndcp->c_fid, fnm, flen, tnm, tlen, cred, p);
1394 
1395  exit:
1396     CODADEBUG(CODA_RENAME, myprintf(("in rename result %d\n",error));)
1397     /* XXX - do we need to call cache pureg on the moved vnode? */
1398     cache_purge(ap->a_fvp);
1399 
1400     /* It seems to be incumbent on us to drop locks on all four vnodes */
1401     /* From-vnodes are not locked, only ref'd.  To-vnodes are locked. */
1402 
1403     vrele(ap->a_fvp);
1404     vrele(odvp);
1405 
1406     if (ap->a_tvp) {
1407 	if (ap->a_tvp == ndvp) {
1408 	    vrele(ap->a_tvp);
1409 	} else {
1410 	    vput(ap->a_tvp);
1411 	}
1412     }
1413 
1414     vput(ndvp);
1415     return(error);
1416 }
1417 
1418 int
1419 coda_mkdir(v)
1420     void *v;
1421 {
1422 /* true args */
1423     struct vop_mkdir_args *ap = v;
1424     struct vnode *dvp = ap->a_dvp;
1425     struct cnode *dcp = VTOC(dvp);
1426     struct componentname  *cnp = ap->a_cnp;
1427     struct vattr *va = ap->a_vap;
1428     struct vnode **vpp = ap->a_vpp;
1429     struct ucred *cred = cnp->cn_cred;
1430     struct proc *p = cnp->cn_proc;
1431 /* locals */
1432     int error;
1433     const char *nm = cnp->cn_nameptr;
1434     int len = cnp->cn_namelen;
1435     struct cnode *cp;
1436     CodaFid VFid;
1437     struct vattr ova;
1438 
1439     MARK_ENTRY(CODA_MKDIR_STATS);
1440 
1441     /* Check for mkdir of target object. */
1442     if (IS_CTL_NAME(dvp, nm, len)) {
1443 	*vpp = (struct vnode *)0;
1444 	MARK_INT_FAIL(CODA_MKDIR_STATS);
1445 	return(EACCES);
1446     }
1447 
1448     if (len+1 > CODA_MAXNAMLEN) {
1449 	*vpp = (struct vnode *)0;
1450 	MARK_INT_FAIL(CODA_MKDIR_STATS);
1451 	return(EACCES);
1452     }
1453 
1454     error = venus_mkdir(vtomi(dvp), &dcp->c_fid, nm, len, va, cred, p, &VFid, &ova);
1455 
1456     if (!error) {
1457 	if (coda_find(&VFid) != NULL)
1458 	    panic("cnode existed for newly created directory!");
1459 
1460 
1461 	cp =  make_coda_node(&VFid, dvp->v_mount, va->va_type);
1462 	*vpp = CTOV(cp);
1463 
1464 	/* enter the new vnode in the Name Cache */
1465 	coda_nc_enter(VTOC(dvp), nm, len, cred, VTOC(*vpp));
1466 
1467 	/* as a side effect, enter "." and ".." for the directory */
1468 	coda_nc_enter(VTOC(*vpp), ".", 1, cred, VTOC(*vpp));
1469 	coda_nc_enter(VTOC(*vpp), "..", 2, cred, VTOC(dvp));
1470 
1471 	if (coda_attr_cache) {
1472 	    VTOC(*vpp)->c_vattr = ova;		/* update the attr cache */
1473 	    VTOC(*vpp)->c_flags |= C_VATTR;	/* Valid attributes in cnode */
1474 	}
1475 
1476 	/* Invalidate the parent's attr cache, the modification time has changed */
1477 	VTOC(dvp)->c_flags &= ~C_VATTR;
1478 
1479 	CODADEBUG( CODA_MKDIR, myprintf(("mkdir: %s result %d\n",
1480 				    coda_f2s(&VFid), error)); )
1481     } else {
1482 	*vpp = (struct vnode *)0;
1483 	CODADEBUG(CODA_MKDIR, myprintf(("mkdir error %d\n",error));)
1484     }
1485 
1486     /*
1487      * Currently, all mkdirs explicitly vput their dvp's.
1488      * It also appears that we *must* lock the vpp, since
1489      * lockleaf isn't set, but someone down the road is going
1490      * to try to unlock the new directory.
1491      */
1492     vput(dvp);
1493     if (!error) {
1494 	if ((error = vn_lock(*ap->a_vpp, LK_EXCLUSIVE))) {
1495 	    panic("coda_mkdir: couldn't lock child");
1496 	}
1497     }
1498 
1499     /* Have to free the previously saved name */
1500     /*
1501      * ufs_mkdir doesn't check for SAVESTART before freeing the
1502      * pathname buffer, but ufs_create does.  For the moment, I'll
1503      * follow their lead, but this seems like it is probably
1504      * incorrect.
1505      */
1506     PNBUF_PUT(cnp->cn_pnbuf);
1507     return(error);
1508 }
1509 
1510 int
1511 coda_rmdir(v)
1512     void *v;
1513 {
1514 /* true args */
1515     struct vop_rmdir_args *ap = v;
1516     struct vnode *dvp = ap->a_dvp;
1517     struct cnode *dcp = VTOC(dvp);
1518     struct componentname  *cnp = ap->a_cnp;
1519     struct ucred *cred = cnp->cn_cred;
1520     struct proc *p = cnp->cn_proc;
1521 /* true args */
1522     int error;
1523     const char *nm = cnp->cn_nameptr;
1524     int len = cnp->cn_namelen;
1525     struct cnode *cp;
1526 
1527     MARK_ENTRY(CODA_RMDIR_STATS);
1528 
1529     /* Check for rmdir of control object. */
1530     if (IS_CTL_NAME(dvp, nm, len)) {
1531 	MARK_INT_FAIL(CODA_RMDIR_STATS);
1532 	return(ENOENT);
1533     }
1534 
1535     /* We're being conservative here, it might be that this person
1536      * doesn't really have sufficient access to delete the file
1537      * but we feel zapping the entry won't really hurt anyone -- dcs
1538      */
1539     /*
1540      * As a side effect of the rmdir, remove any entries for children of
1541      * the directory, especially "." and "..".
1542      */
1543     cp = coda_nc_lookup(dcp, nm, len, cred);
1544     if (cp) coda_nc_zapParentfid(&(cp->c_fid), NOT_DOWNCALL);
1545 
1546     /* Remove the file's entry from the CODA Name Cache */
1547     coda_nc_zapfile(dcp, nm, len);
1548 
1549     /* Invalidate the parent's attr cache, the modification time has changed */
1550     dcp->c_flags &= ~C_VATTR;
1551 
1552     error = venus_rmdir(vtomi(dvp), &dcp->c_fid, nm, len, cred, p);
1553 
1554     CODADEBUG(CODA_RMDIR, myprintf(("in rmdir result %d\n", error)); )
1555 
1556     /*
1557      * regardless of what happens, we need to drop locks/refs on the
1558      * parent and child.  I think.
1559      */
1560     if (dvp == ap->a_vp) {
1561 	vrele(ap->a_vp);
1562     } else {
1563 	vput(ap->a_vp);
1564     }
1565     vput(dvp);
1566 
1567     if ((cnp->cn_flags & SAVESTART) == 0) {
1568 	PNBUF_PUT(cnp->cn_pnbuf);
1569     }
1570     return(error);
1571 }
1572 
1573 int
1574 coda_symlink(v)
1575     void *v;
1576 {
1577 /* true args */
1578     struct vop_symlink_args *ap = v;
1579     struct vnode *tdvp = ap->a_dvp;
1580     struct cnode *tdcp = VTOC(tdvp);
1581     struct componentname *cnp = ap->a_cnp;
1582     struct vattr *tva = ap->a_vap;
1583     char *path = ap->a_target;
1584     struct ucred *cred = cnp->cn_cred;
1585     struct proc *p = cnp->cn_proc;
1586 /* locals */
1587     int error;
1588     u_long saved_cn_flags;
1589     /*
1590      * XXX I'm assuming the following things about coda_symlink's
1591      * arguments:
1592      *       t(foo) is the new name/parent/etc being created.
1593      *       lname is the contents of the new symlink.
1594      */
1595     const char *nm = cnp->cn_nameptr;
1596     int len = cnp->cn_namelen;
1597     int plen = strlen(path);
1598 
1599     /* XXX What about the vpp argument?  Do we need it? */
1600     /*
1601      * Here's the strategy for the moment: perform the symlink, then
1602      * do a lookup to grab the resulting vnode.  I know this requires
1603      * two communications with Venus for a new sybolic link, but
1604      * that's the way the ball bounces.  I don't yet want to change
1605      * the way the Mach symlink works.  When Mach support is
1606      * deprecated, we should change symlink so that the common case
1607      * returns the resultant vnode in a vpp argument.
1608      */
1609 
1610     MARK_ENTRY(CODA_SYMLINK_STATS);
1611 
1612     /* Check for symlink of control object. */
1613     if (IS_CTL_NAME(tdvp, nm, len)) {
1614 	MARK_INT_FAIL(CODA_SYMLINK_STATS);
1615 	return(EACCES);
1616     }
1617 
1618     if (plen+1 > CODA_MAXPATHLEN) {
1619 	MARK_INT_FAIL(CODA_SYMLINK_STATS);
1620 	return(EINVAL);
1621     }
1622 
1623     if (len+1 > CODA_MAXNAMLEN) {
1624 	MARK_INT_FAIL(CODA_SYMLINK_STATS);
1625 	error = EINVAL;
1626 	goto exit;
1627     }
1628 
1629     error = venus_symlink(vtomi(tdvp), &tdcp->c_fid, path, plen, nm, len, tva, cred, p);
1630 
1631     /* Invalidate the parent's attr cache, the modification time has changed */
1632     tdcp->c_flags &= ~C_VATTR;
1633 
1634     if (!error) {
1635 	/*
1636 	 * VOP_SYMLINK is not defined to pay attention to cnp->cn_flags;
1637 	 * these are defined only for VOP_LOOKUP.   We desire to reuse
1638 	 * cnp for a VOP_LOOKUP operation, and must be sure to not pass
1639 	 * stray flags passed to us.  Such stray flags can occur because
1640 	 * sys_symlink makes a namei call and then reuses the
1641 	 * componentname structure.
1642 	 */
1643 	/*
1644 	 * XXX Arguably we should create our own componentname structure
1645 	 * and not reuse the one that was passed in.
1646 	 */
1647 	saved_cn_flags = cnp->cn_flags;
1648 	cnp->cn_flags &= ~(MODMASK | OPMASK);
1649 	cnp->cn_flags |= LOOKUP;
1650 	error = VOP_LOOKUP(tdvp, ap->a_vpp, cnp);
1651 	cnp->cn_flags = saved_cn_flags;
1652 	/* Either an error occurs, or ap->a_vpp is locked. */
1653     } else {
1654 	/* error, so unlock and deference parent */
1655         vput(tdvp);
1656     }
1657 
1658  exit:
1659     CODADEBUG(CODA_SYMLINK, myprintf(("in symlink result %d\n",error)); )
1660     return(error);
1661 }
1662 
1663 /*
1664  * Read directory entries.
1665  */
1666 int
1667 coda_readdir(v)
1668     void *v;
1669 {
1670 /* true args */
1671     struct vop_readdir_args *ap = v;
1672     struct vnode *vp = ap->a_vp;
1673     struct cnode *cp = VTOC(vp);
1674     struct uio *uiop = ap->a_uio;
1675     struct ucred *cred = ap->a_cred;
1676     int *eofflag = ap->a_eofflag;
1677     off_t **cookies = ap->a_cookies;
1678     int *ncookies = ap->a_ncookies;
1679     struct proc *p = ap->a_uio->uio_procp;
1680 /* upcall decl */
1681 /* locals */
1682     int error = 0;
1683 
1684     MARK_ENTRY(CODA_READDIR_STATS);
1685 
1686     CODADEBUG(CODA_READDIR, myprintf(("coda_readdir(%p, %lu, %lld, %d)\n", uiop->uio_iov->iov_base, (unsigned long) uiop->uio_resid, (long long) uiop->uio_offset, uiop->uio_segflg)); )
1687 
1688     /* Check for readdir of control object. */
1689     if (IS_CTL_VP(vp)) {
1690 	MARK_INT_FAIL(CODA_READDIR_STATS);
1691 	return(ENOENT);
1692     }
1693 
1694     {
1695 	/* Redirect the request to UFS. */
1696 
1697 	/* If directory is not already open do an "internal open" on it. */
1698 	int opened_internally = 0;
1699 	if (cp->c_ovp == NULL) {
1700 	    opened_internally = 1;
1701 	    MARK_INT_GEN(CODA_OPEN_STATS);
1702 	    error = VOP_OPEN(vp, FREAD, cred, p);
1703 #ifdef	CODA_VERBOSE
1704 printf("coda_readdir: Internally Opening %p\n", vp);
1705 #endif
1706 	    if (error) return(error);
1707 	}
1708 
1709 	/* Have UFS handle the call. */
1710 	CODADEBUG(CODA_READDIR, myprintf((
1711 				"indirect readdir: fid = %s, refcnt = %d\n",
1712 				coda_f2s(&cp->c_fid), vp->v_usecount)); )
1713 	error = VOP_READDIR(cp->c_ovp, uiop, cred, eofflag, cookies,
1714 			       ncookies);
1715 	if (error)
1716 	    MARK_INT_FAIL(CODA_READDIR_STATS);
1717 	else
1718 	    MARK_INT_SAT(CODA_READDIR_STATS);
1719 
1720 	/* Do an "internal close" if necessary. */
1721 	if (opened_internally) {
1722 	    MARK_INT_GEN(CODA_CLOSE_STATS);
1723 	    (void)VOP_CLOSE(vp, FREAD, cred, p);
1724 	}
1725     }
1726 
1727     return(error);
1728 }
1729 
1730 /*
1731  * Convert from file system blocks to device blocks
1732  */
1733 int
1734 coda_bmap(v)
1735     void *v;
1736 {
1737     /* XXX on the global proc */
1738 /* true args */
1739     struct vop_bmap_args *ap = v;
1740     struct vnode *vp __attribute__((unused)) = ap->a_vp;	/* file's vnode */
1741     daddr_t bn __attribute__((unused)) = ap->a_bn;	/* fs block number */
1742     struct vnode **vpp = ap->a_vpp;			/* RETURN vp of device */
1743     daddr_t *bnp __attribute__((unused)) = ap->a_bnp;	/* RETURN device block number */
1744     struct proc *p __attribute__((unused)) = curproc;
1745 /* upcall decl */
1746 /* locals */
1747 
1748 	*vpp = (struct vnode *)0;
1749 	myprintf(("coda_bmap called!\n"));
1750 	return(EINVAL);
1751 }
1752 
1753 /*
1754  * I don't think the following two things are used anywhere, so I've
1755  * commented them out
1756  *
1757  * struct buf *async_bufhead;
1758  * int async_daemon_count;
1759  */
1760 int
1761 coda_strategy(v)
1762     void *v;
1763 {
1764 /* true args */
1765     struct vop_strategy_args *ap = v;
1766     struct buf *bp __attribute__((unused)) = ap->a_bp;
1767     struct proc *p __attribute__((unused)) = curproc;
1768 /* upcall decl */
1769 /* locals */
1770 
1771 	myprintf(("coda_strategy called!  "));
1772 	return(EINVAL);
1773 }
1774 
1775 int
1776 coda_reclaim(v)
1777     void *v;
1778 {
1779 /* true args */
1780     struct vop_reclaim_args *ap = v;
1781     struct vnode *vp = ap->a_vp;
1782     struct cnode *cp = VTOC(vp);
1783 /* upcall decl */
1784 /* locals */
1785 
1786 /*
1787  * Forced unmount/flush will let vnodes with non zero use be destroyed!
1788  */
1789     ENTRY;
1790 
1791     if (IS_UNMOUNTING(cp)) {
1792 #ifdef	DEBUG
1793 	if (VTOC(vp)->c_ovp) {
1794 	    if (IS_UNMOUNTING(cp))
1795 		printf("coda_reclaim: c_ovp not void: vp %p, cp %p\n", vp, cp);
1796 	}
1797 #endif
1798     } else {
1799 #ifdef OLD_DIAGNOSTIC
1800 	if (vp->v_usecount != 0)
1801 	    print("coda_reclaim: pushing active %p\n", vp);
1802 	if (VTOC(vp)->c_ovp) {
1803 	    panic("coda_reclaim: c_ovp not void");
1804 	}
1805 #endif
1806     }
1807     cache_purge(vp);
1808     coda_free(VTOC(vp));
1809     SET_VTOC(vp) = NULL;
1810     return (0);
1811 }
1812 
1813 int
1814 coda_lock(v)
1815     void *v;
1816 {
1817 /* true args */
1818     struct vop_lock_args *ap = v;
1819     struct vnode *vp = ap->a_vp;
1820     struct cnode *cp = VTOC(vp);
1821 /* upcall decl */
1822 /* locals */
1823 
1824     ENTRY;
1825 
1826     if (coda_lockdebug) {
1827 	myprintf(("Attempting lock on %s\n",
1828 		  coda_f2s(&cp->c_fid)));
1829     }
1830 
1831     return (lockmgr(&vp->v_lock, ap->a_flags, &vp->v_interlock));
1832 }
1833 
1834 int
1835 coda_unlock(v)
1836     void *v;
1837 {
1838 /* true args */
1839     struct vop_unlock_args *ap = v;
1840     struct vnode *vp = ap->a_vp;
1841     struct cnode *cp = VTOC(vp);
1842 /* upcall decl */
1843 /* locals */
1844 
1845     ENTRY;
1846     if (coda_lockdebug) {
1847 	myprintf(("Attempting unlock on %s\n",
1848 		  coda_f2s(&cp->c_fid)));
1849     }
1850 
1851     return (lockmgr(&vp->v_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock));
1852 }
1853 
1854 int
1855 coda_islocked(v)
1856     void *v;
1857 {
1858 /* true args */
1859     struct vop_islocked_args *ap = v;
1860     ENTRY;
1861 
1862     return (lockstatus(&ap->a_vp->v_lock));
1863 }
1864 
1865 /* How one looks up a vnode given a device/inode pair: */
1866 int
1867 coda_grab_vnode(dev_t dev, ino_t ino, struct vnode **vpp)
1868 {
1869     /* This is like VFS_VGET() or igetinode()! */
1870     int           error;
1871     struct mount *mp;
1872 
1873     if (!(mp = devtomp(dev))) {
1874 	myprintf(("coda_grab_vnode: devtomp(%d) returns NULL\n", dev));
1875 	return(ENXIO);
1876     }
1877 
1878     /* XXX - ensure that nonzero-return means failure */
1879     error = VFS_VGET(mp,ino,vpp);
1880     if (error) {
1881 	myprintf(("coda_grab_vnode: iget/vget(%d, %d) returns %p, err %d\n",
1882 		  dev, ino, *vpp, error));
1883 	return(ENOENT);
1884     }
1885     return(0);
1886 }
1887 
1888 void
1889 print_vattr( attr )
1890 	struct vattr *attr;
1891 {
1892     const char *typestr;
1893 
1894     switch (attr->va_type) {
1895     case VNON:
1896 	typestr = "VNON";
1897 	break;
1898     case VREG:
1899 	typestr = "VREG";
1900 	break;
1901     case VDIR:
1902 	typestr = "VDIR";
1903 	break;
1904     case VBLK:
1905 	typestr = "VBLK";
1906 	break;
1907     case VCHR:
1908 	typestr = "VCHR";
1909 	break;
1910     case VLNK:
1911 	typestr = "VLNK";
1912 	break;
1913     case VSOCK:
1914 	typestr = "VSCK";
1915 	break;
1916     case VFIFO:
1917 	typestr = "VFFO";
1918 	break;
1919     case VBAD:
1920 	typestr = "VBAD";
1921 	break;
1922     default:
1923 	typestr = "????";
1924 	break;
1925     }
1926 
1927 
1928     myprintf(("attr: type %s mode %d uid %d gid %d fsid %d rdev %d\n",
1929 	      typestr, (int)attr->va_mode, (int)attr->va_uid,
1930 	      (int)attr->va_gid, (int)attr->va_fsid, (int)attr->va_rdev));
1931 
1932     myprintf(("      fileid %d nlink %d size %d blocksize %d bytes %d\n",
1933 	      (int)attr->va_fileid, (int)attr->va_nlink,
1934 	      (int)attr->va_size,
1935 	      (int)attr->va_blocksize,(int)attr->va_bytes));
1936     myprintf(("      gen %ld flags %ld vaflags %d\n",
1937 	      attr->va_gen, attr->va_flags, attr->va_vaflags));
1938     myprintf(("      atime sec %d nsec %d\n",
1939 	      (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec));
1940     myprintf(("      mtime sec %d nsec %d\n",
1941 	      (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec));
1942     myprintf(("      ctime sec %d nsec %d\n",
1943 	      (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec));
1944 }
1945 
1946 /* How to print a ucred */
1947 void
1948 print_cred(cred)
1949 	struct ucred *cred;
1950 {
1951 
1952 	int i;
1953 
1954 	myprintf(("ref %d\tuid %d\n",cred->cr_ref,cred->cr_uid));
1955 
1956 	for (i=0; i < cred->cr_ngroups; i++)
1957 		myprintf(("\tgroup %d: (%d)\n",i,cred->cr_groups[i]));
1958 	myprintf(("\n"));
1959 
1960 }
1961 
1962 /*
1963  * Return a vnode for the given fid.
1964  * If no cnode exists for this fid create one and put it
1965  * in a table hashed by coda_f2i().  If the cnode for
1966  * this fid is already in the table return it (ref count is
1967  * incremented by coda_find.  The cnode will be flushed from the
1968  * table when coda_inactive calls coda_unsave.
1969  */
1970 struct cnode *
1971 make_coda_node(fid, vfsp, type)
1972      CodaFid *fid; struct mount *vfsp; short type;
1973 {
1974     struct cnode *cp;
1975     int          err;
1976 
1977     if ((cp = coda_find(fid)) == NULL) {
1978 	struct vnode *vp;
1979 
1980 	cp = coda_alloc();
1981 	cp->c_fid = *fid;
1982 
1983 	err = getnewvnode(VT_CODA, vfsp, coda_vnodeop_p, &vp);
1984 	if (err) {
1985 	    panic("coda: getnewvnode returned error %d", err);
1986 	}
1987 	vp->v_data = cp;
1988 	vp->v_type = type;
1989 	cp->c_vnode = vp;
1990 	coda_save(cp);
1991 
1992     } else {
1993 	vref(CTOV(cp));
1994     }
1995 
1996     return cp;
1997 }
1998 
1999 int
2000 coda_getpages(v)
2001 	void *v;
2002 {
2003 	struct vop_getpages_args /* {
2004 		struct vnode *a_vp;
2005 		voff_t a_offset;
2006 		struct vm_page **a_m;
2007 		int *a_count;
2008 		int a_centeridx;
2009 		vm_prot_t a_access_type;
2010 		int a_advice;
2011 		int a_flags;
2012 	} */ *ap = v;
2013 	struct vnode *vp = ap->a_vp;
2014 	struct cnode *cp = VTOC(vp);
2015 	struct proc *p = curproc;
2016 	struct ucred *cred = p->p_ucred;
2017 	int error;
2018 
2019 	/* Check for control object. */
2020 	if (IS_CTL_VP(vp)) {
2021 		return(EINVAL);
2022 	}
2023 
2024 	error = VOP_OPEN(vp, FREAD, cred, p);
2025 	if (error) {
2026 		return error;
2027 	}
2028 	ap->a_vp = cp->c_ovp;
2029 	error = VOCALL(ap->a_vp->v_op, VOFFSET(vop_getpages), ap);
2030 	(void) VOP_CLOSE(vp, FREAD, cred, p);
2031 	return error;
2032 }
2033 
2034 int
2035 coda_putpages(v)
2036 	void *v;
2037 {
2038 	struct vop_putpages_args /* {
2039 		struct vnode *a_vp;
2040 		voff_t a_offlo;
2041 		voff_t a_offhi;
2042 		int a_flags;
2043 	} */ *ap = v;
2044 	struct vnode *vp = ap->a_vp;
2045 
2046 	simple_unlock(&vp->v_interlock);
2047 
2048 	/* Check for control object. */
2049 	if (IS_CTL_VP(vp)) {
2050 		return(EINVAL);
2051 	}
2052 
2053 	/*
2054 	 * XXX
2055 	 * we'd like to do something useful here for msync(),
2056 	 * but that turns out to be hard.
2057 	 */
2058 
2059 	return 0;
2060 }
2061