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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 478 coda_vmflush(struct cnode *cp) 479 { 480 return 0; 481 } 482 483 484 /* 485 * kernel-internal debugging switches 486 */ 487 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 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 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