📄 tapedrive.c
字号:
/*
This file contributed by the Tape Join project and modified locally.
See attached copyright notice.
*/
/*
$Id: tapedrive.C,v 1.15 1996/12/03 20:24:58 jussi Exp $
$Log: tapedrive.C,v $
Revision 1.15 1996/12/03 20:24:58 jussi
Renamed preprocessor flags.
Revision 1.14 1996/11/23 21:33:35 jussi
Fixed some bugs when compiled with PROCESS_TASK.
Revision 1.13 1996/10/02 15:24:02 wenger
Improved error handling (modified a number of places in the code to use
the DevError class).
Revision 1.12 1996/09/26 18:55:42 jussi
Added support for 64-bit file offsets. Tape commands are now
executed in a subprocess or thread which increases parallelism
in the system.
Revision 1.11 1996/07/18 02:48:24 jussi
Make this code compile in Ultrix.
Revision 1.10 1996/07/12 00:55:39 jussi
Updated copyright information to reflect original source.
Revision 1.9 1996/06/27 16:04:25 jussi
Added a cast in memchr() so that the code compiles cleanly in Linux.
Revision 1.8 1996/04/16 20:56:25 jussi
Replaced assert() calls with DOASSERT macro.
Revision 1.7 1996/01/13 03:22:51 jussi
Removed #include <tar.h> -- this is in tapedrive.h.
Revision 1.6 1995/12/28 17:50:00 jussi
Small fixes to remove new compiler warnings.
Revision 1.5 1995/11/09 22:23:28 jussi
Added debugging statements.
Revision 1.4 1995/10/31 17:13:17 jussi
Added tar archive handling routines and data structures.
Revision 1.3 1995/09/22 15:46:22 jussi
Added copyright message.
Revision 1.2 1995/09/05 20:31:56 jussi
Added CVS header.
*/
/*
Copyright 1993-1996 by Jussi Myllymaki
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name(s) of the copyright holder(s)
not be used in advertising or publicity pertaining to distribution of
the software without specific, written prior permission. The copyright
holder(s) make no representations about the suitability of this
software for any purpose. It is provided "as is" without express
or implied warranty.
Author: Jussi Myllymaki
*/
//#define TAPE_DEBUG
//#define TAPE_DEBUG2
#include <iostream.h>
#include <unistd.h>
#include <string.h>
#include "tapedrive.h"
#include "DCE.h"
#include "Exit.h"
#include "DevError.h"
// Use fake fileno and blkno to make this file compile in Alpha
// until a real fix is found. The problem is that in Alpha/OSF,
// struct mtget (mtio.h) does not include fields mt_fileno and
// mt_blkno. See warning printed out below.
#if defined(__alpha) || defined(__ultrix)
#define mt_fileno mt_resid * 0
#define mt_blkno mt_resid * 0
#endif
#define USE_FWRITEBUF
//#define USE_FREAD
char *TapeDrive::_mt_op_name[] = { "WEOF", "FSF", "BSF", "FSR",
"BSR", "REW", "OFFL", "NOP",
"RETEN", "ERASE", "EOM", "NBSF",
"SRSZ", "GRSZ", "LOAD" };
TapeDrive::TapeDrive(char *name, char *mode, int fno, int blockSz) :
initialized(0), fileNo(fno), blockSize(blockSz),
haveTarHeader(0), tarFileSize(0), tarFileOffset(0)
{
#if defined(__alpha) || defined(__ultrix)
cerr << "Warning: In Ultrix and Alpha/OSF/1, file number and block number"
<< endl;
cerr << " inquiry does not work. TapeDrive will probably fail."
<< endl;
#endif
_child = 0;
if (!(file = fopen(name, mode))) {
reportErrSys("fopen");
return;
}
#ifdef USE_FWRITEBUF
// fwrite() in flushBuffer() will write 8 kB blocks to tape even
// if we request larger blocks; we have to force it to use
// the specified block size
int fwriteBufSize = blockSize + 8;
_fwriteBuf = new char [fwriteBufSize];
DOASSERT(_fwriteBuf, "Out of memory");
if (setvbuf(file, _fwriteBuf, _IOFBF, fwriteBufSize) != 0) {
reportErrSys("setvbuf");
exit(1);
}
#endif
#if 0
setbuf(file, 0);
#endif
atEof = (mode[0] == 'w');
for(unsigned int i = 0; i < _max_mt_op; i++)
mt_tim[i] = mt_ios[i] = mt_cnt[i] = 0;
read_time = read_ios = read_cnt = 0;
write_time = write_ios = write_cnt = 0;
// If the caller gave us the 'target' file number, let's use it.
// If no number was given, use the current file on the tape.
if (fileNo < 0) {
getStatus();
fileNo = tstat.mt_fileno;
}
gotoBeginningOfFile();
buffer = new char [blockSize];
DOASSERT(buffer, "Out of memory");
bufferType = readBuffer;
bufferBlock = 0;
bufferOffset = 0;
bufferBytes = 0;
initialized = 1;
}
TapeDrive::~TapeDrive()
{
if (!initialized)
return;
if (bufferType == writeBuffer)
flushBuffer();
delete buffer;
waitForChildProcess();
setbuf(file, 0);
if (fclose(file))
reportErrSys("fclose");
#ifdef USE_FWRITEBUF
delete _fwriteBuf;
#endif
}
void TapeDrive::printStats()
{
cout << "Tape usage statistics:" << endl;
cout << " cmd\tcalls\tcount\tavgtime" << endl;
for(unsigned int i = 0; i < _max_mt_op; i++) {
if (mt_ios[i] > 0) {
cout << " " << _mt_op_name[i]
<< "\t" << mt_ios[i]
<< "\t" << mt_cnt[i]
<< "\t" << mt_tim[i] / (mt_cnt[i] ? mt_cnt[i] : 1) << endl;
}
}
cout << " read\t" << read_ios << "\t" << read_cnt
<< "\t" << read_time / (read_ios ? read_ios : 1) << endl;
cout << " write\t" << write_ios << "\t" << write_cnt
<< "\t" << write_time / (write_ios ? write_ios : 1) << endl;
}
void TapeDrive::readTarHeader()
{
DOASSERT(!haveTarHeader, "Invalid tar header flag");
int bytes = read(&tarHeader, sizeof tarHeader);
DOASSERT(bytes == sizeof tarHeader, "Invalid tar header size");
tarFileSize = oct2int(tarHeader.dbuf.size);
tarFileOffset = 0;
TAPEDBG(cout << "Read tar header: " << tarHeader.dbuf.name << ", size "
<< tarFileSize << endl);
haveTarHeader = 1;
}
unsigned long int TapeDrive::oct2int(char *buf)
{
unsigned long int num = 0;
while(*buf == ' ') buf++;
while(*buf != ' ')
num = 8 * num + (*buf++ - '0');
return num;
}
void TapeDrive::waitForChildProcess()
{
if (_child > 0) {
TAPEDBG(cout << "Waiting for tape " << fileno(file)
<< " to become idle" << endl);
#ifdef TAPE_THREAD
(void)pthread_join(_child, 0);
#else
while(1) {
int status;
pid_t child = wait(&status);
if (child < 0) {
if (errno == EINTR)
continue;
if (errno != ECHILD) {
reportErrSys("wait");
exit(1);
}
} else
break;
}
#endif
TAPEDBG(cout << "Tape " << fileno(file) << " has become idle" << endl);
_child = 0;
}
}
long long TapeDrive::seek(long long offset)
{
TAPEDBG(cout << "Seek to offset " << offset << " of tape "
<< fileno(file) << endl);
DOASSERT(offset >= 0, "Invalid tape offset");
if (bufferType == writeBuffer) { // flush out write buffer
flushBuffer();
bufferType = readBuffer;
bufferBlock = 0;
}
long long lblock = offset / blockSize;
long long loff = offset % blockSize;
long block = lblock;
long off = loff;
TAPEDBG(cout << "Seeking to lblock " << lblock << ", block "
<< block << " of tape " << fileno(file) << endl);
if (block != bufferBlock - 1) { // not current block?
gotoBlock(block); // goto new block location
fillBuffer(); // read it into buffer
}
bufferBlock = block + 1; // on tape, just past current block
bufferOffset = off;
if (bufferOffset > bufferBytes) {
cerr << "Seeking past end of file" << endl;
exit(1);
}
return offset;
}
int TapeDrive::read(void *buf, int recSize, int binary)
{
TAPEDBG2(cout << "Read request for " << recSize << " "
<< (binary ? "binary" : "ASCII")
<< " bytes to " << (void *)buf << endl);
if (bufferType != readBuffer) {
cerr << "Must do a seek before switching from writing to reading" << endl;
exit(1);
}
DOASSERT(bufferOffset >= 0 && bufferOffset <= blockSize,
"Inconsistent data");
DOASSERT(bufferBytes >= 0 && bufferBytes <= blockSize, "Inconsistent data");
DOASSERT(bufferOffset <= bufferBytes, "Inconsistent data");
#ifdef TARFILESIZE
if (haveTarHeader // is file in a tar archive?
&& tarFileOffset >= tarFileSize) // and at end of file?
atEof = 1;
#endif
if (atEof) // already at end of tape file?
return 0;
if (bufferOffset >= bufferBytes) { // no more bytes in buffer?
fillBuffer(); // get next block from file
if (!bufferBytes) { // end of file?
#ifdef TARFILESIZE
// for non-tar files, end of tape file is the natural end of
// user file; for tar files, the file size indicated in the
// tar header should trigger the atEof statement a few lines
// up; it is an error if the tape file (tar file) ends before
// the file inside the tar file
if (haveTarHeader)
cerr << "File in tar archive prematurely terminated." << endl;
#endif
return 0;
}
}
read_cnt++;
if (read_cnt % 1000 == 0)
TAPEDBG2(cout << read_cnt << " " << flush);
#ifdef TAPE_BLOCK_PADDING
char *start = buffer + bufferOffset; // starting point for this record
DOASSERT(*start != 0, "Unexpected record"); // must not be an empty record
if (recSize > bufferBytes - bufferOffset)
recSize = bufferBytes - bufferOffset;
#ifdef TARFILESIZE
if (haveTarHeader // past EOF of file in tar archive?
&& recSize > tarFileSize - tarFileOffset)
recSize = tarFileSize - tarFileOffset;
#endif
if (!binary) { // reading an ASCII record?
char *end = (char *)memchr(start, 0, recSize);
DOASSERT(end, "End of record not found");
recSize = end - start; // do not include record separator
}
TAPEDBG2(cout << "Copying " << recSize << " bytes to "
<< (void *)buf << endl);
memcpy(buf, start, recSize);
bufferOffset += recSize + 1; // go past record separator too
if (!binary // in ASCII mode?
&& bufferOffset < bufferBytes // still data left but...
&& !buffer[bufferOffset])) // last record in block?
bufferOffset = bufferBytes; // must fetch new block next time
#else
int bytesLeft = recSize;
#ifdef TARFILESIZE
if (haveTarHeader // past EOF of file in tar archive?
&& bytesLeft > tarFileSize - tarFileOffset)
bytesLeft = tarFileSize - tarFileOffset;
#endif
recSize = 0;
char *p = (char *)buf;
while(bytesLeft > 0) {
char *start = buffer + bufferOffset;
int b = bufferBytes - bufferOffset; // bytes left in buffer
if (bytesLeft < b) // caller doesn't want that many?
b = bytesLeft;
char *end = 0;
if (!binary) { // reading an ASCII record?
end = (char *)memchr((void *)start, '\n', b);
if (end) // found newline = end of record?
b = end - start + 1;
}
TAPEDBG2(cout << "Copying " << b << " bytes to " << (void *)p << endl);
memcpy(p, start, b);
bufferOffset += b;
bytesLeft -= b;
recSize += b;
p += b;
if (end) // found newline = end of record?
break;
if (bufferOffset >= bufferBytes) // need next block?
fillBuffer();
if (!bufferBytes) // end of physical file?
break;
}
if (!binary // in ASCII mode?
&& bufferOffset < bufferBytes // still data left but...
&& !buffer[bufferOffset]) // end of logical file (NULL char)?
bufferOffset = bufferBytes; // must try to fetch block next time
#endif
#ifdef TARFILESIZE
if (haveTarHeader) {
tarFileOffset += recSize;
DOASSERT(tarFileOffset <= tarFileSize, "Inconsistent data");
}
#endif
return recSize;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -