1 /* $NetBSD: coda_vfsops.c,v 1.89 2020/11/20 10:08:47 hannken 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 * @(#) cfs/coda_vfsops.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 47 #include <sys/cdefs.h> 48 __KERNEL_RCSID(0, "$NetBSD: coda_vfsops.c,v 1.89 2020/11/20 10:08:47 hannken Exp $"); 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/sysctl.h> 53 #include <sys/malloc.h> 54 #include <sys/conf.h> 55 #include <sys/namei.h> 56 #include <sys/dirent.h> 57 #include <sys/mount.h> 58 #include <sys/proc.h> 59 #include <sys/select.h> 60 #include <sys/kauth.h> 61 #include <sys/module.h> 62 63 #include <coda/coda.h> 64 #include <coda/cnode.h> 65 #include <coda/coda_vfsops.h> 66 #include <coda/coda_venus.h> 67 #include <coda/coda_subr.h> 68 #include <coda/coda_opstats.h> 69 /* for VN_RDEV */ 70 #include <miscfs/specfs/specdev.h> 71 #include <miscfs/genfs/genfs.h> 72 73 MODULE(MODULE_CLASS_VFS, coda, "vcoda"); 74 75 #define ENTRY if(coda_vfsop_print_entry) myprintf(("Entered %s\n",__func__)) 76 77 extern struct vnode *coda_ctlvp; 78 extern struct coda_mntinfo coda_mnttbl[NVCODA]; /* indexed by minor device number */ 79 80 /* structure to keep statistics of internally generated/satisfied calls */ 81 82 struct coda_op_stats coda_vfsopstats[CODA_VFSOPS_SIZE]; 83 84 #define MARK_ENTRY(op) (coda_vfsopstats[op].entries++) 85 #define MARK_INT_SAT(op) (coda_vfsopstats[op].sat_intrn++) 86 #define MARK_INT_FAIL(op) (coda_vfsopstats[op].unsat_intrn++) 87 #define MRAK_INT_GEN(op) (coda_vfsopstats[op].gen_intrn++) 88 89 extern const struct cdevsw vcoda_cdevsw; 90 extern const struct vnodeopv_desc coda_vnodeop_opv_desc; 91 92 const struct vnodeopv_desc * const coda_vnodeopv_descs[] = { 93 &coda_vnodeop_opv_desc, 94 NULL, 95 }; 96 97 struct vfsops coda_vfsops = { 98 .vfs_name = MOUNT_CODA, 99 .vfs_min_mount_data = 256, 100 /* This is the pathname, unlike every other fs */ 101 .vfs_mount = coda_mount, 102 .vfs_start = coda_start, 103 .vfs_unmount = coda_unmount, 104 .vfs_root = coda_root, 105 .vfs_quotactl = (void *)eopnotsupp, 106 .vfs_statvfs = coda_nb_statvfs, 107 .vfs_sync = coda_sync, 108 .vfs_vget = coda_vget, 109 .vfs_loadvnode = coda_loadvnode, 110 .vfs_fhtovp = (void *)eopnotsupp, 111 .vfs_vptofh = (void *)eopnotsupp, 112 .vfs_init = coda_init, 113 .vfs_done = coda_done, 114 .vfs_mountroot = (void *)eopnotsupp, 115 .vfs_snapshot = (void *)eopnotsupp, 116 .vfs_extattrctl = vfs_stdextattrctl, 117 .vfs_suspendctl = genfs_suspendctl, 118 .vfs_renamelock_enter = genfs_renamelock_enter, 119 .vfs_renamelock_exit = genfs_renamelock_exit, 120 .vfs_fsync = (void *)eopnotsupp, 121 .vfs_opv_descs = coda_vnodeopv_descs 122 }; 123 124 static int 125 coda_modcmd(modcmd_t cmd, void *arg) 126 { 127 128 switch (cmd) { 129 case MODULE_CMD_INIT: 130 return vfs_attach(&coda_vfsops); 131 case MODULE_CMD_FINI: 132 return vfs_detach(&coda_vfsops); 133 default: 134 return ENOTTY; 135 } 136 } 137 138 int 139 coda_vfsopstats_init(void) 140 { 141 int i; 142 143 for (i=0;i<CODA_VFSOPS_SIZE;i++) { 144 coda_vfsopstats[i].opcode = i; 145 coda_vfsopstats[i].entries = 0; 146 coda_vfsopstats[i].sat_intrn = 0; 147 coda_vfsopstats[i].unsat_intrn = 0; 148 coda_vfsopstats[i].gen_intrn = 0; 149 } 150 151 return 0; 152 } 153 154 /* 155 * cfs mount vfsop 156 * Set up mount info record and attach it to vfs struct. 157 */ 158 /*ARGSUSED*/ 159 int 160 coda_mount(struct mount *vfsp, /* Allocated and initialized by mount(2) */ 161 const char *path, /* path covered: ignored by the fs-layer */ 162 void *data, /* Need to define a data type for this in netbsd? */ 163 size_t *data_len) 164 { 165 struct lwp *l = curlwp; 166 struct vnode *dvp; 167 struct cnode *cp; 168 dev_t dev; 169 struct coda_mntinfo *mi; 170 struct vnode *rtvp; 171 const struct cdevsw *cdev; 172 CodaFid rootfid = INVAL_FID; 173 CodaFid ctlfid = CTL_FID; 174 int error; 175 176 if (data == NULL) 177 return EINVAL; 178 if (vfsp->mnt_flag & MNT_GETARGS) 179 return EINVAL; 180 ENTRY; 181 182 coda_vfsopstats_init(); 183 coda_vnodeopstats_init(); 184 185 MARK_ENTRY(CODA_MOUNT_STATS); 186 if (CODA_MOUNTED(vfsp)) { 187 MARK_INT_FAIL(CODA_MOUNT_STATS); 188 return(EBUSY); 189 } 190 191 /* Validate mount device. Similar to getmdev(). */ 192 193 /* 194 * XXX: coda passes the mount device as the entire mount args, 195 * All other fs pass a structure contining a pointer. 196 * In order to get sys_mount() to do the copyin() we've set a 197 * fixed default size for the filename buffer. 198 */ 199 /* Ensure that namei() doesn't run off the filename buffer */ 200 if (*data_len < 1 || *data_len > PATH_MAX || 201 strnlen(data, *data_len) >= *data_len) { 202 MARK_INT_FAIL(CODA_MOUNT_STATS); 203 return EINVAL; 204 } 205 error = namei_simple_kernel((char *)data, NSM_FOLLOW_NOEMULROOT, 206 &dvp); 207 208 if (error) { 209 MARK_INT_FAIL(CODA_MOUNT_STATS); 210 return (error); 211 } 212 if (dvp->v_type != VCHR) { 213 MARK_INT_FAIL(CODA_MOUNT_STATS); 214 vrele(dvp); 215 return(ENXIO); 216 } 217 dev = dvp->v_rdev; 218 vrele(dvp); 219 cdev = cdevsw_lookup(dev); 220 if (cdev == NULL) { 221 MARK_INT_FAIL(CODA_MOUNT_STATS); 222 return(ENXIO); 223 } 224 225 /* 226 * See if the device table matches our expectations. 227 */ 228 if (cdev != &vcoda_cdevsw) 229 { 230 MARK_INT_FAIL(CODA_MOUNT_STATS); 231 return(ENXIO); 232 } 233 234 if (minor(dev) >= NVCODA) { 235 MARK_INT_FAIL(CODA_MOUNT_STATS); 236 return(ENXIO); 237 } 238 239 /* 240 * Initialize the mount record and link it to the vfs struct 241 */ 242 mi = &coda_mnttbl[minor(dev)]; 243 244 if (!VC_OPEN(&mi->mi_vcomm)) { 245 MARK_INT_FAIL(CODA_MOUNT_STATS); 246 return(ENODEV); 247 } 248 249 /* No initialization (here) of mi_vcomm! */ 250 vfsp->mnt_data = mi; 251 vfsp->mnt_stat.f_fsidx.__fsid_val[0] = 0; 252 vfsp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_CODA); 253 vfsp->mnt_stat.f_fsid = vfsp->mnt_stat.f_fsidx.__fsid_val[0]; 254 vfsp->mnt_stat.f_namemax = CODA_MAXNAMLEN; 255 mi->mi_vfsp = vfsp; 256 257 /* 258 * Make a root vnode to placate the Vnode interface, but don't 259 * actually make the CODA_ROOT call to venus until the first call 260 * to coda_root in case a server is down while venus is starting. 261 */ 262 cp = make_coda_node(&rootfid, vfsp, VDIR); 263 rtvp = CTOV(cp); 264 rtvp->v_vflag |= VV_ROOT; 265 266 cp = make_coda_node(&ctlfid, vfsp, VCHR); 267 268 coda_ctlvp = CTOV(cp); 269 270 /* Add vfs and rootvp to chain of vfs hanging off mntinfo */ 271 mi->mi_vfsp = vfsp; 272 mi->mi_rootvp = rtvp; 273 274 /* set filesystem block size */ 275 vfsp->mnt_stat.f_bsize = 8192; /* XXX -JJK */ 276 vfsp->mnt_stat.f_frsize = 8192; /* XXX -JJK */ 277 278 /* error is currently guaranteed to be zero, but in case some 279 code changes... */ 280 CODADEBUG(1, 281 myprintf(("coda_mount returned %d\n",error));); 282 if (error) 283 MARK_INT_FAIL(CODA_MOUNT_STATS); 284 else 285 MARK_INT_SAT(CODA_MOUNT_STATS); 286 287 return set_statvfs_info("/coda", UIO_SYSSPACE, "CODA", UIO_SYSSPACE, 288 vfsp->mnt_op->vfs_name, vfsp, l); 289 } 290 291 int 292 coda_start(struct mount *vfsp, int flags) 293 { 294 ENTRY; 295 vftomi(vfsp)->mi_started = 1; 296 return (0); 297 } 298 299 int 300 coda_unmount(struct mount *vfsp, int mntflags) 301 { 302 struct coda_mntinfo *mi = vftomi(vfsp); 303 int active, error = 0; 304 305 ENTRY; 306 MARK_ENTRY(CODA_UMOUNT_STATS); 307 if (!CODA_MOUNTED(vfsp)) { 308 MARK_INT_FAIL(CODA_UMOUNT_STATS); 309 return(EINVAL); 310 } 311 312 if (mi->mi_vfsp == vfsp) { /* We found the victim */ 313 if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp))) 314 return (EBUSY); /* Venus is still running */ 315 316 #ifdef DEBUG 317 printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp, VTOC(mi->mi_rootvp)); 318 #endif 319 mi->mi_started = 0; 320 321 vrele(mi->mi_rootvp); 322 vrele(coda_ctlvp); 323 324 active = coda_kill(vfsp, NOT_DOWNCALL); 325 mi->mi_rootvp->v_vflag &= ~VV_ROOT; 326 error = vflush(mi->mi_vfsp, NULLVP, FORCECLOSE); 327 printf("coda_unmount: active = %d, vflush active %d\n", active, error); 328 error = 0; 329 330 /* I'm going to take this out to allow lookups to go through. I'm 331 * not sure it's important anyway. -- DCS 2/2/94 332 */ 333 /* vfsp->VFS_DATA = NULL; */ 334 335 /* No more vfsp's to hold onto */ 336 mi->mi_vfsp = NULL; 337 mi->mi_rootvp = NULL; 338 339 if (error) 340 MARK_INT_FAIL(CODA_UMOUNT_STATS); 341 else 342 MARK_INT_SAT(CODA_UMOUNT_STATS); 343 344 return(error); 345 } 346 return (EINVAL); 347 } 348 349 /* 350 * find root of cfs 351 */ 352 int 353 coda_root(struct mount *vfsp, int lktype, struct vnode **vpp) 354 { 355 struct coda_mntinfo *mi = vftomi(vfsp); 356 int error; 357 struct lwp *l = curlwp; /* XXX - bnoble */ 358 CodaFid VFid; 359 static const CodaFid invalfid = INVAL_FID; 360 361 ENTRY; 362 MARK_ENTRY(CODA_ROOT_STATS); 363 364 if (vfsp == mi->mi_vfsp) { 365 if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid, sizeof(CodaFid))) 366 { /* Found valid root. */ 367 *vpp = mi->mi_rootvp; 368 /* On Mach, this is vref. On NetBSD, VOP_LOCK */ 369 vref(*vpp); 370 vn_lock(*vpp, lktype); 371 MARK_INT_SAT(CODA_ROOT_STATS); 372 return(0); 373 } 374 } 375 376 error = venus_root(vftomi(vfsp), l->l_cred, l->l_proc, &VFid); 377 378 if (!error) { 379 struct cnode *cp = VTOC(mi->mi_rootvp); 380 381 /* 382 * Save the new rootfid in the cnode, and rekey the cnode 383 * with the new fid key. 384 */ 385 error = vcache_rekey_enter(vfsp, mi->mi_rootvp, 386 &invalfid, sizeof(CodaFid), &VFid, sizeof(CodaFid)); 387 if (error) 388 goto exit; 389 cp->c_fid = VFid; 390 vcache_rekey_exit(vfsp, mi->mi_rootvp, 391 &invalfid, sizeof(CodaFid), &cp->c_fid, sizeof(CodaFid)); 392 393 *vpp = mi->mi_rootvp; 394 vref(*vpp); 395 vn_lock(*vpp, lktype); 396 MARK_INT_SAT(CODA_ROOT_STATS); 397 goto exit; 398 } else if (error == ENODEV || error == EINTR) { 399 /* Gross hack here! */ 400 /* 401 * If Venus fails to respond to the CODA_ROOT call, coda_call returns 402 * ENODEV. Return the uninitialized root vnode to allow vfs 403 * operations such as unmount to continue. Without this hack, 404 * there is no way to do an unmount if Venus dies before a 405 * successful CODA_ROOT call is done. All vnode operations 406 * will fail. 407 */ 408 *vpp = mi->mi_rootvp; 409 vref(*vpp); 410 vn_lock(*vpp, lktype); 411 MARK_INT_FAIL(CODA_ROOT_STATS); 412 error = 0; 413 goto exit; 414 } else { 415 CODADEBUG( CODA_ROOT, myprintf(("error %d in CODA_ROOT\n", error)); ); 416 MARK_INT_FAIL(CODA_ROOT_STATS); 417 418 goto exit; 419 } 420 exit: 421 return(error); 422 } 423 424 /* 425 * Get file system statistics. 426 */ 427 int 428 coda_nb_statvfs(struct mount *vfsp, struct statvfs *sbp) 429 { 430 struct lwp *l = curlwp; 431 struct coda_statfs fsstat; 432 int error; 433 434 ENTRY; 435 MARK_ENTRY(CODA_STATFS_STATS); 436 if (!CODA_MOUNTED(vfsp)) { 437 /* MARK_INT_FAIL(CODA_STATFS_STATS); */ 438 return(EINVAL); 439 } 440 441 /* XXX - what to do about f_flags, others? --bnoble */ 442 /* Below This is what AFS does 443 #define NB_SFS_SIZ 0x895440 444 */ 445 /* Note: Normal fs's have a bsize of 0x400 == 1024 */ 446 447 error = venus_statfs(vftomi(vfsp), l->l_cred, l, &fsstat); 448 449 if (!error) { 450 sbp->f_bsize = 8192; /* XXX */ 451 sbp->f_frsize = 8192; /* XXX */ 452 sbp->f_iosize = 8192; /* XXX */ 453 sbp->f_blocks = fsstat.f_blocks; 454 sbp->f_bfree = fsstat.f_bfree; 455 sbp->f_bavail = fsstat.f_bavail; 456 sbp->f_bresvd = 0; 457 sbp->f_files = fsstat.f_files; 458 sbp->f_ffree = fsstat.f_ffree; 459 sbp->f_favail = fsstat.f_ffree; 460 sbp->f_fresvd = 0; 461 copy_statvfs_info(sbp, vfsp); 462 } 463 464 MARK_INT_SAT(CODA_STATFS_STATS); 465 return(error); 466 } 467 468 /* 469 * Flush any pending I/O. 470 */ 471 int 472 coda_sync(struct mount *vfsp, int waitfor, 473 kauth_cred_t cred) 474 { 475 ENTRY; 476 MARK_ENTRY(CODA_SYNC_STATS); 477 MARK_INT_SAT(CODA_SYNC_STATS); 478 return(0); 479 } 480 481 int 482 coda_vget(struct mount *vfsp, ino_t ino, int lktype, 483 struct vnode **vpp) 484 { 485 ENTRY; 486 return (EOPNOTSUPP); 487 } 488 489 int 490 coda_loadvnode(struct mount *mp, struct vnode *vp, 491 const void *key, size_t key_len, const void **new_key) 492 { 493 CodaFid fid; 494 struct cnode *cp; 495 extern int (**coda_vnodeop_p)(void *); 496 497 KASSERT(key_len == sizeof(CodaFid)); 498 memcpy(&fid, key, key_len); 499 500 cp = kmem_zalloc(sizeof(*cp), KM_SLEEP); 501 mutex_init(&cp->c_lock, MUTEX_DEFAULT, IPL_NONE); 502 cp->c_fid = fid; 503 cp->c_vnode = vp; 504 vp->v_op = coda_vnodeop_p; 505 vp->v_tag = VT_CODA; 506 vp->v_type = VNON; 507 vp->v_data = cp; 508 509 *new_key = &cp->c_fid; 510 511 return 0; 512 } 513 514 /* 515 * fhtovp is now what vget used to be in 4.3-derived systems. For 516 * some silly reason, vget is now keyed by a 32 bit ino_t, rather than 517 * a type-specific fid. 518 */ 519 int 520 coda_fhtovp(struct mount *vfsp, struct fid *fhp, struct mbuf *nam, 521 struct vnode **vpp, int *exflagsp, 522 kauth_cred_t *creadanonp, int lktype) 523 { 524 struct cfid *cfid = (struct cfid *)fhp; 525 struct cnode *cp = 0; 526 int error; 527 struct lwp *l = curlwp; /* XXX -mach */ 528 CodaFid VFid; 529 int vtype; 530 531 ENTRY; 532 533 MARK_ENTRY(CODA_VGET_STATS); 534 /* Check for vget of control object. */ 535 if (IS_CTL_FID(&cfid->cfid_fid)) { 536 *vpp = coda_ctlvp; 537 vref(coda_ctlvp); 538 MARK_INT_SAT(CODA_VGET_STATS); 539 return(0); 540 } 541 542 error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, l->l_cred, l->l_proc, &VFid, &vtype); 543 544 if (error) { 545 CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error));) 546 *vpp = (struct vnode *)0; 547 } else { 548 CODADEBUG(CODA_VGET, 549 myprintf(("vget: %s type %d result %d\n", 550 coda_f2s(&VFid), vtype, error)); ) 551 552 cp = make_coda_node(&VFid, vfsp, vtype); 553 *vpp = CTOV(cp); 554 } 555 return(error); 556 } 557 558 int 559 coda_vptofh(struct vnode *vnp, struct fid *fidp) 560 { 561 ENTRY; 562 return (EOPNOTSUPP); 563 } 564 565 void 566 coda_init(void) 567 { 568 ENTRY; 569 } 570 571 void 572 coda_done(void) 573 { 574 ENTRY; 575 } 576 577 SYSCTL_SETUP(sysctl_vfs_coda_setup, "sysctl vfs.coda subtree setup") 578 { 579 580 sysctl_createv(clog, 0, NULL, NULL, 581 CTLFLAG_PERMANENT, 582 CTLTYPE_NODE, "coda", 583 SYSCTL_DESCR("code vfs options"), 584 NULL, 0, NULL, 0, 585 CTL_VFS, 18, CTL_EOL); 586 /* 587 * XXX the "18" above could be dynamic, thereby eliminating 588 * one more instance of the "number to vfs" mapping problem, 589 * but "18" is the order as taken from sys/mount.h 590 */ 591 592 /* 593 sysctl_createv(clog, 0, NULL, NULL, 594 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 595 CTLTYPE_INT, "clusterread", 596 SYSCTL_DESCR( anyone? ), 597 NULL, 0, &doclusterread, 0, 598 CTL_VFS, 18, FFS_CLUSTERREAD, CTL_EOL); 599 */ 600 } 601 602 /* 603 * To allow for greater ease of use, some vnodes may be orphaned when 604 * Venus dies. Certain operations should still be allowed to go 605 * through, but without propagating orphan-ness. So this function will 606 * get a new vnode for the file from the current run of Venus. 607 */ 608 609 int 610 getNewVnode(struct vnode **vpp) 611 { 612 struct cfid cfid; 613 struct coda_mntinfo *mi = vftomi((*vpp)->v_mount); 614 615 ENTRY; 616 617 cfid.cfid_len = (short)sizeof(CodaFid); 618 cfid.cfid_fid = VTOC(*vpp)->c_fid; /* Structure assignment. */ 619 /* XXX ? */ 620 621 /* We're guessing that if set, the 1st element on the list is a 622 * valid vnode to use. If not, return ENODEV as venus is dead. 623 */ 624 if (mi->mi_vfsp == NULL) 625 return ENODEV; 626 627 return coda_fhtovp(mi->mi_vfsp, (struct fid*)&cfid, NULL, vpp, 628 NULL, NULL, LK_EXCLUSIVE); 629 } 630 631 /* Get the mount structure corresponding to a given device. 632 * Return NULL if no device is found or the device is not mounted. 633 */ 634 struct mount *devtomp(dev_t dev) 635 { 636 struct mount *mp; 637 struct vnode *vp; 638 639 if (spec_node_lookup_by_dev(VBLK, dev, &vp) == 0) { 640 mp = spec_node_getmountedfs(vp); 641 vrele(vp); 642 } else { 643 mp = NULL; 644 } 645 646 return mp; 647 } 648