示例#1
0
static int UnEvalFakeStat(struct vrequest *areq, struct vcache **vcpp)
{
    struct VenusFid tfid;
    struct volume *tvp;
    struct vcache *tvc;
    int code;

    if (!afs_fakestat_enable)
	return 0;

    if (*vcpp == afs_globalVp || vType(*vcpp) != VDIR || (*vcpp)->mvstat != 2)
	return 0;

    /* Figure out what FID to look for */
    tvp = afs_GetVolume(&(*vcpp)->f.fid, 0, READ_LOCK);
    if (!tvp) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: UnEvalFakeStat(0x%08x/%d/%d.%d): no volume\n",
	       (*vcpp)->f.fid.Cell,      (*vcpp)->f.fid.Fid.Volume,
	       (*vcpp)->f.fid.Fid.Vnode, (*vcpp)->f.fid.Fid.Unique);
#endif
	return ENOENT;
    }
    tfid = tvp->mtpoint;
    afs_PutVolume(tvp, READ_LOCK);

    tvc = afs_GetVCache(&tfid, areq, NULL, NULL);
    if (!tvc) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: UnEvalFakeStat(0x%08x/%d/%d.%d): GetVCache(0x%08x/%d/%d.%d) failed\n",
	       (*vcpp)->f.fid.Cell,      (*vcpp)->f.fid.Fid.Volume,
	       (*vcpp)->f.fid.Fid.Vnode, (*vcpp)->f.fid.Fid.Unique,
	       tfid.Cell,          tfid.Fid.Volume,
	       tfid.Fid.Vnode,     tfid.Fid.Unique);
#endif
	return ENOENT;
    }

    if (afs_fakestat_enable == 2) {
	ObtainWriteLock(&tvc->lock, 806);
	code = afs_HandleLink(tvc, areq);
	if (code) {
	    ReleaseWriteLock(&tvc->lock);
	    afs_PutVCache(tvc);
	    return code;
	}
	if (!strchr(tvc->linkData, ':')) {
	    ReleaseWriteLock(&tvc->lock);
	    afs_PutVCache(tvc);
	    return 0;
	}
	ReleaseWriteLock(&tvc->lock);
    }

    afs_PutVCache(*vcpp);
    *vcpp = tvc;
    return 0;
}
示例#2
0
/*
 * Invalidate the /afs vnode for dynroot; called when the underlying
 * directory has changed and needs to be re-read.
 */
void
afs_DynrootInvalidate(void)
{
    afs_int32 retry;
    struct vcache *tvc;
    struct VenusFid tfid;

    if (!afs_dynrootEnable)
	return;

    ObtainWriteLock(&afs_dynrootDirLock, 687);
    afs_dynrootVersion++;
    afs_dynrootVersionHigh = osi_Time();
    ReleaseWriteLock(&afs_dynrootDirLock);

    afs_GetDynrootFid(&tfid);
    do {
	retry = 0;
	ObtainReadLock(&afs_xvcache);
	tvc = afs_FindVCache(&tfid, &retry, 0);
	ReleaseReadLock(&afs_xvcache);
    } while (retry);
    if (tvc) {
	tvc->f.states &= ~(CStatd | CUnique);
	osi_dnlc_purgedp(tvc);
	afs_PutVCache(tvc);
    }
}
示例#3
0
/*!
 * Generate a fake fid for a disconnected shadow dir.
 * Similar to afs_GenFakeFid, only that it uses the dhash
 * to search for a uniquifier because a shadow dir lives only
 * in the dcache.
 *
 * \param afid
 *
 * \note Don't forget to fill in afid with Cell and Volume.
 */
void
afs_GenShadowFid(struct VenusFid *afid)
{
    afs_uint32 i, index, max_unique = 1;
    struct vcache *tvc = NULL;

    /* Try generating a fid that isn't used in the vhash. */
    do {
	/* Shadow Fids are always directories */
    	afid->Fid.Vnode = afs_DisconVnode + 1;

    	i = DVHash(afid);
    	ObtainWriteLock(&afs_xdcache, 737);
    	for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
            i = afs_dvnextTbl[index];
            if (afs_indexUnique[index] > max_unique)
	    	max_unique = afs_indexUnique[index];
    	}

	ReleaseWriteLock(&afs_xdcache);
	afid->Fid.Unique = max_unique + 1;
	afs_DisconVnode += 2;
	if (!afs_DisconVnode)
    	    afs_DisconVnode = 2;

	/* Is this a used vnode? */
    	ObtainSharedLock(&afs_xvcache, 762);
    	tvc = afs_FindVCache(afid, 0, 1);
    	ReleaseSharedLock(&afs_xvcache);
	if (tvc)
	    afs_PutVCache(tvc);
    } while (tvc);
}
示例#4
0
/* afs_root - stat the root of the file system. AFS global held on entry. */
static int
afs_root(struct super_block *afsp)
{
    afs_int32 code = 0;
    struct vcache *tvp = 0;

    AFS_STATCNT(afs_root);
    if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
	tvp = afs_globalVp;
    } else {
	struct vrequest *treq = NULL;
	cred_t *credp = crref();

	if (afs_globalVp) {
	    afs_PutVCache(afs_globalVp);
	    afs_globalVp = NULL;
	}

	if (!(code = afs_CreateReq(&treq, credp)) && !(code = afs_CheckInit())) {
	    tvp = afs_GetVCache(&afs_rootFid, treq, NULL, NULL);
	    if (tvp) {
		struct inode *ip = AFSTOV(tvp);
		struct vattr *vattr = NULL;

		code = afs_CreateAttr(&vattr);
		if (!code) {
		    afs_getattr(tvp, vattr, credp);
		    afs_fill_inode(ip, vattr);

		    /* setup super_block and mount point inode. */
		    afs_globalVp = tvp;
#if defined(HAVE_LINUX_D_MAKE_ROOT)
		    afsp->s_root = d_make_root(ip);
#else
		    afsp->s_root = d_alloc_root(ip);
#endif
#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
		    afsp->s_root->d_op = &afs_dentry_operations;
#endif
		    afs_DestroyAttr(vattr);
		}
	    } else
		code = EIO;
	}
	crfree(credp);
	afs_DestroyReq(treq);
    }

    afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp,
	       ICL_TYPE_INT32, code);
    return code;
}
示例#5
0
/*!
 * Changes a file's parent fid references.
 */
static int
fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode,
		       afs_int32 unique)
{
    struct VenusFid tfid;
    struct VenusFid *afid = (struct VenusFid *) hdata;
    struct vcache *tvc;
    struct dcache *tdc = NULL;

    if ((aname[0] == '.') && !aname[1])
	return 0;

    if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
    	return 0;

    tfid.Cell = afid->Cell;
    tfid.Fid.Volume = afid->Fid.Volume;
    tfid.Fid.Vnode = vnode;
    tfid.Fid.Unique = unique;

    if (!(vnode % 2)) {
	/* vnode's parity indicates that it's a file. */

	/* Get the vcache. */
	ObtainSharedLock(&afs_xvcache, 759);
	tvc = afs_FindVCache(&tfid, 0, 1);
	ReleaseSharedLock(&afs_xvcache);

	/* Change the fields. */
	if (tvc) {
	    tvc->f.parent.vnode = afid->Fid.Vnode;
	    tvc->f.parent.unique = afid->Fid.Unique;

	    afs_PutVCache(tvc);
	}
    } else {
    	/* It's a dir. Fix this dir's .. entry to contain the new fid. */
	/* Seek the dir's dcache. */
    	tdc = afs_FindDCacheByFid(&tfid);
	if (tdc) {
	    /* Change the .. entry fid. */
	    afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
	    afs_PutDCache(tdc);
	}
    }			/* if (!(vnode % 2))*/

    return 0;
}
示例#6
0
/* afs_root - stat the root of the file system. AFS global held on entry. */
static int
afs_root(struct super_block *afsp)
{
    afs_int32 code = 0;
    struct vrequest treq;
    struct vcache *tvp = 0;

    AFS_STATCNT(afs_root);
    if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
	tvp = afs_globalVp;
    } else {
	cred_t *credp = crref();

	if (afs_globalVp) {
	    afs_PutVCache(afs_globalVp);
	    afs_globalVp = NULL;
	}

	if (!(code = afs_InitReq(&treq, credp)) && !(code = afs_CheckInit())) {
	    tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL);
	    if (tvp) {
		struct inode *ip = AFSTOV(tvp);
		struct vattr vattr;

		afs_getattr(tvp, &vattr, credp);
		afs_fill_inode(ip, &vattr);

		/* setup super_block and mount point inode. */
		afs_globalVp = tvp;
#if defined(AFS_LINUX24_ENV)
		afsp->s_root = d_alloc_root(ip);
#else
		afsp->s_root = d_alloc_root(ip, NULL);
#endif
		afsp->s_root->d_op = &afs_dentry_operations;
	    } else
		code = ENOENT;
	}
	crfree(credp);
    }

    afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp,
	       ICL_TYPE_INT32, code);
    return code;
}
示例#7
0
afs_unmount(struct mount *mp, int flags, struct thread *p)
#endif
{
    int error = 0;

    AFS_GLOCK();
    if (afs_globalVp &&
	((flags & MNT_FORCE) || !VREFCOUNT_GT(afs_globalVp, 1))) {
	/* Put back afs_root's ref */
	struct vcache *gvp = afs_globalVp;
	afs_globalVp = NULL;
	afs_PutVCache(gvp);
    }
    if (afs_globalVp)
	error = EBUSY;
    AFS_GUNLOCK();

    /*
     * Release any remaining vnodes on this mount point.
     * The `1' means that we hold one extra reference on
     * the root vnode (this is just a guess right now).
     * This has to be done outside the global lock.
     */
    if (!error) {
#if defined(AFS_FBSD80_ENV)
	error = vflush(mp, 1, (flags & MNT_FORCE) ? FORCECLOSE : 0, curthread);
#elif defined(AFS_FBSD53_ENV)
	error = vflush(mp, 1, (flags & MNT_FORCE) ? FORCECLOSE : 0, p);
#else
	error = vflush(mp, 1, (flags & MNT_FORCE) ? FORCECLOSE : 0);
#endif
    }
    if (error)
	goto out;
    AFS_GLOCK();
    AFS_STATCNT(afs_unmount);
    afs_globalVFS = 0;
    afs_shutdown();
    AFS_GUNLOCK();

out:
    return error;
}
示例#8
0
int
afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid,
		    char *aname, struct vcache **adp)
{
    int code;

    *adp = NULL;

    if (afs_GetParentDirFid(avc, afid)) {
	/* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */
	return ENOENT;
    }

    code = afs_GetVnodeName(avc, afid, aname, deleted);
    if (code) {
	/* printf("afs_GetParentVCache: Couldn't find file name\n"); */
	goto end;
    }

    ObtainSharedLock(&afs_xvcache, 766);
    *adp = afs_FindVCache(afid, 0, 1);
    ReleaseSharedLock(&afs_xvcache);
    if (!*adp) {
	/* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */
	code = ENOENT;
	goto end;
    }

    if ((*adp)->f.ddirty_flags & VDisconCreate) {
	/* printf("afs_GetParentVCache: deferring until parent exists\n"); */
	code = EAGAIN;
	goto end;
    }

end:
    if (code && *adp) {
	afs_PutVCache(*adp);
	*adp = NULL;
    }
    return code;
}
示例#9
0
/*!
 * Lookup dirty deleted vnodes in this dir.
 */
