summaryrefslogtreecommitdiffstats
path: root/kdat/TapeDrive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdat/TapeDrive.cpp')
-rw-r--r--kdat/TapeDrive.cpp642
1 files changed, 642 insertions, 0 deletions
diff --git a/kdat/TapeDrive.cpp b/kdat/TapeDrive.cpp
new file mode 100644
index 0000000..64cb46f
--- /dev/null
+++ b/kdat/TapeDrive.cpp
@@ -0,0 +1,642 @@
+// KDat - a tar-based DAT archiver
+// Copyright (C) 1998-2000 Sean Vyain, svyain@mail.tds.net
+// Copyright (C) 2001-2002 Lawrence Widman, kdat@cardiothink.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mtio.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <qfile.h>
+
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+#include "KDatMainWindow.h"
+#include "Options.h"
+#include "Tape.h"
+#include "TapeManager.h"
+#include "TapeDrive.h"
+#include "kdat.h"
+
+#include "TapeDrive.moc"
+
+TapeDrive* TapeDrive::_instance = 0;
+
+TapeDrive* TapeDrive::instance()
+{
+ if ( !_instance ) {
+ _instance = new TapeDrive();
+ }
+
+ return _instance;
+}
+
+TapeDrive::TapeDrive()
+ : _fd ( -1 ),
+ _readOnly( TRUE ),
+ _tape( 0 ),
+ _writeBuf( 0 ),
+ _readBuf( 0 ),
+ _writeBufIdx( 0 ),
+ _readBufIdx( Options::instance()->getTapeBlockSize() )
+{
+ setBlockSize( Options::instance()->getTapeBlockSize() );
+ _writeBuf = new char[ Options::instance()->getTapeBlockSize() ];
+ _readBuf = new char[ Options::instance()->getTapeBlockSize() ];
+}
+
+TapeDrive::~TapeDrive()
+{
+ if ( Options::instance()->getLockOnMount() ) {
+ unlock();
+ }
+
+ delete [] _writeBuf;
+ delete [] _readBuf;
+}
+
+void TapeDrive::flush()
+{
+ _readBufIdx = Options::instance()->getTapeBlockSize();
+
+ if ( _writeBufIdx <= 0 ) {
+ return;
+ }
+
+ memset( _writeBuf + _writeBufIdx, 0, Options::instance()->getTapeBlockSize() - _writeBufIdx );
+ int ret = ::write( _fd, _writeBuf, Options::instance()->getTapeBlockSize() );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::flush() -- write failed!\n" );
+ }
+
+ _writeBufIdx = 0;
+}
+
+bool TapeDrive::load()
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+#ifdef MTLOAD
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTLOAD;
+ tapeOp.mt_count = 1;
+
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::lock() -- ioctl( MTLOAD ) failed!\n" );
+ }
+
+ return ret >= 0;
+#else
+ return TRUE;
+#endif
+}
+
+bool TapeDrive::lock()
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+#ifdef MTLOCK
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTLOCK;
+ tapeOp.mt_count = 1;
+
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::lock() -- ioctl( MTLOCK ) failed!\n" );
+ }
+
+ return ret >= 0;
+#else
+ return TRUE;
+#endif
+}
+
+bool TapeDrive::unlock()
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+#ifdef MTUNLOCK
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTUNLOCK;
+ tapeOp.mt_count = 1;
+
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::unlock() -- ioctl( MTUNLOCK ) failed!\n" );
+ }
+
+ return ret >= 0;
+#else
+ return TRUE;
+#endif
+}
+
+bool TapeDrive::isReadOnly()
+{
+ return _readOnly;
+}
+
+bool TapeDrive::isTapePresent()
+{
+ open();
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ // Get tape status.
+ struct mtget tapeStatus;
+ int ret = ioctl( _fd, MTIOCGET, &tapeStatus );
+ if ( ret < 0 ) {
+ return FALSE;
+ }
+
+ // Check for the presence of a tape.
+// if ( !GMT_DR_OPEN( tapeStatus.mt_gstat ) ) {
+ if ( GMT_ONLINE( tapeStatus.mt_gstat ) ) {
+ // Lock the tape drive door.
+ //struct mtop tapeOp;
+ //tapeOp.mt_op = MTLOCK;
+ //tapeOp.mt_count = 1;
+ //int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ //if ( ret < 0 ) {
+ // printf( "TapeDrive::isTapePresent() -- ioctl( MTLOCK ) failed!\n" );
+ //}
+
+ if ( _readOnly ) {
+ emit sigStatus( i18n( "Tape mounted readonly." ) );
+ } else {
+ emit sigStatus( i18n( "Tape mounted read/write." ) );
+ }
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void TapeDrive::eject()
+{
+ if ( _fd < 0 ) {
+ return;
+ }
+
+ flush();
+
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTOFFL;
+ tapeOp.mt_count = 1;
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::ejectTape() -- ioctl( MTOFFL ) failed!\n" );
+ }
+}
+
+Tape* TapeDrive::readHeader()
+{
+ _tape = NULL;
+
+ // Rewind tape.
+ emit sigStatus( "Rewinding tape..." );
+ if ( !rewind() ) {
+ KMessageBox::error( KDatMainWindow::getInstance(), i18n( "Rewinding tape failed." ));
+ return NULL;
+ }
+
+ // KDat magic string.
+ emit sigStatus( i18n( "Reading magic string..." ) );
+ char magic[9];
+ if ( read( magic, 9 ) < 9 ) {
+ KMessageBox::error( KDatMainWindow::getInstance(), i18n( "Reading magic string failed." ));
+ return NULL;
+ }
+ if ( strncmp( "KDatMAGIC", magic, 9 ) ) {
+ // Bad magic.
+ return NULL;
+ }
+
+ // Read version number.
+ emit sigStatus( i18n( "Reading version number..." ) );
+ int version;
+ if ( read( (char*)&version, 4 ) < 4 ) {
+ KMessageBox::error( KDatMainWindow::getInstance(), i18n( "Reading version number failed." ));
+ return NULL;
+ }
+
+ if ( version > KDAT_TAPE_HEADER_VERSION ) {
+ KMessageBox::information( KDatMainWindow::getInstance(), i18n( "Tape was formatted by a more recent version of KDat. Consider upgrading." ));
+ }
+
+ // Read tape ID.
+ emit sigStatus( i18n( "Reading tape ID..." ) );
+ int len;
+ if ( read( (char*)&len, 4 ) < 4 ) {
+ KMessageBox::error( KDatMainWindow::getInstance(), i18n( "Reading tape ID length failed." ));
+ return NULL;
+ }
+ char* tapeID = new char[len];
+ if ( read( tapeID, len ) < len ) {
+ KMessageBox::error( KDatMainWindow::getInstance(), i18n( "Reading tape ID failed." ));
+ delete [] tapeID;
+ return NULL;
+ }
+
+ _tape = TapeManager::instance()->findTape( tapeID );
+ delete [] tapeID;
+ return _tape;
+}
+
+bool TapeDrive::rewind()
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ flush();
+
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTREW;
+ tapeOp.mt_count = 1;
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::rewind() -- ioctl( MTREW ) failed!\n" );
+ }
+
+ return ret >= 0;
+}
+
+int TapeDrive::getFile()
+{
+ if ( _fd < 0 ) {
+ return -1;
+ }
+
+ struct mtget tapePos;
+
+ if ( ioctl( _fd, MTIOCGET, &tapePos ) < 0 ) {
+ printf( "TapeDrive::getFile() -- ioctl( MTIOCGET ) failed!\n" );
+ return -1;
+ }
+
+ return tapePos.mt_fileno;
+}
+
+int TapeDrive::getBlock()
+{
+ if ( _fd < 0 ) {
+ return -1;
+ }
+
+ struct mtget tapePos;
+
+ if ( ioctl( _fd, MTIOCGET, &tapePos ) < 0 ) {
+ printf( "TapeDrive::getBlock() -- ioctl( MTIOCGET ) failed!\n" );
+ return -1;
+ }
+
+ //%%% I don't think this makes sense anymore because the tape buffer size == the tape block size.
+ // Need to subtract off the blocks that the application has not "read" yet.
+ //int unread = ( Options::instance()->getTapeBlockSize() - _readBufIdx ) / Options::instance()->getTapeBlockSize();
+
+ //return tapePos.mt_blkno - unread;
+
+ return tapePos.mt_blkno;
+}
+
+bool TapeDrive::nextFile( int count )
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ flush();
+
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTFSF;
+ tapeOp.mt_count = count;
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::nextFile() -- ioctl( MTFSF ) failed!\n" );
+ }
+ return ret >= 0;
+}
+
+bool TapeDrive::prevFile( int count )
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ flush();
+
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTBSF;
+ tapeOp.mt_count = count;
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::prevFile() -- ioctl( MTBSF ) failed!\n" );
+ }
+ return ret >= 0;
+}
+
+bool TapeDrive::nextRecord( int count )
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ flush();
+
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTFSR;
+ tapeOp.mt_count = count;
+
+ bool status = ( ioctl( _fd, MTIOCTOP, &tapeOp ) >= 0 );
+
+ if ( !status ) {
+ // Try reading count * TapeBlockSize bytes.
+ char *buf = new char[Options::instance()->getTapeBlockSize()];
+ int bytes = count * Options::instance()->getTapeBlockSize();
+ int ret;
+ while ( bytes > 0 ) {
+ ret = read( buf, bytes > Options::instance()->getTapeBlockSize() ? Options::instance()->getTapeBlockSize() : bytes );
+ if ( ret <= 0 ) {
+ status = FALSE;
+ break;
+ }
+ bytes -= ret;
+ }
+ delete [] buf;
+ status = TRUE;
+ }
+ return status;
+}
+
+bool TapeDrive::prevRecord( int count )
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ flush();
+
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTBSR;
+ tapeOp.mt_count = count;
+ int ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::prevRecord() -- ioctl( MTBSR ) failed!\n" );
+ }
+ return ret >= 0;
+}
+
+void TapeDrive::close()
+{
+ if ( _fd < 0 ) {
+ return;
+ }
+
+ flush();
+
+ ::close( _fd );
+}
+
+void TapeDrive::open()
+{
+ close();
+
+ // Open the tape device.
+ _fd = ::open( QFile::encodeName(Options::instance()->getTapeDevice()), O_RDWR );
+ if ( _fd < 0 ) {
+ _fd = ::open( QFile::encodeName(Options::instance()->getTapeDevice()), O_RDONLY );
+ if ( _fd < 0 ) {
+ return;
+ } else {
+ _readOnly = TRUE;
+ }
+ } else {
+ _readOnly = FALSE;
+ }
+
+ // Set the tape block size after the device is opened.
+ setBlockSize( Options::instance()->getTapeBlockSize() );
+}
+
+int TapeDrive::read( char* buf, int len )
+{
+ if ( _fd < 0 ) {
+ return -1;
+ }
+
+ //printf( "TapeDrive::read() -- _readBufIdx = %d\n", _readBufIdx );
+
+ int remain = Options::instance()->getTapeBlockSize() - _readBufIdx;
+ if ( len <= remain ) {
+ memcpy( buf, _readBuf + _readBufIdx, len );
+ _readBufIdx += len;
+ } else {
+ memcpy( buf, _readBuf + _readBufIdx, remain );
+ _readBufIdx = Options::instance()->getTapeBlockSize();
+
+ int need = Options::instance()->getTapeBlockSize();
+ int count = 0;
+ while ( need > 0 ) {
+ int ret = ::read( _fd, _readBuf + count, Options::instance()->getTapeBlockSize() );
+ if ( ret <= 0 ) return ret;
+ need -= ret;
+ count += ret;
+ }
+
+ memcpy( buf + remain, _readBuf, len - remain );
+ _readBufIdx = len - remain;
+ }
+
+ //int ret = ::read( _fd, buf, len );
+ return len;
+}
+
+int TapeDrive::write( const char* buf, int len )
+{
+ if ( _fd < 0 ) {
+ return -1;
+ }
+
+ int bufIdx = 0;
+ while ( len + _writeBufIdx - bufIdx > Options::instance()->getTapeBlockSize() ) {
+ memcpy( _writeBuf + _writeBufIdx, buf + bufIdx, Options::instance()->getTapeBlockSize() - _writeBufIdx );
+ int ret = ::write( _fd, _writeBuf, Options::instance()->getTapeBlockSize() );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::write() -- write failed!\n" );
+ return ret;
+ }
+ bufIdx += Options::instance()->getTapeBlockSize() - _writeBufIdx;
+ _writeBufIdx = 0;
+ }
+
+ if ( bufIdx < len ) {
+ memcpy( _writeBuf + _writeBufIdx, buf + bufIdx, len - bufIdx );
+ _writeBufIdx += len - bufIdx;
+ }
+
+ return len;
+}
+
+bool TapeDrive::seek( int file, int tarBlock )
+{
+// printf( "TapeDrive::seek() -- file = %d, block = %d\n", file, tarBlock );
+
+ if ( _fd < 0 ) {
+ printf( "bailing1\n" );
+ return FALSE;
+ }
+
+ flush();
+
+ // Go to the desired archive.
+ emit sigStatus( i18n( "Skipping to archive..." ) );
+
+ int curFile = getFile();
+// printf( "TapeDrive::seek() -- curFile = %d\n", curFile );
+ if ( curFile < 0 ) {
+ emit sigStatus( i18n( "Rewinding tape..." ) );
+ rewind();
+ curFile = 0;
+ }
+
+ int fileDiff = file - curFile;
+ if ( fileDiff > 0 ) {
+ nextFile( fileDiff );
+ } else if ( fileDiff < 0 ) {
+ prevFile( -fileDiff + 1 );
+ nextFile( 1 );
+ }
+
+ int tapeBlock = tarBlock / ( Options::instance()->getTapeBlockSize() / 512 );
+// printf( "TapeDrive::seek() -- desired tapeBlock = %d\n", tapeBlock );
+
+ // Go to the desired record within the archive.
+ emit sigStatus( i18n( "Skipping to block..." ) );
+ int curBlock = getBlock();
+// printf( "TapeDrive::seek() -- curBlock = %d\n", curBlock );
+ if ( curBlock < 0 ) {
+ emit sigStatus( i18n( "Rewinding tape..." ) );
+ rewind();
+ nextFile( file );
+ if ( ( curBlock = getBlock() ) < 0 ) {
+ printf( "bailing2\n" );
+ return FALSE;
+ }
+ }
+
+ if ( tapeBlock > curBlock ) {
+ if ( !nextRecord( tapeBlock - curBlock ) ) {
+ printf( "bailing3\n" );
+ return FALSE;
+ }
+ } else if ( tapeBlock < curBlock ) {
+ if ( tapeBlock == 0 ) {
+ if ( !prevFile( 1 ) ) {
+ printf( "bailing6\n" );
+ return FALSE;
+ }
+ if ( !nextFile( 1 ) ) {
+ printf( "bailing7\n" );
+ return FALSE;
+ }
+ } else {
+ if ( !prevRecord( curBlock - tapeBlock + 1 ) ) {
+ printf( "bailing4\n" );
+ return FALSE;
+ }
+ if ( !nextRecord( 1 ) ) {
+ printf( "bailing5\n" );
+ return FALSE;
+ }
+ }
+ }
+
+// printf( "TapeDrive::seek() -- now: file = %d, block = %d\n", getFile(), getBlock() );
+
+ // If tapeBlockSize > 512, we may need to skip some tar blocks.
+// printf ( "TapeDrive::seek() -- skipping %d tar blocks.\n", tarBlock - tapeBlock * ( Options::instance()->getTapeBlockSize() / 512 ) );
+ char *buf = new char[ Options::instance()->getTapeBlockSize() ];
+ read( buf, 512 * ( tarBlock - tapeBlock * ( Options::instance()->getTapeBlockSize() / 512 ) ) );
+
+ delete [] buf;
+
+ return TRUE;
+}
+
+bool TapeDrive::pastEOF()
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+ struct mtget tapeStatus;
+ if ( ioctl( _fd, MTIOCGET, &tapeStatus ) < 0 ) {
+ printf( "TapeDrive::pastEOF() -- ioctl( MTIOCGET ) failed!\n" );
+ return FALSE;
+ }
+
+ return GMT_EOF( tapeStatus.mt_gstat );
+}
+
+bool TapeDrive::setBlockSize( int blockSize )
+{
+ if ( _fd < 0 ) {
+ return FALSE;
+ }
+
+#ifdef MTSETBLK
+ flush();
+
+ int ret = 0;
+ if ( Options::instance()->getVariableBlockSize() ) {
+ struct mtop tapeOp;
+ tapeOp.mt_op = MTSETBLK;
+ tapeOp.mt_count = blockSize;
+ ret = ioctl( _fd, MTIOCTOP, &tapeOp );
+ if ( ret < 0 ) {
+ printf( "TapeDrive::setBlockSize() -- ioctl( MTSETBLK ) failed!\n" );
+ }
+ }
+
+ _readBufIdx = blockSize;
+ _writeBufIdx = 0;
+ delete [] _readBuf;
+ _readBuf = new char[ blockSize ];
+ delete [] _writeBuf;
+ _writeBuf = new char[ blockSize ];
+
+ return ret >= 0;
+#else
+ // some systems (e.g. HP-UX) encode block size into device file names
+ // so setting the block size by software does not make sense
+ return TRUE;
+#endif
+}