Ejemplo n.º 1
0
void
osi_PostPopulateVCache(struct vcache *avc) {
    AFSTOV(avc)->v_mount = afs_globalVFS;
    vSetType(avc, VREG);
}
Ejemplo n.º 2
0
/** 
 *   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 */
Ejemplo n.º 3
0
/* 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);
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
/* 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();
}
Ejemplo n.º 6
0
/* 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;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
/* 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;
    }
}
Ejemplo n.º 9
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;
}
Ejemplo n.º 10
0
/* 
 * 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;
}
Ejemplo n.º 11
0
/* 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;
}
Ejemplo n.º 12
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);
	    }
	}
Ejemplo n.º 13
0
/* 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;
}
Ejemplo n.º 14
0
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 */
Ejemplo n.º 15
0
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 */