static int
chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode,
		      afs_int32 unique)
{
    struct VenusFid tfid;
    struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
    struct vcache *tvc;

    if ((aname[0] == '.') && !aname[1])
    	/* Skip processing this dir again.
	 * It would result in an endless loop.
	 */
	return 0;

    if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
    	/* Don't process parent dir. */
    	return 0;

    /* Get this file's vcache. */
    tfid.Cell = v->vc->f.fid.Cell;
    tfid.Fid.Volume = v->vc->f.fid.Fid.Volume;
    tfid.Fid.Vnode = vnode;
    tfid.Fid.Unique = unique;

    ObtainSharedLock(&afs_xvcache, 757);
    tvc = afs_FindVCache(&tfid, 0, 1);
    ReleaseSharedLock(&afs_xvcache);

    /* Count unfinished dirty children. */
    if (tvc) {
	ObtainReadLock(&tvc->lock);
	if (tvc->f.ddirty_flags)
	    v->count++;
	ReleaseReadLock(&tvc->lock);

	afs_PutVCache(tvc);
    }

    return 0;
}
示例#10
0
int
afs_root(OSI_VFS_DECL(afsp), struct vnode **avpp)
{
    afs_int32 code = 0;
    struct vrequest treq;
    struct vcache *tvp = 0;
    OSI_VFS_CONVERT(afsp);

    AFS_STATCNT(afs_root);
    if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
	tvp = afs_globalVp;
    } else {
	if (afs_globalVp) {
	    afs_PutVCache(afs_globalVp);
	    afs_globalVp = NULL;
	}

	if (!(code = afs_InitReq(&treq, get_user_struct()->u_cred))
	    && !(code = afs_CheckInit())) {
	    tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL);
	    /* we really want this to stay around */
	    if (tvp) {
		afs_globalVp = tvp;
	    } else
		code = ENOENT;
	}
    }
    if (tvp) {
	VN_HOLD(AFSTOV(tvp));

	AFSTOV(tvp)->v_flag |= VROOT;	/* No-op on Ultrix 2.2 */
	afs_globalVFS = afsp;
	*avpp = AFSTOV(tvp);
    }

    afs_Trace3(afs_iclSetp, CM_TRACE_GOPEN, ICL_TYPE_POINTER, *avpp,
	       ICL_TYPE_INT32, 0, ICL_TYPE_INT32, code);
    return code;
}
示例#11
0
/*!
 * This function throws away the whole disconnected state, allowing
 * the cache manager to reconnect to a server if we get into a state
 * where reconiliation is impossible.
 *
 * \param acred
 *
 */
void
afs_DisconDiscardAll(afs_ucred_t *acred)
{
    struct vcache *tvc;

    ObtainWriteLock(&afs_disconDirtyLock, 717);
    while (!QEmpty(&afs_disconDirty)) {
	tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
	QRemove(&tvc->dirtyq);
	ReleaseWriteLock(&afs_disconDirtyLock);

	ObtainWriteLock(&tvc->lock, 718);
	afs_ResetVCache(tvc, acred, 0);
	tvc->f.truncPos = AFS_NOTRUNC;
	ReleaseWriteLock(&tvc->lock);
	ObtainWriteLock(&afs_disconDirtyLock, 719);
	afs_PutVCache(tvc);
    }

    afs_DisconDiscardAllShadows(1, acred);

    ReleaseWriteLock(&afs_disconDirtyLock);
}
示例#12
0
int
afsrename(struct vcache *aodp, char *aname1, struct vcache *andp,
	  char *aname2, struct AFS_UCRED *acred, struct vrequest *areq)
{
    register struct afs_conn *tc;
    register afs_int32 code = 0;
    afs_int32 returnCode;
    int oneDir, doLocally;
    afs_size_t offset, len;
    struct VenusFid unlinkFid, fileFid;
    struct vcache *tvc;
    struct dcache *tdc1, *tdc2;
    struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
    struct AFSVolSync tsync;
    XSTATS_DECLS;
    AFS_STATCNT(afs_rename);
    afs_Trace4(afs_iclSetp, CM_TRACE_RENAME, ICL_TYPE_POINTER, aodp,
	       ICL_TYPE_STRING, aname1, ICL_TYPE_POINTER, andp,
	       ICL_TYPE_STRING, aname2);

    if (strlen(aname1) > AFSNAMEMAX || strlen(aname2) > AFSNAMEMAX) {
	code = ENAMETOOLONG;
	goto done;
    }

    /* verify the latest versions of the stat cache entries */
  tagain:
    code = afs_VerifyVCache(aodp, areq);
    if (code)
	goto done;
    code = afs_VerifyVCache(andp, areq);
    if (code)
	goto done;

    /* lock in appropriate order, after some checks */
    if (aodp->f.fid.Cell != andp->f.fid.Cell
	|| aodp->f.fid.Fid.Volume != andp->f.fid.Fid.Volume) {
	code = EXDEV;
	goto done;
    }
    oneDir = 0;
    code = 0;
    if (andp->f.fid.Fid.Vnode == aodp->f.fid.Fid.Vnode) {
	if (!strcmp(aname1, aname2)) {
	    /* Same directory and same name; this is a noop and just return success
	     * to save cycles and follow posix standards */

	    code = 0;
	    goto done;
	}
	
	if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
	    code = ENETDOWN;
	    goto done;
	}
	
	ObtainWriteLock(&andp->lock, 147);
	tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0);
	if (!tdc1) {
	    code = ENOENT;
	} else {
	    ObtainWriteLock(&tdc1->lock, 643);
	}
	tdc2 = tdc1;
	oneDir = 1;		/* only one dude locked */
    } else if ((andp->f.states & CRO) || (aodp->f.states & CRO)) {
	code = EROFS;
	goto done;
    } else if (andp->f.fid.Fid.Vnode < aodp->f.fid.Fid.Vnode) {
	ObtainWriteLock(&andp->lock, 148);	/* lock smaller one first */
	ObtainWriteLock(&aodp->lock, 149);
	tdc2 = afs_FindDCache(andp, (afs_size_t) 0);
	if (tdc2)
	    ObtainWriteLock(&tdc2->lock, 644);
	tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0);
	if (tdc1)
	    ObtainWriteLock(&tdc1->lock, 645);
	else
	    code = ENOENT;
    } else {
	ObtainWriteLock(&aodp->lock, 150);	/* lock smaller one first */
	ObtainWriteLock(&andp->lock, 557);
	tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0);
	if (tdc1)
	    ObtainWriteLock(&tdc1->lock, 646);
	else
	    code = ENOENT;
	tdc2 = afs_FindDCache(andp, (afs_size_t) 0);
	if (tdc2)
	    ObtainWriteLock(&tdc2->lock, 647);
    }

    osi_dnlc_remove(aodp, aname1, 0);
    osi_dnlc_remove(andp, aname2, 0);

    /*
     * 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 (tdc1) {
	if (!(aodp->f.states & CStatd)
	    || !hsame(aodp->f.m.DataVersion, tdc1->f.versionNo)) {

	    ReleaseWriteLock(&aodp->lock);
	    if (!oneDir) {
		if (tdc2) {
		    ReleaseWriteLock(&tdc2->lock);
		    afs_PutDCache(tdc2);
		}
		ReleaseWriteLock(&andp->lock);
	    }
	    ReleaseWriteLock(&tdc1->lock);
	    afs_PutDCache(tdc1);
	    goto tagain;
	}
    }

    if (code == 0)
	code = afs_dir_Lookup(tdc1, aname1, &fileFid.Fid);
    if (code) {
	if (tdc1) {
	    ReleaseWriteLock(&tdc1->lock);
	    afs_PutDCache(tdc1);
	}
	ReleaseWriteLock(&aodp->lock);
	if (!oneDir) {
	    if (tdc2) {
		ReleaseWriteLock(&tdc2->lock);
		afs_PutDCache(tdc2);
	    }
	    ReleaseWriteLock(&andp->lock);
	}
	goto done;
    }

    if (!AFS_IS_DISCON_RW) {
    	/* Connected. */
	do {
	    tc = afs_Conn(&aodp->f.fid, areq, SHARED_LOCK);
	    if (tc) {
	    	XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
	    	RX_AFS_GUNLOCK();
	    	code =
		    RXAFS_Rename(tc->id,
		    			(struct AFSFid *)&aodp->f.fid.Fid,
					aname1,
					(struct AFSFid *)&andp->f.fid.Fid,
					aname2,
					&OutOldDirStatus,
					&OutNewDirStatus,
					&tsync);
	        RX_AFS_GLOCK();
	        XSTATS_END_TIME;
	    } else
	    	code = -1;

	} while (afs_Analyze
	     (tc, code, &andp->f.fid, areq, AFS_STATS_FS_RPCIDX_RENAME,
	      SHARED_LOCK, NULL));

    } else {
#if defined(AFS_DISCON_ENV)
	/* Disconnected. */

	/* Seek moved file vcache. */
	fileFid.Cell = aodp->f.fid.Cell;
	fileFid.Fid.Volume = aodp->f.fid.Fid.Volume;
	ObtainSharedLock(&afs_xvcache, 754);
	tvc = afs_FindVCache(&fileFid, 0 , 1);
	ReleaseSharedLock(&afs_xvcache);

	if (tvc) {
	    /* XXX - We're locking this vcache whilst holding dcaches. Ooops */
	    ObtainWriteLock(&tvc->lock, 750);
	    if (!(tvc->f.ddirty_flags & (VDisconRename|VDisconCreate))) {
		/* If the vnode was created locally, then we don't care
		 * about recording the rename - we'll do it automatically
		 * on replay. If we've already renamed, we've already stored
		 * the required information about where we came from.
		 */
		
		if (!aodp->f.shadow.vnode) {
	    	    /* Make shadow copy of parent dir only. */
    	    	    afs_MakeShadowDir(aodp, tdc1);
	        }

		/* Save old parent dir fid so it will be searchable
	     	 * in the shadow dir.
	     	 */
    	    	tvc->f.oldParent.vnode = aodp->f.fid.Fid.Vnode;
	    	tvc->f.oldParent.unique = aodp->f.fid.Fid.Unique;

		afs_DisconAddDirty(tvc, 
				   VDisconRename 
				     | (oneDir ? VDisconRenameSameDir:0), 
				   1);
	    }

	    ReleaseWriteLock(&tvc->lock);
	    afs_PutVCache(tvc);
	} else {
	    code = ENOENT;
	}			/* if (tvc) */
#endif
    }				/* if !(AFS_IS_DISCON_RW)*/
    returnCode = code;		/* remember for later */

    /* Now we try to do things locally.  This is really loathsome code. */
    unlinkFid.Fid.Vnode = 0;
    if (code == 0) {
	/*  In any event, we don't really care if the data (tdc2) is not
	 * in the cache; if it isn't, we won't do the update locally.  */
	/* see if version numbers increased properly */
	doLocally = 1;
	if (!AFS_IS_DISCON_RW) {
	    if (oneDir) {
	    	/* number increases by 1 for whole rename operation */
	    	if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
		    doLocally = 0;
	    	}
	    } else {
	    	/* two separate dirs, each increasing by 1 */
	    	if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
		    doLocally = 0;
	    	if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
		    doLocally = 0;
	    	if (!doLocally) {
		    if (tdc1) {
		    	ZapDCE(tdc1);
		    	DZap(tdc1);
		    }
		    if (tdc2) {
		    	ZapDCE(tdc2);
		    	DZap(tdc2);
		    }
	        }
	    }
	}			/* if (!AFS_IS_DISCON_RW) */

	/* now really do the work */
	if (doLocally) {
	    /* first lookup the fid of the dude we're moving */
	    code = afs_dir_Lookup(tdc1, aname1, &fileFid.Fid);
	    if (code == 0) {
		/* delete the source */
		code = afs_dir_Delete(tdc1, aname1);
	    }
	    /* first see if target is there */
	    if (code == 0
		&& afs_dir_Lookup(tdc2, aname2,
				  &unlinkFid.Fid) == 0) {
		/* target already exists, and will be unlinked by server */
		code = afs_dir_Delete(tdc2, aname2);
	    }
	    if (code == 0) {
		ObtainWriteLock(&afs_xdcache, 292);
		code = afs_dir_Create(tdc2, aname2, &fileFid.Fid);
		ReleaseWriteLock(&afs_xdcache);
	    }
	    if (code != 0) {
		ZapDCE(tdc1);
		DZap(tdc1);
		if (!oneDir) {
		    ZapDCE(tdc2);
		    DZap(tdc2);
		}
	    }
	}


	/* update dir link counts */
	if (AFS_IS_DISCON_RW) {
	    if (!oneDir) {
		aodp->f.m.LinkCount--;
		andp->f.m.LinkCount++;
	    }
	    /* If we're in the same directory, link count doesn't change */
	} else {
	    aodp->f.m.LinkCount = OutOldDirStatus.LinkCount;
	    if (!oneDir)
		andp->f.m.LinkCount = OutNewDirStatus.LinkCount;
	}

    } else {			/* operation failed (code != 0) */
	if (code < 0) {
	    /* if failed, server might have done something anyway, and 
	     * assume that we know about it */
	    ObtainWriteLock(&afs_xcbhash, 498);
	    afs_DequeueCallback(aodp);
	    afs_DequeueCallback(andp);
	    andp->f.states &= ~CStatd;
	    aodp->f.states &= ~CStatd;
	    ReleaseWriteLock(&afs_xcbhash);
	    osi_dnlc_purgedp(andp);
	    osi_dnlc_purgedp(aodp);
	}
    }

    /* release locks */
    if (tdc1) {
	ReleaseWriteLock(&tdc1->lock);
	afs_PutDCache(tdc1);
    }

    if ((!oneDir) && tdc2) {
	ReleaseWriteLock(&tdc2->lock);
	afs_PutDCache(tdc2);
    }

    ReleaseWriteLock(&aodp->lock);

    if (!oneDir) {
	ReleaseWriteLock(&andp->lock);
    }

    if (returnCode) {
	code = returnCode;
	goto done;
    }

    /* now, some more details.  if unlinkFid.Fid.Vnode then we should decrement
     * the link count on this file.  Note that if fileFid is a dir, then we don't
     * have to invalidate its ".." entry, since its DataVersion # should have
     * changed. However, interface is not good enough to tell us the
     * *file*'s new DataVersion, so we're stuck.  Our hack: delete mark
     * the data as having an "unknown" version (effectively discarding the ".."
     * entry */
    if (unlinkFid.Fid.Vnode) {

	unlinkFid.Fid.Volume = aodp->f.fid.Fid.Volume;
	unlinkFid.Cell = aodp->f.fid.Cell;
	tvc = NULL;
	if (!unlinkFid.Fid.Unique) {
	    tvc = afs_LookupVCache(&unlinkFid, areq, NULL, aodp, aname1);
	}
	if (!tvc)		/* lookup failed or wasn't called */
	    tvc = afs_GetVCache(&unlinkFid, areq, NULL, NULL);

	if (tvc) {
#ifdef AFS_BOZONLOCK_ENV
	    afs_BozonLock(&tvc->pvnLock, tvc);	/* Since afs_TryToSmush will do a pvn_vptrunc */
#endif
	    ObtainWriteLock(&tvc->lock, 151);
	    tvc->f.m.LinkCount--;
	    tvc->f.states &= ~CUnique;	/* For the dfs xlator */
	    if (tvc->f.m.LinkCount == 0 && !osi_Active(tvc)) {
		/* if this was last guy (probably) discard from cache.
		 * We have to be careful to not get rid of the stat
		 * information, since otherwise operations will start
		 * failing even if the file was still open (or
		 * otherwise active), and the server no longer has the
		 * info.  If the file still has valid links, we'll get
		 * a break-callback msg from the server, so it doesn't
		 * matter that we don't discard the status info */
		if (!AFS_NFSXLATORREQ(acred))
		    afs_TryToSmush(tvc, acred, 0);
	    }
	    ReleaseWriteLock(&tvc->lock);
#ifdef AFS_BOZONLOCK_ENV
	    afs_BozonUnlock(&tvc->pvnLock, tvc);
#endif
	    afs_PutVCache(tvc);
	}
    }

    /* now handle ".." invalidation */
    if (!oneDir) {
	fileFid.Fid.Volume = aodp->f.fid.Fid.Volume;
	fileFid.Cell = aodp->f.fid.Cell;
	if (!fileFid.Fid.Unique)
	    tvc = afs_LookupVCache(&fileFid, areq, NULL, andp, aname2);
	else
	    tvc = afs_GetVCache(&fileFid, areq, NULL, (struct vcache *)0);
	if (tvc && (vType(tvc) == VDIR)) {
	    ObtainWriteLock(&tvc->lock, 152);
	    tdc1 = afs_FindDCache(tvc, (afs_size_t) 0);
	    if (tdc1) {
		if (AFS_IS_DISCON_RW) {
#if defined(AFS_DISCON_ENV)
		    /* If disconnected, we need to fix (not discard) the "..".*/
		    afs_dir_ChangeFid(tdc1,
		    	"..",
		    	&aodp->f.fid.Fid.Vnode,
			&andp->f.fid.Fid.Vnode);
#endif
		} else {
		    ObtainWriteLock(&tdc1->lock, 648);
		    ZapDCE(tdc1);	/* mark as unknown */
		    DZap(tdc1);
		    ReleaseWriteLock(&tdc1->lock);
		    afs_PutDCache(tdc1);	/* put it back */
		}
	    }
	    osi_dnlc_remove(tvc, "..", 0);
	    ReleaseWriteLock(&tvc->lock);
	    afs_PutVCache(tvc);
	} else if (AFS_IS_DISCON_RW && tvc && (vType(tvc) == VREG)) {
	    /* XXX - Should tvc not get locked here? */
	    tvc->f.parent.vnode = andp->f.fid.Fid.Vnode;
	    tvc->f.parent.unique = andp->f.fid.Fid.Unique;
	} else if (tvc) {
	    /* True we shouldn't come here since tvc SHOULD be a dir, but we
	     * 'syntactically' need to unless  we change the 'if' above...
	     */
	    afs_PutVCache(tvc);
	}
    }
    code = returnCode;
  done:
    return code;
}
示例#13
0
int
afsremove(register struct vcache *adp, register struct dcache *tdc,
	  register struct vcache *tvc, char *aname, afs_ucred_t *acred,
	  struct vrequest *treqp)
{
    register afs_int32 code = 0;
    register struct afs_conn *tc;
    struct AFSFetchStatus OutDirStatus;
    struct AFSVolSync tsync;
    XSTATS_DECLS;
    if (!AFS_IS_DISCONNECTED) {
        do {
	    tc = afs_Conn(&adp->f.fid, treqp, SHARED_LOCK);
	    if (tc) {
	        XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
	        RX_AFS_GUNLOCK();
	        code =
		    RXAFS_RemoveFile(tc->id, (struct AFSFid *)&adp->f.fid.Fid,
		  		     aname, &OutDirStatus, &tsync);
	        RX_AFS_GLOCK();
	        XSTATS_END_TIME;
	    } else
	        code = -1;
        } while (afs_Analyze
	         (tc, code, &adp->f.fid, treqp, AFS_STATS_FS_RPCIDX_REMOVEFILE,
	          SHARED_LOCK, NULL));
    }

    osi_dnlc_remove(adp, aname, tvc);

    if (code) {
	if (tdc) {
	    ReleaseSharedLock(&tdc->lock);
	    afs_PutDCache(tdc);
	}

	if (tvc)
	    afs_PutVCache(tvc);

	if (code < 0) {
	    ObtainWriteLock(&afs_xcbhash, 497);
	    afs_DequeueCallback(adp);
	    adp->f.states &= ~CStatd;
	    ReleaseWriteLock(&afs_xcbhash);
	    osi_dnlc_purgedp(adp);
	}
	ReleaseWriteLock(&adp->lock);
	code = afs_CheckCode(code, treqp, 21);
	return code;
    }
    if (tdc)
	UpgradeSToWLock(&tdc->lock, 637);
    if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
	/* we can do it locally */
	code = afs_dir_Delete(tdc, aname);
	if (code) {
	    ZapDCE(tdc);	/* surprise error -- invalid value */
	    DZap(tdc);
	}
    }
    if (tdc) {
	ReleaseWriteLock(&tdc->lock);
	afs_PutDCache(tdc);	/* drop ref count */
    }
    ReleaseWriteLock(&adp->lock);

    /* now, get vnode for unlinked dude, and see if we should force it
     * from cache.  adp is now the deleted files vnode.  Note that we
     * call FindVCache instead of GetVCache since if the file's really
     * gone, we won't be able to fetch the status info anyway.  */
    if (tvc) {
	afs_MarinerLog("store$Removing", tvc);
#ifdef AFS_BOZONLOCK_ENV
	afs_BozonLock(&tvc->pvnLock, tvc);
	/* Since afs_TryToSmush will do a pvn_vptrunc */
#endif
	ObtainWriteLock(&tvc->lock, 141);
	/* note that callback will be broken on the deleted file if there are
	 * still >0 links left to it, so we'll get the stat right */
	tvc->f.m.LinkCount--;
	tvc->f.states &= ~CUnique;	/* For the dfs xlator */
	if (tvc->f.m.LinkCount == 0 && !osi_Active(tvc)) {
	    if (!AFS_NFSXLATORREQ(acred))
		afs_TryToSmush(tvc, acred, 0);
	}
	ReleaseWriteLock(&tvc->lock);
#ifdef AFS_BOZONLOCK_ENV
	afs_BozonUnlock(&tvc->pvnLock, tvc);
#endif
	afs_PutVCache(tvc);
    }
    return (0);
}
示例#14
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;
    register struct dcache *tdc;
    struct VenusFid unlinkFid;
    register afs_int32 code;
    register 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);
#ifndef AFS_DARWIN80_ENV
    /* 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(register struct vcache *avc, register int doit)
{
    afs_ucred_t *cred;
    char *unlname;
    struct vcache *adp;
    struct vrequest treq;
    struct VenusFid dirFid;
    register 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;
}
示例#15
0
afs_root(struct mount *mp, struct vnode **vpp)
#endif
{
    int error;
    struct vrequest treq;
    register struct vcache *tvp = 0;
#ifdef AFS_FBSD50_ENV
#ifndef AFS_FBSD53_ENV
    struct thread *td = curthread;
#endif
    struct ucred *cr = td->td_ucred;
#else
    struct proc *p = curproc;
    struct ucred *cr = p->p_cred->pc_ucred;
#endif

    AFS_GLOCK();
    AFS_STATCNT(afs_root);
    crhold(cr);
    if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
	tvp = afs_globalVp;
	error = 0;
    } else {
tryagain:
#ifndef AFS_FBSD80_ENV
	if (afs_globalVp) {
	    afs_PutVCache(afs_globalVp);
	    /* vrele() needed here or not? */
	    afs_globalVp = NULL;
	}
#endif
	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)
		afs_globalVp = tvp;
	    else
		error = ENOENT;
	}
    }
    if (tvp) {
	struct vnode *vp = AFSTOV(tvp);

#ifdef AFS_FBSD50_ENV
	ASSERT_VI_UNLOCKED(vp, "afs_root");
#endif
	AFS_GUNLOCK();
	/*
	 * 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?
	 */
#ifdef AFS_FBSD50_ENV
	error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td);
	vp->v_vflag |= VV_ROOT;
#else
	error = vget(vp, LK_EXCLUSIVE | LK_RETRY, p);
	vp->v_flag |= VROOT;
#endif
	AFS_GLOCK();
	if (error != 0)
		goto tryagain;

	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;
}
示例#16
0
static int afs_export_get_name(struct dentry *parent, char *name,
			       struct dentry *child)
{
    struct afs_fakestat_state fakestate;
    struct get_name_data data;
    struct vrequest treq;
    struct volume *tvp;
    struct vcache *vcp;
    struct dcache *tdc;
    cred_t *credp;
    afs_size_t dirOffset, dirLen;
    afs_int32 code = 0;

    if (!parent->d_inode) {
#ifdef OSI_EXPORT_DEBUG
	/* can't lookup name in a negative dentry */
	printk("afs: get_name(%s, %s): no parent inode\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       child->d_name.name  ? (char *)child->d_name.name  : "?");
#endif
	return -EIO;
    }
    if (!child->d_inode) {
#ifdef OSI_EXPORT_DEBUG
	/* can't find the FID of negative dentry */
	printk("afs: get_name(%s, %s): no child inode\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       child->d_name.name  ? (char *)child->d_name.name  : "?");
#endif
	return -ENOENT;
    }

    afs_InitFakeStat(&fakestate);

    credp = crref();
    AFS_GLOCK();

    vcp = VTOAFS(child->d_inode);

    /* special case dynamic mount directory */
    if (afs_IsDynrootMount(vcp)) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): this is the dynmount dir\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
	       vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique);
#endif
	data.fid = vcp->f.fid;
	if (VTOAFS(parent->d_inode) == afs_globalVp)
	    strcpy(name, AFS_DYNROOT_MOUNTNAME);
	else
	    code = -ENOENT;
	goto done;
    }

    /* Figure out what FID to look for */
    if (vcp->mvstat == 2) { /* volume root */
	tvp = afs_GetVolume(&vcp->f.fid, 0, READ_LOCK);
	if (!tvp) {
#ifdef OSI_EXPORT_DEBUG
	    printk("afs: get_name(%s, 0x%08x/%d/%d.%d): no volume for root\n",
		   parent->d_name.name ? (char *)parent->d_name.name : "?",
		   vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
		   vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique);
#endif
	    code = ENOENT;
	    goto done;
	}
	data.fid = tvp->mtpoint;
	afs_PutVolume(tvp, READ_LOCK);
    } else {
	data.fid = vcp->f.fid;
    }

    vcp = VTOAFS(parent->d_inode);
#ifdef OSI_EXPORT_DEBUG
    printk("afs: get_name(%s, 0x%08x/%d/%d.%d): parent is 0x%08x/%d/%d.%d\n",
	   parent->d_name.name ? (char *)parent->d_name.name : "?",
	   data.fid.Cell,      data.fid.Fid.Volume,
	   data.fid.Fid.Vnode, data.fid.Fid.Unique,
	   vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
	   vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique);
#endif

    code = afs_InitReq(&treq, credp);
    if (code) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): afs_InitReq: %d\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique, code);
#endif
	goto done;
    }

    /* a dynamic mount point in the dynamic mount directory */
    if (afs_IsDynrootMount(vcp) && afs_IsDynrootAnyFid(&data.fid)
	&& VNUM_TO_VNTYPE(data.fid.Fid.Vnode) == VN_TYPE_MOUNT) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): dynamic mount point\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique);
#endif
	vcp = afs_GetVCache(&data.fid, &treq, NULL, NULL);
	if (vcp) {
	    ObtainReadLock(&vcp->lock);
	    if (strlen(vcp->linkData + 1) <= NAME_MAX)
		strcpy(name, vcp->linkData + 1);
	    else
		code = ENOENT;
	    ReleaseReadLock(&vcp->lock);
	    afs_PutVCache(vcp);
	} else {
#ifdef OSI_EXPORT_DEBUG
	    printk("afs: get_name(%s, 0x%08x/%d/%d.%d): no vcache\n",
		   parent->d_name.name ? (char *)parent->d_name.name : "?",
		   data.fid.Cell,      data.fid.Fid.Volume,
		   data.fid.Fid.Vnode, data.fid.Fid.Unique);
#endif
	    code = ENOENT;
	}
	goto done;
    }

    code = afs_EvalFakeStat(&vcp, &fakestate, &treq);
    if (code)
	goto done;

    if (vcp->f.fid.Cell != data.fid.Cell ||
	vcp->f.fid.Fid.Volume != data.fid.Fid.Volume) {
	/* parent is not the expected cell and volume; thus it
	 * cannot possibly contain the fid we are looking for */
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): wrong parent 0x%08x/%d\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique,
	       vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume);
