xref: /netbsd-src/sys/coda/coda_subr.c (revision 4ea2e44bb61ff86f86e3be96ca8b9926e20b512f)
1 /*	$NetBSD: coda_subr.c,v 1.33 2024/05/17 23:57:46 thorpej Exp $	*/
2 
3 /*
4  *
5  *             Coda: an Experimental Distributed File System
6  *                              Release 3.1
7  *
8  *           Copyright (c) 1987-1998 Carnegie Mellon University
9  *                          All Rights Reserved
10  *
11  * Permission  to  use, copy, modify and distribute this software and its
12  * documentation is hereby granted,  provided  that  both  the  copyright
13  * notice  and  this  permission  notice  appear  in  all  copies  of the
14  * software, derivative works or  modified  versions,  and  any  portions
15  * thereof, and that both notices appear in supporting documentation, and
16  * that credit is given to Carnegie Mellon University  in  all  documents
17  * and publicity pertaining to direct or indirect use of this code or its
18  * derivatives.
19  *
20  * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
21  * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
22  * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
23  * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
24  * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
25  * ANY DERIVATIVE WORK.
26  *
27  * Carnegie  Mellon  encourages  users  of  this  software  to return any
28  * improvements or extensions that  they  make,  and  to  grant  Carnegie
29  * Mellon the rights to redistribute these changes without encumbrance.
30  *
31  * 	@(#) coda/coda_subr.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $
32  */
33 
34 /*
35  * Mach Operating System
36  * Copyright (c) 1989 Carnegie-Mellon University
37  * All rights reserved.  The CMU software License Agreement specifies
38  * the terms and conditions for use and redistribution.
39  */
40 
41 /*
42  * This code was written for the Coda file system at Carnegie Mellon
43  * University.  Contributers include David Steere, James Kistler, and
44  * M. Satyanarayanan.  */
45 
46 /* NOTES: rvb
47  * 1.	Added coda_unmounting to mark all cnodes as being UNMOUNTING.  This has to
48  *	 be done before dounmount is called.  Because some of the routines that
49  *	 dounmount calls before coda_unmounted might try to force flushes to venus.
50  *	 The vnode pager does this.
51  * 2.	coda_unmounting marks all cnodes scanning coda_cache.
52  * 3.	cfs_checkunmounting (under DEBUG) checks all cnodes by chasing the vnodes
53  *	 under the /coda mount point.
54  * 4.	coda_cacheprint (under DEBUG) prints names with vnode/cnode address
55  */
56 
57 #include <sys/cdefs.h>
58 __KERNEL_RCSID(0, "$NetBSD: coda_subr.c,v 1.33 2024/05/17 23:57:46 thorpej Exp $");
59 
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/proc.h>
63 #include <sys/select.h>
64 #include <sys/mount.h>
65 #include <sys/kauth.h>
66 
67 #include <coda/coda.h>
68 #include <coda/cnode.h>
69 #include <coda/coda_subr.h>
70 #include <coda/coda_namecache.h>
71 
72 int codadebug = 0;
73 int coda_printf_delay = 0;  /* in microseconds */
74 int coda_vnop_print_entry = 0;
75 int coda_vfsop_print_entry = 0;
76 
77 #ifdef CODA_COMPAT_5
78 #define coda_hash(fid) \
79     (((fid)->Volume + (fid)->Vnode) & (CODA_CACHESIZE-1))
80 #define IS_DIR(cnode)        (cnode.Vnode & 0x1)
81 #else
82 #define coda_hash(fid) \
83     (coda_f2i(fid) & (CODA_CACHESIZE-1))
84 #define IS_DIR(cnode)        (cnode.opaque[2] & 0x1)
85 #endif
86 
87 struct vnode *coda_ctlvp;
88 
89 /*
90  * Lookup a cnode by fid. If the cnode is dying, it is bogus so skip it.
91  * The cnode is returned locked with the vnode referenced.
92  */
93 struct cnode *
coda_find(CodaFid * fid)94 coda_find(CodaFid *fid)
95 {
96 	int i;
97 	struct vnode *vp;
98 	struct cnode *cp;
99 
100 	for (i = 0; i < NVCODA; i++) {
101 		if (!coda_mnttbl[i].mi_started)
102 			continue;
103 		if (vcache_get(coda_mnttbl[i].mi_vfsp,
104 		    fid, sizeof(CodaFid), &vp) != 0)
105 			continue;
106 		mutex_enter(vp->v_interlock);
107 		cp = VTOC(vp);
108 		if (vp->v_type == VNON || cp == NULL || IS_UNMOUNTING(cp)) {
109 			mutex_exit(vp->v_interlock);
110 			vrele(vp);
111 			continue;
112 		}
113 		mutex_enter(&cp->c_lock);
114 		mutex_exit(vp->v_interlock);
115 
116 		return cp;
117 	}
118 
119 	return NULL;
120 }
121 
122 /*
123  * Iterate over all nodes attached to coda mounts.
124  */
125 static void
coda_iterate(bool (* f)(void *,struct vnode *),void * cl)126 coda_iterate(bool (*f)(void *, struct vnode *), void *cl)
127 {
128 	int i;
129 	struct vnode_iterator *marker;
130 	struct vnode *vp;
131 
132 	for (i = 0; i < NVCODA; i++) {
133 		if (coda_mnttbl[i].mi_vfsp == NULL)
134 			continue;
135 		vfs_vnode_iterator_init(coda_mnttbl[i].mi_vfsp, &marker);
136 		while ((vp = vfs_vnode_iterator_next(marker, f, cl)) != NULL)
137 			vrele(vp);
138 		vfs_vnode_iterator_destroy(marker);
139 	}
140 }
141 
142 /*
143  * coda_kill is called as a side effect to vcopen. To prevent any
144  * cnodes left around from an earlier run of a venus or warden from
145  * causing problems with the new instance, mark any outstanding cnodes
146  * as dying. Future operations on these cnodes should fail (excepting
147  * coda_inactive of course!). Since multiple venii/wardens can be
148  * running, only kill the cnodes for a particular entry in the
149  * coda_mnttbl. -- DCS 12/1/94 */
150 
151 static bool
coda_kill_selector(void * cl,struct vnode * vp)152 coda_kill_selector(void *cl, struct vnode *vp)
153 {
154 	int *count = cl;
155 
156 	(*count)++;
157 
158 	return false;
159 }
160 
161 int
coda_kill(struct mount * whoIam,enum dc_status dcstat)162 coda_kill(struct mount *whoIam, enum dc_status dcstat)
163 {
164 	int count = 0;
165 	struct vnode_iterator *marker;
166 
167 	/*
168 	 * Algorithm is as follows:
169 	 *     Second, flush whatever vnodes we can from the name cache.
170 	 */
171 
172 	/* This is slightly overkill, but should work. Eventually it'd be
173 	 * nice to only flush those entries from the namecache that
174 	 * reference a vnode in this vfs.  */
175 	coda_nc_flush(dcstat);
176 
177 
178 	vfs_vnode_iterator_init(whoIam, &marker);
179 	vfs_vnode_iterator_next(marker, coda_kill_selector, &count);
180 	vfs_vnode_iterator_destroy(marker);
181 
182 	return count;
183 }
184 
185 /*
186  * There are two reasons why a cnode may be in use, it may be in the
187  * name cache or it may be executing.
188  */
189 static bool
coda_flush_selector(void * cl,struct vnode * vp)190 coda_flush_selector(void *cl, struct vnode *vp)
191 {
192 	struct cnode *cp = VTOC(vp);
193 
194 	if (cp != NULL && !IS_DIR(cp->c_fid)) /* only files can be executed */
195 		coda_vmflush(cp);
196 
197 	return false;
198 }
199 void
coda_flush(enum dc_status dcstat)200 coda_flush(enum dc_status dcstat)
201 {
202 
203     coda_clstat.ncalls++;
204     coda_clstat.reqs[CODA_FLUSH]++;
205 
206     coda_nc_flush(dcstat);	    /* flush files from the name cache */
207 
208     coda_iterate(coda_flush_selector, NULL);
209 }
210 
211 /*
212  * As a debugging measure, print out any cnodes that lived through a
213  * name cache flush.
214  */
215 static bool
coda_testflush_selector(void * cl,struct vnode * vp)216 coda_testflush_selector(void *cl, struct vnode *vp)
217 {
218 	struct cnode *cp = VTOC(vp);
219 
220 	if (cp != NULL)
221 		myprintf(("Live cnode fid %s count %d\n",
222 		     coda_f2s(&cp->c_fid), vrefcnt(CTOV(cp))));
223 
224 	return false;
225 }
226 void
coda_testflush(void)227 coda_testflush(void)
228 {
229 
230 	coda_iterate(coda_testflush_selector, NULL);
231 }
232 
233 /*
234  *     First, step through all cnodes and mark them unmounting.
235  *         NetBSD kernels may try to fsync them now that venus
236  *         is dead, which would be a bad thing.
237  *
238  */
239 static bool
coda_unmounting_selector(void * cl,struct vnode * vp)240 coda_unmounting_selector(void *cl, struct vnode *vp)
241 {
242 	struct cnode *cp = VTOC(vp);
243 
244 	if (cp)
245 		cp->c_flags |= C_UNMOUNTING;
246 
247 	return false;
248 }
249 void
coda_unmounting(struct mount * whoIam)250 coda_unmounting(struct mount *whoIam)
251 {
252 	struct vnode_iterator *marker;
253 
254 	vfs_vnode_iterator_init(whoIam, &marker);
255 	vfs_vnode_iterator_next(marker, coda_unmounting_selector, NULL);
256 	vfs_vnode_iterator_destroy(marker);
257 }
258 
259 #ifdef	DEBUG
260 static bool
coda_checkunmounting_selector(void * cl,struct vnode * vp)261 coda_checkunmounting_selector(void *cl, struct vnode *vp)
262 {
263 	struct cnode *cp = VTOC(vp);
264 
265 	if (cp && !(cp->c_flags & C_UNMOUNTING)) {
266 		printf("vp %p, cp %p missed\n", vp, cp);
267 		cp->c_flags |= C_UNMOUNTING;
268 	}
269 
270 	return false;
271 }
272 void
coda_checkunmounting(struct mount * mp)273 coda_checkunmounting(struct mount *mp)
274 {
275 	struct vnode_iterator *marker;
276 
277 	vfs_vnode_iterator_init(mp, &marker);
278 	vfs_vnode_iterator_next(marker, coda_checkunmounting_selector, NULL);
279 	vfs_vnode_iterator_destroy(marker);
280 }
281 
282 void
coda_cacheprint(struct mount * whoIam)283 coda_cacheprint(struct mount *whoIam)
284 {
285 	struct vnode *vp;
286 	struct vnode_iterator *marker;
287 	int count = 0;
288 
289 	printf("coda_cacheprint: coda_ctlvp %p, cp %p", coda_ctlvp, VTOC(coda_ctlvp));
290 	coda_nc_name(VTOC(coda_ctlvp));
291 	printf("\n");
292 
293 	vfs_vnode_iterator_init(whoIam, &marker);
294 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL)) != NULL) {
295 		printf("coda_cacheprint: vp %p, cp %p", vp, VTOC(vp));
296 		coda_nc_name(VTOC(vp));
297 		printf("\n");
298 		count++;
299 		vrele(vp);
300 	}
301 	printf("coda_cacheprint: count %d\n", count);
302 	vfs_vnode_iterator_destroy(marker);
303 }
304 #endif
305 
306 /*
307  * There are 6 cases where invalidations occur. The semantics of each
308  * is listed here.
309  *
310  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
311  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
312  *                  This call is a result of token expiration.
313  *
314  * The next two are the result of callbacks on a file or directory.
315  * CODA_ZAPDIR    -- flush the attributes for the dir from its cnode.
316  *                  Zap all children of this directory from the namecache.
317  * CODA_ZAPFILE   -- flush the attributes for a file.
318  *
319  * The fifth is a result of Venus detecting an inconsistent file.
320  * CODA_PURGEFID  -- flush the attribute for the file
321  *                  If it is a dir (odd vnode), purge its
322  *                  children from the namecache
323  *                  remove the file from the namecache.
324  *
325  * The sixth allows Venus to replace local fids with global ones
326  * during reintegration.
327  *
328  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache
329  */
330 
handleDownCall(int opcode,union outputArgs * out)331 int handleDownCall(int opcode, union outputArgs *out)
332 {
333     int error;
334 
335     /* Handle invalidate requests. */
336     switch (opcode) {
337       case CODA_FLUSH : {
338 
339 	  coda_flush(IS_DOWNCALL);
340 
341 	  CODADEBUG(CODA_FLUSH,coda_testflush();)    /* print remaining cnodes */
342 	      return(0);
343       }
344 
345       case CODA_PURGEUSER : {
346 	  coda_clstat.ncalls++;
347 	  coda_clstat.reqs[CODA_PURGEUSER]++;
348 
349 	  /* XXX - need to prevent fsync's */
350 #ifdef CODA_COMPAT_5
351 	  coda_nc_purge_user(out->coda_purgeuser.cred.cr_uid, IS_DOWNCALL);
352 #else
353 	  coda_nc_purge_user(out->coda_purgeuser.uid, IS_DOWNCALL);
354 #endif
355 	  return(0);
356       }
357 
358       case CODA_ZAPFILE : {
359 	  struct cnode *cp;
360 
361 	  error = 0;
362 	  coda_clstat.ncalls++;
363 	  coda_clstat.reqs[CODA_ZAPFILE]++;
364 
365 	  cp = coda_find(&out->coda_zapfile.Fid);
366 	  if (cp != NULL) {
367 	      cp->c_flags &= ~C_VATTR;
368 	      if (CTOV(cp)->v_iflag & VI_TEXT)
369 		  error = coda_vmflush(cp);
370 	      CODADEBUG(CODA_ZAPFILE, myprintf((
371 		    "zapfile: fid = %s, refcnt = %d, error = %d\n",
372 		    coda_f2s(&cp->c_fid), vrefcnt(CTOV(cp)) - 1, error)););
373 	      if (vrefcnt(CTOV(cp)) == 1) {
374 		  cp->c_flags |= C_PURGING;
375 	      }
376 	      mutex_exit(&cp->c_lock);
377 	      vrele(CTOV(cp));
378 	  }
379 
380 	  return(error);
381       }
382 
383       case CODA_ZAPDIR : {
384 	  struct cnode *cp;
385 
386 	  coda_clstat.ncalls++;
387 	  coda_clstat.reqs[CODA_ZAPDIR]++;
388 
389 	  cp = coda_find(&out->coda_zapdir.Fid);
390 	  if (cp != NULL) {
391 	      cp->c_flags &= ~C_VATTR;
392 	      coda_nc_zapParentfid(&out->coda_zapdir.Fid, IS_DOWNCALL);
393 
394 	      CODADEBUG(CODA_ZAPDIR, myprintf((
395 		    "zapdir: fid = %s, refcnt = %d\n",
396 		    coda_f2s(&cp->c_fid), vrefcnt(CTOV(cp)) - 1)););
397 	      if (vrefcnt(CTOV(cp)) == 1) {
398 		  cp->c_flags |= C_PURGING;
399 	      }
400 	      mutex_exit(&cp->c_lock);
401 	      vrele(CTOV(cp));
402 	  }
403 
404 	  return(0);
405       }
406 
407       case CODA_PURGEFID : {
408 	  struct cnode *cp;
409 
410 	  error = 0;
411 	  coda_clstat.ncalls++;
412 	  coda_clstat.reqs[CODA_PURGEFID]++;
413 
414 	  cp = coda_find(&out->coda_purgefid.Fid);
415 	  if (cp != NULL) {
416 	      if (IS_DIR(out->coda_purgefid.Fid)) { /* Vnode is a directory */
417 		  coda_nc_zapParentfid(&out->coda_purgefid.Fid,
418 				     IS_DOWNCALL);
419 	      }
420 	      cp->c_flags &= ~C_VATTR;
421 	      coda_nc_zapfid(&out->coda_purgefid.Fid, IS_DOWNCALL);
422 	      if (!(IS_DIR(out->coda_purgefid.Fid))
423 		  && (CTOV(cp)->v_iflag & VI_TEXT)) {
424 
425 		  error = coda_vmflush(cp);
426 	      }
427 	      CODADEBUG(CODA_PURGEFID, myprintf((
428 			 "purgefid: fid = %s, refcnt = %d, error = %d\n",
429 			 coda_f2s(&cp->c_fid), vrefcnt(CTOV(cp)) - 1, error)););
430 	      if (vrefcnt(CTOV(cp)) == 1) {
431 		  cp->c_flags |= C_PURGING;
432 	      }
433 	      mutex_exit(&cp->c_lock);
434 	      vrele(CTOV(cp));
435 	  }
436 	  return(error);
437       }
438 
439       case CODA_REPLACE : {
440 	  struct cnode *cp = NULL;
441 
442 	  coda_clstat.ncalls++;
443 	  coda_clstat.reqs[CODA_REPLACE]++;
444 
445 	  cp = coda_find(&out->coda_replace.OldFid);
446 	  if (cp != NULL) {
447 	      error = vcache_rekey_enter(CTOV(cp)->v_mount, CTOV(cp),
448 		  &out->coda_replace.OldFid, sizeof(CodaFid),
449 		  &out->coda_replace.NewFid, sizeof(CodaFid));
450 	      if (error) {
451 		  mutex_exit(&cp->c_lock);
452 		  vrele(CTOV(cp));
453 		  return error;
454 	      }
455 	      cp->c_fid = out->coda_replace.NewFid;
456 	      vcache_rekey_exit(CTOV(cp)->v_mount, CTOV(cp),
457 		  &out->coda_replace.OldFid, sizeof(CodaFid),
458 		  &cp->c_fid, sizeof(CodaFid));
459 
460 	      CODADEBUG(CODA_REPLACE, myprintf((
461 			"replace: oldfid = %s, newfid = %s, cp = %p\n",
462 			coda_f2s(&out->coda_replace.OldFid),
463 			coda_f2s(&cp->c_fid), cp));)
464 	      mutex_exit(&cp->c_lock);
465 	      vrele(CTOV(cp));
466 	  }
467 	  return (0);
468       }
469       default:
470       	myprintf(("handleDownCall: unknown opcode %d\n", opcode));
471 	return (EINVAL);
472     }
473 }
474 
475 /* coda_grab_vnode: lives in either cfs_mach.c or cfs_nbsd.c */
476 
477 int
coda_vmflush(struct cnode * cp)478 coda_vmflush(struct cnode *cp)
479 {
480     return 0;
481 }
482 
483 
484 /*
485  * kernel-internal debugging switches
486  */
487 
coda_debugon(void)488 void coda_debugon(void)
489 {
490     codadebug = -1;
491     coda_nc_debug = -1;
492     coda_vnop_print_entry = 1;
493     coda_psdev_print_entry = 1;
494     coda_vfsop_print_entry = 1;
495 }
496 
coda_debugoff(void)497 void coda_debugoff(void)
498 {
499     codadebug = 0;
500     coda_nc_debug = 0;
501     coda_vnop_print_entry = 0;
502     coda_psdev_print_entry = 0;
503     coda_vfsop_print_entry = 0;
504 }
505 
506 /* How to print a ucred */
507 void
coda_print_cred(kauth_cred_t cred)508 coda_print_cred(kauth_cred_t cred)
509 {
510 
511 	uint16_t ngroups;
512 	int i;
513 
514 	myprintf(("ref %d\tuid %d\n", kauth_cred_getrefcnt(cred),
515 		 kauth_cred_geteuid(cred)));
516 
517 	ngroups = kauth_cred_ngroups(cred);
518 	for (i=0; i < ngroups; i++)
519 		myprintf(("\tgroup %d: (%d)\n", i, kauth_cred_group(cred, i)));
520 	myprintf(("\n"));
521 
522 }
523 
524 /*
525  * Utilities used by both client and server
526  * Standard levels:
527  * 0) no debugging
528  * 1) hard failures
529  * 2) soft failures
530  * 3) current test software
531  * 4) main procedure entry points
532  * 5) main procedure exit points
533  * 6) utility procedure entry points
534  * 7) utility procedure exit points
535  * 8) obscure procedure entry points
536  * 9) obscure procedure exit points
537  * 10) random stuff
538  * 11) all <= 1
539  * 12) all <= 2
540  * 13) all <= 3
541  * ...
542  */
543