1 /* 2 * Copyright (c) 1992 The Regents of the University of California 3 * All rights reserved. 4 * 5 * This code is derived from the null layer of 6 * John Heidemann of the UCLA Ficus project and 7 * the Jan-Simon Pendry's loopback file system. 8 * 9 * %sccs.include.redist.c% 10 * 11 * @(#)null_vnops.c 1.3 (Berkeley) 07/07/92 12 * 13 * Ancestors: 14 * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92 15 * $Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $ 16 * ...and... 17 * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project 18 */ 19 20 /* 21 * Null Layer 22 * 23 * The null layer duplicates a portion of the file system 24 * name space under a new name. In this respect, it is 25 * similar to the loopback file system. It differs from 26 * the loopback fs in two respects: it is implemented using 27 * a bypass operation, and it's "null-nodes" stack above 28 * all lower-layer vnodes, not just over directory vnodes. 29 * 30 * The null layer is the minimum file system layer, 31 * simply bypassing all possible operations to the lower layer 32 * for processing there. All but vop_getattr, _inactive, _reclaim, 33 * and _print are bypassed. 34 * 35 * Vop_getattr is not bypassed so that we can change the fsid being 36 * returned. Vop_{inactive,reclaim} are bypassed so that 37 * they can handle freeing null-layer specific data. 38 * Vop_print is not bypassed for debugging. 39 * 40 * NEEDSWORK: Describe methods to invoke operations on the lower layer 41 * (bypass vs. VOP). 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/proc.h> 47 #include <sys/time.h> 48 #include <sys/types.h> 49 #include <sys/vnode.h> 50 #include <sys/mount.h> 51 #include <sys/namei.h> 52 #include <sys/malloc.h> 53 #include <sys/buf.h> 54 #include <lofs/lofs.h> 55 56 57 int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ 58 59 /* 60 * This is the 10-Apr-92 bypass routine. 61 * This version has been optimized for speed, throwing away some 62 * safety checks. It should still always work, but it's not as 63 * robust to programmer errors. 64 * Define SAFETY to include some error checking code. 65 * 66 * In general, we map all vnodes going down and unmap them on the way back. 67 * As an exception to this, vnodes can be marked "unmapped" by setting 68 * the Nth bit in operation's vdesc_flags. 69 * 70 * Also, some BSD vnode operations have the side effect of vrele'ing 71 * their arguments. With stacking, the reference counts are held 72 * by the upper node, not the lower one, so we must handle these 73 * side-effects here. This is not of concern in Sun-derived systems 74 * since there are no such side-effects. 75 * 76 * This makes the following assumptions: 77 * - only one returned vpp 78 * - no INOUT vpp's (Sun's vop_open has one of these) 79 * - the vnode operation vector of the first vnode should be used 80 * to determine what implementation of the op should be invoked 81 * - all mapped vnodes are of our vnode-type (NEEDSWORK: 82 * problems on rmdir'ing mount points and renaming?) 83 */ 84 int 85 null_bypass(ap) 86 struct nvop_generic_args *ap; 87 { 88 register int this_vp_p; 89 int error; 90 struct vnode *old_vps[VDESC_MAX_VPS]; 91 struct vnode **vps_p[VDESC_MAX_VPS]; 92 struct vnode ***vppp; 93 struct vnodeop_desc *descp = ap->a_desc; 94 int maps, reles, i; 95 96 if (null_bug_bypass) 97 printf ("null_bypass: %s\n", descp->vdesc_name); 98 99 #ifdef SAFETY 100 /* 101 * We require at least one vp. 102 */ 103 if (descp->vdesc_vp_offsets==NULL || 104 descp->vdesc_vp_offsets[0]==VDESC_NO_OFFSET) 105 panic ("null_bypass: no vp's in map.\n"); 106 #endif 107 108 /* 109 * Map the vnodes going in. 110 * Later, we'll invoke the operation based on 111 * the first mapped vnode's operation vector. 112 */ 113 maps = descp->vdesc_flags; 114 reles = descp->vdesc_rele_flags; 115 for (i=0; i<VDESC_MAX_VPS; maps>>=1, reles>>=1, i++) { 116 if (descp->vdesc_vp_offsets[i]==VDESC_NO_OFFSET) 117 break; /* bail out at end of list */ 118 if (maps & 1) /* skip vps that aren't to be mapped */ 119 continue; 120 vps_p[i] = this_vp_p = 121 VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap); 122 old_vps[i] = *this_vp_p; 123 *(vps_p[i]) = NULLTOLOWERVP(VTONULLNODE(*this_vp_p)); 124 if (reles & 1) 125 VREF(*this_vp_p); 126 127 }; 128 129 /* 130 * Call the operation on the lower layer 131 * with the modified argument structure. 132 */ 133 error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap); 134 135 /* 136 * Maintain the illusion of call-by-value 137 * by restoring vnodes in the argument structure 138 * to their original value. 139 */ 140 maps = descp->vdesc_flags; 141 reles = descp->vdesc_rele_flags; 142 for (i=0; i<VDESC_MAX_VPS; maps>>=1, i++) { 143 if (descp->vdesc_vp_offsets[i]==VDESC_NO_OFFSET) 144 break; /* bail out at end of list */ 145 if (maps & 1) /* skip vps that aren't to be mapped */ 146 continue; 147 *(vps_p[i]) = old_vps[i]; 148 if (reles & 1) 149 vrele(*(vps_p[i])); 150 }; 151 152 /* 153 * Map the possible out-going vpp. 154 */ 155 if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && 156 !(descp->vdesc_flags & VDESC_NOMAP_VPP) && 157 !error) { 158 vppp=VOPARG_OFFSETTO(struct vnode***, 159 descp->vdesc_vpp_offset,ap); 160 error = make_null_node(old_vps[0]->v_mount, **vppp, *vppp); 161 }; 162 163 return (error); 164 } 165 166 167 /* 168 * We handle getattr to change the fsid. 169 */ 170 int 171 null_getattr(ap) 172 struct nvop_getattr_args *ap; 173 { 174 int error; 175 if (error=null_bypass(ap)) 176 return error; 177 /* Requires that arguments be restored. */ 178 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; 179 return 0; 180 } 181 182 183 #if 0 184 null_rename (ap) 185 struct vop_rename_args *ap; 186 { 187 USES_VOP_RENAME; 188 struct vnode *fvp, *tvp; 189 struct vnode *tdvp; 190 #if 0 191 struct vnode *fsvp, *tsvp; 192 #endif 193 int error; 194 195 #ifdef NULLFS_DIAGNOSTIC 196 printf("null_rename(fdvp = %x->%x)\n", ap->a_fdvp, NULLTOLOWERVP(ap->a_fdvp)); 197 /*printf("null_rename(tdvp = %x->%x)\n", tndp->ni_dvp, NULLTOLOWERVP(tndp->ni_dvp));*/ 198 #endif 199 200 #ifdef NULLFS_DIAGNOSTIC 201 printf("null_rename - switch source dvp\n"); 202 #endif 203 /* 204 * Switch source directory to point to lofsed vnode 205 */ 206 PUSHREF(fdvp, ap->a_fdvp); 207 VREF(ap->a_fdvp); 208 209 #ifdef NULLFS_DIAGNOSTIC 210 printf("null_rename - switch source vp\n"); 211 #endif 212 /* 213 * And source object if it is lofsed... 214 */ 215 fvp = ap->a_fvp; 216 if (fvp && fvp->v_op == null_vnodeop_p) { 217 ap->a_fvp = NULLTOLOWERVP(fvp); 218 VREF(ap->a_fvp); 219 } else { 220 fvp = 0; 221 } 222 223 #if 0 224 #ifdef NULLFS_DIAGNOSTIC 225 printf("null_rename - switch source start vp\n"); 226 #endif 227 /* 228 * And source startdir object if it is lofsed... 229 */ 230 fsvp = fndp->ni_startdir; 231 if (fsvp && fsvp->v_op == null_vnodeop_p) { 232 fndp->ni_startdir = NULLTOLOWERVP(fsvp); 233 VREF(fndp->ni_startdir); 234 } else { 235 fsvp = 0; 236 } 237 #endif 238 239 #ifdef NULLFS_DIAGNOSTIC 240 printf("null_rename - switch target dvp\n"); 241 #endif 242 /* 243 * Switch target directory to point to lofsed vnode 244 */ 245 tdvp = ap->a_tdvp; 246 if (tdvp && tdvp->v_op == null_vnodeop_p) { 247 ap->a_tdvp = NULLTOLOWERVP(tdvp); 248 VREF(ap->a_tdvp); 249 } else { 250 tdvp = 0; 251 } 252 253 #ifdef NULLFS_DIAGNOSTIC 254 printf("null_rename - switch target vp\n"); 255 #endif 256 /* 257 * And target object if it is lofsed... 258 */ 259 tvp = ap->a_tvp; 260 if (tvp && tvp->v_op == null_vnodeop_p) { 261 ap->a_tvp = NULLTOLOWERVP(tvp); 262 VREF(ap->a_tvp); 263 } else { 264 tvp = 0; 265 } 266 267 #if 0 268 #ifdef NULLFS_DIAGNOSTIC 269 printf("null_rename - switch target start vp\n"); 270 #endif 271 /* 272 * And target startdir object if it is lofsed... 273 */ 274 tsvp = tndp->ni_startdir; 275 if (tsvp && tsvp->v_op == null_vnodeop_p) { 276 tndp->ni_startdir = NULLTOLOWERVP(fsvp); 277 VREF(tndp->ni_startdir); 278 } else { 279 tsvp = 0; 280 } 281 #endif 282 283 #ifdef NULLFS_DIAGNOSTIC 284 printf("null_rename - VOP_RENAME(%x, %x, %x, %x)\n", 285 ap->a_fdvp, ap->a_fvp, ap->a_tdvp, ap->a_tvp); 286 vprint("ap->a_fdvp", ap->a_fdvp); 287 vprint("ap->a_fvp", ap->a_fvp); 288 vprint("ap->a_tdvp", ap->a_tdvp); 289 if (ap->a_tvp) vprint("ap->a_tvp", ap->a_tvp); 290 DELAY(16000000); 291 #endif 292 293 error = VOP_RENAME(ap->a_fdvp, ap->a_fvp, ap->a_fcnp, ap->a_tdvp, ap->a_tvp, ap->a_tcnp); 294 295 /* 296 * Put everything back... 297 */ 298 299 #if 0 300 #ifdef NULLFS_DIAGNOSTIC 301 printf("null_rename - restore target startdir\n"); 302 #endif 303 304 if (tsvp) { 305 if (tndp->ni_startdir) 306 vrele(tndp->ni_startdir); 307 tndp->ni_startdir = tsvp; 308 } 309 #endif 310 311 #ifdef NULLFS_DIAGNOSTIC 312 printf("null_rename - restore target vp\n"); 313 #endif 314 315 if (tvp) { 316 ap->a_tvp = tvp; 317 vrele(ap->a_tvp); 318 } 319 320 #ifdef NULLFS_DIAGNOSTIC 321 printf("null_rename - restore target dvp\n"); 322 #endif 323 324 if (tdvp) { 325 ap->a_tdvp = tdvp; 326 vrele(ap->a_tdvp); 327 } 328 329 #if 0 330 #ifdef NULLFS_DIAGNOSTIC 331 printf("null_rename - restore source startdir\n"); 332 #endif 333 334 if (fsvp) { 335 if (fndp->ni_startdir) 336 vrele(fndp->ni_startdir); 337 fndp->ni_startdir = fsvp; 338 } 339 #endif 340 341 #ifdef NULLFS_DIAGNOSTIC 342 printf("null_rename - restore source vp\n"); 343 #endif 344 345 346 if (fvp) { 347 ap->a_fvp = fvp; 348 vrele(ap->a_fvp); 349 } 350 351 #ifdef NULLFS_DIAGNOSTIC 352 printf("null_rename - restore source dvp\n"); 353 #endif 354 355 POP(fdvp, ap->a_fdvp); 356 vrele(ap->a_fdvp); 357 358 return (error); 359 } 360 #endif 361 362 363 int 364 null_inactive (ap) 365 struct vop_inactive_args *ap; 366 { 367 #ifdef NULLFS_DIAGNOSTIC 368 printf("null_inactive(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp)); 369 #endif 370 /* 371 * Do nothing (and _don't_ bypass). 372 * Wait to vrele lowervp until reclaim, 373 * so that until then our null_node is in the 374 * cache and reusable. 375 * 376 * NEEDSWORK: Someday, consider inactive'ing 377 * the lowervp and then trying to reactivate it 378 * like they do in the name lookup cache code. 379 * That's too much work for now. 380 */ 381 return 0; 382 } 383 384 null_reclaim (ap) 385 struct vop_reclaim_args *ap; 386 { 387 USES_VOP_RECLAIM; 388 struct vnode *targetvp; 389 #ifdef NULLFS_DIAGNOSTIC 390 printf("null_reclaim(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp)); 391 #endif 392 remque(VTONULLNODE(ap->a_vp)); /* NEEDSWORK: What? */ 393 vrele (NULLTOLOWERVP(ap->a_vp)); /* release lower layer */ 394 FREE(ap->a_vp->v_data, M_TEMP); 395 ap->a_vp->v_data = 0; 396 return (0); 397 } 398 399 null_bmap (ap) 400 struct vop_bmap_args *ap; 401 { 402 USES_VOP_BMAP; 403 #ifdef NULLFS_DIAGNOSTIC 404 printf("null_bmap(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp)); 405 #endif 406 407 return VOP_BMAP(NULLTOLOWERVP(ap->a_vp), ap->a_bn, ap->a_vpp, ap->a_bnp); 408 } 409 410 null_strategy (ap) 411 struct vop_strategy_args *ap; 412 { 413 USES_VOP_STRATEGY; 414 int error; 415 struct vnode *savedvp; 416 417 #ifdef NULLFS_DIAGNOSTIC 418 printf("null_strategy(vp = %x->%x)\n", ap->a_bp->b_vp, NULLTOLOWERVP(ap->a_bp->b_vp)); 419 #endif 420 421 savedvp = ap->a_bp->b_vp; 422 423 error = VOP_STRATEGY(ap->a_bp); 424 425 ap->a_bp->b_vp = savedvp; 426 427 return error; 428 } 429 430 431 int 432 null_print (ap) 433 struct vop_print_args *ap; 434 { 435 register struct vnode *vp = ap->a_vp; 436 printf ("tag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLTOLOWERVP(vp)); 437 return 0; 438 } 439 440 441 /* 442 * Global vfs data structures 443 */ 444 /* 445 * NEEDSWORK: strategy,bmap are hand coded currently. They should 446 * go away with a merged buffer/block cache. 447 * 448 */ 449 int (**null_vnodeop_p)(); 450 struct vnodeopv_entry_desc lofs_vnodeop_entries[] = { 451 { &vop_default_desc, null_bypass }, 452 453 { &vop_getattr_desc, null_getattr }, 454 { &vop_inactive_desc, null_inactive }, 455 { &vop_reclaim_desc, null_reclaim }, 456 { &vop_print_desc, null_print }, 457 458 { &vop_bmap_desc, null_bmap }, 459 { &vop_strategy_desc, null_strategy }, 460 461 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 462 }; 463 struct vnodeopv_desc lofs_vnodeop_opv_desc = 464 { &null_vnodeop_p, lofs_vnodeop_entries }; 465