#endif
	code = ENOENT;
	goto done;
    }


redo:
    if (!(vcp->f.states & CStatd)) {
	if ((code = afs_VerifyVCache2(vcp, &treq))) {
#ifdef OSI_EXPORT_DEBUG
	    printk("afs: get_name(%s, 0x%08x/%d/%d.%d): VerifyVCache2(0x%08x/%d/%d.%d): %d\n",
		   parent->d_name.name ? (char *)parent->d_name.name : "?",
		   data.fid.Cell,      data.fid.Fid.Volume,
		   data.fid.Fid.Vnode, data.fid.Fid.Unique,
		   vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
		   vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code);
#endif
	    goto done;
	}
    }

    tdc = afs_GetDCache(vcp, (afs_size_t) 0, &treq, &dirOffset, &dirLen, 1);
    if (!tdc) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): GetDCache(0x%08x/%d/%d.%d): %d\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique,
	       vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
	       vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code);
#endif
	code = EIO;
	goto done;
    }

    ObtainReadLock(&vcp->lock);
    ObtainReadLock(&tdc->lock);

    /*
     * Make sure that the data in the cache is current. There are two
     * cases we need to worry about:
     * 1. The cache data is being fetched by another process.
     * 2. The cache data is no longer valid
     */
    while ((vcp->f.states & CStatd)
	   && (tdc->dflags & DFFetching)
	   && hsame(vcp->f.m.DataVersion, tdc->f.versionNo)) {
	ReleaseReadLock(&tdc->lock);
	ReleaseReadLock(&vcp->lock);
	afs_osi_Sleep(&tdc->validPos);
	ObtainReadLock(&vcp->lock);
	ObtainReadLock(&tdc->lock);
    }
    if (!(vcp->f.states & CStatd)
	|| !hsame(vcp->f.m.DataVersion, tdc->f.versionNo)) {
	ReleaseReadLock(&tdc->lock);
	ReleaseReadLock(&vcp->lock);
	afs_PutDCache(tdc);
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): dir (0x%08x/%d/%d.%d) changed; retrying\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique,
	       vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
	       vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique);
#endif
	goto redo;
    }

    data.name  = name;
    data.found = 0;
    code = afs_dir_EnumerateDir(tdc, get_name_hook, &data);
    if (!code && !data.found) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): not found\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique);
#endif
	code = ENOENT;
    } else if (code) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d): Enumeratedir(0x%08x/%d/%d.%d): %d\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique,
	       vcp->f.fid.Cell,      vcp->f.fid.Fid.Volume,
	       vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code);
#endif
    }

    ReleaseReadLock(&tdc->lock);
    ReleaseReadLock(&vcp->lock);
    afs_PutDCache(tdc);

done:
    if (!code) {
	printk("afs: get_name(%s, 0x%08x/%d/%d.%d) => %s\n",
	       parent->d_name.name ? (char *)parent->d_name.name : "?",
	       data.fid.Cell,      data.fid.Fid.Volume,
	       data.fid.Fid.Vnode, data.fid.Fid.Unique, name);
    }
    afs_PutFakeStat(&fakestate);
    AFS_GUNLOCK();
    crfree(credp);
    code = afs_CheckCode(code, &treq, 102);
    return -code;
}
示例#17
0
/*!
 * All files that have been dirty before disconnection are going to
 * be replayed back to the server.
 *
 * \param areq Request from the user.
 * \param acred User credentials.
 *
 * \return If all files synchronized succesfully, return 0, otherwise
 * return error code
 *
 * \note For now, it's the request from the PDiscon pioctl.
 *
 */
int
afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred)
{
    struct afs_conn *tc;
    struct rx_connection *rxconn;
    struct vcache *tvc;
    struct AFSFetchStatus fstat;
    struct AFSCallBack callback;
    struct AFSVolSync tsync;
    int code = 0;
    afs_int32 start = 0;
    XSTATS_DECLS;
    /*AFS_STATCNT(afs_ResyncDisconFiles);*/

    ObtainWriteLock(&afs_disconDirtyLock, 707);

    while (!QEmpty(&afs_disconDirty)) {
	tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);

	/* Can't lock tvc whilst holding the discon dirty lock */
	ReleaseWriteLock(&afs_disconDirtyLock);

	/* Get local write lock. */
	ObtainWriteLock(&tvc->lock, 705);

	if (tvc->f.ddirty_flags & VDisconRemove) {
	    /* Delete the file on the server and just move on
	     * to the next file. After all, it has been deleted
	     * we can't replay any other operation it.
	     */
	    code = afs_ProcessOpRemove(tvc, areq);
	    goto next_file;

	} else if (tvc->f.ddirty_flags & VDisconCreate) {
	    /* For newly created files, we don't need a server lock. */
	    code = afs_ProcessOpCreate(tvc, areq, acred);
	    if (code)
	    	goto next_file;

	    tvc->f.ddirty_flags &= ~VDisconCreate;
	    tvc->f.ddirty_flags |= VDisconCreated;
	}
#if 0
  	/* Get server write lock. */
  	do {
	    tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
  	    if (tc) {
	    	XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
  		RX_AFS_GUNLOCK();
  		code = RXAFS_SetLock(rxconn,
					(struct AFSFid *)&tvc->f.fid.Fid,
					LockWrite,
					&tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	   } else
		code = -1;

	} while (afs_Analyze(tc,
			     rxconn,
			     code,
			     &tvc->f.fid,
			     areq,
			     AFS_STATS_FS_RPCIDX_SETLOCK,
			     SHARED_LOCK,
			     NULL));

	if (code)
	    goto next_file;
#endif
	if (tvc->f.ddirty_flags & VDisconRename) {
	    /* If we're renaming the file, do so now */
	    code = afs_ProcessOpRename(tvc, areq);
	    if (code)
	    	goto unlock_srv_file;
	}

	/* Issue a FetchStatus to get info about DV and callbacks. */
	do {
	    tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
	    if (tc) {
	    	tvc->callback = tc->srvr->server;
		start = osi_Time();
		XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
		RX_AFS_GUNLOCK();
		code = RXAFS_FetchStatus(rxconn,
				(struct AFSFid *)&tvc->f.fid.Fid,
				&fstat,
				&callback,
				&tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	    } else
		code = -1;

	} while (afs_Analyze(tc,
			     rxconn,
			     code,
			     &tvc->f.fid,
			     areq,
			     AFS_STATS_FS_RPCIDX_FETCHSTATUS,
			     SHARED_LOCK,
			     NULL));

	if (code) {
	    goto unlock_srv_file;
	}

	if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
	    	(afs_ConflictPolicy == CLIENT_WINS) ||
		(tvc->f.ddirty_flags & VDisconCreated)) {
	    /*
	     * Send changes to the server if there's data version match, or
	     * client wins policy has been selected or file has been created
	     * but doesn't have it's the contents on to the server yet.
	     */
	   /*
	    * XXX: Checking server attr changes by timestamp might not the
	    * most elegant solution, but it's the most viable one that we could find.
	    */
	    afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
	    code = afs_SendChanges(tvc, areq);

	} else if (afs_ConflictPolicy == SERVER_WINS) {
	    /* DV mismatch, apply collision resolution policy. */
	    /* Discard this files chunks and remove from current dir. */
	    afs_ResetVCache(tvc, acred, 0);
	    tvc->f.truncPos = AFS_NOTRUNC;
	} else {
	    /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */
	}		/* if DV match or client wins policy */

unlock_srv_file:
	/* Release server write lock. */
#if 0
	do {
	    tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
	    if (tc) {
	    	XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
	    	RX_AFS_GUNLOCK();
		ucode = RXAFS_ReleaseLock(rxconn,
				(struct AFSFid *) &tvc->f.fid.Fid,
				&tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	    } else
		ucode = -1;
	} while (afs_Analyze(tc,
			     rxconn,
			     ucode,
			     &tvc->f.fid,
			     areq,
			     AFS_STATS_FS_RPCIDX_RELEASELOCK,
			     SHARED_LOCK,
			     NULL));
#endif
next_file:
	ObtainWriteLock(&afs_disconDirtyLock, 710);
	if (code == 0) {
	    /* Replayed successfully - pull the vcache from the
	     * disconnected list */
	    tvc->f.ddirty_flags = 0;
	    QRemove(&tvc->dirtyq);
	    afs_PutVCache(tvc);
	} else {
	    if (code == EAGAIN)	{
		/* Operation was deferred. Pull it from the current place in
		 * the list, and stick it at the end again */
		QRemove(&tvc->dirtyq);
	   	QAdd(&afs_disconDirty, &tvc->dirtyq);
	    } else {
		/* Failed - keep state as is, and let the user know we died */

    		ReleaseWriteLock(&tvc->lock);
		break;
	    }
	}

	/* Release local write lock. */
	ReleaseWriteLock(&tvc->lock);
    }			/* while (tvc) */

    if (code) {
        ReleaseWriteLock(&afs_disconDirtyLock);
	return code;
    }

    /* Dispose of all of the shadow directories */
    afs_DisconDiscardAllShadows(0, acred);

    ReleaseWriteLock(&afs_disconDirtyLock);
    return code;
}
示例#18
0
int
afs_AccessOK(struct vcache *avc, afs_int32 arights, struct vrequest *areq,
	     afs_int32 check_mode_bits)
{
    register struct vcache *tvc;
    struct VenusFid dirFid;
    register afs_int32 mask;
    afs_int32 dirBits;
    register afs_int32 fileBits;

    AFS_STATCNT(afs_AccessOK);

    if ((vType(avc) == VDIR) || (avc->f.states & CForeign)) {
	/* rights are just those from acl */
	if (afs_InReadDir(avc)) {
	    /* if we are already in readdir, then they may have read and
	     * lookup, and nothing else, and nevermind the real ACL.
	     * Otherwise we might end up with problems trying to call
	     * FetchStatus on the vnode readdir is working on, and that
	     * would be a real mess.
	     */
	    dirBits = PRSFS_LOOKUP | PRSFS_READ;
	    return (arights == (dirBits & arights));
	}
	return (arights == afs_GetAccessBits(avc, arights, areq));
    } else {
	/* some rights come from dir and some from file.  Specifically, you 
	 * have "a" rights to a file if you are its owner, which comes
	 * back as "a" rights to the file. You have other rights just
	 * from dir, but all are restricted by the file mode bit. Now,
	 * if you have I and A rights to a file, we throw in R and W
	 * rights for free. These rights will then be restricted by
	 * the access mask. */
	dirBits = 0;
	if (avc->f.parent.vnode) {
	    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;
	    /* Avoid this GetVCache call */
	    tvc = afs_GetVCache(&dirFid, areq, NULL, NULL);
	    if (tvc) {
		dirBits = afs_GetAccessBits(tvc, arights, areq);
		afs_PutVCache(tvc);
	    }
	} else
	    dirBits = 0xffffffff;	/* assume OK; this is a race condition */
	if (arights & PRSFS_ADMINISTER)
	    fileBits = afs_GetAccessBits(avc, arights, areq);
	else
	    fileBits = 0;	/* don't make call if results don't matter */

	/* compute basic rights in fileBits, taking A from file bits */
	fileBits =
	    (fileBits & PRSFS_ADMINISTER) | (dirBits & ~PRSFS_ADMINISTER);

	/* for files, throw in R and W if have I and A (owner).  This makes
	 * insert-only dirs work properly */
	if (vType(avc) != VDIR
	    && (fileBits & (PRSFS_ADMINISTER | PRSFS_INSERT)) ==
	    (PRSFS_ADMINISTER | PRSFS_INSERT))
	    fileBits |= (PRSFS_READ | PRSFS_WRITE);

	if (check_mode_bits & CHECK_MODE_BITS) {
	    /* owner mode bits are further restrictions on the access mode
	     * The mode bits are mapped to protection bits through the
	     * fileModeMap. If CMB_ALLOW_EXEC_AS_READ is set, it's from the
	     * NFS translator and we don't know if it's a read or execute
	     * on the NFS client, but both need to read the data.
	     */
	    mask = (avc->f.m.Mode & 0700) >> 6;	/* file restrictions to use */
	    fileBits &= ~fileModeMap[mask];
	    if (check_mode_bits & CMB_ALLOW_EXEC_AS_READ) {
		if (avc->f.m.Mode & 0100)
		    fileBits |= PRSFS_READ;
	    }
	}
	return ((fileBits & arights) == arights);	/* true if all rights bits are on */
    }
示例#19
0
/* don't set CDirty in here because RPC is called synchronously */
int 
afs_symlink(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, 
	    char *atargetName, struct vcache **tvcp, afs_ucred_t *acred)
{
    afs_uint32 now = 0;
    struct vrequest *treq = NULL;
    afs_int32 code = 0;
    struct afs_conn *tc;
    struct VenusFid newFid;
    struct dcache *tdc;
    afs_size_t offset, len;
    afs_int32 alen;
    struct server *hostp = 0;
    struct vcache *tvc;
    struct AFSStoreStatus InStatus;
    struct AFSFetchStatus *OutFidStatus, *OutDirStatus;
    struct AFSCallBack CallBack;
    struct AFSVolSync tsync;
    struct volume *volp = 0;
    struct afs_fakestat_state fakestate;
    struct rx_connection *rxconn;
    XSTATS_DECLS;
    OSI_VC_CONVERT(adp);

    AFS_STATCNT(afs_symlink);
    afs_Trace2(afs_iclSetp, CM_TRACE_SYMLINK, ICL_TYPE_POINTER, adp,
	       ICL_TYPE_STRING, aname);

    OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
    OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
    memset(&InStatus, 0, sizeof(InStatus));

    if ((code = afs_CreateReq(&treq, acred)))
	goto done2;

    afs_InitFakeStat(&fakestate);

    AFS_DISCON_LOCK();
    
    code = afs_EvalFakeStat(&adp, &fakestate, treq);
    if (code)
	goto done;

    if (strlen(aname) > AFSNAMEMAX || strlen(atargetName) > AFSPATHMAX) {
	code = ENAMETOOLONG;
	goto done;
    }

    if (afs_IsDynroot(adp)) {
	code = afs_DynrootVOPSymlink(adp, acred, aname, atargetName);
	goto done;
    }
    if (afs_IsDynrootMount(adp)) {
	code = EROFS;
	goto done;
    }

    code = afs_VerifyVCache(adp, treq);
    if (code) {
	code = afs_CheckCode(code, treq, 30);
	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;
    }
    
    InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE;
    InStatus.ClientModTime = osi_Time();
    alen = strlen(atargetName);	/* we want it to include the null */
    if ( (*atargetName == '#' || *atargetName == '%') && alen > 1 && atargetName[alen-1] == '.') {
	InStatus.UnixModeBits = 0644;	/* mt pt: null from "." at end */
	if (alen == 1)
	    alen++;		/* Empty string */
    } else {
	InStatus.UnixModeBits = 0755;
	alen++;			/* add in the null */
    }
    tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);
    volp = afs_FindVolume(&adp->f.fid, READ_LOCK);	/*parent is also in same vol */
    ObtainWriteLock(&adp->lock, 156);
    if (tdc)
	ObtainWriteLock(&tdc->lock, 636);
    /* No further locks: if the SymLink succeeds, it does not matter what happens
     * to our local copy of the directory. If somebody tampers with it in the meantime,
     * the copy will be invalidated */
    if (!AFS_IS_DISCON_RW) {
	do {
	    tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
	    if (tc) {
		hostp = tc->parent->srvr->server;
		XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SYMLINK);
		if (adp->f.states & CForeign) {
		    now = osi_Time();
		    RX_AFS_GUNLOCK();
		    code = 
			RXAFS_DFSSymlink(rxconn,
					 (struct AFSFid *)&adp->f.fid.Fid,
					 aname, atargetName, &InStatus,
					 (struct AFSFid *)&newFid.Fid,
					 OutFidStatus, OutDirStatus,
					 &CallBack, &tsync);
		    RX_AFS_GLOCK();
		} else {
		    RX_AFS_GUNLOCK();
		    code =
			RXAFS_Symlink(rxconn, (struct AFSFid *)&adp->f.fid.Fid,
				      aname, atargetName, &InStatus,
				      (struct AFSFid *)&newFid.Fid, 
				      OutFidStatus, OutDirStatus, &tsync);
		    RX_AFS_GLOCK();
	    	}
		XSTATS_END_TIME;
	    } else
		code = -1;
	} while (afs_Analyze
		    (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_SYMLINK,
		     SHARED_LOCK, NULL));
    } else {
	newFid.Cell = adp->f.fid.Cell;
	newFid.Fid.Volume = adp->f.fid.Fid.Volume;
	afs_GenFakeFid(&newFid, VREG, 0);
    }

    ObtainWriteLock(&afs_xvcache, 40);
    if (code) {
	if (code < 0) {
	    afs_StaleVCache(adp);
	}
	ReleaseWriteLock(&adp->lock);
	ReleaseWriteLock(&afs_xvcache);
	if (tdc) {
	    ReleaseWriteLock(&tdc->lock);
	    afs_PutDCache(tdc);
	}
	goto done;
    }
    /* otherwise, we should see if we can make the change to the dir locally */
    if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) {
	/* we can do it locally */
	ObtainWriteLock(&afs_xdcache, 293);
	/* If the following fails because the name has been created in the meantime, the
	 * directory is out-of-date - the file server knows best! */
	code = afs_dir_Create(tdc, aname, &newFid.Fid);
	ReleaseWriteLock(&afs_xdcache);
	if (code && !AFS_IS_DISCON_RW) {
	    ZapDCE(tdc);	/* surprise error -- use invalid value */
	    DZap(tdc);
	}
    }
    if (tdc) {
	ReleaseWriteLock(&tdc->lock);
	afs_PutDCache(tdc);
    }
    newFid.Cell = adp->f.fid.Cell;
    newFid.Fid.Volume = adp->f.fid.Fid.Volume;
    ReleaseWriteLock(&adp->lock);

    /* now we're done with parent dir, create the link's entry.  Note that
     * no one can get a pointer to the new cache entry until we release 
     * the xvcache lock. */
    tvc = afs_NewVCache(&newFid, hostp);
    if (!tvc)
    {
	code = -2;
	ReleaseWriteLock(&afs_xvcache);
	goto done;
    }
    ObtainWriteLock(&tvc->lock, 157);
    ObtainWriteLock(&afs_xcbhash, 500);
    tvc->f.states |= CStatd;	/* have valid info */
    tvc->f.states &= ~CBulkFetching;

    if (adp->f.states & CForeign) {
	tvc->f.states |= CForeign;
	/* We don't have to worry about losing the callback since we're doing it 
	 * under the afs_xvcache lock actually, afs_NewVCache may drop the 
	 * afs_xvcache lock, if it calls afs_FlushVCache */
	tvc->cbExpires = CallBack.ExpirationTime + now;
	afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp);
    } else {
	tvc->cbExpires = 0x7fffffff;	/* never expires, they can't change */
	/* since it never expires, we don't have to queue the callback */
    }
    ReleaseWriteLock(&afs_xcbhash);

    if (AFS_IS_DISCON_RW) {
	attrs->va_mode = InStatus.UnixModeBits;
	afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VLNK);
	code = afs_DisconCreateSymlink(tvc, atargetName, treq);
	if (code) {
	    /* XXX - When this goes wrong, we need to tidy up the changes we made to
	     * the parent, and get rid of the vcache we just created */
	    ReleaseWriteLock(&tvc->lock);
	    ReleaseWriteLock(&afs_xvcache);
	    afs_PutVCache(tvc);
	    goto done;
	}
	afs_DisconAddDirty(tvc, VDisconCreate, 0);
    } else {
	afs_ProcessFS(tvc, OutFidStatus, treq);
    }

    if (!tvc->linkData) {
	tvc->linkData = afs_osi_Alloc(alen);
	osi_Assert(tvc->linkData != NULL);
	strncpy(tvc->linkData, atargetName, alen - 1);
	tvc->linkData[alen - 1] = 0;
    }
    ReleaseWriteLock(&tvc->lock);
    ReleaseWriteLock(&afs_xvcache);
    if (tvcp)
	*tvcp = tvc;
    else
	afs_PutVCache(tvc);
    code = 0;
  done:
    afs_PutFakeStat(&fakestate);
    if (volp)
	afs_PutVolume(volp, READ_LOCK);
    AFS_DISCON_UNLOCK();
    code = afs_CheckCode(code, treq, 31);
    afs_DestroyReq(treq);
  done2:
    osi_FreeSmallSpace(OutFidStatus);
    osi_FreeSmallSpace(OutDirStatus);
    return code;
}
示例#20
0
/*!
 * Handles all the reconnection details:
 * - Get all the details about the vnode: name, fid, and parent dir fid.
 * - Send data to server.
 * - Handle errors.
 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
 */
int
afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
		    afs_ucred_t *acred)
{
    char *tname = NULL, *ttargetName = NULL;
    struct AFSStoreStatus InStatus;
    struct AFSFetchStatus OutFidStatus, OutDirStatus;
    struct VenusFid pdir_fid, newFid;
    struct AFSCallBack CallBack;
    struct AFSVolSync tsync;
    struct vcache *tdp = NULL, *tvc = NULL;
    struct dcache *tdc = NULL;
    struct afs_conn *tc;
    struct rx_connection *rxconn;
    afs_int32 hash, new_hash, index;
    afs_size_t tlen;
    int code, op = 0;
    XSTATS_DECLS;

    tname = afs_osi_Alloc(AFSNAMEMAX);
    if (!tname)
	return ENOMEM;

    code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
    if (code)
	goto end;

    /* This data may also be in linkData, but then we have to deal with
     * the joy of terminating NULLs and . and file modes. So just get
     * it from the dcache where it won't have been fiddled with.
     */
    if (vType(avc) == VLNK) {
	afs_size_t offset;
	struct dcache *tdc;
	struct osi_file *tfile;

	tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
	if (!tdc) {
	    code = ENOENT;
	    goto end;
	}

	if (tlen > 1024) {
	    afs_PutDCache(tdc);
	    code = EFAULT;
	    goto end;
	}

	tlen++; /* space for NULL */
	ttargetName = afs_osi_Alloc(tlen);
	if (!ttargetName) {
	    afs_PutDCache(tdc);
	    return ENOMEM;
	}
	ObtainReadLock(&tdc->lock);
	tfile = afs_CFileOpen(&tdc->f.inode);
	code = afs_CFileRead(tfile, 0, ttargetName, tlen);
	ttargetName[tlen-1] = '\0';
	afs_CFileClose(tfile);
	ReleaseReadLock(&tdc->lock);
	afs_PutDCache(tdc);
    }

    /* Set status. */
    InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
    InStatus.ClientModTime = avc->f.m.Date;
    InStatus.Owner = avc->f.m.Owner;
    InStatus.Group = (afs_int32) afs_cr_gid(acred);
    /* Only care about protection bits. */
    InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;

    do {
	tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK, &rxconn);
	if (tc) {
	    switch (vType(avc)) {
	    case VREG:
                /* Make file on server. */
		op = AFS_STATS_FS_RPCIDX_CREATEFILE;
		XSTATS_START_TIME(op);
                RX_AFS_GUNLOCK();
                code = RXAFS_CreateFile(tc->id,
					(struct AFSFid *)&tdp->f.fid.Fid,
					tname, &InStatus,
					(struct AFSFid *) &newFid.Fid,
					&OutFidStatus, &OutDirStatus,
					&CallBack, &tsync);
                RX_AFS_GLOCK();
                XSTATS_END_TIME;
		break;
	    case VDIR:
		/* Make dir on server. */
		op = AFS_STATS_FS_RPCIDX_MAKEDIR;
		XSTATS_START_TIME(op);
                RX_AFS_GUNLOCK();
		code = RXAFS_MakeDir(rxconn, (struct AFSFid *) &tdp->f.fid.Fid,
				     tname, &InStatus,
				     (struct AFSFid *) &newFid.Fid,
				     &OutFidStatus, &OutDirStatus,
				     &CallBack, &tsync);
                RX_AFS_GLOCK();
                XSTATS_END_TIME;
		break;
	    case VLNK:
		/* Make symlink on server. */
		op = AFS_STATS_FS_RPCIDX_SYMLINK;
		XSTATS_START_TIME(op);
		RX_AFS_GUNLOCK();
		code = RXAFS_Symlink(rxconn,
				(struct AFSFid *) &tdp->f.fid.Fid,
				tname, ttargetName, &InStatus,
				(struct AFSFid *) &newFid.Fid,
				&OutFidStatus, &OutDirStatus, &tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	        break;
	    default:
		op = AFS_STATS_FS_RPCIDX_CREATEFILE;
		code = 1;
		break;
	    }
        } else
	    code = -1;
    } while (afs_Analyze(tc, rxconn, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));

    /* TODO: Handle errors. */
    if (code) {
	/* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
	goto end;
    }

    /* The rpc doesn't set the cell number. */
    newFid.Cell = avc->f.fid.Cell;

    /*
     * Change the fid in the dir entry.
     */

    /* Seek the dir's dcache. */
    tdc = afs_FindDCacheByFid(&tdp->f.fid);
    if (tdc) {
    	/* And now change the fid in the parent dir entry. */
    	afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
    	afs_PutDCache(tdc);
    }

    if (vType(avc) == VDIR) {
	/* Change fid in the dir for the "." entry. ".." has alredy been
	 * handled by afs_FixChildrenFids when processing the parent dir.
	 */
	tdc = afs_FindDCacheByFid(&avc->f.fid);
	if (tdc) {
   	    afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode,
			      &newFid.Fid.Vnode);

	    if (avc->f.m.LinkCount >= 2)
	        /* For non empty dirs, fix children's parentVnode and
		 * parentUnique reference.
	     	 */
	    	afs_FixChildrenFids(&avc->f.fid, &newFid);

	    afs_PutDCache(tdc);
	}
    }

    /* Recompute hash chain positions for vnode and dcaches.
     * Then change to the new FID.
     */

    /* The vcache goes first. */
    ObtainWriteLock(&afs_xvcache, 735);

    /* Old fid hash. */
    hash = VCHash(&avc->f.fid);
    /* New fid hash. */
    new_hash = VCHash(&newFid);

    /* Remove hash from old position. */
    /* XXX: not checking array element contents. It shouldn't be empty.
     * If it oopses, then something else might be wrong.
     */
    if (afs_vhashT[hash] == avc) {
        /* First in hash chain (might be the only one). */
	afs_vhashT[hash] = avc->hnext;
    } else {
        /* More elements in hash chain. */
 	for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
	    if (tvc->hnext == avc) {
		tvc->hnext = avc->hnext;
		break;
	    }
        }
    }                           /* if (!afs_vhashT[i]->hnext) */
    QRemove(&avc->vhashq);

    /* Insert hash in new position. */
    avc->hnext = afs_vhashT[new_hash];
    afs_vhashT[new_hash] = avc;
    QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);

    ReleaseWriteLock(&afs_xvcache);

    /* Do the same thing for all dcaches. */
    hash = DVHash(&avc->f.fid);
    ObtainWriteLock(&afs_xdcache, 743);
    for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
        hash = afs_dvnextTbl[index];
        tdc = afs_GetValidDSlot(index);
        ReleaseReadLock(&tdc->tlock);
	if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {

		/* Safer but slower. */
 		afs_HashOutDCache(tdc, 0);

                /* Put dcache in new positions in the dchash and dvhash. */
 		new_hash = DCHash(&newFid, tdc->f.chunk);
 		afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
 		afs_dchashTbl[new_hash] = tdc->index;

 		new_hash = DVHash(&newFid);
 		afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
 		afs_dvhashTbl[new_hash] = tdc->index;

 		afs_indexUnique[tdc->index] = newFid.Fid.Unique;
		memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
           }                   /* if fid match */
	}                       /* if uniquifier match */
    	if (tdc)
	    afs_PutDCache(tdc);
    }                           /* for all dcaches in this hash bucket */
    ReleaseWriteLock(&afs_xdcache);

    /* Now we can set the new fid. */
    memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid));

end:
    if (tdp)
    	afs_PutVCache(tdp);
    afs_osi_Free(tname, AFSNAMEMAX);
    if (ttargetName)
	afs_osi_Free(ttargetName, tlen);
    return code;
}
示例#21
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;
}
示例#22
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 = NULL;
		    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_CreateReq(&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
#if defined(D_ALIAS_IS_HLIST)
		    hlist_del_init(&dp->d_alias);
		    hlist_add_head(&dp->d_alias, &(AFSTOV(vcp)->i_dentry));
#else
		    list_del_init(&dp->d_alias);
		    list_add(&dp->d_alias, &(AFSTOV(vcp)->i_dentry));
#endif
		    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);
		    afs_DestroyReq(treq);
		}
