static int afs_DisconCreateSymlink(struct vcache *avc, char *aname, struct vrequest *areq) { struct dcache *tdc; struct osi_file *tfile; afs_size_t offset, len; tdc = afs_GetDCache(avc, 0, areq, &offset, &len, 0); if (!tdc) { /* printf("afs_DisconCreateSymlink: can't get new dcache for symlink.\n"); */ return ENETDOWN; } len = strlen(aname); avc->f.m.Length = len; ObtainWriteLock(&tdc->lock, 720); afs_AdjustSize(tdc, len); tdc->validPos = len; tfile = afs_CFileOpen(&tdc->f.inode); afs_CFileWrite(tfile, 0, aname, len); afs_CFileClose(tfile); ReleaseWriteLock(&tdc->lock); return 0; }
static void DFlushBuffer(struct buffer *ab) { struct osi_file *tfile; tfile = afs_CFileOpen(&ab->inode); afs_CFileWrite(tfile, ab->page * AFS_BUFFER_PAGESIZE, ab->data, AFS_BUFFER_PAGESIZE); ab->dirty = 0; /* Clear the dirty flag */ afs_CFileClose(tfile); }
/*! * * Extend a cache file * * \param avc pointer to vcache to extend data for * \param alen Length to extend file to * \param areq * * \note avc must be write locked. May release and reobtain avc and GLOCK */ int afs_ExtendSegments(struct vcache *avc, afs_size_t alen, struct vrequest *areq) { afs_size_t offset, toAdd; struct osi_file *tfile; afs_int32 code = 0; struct dcache *tdc; void *zeros; zeros = afs_osi_Alloc(AFS_PAGESIZE); if (zeros == NULL) return ENOMEM; memset(zeros, 0, AFS_PAGESIZE); while (avc->f.m.Length < alen) { tdc = afs_ObtainDCacheForWriting(avc, avc->f.m.Length, alen - avc->f.m.Length, areq, 0); if (!tdc) { code = EIO; break; } toAdd = alen - avc->f.m.Length; offset = avc->f.m.Length - AFS_CHUNKTOBASE(tdc->f.chunk); if (offset + toAdd > AFS_CHUNKTOSIZE(tdc->f.chunk)) { toAdd = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; } tfile = afs_CFileOpen(&tdc->f.inode); while(tdc->validPos < avc->f.m.Length + toAdd) { afs_size_t towrite; towrite = (avc->f.m.Length + toAdd) - tdc->validPos; if (towrite > AFS_PAGESIZE) towrite = AFS_PAGESIZE; code = afs_CFileWrite(tfile, tdc->validPos - AFS_CHUNKTOBASE(tdc->f.chunk), zeros, towrite); tdc->validPos += towrite; } afs_CFileClose(tfile); afs_AdjustSize(tdc, offset + toAdd ); avc->f.m.Length += toAdd; ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } afs_osi_Free(zeros, AFS_PAGESIZE); return code; }
/* * afs_TruncateAllSegments * * Description: * Truncate a cache file. * * Parameters: * avc : Ptr to vcache entry to truncate. * alen : Number of bytes to make the file. * areq : Ptr to request structure. * * Environment: * Called with avc write-locked; in VFS40 systems, pvnLock is also * held. */ int afs_TruncateAllSegments(struct vcache *avc, afs_size_t alen, struct vrequest *areq, afs_ucred_t *acred) { struct dcache *tdc; afs_int32 code; afs_int32 index; afs_size_t newSize; int dcCount, dcPos; struct dcache **tdcArray = NULL; AFS_STATCNT(afs_TruncateAllSegments); avc->f.m.Date = osi_Time(); afs_Trace3(afs_iclSetp, CM_TRACE_TRUNCALL, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length), ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(alen)); if (alen >= avc->f.m.Length) { /* * Special speedup since Sun's vm extends the file this way; * we've never written to the file thus we can just set the new * length and avoid the needless calls below. * Also used for ftruncate calls which can extend the file. * To completely minimize the possible extra StoreMini RPC, we really * should keep the ExtendedPos as well and clear this flag if we * truncate below that value before we store the file back. */ avc->f.states |= CExtendedFile; avc->f.m.Length = alen; return 0; } #if (defined(AFS_SUN5_ENV)) /* Zero unused portion of last page */ osi_VM_PreTruncate(avc, alen, acred); #endif #if (defined(AFS_SUN5_ENV)) ObtainWriteLock(&avc->vlock, 546); avc->activeV++; /* Block new getpages */ ReleaseWriteLock(&avc->vlock); #endif ReleaseWriteLock(&avc->lock); AFS_GUNLOCK(); /* Flush pages beyond end-of-file. */ osi_VM_Truncate(avc, alen, acred); AFS_GLOCK(); ObtainWriteLock(&avc->lock, 79); avc->f.m.Length = alen; if (alen < avc->f.truncPos) avc->f.truncPos = alen; code = DVHash(&avc->f.fid); /* block out others from screwing with this table */ ObtainWriteLock(&afs_xdcache, 287); dcCount = 0; for (index = afs_dvhashTbl[code]; index != NULLIDX;) { if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) { tdc = afs_GetValidDSlot(index); if (!tdc) { ReleaseWriteLock(&afs_xdcache); code = EIO; goto done; } ReleaseReadLock(&tdc->tlock); if (!FidCmp(&tdc->f.fid, &avc->f.fid)) dcCount++; afs_PutDCache(tdc); } index = afs_dvnextTbl[index]; } /* Now allocate space where we can save those dcache entries, and * do a second pass over them.. Since we're holding xdcache, it * shouldn't be changing. */ tdcArray = osi_Alloc(dcCount * sizeof(struct dcache *)); dcPos = 0; for (index = afs_dvhashTbl[code]; index != NULLIDX; index = afs_dvnextTbl[index]) { if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) { tdc = afs_GetValidDSlot(index); if (!tdc) { /* make sure we put back all of the tdcArray members before * bailing out */ /* remember, the last valid tdc is at dcPos-1, so start at * dcPos-1, not at dcPos itself. */ for (dcPos = dcPos - 1; dcPos >= 0; dcPos--) { tdc = tdcArray[dcPos]; afs_PutDCache(tdc); } code = EIO; goto done; } ReleaseReadLock(&tdc->tlock); if (!FidCmp(&tdc->f.fid, &avc->f.fid)) { /* same file, and modified, we'll store it back */ if (dcPos < dcCount) { tdcArray[dcPos++] = tdc; } else { afs_PutDCache(tdc); } } else { afs_PutDCache(tdc); } } } ReleaseWriteLock(&afs_xdcache); /* Now we loop over the array of dcache entries and truncate them */ for (index = 0; index < dcPos; index++) { struct osi_file *tfile; tdc = tdcArray[index]; newSize = alen - AFS_CHUNKTOBASE(tdc->f.chunk); if (newSize < 0) newSize = 0; ObtainSharedLock(&tdc->lock, 672); if (newSize < tdc->f.chunkBytes && newSize < MAX_AFS_UINT32) { UpgradeSToWLock(&tdc->lock, 673); tdc->f.states |= DWriting; tfile = afs_CFileOpen(&tdc->f.inode); afs_CFileTruncate(tfile, (afs_int32)newSize); afs_CFileClose(tfile); afs_AdjustSize(tdc, (afs_int32)newSize); if (alen < tdc->validPos) { if (alen < AFS_CHUNKTOBASE(tdc->f.chunk)) tdc->validPos = 0; else tdc->validPos = alen; } ConvertWToSLock(&tdc->lock); } ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } code = 0; done: if (tdcArray) { osi_Free(tdcArray, dcCount * sizeof(struct dcache *)); } #if (defined(AFS_SUN5_ENV)) ObtainWriteLock(&avc->vlock, 547); if (--avc->activeV == 0 && (avc->vstates & VRevokeWait)) { avc->vstates &= ~VRevokeWait; afs_osi_Wakeup((char *)&avc->vstates); } ReleaseWriteLock(&avc->vlock); #endif return code; }
/* lp is pointer to a fairly-old buffer */ static struct buffer * afs_newslot(struct dcache *adc, afs_int32 apage, struct buffer *lp) { /* Find a usable buffer slot */ afs_int32 i; afs_int32 lt = 0; struct buffer *tp; struct osi_file *tfile; AFS_STATCNT(afs_newslot); /* we take a pointer here to a buffer which was at the end of an * LRU hash chain. Odds are, it's one of the older buffers, not * one of the newer. Having an older buffer to start with may * permit us to avoid a few of the assignments in the "typical * case" for loop below. */ if (lp && (lp->lockers == 0)) { lt = lp->accesstime; } else { lp = NULL; } /* timecounter might have wrapped, if machine is very very busy * and stays up for a long time. Timecounter mustn't wrap twice * (positive->negative->positive) before calling newslot, but that * would require 2 billion consecutive cache hits... Anyway, the * penalty is only that the cache replacement policy will be * almost MRU for the next ~2 billion DReads... newslot doesn't * get called nearly as often as DRead, so in order to avoid the * performance penalty of using the hypers, it's worth doing the * extra check here every time. It's probably cheaper than doing * hcmp, anyway. There is a little performance hit resulting from * resetting all the access times to 0, but it only happens once * every month or so, and the access times will rapidly sort * themselves back out after just a few more DReads. */ if (timecounter < 0) { timecounter = 1; tp = Buffers; for (i = 0; i < nbuffers; i++, tp++) { tp->accesstime = 0; if (!lp && !tp->lockers) /* one is as good as the rest, I guess */ lp = tp; } } else { /* this is the typical case */ tp = Buffers; for (i = 0; i < nbuffers; i++, tp++) { if (tp->lockers == 0) { if (!lp || tp->accesstime < lt) { lp = tp; lt = tp->accesstime; } } } } if (lp == 0) { /* No unlocked buffers. If still possible, allocate a new increment */ if (nbuffers + NPB > afs_max_buffers) { /* There are no unlocked buffers -- this used to panic, but that * seems extreme. To the best of my knowledge, all the callers * of DRead are prepared to handle a zero return. Some of them * just panic directly, but not all of them. */ afs_warn("afs: all buffers locked\n"); return 0; } BufferData = afs_osi_Alloc(AFS_BUFFER_PAGESIZE * NPB); osi_Assert(BufferData != NULL); for (i = 0; i< NPB; i++) { /* Fill in each buffer with an empty indication. */ tp = &Buffers[i + nbuffers]; tp->fid = NULLIDX; afs_reset_inode(&tp->inode); tp->accesstime = 0; tp->lockers = 0; tp->data = &BufferData[AFS_BUFFER_PAGESIZE * i]; tp->hashIndex = 0; tp->dirty = 0; AFS_RWLOCK_INIT(&tp->lock, "buffer lock"); } lp = &Buffers[nbuffers]; nbuffers += NPB; } if (lp->dirty) { /* see DFlush for rationale for not getting and locking the dcache */ tfile = afs_CFileOpen(&lp->inode); afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, lp->data, AFS_BUFFER_PAGESIZE); lp->dirty = 0; afs_CFileClose(tfile); AFS_STATS(afs_stats_cmperf.bufFlushDirty++); } /* Now fill in the header. */ lp->fid = adc->index; afs_copy_inode(&lp->inode, &adc->f.inode); lp->page = apage; lp->accesstime = timecounter++; FixupBucket(lp); /* move to the right hash bucket */ return lp; }
int DRead(struct dcache *adc, int page, struct DirBuffer *entry) { /* Read a page from the disk. */ struct buffer *tb, *tb2; struct osi_file *tfile; int code; AFS_STATCNT(DRead); memset(entry, 0, sizeof(struct DirBuffer)); ObtainWriteLock(&afs_bufferLock, 256); #define bufmatch(tb) (tb->page == page && tb->fid == adc->index) #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);} /* this apparently-complicated-looking code is simply an example of * a little bit of loop unrolling, and is a standard linked-list * traversal trick. It saves a few assignments at the the expense * of larger code size. This could be simplified by better use of * macros. */ if ((tb = phTable[pHash(adc->index, page)])) { if (bufmatch(tb)) { ObtainWriteLock(&tb->lock, 257); tb->lockers++; ReleaseWriteLock(&afs_bufferLock); tb->accesstime = timecounter++; AFS_STATS(afs_stats_cmperf.bufHits++); ReleaseWriteLock(&tb->lock); entry->buffer = tb; entry->data = tb->data; return 0; } else { struct buffer **bufhead; bufhead = &(phTable[pHash(adc->index, page)]); while ((tb2 = tb->hashNext)) { if (bufmatch(tb2)) { buf_Front(bufhead, tb, tb2); ObtainWriteLock(&tb2->lock, 258); tb2->lockers++; ReleaseWriteLock(&afs_bufferLock); tb2->accesstime = timecounter++; AFS_STATS(afs_stats_cmperf.bufHits++); ReleaseWriteLock(&tb2->lock); entry->buffer = tb2; entry->data = tb2->data; return 0; } if ((tb = tb2->hashNext)) { if (bufmatch(tb)) { buf_Front(bufhead, tb2, tb); ObtainWriteLock(&tb->lock, 259); tb->lockers++; ReleaseWriteLock(&afs_bufferLock); tb->accesstime = timecounter++; AFS_STATS(afs_stats_cmperf.bufHits++); ReleaseWriteLock(&tb->lock); entry->buffer = tb; entry->data = tb->data; return 0; } } else break; } } } else tb2 = NULL; AFS_STATS(afs_stats_cmperf.bufMisses++); /* can't find it */ /* The last thing we looked at was either tb or tb2 (or nothing). That * is at least the oldest buffer on one particular hash chain, so it's * a pretty good place to start looking for the truly oldest buffer. */ tb = afs_newslot(adc, page, (tb ? tb : tb2)); if (!tb) { ReleaseWriteLock(&afs_bufferLock); return EIO; } ObtainWriteLock(&tb->lock, 260); tb->lockers++; ReleaseWriteLock(&afs_bufferLock); if (page * AFS_BUFFER_PAGESIZE >= adc->f.chunkBytes) { tb->fid = NULLIDX; afs_reset_inode(&tb->inode); tb->lockers--; ReleaseWriteLock(&tb->lock); return EIO; } tfile = afs_CFileOpen(&adc->f.inode); code = afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE, tb->data, AFS_BUFFER_PAGESIZE); afs_CFileClose(tfile); if (code < AFS_BUFFER_PAGESIZE) { tb->fid = NULLIDX; afs_reset_inode(&tb->inode); tb->lockers--; ReleaseWriteLock(&tb->lock); return EIO; } /* Note that findslot sets the page field in the buffer equal to * what it is searching for. */ ReleaseWriteLock(&tb->lock); entry->buffer = tb; entry->data = tb->data; return 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; }