coda_vfsops.c revision 175202
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 175202 2008-01-10 01:10:58Z attilio $"); 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 <fs/coda/coda.h> 57#include <fs/coda/cnode.h> 58#include <fs/coda/coda_vfsops.h> 59#include <fs/coda/coda_venus.h> 60#include <fs/coda/coda_subr.h> 61#include <fs/coda/coda_opstats.h> 62 63MALLOC_DEFINE(M_CODA, "coda", "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 MARK_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 * Initialize the mount record and link it to the vfs struct 157 */ 158 mi = dev2coda_mntinfo(dev); 159 if (!mi) { 160 MARK_INT_FAIL(CODA_MOUNT_STATS); 161 printf("Coda mount: %s is not a cfs device\n", from); 162 return(ENXIO); 163 } 164 165 if (!VC_OPEN(&mi->mi_vcomm)) { 166 MARK_INT_FAIL(CODA_MOUNT_STATS); 167 return(ENODEV); 168 } 169 170 /* No initialization (here) of mi_vcomm! */ 171 vfsp->mnt_data = mi; 172 vfs_getnewfsid (vfsp); 173 174 mi->mi_vfsp = vfsp; 175 mi->mi_started = 0; /* XXX See coda_root() */ 176 177 /* 178 * Make a root vnode to placate the Vnode interface, but don't 179 * actually make the CODA_ROOT call to venus until the first call 180 * to coda_root in case a server is down while venus is starting. 181 */ 182 cp = make_coda_node(&rootfid, vfsp, VDIR); 183 rootvp = CTOV(cp); 184 rootvp->v_vflag |= VV_ROOT; 185 186 cp = make_coda_node(&ctlfid, vfsp, VREG); 187 coda_ctlvp = CTOV(cp); 188 189 /* Add vfs and rootvp to chain of vfs hanging off mntinfo */ 190 mi->mi_vfsp = vfsp; 191 mi->mi_rootvp = rootvp; 192 193 vfs_mountedfrom(vfsp, from); 194 /* error is currently guaranteed to be zero, but in case some 195 code changes... */ 196 CODADEBUG(1, 197 myprintf(("coda_omount returned %d\n",error));); 198 if (error) 199 MARK_INT_FAIL(CODA_MOUNT_STATS); 200 else 201 MARK_INT_SAT(CODA_MOUNT_STATS); 202 203 return(error); 204} 205 206int 207coda_unmount(vfsp, mntflags, td) 208 struct mount *vfsp; 209 int mntflags; 210 struct thread *td; 211{ 212 struct coda_mntinfo *mi = vftomi(vfsp); 213 int active, error = 0; 214 215 ENTRY; 216 MARK_ENTRY(CODA_UMOUNT_STATS); 217 if (!CODA_MOUNTED(vfsp)) { 218 MARK_INT_FAIL(CODA_UMOUNT_STATS); 219 return(EINVAL); 220 } 221 222 if (mi->mi_vfsp == vfsp) { /* We found the victim */ 223 if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp))) 224 return (EBUSY); /* Venus is still running */ 225 226#ifdef DEBUG 227 printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp, VTOC(mi->mi_rootvp)); 228#endif 229 vrele(mi->mi_rootvp); 230 vrele(coda_ctlvp); 231 active = coda_kill(vfsp, NOT_DOWNCALL); 232 ASSERT_VOP_LOCKED(mi->mi_rootvp, "coda_unmount"); 233 mi->mi_rootvp->v_vflag &= ~VV_ROOT; 234 error = vflush(mi->mi_vfsp, 0, FORCECLOSE, td); 235#ifdef CODA_VERBOSE 236 printf("coda_unmount: active = %d, vflush active %d\n", active, error); 237#endif 238 error = 0; 239 /* I'm going to take this out to allow lookups to go through. I'm 240 * not sure it's important anyway. -- DCS 2/2/94 241 */ 242 /* vfsp->VFS_DATA = NULL; */ 243 244 /* No more vfsp's to hold onto */ 245 mi->mi_vfsp = NULL; 246 mi->mi_rootvp = NULL; 247 248 if (error) 249 MARK_INT_FAIL(CODA_UMOUNT_STATS); 250 else 251 MARK_INT_SAT(CODA_UMOUNT_STATS); 252 253 return(error); 254 } 255 return (EINVAL); 256} 257 258/* 259 * find root of cfs 260 */ 261int 262coda_root(vfsp, flags, vpp, td) 263 struct mount *vfsp; 264 int flags; 265 struct vnode **vpp; 266 struct thread *td; 267{ 268 struct coda_mntinfo *mi = vftomi(vfsp); 269 struct vnode **result; 270 int error; 271 struct proc *p = td->td_proc; 272 CodaFid VFid; 273 static const CodaFid invalfid = INVAL_FID; 274 275 ENTRY; 276 MARK_ENTRY(CODA_ROOT_STATS); 277 result = NULL; 278 279 if (vfsp == mi->mi_vfsp) { 280 /* 281 * Cache the root across calls. We only need to pass the request 282 * on to Venus if the root vnode is the dummy we installed in 283 * coda_omount() with all c_fid members zeroed. 284 * 285 * XXX In addition, we assume that the first call to coda_root() 286 * is from vfs_omount() 287 * (before the call to checkdirs()) and return the dummy root 288 * node to avoid a deadlock. This bug is fixed in the Coda CVS 289 * repository but not in any released versions as of 6 Mar 2003. 290 */ 291 if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid, 292 sizeof(CodaFid)) != 0 || mi->mi_started == 0) 293 { /* Found valid root. */ 294 *vpp = mi->mi_rootvp; 295 mi->mi_started = 1; 296 297 /* On Mach, this is vref. On NetBSD, VOP_LOCK */ 298#if 1 299 vref(*vpp); 300 vn_lock(*vpp, LK_EXCLUSIVE); 301#else 302 vget(*vpp, LK_EXCLUSIVE, td); 303#endif 304 MARK_INT_SAT(CODA_ROOT_STATS); 305 return(0); 306 } 307 } 308 309 error = venus_root(vftomi(vfsp), td->td_ucred, p, &VFid); 310 311 if (!error) { 312 /* 313 * Save the new rootfid in the cnode, and rehash the cnode into the 314 * cnode hash with the new fid key. 315 */ 316 coda_unsave(VTOC(mi->mi_rootvp)); 317 VTOC(mi->mi_rootvp)->c_fid = VFid; 318 coda_save(VTOC(mi->mi_rootvp)); 319 320 *vpp = mi->mi_rootvp; 321#if 1 322 vref(*vpp); 323 vn_lock(*vpp, LK_EXCLUSIVE); 324#else 325 vget(*vpp, LK_EXCLUSIVE, td); 326#endif 327 328 MARK_INT_SAT(CODA_ROOT_STATS); 329 goto exit; 330 } else if (error == ENODEV || error == EINTR) { 331 /* Gross hack here! */ 332 /* 333 * If Venus fails to respond to the CODA_ROOT call, coda_call returns 334 * ENODEV. Return the uninitialized root vnode to allow vfs 335 * operations such as unmount to continue. Without this hack, 336 * there is no way to do an unmount if Venus dies before a 337 * successful CODA_ROOT call is done. All vnode operations 338 * will fail. 339 */ 340 *vpp = mi->mi_rootvp; 341#if 1 342 vref(*vpp); 343 vn_lock(*vpp, LK_EXCLUSIVE); 344#else 345 vget(*vpp, LK_EXCLUSIVE, td); 346#endif 347 348 MARK_INT_FAIL(CODA_ROOT_STATS); 349 error = 0; 350 goto exit; 351 } else { 352 CODADEBUG( CODA_ROOT, myprintf(("error %d in CODA_ROOT\n", error)); ); 353 MARK_INT_FAIL(CODA_ROOT_STATS); 354 355 goto exit; 356 } 357 358 exit: 359 return(error); 360} 361 362/* 363 * Get filesystem statistics. 364 */ 365int 366coda_nb_statfs(vfsp, sbp, td) 367 register struct mount *vfsp; 368 struct statfs *sbp; 369 struct thread *td; 370{ 371 ENTRY; 372/* MARK_ENTRY(CODA_STATFS_STATS); */ 373 if (!CODA_MOUNTED(vfsp)) { 374/* MARK_INT_FAIL(CODA_STATFS_STATS);*/ 375 return(EINVAL); 376 } 377 378 bzero(sbp, sizeof(struct statfs)); 379 /* XXX - what to do about f_flags, others? --bnoble */ 380 /* Below This is what AFS does 381 #define NB_SFS_SIZ 0x895440 382 */ 383 /* Note: Normal fs's have a bsize of 0x400 == 1024 */ 384 sbp->f_type = vfsp->mnt_vfc->vfc_typenum; 385 sbp->f_bsize = 8192; /* XXX */ 386 sbp->f_iosize = 8192; /* XXX */ 387#define NB_SFS_SIZ 0x8AB75D 388 sbp->f_blocks = NB_SFS_SIZ; 389 sbp->f_bfree = NB_SFS_SIZ; 390 sbp->f_bavail = NB_SFS_SIZ; 391 sbp->f_files = NB_SFS_SIZ; 392 sbp->f_ffree = NB_SFS_SIZ; 393 bcopy((caddr_t)&(vfsp->mnt_stat.f_fsid), (caddr_t)&(sbp->f_fsid), sizeof (fsid_t)); 394 snprintf(sbp->f_mntonname, sizeof(sbp->f_mntonname), "/coda"); 395 snprintf(sbp->f_fstypename, sizeof(sbp->f_fstypename), "coda"); 396/* MARK_INT_SAT(CODA_STATFS_STATS); */ 397 return(0); 398} 399 400/* 401 * Flush any pending I/O. 402 */ 403int 404coda_sync(vfsp, waitfor, td) 405 struct mount *vfsp; 406 int waitfor; 407 struct thread *td; 408{ 409 ENTRY; 410 MARK_ENTRY(CODA_SYNC_STATS); 411 MARK_INT_SAT(CODA_SYNC_STATS); 412 return(0); 413} 414 415/* 416 * fhtovp is now what vget used to be in 4.3-derived systems. For 417 * some silly reason, vget is now keyed by a 32 bit ino_t, rather than 418 * a type-specific fid. 419 */ 420int 421coda_fhtovp(vfsp, fhp, nam, vpp, exflagsp, creadanonp) 422 register struct mount *vfsp; 423 struct fid *fhp; 424 struct mbuf *nam; 425 struct vnode **vpp; 426 int *exflagsp; 427 struct ucred **creadanonp; 428{ 429 struct cfid *cfid = (struct cfid *)fhp; 430 struct cnode *cp = 0; 431 int error; 432 struct thread *td = curthread; /* XXX -mach */ 433 struct proc *p = td->td_proc; 434 CodaFid VFid; 435 int vtype; 436 437 ENTRY; 438 439 MARK_ENTRY(CODA_VGET_STATS); 440 /* Check for vget of control object. */ 441 if (IS_CTL_FID(&cfid->cfid_fid)) { 442 *vpp = coda_ctlvp; 443 vref(coda_ctlvp); 444 MARK_INT_SAT(CODA_VGET_STATS); 445 return(0); 446 } 447 448 error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, td->td_ucred, p, &VFid, &vtype); 449 450 if (error) { 451 CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error));) 452 *vpp = (struct vnode *)0; 453 } else { 454 CODADEBUG(CODA_VGET, 455 myprintf(("vget: %s type %d result %d\n", 456 coda_f2s(&VFid), vtype, error)); ) 457 cp = make_coda_node(&VFid, vfsp, vtype); 458 *vpp = CTOV(cp); 459 } 460 return(error); 461} 462 463/* 464 * To allow for greater ease of use, some vnodes may be orphaned when 465 * Venus dies. Certain operations should still be allowed to go 466 * through, but without propagating ophan-ness. So this function will 467 * get a new vnode for the file from the current run of Venus. */ 468 469int 470getNewVnode(vpp) 471 struct vnode **vpp; 472{ 473 struct cfid cfid; 474 struct coda_mntinfo *mi = vftomi((*vpp)->v_mount); 475 476 ENTRY; 477 478 cfid.cfid_len = (short)sizeof(CodaFid); 479 cfid.cfid_fid = VTOC(*vpp)->c_fid; /* Structure assignment. */ 480 /* XXX ? */ 481 482 /* We're guessing that if set, the 1st element on the list is a 483 * valid vnode to use. If not, return ENODEV as venus is dead. 484 */ 485 if (mi->mi_vfsp == NULL) 486 return ENODEV; 487 488 return coda_fhtovp(mi->mi_vfsp, (struct fid*)&cfid, NULL, vpp, 489 NULL, NULL); 490} 491 492#include <ufs/ufs/extattr.h> 493#include <ufs/ufs/quota.h> 494#include <ufs/ufs/ufsmount.h> 495/* get the mount structure corresponding to a given device. Assume 496 * device corresponds to a UFS. Return NULL if no device is found. 497 */ 498struct mount *devtomp(dev) 499 struct cdev *dev; 500{ 501 struct mount *mp; 502 503 TAILQ_FOREACH(mp, &mountlist, mnt_list) { 504 if (((VFSTOUFS(mp))->um_dev == dev)) { 505 /* mount corresponds to UFS and the device matches one we want */ 506 return(mp); 507 } 508 } 509 /* mount structure wasn't found */ 510 return(NULL); 511} 512 513struct vfsops coda_vfsops = { 514 .vfs_mount = coda_mount, 515 .vfs_root = coda_root, 516 .vfs_statfs = coda_nb_statfs, 517 .vfs_sync = coda_sync, 518 .vfs_unmount = coda_unmount, 519}; 520 521VFS_SET(coda_vfsops, coda, VFCF_NETWORK); 522