/* * Read from a logical tape. * * Early EOF is indicated by return value less than #bytes requested. */ size_t LogicalTapeRead(LogicalTapeSet *lts, LogicalTape *lt, void *ptr, size_t size) { size_t nread = 0; size_t nthistime; Assert(!lt->writing); if(lt->currPos.blkNum == -1) return nread; while (size > 0) { Assert(lt->currPos.offset <= lt->currBlk.payload_tail); if(lt->currPos.offset == lt->currBlk.payload_tail) { if(lt->currBlk.next_blk == -1) { if(!lt->frozen) { ltsReleaseBlock(lts, lt->currPos.blkNum); lt->firstBlkNum = -1L; lt->currPos.blkNum = -1L; lt->currPos.offset = 0; } return nread; } lt->currPos.blkNum = lt->currBlk.next_blk; lt->currPos.offset = 0; ltsReadBlock(lts, lt->currBlk.next_blk, <->currBlk); if(!lt->frozen) { ltsReleaseBlock(lts, lt->currBlk.prev_blk); lt->firstBlkNum = lt->currPos.blkNum; } } if(lt->currPos.offset < lt->currBlk.payload_tail) { nthistime = size > (lt->currBlk.payload_tail - lt->currPos.offset) ? lt->currBlk.payload_tail - lt->currPos.offset : size; memcpy(ptr, lt->currBlk.payload + lt->currPos.offset, nthistime); size -= nthistime; ptr = (void *) ((char *) ptr + nthistime); lt->currPos.offset += nthistime; nread += nthistime; } } return nread; }
/* * Obtain next data block number in the forward direction, or -1L if no more. * * Unless 'frozen' is true, release indirect blocks to the free pool after * reading them. */ static long ltsRecallNextBlockNum(LogicalTapeSet *lts, IndirectBlock *indirect, bool frozen) { /* Handle case of never-written-to tape */ if (indirect == NULL) return -1L; if (indirect->nextSlot >= BLOCKS_PER_INDIR_BLOCK || indirect->ptrs[indirect->nextSlot] == -1L) { long indirblock; if (indirect->nextup == NULL) return -1L; /* nothing left at this level */ indirblock = ltsRecallNextBlockNum(lts, indirect->nextup, frozen); if (indirblock == -1L) return -1L; /* nothing left at this level */ ltsReadBlock(lts, indirblock, (void *) indirect->ptrs); if (!frozen) ltsReleaseBlock(lts, indirblock); indirect->nextSlot = 0; } if (indirect->ptrs[indirect->nextSlot] == -1L) return -1L; return indirect->ptrs[indirect->nextSlot++]; }
/* * Read from a logical tape. * * Early EOF is indicated by return value less than #bytes requested. */ size_t LogicalTapeRead(LogicalTapeSet *lts, int tapenum, void *ptr, size_t size) { LogicalTape *lt; size_t nread = 0; size_t nthistime; Assert(tapenum >= 0 && tapenum < lts->nTapes); lt = <s->tapes[tapenum]; Assert(!lt->writing); while (size > 0) { if (lt->pos >= lt->nbytes) { /* Try to load more data into buffer. */ long datablocknum = ltsRecallNextBlockNum(lts, lt->indirect, lt->frozen); if (datablocknum == -1L) break; /* EOF */ lt->curBlockNumber++; lt->pos = 0; ltsReadBlock(lts, datablocknum, (void *) lt->buffer); if (!lt->frozen) ltsReleaseBlock(lts, datablocknum); lt->nbytes = (lt->curBlockNumber < lt->numFullBlocks) ? BLCKSZ : lt->lastBlockBytes; if (lt->nbytes <= 0) break; /* EOF (possible here?) */ } nthistime = lt->nbytes - lt->pos; if (nthistime > size) nthistime = size; Assert(nthistime > 0); memcpy(ptr, lt->buffer + lt->pos, nthistime); lt->pos += nthistime; ptr = (void *) ((char *) ptr + nthistime); size -= nthistime; nread += nthistime; } return nread; }
/* * Reset a logical tape's indirect-block hierarchy after a write pass * to prepare for reading. We dump out partly-filled blocks except * at the top of the hierarchy, and we rewind each level to the start. * This call returns the first data block number, or -1L if the tape * is empty. * * Unless 'freezing' is true, release indirect blocks to the free pool after * reading them. */ static long ltsRewindIndirectBlock(LogicalTapeSet *lts, IndirectBlock *indirect, bool freezing) { /* Handle case of never-written-to tape */ if (indirect == NULL) return -1L; /* Insert sentinel if block is not full */ if (indirect->nextSlot < BLOCKS_PER_INDIR_BLOCK) indirect->ptrs[indirect->nextSlot] = -1L; /* * If block is not topmost, write it out, and recurse to obtain address of * first block in this hierarchy level. Read that one in. */ if (indirect->nextup != NULL) { long indirblock = ltsGetFreeBlock(lts); ltsWriteBlock(lts, indirblock, (void *) indirect->ptrs); ltsRecordBlockNum(lts, indirect->nextup, indirblock); indirblock = ltsRewindIndirectBlock(lts, indirect->nextup, freezing); Assert(indirblock != -1L); ltsReadBlock(lts, indirblock, (void *) indirect->ptrs); if (!freezing) ltsReleaseBlock(lts, indirblock); } /* * Reset my next-block pointer, and then fetch a block number if any. */ indirect->nextSlot = 0; if (indirect->ptrs[0] == -1L) return -1L; return indirect->ptrs[indirect->nextSlot++]; }
/* * Read as many blocks as we can into the per-tape buffer. * * Returns true if anything was read, 'false' on EOF. */ static bool ltsReadFillBuffer(LogicalTapeSet *lts, LogicalTape *lt) { lt->pos = 0; lt->nbytes = 0; do { char *thisbuf = lt->buffer + lt->nbytes; long datablocknum = lt->nextBlockNumber; /* Fetch next block number */ if (datablocknum == -1L) break; /* EOF */ /* Apply worker offset, needed for leader tapesets */ datablocknum += lt->offsetBlockNumber; /* Read the block */ ltsReadBlock(lts, datablocknum, (void *) thisbuf); if (!lt->frozen) ltsReleaseBlock(lts, datablocknum); lt->curBlockNumber = lt->nextBlockNumber; lt->nbytes += TapeBlockGetNBytes(thisbuf); if (TapeBlockIsLast(thisbuf)) { lt->nextBlockNumber = -1L; /* EOF */ break; } else lt->nextBlockNumber = TapeBlockGetTrailer(thisbuf)->next; /* Advance to next block, if we have buffer space left */ } while (lt->buffer_size - lt->nbytes > BLCKSZ); return (lt->nbytes > 0); }
/* * Rewind logical tape and switch from writing to reading or vice versa. * * Unless the tape has been "frozen" in read state, forWrite must be the * opposite of the previous tape state. */ void LogicalTapeRewind(LogicalTapeSet *lts, int tapenum, bool forWrite) { LogicalTape *lt; long datablocknum; Assert(tapenum >= 0 && tapenum < lts->nTapes); lt = <s->tapes[tapenum]; if (!forWrite) { if (lt->writing) { /* * Completion of a write phase. Flush last partial data block, * flush any partial indirect blocks, rewind for normal * (destructive) read. */ if (lt->dirty) ltsDumpBuffer(lts, lt); lt->lastBlockBytes = lt->nbytes; lt->writing = false; datablocknum = ltsRewindIndirectBlock(lts, lt->indirect, false); } else { /* * This is only OK if tape is frozen; we rewind for (another) read * pass. */ Assert(lt->frozen); datablocknum = ltsRewindFrozenIndirectBlock(lts, lt->indirect); } /* Read the first block, or reset if tape is empty */ lt->curBlockNumber = 0L; lt->pos = 0; lt->nbytes = 0; if (datablocknum != -1L) { ltsReadBlock(lts, datablocknum, (void *) lt->buffer); if (!lt->frozen) ltsReleaseBlock(lts, datablocknum); lt->nbytes = (lt->curBlockNumber < lt->numFullBlocks) ? BLCKSZ : lt->lastBlockBytes; } } else { /* * Completion of a read phase. Rewind and prepare for write. * * NOTE: we assume the caller has read the tape to the end; otherwise * untouched data and indirect blocks will not have been freed. We * could add more code to free any unread blocks, but in current usage * of this module it'd be useless code. */ IndirectBlock *ib, *nextib; Assert(!lt->writing && !lt->frozen); /* Must truncate the indirect-block hierarchy down to one level. */ if (lt->indirect) { for (ib = lt->indirect->nextup; ib != NULL; ib = nextib) { nextib = ib->nextup; pfree(ib); } lt->indirect->nextSlot = 0; lt->indirect->nextup = NULL; } lt->writing = true; lt->dirty = false; lt->numFullBlocks = 0L; lt->lastBlockBytes = 0; lt->curBlockNumber = 0L; lt->pos = 0; lt->nbytes = 0; } }