Esempio n. 1
0
int
afscp_Symlink(const struct afscp_venusfid *dir, char *name,
	      char *target, struct AFSStoreStatus *sst)
{
    int code, i, j;
    struct AFSFid df = dir->fid;
    struct afscp_volume *vol;
    struct AFSFetchStatus dfst, fst;
    struct AFSVolSync vs;
    struct AFSFid ff;
    struct afscp_server *server;
    struct rx_connection *c;

    vol = afscp_VolumeById(dir->cell, dir->fid.Volume);
    if (vol == NULL) {
	afscp_errno = ENOENT;
	return -1;
    }
    code = ENOENT;
    for (i = 0; i < vol->nservers; i++) {
	server = afscp_ServerByIndex(vol->servers[i]);
	if (server && server->naddrs > 0) {
	    for (j = 0; j < server->naddrs; j++) {
		c = afscp_ServerConnection(server, j);
		if (c == NULL)
		    break;
		code = RXAFS_Symlink(c, &df, name, target, sst,
				     &ff, &fst, &dfst, &vs);
		if (code >= 0)
		    break;
	    }
	}
	if (code >= 0)
	    break;
    }
    if (code != 0) {
	_StatInvalidate(dir);
	afscp_errno = code;
	return -1;
    }
    _StatStuff(dir, &dfst);
    return 0;
}
Esempio n. 2
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;
}
Esempio n. 3
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;
}