/* * Portions Copyright (c) 2003 Apple Computer, Inc. All rights reserved. */ #include #include #include /* Standard vendor system headers */ #include /* Afs-based standard headers */ #include /* statistics */ #include #include #include #include #ifdef AFS_DARWIN80_ENV #include #include #endif #ifdef AFS_DARWIN80_ENV #define VOPPREF(x) &vnop_ ## x #define VOPPROT(x) vnop_ ## x #define OSI_UPL_ABORT_RANGE(pl, offset, size, flags) \ ubc_upl_abort_range((pl), (offset), (size), (flags)) #define OSI_UPL_COMMIT_RANGE(pl, offset, size, flags) \ ubc_upl_commit_range((pl), (offset), (size), (flags)) #define OSI_UPL_MAP(upl, offset) ubc_upl_map((upl), (offset)) #define OSI_UPL_UNMAP(upl) ubc_upl_unmap((upl)) #define VOP_ABORTOP(x, y) #else #define VOPPREF(x) &vop_ ## x #define VOPPROT(x) vop_ ## x #define OSI_UPL_ABORT_RANGE(pl, offset, size, flags) \ kernel_upl_abort_range((pl), (offset), (size), (flags)) #define OSI_UPL_COMMIT_RANGE(pl, offset, size, flags) \ kernel_upl_commit_range((pl), (offset), (size), (flags), \ UPL_GET_INTERNAL_PAGE_LIST((pl)),\ MAX_UPL_TRANSFER) #define OSI_UPL_MAP(upl, offset) kernel_upl_map(kernel_map, (upl), (offset)) #define OSI_UPL_UNMAP(upl) kernel_upl_unmap(kernel_map, (upl)) #endif extern char afs_zeros[AFS_ZEROS]; int afs_vop_lookup(struct VOPPROT(lookup_args) *); int afs_vop_create(struct VOPPROT(create_args) *); int afs_vop_mknod(struct VOPPROT(mknod_args) *); int afs_vop_open(struct VOPPROT(open_args) *); int afs_vop_close(struct VOPPROT(close_args) *); int afs_vop_access(struct VOPPROT(access_args) *); int afs_vop_getattr(struct VOPPROT(getattr_args) *); int afs_vop_setattr(struct VOPPROT(setattr_args) *); int afs_vop_read(struct VOPPROT(read_args) *); int afs_vop_write(struct VOPPROT(write_args) *); int afs_vop_pagein(struct VOPPROT(pagein_args) *); int afs_vop_pageout(struct VOPPROT(pageout_args) *); int afs_vop_ioctl(struct VOPPROT(ioctl_args) *); int afs_vop_select(struct VOPPROT(select_args) *); int afs_vop_mmap(struct VOPPROT(mmap_args) *); int afs_vop_fsync(struct VOPPROT(fsync_args) *); int afs_vop_remove(struct VOPPROT(remove_args) *); int afs_vop_link(struct VOPPROT(link_args) *); int afs_vop_rename(struct VOPPROT(rename_args) *); int afs_vop_mkdir(struct VOPPROT(mkdir_args) *); int afs_vop_rmdir(struct VOPPROT(rmdir_args) *); int afs_vop_symlink(struct VOPPROT(symlink_args) *); int afs_vop_readdir(struct VOPPROT(readdir_args) *); int afs_vop_readlink(struct VOPPROT(readlink_args) *); int afs_vop_inactive(struct VOPPROT(inactive_args) *); int afs_vop_reclaim(struct VOPPROT(reclaim_args) *); int afs_vop_strategy(struct VOPPROT(strategy_args) *); int afs_vop_pathconf(struct VOPPROT(pathconf_args) *); int afs_vop_advlock(struct VOPPROT(advlock_args) *); int afs_vop_blktooff __P((struct VOPPROT(blktooff_args) *)); int afs_vop_offtoblk __P((struct VOPPROT(offtoblk_args) *)); #ifndef AFS_DARWIN80_ENV int afs_vop_truncate(struct VOPPROT(truncate_args) *); int afs_vop_update(struct VOPPROT(update_args) *); int afs_vop_lock(struct VOPPROT(lock_args) *); int afs_vop_unlock(struct VOPPROT(unlock_args) *); int afs_vop_bmap(struct VOPPROT(bmap_args) *); int afs_vop_seek(struct VOPPROT(seek_args) *); int afs_vop_cmap __P((struct VOPPROT(cmap_args) *)); int afs_vop_print(struct VOPPROT(print_args) *); int afs_vop_islocked(struct VOPPROT(islocked_args) *); #endif #define afs_vop_opnotsupp \ ((int (*) __P((struct vop_reallocblks_args *)))eopnotsupp) #define afs_vop_valloc afs_vop_opnotsupp #define afs_vop_vfree afs_vop_opnotsupp #define afs_vop_blkatoff afs_vop_opnotsupp #define afs_vop_reallocblks afs_vop_opnotsupp /* Global vfs data structures for AFS. */ int (**afs_vnodeop_p) (); #define VOPFUNC int (*)(void *) struct vnodeopv_entry_desc afs_vnodeop_entries[] = { {VOPPREF(default_desc), (VOPFUNC)vn_default_error}, {VOPPREF(lookup_desc), (VOPFUNC)afs_vop_lookup}, /* lookup */ {VOPPREF(create_desc), (VOPFUNC)afs_vop_create}, /* create */ {VOPPREF(mknod_desc), (VOPFUNC)afs_vop_mknod}, /* mknod */ {VOPPREF(open_desc), (VOPFUNC)afs_vop_open}, /* open */ {VOPPREF(close_desc), (VOPFUNC)afs_vop_close}, /* close */ {VOPPREF(access_desc), (VOPFUNC)afs_vop_access}, /* access */ {VOPPREF(getattr_desc), (VOPFUNC)afs_vop_getattr}, /* getattr */ {VOPPREF(setattr_desc), (VOPFUNC)afs_vop_setattr}, /* setattr */ {VOPPREF(read_desc), (VOPFUNC)afs_vop_read}, /* read */ {VOPPREF(write_desc), (VOPFUNC)afs_vop_write}, /* write */ {VOPPREF(pagein_desc), (VOPFUNC)afs_vop_pagein}, /* read */ {VOPPREF(pageout_desc), (VOPFUNC)afs_vop_pageout}, /* write */ {VOPPREF(ioctl_desc), (VOPFUNC)afs_vop_ioctl}, /* XXX ioctl */ {VOPPREF(select_desc), (VOPFUNC)afs_vop_select}, /* select */ {VOPPREF(mmap_desc), (VOPFUNC)afs_vop_mmap}, /* mmap */ {VOPPREF(fsync_desc), (VOPFUNC)afs_vop_fsync}, /* fsync */ #ifndef AFS_DARWIN80_ENV {VOPPREF(seek_desc), (VOPFUNC)afs_vop_seek}, /* seek */ #endif {VOPPREF(remove_desc), (VOPFUNC)afs_vop_remove}, /* remove */ {VOPPREF(link_desc), (VOPFUNC)afs_vop_link}, /* link */ {VOPPREF(rename_desc), (VOPFUNC)afs_vop_rename}, /* rename */ {VOPPREF(mkdir_desc), (VOPFUNC)afs_vop_mkdir}, /* mkdir */ {VOPPREF(rmdir_desc), (VOPFUNC)afs_vop_rmdir}, /* rmdir */ {VOPPREF(symlink_desc), (VOPFUNC)afs_vop_symlink}, /* symlink */ {VOPPREF(readdir_desc), (VOPFUNC)afs_vop_readdir}, /* readdir */ {VOPPREF(readlink_desc), (VOPFUNC)afs_vop_readlink}, /* readlink */ #ifndef AFS_DARWIN80_ENV {VOPPREF(abortop_desc), (VOPFUNC)nop_abortop }, /* abortop */ #endif {VOPPREF(inactive_desc), (VOPFUNC)afs_vop_inactive}, /* inactive */ {VOPPREF(reclaim_desc), (VOPFUNC)afs_vop_reclaim}, /* reclaim */ #ifndef AFS_DARWIN80_ENV {VOPPREF(lock_desc), (VOPFUNC)afs_vop_lock}, /* lock */ {VOPPREF(unlock_desc), (VOPFUNC)afs_vop_unlock}, /* unlock */ {VOPPREF(bmap_desc), (VOPFUNC)afs_vop_bmap}, /* bmap */ #endif #ifdef AFS_DARWIN80_ENV {VOPPREF(strategy_desc), (VOPFUNC)err_strategy}, /* strategy */ #else {VOPPREF(strategy_desc), (VOPFUNC)afs_vop_strategy}, /* strategy */ #endif #ifndef AFS_DARWIN80_ENV {VOPPREF(print_desc), (VOPFUNC)afs_vop_print}, /* print */ {VOPPREF(islocked_desc), (VOPFUNC)afs_vop_islocked}, /* islocked */ #endif {VOPPREF(pathconf_desc), (VOPFUNC)afs_vop_pathconf}, /* pathconf */ {VOPPREF(advlock_desc), (VOPFUNC)afs_vop_advlock}, /* advlock */ #ifndef AFS_DARWIN80_ENV {VOPPREF(blkatoff_desc), (VOPFUNC)afs_vop_blkatoff}, /* blkatoff */ {VOPPREF(valloc_desc), (VOPFUNC)afs_vop_valloc}, /* valloc */ {VOPPREF(reallocblks_desc), (VOPFUNC)afs_vop_reallocblks}, /* reallocblks */ {VOPPREF(vfree_desc), (VOPFUNC)afs_vop_vfree}, /* vfree */ {VOPPREF(update_desc), (VOPFUNC)afs_vop_update}, /* update */ {VOPPREF(cmap_desc), (VOPFUNC)afs_vop_cmap}, /* cmap */ {VOPPREF(truncate_desc), (VOPFUNC)afs_vop_truncate}, /* truncate */ #endif {VOPPREF(blktooff_desc), (VOPFUNC)afs_vop_blktooff}, /* blktooff */ {VOPPREF(offtoblk_desc), (VOPFUNC)afs_vop_offtoblk}, /* offtoblk */ {VOPPREF(bwrite_desc), (VOPFUNC)vn_bwrite}, {(struct vnodeop_desc *)NULL, (void (*)())NULL} }; struct vnodeopv_desc afs_vnodeop_opv_desc = { &afs_vnodeop_p, afs_vnodeop_entries }; #ifdef AFS_DARWIN80_ENV /* vfs structures for incompletely initialized vnodes */ int (**afs_dead_vnodeop_p) (); struct vnodeopv_entry_desc afs_dead_vnodeop_entries[] = { {VOPPREF(default_desc), (VOPFUNC)vn_default_error}, {VOPPREF(lookup_desc), (VOPFUNC)vn_default_error}, /* lookup */ {VOPPREF(create_desc), (VOPFUNC)err_create}, /* create */ {VOPPREF(mknod_desc), (VOPFUNC)err_mknod}, /* mknod */ {VOPPREF(open_desc), (VOPFUNC)err_open}, /* open */ {VOPPREF(close_desc), (VOPFUNC)err_close}, /* close */ {VOPPREF(access_desc), (VOPFUNC)err_access}, /* access */ {VOPPREF(getattr_desc), (VOPFUNC)err_getattr}, /* getattr */ {VOPPREF(setattr_desc), (VOPFUNC)err_setattr}, /* setattr */ {VOPPREF(read_desc), (VOPFUNC)err_read}, /* read */ {VOPPREF(write_desc), (VOPFUNC)err_write}, /* write */ {VOPPREF(pagein_desc), (VOPFUNC)err_pagein}, /* read */ {VOPPREF(pageout_desc), (VOPFUNC)err_pageout}, /* write */ {VOPPREF(ioctl_desc), (VOPFUNC)err_ioctl}, /* XXX ioctl */ {VOPPREF(select_desc), (VOPFUNC)nop_select}, /* select */ {VOPPREF(mmap_desc), (VOPFUNC)err_mmap}, /* mmap */ {VOPPREF(fsync_desc), (VOPFUNC)err_fsync}, /* fsync */ {VOPPREF(remove_desc), (VOPFUNC)err_remove}, /* remove */ {VOPPREF(link_desc), (VOPFUNC)err_link}, /* link */ {VOPPREF(rename_desc), (VOPFUNC)err_rename}, /* rename */ {VOPPREF(mkdir_desc), (VOPFUNC)err_mkdir}, /* mkdir */ {VOPPREF(rmdir_desc), (VOPFUNC)err_rmdir}, /* rmdir */ {VOPPREF(symlink_desc), (VOPFUNC)err_symlink}, /* symlink */ {VOPPREF(readdir_desc), (VOPFUNC)err_readdir}, /* readdir */ {VOPPREF(readlink_desc), (VOPFUNC)err_readlink}, /* readlink */ {VOPPREF(inactive_desc), (VOPFUNC)afs_vop_inactive}, /* inactive */ {VOPPREF(reclaim_desc), (VOPFUNC)afs_vop_reclaim}, /* reclaim */ {VOPPREF(strategy_desc), (VOPFUNC)err_strategy}, /* strategy */ {VOPPREF(pathconf_desc), (VOPFUNC)err_pathconf}, /* pathconf */ {VOPPREF(advlock_desc), (VOPFUNC)err_advlock}, /* advlock */ {VOPPREF(blktooff_desc), (VOPFUNC)err_blktooff}, /* blktooff */ {VOPPREF(offtoblk_desc), (VOPFUNC)err_offtoblk}, /* offtoblk */ {VOPPREF(bwrite_desc), (VOPFUNC)err_bwrite}, {(struct vnodeop_desc *)NULL, (void (*)())NULL} }; struct vnodeopv_desc afs_dead_vnodeop_opv_desc = { &afs_dead_vnodeop_p, afs_dead_vnodeop_entries }; #endif #define GETNAME() \ struct componentname *cnp = ap->a_cnp; \ char *name; \ MALLOC(name, char *, cnp->cn_namelen+1, M_TEMP, M_WAITOK); \ memcpy(name, cnp->cn_nameptr, cnp->cn_namelen); \ name[cnp->cn_namelen] = '\0' #define DROPNAME() FREE(name, M_TEMP) void darwin_vn_hold(struct vnode *vp) { int haveGlock=ISAFS_GLOCK(); struct vcache *tvc = VTOAFS(vp); #ifndef AFS_DARWIN80_ENV tvc->f.states |= CUBCinit; #endif #ifdef AFS_DARWIN80_ENV osi_Assert((tvc->f.states & CVInit) == 0); if (tvc->f.states & CDeadVnode) osi_Assert(!vnode_isinuse(vp, 1)); #endif if (haveGlock) AFS_GUNLOCK(); #ifdef AFS_DARWIN80_ENV if (vnode_get(vp)) { /* being terminated. kernel won't give us a ref. Now what? our callers don't expect us to fail */ if (haveGlock) AFS_GLOCK(); return; } if (vnode_ref(vp)) { vnode_put(vp); if (haveGlock) AFS_GLOCK(); return; } vnode_put(vp); #else /* vget needed for 0 ref'd vnode in GetVCache to not panic in vref. vref needed for multiref'd vnode in vnop_remove not to deadlock ourselves during vop_inactive, except we also need to not reinst the ubc... so we just call VREF there now anyway. */ if (VREFCOUNT_GT(tvc, 0)) VREF(((struct vnode *)(vp))); else afs_vget(afs_globalVFS, 0, (vp)); #endif #ifndef AFS_DARWIN80_ENV if (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp)) { ubc_info_init(vp); } #endif if (haveGlock) AFS_GLOCK(); #ifndef AFS_DARWIN80_ENV tvc->f.states &= ~CUBCinit; #endif } int afs_vop_lookup(ap) struct VOPPROT(lookup_args)/* { * struct vnodeop_desc * a_desc; * struct vnode *a_dvp; * struct vnode **a_vpp; * struct componentname *a_cnp; * } */ *ap; { int error; struct vcache *vcp; struct vnode *vp, *dvp; int flags = ap->a_cnp->cn_flags; int lockparent; /* 1 => lockparent flag is set */ int wantparent; /* 1 => wantparent or lockparent flag */ struct proc *p; #ifdef AFS_DARWIN80_ENV vcp = VTOAFS(ap->a_dvp); /* * ._ file attribute mirroring touches this. * we can't flag the vcache as there is none, so fail here. * needed for fsevents support. */ if (ap->a_context == afs_osi_ctxtp) return ENOENT; if (vcp->mvstat != 1) { error = cache_lookup(ap->a_dvp, ap->a_vpp, ap->a_cnp); if (error == -1) return 0; if (error == ENOENT) return error; } #endif GETNAME(); p = vop_cn_proc; lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT | WANTPARENT); if (!vnode_isdir(ap->a_dvp)) { *ap->a_vpp = 0; DROPNAME(); return ENOTDIR; } dvp = ap->a_dvp; #ifndef AFS_DARWIN80_ENV if (flags & ISDOTDOT) VOP_UNLOCK(dvp, 0, p); #endif AFS_GLOCK(); error = afs_lookup(VTOAFS(dvp), name, &vcp, vop_cn_cred); AFS_GUNLOCK(); if (error) { #ifndef AFS_DARWIN80_ENV if (flags & ISDOTDOT) VOP_LOCK(dvp, LK_EXCLUSIVE | LK_RETRY, p); #endif if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && (flags & ISLASTCN) && error == ENOENT) error = EJUSTRETURN; #ifndef AFS_DARWIN80_ENV if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; #endif DROPNAME(); *ap->a_vpp = 0; return (error); } #ifdef AFS_DARWIN80_ENV if ((error=afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0, 0))) { DROPNAME(); *ap->a_vpp = 0; return error; } #endif vp = AFSTOV(vcp); /* always get a node if no error */ #ifndef AFS_DARWIN80_ENV /* XXX needed for multi-mount thing, but can't have it yet */ vp->v_vfsp = dvp->v_vfsp; if (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp)) { ubc_info_init(vp); } #endif #ifndef AFS_DARWIN80_ENV /* The parent directory comes in locked. We unlock it on return * unless the caller wants it left locked. * we also always return the vnode locked. */ if (flags & ISDOTDOT) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* always return the child locked */ if (lockparent && (flags & ISLASTCN) && (error = vn_lock(dvp, LK_EXCLUSIVE, p))) { vput(vp); DROPNAME(); return (error); } } else if (vp == dvp) { /* they're the same; afs_lookup() already ref'ed the leaf. * It came in locked, so we don't need to ref OR lock it */ } else { if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, p); /* done with parent. */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* always return the child locked */ } #endif *ap->a_vpp = vp; #ifndef AFS_DARWIN80_ENV if ((cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN) || (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)))) cnp->cn_flags |= SAVENAME; #endif DROPNAME(); return error; } int afs_vop_create(ap) struct VOPPROT(create_args) /* { * struct vnode *a_dvp; * struct vnode **a_vpp; * struct componentname *a_cnp; * struct vattr *a_vap; * } */ *ap; { int error = 0; struct vcache *vcp; struct vnode *dvp = ap->a_dvp; struct proc *p; GETNAME(); p = vop_cn_proc; /* vnode layer handles excl/nonexcl */ AFS_GLOCK(); error = afs_create(VTOAFS(dvp), name, ap->a_vap, NONEXCL, ap->a_vap->va_mode, &vcp, vop_cn_cred); AFS_GUNLOCK(); if (error) { #ifndef AFS_DARWIN80_ENV VOP_ABORTOP(dvp, cnp); vput(dvp); #endif DROPNAME(); return (error); } if (vcp) { #ifdef AFS_DARWIN80_ENV if ((error=afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0, 0))) { DROPNAME(); *ap->a_vpp=0; return error; } #endif *ap->a_vpp = AFSTOV(vcp); #ifndef AFS_DARWIN80_ENV /* XXX needed for multi-mount thing, but can't have it yet */ (*ap->a_vpp)->v_vfsp = dvp->v_vfsp; vn_lock(*ap->a_vpp, LK_EXCLUSIVE | LK_RETRY, p); if (UBCINFOMISSING(*ap->a_vpp) || UBCINFORECLAIMED(*ap->a_vpp)) { vcp->f.states |= CUBCinit; ubc_info_init(*ap->a_vpp); vcp->f.states &= ~CUBCinit; } #endif } else *ap->a_vpp = 0; #ifndef AFS_DARWIN80_ENV if ((cnp->cn_flags & SAVESTART) == 0) FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); vput(dvp); #endif DROPNAME(); return error; } int afs_vop_mknod(ap) struct VOPPROT(mknod_args) /* { * struct vnode *a_dvp; * struct vnode **a_vpp; * struct componentname *a_cnp; * struct vattr *a_vap; * } */ *ap; { #ifndef AFS_DARWIN80_ENV FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI); vput(ap->a_dvp); #endif return (ENODEV); } int afs_vop_open(ap) struct VOPPROT(open_args) /* { * struct vnode *a_vp; * int a_mode; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { int error; struct vnode *vp = ap->a_vp; struct vcache *vc = VTOAFS(vp); #if !defined(AFS_DARWIN80_ENV) int didhold = 0; /*---------------------------------------------------------------- * osi_VM_TryReclaim() removes the ubcinfo of a vnode, but that vnode * can later be passed to vn_open(), which will skip the call to * ubc_hold(), and when the ubcinfo is later added, the ui_refcount * will be off. So we compensate by calling ubc_hold() ourselves * when ui_refcount is less than 2. If an error occurs in afs_open() * we must call ubc_rele(), which is what vn_open() would do if it * was able to call ubc_hold() in the first place. *----------------------------------------------------------------*/ if (vp->v_type == VREG && !(vp->v_flag & VSYSTEM) && vp->v_ubcinfo->ui_refcount < 2) didhold = ubc_hold(vp); #endif /* !AFS_DARWIN80_ENV */ AFS_GLOCK(); error = afs_open(&vc, ap->a_mode, vop_cred); #ifdef DIAGNOSTIC if (AFSTOV(vc) != vp) panic("AFS open changed vnode!"); #endif osi_FlushPages(vc, vop_cred); AFS_GUNLOCK(); #if !defined(AFS_DARWIN80_ENV) if (error && didhold) ubc_rele(vp); #endif /* !AFS_DARWIN80_ENV */ return error; } int afs_vop_close(ap) struct VOPPROT(close_args) /* { * struct vnode *a_vp; * int a_fflag; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { int code; struct vnode *vp = ap->a_vp; struct vcache *avc = VTOAFS(vp); /* allows faking FSE_CONTENT_MODIFIED */ if (afs_osi_ctxtp == ap->a_context) return 0; AFS_GLOCK(); if (vop_cred) code = afs_close(avc, ap->a_fflag, vop_cred); else code = afs_close(avc, ap->a_fflag, &afs_osi_cred); osi_FlushPages(avc, vop_cred); /* hold bozon lock, but not basic vnode lock */ /* This is legit; it just forces the fstrace event to happen */ code = afs_CheckCode(code, NULL, 60); AFS_GUNLOCK(); return code; } #ifdef AFS_DARWIN80_ENV extern int afs_fakestat_enable; int afs_vop_access(ap) struct VOPPROT(access_args) /* { * struct vnode *a_vp; * int a_action; * vfs_context_t a_context; * } */ *ap; { int code; struct vrequest treq; struct afs_fakestat_state fakestate; struct vcache * tvc = VTOAFS(ap->a_vp); int bits=0; int cmb = CHECK_MODE_BITS; #ifdef AFS_DARWIN80_ENV /* * needed for fsevents. ._ file attribute mirroring touches this. * we can't flag the vcache, as there is none, so fail here. */ if (ap->a_context == afs_osi_ctxtp) return ENOENT; #endif AFS_GLOCK(); afs_InitFakeStat(&fakestate); if ((code = afs_InitReq(&treq, vop_cred))) goto out2; code = afs_TryEvalFakeStat(&tvc, &fakestate, &treq); if (code) { code = afs_CheckCode(code, &treq, 55); goto out; } code = afs_VerifyVCache(tvc, &treq); if (code) { code = afs_CheckCode(code, &treq, 56); goto out; } if (afs_fakestat_enable && tvc->mvstat && !(tvc->f.states & CStatd)) { code = 0; goto out; } if (vnode_isdir(ap->a_vp)) { if (ap->a_action & KAUTH_VNODE_LIST_DIRECTORY) bits |= PRSFS_LOOKUP; if (ap->a_action & KAUTH_VNODE_ADD_FILE) bits |= PRSFS_INSERT; if (ap->a_action & KAUTH_VNODE_SEARCH) bits |= PRSFS_LOOKUP; if (ap->a_action & KAUTH_VNODE_DELETE) bits |= PRSFS_DELETE; if (ap->a_action & KAUTH_VNODE_ADD_SUBDIRECTORY) bits |= PRSFS_INSERT; if (ap->a_action & KAUTH_VNODE_DELETE_CHILD) bits |= PRSFS_DELETE; #if 0 /* I'd argue this should be enforced on the parent. But that's ugly */ if (ap->a_action & KAUTH_VNODE_READ_ATTRIBUTES) bits |= PRSFS_LOOKUP; if (ap->a_action & KAUTH_VNODE_READ_SECURITY) /* mode bits/gid, not afs acl */ bits |= PRSFS_LOOKUP; #endif } else { if (ap->a_action & KAUTH_VNODE_READ_DATA) bits |= PRSFS_READ; if (ap->a_action & KAUTH_VNODE_WRITE_DATA) bits |= PRSFS_WRITE; if (ap->a_action & KAUTH_VNODE_EXECUTE) bits |= PRSFS_READ; /* and mode bits.... */ if (ap->a_action & KAUTH_VNODE_READ_ATTRIBUTES) bits |= PRSFS_LOOKUP; if (ap->a_action & KAUTH_VNODE_READ_SECURITY) /* mode bits/gid, not afs acl */ bits |= PRSFS_LOOKUP; if ((ap->a_action & ((1 << 25) - 1)) == KAUTH_VNODE_EXECUTE) /* if only exec, don't check for read mode bit */ /* high bits of ap->a_action are not for 'generic rights bits', and so should not be checked (KAUTH_VNODE_ACCESS is often present and needs to be masked off) */ cmb |= CMB_ALLOW_EXEC_AS_READ; } if (ap->a_action & KAUTH_VNODE_WRITE_ATTRIBUTES) bits |= PRSFS_WRITE; #if 0 /* no extended attributes */ if (ap->a_action & KAUTH_VNODE_READ_EXTATTRIBUTES) bits |= PRSFS_READ; if (ap->a_action & KAUTH_VNODE_WRITE_EXTATTRIBUTES) bits |= PRSFS_WRITE; #endif if (ap->a_action & KAUTH_VNODE_WRITE_SECURITY) bits |= PRSFS_WRITE; /* we can't check for KAUTH_VNODE_TAKE_OWNERSHIP, so we always permit it */ code = afs_AccessOK(tvc, bits, &treq, cmb); /* * Special cased dropbox handling: * cp on 10.4 behaves badly, looping on EACCES * Finder may reopen the file. Let it. */ if (code == 0 && ((bits &~(PRSFS_READ|PRSFS_WRITE)) == 0)) code = afs_AccessOK(tvc, PRSFS_ADMINISTER|PRSFS_INSERT|bits, &treq, cmb); /* Finder also treats dropboxes as insert+delete. fake it out. */ if (code == 0 && (bits == (PRSFS_INSERT|PRSFS_DELETE))) code = afs_AccessOK(tvc, PRSFS_INSERT, &treq, cmb); if (code == 1 && vnode_vtype(ap->a_vp) == VREG && ap->a_action & KAUTH_VNODE_EXECUTE && (tvc->f.m.Mode & 0100) != 0100) { code = 0; } if (code) { code= 0; /* if access is ok */ } else { code = afs_CheckCode(EACCES, &treq, 57); /* failure code */ } out: afs_PutFakeStat(&fakestate); out2: AFS_GUNLOCK(); return code; } #else int afs_vop_access(ap) struct VOPPROT(access_args) /* { * struct vnode *a_vp; * int a_mode; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { int code; AFS_GLOCK(); code = afs_access(VTOAFS(ap->a_vp), ap->a_mode, vop_cred); AFS_GUNLOCK(); return code; } #endif int afs_vop_getattr(ap) struct VOPPROT(getattr_args) /* { * struct vnode *a_vp; * struct vattr *a_vap; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { int code; #ifdef AFS_DARWIN80_ENV /* CEvent excludes the fsevent. our context excludes the ._ */ if ((VTOAFS(ap->a_vp)->f.states & CEvent) || (ap->a_context == afs_osi_ctxtp)){ struct vcache *avc = VTOAFS(ap->a_vp); int isglock = ISAFS_GLOCK(); /* this is needed because of how and when we re-enter */ if (!isglock) AFS_GLOCK(); /* do minimal work to return fake result for fsevents */ if (afs_fakestat_enable && VTOAFS(ap->a_vp)->mvstat == 1) { struct afs_fakestat_state fakestat; struct vrequest treq; code = afs_InitReq(&treq, vop_cred); if (code) { if (!isglock) AFS_GUNLOCK(); return code; } afs_InitFakeStat(&fakestat); /* expects GLOCK */ code = afs_TryEvalFakeStat(&avc, &fakestat, &treq); if (code) { if (!isglock) AFS_GUNLOCK(); afs_PutFakeStat(&fakestat); return code; } } code = afs_CopyOutAttrs(avc, ap->a_vap); if (!isglock) AFS_GUNLOCK(); if (0 && !code) { /* tweak things so finder will recheck */ (ap->a_vap)->va_gid = ((ap->a_vap)->va_gid == 1) ? 2 : 1; (ap->a_vap)->va_mode &= ~(VSGID); } } else #endif { AFS_GLOCK(); code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred); /* This is legit; it just forces the fstrace event to happen */ code = afs_CheckCode(code, NULL, 58); AFS_GUNLOCK(); } #ifdef AFS_DARWIN80_ENV VATTR_SET_SUPPORTED(ap->a_vap, va_type); VATTR_SET_SUPPORTED(ap->a_vap, va_mode); VATTR_SET_SUPPORTED(ap->a_vap, va_uid); VATTR_SET_SUPPORTED(ap->a_vap, va_gid); VATTR_SET_SUPPORTED(ap->a_vap, va_fsid); VATTR_SET_SUPPORTED(ap->a_vap, va_fileid); VATTR_SET_SUPPORTED(ap->a_vap, va_nlink); VATTR_SET_SUPPORTED(ap->a_vap, va_data_size); VATTR_SET_SUPPORTED(ap->a_vap, va_access_time); VATTR_SET_SUPPORTED(ap->a_vap, va_modify_time); VATTR_SET_SUPPORTED(ap->a_vap, va_change_time); VATTR_SET_SUPPORTED(ap->a_vap, va_gen); VATTR_SET_SUPPORTED(ap->a_vap, va_flags); VATTR_SET_SUPPORTED(ap->a_vap, va_iosize); VATTR_SET_SUPPORTED(ap->a_vap, va_total_alloc); #endif return code; } int afs_vop_setattr(ap) struct VOPPROT(setattr_args) /* { * struct vnode *a_vp; * struct vattr *a_vap; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { int code, pass = 0; struct vcache *avc = VTOAFS(ap->a_vp); #ifdef AFS_DARWIN80_ENV /* fsevents tries to set attributes. drop it. */ if (ap->a_context == afs_osi_ctxtp) return 0; #endif AFS_GLOCK(); retry: code = afs_setattr(avc, ap->a_vap, vop_cred); /* This is legit; it just forces the fstrace event to happen */ code = afs_CheckCode(code, NULL, 59); if (!pass && code == EINVAL && (VATTR_IS_ACTIVE(ap->a_vap, va_mode) && (vType(avc) == VLNK))) { VATTR_CLEAR_ACTIVE(ap->a_vap, va_mode); pass++; goto retry; } AFS_GUNLOCK(); return code; } int afs_vop_read(ap) struct VOPPROT(read_args) /* { * struct vnode *a_vp; * struct uio *a_uio; * int a_ioflag; * struct ucred *a_cred; * } */ *ap; { int code; struct vnode *vp = ap->a_vp; struct vcache *avc = VTOAFS(vp); if (vnode_isdir(ap->a_vp)) return EISDIR; #ifdef AFS_DARWIN80_ENV ubc_msync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_PUSHDIRTY); #else if (UBCINFOEXISTS(ap->a_vp)) { ubc_clean(ap->a_vp, 0); } #endif AFS_GLOCK(); osi_FlushPages(avc, vop_cred); /* hold bozon lock, but not basic vnode lock */ code = afs_read(avc, ap->a_uio, vop_cred, 0); AFS_GUNLOCK(); return code; } int afs_vop_pagein(ap) struct VOPPROT(pagein_args) /* { * struct vnode *a_vp; * upl_t a_pl; * vm_offset_t a_pl_offset; * off_t a_f_offset; * size_t a_size; * struct ucred *a_cred; * int a_flags; * } */ *ap; { struct vnode *vp = ap->a_vp; upl_t pl = ap->a_pl; size_t size = ap->a_size; off_t f_offset = ap->a_f_offset; vm_offset_t pl_offset = ap->a_pl_offset; int flags = ap->a_flags; struct ucred *cred; vm_offset_t ioaddr; #ifdef AFS_DARWIN80_ENV struct uio *uio; #else struct uio auio; struct iovec aiov; struct uio *uio = &auio; #endif int nocommit = flags & UPL_NOCOMMIT; int code; struct vcache *tvc = VTOAFS(vp); #ifndef AFS_DARWIN80_ENV if (UBCINVALID(vp)) { #if DIAGNOSTIC panic("afs_vop_pagein: invalid vp"); #endif /* DIAGNOSTIC */ return (EPERM); } UBCINFOCHECK("afs_vop_pagein", vp); #endif if (pl == (upl_t) NULL) { panic("afs_vop_pagein: no upl"); } cred = ubc_getcred(vp); if (cred == NOCRED) cred = vop_cred; if (size == 0) { if (!nocommit) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY); return (0); } if (f_offset < 0) { if (!nocommit) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY); return (EINVAL); } if (f_offset & PAGE_MASK) panic("afs_vop_pagein: offset not page aligned"); OSI_UPL_MAP(pl, &ioaddr); ioaddr += pl_offset; #ifdef AFS_DARWIN80_ENV uio = uio_create(1, f_offset, UIO_SYSSPACE32, UIO_READ); uio_addiov(uio, CAST_USER_ADDR_T(ioaddr), size); #else auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = f_offset; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = NULL; auio.uio_resid = aiov.iov_len = size; aiov.iov_base = (caddr_t) ioaddr; #endif AFS_GLOCK(); osi_FlushPages(tvc, vop_cred); /* hold bozon lock, but not basic vnode lock */ code = afs_read(tvc, uio, cred, 0); if (code == 0) { ObtainWriteLock(&tvc->lock, 2); tvc->f.states |= CMAPPED; ReleaseWriteLock(&tvc->lock); } AFS_GUNLOCK(); /* Zero out rest of last page if there wasn't enough data in the file */ if (code == 0 && AFS_UIO_RESID(uio) > 0) { #ifdef AFS_DARWIN80_ENV memset(((caddr_t)ioaddr) + (size - AFS_UIO_RESID(uio)), 0, AFS_UIO_RESID(uio)); #else memset(aiov.iov_base, 0, auio.uio_resid); #endif } OSI_UPL_UNMAP(pl); if (!nocommit) { if (code) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY); else OSI_UPL_COMMIT_RANGE(pl, pl_offset, size, UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_FREE_ON_EMPTY); } #ifdef AFS_DARWIN80_ENV uio_free(uio); #endif return code; } int afs_vop_write(ap) struct VOPPROT(write_args) /* { * struct vnode *a_vp; * struct uio *a_uio; * int a_ioflag; * struct ucred *a_cred; * } */ *ap; { int code; struct vcache *avc = VTOAFS(ap->a_vp); void *object; #ifdef AFS_DARWIN80_ENV ubc_msync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_INVALIDATE); #else if (UBCINFOEXISTS(ap->a_vp)) { ubc_clean(ap->a_vp, 1); } if (UBCINFOEXISTS(ap->a_vp)) osi_VM_NukePages(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_RESID(ap->a_uio)); #endif AFS_GLOCK(); osi_FlushPages(avc, vop_cred); /* hold bozon lock, but not basic vnode lock */ code = afs_write(VTOAFS(ap->a_vp), ap->a_uio, ap->a_ioflag, vop_cred, 0); AFS_GUNLOCK(); return code; } int afs_vop_pageout(ap) struct VOPPROT(pageout_args) /* { * struct vnode *a_vp; * upl_t a_pl, * vm_offset_t a_pl_offset, * off_t a_f_offset, * size_t a_size, * struct ucred *a_cred, * int a_flags * } */ *ap; { struct vnode *vp = ap->a_vp; upl_t pl = ap->a_pl; size_t size = ap->a_size; off_t f_offset = ap->a_f_offset; vm_offset_t pl_offset = ap->a_pl_offset; int flags = ap->a_flags; struct ucred *cred; vm_offset_t ioaddr; #ifdef AFS_DARWIN80_ENV struct uio *uio; #else struct uio auio; struct iovec aiov; struct uio *uio = &auio; #endif int nocommit = flags & UPL_NOCOMMIT; int iosize; int code; struct vcache *tvc = VTOAFS(vp); #ifndef AFS_DARWIN80_ENV if (UBCINVALID(vp)) { #if DIAGNOSTIC panic("afs_vop_pageout: invalid vp"); #endif /* DIAGNOSTIC */ return (EPERM); } UBCINFOCHECK("afs_vop_pageout", vp); #endif if (pl == (upl_t) NULL) { panic("afs_vop_pageout: no upl"); } #if !defined(AFS_DARWIN80_ENV) /* XXX nfs now uses it's own bufs (struct nfsbuf) maybe the generic layer doesn't have them anymore? In any case, we can't just copy code from nfs... */ { int lbn, s; struct buf *bp; int biosize = DEV_BSIZE; lbn = f_offset / DEV_BSIZE; for (iosize = size; iosize > 0; iosize -= biosize, lbn++) { s = splbio(); if (bp = incore(vp, lbn)) { if (ISSET(bp->b_flags, B_BUSY)) panic("nfs_pageout: found BUSY buffer incore\n"); bremfree(bp); SET(bp->b_flags, (B_BUSY | B_INVAL)); brelse(bp); } splx(s); } } #endif cred = ubc_getcred(vp); if (cred == NOCRED) cred = vop_cred; if (size == 0) { if (!nocommit) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_FREE_ON_EMPTY); return (0); } if (flags & (IO_APPEND | IO_SYNC)) panic("nfs_pageout: (IO_APPEND | IO_SYNC)"); if (f_offset < 0) { if (!nocommit) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_FREE_ON_EMPTY); return (EINVAL); } if (f_offset >= tvc->f.m.Length) { if (!nocommit) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_FREE_ON_EMPTY); return (EINVAL); } if (f_offset & PAGE_MASK) panic("afs_vop_pageout: offset not page aligned"); /* size will always be a multiple of PAGE_SIZE */ /* pageout isn't supposed to extend files */ if (f_offset + size > tvc->f.m.Length) iosize = tvc->f.m.Length - f_offset; else iosize = size; if (size > (iosize + (PAGE_SIZE - 1)) & ~PAGE_MASK && !nocommit) { int iosize_rnd=(iosize + (PAGE_SIZE - 1)) & ~PAGE_MASK; OSI_UPL_ABORT_RANGE(pl, pl_offset + iosize_rnd, size - iosize_rnd, UPL_ABORT_FREE_ON_EMPTY); } OSI_UPL_MAP(pl, &ioaddr); ioaddr += pl_offset; #ifdef AFS_DARWIN80_ENV uio = uio_create(1, f_offset, UIO_SYSSPACE32, UIO_READ); uio_addiov(uio, CAST_USER_ADDR_T(ioaddr), size); #else auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = f_offset; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_WRITE; auio.uio_procp = NULL; auio.uio_resid = aiov.iov_len = iosize; aiov.iov_base = (caddr_t) ioaddr; #endif { /* USV? * check for partial page and clear the * contents past end of the file before * releasing it in the VM page cache */ if ((f_offset < tvc->f.m.Length) && (f_offset + size) > tvc->f.m.Length) { size_t io = tvc->f.m.Length - f_offset; memset((caddr_t) (ioaddr + pl_offset + io), 0, size - io); } } AFS_GLOCK(); osi_FlushPages(tvc, vop_cred); /* hold bozon lock, but not basic vnode lock */ ObtainWriteLock(&tvc->lock, 1); afs_FakeOpen(tvc); ReleaseWriteLock(&tvc->lock); code = afs_write(tvc, uio, flags, cred, 0); ObtainWriteLock(&tvc->lock, 1); afs_FakeClose(tvc, cred); ReleaseWriteLock(&tvc->lock); AFS_GUNLOCK(); OSI_UPL_UNMAP(pl); if (!nocommit) { if (code) OSI_UPL_ABORT_RANGE(pl, pl_offset, size, UPL_ABORT_FREE_ON_EMPTY); else OSI_UPL_COMMIT_RANGE(pl, pl_offset, size, UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_FREE_ON_EMPTY); } #ifdef AFS_DARWIN80_ENV uio_free(uio); #endif return code; } int afs_vop_ioctl(ap) struct VOPPROT(ioctl_args) /* { * struct vnode *a_vp; * int a_command; * caddr_t a_data; * int a_fflag; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { struct vcache *tvc = VTOAFS(ap->a_vp); struct afs_ioctl data; int error = 0; /* in case we ever get in here... */ AFS_STATCNT(afs_ioctl); if (((ap->a_command >> 8) & 0xff) == 'V') { /* This is a VICEIOCTL call */ AFS_GLOCK(); error = HandleIoctl(tvc, ap->a_command, ap->a_data); AFS_GUNLOCK(); return (error); } else { /* No-op call; just return. */ return (ENOTTY); } } /* ARGSUSED */ int afs_vop_select(ap) struct VOPPROT(select_args) /* { * struct vnode *a_vp; * int a_which; * int a_fflags; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { /* * We should really check to see if I/O is possible. */ return (1); } /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ int afs_vop_mmap(ap) struct VOPPROT(mmap_args) /* { * struct vnode *a_vp; * int a_fflags; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { return (EINVAL); } int afs_vop_fsync(ap) struct VOPPROT(fsync_args) /* { * struct vnode *a_vp; * struct ucred *a_cred; * int a_waitfor; * struct proc *a_p; * } */ *ap; { int wait = ap->a_waitfor == MNT_WAIT; int error; struct vnode *vp = ap->a_vp; int haveGlock = ISAFS_GLOCK(); /* in order to recycle faked vnodes for bulkstat */ if (VTOAFS(vp) == NULL) return ENOTSUP; /* afs_vop_lookup glocks, can call us through vinvalbuf from GetVCache */ if (!haveGlock) AFS_GLOCK(); if (vop_cred) error = afs_fsync(VTOAFS(vp), vop_cred); else error = afs_fsync(VTOAFS(vp), &afs_osi_cred); if (!haveGlock) AFS_GUNLOCK(); return error; } #ifndef AFS_DARWIN80_ENV int afs_vop_seek(ap) struct VOPPROT(seek_args) /* { * struct vnode *a_vp; * off_t a_oldoff; * off_t a_newoff; * struct ucred *a_cred; * } */ *ap; { if (ap->a_newoff > ULONG_MAX) /* AFS doesn't support 64-bit offsets */ return EINVAL; return (0); } #endif int afs_vop_remove(ap) struct VOPPROT(remove_args) /* { * struct vnode *a_dvp; * struct vnode *a_vp; * struct componentname *a_cnp; * } */ *ap; { int error = 0; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; #ifdef AFS_DARWIN80_ENV if (ap->a_flags & VNODE_REMOVE_NODELETEBUSY) { /* Caller requested Carbon delete semantics */ if (vnode_isinuse(vp, 0)) { return EBUSY; } } #endif GETNAME(); AFS_GLOCK(); error = afs_remove(VTOAFS(dvp), name, vop_cn_cred); error = afs_CheckCode(error, NULL, 61); AFS_GUNLOCK(); cache_purge(vp); if (!error) { #ifdef AFS_DARWIN80_ENV struct vcache *tvc = VTOAFS(vp); if (!(tvc->f.states & CUnlinked)) { ubc_setsize(vp, (off_t)0); vnode_recycle(vp); } #else /* necessary so we don't deadlock ourselves in vclean */ VOP_UNLOCK(vp, 0, cnp->cn_proc); /* If crashes continue in ubc_hold, comment this out */ (void)ubc_uncache(vp); #endif } else { /* should check for PRSFS_INSERT and not PRSFS_DELETE, but the goal here is to deal with Finder's unhappiness with resource forks that have no resources in a dropbox setting */ if (name[0] == '.' && name[1] == '_' && error == EACCES) error = 0; } #ifndef AFS_DARWIN80_ENV vput(dvp); if (dvp == vp) vrele(vp); else vput(vp); #endif #ifndef AFS_DARWIN80_ENV FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); #endif DROPNAME(); return error; } int afs_vop_link(ap) struct VOPPROT(link_args) /* { * struct vnode *a_vp; * struct vnode *a_tdvp; * struct componentname *a_cnp; * } */ *ap; { int error = 0; struct vnode *dvp = ap->a_tdvp; struct vnode *vp = ap->a_vp; struct proc *p; GETNAME(); p = vop_cn_proc; if (vnode_isdir(vp)) { VOP_ABORTOP(vp, cnp); error = EISDIR; goto out; } #ifndef AFS_DARWIN80_ENV if (error = vn_lock(vp, LK_EXCLUSIVE, p)) { VOP_ABORTOP(dvp, cnp); goto out; } #endif AFS_GLOCK(); error = afs_link(VTOAFS(vp), VTOAFS(dvp), name, vop_cn_cred); AFS_GUNLOCK(); #ifndef AFS_DARWIN80_ENV FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); #endif #ifndef AFS_DARWIN80_ENV if (dvp != vp) VOP_UNLOCK(vp, 0, p); #endif out: #ifndef AFS_DARWIN80_ENV vput(dvp); #endif DROPNAME(); return error; } int afs_vop_rename(ap) struct VOPPROT(rename_args) /* { * struct vnode *a_fdvp; * struct vnode *a_fvp; * struct componentname *a_fcnp; * struct vnode *a_tdvp; * struct vnode *a_tvp; * struct componentname *a_tcnp; * } */ *ap; { int error = 0; struct componentname *fcnp = ap->a_fcnp; char *fname; struct componentname *tcnp = ap->a_tcnp; char *tname; struct vnode *tvp = ap->a_tvp; struct vnode *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; struct vnode *fdvp = ap->a_fdvp; struct proc *p; p = cn_proc(fcnp); #ifdef AFS_DARWIN80_ENV /* * generic code tests for v_mount equality, so we don't have to, but we * don't get the multiple-mount "benefits" of the old behavior * the generic code doesn't do this, so we really should, but all the * vrele's are wrong... */ #else /* Check for cross-device rename. * For AFS, this means anything not in AFS-space */ if ((0 != strcmp(tdvp->v_mount->mnt_stat.f_fstypename, "afs")) || (tvp && (0 != strcmp(tvp->v_mount->mnt_stat.f_fstypename, "afs")))) { error = EXDEV; goto abortit; } /* * if fvp == tvp, we're just removing one name of a pair of * directory entries for the same element. convert call into rename. ( (pinched from NetBSD 1.0's ufs_rename()) */ if (fvp == tvp) { if (vnode_isdir(fvp)) { error = EINVAL; abortit: VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ vrele(fdvp); vrele(fvp); return (error); } /* Release destination completely. */ VOP_ABORTOP(tdvp, tcnp); vput(tdvp); vput(tvp); /* Delete source. */ vrele(fdvp); vrele(fvp); fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) panic("afs_rename: lost from startdir"); fcnp->cn_nameiop = DELETE; VREF(fdvp); error=relookup(fdvp, &fvp, fcnp); if (error == 0) vrele(fdvp); if (fvp == NULL) { return (ENOENT); } error=VOP_REMOVE(fdvp, fvp, fcnp); if (fdvp == fvp) vrele(fdvp); else vput(fdvp); vput(fvp); return (error); } if (error = vn_lock(fvp, LK_EXCLUSIVE, p)) goto abortit; #endif MALLOC(fname, char *, fcnp->cn_namelen + 1, M_TEMP, M_WAITOK); memcpy(fname, fcnp->cn_nameptr, fcnp->cn_namelen); fname[fcnp->cn_namelen] = '\0'; MALLOC(tname, char *, tcnp->cn_namelen + 1, M_TEMP, M_WAITOK); memcpy(tname, tcnp->cn_nameptr, tcnp->cn_namelen); tname[tcnp->cn_namelen] = '\0'; AFS_GLOCK(); /* XXX use "from" or "to" creds? NFS uses "to" creds */ error = afs_rename(VTOAFS(fdvp), fname, VTOAFS(tdvp), tname, cn_cred(tcnp)); #if !defined(AFS_DARWIN80_ENV) AFS_GUNLOCK(); VOP_UNLOCK(fvp, 0, p); if (error) goto abortit; /* XXX */ if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); #else if (error == EXDEV) { struct brequest *tb; struct afs_uspc_param mvReq; struct vcache *tvc; struct vcache *fvc = VTOAFS(fdvp); int code = 0; struct afs_fakestat_state fakestate; int fakestatdone = 0; tvc = VTOAFS(tdvp); /* unrewritten mount point? */ if (tvc->mvstat == 1) { if (tvc->mvid && (tvc->f.states & CMValid)) { struct vrequest treq; afs_InitFakeStat(&fakestate); code = afs_InitReq(&treq, vop_cred); if (!code) { fakestatdone = 1; code = afs_EvalFakeStat(&tvc, &fakestate, &treq); } else afs_PutFakeStat(&fakestate); } } if (!code) { /* at some point in the future we should allow other types */ mvReq.reqtype = AFS_USPC_UMV; mvReq.req.umv.id = afs_cr_uid(cn_cred(tcnp)); mvReq.req.umv.idtype = IDTYPE_UID; mvReq.req.umv.sCell = fvc->f.fid.Cell; mvReq.req.umv.sVolume = fvc->f.fid.Fid.Volume; mvReq.req.umv.sVnode = fvc->f.fid.Fid.Vnode; mvReq.req.umv.sUnique = fvc->f.fid.Fid.Unique; mvReq.req.umv.dCell = tvc->f.fid.Cell; mvReq.req.umv.dVolume = tvc->f.fid.Fid.Volume; mvReq.req.umv.dVnode = tvc->f.fid.Fid.Vnode; mvReq.req.umv.dUnique = tvc->f.fid.Fid.Unique; /* * su %d -c mv /afs/.:mount/%d:%d:%d:%d/%s * /afs/.:mount/%d:%d:%d:%d/%s where: * mvReq.req.umv.id, fvc->f.fid.Cell, fvc->f.fid.Fid.Volume, * fvc->f.fid.Fid.Vnode, fvc->f.fid.Fid.Unique, fname, * tvc->f.fid.Cell, tvc->f.fid.Fid.Volume, tvc->f.fid.Fid.Vnode, * tvc->f.fid.Fid.Unique, tname */ tb = afs_BQueue(BOP_MOVE, NULL, 0, 1, cn_cred(tcnp), 0L, 0L, &mvReq, fname, tname); /* wait to collect result */ while ((tb->flags & BUVALID) == 0) { tb->flags |= BUWAIT; afs_osi_Sleep(tb); } /* if we succeeded, clear the error. otherwise, EXDEV */ if (mvReq.retval == 0) error = 0; afs_BRelease(tb); } if (fakestatdone) afs_PutFakeStat(&fakestate); } AFS_GUNLOCK(); cache_purge(fdvp); cache_purge(fvp); cache_purge(tdvp); if (tvp) { cache_purge(tvp); if (!error) { vnode_recycle(tvp); } } if (!error) cache_enter(tdvp, fvp, tcnp); #endif FREE(fname, M_TEMP); FREE(tname, M_TEMP); return error; } int afs_vop_mkdir(ap) struct VOPPROT(mkdir_args) /* { * struct vnode *a_dvp; * struct vnode **a_vpp; * struct componentname *a_cnp; * struct vattr *a_vap; * } */ *ap; { struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; int error = 0; struct vcache *vcp; struct proc *p; GETNAME(); p = vop_cn_proc; #if defined(DIAGNOSTIC) && !defined(AFS_DARWIN80_ENV) if ((cnp->cn_flags & HASBUF) == 0) panic("afs_vop_mkdir: no name"); #endif AFS_GLOCK(); error = afs_mkdir(VTOAFS(dvp), name, vap, &vcp, vop_cn_cred); AFS_GUNLOCK(); if (error) { #ifndef AFS_DARWIN80_ENV VOP_ABORTOP(dvp, cnp); vput(dvp); #endif DROPNAME(); return (error); } if (vcp) { #ifdef AFS_DARWIN80_ENV afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0, 0); #endif *ap->a_vpp = AFSTOV(vcp); #ifndef AFS_DARWIN80_ENV /* XXX needed for multi-mount thing, but can't have it yet */ (*ap->a_vpp)->v_vfsp = dvp->v_vfsp; vn_lock(*ap->a_vpp, LK_EXCLUSIVE | LK_RETRY, p); #endif } else *ap->a_vpp = 0; DROPNAME(); #ifndef AFS_DARWIN80_ENV FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); vput(dvp); #endif return error; } int afs_vop_rmdir(ap) struct VOPPROT(rmdir_args) /* { * struct vnode *a_dvp; * struct vnode *a_vp; * struct componentname *a_cnp; * } */ *ap; { int error = 0; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; GETNAME(); if (dvp == vp) { #ifndef AFS_DARWIN80_ENV vrele(dvp); vput(vp); FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); #endif DROPNAME(); return (EINVAL); } AFS_GLOCK(); error = afs_rmdir(VTOAFS(dvp), name, vop_cn_cred); AFS_GUNLOCK(); DROPNAME(); cache_purge(dvp); cache_purge(vp); #ifndef AFS_DARWIN80_ENV vput(dvp); vput(vp); #endif return error; } int afs_vop_symlink(ap) struct VOPPROT(symlink_args) /* { * struct vnode *a_dvp; * struct vnode **a_vpp; * struct componentname *a_cnp; * struct vattr *a_vap; * char *a_target; * } */ *ap; { struct vnode *dvp = ap->a_dvp; int error = 0; /* NFS ignores a_vpp; so do we. */ GETNAME(); AFS_GLOCK(); error = afs_symlink(VTOAFS(dvp), name, ap->a_vap, ap->a_target, vop_cn_cred); AFS_GUNLOCK(); DROPNAME(); #ifndef AFS_DARWIN80_ENV FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); vput(dvp); #endif return error; } int afs_vop_readdir(ap) struct VOPPROT(readdir_args) /* { * struct vnode *a_vp; * struct uio *a_uio; * struct ucred *a_cred; * int *a_eofflag; * u_long *a_cookies; * int ncookies; * } */ *ap; { int error; off_t off; /* printf("readdir %x cookies %x ncookies %d\n", ap->a_vp, ap->a_cookies, ap->a_ncookies); */ #ifdef AFS_DARWIN80_ENV /* too much work for now */ /* should only break nfs exports */ if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) return (EINVAL); #endif off = AFS_UIO_OFFSET(ap->a_uio); AFS_GLOCK(); error = afs_readdir(VTOAFS(ap->a_vp), ap->a_uio, vop_cred, ap->a_eofflag); AFS_GUNLOCK(); #ifndef AFS_DARWIN80_ENV if (!error && ap->a_ncookies != NULL) { struct uio *uio = ap->a_uio; const struct dirent *dp, *dp_start, *dp_end; int ncookies; u_long *cookies, *cookiep; if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) panic("afs_readdir: burned cookies"); dp = (const struct dirent *) ((const char *)uio->uio_iov->iov_base - (uio->uio_offset - off)); dp_end = (const struct dirent *)uio->uio_iov->iov_base; for (dp_start = dp, ncookies = 0; dp < dp_end; dp = (const struct dirent *)((const char *)dp + dp->d_reclen)) ncookies++; MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); for (dp = dp_start, cookiep = cookies; dp < dp_end; dp = (const struct dirent *)((const char *)dp + dp->d_reclen)) { off += dp->d_reclen; *cookiep++ = off; } *ap->a_cookies = cookies; *ap->a_ncookies = ncookies; } #endif return error; } int afs_vop_readlink(ap) struct VOPPROT(readlink_args) /* { * struct vnode *a_vp; * struct uio *a_uio; * struct ucred *a_cred; * } */ *ap; { int error; /* printf("readlink %x\n", ap->a_vp);*/ AFS_GLOCK(); error = afs_readlink(VTOAFS(ap->a_vp), ap->a_uio, vop_cred); AFS_GUNLOCK(); return error; } extern int prtactive; int afs_vop_inactive(ap) struct VOPPROT(inactive_args) /* { * struct vnode *a_vp; * struct proc *a_p; * } */ *ap; { struct vnode *vp = ap->a_vp; struct vcache *tvc = VTOAFS(vp); #ifndef AFS_DARWIN80_ENV if (prtactive && vp->v_usecount != 0) vprint("afs_vop_inactive(): pushing active", vp); #endif if (tvc) { #ifdef AFS_DARWIN80_ENV int unlinked = tvc->f.states & CUnlinked; #endif AFS_GLOCK(); afs_InactiveVCache(tvc, 0); /* decrs ref counts */ AFS_GUNLOCK(); #ifdef AFS_DARWIN80_ENV if (unlinked) { vnode_recycle(vp); cache_purge(vp); } #endif } #ifndef AFS_DARWIN80_ENV VOP_UNLOCK(vp, 0, ap->a_p); #endif return 0; } int afs_vop_reclaim(ap) struct VOPPROT(reclaim_args) /* { * struct vnode *a_vp; * } */ *ap; { int error = 0; int sl, writelocked; struct vnode *vp = ap->a_vp; struct vcache *tvc = VTOAFS(vp); osi_Assert(!ISAFS_GLOCK()); cache_purge(vp); /* just in case... */ if (tvc) { AFS_GLOCK(); writelocked = (0 == NBObtainWriteLock(&afs_xvcache, 335)); if (!writelocked) { ObtainWriteLock(&afs_xvreclaim, 176); #ifdef AFS_DARWIN80_ENV vnode_clearfsnode(AFSTOV(tvc)); vnode_removefsref(AFSTOV(tvc)); #else tvc->v->v_data = NULL; /* remove from vnode */ #endif AFSTOV(tvc) = NULL; /* also drop the ptr to vnode */ tvc->f.states |= CVInit; /* also CDeadVnode? */ tvc->nextfree = ReclaimedVCList; ReclaimedVCList = tvc; ReleaseWriteLock(&afs_xvreclaim); } else { error = afs_FlushVCache(tvc, &sl); /* toss our stuff from vnode */ if (tvc->f.states & (CVInit #ifdef AFS_DARWIN80_ENV | CDeadVnode #endif )) { tvc->f.states &= ~(CVInit #ifdef AFS_DARWIN80_ENV | CDeadVnode #endif ); afs_osi_Wakeup(&tvc->f.states); } if (!error && vnode_fsnode(vp)) panic("afs_reclaim: vnode not cleaned"); if (!error && (tvc->v != NULL)) panic("afs_reclaim: vcache not cleaned"); ReleaseWriteLock(&afs_xvcache); } AFS_GUNLOCK(); } return error; } /* * Return POSIX pathconf information applicable to ufs filesystems. */ afs_vop_pathconf(ap) struct VOPPROT(pathconf_args) /* { * struct vnode *a_vp; * int a_name; * int *a_retval; * } */ *ap; { AFS_STATCNT(afs_cntl); switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; break; case _PC_NAME_MAX: *ap->a_retval = NAME_MAX; break; case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; break; case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; break; case _PC_NO_TRUNC: *ap->a_retval = 1; break; case _PC_PIPE_BUF: return EINVAL; break; case _PC_NAME_CHARS_MAX: *ap->a_retval = NAME_MAX; break; case _PC_CASE_SENSITIVE: *ap->a_retval = 1; break; case _PC_CASE_PRESERVING: *ap->a_retval = 1; break; default: return EINVAL; } return 0; } /* * Advisory record locking support (fcntl() POSIX style) */ int afs_vop_advlock(ap) struct VOPPROT(advlock_args) /* { * struct vnode *a_vp; * caddr_t a_id; * int a_op; * struct flock *a_fl; * int a_flags; * } */ *ap; { int error; struct ucred *tcr; int clid; int op; #ifdef AFS_DARWIN80_ENV proc_t p; tcr=vop_cred; #else struct proc *p = current_proc(); struct ucred cr; pcred_readlock(p); cr = *p->p_cred->pc_ucred; pcred_unlock(p); tcr=&cr; #endif if (ap->a_flags & F_POSIX) { #ifdef AFS_DARWIN80_ENV p = (proc_t) ap->a_id; clid = proc_pid(p); #else p = (struct proc *) ap->a_id; clid = p->p_pid; #endif } else { clid = (int)ap->a_id; } if (ap->a_op == F_UNLCK) { op = F_SETLK; } else if (ap->a_op == F_SETLK && ap->a_flags & F_WAIT) { op = F_SETLKW; } else { op = ap->a_op; } AFS_GLOCK(); error = afs_lockctl(VTOAFS(ap->a_vp), ap->a_fl, op, tcr, clid); AFS_GUNLOCK(); return error; } int afs_vop_blktooff(ap) struct VOPPROT(blktooff_args) /* { * struct vnode *a_vp; * daddr_t a_lblkno; * off_t *a_offset; * } */ *ap; { *ap->a_offset = (off_t) (ap->a_lblkno * DEV_BSIZE); return 0; } int afs_vop_offtoblk(ap) struct VOPPROT(offtoblk_args) /* { * struct vnode *a_vp; * off_t a_offset; * daddr_t *a_lblkno; * } */ *ap; { *ap->a_lblkno = (daddr_t) (ap->a_offset / DEV_BSIZE); return (0); } #ifndef AFS_DARWIN80_ENV int afs_vop_lock(ap) struct VOPPROT(lock_args) /* { * struct vnode *a_vp; * } */ *ap; { struct vnode *vp = ap->a_vp; struct vcache *avc = VTOAFS(vp); if (vp->v_tag == VT_NON) return (ENOENT); return (lockmgr(&avc->rwlock, ap->a_flags, &vp->v_interlock, ap->a_p)); } int afs_vop_unlock(ap) struct VOPPROT(unlock_args) /* { * struct vnode *a_vp; * } */ *ap; { struct vnode *vp = ap->a_vp; struct vcache *avc = VTOAFS(vp); return (lockmgr (&avc->rwlock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } int afs_vop_truncate(ap) struct VOPPROT(truncate_args) /* { * struct vnode *a_vp; * off_t a_length; * int a_flags; * struct ucred *a_cred; * struct proc *a_p; * } */ *ap; { /* printf("stray afs_vop_truncate\n"); */ return ENOTSUP; } int afs_vop_update(ap) struct VOPPROT(update_args) /* { * struct vnode *a_vp; * struct timeval *a_access; * struct timeval *a_modify; * int a_waitfor; * } */ *ap; { /* printf("stray afs_vop_update\n"); */ return ENOTSUP; } int afs_vop_bmap(ap) struct VOPPROT(bmap_args) /* { * struct vnode *a_vp; * daddr_t a_bn; * struct vnode **a_vpp; * daddr_t *a_bnp; * int *a_runp; * int *a_runb; * } */ *ap; { int error; if (ap->a_bnp) { *ap->a_bnp = ap->a_bn * (PAGE_SIZE / DEV_BSIZE); } if (ap->a_vpp) { *ap->a_vpp = ap->a_vp; } if (ap->a_runp != NULL) *ap->a_runp = 0; #ifdef notyet if (ap->a_runb != NULL) *ap->a_runb = 0; #endif return 0; } int afs_vop_strategy(ap) struct VOPPROT(strategy_args) /* { * struct buf *a_bp; * } */ *ap; { int error; AFS_GLOCK(); error = afs_ustrategy(ap->a_bp); AFS_GUNLOCK(); return error; } int afs_vop_print(ap) struct VOPPROT(print_args) /* { * struct vnode *a_vp; * } */ *ap; { struct vnode *vp = ap->a_vp; struct vcache *vc = VTOAFS(ap->a_vp); int s = vc->f.states; printf("tag %d, fid: %ld.%x.%x.%x, opens %d, writers %d", vp->v_tag, vc->f.fid.Cell, vc->f.fid.Fid.Volume, vc->f.fid.Fid.Vnode, vc->f.fid.Fid.Unique, vc->opens, vc->execsOrWriters); printf("\n states%s%s%s%s%s", (s & CStatd) ? " statd" : "", (s & CRO) ? " readonly" : "", (s & CDirty) ? " dirty" : "", (s & CMAPPED) ? " mapped" : "", (s & CVFlushed) ? " flush in progress" : ""); if (UBCISVALID(vp)) { printf("\n UBC: "); if (UBCINFOEXISTS(vp)) { printf("exists, "); printf("refs %d%s%s", vp->v_ubcinfo->ui_refcount, ubc_issetflags(vp, UI_HASOBJREF) ? " HASOBJREF" : "", ubc_issetflags(vp, UI_WASMAPPED) ? " WASMAPPED" : ""); } else printf("does not exist"); } printf("\n"); return 0; } int afs_vop_islocked(ap) struct VOPPROT(islocked_args) /* { * struct vnode *a_vp; * } */ *ap; { struct vcache *vc = VTOAFS(ap->a_vp); return lockstatus(&vc->rwlock); } int afs_vop_cmap(ap) struct VOPPROT(cmap_args) /* { * struct vnode *a_vp; * off_t a_foffset; * size_t a_size; * daddr_t *a_bpn; * size_t *a_run; * void *a_poff; * } */ *ap; { *ap->a_bpn = (daddr_t) (ap->a_foffset / DEV_BSIZE); *ap->a_run = MAX(ap->a_size, AFS_CHUNKSIZE(ap->a_foffset)); return 0; } #endif int afs_darwin_getnewvnode(struct vcache *avc) { #ifdef AFS_DARWIN80_ENV vnode_t vp; int error, dead; struct vnode_fsparam par; memset(&par, 0, sizeof(struct vnode_fsparam)); par.vnfs_vtype = VNON; par.vnfs_vops = afs_dead_vnodeop_p; par.vnfs_flags = VNFS_NOCACHE|VNFS_CANTCACHE; par.vnfs_mp = afs_globalVFS; par.vnfs_fsnode = avc; error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &par, &vp); if (!error) { vnode_addfsref(vp); vnode_ref(vp); avc->v = vp; vnode_recycle(vp); /* terminate as soon as iocount drops */ avc->f.states |= CDeadVnode; } return error; #else while (getnewvnode(VT_AFS, afs_globalVFS, afs_vnodeop_p, &avc->v)) { /* no vnodes available, force an alloc (limits be damned)! */ printf("failed to get vnode\n"); } avc->v->v_data = (void *)avc; return 0; #endif } #ifdef AFS_DARWIN80_ENV /* if this fails, then tvc has been unrefed and may have been freed. Don't touch! */ int afs_darwin_finalizevnode(struct vcache *avc, struct vnode *dvp, struct componentname *cnp, int isroot, int locked) { vnode_t ovp; vnode_t nvp; int error; struct vnode_fsparam par; if (!locked) { AFS_GLOCK(); ObtainWriteLock(&avc->lock,325); } ovp = AFSTOV(avc); if (!(avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON) { AFS_GUNLOCK(); #if 0 /* unsupported */ if (dvp && cnp) vnode_update_identity(ovp, dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_hash, VNODE_UPDATE_PARENT|VNODE_UPDATE_NAME); #endif /* Can end up in reclaim... drop GLOCK */ vnode_rele(ovp); AFS_GLOCK(); if (!locked) { ReleaseWriteLock(&avc->lock); AFS_GUNLOCK(); } return 0; } if ((avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON) panic("vcache %p should not be CDeadVnode", avc); AFS_GUNLOCK(); memset(&par, 0, sizeof(struct vnode_fsparam)); par.vnfs_mp = afs_globalVFS; par.vnfs_vtype = avc->f.m.Type; par.vnfs_vops = afs_vnodeop_p; par.vnfs_filesize = avc->f.m.Length; par.vnfs_fsnode = avc; par.vnfs_dvp = dvp; if (cnp && (cnp->cn_flags & ISDOTDOT) == 0) par.vnfs_cnp = cnp; if (!dvp || !cnp || (cnp->cn_flags & MAKEENTRY) == 0) par.vnfs_flags = VNFS_NOCACHE; if (isroot) par.vnfs_markroot = 1; error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &par, &nvp); if (!error) { vnode_addfsref(nvp); if ((avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON) printf("vcache %p should not be CDeadVnode", avc); if (avc->v == ovp) { if (!(avc->f.states & CVInit)) { vnode_clearfsnode(ovp); vnode_removefsref(ovp); } /* we're discarding on a fixup. mark for recycle */ if (!(avc->f.states & CDeadVnode)) vnode_recycle(ovp); } avc->v = nvp; avc->f.states &=~ CDeadVnode; /* If we were carrying an extra ref for dirty, hold/push it. */ if (avc->f.ddirty_flags) { vnode_get(nvp); vnode_ref(nvp); } /* If we were carrying an extra ref for shadow, hold/push it. */ if (avc->f.shadow.vnode) { vnode_get(nvp); vnode_ref(nvp); } } /* Drop any extra dirty ref on the old vnode */ if (avc->f.ddirty_flags) { vnode_put(ovp); vnode_rele(ovp); } /* Drop any extra shadow ref on the old vnode */ if (avc->f.shadow.vnode) { vnode_put(ovp); vnode_rele(ovp); } /* If it's ref'd still, unmark stat'd to force new lookup */ if ((vnode_vtype(ovp) != avc->f.m.Type) && VREFCOUNT_GT(avc, 1)) avc->f.states &= ~CStatd; vnode_put(ovp); vnode_rele(ovp); AFS_GLOCK(); if (!locked) ReleaseWriteLock(&avc->lock); if (!error) afs_osi_Wakeup(&avc->f.states); if (!locked) AFS_GUNLOCK(); return error; } #endif