void osi_PostPopulateVCache(struct vcache *avc) { AFSTOV(avc)->v_mount = afs_globalVFS; vSetType(avc, VREG); }
/** * Reset volume name to volume id mapping cache. * @param flags */ void afs_CheckVolumeNames(int flags) { afs_int32 i, j; struct volume *tv; unsigned int now; struct vcache *tvc; afs_int32 *volumeID, *cellID, vsize, nvols; #ifdef AFS_DARWIN80_ENV vnode_t tvp; #endif AFS_STATCNT(afs_CheckVolumeNames); nvols = 0; volumeID = cellID = NULL; vsize = 0; ObtainReadLock(&afs_xvolume); if (flags & AFS_VOLCHECK_EXPIRED) { /* * allocate space to hold the volumeIDs and cellIDs, only if * we will be invalidating the mountpoints later on */ for (i = 0; i < NVOLS; i++) for (tv = afs_volumes[i]; tv; tv = tv->next) ++vsize; volumeID = (afs_int32 *) afs_osi_Alloc(2 * vsize * sizeof(*volumeID)); cellID = (volumeID) ? volumeID + vsize : 0; } now = osi_Time(); for (i = 0; i < NVOLS; i++) { for (tv = afs_volumes[i]; tv; tv = tv->next) { if (flags & AFS_VOLCHECK_EXPIRED) { if (((tv->expireTime < (now + 10)) && (tv->states & VRO)) || (flags & AFS_VOLCHECK_FORCE)) { afs_ResetVolumeInfo(tv); /* also resets status */ if (volumeID) { volumeID[nvols] = tv->volume; cellID[nvols] = tv->cell; } ++nvols; continue; } } /* ??? */ if (flags & (AFS_VOLCHECK_BUSY | AFS_VOLCHECK_FORCE)) { for (j = 0; j < AFS_MAXHOSTS; j++) tv->status[j] = not_busy; } } } ReleaseReadLock(&afs_xvolume); /* next ensure all mt points are re-evaluated */ if (nvols || (flags & (AFS_VOLCHECK_FORCE | AFS_VOLCHECK_MTPTS))) { loop: ObtainReadLock(&afs_xvcache); for (i = 0; i < VCSIZE; i++) { for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) { /* if the volume of "mvid" of the vcache entry is among the * ones we found earlier, then we re-evaluate it. Also, if the * force bit is set or we explicitly asked to reevaluate the * mt-pts, we clean the cmvalid bit */ if ((flags & (AFS_VOLCHECK_FORCE | AFS_VOLCHECK_MTPTS)) || (tvc->mvid && inVolList(tvc->mvid, nvols, volumeID, cellID))) tvc->f.states &= ~CMValid; /* If the volume that this file belongs to was reset earlier, * then we should remove its callback. * Again, if forced, always do it. */ if ((tvc->f.states & CRO) && (inVolList(&tvc->f.fid, nvols, volumeID, cellID) || (flags & AFS_VOLCHECK_FORCE))) { if (tvc->f.states & CVInit) { ReleaseReadLock(&afs_xvcache); afs_osi_Sleep(&tvc->f.states); goto loop; } #ifdef AFS_DARWIN80_ENV if (tvc->f.states & CDeadVnode) { ReleaseReadLock(&afs_xvcache); afs_osi_Sleep(&tvc->f.states); goto loop; } tvp = AFSTOV(tvc); if (vnode_get(tvp)) continue; if (vnode_ref(tvp)) { AFS_GUNLOCK(); /* AFSTOV(tvc) may be NULL */ vnode_put(tvp); AFS_GLOCK(); continue; } #else AFS_FAST_HOLD(tvc); #endif ReleaseReadLock(&afs_xvcache); ObtainWriteLock(&afs_xcbhash, 485); /* LOCKXXX: We aren't holding tvc write lock? */ afs_DequeueCallback(tvc); tvc->f.states &= ~CStatd; ReleaseWriteLock(&afs_xcbhash); if (tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR)) osi_dnlc_purgedp(tvc); #ifdef AFS_DARWIN80_ENV vnode_put(AFSTOV(tvc)); /* our tvc ptr is still good until now */ AFS_FAST_RELE(tvc); ObtainReadLock(&afs_xvcache); #else ObtainReadLock(&afs_xvcache); /* our tvc ptr is still good until now */ AFS_FAST_RELE(tvc); #endif } } } osi_dnlc_purge(); /* definitely overkill, but it's safer this way. */ ReleaseReadLock(&afs_xvcache); } if (volumeID) afs_osi_Free(volumeID, 2 * vsize * sizeof(*volumeID)); } /*afs_CheckVolumeNames */
/* Purge pages beyond end-of-file, when truncating a file. * * Locking: no lock is held, not even the global lock. * activeV is raised. This is supposed to block pageins, but at present * it only works on Solaris. */ void osi_VM_Truncate(struct vcache *avc, int alen, afs_ucred_t *acred) { vnode_pager_setsize(AFSTOV(avc), alen); }
int afs_CheckRootVolume(void) { char rootVolName[32]; struct volume *tvp = NULL; int usingDynroot = afs_GetDynrootEnable(); int localcell; AFS_STATCNT(afs_CheckRootVolume); if (*afs_rootVolumeName == 0) { strcpy(rootVolName, "root.afs"); } else { strcpy(rootVolName, afs_rootVolumeName); } if (usingDynroot) { afs_GetDynrootFid(&afs_rootFid); tvp = afs_GetVolume(&afs_rootFid, NULL, READ_LOCK); } else { struct cell *lc = afs_GetPrimaryCell(READ_LOCK); if (!lc) return ENOENT; localcell = lc->cellNum; afs_PutCell(lc, READ_LOCK); tvp = afs_GetVolumeByName(rootVolName, localcell, 1, NULL, READ_LOCK); if (!tvp) { char buf[128]; int len = strlen(rootVolName); if ((len < 9) || strcmp(&rootVolName[len - 9], ".readonly")) { strcpy(buf, rootVolName); afs_strcat(buf, ".readonly"); tvp = afs_GetVolumeByName(buf, localcell, 1, NULL, READ_LOCK); } } if (tvp) { int volid = (tvp->roVol ? tvp->roVol : tvp->volume); afs_rootFid.Cell = localcell; if (afs_rootFid.Fid.Volume && afs_rootFid.Fid.Volume != volid && afs_globalVp) { /* If we had a root fid before and it changed location we reset * the afs_globalVp so that it will be reevaluated. * Just decrement the reference count. This only occurs during * initial cell setup and can panic the machine if we set the * count to zero and fs checkv is executed when the current * directory is /afs. */ #ifdef AFS_LINUX20_ENV { struct vrequest treq; struct vattr vattr; cred_t *credp; struct dentry *dp; struct vcache *vcp; afs_rootFid.Fid.Volume = volid; afs_rootFid.Fid.Vnode = 1; afs_rootFid.Fid.Unique = 1; credp = crref(); if (afs_InitReq(&treq, credp)) goto out; vcp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL); if (!vcp) goto out; afs_getattr(vcp, &vattr, credp); afs_fill_inode(AFSTOV(vcp), &vattr); dp = d_find_alias(AFSTOV(afs_globalVp)); #if defined(AFS_LINUX24_ENV) #if defined(HAVE_DCACHE_LOCK) spin_lock(&dcache_lock); #else spin_lock(&AFSTOV(vcp)->i_lock); #endif #if defined(AFS_LINUX26_ENV) spin_lock(&dp->d_lock); #endif #endif list_del_init(&dp->d_alias); list_add(&dp->d_alias, &(AFSTOV(vcp)->i_dentry)); dp->d_inode = AFSTOV(vcp); #if defined(AFS_LINUX24_ENV) #if defined(AFS_LINUX26_ENV) spin_unlock(&dp->d_lock); #endif #if defined(HAVE_DCACHE_LOCK) spin_unlock(&dcache_lock); #else spin_unlock(&AFSTOV(vcp)->i_lock); #endif #endif dput(dp); AFS_FAST_RELE(afs_globalVp); afs_globalVp = vcp; out: crfree(credp); } #else #ifdef AFS_DARWIN80_ENV afs_PutVCache(afs_globalVp); #else AFS_FAST_RELE(afs_globalVp); #endif afs_globalVp = 0; #endif } afs_rootFid.Fid.Volume = volid; afs_rootFid.Fid.Vnode = 1; afs_rootFid.Fid.Unique = 1; } } if (tvp) { afs_initState = 300; /* won */ afs_osi_Wakeup(&afs_initState); afs_PutVolume(tvp, READ_LOCK); } if (afs_rootFid.Fid.Volume) return 0; else return ENOENT; }
/* works like PFlushVolumeData */ void darwin_notify_perms(struct unixuser *auser, int event) { int i; struct afs_q *tq, *uq = NULL; struct vcache *tvc, *hnext; int isglock = ISAFS_GLOCK(); struct vnode *vp; struct vnode_attr va; int isctxtowner = 0; if (!afs_darwin_fsevents) return; VATTR_INIT(&va); VATTR_SET(&va, va_mode, 0777); if (event & UTokensObtained) VATTR_SET(&va, va_uid, auser->uid); else VATTR_SET(&va, va_uid, -2); /* nobody */ if (!isglock) AFS_GLOCK(); if (!(vfs_context_owner == current_thread())) { get_vfs_context(); isctxtowner = 1; } loop: ObtainReadLock(&afs_xvcache); for (i = 0; i < VCSIZE; i++) { for (tq = afs_vhashTV[i].prev; tq != &afs_vhashTV[i]; tq = uq) { uq = QPrev(tq); tvc = QTOVH(tq); if (tvc->f.states & CDeadVnode) { /* we can afford to be best-effort */ continue; } /* no per-file acls, so only notify on directories */ if (!(vp = AFSTOV(tvc)) || !vnode_isdir(AFSTOV(tvc))) continue; /* dynroot object. no callbacks. anonymous ACL. just no. */ if (afs_IsDynrootFid(&tvc->f.fid)) continue; /* no fake fsevents on mount point sources. leaks refs */ if (tvc->mvstat == 1) continue; /* if it's being reclaimed, just pass */ if (vnode_get(vp)) continue; if (vnode_ref(vp)) { AFS_GUNLOCK(); vnode_put(vp); AFS_GLOCK(); continue; } ReleaseReadLock(&afs_xvcache); /* Avoid potentially re-entering on this lock */ if (0 == NBObtainWriteLock(&tvc->lock, 234)) { tvc->f.states |= CEvent; AFS_GUNLOCK(); vnode_setattr(vp, &va, afs_osi_ctxtp); tvc->f.states &= ~CEvent; vnode_put(vp); AFS_GLOCK(); ReleaseWriteLock(&tvc->lock); } ObtainReadLock(&afs_xvcache); uq = QPrev(tq); /* our tvc ptr is still good until now */ AFS_FAST_RELE(tvc); } } ReleaseReadLock(&afs_xvcache); if (isctxtowner) put_vfs_context(); if (!isglock) AFS_GUNLOCK(); }
/* Try to invalidate pages, for "fs flush" or "fs flushv"; or * try to free pages, when deleting a file. * * Locking: the vcache entry's lock is held. It may be dropped and * re-obtained. * * Since we drop and re-obtain the lock, we can't guarantee that there won't * be some pages around when we return, newly created by concurrent activity. */ void osi_VM_TryToSmush(struct vcache *avc, afs_ucred_t *acred, int sync) { struct vnode *vp; int tries, code; int islocked; SPLVAR; vp = AFSTOV(avc); VI_LOCK(vp); if (vp->v_iflag & VI_DOOMED) { VI_UNLOCK(vp); USERPRI; return; } VI_UNLOCK(vp); islocked = VOP_ISLOCKED(vp); if (islocked == LK_EXCLOTHER) panic("Trying to Smush over someone else's lock"); else if (islocked == LK_SHARED) { afs_warn("Trying to Smush with a shared lock"); vn_lock(vp, LK_UPGRADE); } else if (!islocked) vn_lock(vp, LK_EXCLUSIVE); if (vp->v_bufobj.bo_object != NULL) { VM_OBJECT_LOCK(vp->v_bufobj.bo_object); /* * Do we really want OBJPC_SYNC? OBJPC_INVAL would be * faster, if invalidation is really what we are being * asked to do. (It would make more sense, too, since * otherwise this function is practically identical to * osi_VM_StoreAllSegments().) -GAW */ /* * Dunno. We no longer resemble osi_VM_StoreAllSegments, * though maybe that's wrong, now. And OBJPC_SYNC is the * common thing in 70 file systems, it seems. Matt. */ vm_object_page_clean(vp->v_bufobj.bo_object, 0, 0, OBJPC_SYNC); VM_OBJECT_UNLOCK(vp->v_bufobj.bo_object); } tries = 5; code = osi_vinvalbuf(vp, V_SAVE, PCATCH, 0); while (code && (tries > 0)) { afs_warn("TryToSmush retrying vinvalbuf"); code = osi_vinvalbuf(vp, V_SAVE, PCATCH, 0); --tries; } if (islocked == LK_SHARED) vn_lock(vp, LK_DOWNGRADE); else if (!islocked) VOP_UNLOCK(vp, 0); USERPRI; }
afs_root(struct mount *mp, struct vnode **vpp) #endif { int error; struct vrequest treq; struct vcache *tvp = 0; struct vcache *gvp; #if !defined(AFS_FBSD53_ENV) || defined(AFS_FBSD80_ENV) struct thread *td = curthread; #endif struct ucred *cr = osi_curcred(); AFS_GLOCK(); AFS_STATCNT(afs_root); crhold(cr); tryagain: if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { tvp = afs_globalVp; error = 0; } else { if (!(error = afs_InitReq(&treq, cr)) && !(error = afs_CheckInit())) { tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL); /* we really want this to stay around */ if (tvp) { gvp = afs_globalVp; afs_globalVp = tvp; if (gvp) { afs_PutVCache(gvp); if (tvp != afs_globalVp) { /* someone raced us and won */ afs_PutVCache(tvp); goto tryagain; } } } else error = ENOENT; } } if (tvp) { struct vnode *vp = AFSTOV(tvp); ASSERT_VI_UNLOCKED(vp, "afs_root"); AFS_GUNLOCK(); error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td); AFS_GLOCK(); /* we dropped the glock, so re-check everything it had serialized */ if (!afs_globalVp || !(afs_globalVp->f.states & CStatd) || tvp != afs_globalVp) { vput(vp); afs_PutVCache(tvp); goto tryagain; } if (error != 0) goto tryagain; /* * I'm uncomfortable about this. Shouldn't this happen at a * higher level, and shouldn't we busy the top-level directory * to prevent recycling? */ vp->v_vflag |= VV_ROOT; afs_globalVFS = mp; *vpp = vp; } afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, tvp ? AFSTOV(tvp) : NULL, ICL_TYPE_INT32, error); AFS_GUNLOCK(); crfree(cr); return error; }
/* question: does afs_create need to set CDirty in the adp or the avc? * I think we can get away without it, but I'm not sure. Note that * afs_setattr is called in here for truncation. */ #ifdef AFS_SGI64_ENV int afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, int flags, int amode, struct vcache **avcp, afs_ucred_t *acred) #else /* AFS_SGI64_ENV */ int afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, enum vcexcl aexcl, int amode, struct vcache **avcp, afs_ucred_t *acred) #endif /* AFS_SGI64_ENV */ { afs_int32 origCBs, origZaps, finalZaps; struct vrequest treq; afs_int32 code; struct afs_conn *tc; struct VenusFid newFid; struct AFSStoreStatus InStatus; struct AFSFetchStatus OutFidStatus, OutDirStatus; struct AFSVolSync tsync; struct AFSCallBack CallBack; afs_int32 now; struct dcache *tdc; afs_size_t offset, len; struct server *hostp = 0; struct vcache *tvc; struct volume *volp = 0; struct afs_fakestat_state fakestate; struct rx_connection *rxconn; XSTATS_DECLS; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_create); if ((code = afs_InitReq(&treq, acred))) goto done2; afs_Trace3(afs_iclSetp, CM_TRACE_CREATE, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname, ICL_TYPE_INT32, amode); afs_InitFakeStat(&fakestate); #ifdef AFS_SGI65_ENV /* If avcp is passed not null, it's the old reference to this file. * We can use this to avoid create races. For now, just decrement * the reference count on it. */ if (*avcp) { AFS_RELE(AFSTOV(*avcp)); *avcp = NULL; } #endif if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done3; } if (!afs_ENameOK(aname)) { code = EINVAL; goto done3; } switch (attrs->va_type) { case VBLK: case VCHR: #if !defined(AFS_SUN5_ENV) case VSOCK: #endif case VFIFO: /* We don't support special devices or FIFOs */ code = EINVAL; goto done3; default: ; } AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, &treq); if (code) goto done; tagain: code = afs_VerifyVCache(adp, &treq); if (code) goto done; /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1); ObtainWriteLock(&adp->lock, 135); if (tdc) ObtainSharedLock(&tdc->lock, 630); /* * Make sure that the data in the cache is current. We may have * received a callback while we were waiting for the write lock. */ if (!(adp->f.states & CStatd) || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto tagain; } if (tdc) { /* see if file already exists. If it does, we only set * the size attributes (to handle O_TRUNC) */ code = afs_dir_Lookup(tdc, aname, &newFid.Fid); /* use dnlc first xxx */ if (code == 0) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); ReleaseWriteLock(&adp->lock); #ifdef AFS_SGI64_ENV if (flags & VEXCL) { #else if (aexcl != NONEXCL) { #endif code = EEXIST; /* file exists in excl mode open */ goto done; } /* found the file, so use it */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; tvc = NULL; if (newFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&newFid, &treq, NULL, adp, aname); } if (!tvc) /* lookup failed or wasn't called */ tvc = afs_GetVCache(&newFid, &treq, NULL, NULL); if (tvc) { /* if the thing exists, we need the right access to open it. * we must check that here, since no other checks are * made by the open system call */ len = attrs->va_size; /* only do the truncate */ /* * We used to check always for READ access before; the * problem is that we will fail if the existing file * has mode -w-w-w, which is wrong. */ if ((amode & VREAD) && !afs_AccessOK(tvc, PRSFS_READ, &treq, CHECK_MODE_BITS)) { afs_PutVCache(tvc); code = EACCES; goto done; } #if defined(AFS_DARWIN80_ENV) if ((amode & VWRITE) || VATTR_IS_ACTIVE(attrs, va_data_size)) #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if ((amode & VWRITE) || (attrs->va_mask & AT_SIZE)) #else if ((amode & VWRITE) || len != 0xffffffff) #endif { /* needed for write access check */ tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; tvc->f.parent.unique = adp->f.fid.Fid.Unique; /* need write mode for these guys */ if (!afs_AccessOK (tvc, PRSFS_WRITE, &treq, CHECK_MODE_BITS)) { afs_PutVCache(tvc); code = EACCES; goto done; } } #if defined(AFS_DARWIN80_ENV) if (VATTR_IS_ACTIVE(attrs, va_data_size)) #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (attrs->va_mask & AT_SIZE) #else if (len != 0xffffffff) #endif { if (vType(tvc) != VREG) { afs_PutVCache(tvc); code = EISDIR; goto done; } /* do a truncate */ #if defined(AFS_DARWIN80_ENV) VATTR_INIT(attrs); VATTR_SET_SUPPORTED(attrs, va_data_size); VATTR_SET_ACTIVE(attrs, va_data_size); #elif defined(UKERNEL) attrs->va_mask = ATTR_SIZE; #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) attrs->va_mask = AT_SIZE; #else VATTR_NULL(attrs); #endif attrs->va_size = len; ObtainWriteLock(&tvc->lock, 136); tvc->f.states |= CCreating; ReleaseWriteLock(&tvc->lock); #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) #if defined(AFS_SGI64_ENV) code = afs_setattr(VNODE_TO_FIRST_BHV((vnode_t *) tvc), attrs, 0, acred); #else code = afs_setattr(tvc, attrs, 0, acred); #endif /* AFS_SGI64_ENV */ #else /* SUN5 || SGI */ code = afs_setattr(tvc, attrs, acred); #endif /* SUN5 || SGI */ ObtainWriteLock(&tvc->lock, 137); tvc->f.states &= ~CCreating; ReleaseWriteLock(&tvc->lock); if (code) { afs_PutVCache(tvc); goto done; } } *avcp = tvc; } else code = ENOENT; /* shouldn't get here */ /* make sure vrefCount bumped only if code == 0 */ goto done; } } /* if we create the file, we don't do any access checks, since * that's how O_CREAT is supposed to work */ if (adp->f.states & CForeign) { origCBs = afs_allCBs; origZaps = afs_allZaps; } else { origCBs = afs_evenCBs; /* if changes, we don't really have a callback */ origZaps = afs_evenZaps; /* number of even numbered vnodes discarded */ } InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; InStatus.ClientModTime = osi_Time(); InStatus.Group = (afs_int32) afs_cr_gid(acred); if (AFS_NFSXLATORREQ(acred)) { /* * XXX The following is mainly used to fix a bug in the HP-UX * nfs client where they create files with mode of 0 without * doing any setattr later on to fix it. * XXX */ #if defined(AFS_AIX_ENV) if (attrs->va_mode != -1) { #else #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (attrs->va_mask & AT_MODE) { #else if (attrs->va_mode != ((unsigned short)-1)) { #endif #endif if (!attrs->va_mode) attrs->va_mode = 0x1b6; /* XXX default mode: rw-rw-rw XXX */ } } if (!AFS_IS_DISCONNECTED) { /* If not disconnected, connect to the server.*/ InStatus.UnixModeBits = attrs->va_mode & 0xffff; /* only care about protection bits */ do { tc = afs_Conn(&adp->f.fid, &treq, SHARED_LOCK, &rxconn); if (tc) { hostp = tc->srvr->server; /* remember for callback processing */ now = osi_Time(); XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE); RX_AFS_GUNLOCK(); code = RXAFS_CreateFile(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; CallBack.ExpirationTime += now; } else code = -1; } while (afs_Analyze (tc, rxconn, code, &adp->f.fid, &treq, AFS_STATS_FS_RPCIDX_CREATEFILE, SHARED_LOCK, NULL)); if ((code == EEXIST || code == UAEEXIST) && #ifdef AFS_SGI64_ENV !(flags & VEXCL) #else /* AFS_SGI64_ENV */ aexcl == NONEXCL #endif ) { /* if we get an EEXIST in nonexcl mode, just do a lookup */ if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } ReleaseWriteLock(&adp->lock); #if defined(AFS_SGI64_ENV) code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, NULL, 0, NULL, acred); #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred); #elif defined(UKERNEL) code = afs_lookup(adp, aname, avcp, acred, 0); #elif !defined(AFS_DARWIN_ENV) code = afs_lookup(adp, aname, avcp, acred); #endif goto done; } if (code) { if (code < 0) { ObtainWriteLock(&afs_xcbhash, 488); afs_DequeueCallback(adp); adp->f.states &= ~CStatd; ReleaseWriteLock(&afs_xcbhash); osi_dnlc_purgedp(adp); } ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto done; } } else { /* Generate a fake FID for disconnected mode. */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; afs_GenFakeFid(&newFid, VREG, 1); } /* if (!AFS_IS_DISCON_RW) */ /* otherwise, we should see if we can make the change to the dir locally */ if (tdc) UpgradeSToWLock(&tdc->lock, 631); if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) { /* we can do it locally */ ObtainWriteLock(&afs_xdcache, 291); code = afs_dir_Create(tdc, aname, &newFid.Fid); ReleaseWriteLock(&afs_xdcache); if (code) { ZapDCE(tdc); DZap(tdc); } } if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } if (AFS_IS_DISCON_RW) adp->f.m.LinkCount++; newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; ReleaseWriteLock(&adp->lock); volp = afs_FindVolume(&newFid, READ_LOCK); /* New tricky optimistic callback handling algorithm for file creation works * as follows. We create the file essentially with no locks set at all. File * server may thus handle operations from others cache managers as well as from * this very own cache manager that reference the file in question before * we managed to create the cache entry. However, if anyone else changes * any of the status information for a file, we'll see afs_evenCBs increase * (files always have even fids). If someone on this workstation manages * to do something to the file, they'll end up having to create a cache * entry for the new file. Either we'll find it once we've got the afs_xvcache * lock set, or it was also *deleted* the vnode before we got there, in which case * we will find evenZaps has changed, too. Thus, we only assume we have the right * status information if no callbacks or vnode removals have occurred to even * numbered files from the time the call started until the time that we got the xvcache * lock set. Of course, this also assumes that any call that modifies a file first * gets a write lock on the file's vnode, but if that weren't true, the whole cache manager * would fail, since no call would be able to update the local vnode status after modifying * a file on a file server. */ ObtainWriteLock(&afs_xvcache, 138); if (adp->f.states & CForeign) finalZaps = afs_allZaps; /* do this before calling newvcache */ else finalZaps = afs_evenZaps; /* do this before calling newvcache */ /* don't need to call RemoveVCB, since only path leaving a callback is the * one where we pass through afs_NewVCache. Can't have queued a VCB unless * we created and freed an entry between file creation time and here, and the * freeing of the vnode will change evenZaps. Don't need to update the VLRU * queue, since the find will only succeed in the event of a create race, and * then the vcache will be at the front of the VLRU queue anyway... */ if (!(tvc = afs_FindVCache(&newFid, 0, DO_STATS))) { tvc = afs_NewVCache(&newFid, hostp); if (tvc) { int finalCBs; ObtainWriteLock(&tvc->lock, 139); ObtainWriteLock(&afs_xcbhash, 489); finalCBs = afs_evenCBs; /* add the callback in */ if (adp->f.states & CForeign) { tvc->f.states |= CForeign; finalCBs = afs_allCBs; } if (origCBs == finalCBs && origZaps == finalZaps) { tvc->f.states |= CStatd; /* we've fake entire thing, so don't stat */ tvc->f.states &= ~CBulkFetching; if (!AFS_IS_DISCON_RW) { tvc->cbExpires = CallBack.ExpirationTime; afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); } } else { afs_DequeueCallback(tvc); tvc->f.states &= ~(CStatd | CUnique); tvc->callback = 0; if (tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR)) osi_dnlc_purgedp(tvc); } ReleaseWriteLock(&afs_xcbhash); if (AFS_IS_DISCON_RW) { afs_DisconAddDirty(tvc, VDisconCreate, 0); afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VREG); } else { afs_ProcessFS(tvc, &OutFidStatus, &treq); } tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; tvc->f.parent.unique = adp->f.fid.Fid.Unique; #if !defined(UKERNEL) if (volp && (volp->states & VPartVisible)) tvc->f.states |= CPartVisible; #endif ReleaseWriteLock(&tvc->lock); *avcp = tvc; code = 0; } else code = ENOENT; } else { /* otherwise cache entry already exists, someone else must * have created it. Comments used to say: "don't need write * lock to *clear* these flags" but we should do it anyway. * Code used to clear stat bit and callback, but I don't see * the point -- we didn't have a create race, somebody else just * snuck into NewVCache before we got here, probably a racing * lookup. */ *avcp = tvc; code = 0; } ReleaseWriteLock(&afs_xvcache); done: AFS_DISCON_UNLOCK(); done3: if (volp) afs_PutVolume(volp, READ_LOCK); if (code == 0) { if (afs_mariner) afs_AddMarinerName(aname, *avcp); /* return the new status in vattr */ afs_CopyOutAttrs(*avcp, attrs); if (afs_mariner) afs_MarinerLog("store$Creating", *avcp); } afs_PutFakeStat(&fakestate); code = afs_CheckCode(code, &treq, 20); done2: return code; } /* * Check to see if we can track the change locally: requires that * we have sufficiently recent info in data cache. If so, we * know the new DataVersion number, and place it correctly in both the * data and stat cache entries. This routine returns 1 if we should * do the operation locally, and 0 otherwise. * * This routine must be called with the stat cache entry write-locked, * and dcache entry write-locked. */ int afs_LocalHero(struct vcache *avc, struct dcache *adc, AFSFetchStatus * astat, int aincr) { afs_int32 ok; afs_hyper_t avers; AFS_STATCNT(afs_LocalHero); hset64(avers, astat->dataVersionHigh, astat->DataVersion); /* avers *is* the version number now, no matter what */ if (adc) { /* does what's in the dcache *now* match what's in the vcache *now*, * and do we have a valid callback? if not, our local copy is not "ok" */ ok = (hsame(avc->f.m.DataVersion, adc->f.versionNo) && avc->callback && (avc->f.states & CStatd) && avc->cbExpires >= osi_Time()); } else { ok = 0; } if (ok) { /* check that the DV on the server is what we expect it to be */ afs_hyper_t newDV; hset(newDV, adc->f.versionNo); hadd32(newDV, aincr); if (!hsame(avers, newDV)) { ok = 0; } } #if defined(AFS_SGI_ENV) osi_Assert(avc->v.v_type == VDIR); #endif /* The bulk status code used the length as a sequence number. */ /* Don't update the vcache entry unless the stats are current. */ if (avc->f.states & CStatd) { hset(avc->f.m.DataVersion, avers); #ifdef AFS_64BIT_CLIENT FillInt64(avc->f.m.Length, astat->Length_hi, astat->Length); #else /* AFS_64BIT_CLIENT */ avc->f.m.Length = astat->Length; #endif /* AFS_64BIT_CLIENT */ avc->f.m.Date = astat->ClientModTime; } if (ok) { /* we've been tracking things correctly */ adc->dflags |= DFEntryMod; adc->f.versionNo = avers; return 1; } else { if (adc) { ZapDCE(adc); DZap(adc); } if (avc->f.states & CStatd) { osi_dnlc_purgedp(avc); } return 0; } }
struct vcache * osi_dnlc_lookup(struct vcache *adp, char *aname, int locktype) { struct vcache *tvc; int LRUme; unsigned int key, skey; char *ts = aname; struct nc *tnc, *tnc1 = 0; int safety; #ifdef AFS_DARWIN80_ENV vnode_t tvp; #endif ma_critical_enter(); if (!afs_usednlc) { ma_critical_exit(); return 0; } dnlcHash(ts, key); /* leaves ts pointing at the NULL */ if (ts - aname >= AFSNCNAMESIZE) { ma_critical_exit(); return 0; } skey = key & (NHSIZE - 1); TRACE(osi_dnlc_lookupT, skey); dnlcstats.lookups++; ObtainReadLock(&afs_xvcache); ObtainReadLock(&afs_xdnlc); for (tvc = NULL, tnc = nameHash[skey], safety = 0; tnc; tnc = tnc->next, safety++) { if ( /* (tnc->key == key) && */ (tnc->dirp == adp) && (!strcmp((char *)tnc->name, aname))) { tvc = tnc->vp; tnc1 = tnc; break; } else if (tnc->next == nameHash[skey]) { /* end of list */ break; } else if (safety > NCSIZE) { afs_warn("DNLC cycle"); dnlcstats.cycles++; ReleaseReadLock(&afs_xdnlc); ReleaseReadLock(&afs_xvcache); osi_dnlc_purge(); ma_critical_exit(); return (0); } } LRUme = 0; /* (tnc != nameHash[skey]); */ ReleaseReadLock(&afs_xdnlc); if (!tvc) { ReleaseReadLock(&afs_xvcache); dnlcstats.misses++; } else { if ((tvc->f.states & CVInit) #ifdef AFS_DARWIN80_ENV ||(tvc->f.states & CDeadVnode) #endif ) { ReleaseReadLock(&afs_xvcache); dnlcstats.misses++; osi_dnlc_remove(adp, aname, tvc); ma_critical_exit(); return 0; } #if defined(AFS_DARWIN80_ENV) tvp = AFSTOV(tvc); if (vnode_get(tvp)) { ReleaseReadLock(&afs_xvcache); dnlcstats.misses++; osi_dnlc_remove(adp, aname, tvc); ma_critical_exit(); return 0; } if (vnode_ref(tvp)) { ReleaseReadLock(&afs_xvcache); AFS_GUNLOCK(); vnode_put(tvp); AFS_GLOCK(); dnlcstats.misses++; osi_dnlc_remove(adp, aname, tvc); ma_critical_exit(); return 0; } #elif defined(AFS_FBSD_ENV) /* can't sleep in a critical section */ ma_critical_exit(); osi_vnhold(tvc, 0); ma_critical_enter(); #else osi_vnhold(tvc, 0); #endif ReleaseReadLock(&afs_xvcache); #ifdef notdef /* * XX If LRUme ever is non-zero change the if statement around because * aix's cc with optimizer on won't necessarily check things in order XX */ if (LRUme && (0 == NBObtainWriteLock(&afs_xdnlc))) { /* don't block to do this */ /* tnc might have been moved during race condition, */ /* but it's always in a legit hash chain when a lock is granted, * or else it's on the freelist so prev == NULL, * so at worst this is redundant */ /* Now that we've got it held, and a lock on the dnlc, we * should check to be sure that there was no race, and * bail out if there was. */ if (tnc->prev) { /* special case for only two elements on list - relative ordering * doesn't change */ if (tnc->prev != tnc->next) { /* remove from old location */ tnc->prev->next = tnc->next; tnc->next->prev = tnc->prev; /* insert into new location */ tnc->next = nameHash[skey]; tnc->prev = tnc->next->prev; tnc->next->prev = tnc; tnc->prev->next = tnc; } nameHash[skey] = tnc; } ReleaseWriteLock(&afs_xdnlc); } #endif } ma_critical_exit(); return tvc; }
/* * Given a FID, obtain or construct a dentry, or return an error. * This should be called with the BKL and AFS_GLOCK held. */ static struct dentry *get_dentry_from_fid(cred_t *credp, struct VenusFid *afid) { struct vrequest *treq = NULL; struct vcache *vcp; struct vattr *vattr = NULL; struct inode *ip; struct dentry *dp; afs_int32 code; code = afs_CreateAttr(&vattr); if (code) { return ERR_PTR(-afs_CheckCode(code, NULL, 104)); } code = afs_CreateReq(&treq, credp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): afs_CreateReq: %d\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, code); #endif afs_DestroyAttr(vattr); return ERR_PTR(-afs_CheckCode(code, NULL, 101)); } vcp = afs_GetVCache(afid, treq, NULL, NULL); if (vcp == NULL) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): no vcache\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique); #endif afs_DestroyReq(treq); afs_DestroyAttr(vattr); return NULL; } /* * Now, it might be that we just caused a directory vnode to * spring into existence, in which case its parent FID is unset. * We need to do something about that, but only because we care * in our own get_parent(), below -- the common code never looks * at parentVnode on directories, except for VIOCGETVCXSTATUS. * So, if this fails, we don't really care very much. */ if (vType(vcp) == VDIR && vcp->mvstat != AFS_MVSTAT_ROOT && !vcp->f.parent.vnode) update_dir_parent(treq, vcp); /* * If this is a volume root directory and fakestat is enabled, * we might need to replace the directory by a mount point. */ code = UnEvalFakeStat(treq, &vcp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): UnEvalFakeStat: %d\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, code); #endif afs_PutVCache(vcp); code = afs_CheckCode(code, treq, 103); afs_DestroyReq(treq); afs_DestroyAttr(vattr); return ERR_PTR(-code); } ip = AFSTOV(vcp); afs_getattr(vcp, vattr, credp); afs_fill_inode(ip, vattr); /* d_alloc_anon might block, so we shouldn't hold the glock */ AFS_GUNLOCK(); dp = d_alloc_anon(ip); AFS_GLOCK(); if (!dp) { iput(ip); #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): out of memory\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique); #endif afs_DestroyReq(treq); afs_DestroyAttr(vattr); return ERR_PTR(-ENOMEM); } dp->d_op = &afs_dentry_operations; afs_DestroyReq(treq); afs_DestroyAttr(vattr); return dp; }
/* copy out attributes from cache entry */ int afs_CopyOutAttrs(struct vcache *avc, struct vattr *attrs) { struct volume *tvp; struct cell *tcell; #if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) struct vnode *vp = AFSTOV(avc); #endif int fakedir = 0; AFS_STATCNT(afs_CopyOutAttrs); if (afs_fakestat_enable && avc->mvstat == 1) fakedir = 1; attrs->va_type = fakedir ? VDIR : vType(avc); #if defined(AFS_SGI_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DARWIN_ENV) attrs->va_mode = fakedir ? S_IFDIR | 0755 : (mode_t) (avc->f.m.Mode & 0xffff); #else attrs->va_mode = fakedir ? VDIR | 0755 : avc->f.m.Mode; #endif if (avc->f.m.Mode & (VSUID | VSGID)) { /* setuid or setgid, make sure we're allowed to run them from this cell */ tcell = afs_GetCell(avc->f.fid.Cell, 0); if (tcell && (tcell->states & CNoSUID)) attrs->va_mode &= ~(VSUID | VSGID); } #if defined(AFS_DARWIN_ENV) { if (!afs_darwin_realmodes) { /* Mac OS X uses the mode bits to determine whether a file or * directory is accessible, and believes them, even though under * AFS they're almost assuredly wrong, especially if the local uid * does not match the AFS ID. So we set the mode bits * conservatively. */ if (S_ISDIR(attrs->va_mode)) { /* all access bits need to be set for directories, since even * a mode 0 directory can still be used normally. */ attrs->va_mode |= ACCESSPERMS; } else { /* for other files, replicate the user bits to group and other */ mode_t ubits = (attrs->va_mode & S_IRWXU) >> 6; attrs->va_mode |= ubits | (ubits << 3); } } } #endif /* AFS_DARWIN_ENV */ attrs->va_uid = fakedir ? 0 : avc->f.m.Owner; attrs->va_gid = fakedir ? 0 : avc->f.m.Group; /* yeah! */ #if defined(AFS_SUN5_ENV) attrs->va_fsid = avc->v.v_vfsp->vfs_fsid.val[0]; #elif defined(AFS_DARWIN80_ENV) VATTR_RETURN(attrs, va_fsid, vfs_statfs(vnode_mount(AFSTOV(avc)))->f_fsid.val[0]); #elif defined(AFS_DARWIN_ENV) attrs->va_fsid = avc->v->v_mount->mnt_stat.f_fsid.val[0]; #else /* ! AFS_DARWIN_ENV */ attrs->va_fsid = 1; #endif if (avc->mvstat == 2) { tvp = afs_GetVolume(&avc->f.fid, 0, READ_LOCK); /* The mount point's vnode. */ if (tvp) { attrs->va_nodeid = afs_calc_inum(tvp->mtpoint.Cell, tvp->mtpoint.Fid.Volume, tvp->mtpoint.Fid.Vnode); if (FidCmp(&afs_rootFid, &avc->f.fid) && !attrs->va_nodeid) attrs->va_nodeid = 2; afs_PutVolume(tvp, READ_LOCK); } else attrs->va_nodeid = 2; } else attrs->va_nodeid = afs_calc_inum(avc->f.fid.Cell, avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode); attrs->va_nodeid &= 0x7fffffff; /* Saber C hates negative inode #s! */ attrs->va_nlink = fakedir ? 100 : avc->f.m.LinkCount; attrs->va_size = fakedir ? 4096 : avc->f.m.Length; #if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) vnode_pager_setsize(vp, (u_long) attrs->va_size); #endif attrs->va_atime.tv_sec = attrs->va_mtime.tv_sec = attrs->va_ctime.tv_sec = fakedir ? 0 : (int)avc->f.m.Date; /* set microseconds to be dataversion # so that we approximate NFS-style * use of mtime as a dataversion #. We take it mod 512K because * microseconds *must* be less than a million, and 512K is the biggest * power of 2 less than such. DataVersions are typically pretty small * anyway, so the difference between 512K and 1000000 shouldn't matter * much, and "&" is a lot faster than "%". */ #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) /* nfs on these systems puts an 0 in nsec and stores the nfs usec (aka * dataversion) in va_gen */ attrs->va_atime.tv_nsec = attrs->va_mtime.tv_nsec = attrs->va_ctime.tv_nsec = 0; attrs->va_gen = hgetlo(avc->f.m.DataVersion); #elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV) || defined(AFS_OBSD_ENV) || defined(AFS_NBSD_ENV) attrs->va_atime.tv_nsec = attrs->va_mtime.tv_nsec = attrs->va_ctime.tv_nsec = (hgetlo(avc->f.m.DataVersion) & 0x7ffff) * 1000; #else attrs->va_atime.tv_usec = attrs->va_mtime.tv_usec = attrs->va_ctime.tv_usec = (hgetlo(avc->f.m.DataVersion) & 0x7ffff); #endif #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) attrs->va_flags = 0; #endif #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) attrs->va_blksize = AFS_BLKSIZE; /* XXX Was 8192 XXX */ #else attrs->va_blocksize = AFS_BLKSIZE; /* XXX Was 8192 XXX */ #endif attrs->va_rdev = 1; #if defined(AFS_HPUX110_ENV) if (afs_globalVFS) attrs->va_fstype = afs_globalVFS->vfs_mtype; #endif /* * Below return 0 (and not 1) blocks if the file is zero length. This conforms * better with the other filesystems that do return 0. */ #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) attrs->va_bytes = (attrs->va_size ? (attrs->va_size + 1023) : 1024); #ifdef va_bytes_rsv attrs->va_bytes_rsv = -1; #endif #elif defined(AFS_HPUX_ENV) attrs->va_blocks = (attrs->va_size ? ((attrs->va_size + 1023)>>10) : 0); #elif defined(AFS_SGI_ENV) attrs->va_blocks = BTOBB(attrs->va_size); #elif defined(AFS_SUN5_ENV) attrs->va_nblocks = (attrs->va_size ? ((attrs->va_size + 1023)>>10)<<1:0); #else /* everything else */ attrs->va_blocks = (attrs->va_size ? ((attrs->va_size + 1023)>>10)<<1:0); #endif return 0; }
int afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, afs_ucred_t *acred) #endif { afs_int32 code; struct vrequest treq; struct unixuser *au; int inited = 0; OSI_VC_CONVERT(avc); AFS_STATCNT(afs_getattr); afs_Trace2(afs_iclSetp, CM_TRACE_GETATTR, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length)); if (afs_fakestat_enable && avc->mvstat == 1) { struct afs_fakestat_state fakestat; code = afs_InitReq(&treq, acred); if (code) return code; afs_InitFakeStat(&fakestat); code = afs_TryEvalFakeStat(&avc, &fakestat, &treq); if (code) { afs_PutFakeStat(&fakestat); return code; } code = afs_CopyOutAttrs(avc, attrs); afs_PutFakeStat(&fakestat); return code; } #if defined(AFS_SUN5_ENV) if (flags & ATTR_HINT) { code = afs_CopyOutAttrs(avc, attrs); return code; } #endif #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV) if (avc->f.states & CUBCinit) { code = afs_CopyOutAttrs(avc, attrs); return code; } #endif AFS_DISCON_LOCK(); #ifdef AFS_BOZONLOCK_ENV afs_BozonLock(&avc->pvnLock, avc); #endif if (afs_shuttingdown) return EIO; if (!(avc->f.states & CStatd)) { if (!(code = afs_InitReq(&treq, acred))) { code = afs_VerifyVCache2(avc, &treq); inited = 1; } } else code = 0; #if defined(AFS_SUN5_ENV) || defined(AFS_BOZONLOCK_ENV) if (code == 0) osi_FlushPages(avc, acred); #endif #ifdef AFS_BOZONLOCK_ENV afs_BozonUnlock(&avc->pvnLock, avc); #endif if (code == 0) { osi_FlushText(avc); /* only needed to flush text if text locked last time */ code = afs_CopyOutAttrs(avc, attrs); if (afs_nfsexporter) { if (!inited) { if ((code = afs_InitReq(&treq, acred))) return code; inited = 1; } if (AFS_NFSXLATORREQ(acred)) { if ((vType(avc) != VDIR) && !afs_AccessOK(avc, PRSFS_READ, &treq, CHECK_MODE_BITS | CMB_ALLOW_EXEC_AS_READ)) { return EACCES; } } if ((au = afs_FindUser(treq.uid, -1, READ_LOCK))) { struct afs_exporter *exporter = au->exporter; if (exporter && !(afs_nfsexporter->exp_states & EXP_UNIXMODE)) { unsigned int ubits; /* * If the remote user wishes to enforce default Unix mode semantics, * like in the nfs exporter case, we OR in the user bits * into the group and other bits. We need to do this * because there is no RFS_ACCESS call and thus nfs * clients implement nfs_access by interpreting the * mode bits in the traditional way, which of course * loses with afs. */ ubits = (attrs->va_mode & 0700) >> 6; attrs->va_mode = attrs->va_mode | ubits | (ubits << 3); /* If it's the root of AFS, replace the inode number with the * inode number of the mounted on directory; otherwise this * confuses getwd()... */ #ifdef AFS_LINUX22_ENV if (avc == afs_globalVp) { struct inode *ip = AFSTOV(avc)->i_sb->s_root->d_inode; attrs->va_nodeid = ip->i_ino; /* VTOI()? */ } #else if ( #if defined(AFS_DARWIN_ENV) vnode_isvroot(AFSTOV(avc)) #elif defined(AFS_NBSD50_ENV) AFSTOV(avc)->v_vflag & VV_ROOT #else AFSTOV(avc)->v_flag & VROOT #endif ) { struct vnode *vp = AFSTOV(avc); #ifdef AFS_DARWIN80_ENV /* XXX vp = vnode_mount(vp)->mnt_vnodecovered; */ vp = 0; #else vp = vp->v_vfsp->vfs_vnodecovered; if (vp) { /* Ignore weird failures */ #ifdef AFS_SGI62_ENV attrs->va_nodeid = VnodeToIno(vp); #else struct inode *ip; ip = (struct inode *)VTOI(vp); if (ip) /* Ignore weird failures */ attrs->va_nodeid = ip->i_number; #endif } #endif } #endif /* AFS_LINUX22_ENV */ } afs_PutUser(au, READ_LOCK); } }
/* Note that we don't set CDirty here, this is OK because the unlink * RPC is called synchronously */ int afs_remove(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred) { struct vrequest treq; struct dcache *tdc; struct VenusFid unlinkFid; afs_int32 code; struct vcache *tvc; afs_size_t offset, len; struct afs_fakestat_state fakestate; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_remove); afs_Trace2(afs_iclSetp, CM_TRACE_REMOVE, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname); if ((code = afs_InitReq(&treq, acred))) { return code; } afs_InitFakeStat(&fakestate); AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, &treq); if (code) goto done; /* Check if this is dynroot */ if (afs_IsDynroot(adp)) { code = afs_DynrootVOPRemove(adp, acred, aname); goto done; } if (afs_IsDynrootMount(adp)) { code = ENOENT; goto done; } if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done; } tagain: code = afs_VerifyVCache(adp, &treq); tvc = NULL; if (code) { code = afs_CheckCode(code, &treq, 23); goto done; } /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } /* If we're running disconnected without logging, go no further... */ if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1); /* test for error below */ ObtainWriteLock(&adp->lock, 142); if (tdc) ObtainSharedLock(&tdc->lock, 638); /* * Make sure that the data in the cache is current. We may have * received a callback while we were waiting for the write lock. */ if (!(adp->f.states & CStatd) || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto tagain; } unlinkFid.Fid.Vnode = 0; if (!tvc) { tvc = osi_dnlc_lookup(adp, aname, WRITE_LOCK); } /* This should not be necessary since afs_lookup() has already * done the work. */ if (!tvc) if (tdc) { code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid); if (code == 0) { afs_int32 cached = 0; unlinkFid.Cell = adp->f.fid.Cell; unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume; if (unlinkFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&unlinkFid, &treq, &cached, adp, aname); } else { ObtainReadLock(&afs_xvcache); tvc = afs_FindVCache(&unlinkFid, 0, DO_STATS); ReleaseReadLock(&afs_xvcache); } } } if (AFS_IS_DISCON_RW) { if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) { /* Make shadow copy of parent dir. */ afs_MakeShadowDir(adp, tdc); } /* Can't hold a dcache lock whilst we're getting a vcache one */ if (tdc) ReleaseSharedLock(&tdc->lock); /* XXX - We're holding adp->lock still, and we've got no * guarantee about whether the ordering matches the lock hierarchy */ ObtainWriteLock(&tvc->lock, 713); /* If we were locally created, then we don't need to do very * much beyond ensuring that we don't exist anymore */ if (tvc->f.ddirty_flags & VDisconCreate) { afs_DisconRemoveDirty(tvc); } else { /* Add removed file vcache to dirty list. */ afs_DisconAddDirty(tvc, VDisconRemove, 1); } adp->f.m.LinkCount--; ReleaseWriteLock(&tvc->lock); if (tdc) ObtainSharedLock(&tdc->lock, 714); } if (tvc && osi_Active(tvc)) { /* about to delete whole file, prefetch it first */ ReleaseWriteLock(&adp->lock); if (tdc) ReleaseSharedLock(&tdc->lock); ObtainWriteLock(&tvc->lock, 143); FetchWholeEnchilada(tvc, &treq); ReleaseWriteLock(&tvc->lock); ObtainWriteLock(&adp->lock, 144); /* Technically I don't think we need this back, but let's hold it anyway; The "got" reference should actually be sufficient. */ if (tdc) ObtainSharedLock(&tdc->lock, 640); } osi_dnlc_remove(adp, aname, tvc); Tadp1 = adp; #ifndef AFS_DARWIN80_ENV Tadpr = VREFCOUNT(adp); #endif Ttvc = tvc; Tnam = aname; Tnam1 = 0; #ifndef AFS_DARWIN80_ENV if (tvc) Ttvcr = VREFCOUNT(tvc); #endif #ifdef AFS_AIX_ENV if (tvc && VREFCOUNT_GT(tvc, 2) && tvc->opens > 0 && !(tvc->f.states & CUnlinked)) { #else if (tvc && VREFCOUNT_GT(tvc, 1) && tvc->opens > 0 && !(tvc->f.states & CUnlinked)) { #endif char *unlname = afs_newname(); ReleaseWriteLock(&adp->lock); if (tdc) ReleaseSharedLock(&tdc->lock); code = afsrename(adp, aname, adp, unlname, acred, &treq); Tnam1 = unlname; if (!code) { struct VenusFid *oldmvid = NULL; if (tvc->mvid) oldmvid = tvc->mvid; tvc->mvid = (struct VenusFid *)unlname; if (oldmvid) osi_FreeSmallSpace(oldmvid); crhold(acred); if (tvc->uncred) { crfree(tvc->uncred); } tvc->uncred = acred; tvc->f.states |= CUnlinked; /* if rename succeeded, remove should not */ ObtainWriteLock(&tvc->lock, 715); if (tvc->f.ddirty_flags & VDisconRemove) { tvc->f.ddirty_flags &= ~VDisconRemove; } ReleaseWriteLock(&tvc->lock); } else { osi_FreeSmallSpace(unlname); } if (tdc) afs_PutDCache(tdc); afs_PutVCache(tvc); } else { code = afsremove(adp, tdc, tvc, aname, acred, &treq); } done: afs_PutFakeStat(&fakestate); #if !defined(AFS_DARWIN80_ENV) && !defined(UKERNEL) /* we can't track by thread, it's not exported in the KPI; only do this on !macos */ osi_Assert(!WriteLocked(&adp->lock) || (adp->lock.pid_writer != MyPidxx)); #endif AFS_DISCON_UNLOCK(); return code; } /* afs_remunlink -- This tries to delete the file at the server after it has * been renamed when unlinked locally but now has been finally released. * * CAUTION -- may be called with avc unheld. */ int afs_remunlink(struct vcache *avc, int doit) { afs_ucred_t *cred; char *unlname; struct vcache *adp; struct vrequest treq; struct VenusFid dirFid; struct dcache *tdc; afs_int32 code = 0; if (NBObtainWriteLock(&avc->lock, 423)) return 0; #if defined(AFS_DARWIN80_ENV) if (vnode_get(AFSTOV(avc))) { ReleaseWriteLock(&avc->lock); return 0; } #endif if (avc->mvid && (doit || (avc->f.states & CUnlinkedDel))) { if ((code = afs_InitReq(&treq, avc->uncred))) { ReleaseWriteLock(&avc->lock); } else { /* Must bump the refCount because GetVCache may block. * Also clear mvid so no other thread comes here if we block. */ unlname = (char *)avc->mvid; avc->mvid = NULL; cred = avc->uncred; avc->uncred = NULL; #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV) VREF(AFSTOV(avc)); #else AFS_FAST_HOLD(avc); #endif /* We'll only try this once. If it fails, just release the vnode. * Clear after doing hold so that NewVCache doesn't find us yet. */ avc->f.states &= ~(CUnlinked | CUnlinkedDel); ReleaseWriteLock(&avc->lock); dirFid.Cell = avc->f.fid.Cell; dirFid.Fid.Volume = avc->f.fid.Fid.Volume; dirFid.Fid.Vnode = avc->f.parent.vnode; dirFid.Fid.Unique = avc->f.parent.unique; adp = afs_GetVCache(&dirFid, &treq, NULL, NULL); if (adp) { tdc = afs_FindDCache(adp, (afs_size_t) 0); ObtainWriteLock(&adp->lock, 159); if (tdc) ObtainSharedLock(&tdc->lock, 639); /* afsremove releases the adp & tdc locks, and does vn_rele(avc) */ code = afsremove(adp, tdc, avc, unlname, cred, &treq); afs_PutVCache(adp); } else { /* we failed - and won't be back to try again. */ afs_PutVCache(avc); } osi_FreeSmallSpace(unlname); crfree(cred); } } else { #if defined(AFS_DARWIN80_ENV) vnode_put(AFSTOV(avc)); #endif ReleaseWriteLock(&avc->lock); } return code; }
int SRXAFSCB_GetCE(struct rx_call *a_call, afs_int32 a_index, struct AFSDBCacheEntry *a_result) { int i; /*Loop variable */ struct vcache *tvc; /*Ptr to current cache entry */ int code; /*Return code */ XSTATS_DECLS; RX_AFS_GLOCK(); XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_GETCE); AFS_STATCNT(SRXAFSCB_GetCE); for (i = 0; i < VCSIZE; i++) { for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) { if (a_index == 0) goto searchDone; a_index--; } /*Zip through current hash chain */ } /*Zip through hash chains */ searchDone: if (tvc == NULL) { /*Past EOF */ code = 1; goto fcnDone; } /* * Copy out the located entry. */ a_result->addr = afs_data_pointer_to_int32(tvc); a_result->cell = tvc->f.fid.Cell; a_result->netFid.Volume = tvc->f.fid.Fid.Volume; a_result->netFid.Vnode = tvc->f.fid.Fid.Vnode; a_result->netFid.Unique = tvc->f.fid.Fid.Unique; a_result->lock.waitStates = tvc->lock.wait_states; a_result->lock.exclLocked = tvc->lock.excl_locked; a_result->lock.readersReading = tvc->lock.readers_reading; a_result->lock.numWaiting = tvc->lock.num_waiting; #if defined(INSTRUMENT_LOCKS) a_result->lock.pid_last_reader = MyPidxx2Pid(tvc->lock.pid_last_reader); a_result->lock.pid_writer = MyPidxx2Pid(tvc->lock.pid_writer); a_result->lock.src_indicator = tvc->lock.src_indicator; #else /* On osf20 , the vcache does not maintain these three fields */ a_result->lock.pid_last_reader = 0; a_result->lock.pid_writer = 0; a_result->lock.src_indicator = 0; #endif /* INSTRUMENT_LOCKS */ #ifdef AFS_64BIT_CLIENT a_result->Length = (afs_int32) tvc->f.m.Length & 0xffffffff; #else /* AFS_64BIT_CLIENT */ a_result->Length = tvc->f.m.Length; #endif /* AFS_64BIT_CLIENT */ a_result->DataVersion = hgetlo(tvc->f.m.DataVersion); a_result->callback = afs_data_pointer_to_int32(tvc->callback); /* XXXX Now a pointer; change it XXXX */ a_result->cbExpires = tvc->cbExpires; if (tvc->f.states & CVInit) { a_result->refCount = 1; } else { #ifdef AFS_DARWIN80_ENV a_result->refCount = vnode_isinuse(AFSTOV(tvc),0)?1:0; /* XXX fix */ #else a_result->refCount = VREFCOUNT(tvc); #endif } a_result->opens = tvc->opens; a_result->writers = tvc->execsOrWriters; a_result->mvstat = tvc->mvstat; a_result->states = tvc->f.states; code = 0; /* * Return our results. */ fcnDone: XSTATS_END_TIME; RX_AFS_GUNLOCK(); return (code); } /*SRXAFSCB_GetCE */
static int ClearCallBack(struct rx_connection *a_conn, struct AFSFid *a_fid) { struct vcache *tvc; int i; struct VenusFid localFid; struct volume *tv; #ifdef AFS_DARWIN80_ENV vnode_t vp; #endif AFS_STATCNT(ClearCallBack); AFS_ASSERT_GLOCK(); /* * XXXX Don't hold any server locks here because of callback protocol XXX */ localFid.Cell = 0; localFid.Fid.Volume = a_fid->Volume; localFid.Fid.Vnode = a_fid->Vnode; localFid.Fid.Unique = a_fid->Unique; /* * Volume ID of zero means don't do anything. */ if (a_fid->Volume != 0) { if (a_fid->Vnode == 0) { struct afs_q *tq, *uq; /* * Clear callback for the whole volume. Zip through the * hash chain, nullifying entries whose volume ID matches. */ loop1: ObtainReadLock(&afs_xvcache); i = VCHashV(&localFid); for (tq = afs_vhashTV[i].prev; tq != &afs_vhashTV[i]; tq = uq) { uq = QPrev(tq); tvc = QTOVH(tq); if (tvc->f.fid.Fid.Volume == a_fid->Volume) { tvc->callback = NULL; if (!localFid.Cell) localFid.Cell = tvc->f.fid.Cell; tvc->dchint = NULL; /* invalidate hints */ if (tvc->f.states & CVInit) { ReleaseReadLock(&afs_xvcache); afs_osi_Sleep(&tvc->f.states); goto loop1; } #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_LINUX20_ENV) AFS_FAST_HOLD(tvc); #else #ifdef AFS_DARWIN80_ENV if (tvc->f.states & CDeadVnode) { if (!(tvc->f.states & CBulkFetching)) { ReleaseReadLock(&afs_xvcache); afs_osi_Sleep(&tvc->f.states); goto loop1; } } vp = AFSTOV(tvc); if (vnode_get(vp)) continue; if (vnode_ref(vp)) { AFS_GUNLOCK(); vnode_put(vp); AFS_GLOCK(); continue; } if (tvc->f.states & (CBulkFetching|CDeadVnode)) { AFS_GUNLOCK(); vnode_recycle(AFSTOV(tvc)); AFS_GLOCK(); } #else AFS_FAST_HOLD(tvc); #endif #endif ReleaseReadLock(&afs_xvcache); ObtainWriteLock(&afs_xcbhash, 449); afs_DequeueCallback(tvc); tvc->f.states &= ~(CStatd | CUnique | CBulkFetching); afs_allCBs++; if (tvc->f.fid.Fid.Vnode & 1) afs_oddCBs++; else afs_evenCBs++; ReleaseWriteLock(&afs_xcbhash); if ((tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR))) osi_dnlc_purgedp(tvc); afs_Trace3(afs_iclSetp, CM_TRACE_CALLBACK, ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32, tvc->f.states, ICL_TYPE_INT32, a_fid->Volume); #ifdef AFS_DARWIN80_ENV vnode_put(AFSTOV(tvc)); #endif ObtainReadLock(&afs_xvcache); uq = QPrev(tq); AFS_FAST_RELE(tvc); } else if ((tvc->f.states & CMValid) && (tvc->mvid->Fid.Volume == a_fid->Volume)) { tvc->f.states &= ~CMValid; if (!localFid.Cell) localFid.Cell = tvc->mvid->Cell; } } ReleaseReadLock(&afs_xvcache); /* * XXXX Don't hold any locks here XXXX */ tv = afs_FindVolume(&localFid, 0); if (tv) { afs_ResetVolumeInfo(tv); afs_PutVolume(tv, 0); /* invalidate mtpoint? */ } } /*Clear callbacks for whole volume */ else { /* * Clear callbacks just for the one file. */ struct vcache *uvc; afs_allCBs++; if (a_fid->Vnode & 1) afs_oddCBs++; /*Could do this on volume basis, too */ else afs_evenCBs++; /*A particular fid was specified */ loop2: ObtainReadLock(&afs_xvcache); i = VCHash(&localFid); for (tvc = afs_vhashT[i]; tvc; tvc = uvc) { uvc = tvc->hnext; if (tvc->f.fid.Fid.Vnode == a_fid->Vnode && tvc->f.fid.Fid.Volume == a_fid->Volume && tvc->f.fid.Fid.Unique == a_fid->Unique) { tvc->callback = NULL; tvc->dchint = NULL; /* invalidate hints */ if (tvc->f.states & CVInit) { ReleaseReadLock(&afs_xvcache); afs_osi_Sleep(&tvc->f.states); goto loop2; } #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_LINUX20_ENV) AFS_FAST_HOLD(tvc); #else #ifdef AFS_DARWIN80_ENV if (tvc->f.states & CDeadVnode) { if (!(tvc->f.states & CBulkFetching)) { ReleaseReadLock(&afs_xvcache); afs_osi_Sleep(&tvc->f.states); goto loop2; } } vp = AFSTOV(tvc); if (vnode_get(vp)) continue; if (vnode_ref(vp)) { AFS_GUNLOCK(); vnode_put(vp); AFS_GLOCK(); continue; } if (tvc->f.states & (CBulkFetching|CDeadVnode)) { AFS_GUNLOCK(); vnode_recycle(AFSTOV(tvc)); AFS_GLOCK(); } #else AFS_FAST_HOLD(tvc); #endif #endif ReleaseReadLock(&afs_xvcache); ObtainWriteLock(&afs_xcbhash, 450); afs_DequeueCallback(tvc); tvc->f.states &= ~(CStatd | CUnique | CBulkFetching); ReleaseWriteLock(&afs_xcbhash); if ((tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR))) osi_dnlc_purgedp(tvc); afs_Trace3(afs_iclSetp, CM_TRACE_CALLBACK, ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32, tvc->f.states, ICL_TYPE_LONG, 0); #ifdef CBDEBUG lastCallBack_vnode = afid->Vnode; lastCallBack_dv = tvc->mstat.DataVersion.low; osi_GetuTime(&lastCallBack_time); #endif /* CBDEBUG */ #ifdef AFS_DARWIN80_ENV vnode_put(AFSTOV(tvc)); #endif ObtainReadLock(&afs_xvcache); uvc = tvc->hnext; AFS_FAST_RELE(tvc); } } /*Walk through hash table */ ReleaseReadLock(&afs_xvcache); } /*Clear callbacks for one file */ } /*Fid has non-zero volume ID */ /* * Always return a predictable value. */ return (0); } /*ClearCallBack */