#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;
}
示例#23
0
/*!
 * Try to get a vnode's name by comparing all parent dir's entries
 * to the given fid. It can also return the dir's dcache.
 *
 * \param avc The file's vcache.
 * \param afid The parent dir's fid.
 * \param aname A preallocated string for the name.
 * \param deleted Has this file been deleted? If yes, use the shadow
 * dir for looking up the name.
 */
int
afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname,
		 int deleted)
{
    int code = 0;
    struct dcache *tdc;
    struct vcache *parent_vc;
    struct NameAndFid tnf;
    struct VenusFid parent_fid;
    struct VenusFid shadow_fid;

    /* List dir contents and get it's tdc. */
    if (deleted) {
	/* For deleted files, get the shadow dir's tdc: */

	/* Get the parent dir's vcache that contains the shadow fid. */
	parent_fid.Cell = avc->f.fid.Cell;
	parent_fid.Fid.Volume = avc->f.fid.Fid.Volume;
	if (avc->f.ddirty_flags & VDisconRename) {
	    /* For renames the old dir fid is needed. */
	    parent_fid.Fid.Vnode = avc->f.oldParent.vnode;
	    parent_fid.Fid.Unique = avc->f.oldParent.unique;
	} else {
	    parent_fid.Fid.Vnode = afid->Fid.Vnode;
	    parent_fid.Fid.Unique = afid->Fid.Unique;
	}

	/* Get the parent dir's vcache that contains the shadow fid. */
	ObtainSharedLock(&afs_xvcache, 755);
	parent_vc = afs_FindVCache(&parent_fid, 0, 1);
	ReleaseSharedLock(&afs_xvcache);
	if (!parent_vc) {
	    return ENOENT;
	}

	shadow_fid.Cell = parent_vc->f.fid.Cell;
    	shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume;
    	shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode;
    	shadow_fid.Fid.Unique = parent_vc->f.shadow.unique;

	afs_PutVCache(parent_vc);

	/* Get shadow dir's dcache. */
	tdc = afs_FindDCacheByFid(&shadow_fid);

    } else {

	/* For normal files, look into the current dir's entry. */
	tdc = afs_FindDCacheByFid(afid);
    }			/* if (deleted) */

    if (tdc) {
	tnf.fid = &avc->f.fid;
   	tnf.name_len = -1;
    	tnf.name = aname;
    	afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
	afs_PutDCache(tdc);
	if (tnf.name_len == -1)
	    code = ENOENT;
    } else {
	/* printf("Directory dcache not found!\n"); */
        code = ENOENT;
    }

    return code;
}
示例#24
0
afs_rmdir(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred)
#endif
{
    struct vrequest *treq = NULL;
    struct dcache *tdc;
    struct vcache *tvc = NULL;
    afs_int32 code;
    struct afs_conn *tc;
    afs_size_t offset, len;
    struct AFSFetchStatus OutDirStatus;
    struct AFSVolSync tsync;
    struct afs_fakestat_state fakestate;
    struct rx_connection *rxconn;
    XSTATS_DECLS;
    OSI_VC_CONVERT(adp);

    AFS_STATCNT(afs_rmdir);

    afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp,
	       ICL_TYPE_STRING, aname);

    if ((code = afs_CreateReq(&treq, acred)))
	goto done2;
    afs_InitFakeStat(&fakestate);

    if (strlen(aname) > AFSNAMEMAX) {
	code = ENAMETOOLONG;
	goto done;
    }

    AFS_DISCON_LOCK();

    code = afs_EvalFakeStat(&adp, &fakestate, treq);
    if (code)
	goto done;

    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) {
    	/* Disconnected read only mode. */
        code = ENETDOWN;
        goto done;
    }

    tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);	/* test for error below */
    ObtainWriteLock(&adp->lock, 154);
    if (tdc)
	ObtainSharedLock(&tdc->lock, 633);
    if (tdc && (adp->f.states & CForeign)) {
	struct VenusFid unlinkFid;

	unlinkFid.Fid.Vnode = 0;
	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, 1 /* do xstats */ );
		ReleaseReadLock(&afs_xvcache);
	    }
	}
    }

    if (!AFS_IS_DISCON_RW) {
	/* Not disconnected, can connect to server. */
    	do {
	    tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
	    if (tc) {
	    	XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
	    	RX_AFS_GUNLOCK();
	    	code =
		    RXAFS_RemoveDir(rxconn,
		    		(struct AFSFid *)&adp->f.fid.Fid,
				aname,
				&OutDirStatus,
				&tsync);
	    	RX_AFS_GLOCK();
	    	XSTATS_END_TIME;
	    } else
	    	code = -1;
    	} while (afs_Analyze
	         (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_REMOVEDIR,
	         SHARED_LOCK, NULL));

    	if (code) {
	    if (tdc) {
	    	ReleaseSharedLock(&tdc->lock);
	    	afs_PutDCache(tdc);
	    }

	    if (code < 0) {
		afs_StaleVCache(adp);
	    }
	    ReleaseWriteLock(&adp->lock);
	    goto done;
    	}

    	/* here if rpc worked; update the in-core link count */
    	adp->f.m.LinkCount = OutDirStatus.LinkCount;

    } else {
    	/* Disconnected. */

	if (!tdc) {
	    ReleaseWriteLock(&adp->lock);
	    /* printf("afs_rmdir: No local dcache!\n"); */
	    code = ENETDOWN;
	    goto done;
	}
	
	if (!tvc) {
	    /* Find the vcache. */
	    struct VenusFid tfid;

	    tfid.Cell = adp->f.fid.Cell;
	    tfid.Fid.Volume = adp->f.fid.Fid.Volume;
	    code = afs_dir_Lookup(tdc, aname, &tfid.Fid);

	    ObtainSharedLock(&afs_xvcache, 764);
	    tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ );
	    ReleaseSharedLock(&afs_xvcache);
	    
	    if (!tvc) {
		/* printf("afs_rmdir: Can't find dir's vcache!\n"); */
		ReleaseSharedLock(&tdc->lock);
	        afs_PutDCache(tdc);	/* drop ref count */
    	        ReleaseWriteLock(&adp->lock);
		code = ENETDOWN;
	        goto done;
	    }
	}

	if (tvc->f.m.LinkCount > 2) {
	    /* This dir contains more than . and .., thus it can't be
	     * deleted.
	     */
	    ReleaseSharedLock(&tdc->lock);
	    afs_PutDCache(tdc);
	    afs_PutVCache(tvc);
	    ReleaseWriteLock(&adp->lock);
	    code = ENOTEMPTY;
	    goto done;
	}

    	/* Make a shadow copy of the parent dir (if not done already).
	 * If we were created locally, then we don't need to have a shadow
	 * directory (as there's no server state to remember)
	 */
	if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) {
	    afs_MakeShadowDir(adp, tdc);
	}

	adp->f.m.LinkCount--;
    }				/* if (!AFS_IS_DISCON_RW) */

    if (tdc)
	UpgradeSToWLock(&tdc->lock, 634);
    if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
	/* we can do it locally */
	code = afs_dir_Delete(tdc, aname);
	if (code) {
	    ZapDCE(tdc);	/* surprise error -- invalid value */
	    DZap(tdc);
	}
    }
    if (tdc) {
	ReleaseWriteLock(&tdc->lock);
	afs_PutDCache(tdc);	/* drop ref count */
    }

    if (tvc)
	osi_dnlc_purgedp(tvc);	/* get rid of any entries for this directory */
    else
	osi_dnlc_remove(adp, aname, 0);

    if (tvc) {
	ObtainWriteLock(&tvc->lock, 155);
	tvc->f.states &= ~CUnique;	/* For the dfs xlator */
	if (AFS_IS_DISCON_RW) {
	    if (tvc->f.ddirty_flags & VDisconCreate) {
		/* If we we were created whilst disconnected, removal doesn't
		 * need to get logged. Just go away gracefully */
		afs_DisconRemoveDirty(tvc);
	    } else {
		afs_DisconAddDirty(tvc, VDisconRemove, 1);
	    }
	}
	ReleaseWriteLock(&tvc->lock);
	afs_PutVCache(tvc);
    }
    ReleaseWriteLock(&adp->lock);
    /* don't worry about link count since dirs can not be hardlinked */
    code = 0;

  done:
    AFS_DISCON_UNLOCK();
    afs_PutFakeStat(&fakestate);
    code = afs_CheckCode(code, treq, 27);
    afs_DestroyReq(treq);
  done2:
    return code;
}
示例#25
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 = NULL;
    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);

    OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
    OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
    memset(&InStatus, 0, sizeof(InStatus));

    if ((code = afs_CreateReq(&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);
    afs_DestroyReq(treq);

  done2:
    osi_FreeSmallSpace(OutFidStatus);
    osi_FreeSmallSpace(OutDirStatus);
    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;
    }
}
示例#26
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;
    struct vcache *vcp;
    struct vattr vattr;
    struct inode *ip;
    struct dentry *dp;
    afs_int32 code;

    code = afs_InitReq(&treq, credp);
    if (code) {
#ifdef OSI_EXPORT_DEBUG
	printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): afs_InitReq: %d\n",
	       afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique,
	       code);
#endif
	return ERR_PTR(-afs_CheckCode(code, &treq, 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
	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 != 2 && !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);
	return ERR_PTR(-afs_CheckCode(code, &treq, 101));
    }

    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
	return ERR_PTR(-ENOMEM);
    }

    dp->d_op = &afs_dentry_operations;
    return dp;
}