📄 tapedrive.c
字号:
int TapeDrive::write(void *buf, int recSize)
{
buf = buf; // make compiler happy
recSize = recSize;
write_cnt++;
DOASSERT(0, "Random writes not supported on tapes");
return 0;
}
int TapeDrive::append(void *buf, int recSize)
{
write_cnt++;
if (!atEof) {
gotoEndOfFile();
atEof = 1;
bufferBlock = tstat.mt_blkno;
}
if (bufferType != writeBuffer) {
// can simply discard read buffer
bufferOffset = 0;
bufferType = writeBuffer;
}
#ifdef TAPE_BLOCK_PADDING
if (recSize > blockSize - 1)
recSize = blockSize - 1;
if (bufferOffset + recSize + 1 > blockSize)
flushBuffer();
DOASSERT(bufferOffset + recSize + 1 <= blockSize, "Inconsistent data");
memcpy(buffer + bufferOffset, buf, recSize);
*(buffer + bufferOffset + recSize) = '\0';
bufferOffset += recSize + 1;
#else
int bytes = recSize;
char *p = (char *)buf;
while(bytes > 0) {
int spaceLeft = blockSize - bufferOffset;
int b = (bytes <= spaceLeft ? bytes : spaceLeft);
DOASSERT(bufferOffset + b <= blockSize, "Inconsistent data");
memcpy(buffer + bufferOffset, p, b);
bufferOffset += b;
bytes -= b;
p += b;
if (bufferOffset >= blockSize)
flushBuffer();
}
#endif
return recSize;
}
void TapeDrive::Recover(struct mtget &otstat, short mt_op,
daddr_t mt_count)
{
mt_count = mt_count; // make compiler happy
otstat = otstat;
int status = 0;
switch(mt_op) {
case MTFSF:
status = ProcessCmdNR(MTBSF, 1);
if (status >= 0)
ProcessCmdNR(MTFSF, 1);
break;
case MTBSF:
status = ProcessCmdNR(MTFSF, 1);
if (status >= 0)
ProcessCmdNR(MTBSF, 1);
break;
case MTFSR:
status = ProcessCmdNR(MTBSR, 1);
if (status >= 0)
ProcessCmdNR(MTFSR, 1);
break;
case MTBSR:
status = ProcessCmdNR(MTFSR, 1);
if (status >= 0)
ProcessCmdNR(MTBSR, 1);
break;
case MTREW:
status = ProcessCmdNR(MTFSF, 1);
break;
default:
cout << "Don't know how to recover from an error with op "
<< mt_op << endl;
break;
}
DOASSERT(status >= 0, "Recovery attempt failed");
}
#ifdef TAPE_THREAD
void *TapeDrive::ProcessCmd(void *arg)
{
TapeDrive &me = *(TapeDrive *)arg;
return me.ProcessCmd(me._proc_mt_op, me._proc_mt_count);
}
#endif
void *TapeDrive::ProcessCmd(short mt_op, daddr_t mt_count)
{
static struct mtget otstat; // original tape status
int status = ioctl(fileno(file), MTIOCGET, (char *)&otstat);
if (status < 0)
reportErrSys("ioctl4");
DOASSERT(status >= 0, "Cannot get tape status");
for(int attempt = 0; attempt < 10; attempt++) {
if (attempt > 0) {
cout << "Sleeping 2 seconds..." << endl;
sleep(2);
cout << "Recovering..." << endl;
Recover(otstat, mt_op, mt_count);
cout << "Retrying..." << endl;
}
status = ProcessCmdNR(mt_op, mt_count);
if (status >= 0)
break;
cout << "Tape command " << mt_op << ", count " << mt_count
<< " failed, attempt " << attempt << endl;
}
return (void *)0;
}
int TapeDrive::ProcessCmdNR(short mt_op, daddr_t mt_count)
{
static struct mtop cmd;
cmd.mt_op = mt_op;
cmd.mt_count = mt_count;
DOASSERT(mt_op >= 0 && mt_op < _max_mt_op, "Invalid tape command");
mt_ios[mt_op]++;
mt_cnt[mt_op] += (mt_count >= 0 ? mt_count : -mt_count);
TAPEDBG(cout << "Tape " << fileno(file) << ", command " << mt_op
<< ", count " << mt_count << " started" << endl);
startTimer();
int status = ioctl(fileno(file), MTIOCTOP, (char *)&cmd);
mt_tim[mt_op] += getTimer();
if (status < 0)
reportErrSys("ioctl");
TAPEDBG(cout << "Tape " << fileno(file) << ", command " << mt_op
<< ", count " << mt_count << " finished, status = " << status
<< endl);
return status;
}
int TapeDrive::command(short mt_op, daddr_t mt_count)
{
waitForChildProcess();
DOASSERT(_child <= 0, "Invalid child process ID");
#ifdef TAPE_THREAD
_proc_mt_op = mt_op;
_proc_mt_count = mt_count;
if (pthread_create(&_child, 0, ProcessCmd, this)) {
reportErrSys("pthread_create");
return -1;
}
#else
// There seems to be a problem forking the tape command. Do it
// in same process for now.
_child = fork();
if (!_child) {
(void)ProcessCmd(mt_op, mt_count);
exit(1);
}
if (_child < 0) {
reportErrSys("fork");
return -1;
}
#endif
return 0;
}
void TapeDrive::getStatus()
{
waitForChildProcess();
#if defined(__aix) || defined(_AIX)
tstat.mt_resid = 0;
tstat.mt_fileno = 0;
tstat.mt_blkno = 0;
#else
int status = ioctl(fileno(file), MTIOCGET, (char *)&tstat);
if (status < 0)
reportErrSys("ioctl2");
DOASSERT(status >= 0, "Cannot get tape status");
#endif
}
void TapeDrive::fillBuffer()
{
waitForChildProcess();
read_ios++;
TAPEDBG2(cout << "Reading " << blockSize << " bytes to " << (void *)buffer
<< " from fd " << fileno(file) << endl);
TAPEDBG2(cout << "Bufferblock " << bufferBlock << ", bufferOffset "
<< bufferOffset << endl);
#if 0
startTimer();
#endif
#ifdef USE_FREAD
size_t status;
#else
int status;
#endif
while (1) {
#ifdef USE_FREAD
status = fread(buffer, blockSize, 1, file);
if (!status && ferror(file) && errno == EINTR)
continue;
#else
status = ::read(fileno(file), buffer, blockSize);
if (status < 0 && errno == EINTR)
continue;
#endif
break;
}
#if 0
read_time += getTimer();
#endif
bufferBlock++;
bufferOffset = 0;
bufferBytes = status;
#ifdef USE_FREAD
if (!status && feof(file)) { // end of tape file?
#else
if (!status) { // end of tape file?
#endif
atEof = 1;
TAPEDBG(cout << "Backing up past file mark we just passed" << endl);
int status = command(MTBSF, 1);
DOASSERT(status >= 0, "Cannot operate tape drive");
return;
}
atEof = 0;
#ifdef USE_FREAD
if (!status && ferror(file)) { // read error?
#else
if (status < 0) { // read error?
#endif
cerr << "Read failed: fd " << fileno(file) << ", buffer "
<< (void *)buffer << ", bytes " << blockSize << endl;
reportErrSys("read");
exit(1);
}
#ifdef PARTIAL_BLOCK_ERROR
if (status < blockSize) { // partial block read?
cerr << "Partial block read: " << status << " vs. " << blockSize << endl;
exit(1);
}
#endif
}
void TapeDrive::flushBuffer()
{
waitForChildProcess();
DOASSERT(bufferType == writeBuffer, "Inconsistent data");
if (!bufferOffset)
return;
write_ios++;
if (bufferOffset < blockSize)
memset(buffer + bufferOffset, 0, blockSize - bufferOffset);
#if 0
startTimer();
#endif
while (1) {
size_t status = fwrite(buffer, blockSize, 1, file);
if (!status && ferror(file) && errno == EINTR)
continue;
if (status < 1) {
reportErrSys("fwrite");
exit(1);
}
break;
}
#if 0
write_time += getTimer();
#endif
bufferBlock++;
bufferOffset = 0;
}
void TapeDrive::gotoBlock(long block)
{
TAPEDBG(cout << "Go to block " << block << " of tape "
<< fileno(file) << endl);
getStatus();
if (fileNo != tstat.mt_fileno // oops, we're in another file
|| tstat.mt_blkno > 900000000) { // unsure about location
gotoBeginningOfFile();
getStatus();
}
long diff = block - tstat.mt_blkno; // difference in block numbers
if (!diff) // no movement required?
return;
if (!block) { // do we just want to go to BOF?
gotoBeginningOfFile();
return;
}
int status = 0;
if (diff > 0)
status = command(MTFSR, diff); // go forward
else
status = command(MTBSR, -diff); // go backward
DOASSERT(status >= 0, "Cannot operate tape drive");
}
void TapeDrive::gotoBeginningOfFile()
{
TAPEDBG(cout << "Going to beginning of file" << endl);
int status = fseek(file, 0, SEEK_SET);
if (status < 0)
reportErrSys("fseek");
DOASSERT(status >= 0, "Cannot operate tape drive");
if (!fileNo) { // first file? just rewind
int status = command(MTREW, 1);
DOASSERT(status >= 0, "Cannot operate tape drive");
#if !defined(__alpha) && !defined(__ultrix)
tstat.mt_fileno = 0;
tstat.mt_blkno = 0;
tstat.mt_resid = 0;
#endif
return;
}
getStatus();
long diff = fileNo - tstat.mt_fileno;
if (diff > 0) { // go forward?
int status = command(MTFSF, diff);
DOASSERT(status >= 0, "Cannot operate tape drive");
} else { // else go backward
int status = command(MTBSF, -diff + 1);
DOASSERT(status >= 0, "Cannot operate tape drive");
status = command(MTFSF, 1);
DOASSERT(status >= 0, "Cannot operate tape drive");
}
#if !defined(__alpha) && !defined(__ultrix)
tstat.mt_fileno = fileNo;
tstat.mt_blkno = 0;
tstat.mt_resid = 0;
#endif
}
void TapeDrive::gotoEndOfFile()
{
if (atEof)
return;
waitForChildProcess();
const unsigned long skipSize = 1000000;
static struct mtop cmd;
cmd.mt_op = MTFSR;
cmd.mt_count = skipSize;
while(1) {
TAPEDBG(cout << "Tape command " << MTFSR << ", count " << skipSize
<< ", " << flush);
int status = ioctl(fileno(file), MTIOCTOP, (char *)&cmd);
TAPEDBG(cout << "status = " << status << endl);
if (status < 0) {
getStatus();
if (tstat.mt_resid > 0) {
TAPEDBG(cout << "At end of file, drive status " << tstat.mt_dsreg
<< ", error status " << tstat.mt_erreg << endl);
mt_ios[MTFSR] -= tstat.mt_resid;
return;
}
}
reportErrSys("ioctl3");
DOASSERT(status >= 0, "Cannot operate tape drive");
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -