coda_vfsops.c revision 139745
1/*- 2 * Coda: an Experimental Distributed File System 3 * Release 3.1 4 * 5 * Copyright (c) 1987-1998 Carnegie Mellon University 6 * All Rights Reserved 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation, and 13 * that credit is given to Carnegie Mellon University in all documents 14 * and publicity pertaining to direct or indirect use of this code or its 15 * derivatives. 16 * 17 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS, 18 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS 19 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON 20 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER 21 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF 22 * ANY DERIVATIVE WORK. 23 * 24 * Carnegie Mellon encourages users of this software to return any 25 * improvements or extensions that they make, and to grant Carnegie 26 * Mellon the rights to redistribute these changes without encumbrance. 27 * 28 * @(#) src/sys/cfs/coda_vfsops.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $ 29 */ 30/*- 31 * Mach Operating System 32 * Copyright (c) 1989 Carnegie-Mellon University 33 * All rights reserved. The CMU software License Agreement specifies 34 * the terms and conditions for use and redistribution. 35 */ 36 37/* 38 * This code was written for the Coda filesystem at Carnegie Mellon 39 * University. Contributers include David Steere, James Kistler, and 40 * M. Satyanarayanan. 41 */ 42 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: head/sys/fs/coda/coda_vfsops.c 139745 2005-01-05 23:35:00Z imp $"); 45 46#include <sys/param.h> 47#include <sys/systm.h> 48#include <sys/conf.h> 49#include <sys/kernel.h> 50#include <sys/lock.h> 51#include <sys/malloc.h> 52#include <sys/mount.h> 53#include <sys/namei.h> 54#include <sys/proc.h> 55 56#include <coda/coda.h> 57#include <coda/cnode.h> 58#include <coda/coda_vfsops.h> 59#include <coda/coda_venus.h> 60#include <coda/coda_subr.h> 61#include <coda/coda_opstats.h> 62 63MALLOC_DEFINE(M_CODA, "CODA storage", "Various Coda Structures"); 64 65int codadebug = 0; 66int coda_vfsop_print_entry = 0; 67#define ENTRY if(coda_vfsop_print_entry) myprintf(("Entered %s\n",__func__)) 68 69struct vnode *coda_ctlvp; 70 71/* structure to keep statistics of internally generated/satisfied calls */ 72 73struct coda_op_stats coda_vfsopstats[CODA_VFSOPS_SIZE]; 74 75#define MARK_ENTRY(op) (coda_vfsopstats[op].entries++) 76#define MARK_INT_SAT(op) (coda_vfsopstats[op].sat_intrn++) 77#define MARK_INT_FAIL(op) (coda_vfsopstats[op].unsat_intrn++) 78#define MRAK_INT_GEN(op) (coda_vfsopstats[op].gen_intrn++) 79 80extern int coda_nc_initialized; /* Set if cache has been initialized */ 81extern int vc_nb_open(struct cdev *, int, int, struct thread *); 82 83int 84coda_vfsopstats_init(void) 85{ 86 register int i; 87 88 for (i=0;i<CODA_VFSOPS_SIZE;i++) { 89 coda_vfsopstats[i].opcode = i; 90 coda_vfsopstats[i].entries = 0; 91 coda_vfsopstats[i].sat_intrn = 0; 92 coda_vfsopstats[i].unsat_intrn = 0; 93 coda_vfsopstats[i].gen_intrn = 0; 94 } 95 96 return 0; 97} 98 99static const char *coda_opts[] = { "from", NULL }; 100/* 101 * cfs mount vfsop 102 * Set up mount info record and attach it to vfs struct. 103 */ 104/*ARGSUSED*/ 105int 106coda_mount(struct mount *vfsp, struct thread *td) 107{ 108 struct vnode *dvp; 109 struct cnode *cp; 110 struct cdev *dev; 111 struct coda_mntinfo *mi; 112 struct vnode *rootvp; 113 CodaFid rootfid = INVAL_FID; 114 CodaFid ctlfid = CTL_FID; 115 int error; 116 struct nameidata ndp; 117 ENTRY; 118 char *from; 119 120 if (vfs_filteropt(vfsp->mnt_optnew, coda_opts)) 121 return (EINVAL); 122 123 from = vfs_getopts(vfsp->mnt_optnew, "from", &error); 124 if (error) 125 return (error); 126 127 coda_vfsopstats_init(); 128 coda_vnodeopstats_init(); 129 130 MARK_ENTRY(CODA_MOUNT_STATS); 131 if (CODA_MOUNTED(vfsp)) { 132 MARK_INT_FAIL(CODA_MOUNT_STATS); 133 return(EBUSY); 134 } 135 136 /* Validate mount device. Similar to getmdev(). */ 137 NDINIT(&ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, from, td); 138 error = namei(&ndp); 139 dvp = ndp.ni_vp; 140 141 if (error) { 142 MARK_INT_FAIL(CODA_MOUNT_STATS); 143 return (error); 144 } 145 if (dvp->v_type != VCHR) { 146 MARK_INT_FAIL(CODA_MOUNT_STATS); 147 vrele(dvp); 148 NDFREE(&ndp, NDF_ONLY_PNBUF); 149 return(ENXIO); 150 } 151 dev = dvp->v_rdev; 152 vrele(dvp); 153 NDFREE(&ndp, NDF_ONLY_PNBUF); 154 155 /* 156 * See if the device table matches our expectations. 157 */ 158 if (dev->si_devsw->d_open != vc_nb_open) 159 { 160 MARK_INT_FAIL(CODA_MOUNT_STATS); 161 return(ENXIO); 162 } 163 164 /* 165 * Initialize the mount record and link it to the vfs struct 166 */ 167 mi = dev2coda_mntinfo(dev); 168 169 if (!VC_OPEN(&mi->mi_vcomm)) { 170 MARK_INT_FAIL(CODA_MOUNT_STATS); 171 return(ENODEV); 172 } 173 174 /* No initialization (here) of mi_vcomm! */ 175 vfsp->mnt_data = (qaddr_t)mi; 176 vfs_getnewfsid (vfsp); 177 178 mi->mi_vfsp = vfsp; 179 mi->mi_started = 0; /* XXX See coda_root() */ 180 181 /* 182 * Make a root vnode to placate the Vnode interface, but don't 183 * actually make the CODA_ROOT call to venus until the first call 184 * to coda_root in case a server is down while venus is starting. 185 */ 186 cp = make_coda_node(&rootfid, vfsp, VDIR); 187 rootvp = CTOV(cp); 188 rootvp->v_vflag |= VV_ROOT; 189 190/* cp = make_coda_node(&ctlfid, vfsp, VCHR); 191 The above code seems to cause a loop in the cnode links. 192 I don't totally understand when it happens, it is caught 193 when closing down the system. 194 */ 195 cp = make_coda_node(&ctlfid, 0, VCHR); 196 197 coda_ctlvp = CTOV(cp); 198 199 /* Add vfs and rootvp to chain of vfs hanging off mntinfo */ 200 mi->mi_vfsp = vfsp; 201 mi->mi_rootvp = rootvp; 202 203 vfs_mountedfrom(vfsp, from); 204 /* error is currently guaranteed to be zero, but in case some 205 code changes... */ 206 CODADEBUG(1, 207 myprintf(("coda_omount returned %d\n",error));); 208 if (error) 209 MARK_INT_FAIL(CODA_MOUNT_STATS); 210 else 211 MARK_INT_SAT(CODA_MOUNT_STATS); 212 213 return(error); 214} 215 216int 217coda_unmount(vfsp, mntflags, td) 218 struct mount *vfsp; 219 int mntflags; 220 struct thread *td; 221{ 222 struct coda_mntinfo *mi = vftomi(vfsp); 223 int active, error = 0; 224 225 ENTRY; 226 MARK_ENTRY(CODA_UMOUNT_STATS); 227 if (!CODA_MOUNTED(vfsp)) { 228 MARK_INT_FAIL(CODA_UMOUNT_STATS); 229 return(EINVAL); 230 } 231 232 if (mi->mi_vfsp == vfsp) { /* We found the victim */ 233 if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp))) 234 return (EBUSY); /* Venus is still running */ 235 236#ifdef DEBUG 237 printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp, VTOC(mi->mi_rootvp)); 238#endif 239 vrele(mi->mi_rootvp); 240 active = coda_kill(vfsp, NOT_DOWNCALL); 241 ASSERT_VOP_LOCKED(mi->mi_rootvp, "coda_unmount"); 242 mi->mi_rootvp->v_vflag &= ~VV_ROOT; 243 error = vflush(mi->mi_vfsp, 0, FORCECLOSE, td); 244#ifdef CODA_VERBOSE 245 printf("coda_unmount: active = %d, vflush active %d\n", active, error); 246#endif 247 error = 0; 248 /* I'm going to take this out to allow lookups to go through. I'm 249 * not sure it's important anyway. -- DCS 2/2/94 250 */ 251 /* vfsp->VFS_DATA = NULL; */ 252 253 /* No more vfsp's to hold onto */ 254 mi->mi_vfsp = NULL; 255 mi->mi_rootvp = NULL; 256 257 if (error) 258 MARK_INT_FAIL(CODA_UMOUNT_STATS); 259 else 260 MARK_INT_SAT(CODA_UMOUNT_STATS); 261 262 return(error); 263 } 264 return (EINVAL); 265} 266 267/* 268 * find root of cfs 269 */ 270int 271coda_root(vfsp, vpp, td) 272 struct mount *vfsp; 273 struct vnode **vpp; 274 struct thread *td; 275{ 276 struct coda_mntinfo *mi = vftomi(vfsp); 277 struct vnode **result; 278 int error; 279 struct proc *p = td->td_proc; 280 CodaFid VFid; 281 static const CodaFid invalfid = INVAL_FID; 282 283 ENTRY; 284 MARK_ENTRY(CODA_ROOT_STATS); 285 result = NULL; 286 287 if (vfsp == mi->mi_vfsp) { 288 /* 289 * Cache the root across calls. We only need to pass the request 290 * on to Venus if the root vnode is the dummy we installed in 291 * coda_omount() with all c_fid members zeroed. 292 * 293 * XXX In addition, if we are called between coda_omount() and 294 * coda_start(), we assume that the request is from vfs_omount() 295 * (before the call to checkdirs()) and return the dummy root 296 * node to avoid a deadlock. This bug is fixed in the Coda CVS 297 * repository but not in any released versions as of 6 Mar 2003. 298 */ 299 if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid, 300 sizeof(CodaFid)) != 0 || mi->mi_started == 0) 301 { /* Found valid root. */ 302 *vpp = mi->mi_rootvp; 303 /* On Mach, this is vref. On NetBSD, VOP_LOCK */ 304#if 1 305 vref(*vpp); 306 vn_lock(*vpp, LK_EXCLUSIVE, td); 307#else 308 vget(*vpp, LK_EXCLUSIVE, td); 309#endif 310 MARK_INT_SAT(CODA_ROOT_STATS); 311 return(0); 312 } 313 } 314 315 error = venus_root(vftomi(vfsp), td->td_ucred, p, &VFid); 316 317 if (!error) { 318 /* 319 * Save the new rootfid in the cnode, and rehash the cnode into the 320 * cnode hash with the new fid key. 321 */ 322 coda_unsave(VTOC(mi->mi_rootvp)); 323 VTOC(mi->mi_rootvp)->c_fid = VFid; 324 coda_save(VTOC(mi->mi_rootvp)); 325 326 *vpp = mi->mi_rootvp; 327#if 1 328 vref(*vpp); 329 vn_lock(*vpp, LK_EXCLUSIVE, td); 330#else 331 vget(*vpp, LK_EXCLUSIVE, td); 332#endif 333 334 MARK_INT_SAT(CODA_ROOT_STATS); 335 goto exit; 336 } else if (error == ENODEV || error == EINTR) { 337 /* Gross hack here! */ 338 /* 339 * If Venus fails to respond to the CODA_ROOT call, coda_call returns 340 * ENODEV. Return the uninitialized root vnode to allow vfs 341 * operations such as unmount to continue. Without this hack, 342 * there is no way to do an unmount if Venus dies before a 343 * successful CODA_ROOT call is done. All vnode operations 344 * will fail. 345 */ 346 *vpp = mi->mi_rootvp; 347#if 1 348 vref(*vpp); 349 vn_lock(*vpp, LK_EXCLUSIVE, td); 350#else 351 vget(*vpp, LK_EXCLUSIVE, td); 352#endif 353 354 MARK_INT_FAIL(CODA_ROOT_STATS); 355 error = 0; 356 goto exit; 357 } else { 358 CODADEBUG( CODA_ROOT, myprintf(("error %d in CODA_ROOT\n", error)); ); 359 MARK_INT_FAIL(CODA_ROOT_STATS); 360 361 goto exit; 362 } 363 364 exit: 365 return(error); 366} 367 368int 369coda_start(mp, flags, td) 370 struct mount *mp; 371 int flags; 372 struct thread *td; 373{ 374 375 /* XXX See coda_root(). */ 376 vftomi(mp)->mi_started = 1; 377 return (0); 378} 379 380/* 381 * Get filesystem statistics. 382 */ 383int 384coda_nb_statfs(vfsp, sbp, td) 385 register struct mount *vfsp; 386 struct statfs *sbp; 387 struct thread *td; 388{ 389 ENTRY; 390/* MARK_ENTRY(CODA_STATFS_STATS); */ 391 if (!CODA_MOUNTED(vfsp)) { 392/* MARK_INT_FAIL(CODA_STATFS_STATS);*/ 393 return(EINVAL); 394 } 395 396 bzero(sbp, sizeof(struct statfs)); 397 /* XXX - what to do about f_flags, others? --bnoble */ 398 /* Below This is what AFS does 399 #define NB_SFS_SIZ 0x895440 400 */ 401 /* Note: Normal fs's have a bsize of 0x400 == 1024 */ 402 sbp->f_type = vfsp->mnt_vfc->vfc_typenum; 403 sbp->f_bsize = 8192; /* XXX */ 404 sbp->f_iosize = 8192; /* XXX */ 405#define NB_SFS_SIZ 0x8AB75D 406 sbp->f_blocks = NB_SFS_SIZ; 407 sbp->f_bfree = NB_SFS_SIZ; 408 sbp->f_bavail = NB_SFS_SIZ; 409 sbp->f_files = NB_SFS_SIZ; 410 sbp->f_ffree = NB_SFS_SIZ; 411 bcopy((caddr_t)&(vfsp->mnt_stat.f_fsid), (caddr_t)&(sbp->f_fsid), sizeof (fsid_t)); 412 snprintf(sbp->f_mntonname, sizeof(sbp->f_mntonname), "/coda"); 413 snprintf(sbp->f_fstypename, sizeof(sbp->f_fstypename), "coda"); 414/* MARK_INT_SAT(CODA_STATFS_STATS); */ 415 return(0); 416} 417 418/* 419 * Flush any pending I/O. 420 */ 421int 422coda_sync(vfsp, waitfor, cred, td) 423 struct mount *vfsp; 424 int waitfor; 425 struct ucred *cred; 426 struct thread *td; 427{ 428 ENTRY; 429 MARK_ENTRY(CODA_SYNC_STATS); 430 MARK_INT_SAT(CODA_SYNC_STATS); 431 return(0); 432} 433 434/* 435 * fhtovp is now what vget used to be in 4.3-derived systems. For 436 * some silly reason, vget is now keyed by a 32 bit ino_t, rather than 437 * a type-specific fid. 438 */ 439int 440coda_fhtovp(vfsp, fhp, nam, vpp, exflagsp, creadanonp) 441 register struct mount *vfsp; 442 struct fid *fhp; 443 struct mbuf *nam; 444 struct vnode **vpp; 445 int *exflagsp; 446 struct ucred **creadanonp; 447{ 448 struct cfid *cfid = (struct cfid *)fhp; 449 struct cnode *cp = 0; 450 int error; 451 struct thread *td = curthread; /* XXX -mach */ 452 struct proc *p = td->td_proc; 453 CodaFid VFid; 454 int vtype; 455 456 ENTRY; 457 458 MARK_ENTRY(CODA_VGET_STATS); 459 /* Check for vget of control object. */ 460 if (IS_CTL_FID(&cfid->cfid_fid)) { 461 *vpp = coda_ctlvp; 462 vref(coda_ctlvp); 463 MARK_INT_SAT(CODA_VGET_STATS); 464 return(0); 465 } 466 467 error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, td->td_ucred, p, &VFid, &vtype); 468 469 if (error) { 470 CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error));) 471 *vpp = (struct vnode *)0; 472 } else { 473 CODADEBUG(CODA_VGET, 474 myprintf(("vget: %s type %d result %d\n", 475 coda_f2s(&VFid), vtype, error)); ) 476 cp = make_coda_node(&VFid, vfsp, vtype); 477 *vpp = CTOV(cp); 478 } 479 return(error); 480} 481 482/* 483 * To allow for greater ease of use, some vnodes may be orphaned when 484 * Venus dies. Certain operations should still be allowed to go 485 * through, but without propagating ophan-ness. So this function will 486 * get a new vnode for the file from the current run of Venus. */ 487 488int 489getNewVnode(vpp) 490 struct vnode **vpp; 491{ 492 struct cfid cfid; 493 struct coda_mntinfo *mi = vftomi((*vpp)->v_mount); 494 495 ENTRY; 496 497 cfid.cfid_len = (short)sizeof(CodaFid); 498 cfid.cfid_fid = VTOC(*vpp)->c_fid; /* Structure assignment. */ 499 /* XXX ? */ 500 501 /* We're guessing that if set, the 1st element on the list is a 502 * valid vnode to use. If not, return ENODEV as venus is dead. 503 */ 504 if (mi->mi_vfsp == NULL) 505 return ENODEV; 506 507 return coda_fhtovp(mi->mi_vfsp, (struct fid*)&cfid, NULL, vpp, 508 NULL, NULL); 509} 510 511#include <ufs/ufs/extattr.h> 512#include <ufs/ufs/quota.h> 513#include <ufs/ufs/ufsmount.h> 514/* get the mount structure corresponding to a given device. Assume 515 * device corresponds to a UFS. Return NULL if no device is found. 516 */ 517struct mount *devtomp(dev) 518 struct cdev *dev; 519{ 520 struct mount *mp; 521 522 TAILQ_FOREACH(mp, &mountlist, mnt_list) { 523 if (((VFSTOUFS(mp))->um_dev == dev)) { 524 /* mount corresponds to UFS and the device matches one we want */ 525 return(mp); 526 } 527 } 528 /* mount structure wasn't found */ 529 return(NULL); 530} 531 532struct vfsops coda_vfsops = { 533 .vfs_mount = coda_mount, 534 .vfs_root = coda_root, 535 .vfs_start = coda_start, 536 .vfs_statfs = coda_nb_statfs, 537 .vfs_sync = coda_sync, 538 .vfs_unmount = coda_unmount, 539}; 540 541VFS_SET(coda_vfsops, coda, VFCF_NETWORK); 542