From: Eric Bollengier Date: Mon, 9 Jun 2008 20:50:15 +0000 (+0000) Subject: ebl rename faketape to vtape X-Git-Tag: Release-7.0.0~4476 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=223f8360e426c9c20ddda610d83cd3f98c9e7062;p=bacula%2Fbacula ebl rename faketape to vtape git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@7124 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/src/stored/Makefile.in b/bacula/src/stored/Makefile.in index d2e7ae8bb1..1b217f7abf 100644 --- a/bacula/src/stored/Makefile.in +++ b/bacula/src/stored/Makefile.in @@ -20,7 +20,7 @@ first_rule: all dummy: # bacula-sd -SDOBJS = stored.o ansi_label.o faketape.o \ +SDOBJS = stored.o ansi_label.o vtape.o \ autochanger.o acquire.o append.o \ askdir.o authenticate.o \ block.o butil.o dev.o \ @@ -31,33 +31,33 @@ SDOBJS = stored.o ansi_label.o faketape.o \ spool.o status.o stored_conf.o wait.o # btape -TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o faketape.o \ +TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o vtape.o \ lock.o ansi_label.o dvd.o ebcdic.o \ autochanger.o acquire.o mount.o record.o read_record.o \ reserve.o \ stored_conf.o match_bsr.o parse_bsr.o scan.o spool.o wait.o # bls -BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o faketape.o \ +BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o parse_bsr.o record.o \ read_record.o reserve.o scan.o stored_conf.o spool.o wait.o # bextract -BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o faketape.o \ +BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \ pythonsd.o read_record.o reserve.o \ scan.o stored_conf.o spool.o wait.o # bscan -SCNOBJS = bscan.o block.o device.o dev.o label.o faketape.o \ +SCNOBJS = bscan.o block.o device.o dev.o label.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \ butil.o read_record.o scan.o reserve.o stored_conf.o spool.o wait.o # bcopy -COPYOBJS = bcopy.o block.o device.o dev.o label.o faketape.o \ +COPYOBJS = bcopy.o block.o device.o dev.o label.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \ butil.o read_record.o reserve.o \ diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index 2aaaad2b58..95ecfc43e1 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -44,8 +44,8 @@ #include "bacula.h" #include "stored.h" -#ifdef USE_FAKETAPE -#include "faketape.h" +#ifdef USE_VTAPE +#include "vtape.h" #endif /* Dummy functions */ diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index b09c33dbcd..793a7a2740 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -127,12 +127,12 @@ init_dev(JCR *jcr, DEVRES *device) device->dev_type = B_TAPE_DEV; } else if (S_ISFIFO(statp.st_mode)) { device->dev_type = B_FIFO_DEV; -#ifdef USE_FAKETAPE +#ifdef USE_VTAPE /* must set DeviceType = Faketape * in normal mode, autodetection is disabled */ } else if (S_ISREG(statp.st_mode)) { - device->dev_type = B_FAKETAPE_DEV; + device->dev_type = B_VTAPE_DEV; #endif } else if (!(device->cap_bits & CAP_REQMOUNT)) { Jmsg2(jcr, M_ERROR, 0, _("%s is an unknown device type. Must be tape or directory\n" @@ -275,12 +275,12 @@ init_dev(JCR *jcr, DEVRES *device) /* Choose the right backend */ void DEVICE::init_backend() { - if (is_faketape()) { - d_open = faketape_open; - d_write = faketape_write; - d_close = faketape_close; - d_ioctl = faketape_ioctl; - d_read = faketape_read; + if (is_vtape()) { + d_open = vtape_open; + d_write = vtape_write; + d_close = vtape_close; + d_ioctl = vtape_ioctl; + d_read = vtape_read; #ifdef HAVE_WIN32 } else if (is_tape()) { @@ -1890,7 +1890,7 @@ void DEVICE::close() switch (dev_type) { case B_VTL_DEV: - case B_FAKETAPE_DEV: + case B_VTAPE_DEV: case B_TAPE_DEV: unlock_door(); default: @@ -1964,7 +1964,7 @@ bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */ Dmsg1(100, "truncate %s\n", print_name()); switch (dev_type) { case B_VTL_DEV: - case B_FAKETAPE_DEV: + case B_VTAPE_DEV: case B_TAPE_DEV: /* maybe we should rewind and write and eof ???? */ return true; /* we don't really truncate tapes */ diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index 9d02218440..0bf6682ca0 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -95,7 +95,7 @@ enum { B_TAPE_DEV, B_DVD_DEV, B_FIFO_DEV, - B_FAKETAPE_DEV, /* change to B_TAPE_DEV after init */ + B_VTAPE_DEV, /* change to B_TAPE_DEV after init */ B_VTL_DEV }; @@ -309,12 +309,12 @@ public: int requires_mount() const { return capabilities & CAP_REQMOUNT; } int is_removable() const { return capabilities & CAP_REM; } int is_tape() const { return (dev_type == B_TAPE_DEV || - dev_type == B_FAKETAPE_DEV); } + dev_type == B_VTAPE_DEV); } int is_file() const { return dev_type == B_FILE_DEV; } int is_fifo() const { return dev_type == B_FIFO_DEV; } int is_dvd() const { return dev_type == B_DVD_DEV; } int is_vtl() const { return dev_type == B_VTL_DEV; } - int is_faketape() const { return dev_type == B_FAKETAPE_DEV; } + int is_vtape() const { return dev_type == B_VTAPE_DEV; } int is_open() const { return m_fd >= 0; } int is_offline() const { return state & ST_OFFLINE; } int is_labeled() const { return state & ST_LABEL; } diff --git a/bacula/src/stored/faketape.c b/bacula/src/stored/faketape.c deleted file mode 100644 index e2cef892e8..0000000000 --- a/bacula/src/stored/faketape.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - Bacula® - The Network Backup Solution - - Copyright (C) 2000-2008 Free Software Foundation Europe e.V. - - The main author of Bacula is Kern Sibbald, with contributions from - many others, a complete list can be found in the file AUTHORS. - This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public - License as published by the Free Software Foundation, which is - listed in the file LICENSE. - - 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. - - Bacula® is a registered trademark of John Walker. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. -*/ - -/* - -Device { - Name = Drive-1 # - Maximum File Size = 800M - Maximum Volume Size = 3G - Device Type = TAPE - Archive Device = /tmp/fake - Media Type = DLT-8000 - AutomaticMount = yes; # when device opened, read it - AlwaysOpen = yes; - RemovableMedia = yes; - RandomAccess = no; -} - - Block description : - - block { - int32 size; - void *data; - } - - EOF description : - - EOF { - int32 size=0; - } - - - */ - -#include "bacula.h" /* define 64bit file usage */ -#include "stored.h" - -#include "vtape.h" - -static int dbglevel = 100; -#define FILE_OFFSET 30 -vtape *ftape_list[FTAPE_MAX_DRIVE]; - -static vtape *get_tape(int fd) -{ - ASSERT(fd >= 0); - - if (fd >= FTAPE_MAX_DRIVE) { - /* error */ - return NULL; - } - - return ftape_list[fd]; -} - -static bool put_tape(vtape *ftape) -{ - ASSERT(ftape != NULL); - - int fd = ftape->get_fd(); - if (fd >= FTAPE_MAX_DRIVE) { - /* error */ - return false; - } - ftape_list[fd] = ftape; - return true; -} - -void vtape_debug(int level) -{ - dbglevel = level; -} - -/****************************************************************/ -/* theses function will replace open/read/write/close/ioctl - * in bacula core - */ -int vtape_open(const char *pathname, int flags, ...) -{ - ASSERT(pathname != NULL); - - int fd; - vtape *tape = new vtape(); - fd = tape->open(pathname, flags); - if (fd > 0) { - put_tape(tape); - } - return fd; -} - -int vtape_read(int fd, void *buffer, unsigned int count) -{ - vtape *tape = get_tape(fd); - ASSERT(tape != NULL); - return tape->read(buffer, count); -} - -int vtape_write(int fd, const void *buffer, unsigned int count) -{ - vtape *tape = get_tape(fd); - ASSERT(tape != NULL); - return tape->write(buffer, count); -} - -int vtape_close(int fd) -{ - vtape *tape = get_tape(fd); - ASSERT(tape != NULL); - tape->close(); - delete tape; - return 0; -} - -int vtape_ioctl(int fd, unsigned long int request, ...) -{ - va_list argp; - int result=0; - - vtape *t = get_tape(fd); - if (!t) { - errno = EBADF; - return -1; - } - - va_start(argp, request); - - if (request == MTIOCTOP) { - result = t->tape_op(va_arg(argp, mtop *)); - } else if (request == MTIOCGET) { - result = t->tape_get(va_arg(argp, mtget *)); - } else if (request == MTIOCPOS) { - result = t->tape_pos(va_arg(argp, mtpos *)); - } else { - errno = ENOTTY; - result = -1; - } - va_end(argp); - - return result; -} - -/****************************************************************/ - -int vtape::tape_op(struct mtop *mt_com) -{ - int result=0; - int count = mt_com->mt_count; - - if (!online) { - errno = ENOMEDIUM; - return -1; - } - - switch (mt_com->mt_op) - { - case MTRESET: - case MTNOP: - case MTSETDRVBUFFER: - break; - - default: - case MTRAS1: - case MTRAS2: - case MTRAS3: - case MTSETDENSITY: - errno = ENOTTY; - result = -1; - break; - - case MTFSF: /* Forward space over mt_count filemarks. */ - do { - result = fsf(); - } while (--count > 0 && result == 0); - break; - - case MTBSF: /* Backward space over mt_count filemarks. */ - do { - result = bsf(); - } while (--count > 0 && result == 0); - break; - - case MTFSR: /* Forward space over mt_count records (tape blocks). */ -/* - file number = 1 - block number = 0 - - file number = 1 - block number = 1 - - mt: /dev/lto2: Erreur d'entree/sortie - - file number = 2 - block number = 0 -*/ - /* tester si on se trouve a la fin du fichier */ - result = fsr(mt_com->mt_count); - break; - - case MTBSR: /* Backward space over mt_count records (tape blocks). */ - result = bsr(mt_com->mt_count); - break; - - case MTWEOF: /* Write mt_count filemarks. */ - do { - result = weof(); - } while (result == 0 && --count > 0); - break; - - case MTREW: /* Rewind. */ - Dmsg0(dbglevel, "rewind vtape\n"); - check_eof(); - atEOF = atEOD = false; - atBOT = true; - current_file = 0; - current_block = 0; - lseek(fd, 0, SEEK_SET); - result = !read_fm(VT_READ_EOF); - break; - - case MTOFFL: /* put tape offline */ - result = offline(); - break; - - case MTRETEN: /* Re-tension tape. */ - result = 0; - break; - - case MTBSFM: /* not used by bacula */ - errno = EIO; - result = -1; - break; - - case MTFSFM: /* not used by bacula */ - errno = EIO; - result = -1; - break; - - case MTEOM:/* Go to the end of the recorded media (for appending files). */ - while (next_FM) { - lseek(fd, next_FM, SEEK_SET); - if (read_fm(VT_READ_EOF)) { - current_file++; - } - } - off_t l; - while (::read(fd, &l, sizeof(l)) > 0) { - if (l) { - lseek(fd, l, SEEK_CUR); - } else { - ASSERT(0); - } - Dmsg0(dbglevel, "skip 1 block\n"); - } - current_block = -1; - atEOF = false; - atEOD = true; - -/* - file number = 3 - block number = -1 -*/ - /* Can be at EOM */ - break; - - case MTERASE: /* not used by bacula */ - atEOD = true; - atEOF = false; - atEOT = false; - - current_file = 0; - current_block = -1; - lseek(fd, 0, SEEK_SET); - read_fm(VT_READ_EOF); - truncate_file(); - break; - - case MTSETBLK: - break; - - case MTSEEK: - break; - - case MTTELL: - break; - - case MTFSS: - break; - - case MTBSS: - break; - - case MTWSM: - break; - - case MTLOCK: - break; - - case MTUNLOCK: - break; - - case MTLOAD: - break; - - case MTUNLOAD: - break; - - case MTCOMPRESSION: - break; - - case MTSETPART: - break; - - case MTMKPART: - break; - } - - return result == 0 ? 0 : -1; -} - -int vtape::tape_get(struct mtget *mt_get) -{ - int density = 1; - int block_size = 1024; - - mt_get->mt_type = MT_ISSCSI2; - mt_get->mt_blkno = current_block; - mt_get->mt_fileno = current_file; - - mt_get->mt_resid = -1; -// pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1; - - /* TODO */ - mt_get->mt_dsreg = - ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) | - ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK); - - - mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/ - - if (atEOF) { - mt_get->mt_gstat |= 0x80000000; // GMT_EOF - } - - if (atBOT) { - mt_get->mt_gstat |= 0x40000000; // GMT_BOT - } - if (atEOT) { - mt_get->mt_gstat |= 0x20000000; // GMT_EOT - } - - if (atEOD) { - mt_get->mt_gstat |= 0x08000000; // GMT_EOD - } - - if (0) { //WriteProtected) { - mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT - } - - if (online) { - mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE - } else { - mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN - } - mt_get->mt_erreg = 0; - - return 0; -} - -int vtape::tape_pos(struct mtpos *mt_pos) -{ - if (current_block >= 0) { - mt_pos->mt_blkno = current_block; - return 0; - } - - return -1; -} - -/* - * This function try to emulate the append only behavior - * of a tape. When you wrote something, data after the - * current position are discarded. - */ -int vtape::truncate_file() -{ - Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block); - ftruncate(fd, lseek(fd, 0, SEEK_CUR)); - last_file = current_file; - atEOD=true; - update_pos(); - return 0; -} - -vtape::vtape() -{ - fd = -1; - - atEOF = false; - atBOT = false; - atEOT = false; - atEOD = false; - online = false; - needEOF = false; - - file_block = 0; - last_file = 0; - current_file = 0; - current_block = -1; - - max_block = 2*1024*2048; /* 2GB */ -} - -vtape::~vtape() -{ -} - -int vtape::get_fd() -{ - return this->fd; -} - -/* - * TODO: check if after a write op, and other tape op put a EOF - */ -int vtape::write(const void *buffer, unsigned int count) -{ - ASSERT(online); - ASSERT(current_file >= 0); - ASSERT(count > 0); - ASSERT(buffer); - - unsigned int nb; - Dmsg3(dbglevel*2, "write len=%i %i:%i\n", - count, current_file,current_block); - - if (atEOT) { - Dmsg0(dbglevel, "write nothing, EOT !\n"); - errno = ENOSPC; - return -1; - } - - if (!atEOD) { /* if not at the end of the data */ - truncate_file(); - } - - if (current_block != -1) { - current_block++; - } - - atBOT = false; - atEOF = false; - atEOD = true; /* End of data */ - - needEOF = true; /* next operation need EOF mark */ - - uint32_t size = count; - ::write(fd, &size, sizeof(uint32_t)); - nb = ::write(fd, buffer, count); - - if (nb != count) { - atEOT = true; - Dmsg2(dbglevel, - "Not enough space writing only %i of %i requested\n", - nb, count); - } - - update_pos(); - - return nb; -} - -/* - * +---+---------+---+------------------+---+-------------------+ - * |00N| DATA |0LN| DATA |0LC| DATA | - * +---+---------+---+------------------+---+-------------------+ - * - * 0 : zero - * L : Last FileMark offset - * N : Next FileMark offset - * C : Current FileMark Offset - */ -int vtape::weof() -{ - ASSERT(online); - ASSERT(current_file >= 0); - - if (atEOT) { - errno = ENOSPC; - current_block = -1; - return -1; - } - - if (!atEOD) { - truncate_file(); /* nothing after this point */ - } - - last_FM = cur_FM; - cur_FM = lseek(fd, 0, SEEK_CUR); // current position - - /* update previous next_FM */ - lseek(fd, last_FM + sizeof(uint32_t)+sizeof(off_t), SEEK_SET); - ::write(fd, &cur_FM, sizeof(off_t)); - lseek(fd, cur_FM, SEEK_SET); - - next_FM = 0; - - uint32_t c=0; - ::write(fd, &c, sizeof(uint32_t)); // EOF - ::write(fd, &last_FM, sizeof(last_FM)); // F-1 - ::write(fd, &next_FM, sizeof(next_FM)); // F (will be updated next time) - - current_file++; - current_block = 0; - - needEOF = false; - atEOD = false; - atBOT = false; - atEOF = true; - - last_file = MAX(current_file, last_file); - - Dmsg4(dbglevel, "Writing EOF %i:%i last=%lli cur=%lli next=0\n", - current_file, current_block, last_FM, cur_FM); - - return 0; -} - -/* - * Go to next FM - */ -int vtape::fsf() -{ - ASSERT(online); - ASSERT(current_file >= 0); - ASSERT(fd >= 0); -/* - * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1 - */ - - int ret=0; - if (atEOT || atEOD) { - errno = EIO; - current_block = -1; - return -1; - } - - atBOT = false; - Dmsg2(dbglevel+1, "fsf %i <= %i\n", current_file, last_file); - - if (next_FM > cur_FM) { /* not the last file */ - lseek(fd, next_FM, SEEK_SET); - read_fm(VT_READ_EOF); - current_file++; - atEOF = true; - ret = 0; - - } else if (atEOF) { /* last file mark */ - current_block=-1; - errno = EIO; - atEOF = false; - atEOD = true; - - } else { /* last file, but no at the end */ - fsr(100000); - - Dmsg0(dbglevel, "Try to FSF after EOT\n"); - errno = EIO; - current_file = last_file ; - current_block = -1; - atEOD=true; - ret = -1; - } - return ret; -} - -/* /------------\ /---------------\ - * +---+------+---+---------------+-+ - * |OLN| |0LN| | | - * +---+------+---+---------------+-+ - */ - -bool vtape::read_fm(VT_READ_FM_MODE read_all) -{ - int ret; - uint32_t c; - if (read_all == VT_READ_EOF) { - ::read(fd, &c, sizeof(c)); - if (c != 0) { - lseek(fd, cur_FM, SEEK_SET); - return false; - } - } - - cur_FM = lseek(fd, 0, SEEK_CUR) - sizeof(c); - - ::read(fd, &last_FM, sizeof(last_FM)); - ret = ::read(fd, &next_FM, sizeof(next_FM)); - - current_block=0; - - Dmsg3(dbglevel, "Read FM cur=%lli last=%lli next=%lli\n", - cur_FM, last_FM, next_FM); - - return (ret == sizeof(next_FM)); -} - -/* - * TODO: Check fsr with EOF - */ -int vtape::fsr(int count) -{ - ASSERT(online); - ASSERT(current_file >= 0); - ASSERT(fd >= 0); - - int i,nb, ret=0; - off_t where=0; - uint32_t s; - Dmsg4(dbglevel, "fsr %i:%i EOF=%i c=%i\n", - current_file,current_block,atEOF,count); - - check_eof(); - - if (atEOT) { - errno = EIO; - current_block = -1; - return -1; - } - - if (atEOD) { - errno = EIO; - return -1; - } - - atBOT = atEOF = false; - - /* check all block record */ - for(i=0; (i < count) && !atEOF ; i++) { - nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */ - if (nb == sizeof(uint32_t) && s) { - current_block++; - where = lseek(fd, s, SEEK_CUR); /* seek after this block */ - } else { - Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n", - current_file, current_block, nb,s); - errno = EIO; - ret = -1; - if (next_FM) { - current_file++; - read_fm(VT_SKIP_EOF); - } - atEOF = true; /* stop the loop */ - } - } - - return ret; -} - -/* - * BSR + EOF => begin of EOF + EIO - * BSR + BSR + EOF => last block - * current_block = -1 - */ -int vtape::bsr(int count) -{ - ASSERT(online); - ASSERT(current_file >= 0); - ASSERT(count == 1); - ASSERT(fd >= 0); - - check_eof(); - - if (!count) { - return 0; - } - - int ret=0; - int last_f=0; - int last_b=0; - - off_t last=-1, last2=-1; - off_t orig = lseek(fd, 0, SEEK_CUR); - int orig_f = current_file; - int orig_b = current_block; - - Dmsg4(dbglevel, "bsr(%i) cur_blk=%i orig=%lli cur_FM=%lli\n", - count, current_block, orig, cur_FM); - - /* begin of tape, do nothing */ - if (atBOT) { - errno = EIO; - return -1; - } - - /* at EOF 0:-1 BOT=0 EOD=0 EOF=0 ERR: Input/output error */ - if (atEOF) { - lseek(fd, cur_FM, SEEK_SET); - atEOF = false; - if (current_file > 0) { - current_file--; - } - current_block=-1; - errno = EIO; - return -1; - } - - /* - * First, go to cur/last_FM and read all blocks to find the good one - */ - if (cur_FM == orig) { /* already just before EOF */ - lseek(fd, last_FM, SEEK_SET); - - } else { - lseek(fd, cur_FM, SEEK_SET); - } - - ret = read_fm(VT_READ_EOF); - - do { - if (!atEOF) { - last2 = last; /* keep track of the 2 last blocs position */ - last = lseek(fd, 0, SEEK_CUR); - last_f = current_file; - last_b = current_block; - Dmsg6(dbglevel, "EOF=%i last2=%lli last=%lli < orig=%lli %i:%i\n", - atEOF, last2, last, orig, current_file, current_block); - } - ret = fsr(1); - } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0)); - - if (last2 > 0 && atEOF) { /* we take the previous position */ - lseek(fd, last2, SEEK_SET); - current_file = last_f; - current_block = last_b - 1; - Dmsg3(dbglevel, "1 set offset2=%lli %i:%i\n", - last, current_file, current_block); - - } else if (last > 0) { - lseek(fd, last, SEEK_SET); - current_file = last_f; - current_block = last_b; - Dmsg3(dbglevel, "2 set offset=%lli %i:%i\n", - last, current_file, current_block); - } else { - lseek(fd, orig, SEEK_SET); - current_file = orig_f; - current_block = orig_b; - return -1; - } - - Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block); - errno=0; - atEOT = atEOF = atEOD = false; - atBOT = (lseek(fd, 0, SEEK_CUR) - (sizeof(uint32_t)+2*sizeof(off_t))) == 0; - - if (orig_b == -1) { - current_block = orig_b; - } - - return 0; -} - -/* BSF => just before last EOF - * EOF + BSF => just before EOF - * file 0 + BSF => BOT + errno - */ -int vtape::bsf() -{ - ASSERT(online); - ASSERT(current_file >= 0); - Dmsg2(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block); - int ret = 0; - - check_eof(); - - atBOT = atEOF = atEOT = atEOD = false; - - if (current_file == 0) {/* BOT + errno */ - lseek(fd, 0, SEEK_SET); - read_fm(VT_READ_EOF); - current_file = 0; - current_block = 0; - atBOT = true; - errno = EIO; - ret = -1; - } else { - Dmsg1(dbglevel, "bsf last=%lli\n", last_FM); - lseek(fd, cur_FM, SEEK_SET); - current_file--; - current_block=-1; - } - return ret; -} - -/* - * Put vtape in offline mode - */ -int vtape::offline() -{ - close(); - - atEOF = false; /* End of file */ - atEOT = false; /* End of tape */ - atEOD = false; /* End of data */ - atBOT = false; /* Begin of tape */ - online = false; - - file_block = 0; - current_file = -1; - current_block = -1; - last_file = -1; - return 0; -} - -/* A filemark is automatically written to tape if the last tape operation - * before close was a write. - */ -int vtape::close() -{ - check_eof(); - ::close(fd); - fd = -1; - return 0; -} - -/* - * When a filemark is encountered while reading, the following happens. If - * there are data remaining in the buffer when the filemark is found, the - * buffered data is returned. The next read returns zero bytes. The following - * read returns data from the next file. The end of recorded data is sig‐ - * naled by returning zero bytes for two consecutive read calls. The third - * read returns an error. - */ -int vtape::read(void *buffer, unsigned int count) -{ - ASSERT(online); - ASSERT(current_file >= 0); - unsigned int nb; - uint32_t s; - - Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block); - - if (atEOT || atEOD) { - errno = EIO; - return -1; - } - - if (atEOF) { - if (!next_FM) { - atEOD = true; - atEOF = false; - current_block=-1; - return 0; - } - atEOF=false; - } - - check_eof(); - - atEOD = atBOT = false; - - /* reading size of data */ - nb = ::read(fd, &s, sizeof(uint32_t)); - if (nb <= 0) { - atEOF = true; /* TODO: check this */ - return 0; - } - - if (s > count) { /* not enough buffer to read block */ - Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count); - lseek(fd, s, SEEK_CUR); - errno = ENOMEM; - return -1; - } - - if (!s) { /* EOF */ - atEOF = true; - if (read_fm(VT_SKIP_EOF)) { - current_file++; - } - - return 0; - } - - /* reading data itself */ - nb = ::read(fd, buffer, s); - if (s != nb) { /* read error */ - errno=EIO; - atEOT=true; - current_block = -1; - Dmsg0(dbglevel, "EOT during reading\n"); - return -1; - } /* read ok */ - - if (current_block >= 0) { - current_block++; - } - - return nb; -} - -int vtape::open(const char *pathname, int uflags) -{ - Dmsg2(dbglevel, "vtape::open(%s, %i)\n", pathname, uflags); - - online = true; /* assume that drive contains a tape */ - - struct stat statp; - if (stat(pathname, &statp) != 0) { - Dmsg1(dbglevel, "Can't stat on %s\n", pathname); - if (uflags & O_NONBLOCK) { - online = false; - fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600); - } - } else { - fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600); - } - - if (fd < 0) { - errno = ENOMEDIUM; - return -1; - } - - file_block = 0; - current_block = 0; - current_file = 0; - cur_FM = next_FM = last_FM = 0; - needEOF = false; - atBOT = true; - atEOT = atEOD = false; - - /* If the vtape is empty, start by writing a EOF */ - if (online && !read_fm(VT_READ_EOF)) { - weof(); - last_file = current_file=0; - } - - return fd; -} - -/* use this to track file usage */ -void vtape::update_pos() -{ - ASSERT(online); - struct stat statp; - if (fstat(fd, &statp) == 0) { - file_block = statp.st_blocks; - } - - Dmsg1(dbglevel+1, "update_pos=%i\n", file_block); - - if (file_block > max_block) { - atEOT = true; - } else { - atEOT = false; - } -} - -void vtape::dump() -{ - Dmsg0(dbglevel+1, "===================\n"); - Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block); - Dmsg1(dbglevel+1, "last_file=%i\n", last_file); - Dmsg1(dbglevel+1, "file_block=%i\n", file_block); - Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", - atEOF, atEOT, atEOD, atBOT); -} - diff --git a/bacula/src/stored/faketape.h b/bacula/src/stored/faketape.h deleted file mode 100644 index 0bef45fe00..0000000000 --- a/bacula/src/stored/faketape.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - Bacula® - The Network Backup Solution - - Copyright (C) 2000-2008 Free Software Foundation Europe e.V. - - The main author of Bacula is Kern Sibbald, with contributions from - many others, a complete list can be found in the file AUTHORS. - This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public - License as published by the Free Software Foundation, which is - listed in the file LICENSE. - - 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. - - Bacula® is a registered trademark of John Walker. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. -*/ -/* - * vtape.h - Emulate the Linux st (scsi tape) driver on file. - * for regression and bug hunting purpose - * - */ - -#ifndef VTAPE_H -#define VTAPE_H - -#include -#include -#include "bacula.h" - -#define FTAPE_MAX_DRIVE 50 - -/* - * Theses functions will replace open/read/write - */ -int vtape_open(const char *pathname, int flags, ...); -int vtape_read(int fd, void *buffer, unsigned int count); -int vtape_write(int fd, const void *buffer, unsigned int count); -int vtape_close(int fd); -int vtape_ioctl(int fd, unsigned long int request, ...); -void vtape_debug(int level); - -typedef enum { - VT_READ_EOF, /* Need to read the entire EOF struct */ - VT_SKIP_EOF /* Have already read the EOF byte */ -} VT_READ_FM_MODE; - -class vtape { -private: - int fd; /* Our file descriptor */ - - off_t file_block; /* size */ - off_t max_block; - - off_t last_FM; /* last file mark (last file) */ - off_t next_FM; /* next file mark (next file) */ - off_t cur_FM; /* current file mark */ - - bool atEOF; /* End of file */ - bool atEOT; /* End of media */ - bool atEOD; /* End of data */ - bool atBOT; /* Begin of tape */ - bool online; /* volume online */ - bool needEOF; /* check if last operation need eof */ - - int32_t last_file; /* last file of the volume */ - int32_t current_file; /* current position */ - int32_t current_block; /* current position */ - - void destroy(); - int offline(); - int truncate_file(); - void check_eof() { if(needEOF) weof();}; - void update_pos(); - bool read_fm(VT_READ_FM_MODE readfirst); - -public: - int fsf(); - int fsr(int count); - int weof(); - int bsf(); - int bsr(int count); - - vtape(); - ~vtape(); - - int get_fd(); - void dump(); - int open(const char *pathname, int flags); - int read(void *buffer, unsigned int count); - int write(const void *buffer, unsigned int count); - int close(); - - int tape_op(struct mtop *mt_com); - int tape_get(struct mtget *mt_com); - int tape_pos(struct mtpos *mt_com); -}; - -#endif /* !VTAPE_H */ diff --git a/bacula/src/stored/stored.h b/bacula/src/stored/stored.h index 764b478f5f..edc2bb9944 100644 --- a/bacula/src/stored/stored.h +++ b/bacula/src/stored/stored.h @@ -78,7 +78,7 @@ const int sd_dbglvl = 300; int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); #endif -#include "faketape.h" +#include "vtape.h" /* Daemon globals from stored.c */ diff --git a/bacula/src/stored/stored_conf.c b/bacula/src/stored/stored_conf.c index d81fad95f9..15818e8bd5 100644 --- a/bacula/src/stored/stored_conf.c +++ b/bacula/src/stored/stored_conf.c @@ -213,7 +213,7 @@ static s_kw dev_types[] = { {"dvd", B_DVD_DEV}, {"fifo", B_FIFO_DEV}, {"vtl", B_VTL_DEV}, - {"faketape", B_FAKETAPE_DEV}, + {"vtape", B_VTAPE_DEV}, {NULL, 0} }; diff --git a/bacula/src/stored/vtape.c b/bacula/src/stored/vtape.c new file mode 100644 index 0000000000..e2cef892e8 --- /dev/null +++ b/bacula/src/stored/vtape.c @@ -0,0 +1,993 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation, which is + listed in the file LICENSE. + + 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. + + Bacula® is a registered trademark of John Walker. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ + +/* + +Device { + Name = Drive-1 # + Maximum File Size = 800M + Maximum Volume Size = 3G + Device Type = TAPE + Archive Device = /tmp/fake + Media Type = DLT-8000 + AutomaticMount = yes; # when device opened, read it + AlwaysOpen = yes; + RemovableMedia = yes; + RandomAccess = no; +} + + Block description : + + block { + int32 size; + void *data; + } + + EOF description : + + EOF { + int32 size=0; + } + + + */ + +#include "bacula.h" /* define 64bit file usage */ +#include "stored.h" + +#include "vtape.h" + +static int dbglevel = 100; +#define FILE_OFFSET 30 +vtape *ftape_list[FTAPE_MAX_DRIVE]; + +static vtape *get_tape(int fd) +{ + ASSERT(fd >= 0); + + if (fd >= FTAPE_MAX_DRIVE) { + /* error */ + return NULL; + } + + return ftape_list[fd]; +} + +static bool put_tape(vtape *ftape) +{ + ASSERT(ftape != NULL); + + int fd = ftape->get_fd(); + if (fd >= FTAPE_MAX_DRIVE) { + /* error */ + return false; + } + ftape_list[fd] = ftape; + return true; +} + +void vtape_debug(int level) +{ + dbglevel = level; +} + +/****************************************************************/ +/* theses function will replace open/read/write/close/ioctl + * in bacula core + */ +int vtape_open(const char *pathname, int flags, ...) +{ + ASSERT(pathname != NULL); + + int fd; + vtape *tape = new vtape(); + fd = tape->open(pathname, flags); + if (fd > 0) { + put_tape(tape); + } + return fd; +} + +int vtape_read(int fd, void *buffer, unsigned int count) +{ + vtape *tape = get_tape(fd); + ASSERT(tape != NULL); + return tape->read(buffer, count); +} + +int vtape_write(int fd, const void *buffer, unsigned int count) +{ + vtape *tape = get_tape(fd); + ASSERT(tape != NULL); + return tape->write(buffer, count); +} + +int vtape_close(int fd) +{ + vtape *tape = get_tape(fd); + ASSERT(tape != NULL); + tape->close(); + delete tape; + return 0; +} + +int vtape_ioctl(int fd, unsigned long int request, ...) +{ + va_list argp; + int result=0; + + vtape *t = get_tape(fd); + if (!t) { + errno = EBADF; + return -1; + } + + va_start(argp, request); + + if (request == MTIOCTOP) { + result = t->tape_op(va_arg(argp, mtop *)); + } else if (request == MTIOCGET) { + result = t->tape_get(va_arg(argp, mtget *)); + } else if (request == MTIOCPOS) { + result = t->tape_pos(va_arg(argp, mtpos *)); + } else { + errno = ENOTTY; + result = -1; + } + va_end(argp); + + return result; +} + +/****************************************************************/ + +int vtape::tape_op(struct mtop *mt_com) +{ + int result=0; + int count = mt_com->mt_count; + + if (!online) { + errno = ENOMEDIUM; + return -1; + } + + switch (mt_com->mt_op) + { + case MTRESET: + case MTNOP: + case MTSETDRVBUFFER: + break; + + default: + case MTRAS1: + case MTRAS2: + case MTRAS3: + case MTSETDENSITY: + errno = ENOTTY; + result = -1; + break; + + case MTFSF: /* Forward space over mt_count filemarks. */ + do { + result = fsf(); + } while (--count > 0 && result == 0); + break; + + case MTBSF: /* Backward space over mt_count filemarks. */ + do { + result = bsf(); + } while (--count > 0 && result == 0); + break; + + case MTFSR: /* Forward space over mt_count records (tape blocks). */ +/* + file number = 1 + block number = 0 + + file number = 1 + block number = 1 + + mt: /dev/lto2: Erreur d'entree/sortie + + file number = 2 + block number = 0 +*/ + /* tester si on se trouve a la fin du fichier */ + result = fsr(mt_com->mt_count); + break; + + case MTBSR: /* Backward space over mt_count records (tape blocks). */ + result = bsr(mt_com->mt_count); + break; + + case MTWEOF: /* Write mt_count filemarks. */ + do { + result = weof(); + } while (result == 0 && --count > 0); + break; + + case MTREW: /* Rewind. */ + Dmsg0(dbglevel, "rewind vtape\n"); + check_eof(); + atEOF = atEOD = false; + atBOT = true; + current_file = 0; + current_block = 0; + lseek(fd, 0, SEEK_SET); + result = !read_fm(VT_READ_EOF); + break; + + case MTOFFL: /* put tape offline */ + result = offline(); + break; + + case MTRETEN: /* Re-tension tape. */ + result = 0; + break; + + case MTBSFM: /* not used by bacula */ + errno = EIO; + result = -1; + break; + + case MTFSFM: /* not used by bacula */ + errno = EIO; + result = -1; + break; + + case MTEOM:/* Go to the end of the recorded media (for appending files). */ + while (next_FM) { + lseek(fd, next_FM, SEEK_SET); + if (read_fm(VT_READ_EOF)) { + current_file++; + } + } + off_t l; + while (::read(fd, &l, sizeof(l)) > 0) { + if (l) { + lseek(fd, l, SEEK_CUR); + } else { + ASSERT(0); + } + Dmsg0(dbglevel, "skip 1 block\n"); + } + current_block = -1; + atEOF = false; + atEOD = true; + +/* + file number = 3 + block number = -1 +*/ + /* Can be at EOM */ + break; + + case MTERASE: /* not used by bacula */ + atEOD = true; + atEOF = false; + atEOT = false; + + current_file = 0; + current_block = -1; + lseek(fd, 0, SEEK_SET); + read_fm(VT_READ_EOF); + truncate_file(); + break; + + case MTSETBLK: + break; + + case MTSEEK: + break; + + case MTTELL: + break; + + case MTFSS: + break; + + case MTBSS: + break; + + case MTWSM: + break; + + case MTLOCK: + break; + + case MTUNLOCK: + break; + + case MTLOAD: + break; + + case MTUNLOAD: + break; + + case MTCOMPRESSION: + break; + + case MTSETPART: + break; + + case MTMKPART: + break; + } + + return result == 0 ? 0 : -1; +} + +int vtape::tape_get(struct mtget *mt_get) +{ + int density = 1; + int block_size = 1024; + + mt_get->mt_type = MT_ISSCSI2; + mt_get->mt_blkno = current_block; + mt_get->mt_fileno = current_file; + + mt_get->mt_resid = -1; +// pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1; + + /* TODO */ + mt_get->mt_dsreg = + ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) | + ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK); + + + mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/ + + if (atEOF) { + mt_get->mt_gstat |= 0x80000000; // GMT_EOF + } + + if (atBOT) { + mt_get->mt_gstat |= 0x40000000; // GMT_BOT + } + if (atEOT) { + mt_get->mt_gstat |= 0x20000000; // GMT_EOT + } + + if (atEOD) { + mt_get->mt_gstat |= 0x08000000; // GMT_EOD + } + + if (0) { //WriteProtected) { + mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT + } + + if (online) { + mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE + } else { + mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN + } + mt_get->mt_erreg = 0; + + return 0; +} + +int vtape::tape_pos(struct mtpos *mt_pos) +{ + if (current_block >= 0) { + mt_pos->mt_blkno = current_block; + return 0; + } + + return -1; +} + +/* + * This function try to emulate the append only behavior + * of a tape. When you wrote something, data after the + * current position are discarded. + */ +int vtape::truncate_file() +{ + Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block); + ftruncate(fd, lseek(fd, 0, SEEK_CUR)); + last_file = current_file; + atEOD=true; + update_pos(); + return 0; +} + +vtape::vtape() +{ + fd = -1; + + atEOF = false; + atBOT = false; + atEOT = false; + atEOD = false; + online = false; + needEOF = false; + + file_block = 0; + last_file = 0; + current_file = 0; + current_block = -1; + + max_block = 2*1024*2048; /* 2GB */ +} + +vtape::~vtape() +{ +} + +int vtape::get_fd() +{ + return this->fd; +} + +/* + * TODO: check if after a write op, and other tape op put a EOF + */ +int vtape::write(const void *buffer, unsigned int count) +{ + ASSERT(online); + ASSERT(current_file >= 0); + ASSERT(count > 0); + ASSERT(buffer); + + unsigned int nb; + Dmsg3(dbglevel*2, "write len=%i %i:%i\n", + count, current_file,current_block); + + if (atEOT) { + Dmsg0(dbglevel, "write nothing, EOT !\n"); + errno = ENOSPC; + return -1; + } + + if (!atEOD) { /* if not at the end of the data */ + truncate_file(); + } + + if (current_block != -1) { + current_block++; + } + + atBOT = false; + atEOF = false; + atEOD = true; /* End of data */ + + needEOF = true; /* next operation need EOF mark */ + + uint32_t size = count; + ::write(fd, &size, sizeof(uint32_t)); + nb = ::write(fd, buffer, count); + + if (nb != count) { + atEOT = true; + Dmsg2(dbglevel, + "Not enough space writing only %i of %i requested\n", + nb, count); + } + + update_pos(); + + return nb; +} + +/* + * +---+---------+---+------------------+---+-------------------+ + * |00N| DATA |0LN| DATA |0LC| DATA | + * +---+---------+---+------------------+---+-------------------+ + * + * 0 : zero + * L : Last FileMark offset + * N : Next FileMark offset + * C : Current FileMark Offset + */ +int vtape::weof() +{ + ASSERT(online); + ASSERT(current_file >= 0); + + if (atEOT) { + errno = ENOSPC; + current_block = -1; + return -1; + } + + if (!atEOD) { + truncate_file(); /* nothing after this point */ + } + + last_FM = cur_FM; + cur_FM = lseek(fd, 0, SEEK_CUR); // current position + + /* update previous next_FM */ + lseek(fd, last_FM + sizeof(uint32_t)+sizeof(off_t), SEEK_SET); + ::write(fd, &cur_FM, sizeof(off_t)); + lseek(fd, cur_FM, SEEK_SET); + + next_FM = 0; + + uint32_t c=0; + ::write(fd, &c, sizeof(uint32_t)); // EOF + ::write(fd, &last_FM, sizeof(last_FM)); // F-1 + ::write(fd, &next_FM, sizeof(next_FM)); // F (will be updated next time) + + current_file++; + current_block = 0; + + needEOF = false; + atEOD = false; + atBOT = false; + atEOF = true; + + last_file = MAX(current_file, last_file); + + Dmsg4(dbglevel, "Writing EOF %i:%i last=%lli cur=%lli next=0\n", + current_file, current_block, last_FM, cur_FM); + + return 0; +} + +/* + * Go to next FM + */ +int vtape::fsf() +{ + ASSERT(online); + ASSERT(current_file >= 0); + ASSERT(fd >= 0); +/* + * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1 + */ + + int ret=0; + if (atEOT || atEOD) { + errno = EIO; + current_block = -1; + return -1; + } + + atBOT = false; + Dmsg2(dbglevel+1, "fsf %i <= %i\n", current_file, last_file); + + if (next_FM > cur_FM) { /* not the last file */ + lseek(fd, next_FM, SEEK_SET); + read_fm(VT_READ_EOF); + current_file++; + atEOF = true; + ret = 0; + + } else if (atEOF) { /* last file mark */ + current_block=-1; + errno = EIO; + atEOF = false; + atEOD = true; + + } else { /* last file, but no at the end */ + fsr(100000); + + Dmsg0(dbglevel, "Try to FSF after EOT\n"); + errno = EIO; + current_file = last_file ; + current_block = -1; + atEOD=true; + ret = -1; + } + return ret; +} + +/* /------------\ /---------------\ + * +---+------+---+---------------+-+ + * |OLN| |0LN| | | + * +---+------+---+---------------+-+ + */ + +bool vtape::read_fm(VT_READ_FM_MODE read_all) +{ + int ret; + uint32_t c; + if (read_all == VT_READ_EOF) { + ::read(fd, &c, sizeof(c)); + if (c != 0) { + lseek(fd, cur_FM, SEEK_SET); + return false; + } + } + + cur_FM = lseek(fd, 0, SEEK_CUR) - sizeof(c); + + ::read(fd, &last_FM, sizeof(last_FM)); + ret = ::read(fd, &next_FM, sizeof(next_FM)); + + current_block=0; + + Dmsg3(dbglevel, "Read FM cur=%lli last=%lli next=%lli\n", + cur_FM, last_FM, next_FM); + + return (ret == sizeof(next_FM)); +} + +/* + * TODO: Check fsr with EOF + */ +int vtape::fsr(int count) +{ + ASSERT(online); + ASSERT(current_file >= 0); + ASSERT(fd >= 0); + + int i,nb, ret=0; + off_t where=0; + uint32_t s; + Dmsg4(dbglevel, "fsr %i:%i EOF=%i c=%i\n", + current_file,current_block,atEOF,count); + + check_eof(); + + if (atEOT) { + errno = EIO; + current_block = -1; + return -1; + } + + if (atEOD) { + errno = EIO; + return -1; + } + + atBOT = atEOF = false; + + /* check all block record */ + for(i=0; (i < count) && !atEOF ; i++) { + nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */ + if (nb == sizeof(uint32_t) && s) { + current_block++; + where = lseek(fd, s, SEEK_CUR); /* seek after this block */ + } else { + Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n", + current_file, current_block, nb,s); + errno = EIO; + ret = -1; + if (next_FM) { + current_file++; + read_fm(VT_SKIP_EOF); + } + atEOF = true; /* stop the loop */ + } + } + + return ret; +} + +/* + * BSR + EOF => begin of EOF + EIO + * BSR + BSR + EOF => last block + * current_block = -1 + */ +int vtape::bsr(int count) +{ + ASSERT(online); + ASSERT(current_file >= 0); + ASSERT(count == 1); + ASSERT(fd >= 0); + + check_eof(); + + if (!count) { + return 0; + } + + int ret=0; + int last_f=0; + int last_b=0; + + off_t last=-1, last2=-1; + off_t orig = lseek(fd, 0, SEEK_CUR); + int orig_f = current_file; + int orig_b = current_block; + + Dmsg4(dbglevel, "bsr(%i) cur_blk=%i orig=%lli cur_FM=%lli\n", + count, current_block, orig, cur_FM); + + /* begin of tape, do nothing */ + if (atBOT) { + errno = EIO; + return -1; + } + + /* at EOF 0:-1 BOT=0 EOD=0 EOF=0 ERR: Input/output error */ + if (atEOF) { + lseek(fd, cur_FM, SEEK_SET); + atEOF = false; + if (current_file > 0) { + current_file--; + } + current_block=-1; + errno = EIO; + return -1; + } + + /* + * First, go to cur/last_FM and read all blocks to find the good one + */ + if (cur_FM == orig) { /* already just before EOF */ + lseek(fd, last_FM, SEEK_SET); + + } else { + lseek(fd, cur_FM, SEEK_SET); + } + + ret = read_fm(VT_READ_EOF); + + do { + if (!atEOF) { + last2 = last; /* keep track of the 2 last blocs position */ + last = lseek(fd, 0, SEEK_CUR); + last_f = current_file; + last_b = current_block; + Dmsg6(dbglevel, "EOF=%i last2=%lli last=%lli < orig=%lli %i:%i\n", + atEOF, last2, last, orig, current_file, current_block); + } + ret = fsr(1); + } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0)); + + if (last2 > 0 && atEOF) { /* we take the previous position */ + lseek(fd, last2, SEEK_SET); + current_file = last_f; + current_block = last_b - 1; + Dmsg3(dbglevel, "1 set offset2=%lli %i:%i\n", + last, current_file, current_block); + + } else if (last > 0) { + lseek(fd, last, SEEK_SET); + current_file = last_f; + current_block = last_b; + Dmsg3(dbglevel, "2 set offset=%lli %i:%i\n", + last, current_file, current_block); + } else { + lseek(fd, orig, SEEK_SET); + current_file = orig_f; + current_block = orig_b; + return -1; + } + + Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block); + errno=0; + atEOT = atEOF = atEOD = false; + atBOT = (lseek(fd, 0, SEEK_CUR) - (sizeof(uint32_t)+2*sizeof(off_t))) == 0; + + if (orig_b == -1) { + current_block = orig_b; + } + + return 0; +} + +/* BSF => just before last EOF + * EOF + BSF => just before EOF + * file 0 + BSF => BOT + errno + */ +int vtape::bsf() +{ + ASSERT(online); + ASSERT(current_file >= 0); + Dmsg2(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block); + int ret = 0; + + check_eof(); + + atBOT = atEOF = atEOT = atEOD = false; + + if (current_file == 0) {/* BOT + errno */ + lseek(fd, 0, SEEK_SET); + read_fm(VT_READ_EOF); + current_file = 0; + current_block = 0; + atBOT = true; + errno = EIO; + ret = -1; + } else { + Dmsg1(dbglevel, "bsf last=%lli\n", last_FM); + lseek(fd, cur_FM, SEEK_SET); + current_file--; + current_block=-1; + } + return ret; +} + +/* + * Put vtape in offline mode + */ +int vtape::offline() +{ + close(); + + atEOF = false; /* End of file */ + atEOT = false; /* End of tape */ + atEOD = false; /* End of data */ + atBOT = false; /* Begin of tape */ + online = false; + + file_block = 0; + current_file = -1; + current_block = -1; + last_file = -1; + return 0; +} + +/* A filemark is automatically written to tape if the last tape operation + * before close was a write. + */ +int vtape::close() +{ + check_eof(); + ::close(fd); + fd = -1; + return 0; +} + +/* + * When a filemark is encountered while reading, the following happens. If + * there are data remaining in the buffer when the filemark is found, the + * buffered data is returned. The next read returns zero bytes. The following + * read returns data from the next file. The end of recorded data is sig‐ + * naled by returning zero bytes for two consecutive read calls. The third + * read returns an error. + */ +int vtape::read(void *buffer, unsigned int count) +{ + ASSERT(online); + ASSERT(current_file >= 0); + unsigned int nb; + uint32_t s; + + Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block); + + if (atEOT || atEOD) { + errno = EIO; + return -1; + } + + if (atEOF) { + if (!next_FM) { + atEOD = true; + atEOF = false; + current_block=-1; + return 0; + } + atEOF=false; + } + + check_eof(); + + atEOD = atBOT = false; + + /* reading size of data */ + nb = ::read(fd, &s, sizeof(uint32_t)); + if (nb <= 0) { + atEOF = true; /* TODO: check this */ + return 0; + } + + if (s > count) { /* not enough buffer to read block */ + Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count); + lseek(fd, s, SEEK_CUR); + errno = ENOMEM; + return -1; + } + + if (!s) { /* EOF */ + atEOF = true; + if (read_fm(VT_SKIP_EOF)) { + current_file++; + } + + return 0; + } + + /* reading data itself */ + nb = ::read(fd, buffer, s); + if (s != nb) { /* read error */ + errno=EIO; + atEOT=true; + current_block = -1; + Dmsg0(dbglevel, "EOT during reading\n"); + return -1; + } /* read ok */ + + if (current_block >= 0) { + current_block++; + } + + return nb; +} + +int vtape::open(const char *pathname, int uflags) +{ + Dmsg2(dbglevel, "vtape::open(%s, %i)\n", pathname, uflags); + + online = true; /* assume that drive contains a tape */ + + struct stat statp; + if (stat(pathname, &statp) != 0) { + Dmsg1(dbglevel, "Can't stat on %s\n", pathname); + if (uflags & O_NONBLOCK) { + online = false; + fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600); + } + } else { + fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600); + } + + if (fd < 0) { + errno = ENOMEDIUM; + return -1; + } + + file_block = 0; + current_block = 0; + current_file = 0; + cur_FM = next_FM = last_FM = 0; + needEOF = false; + atBOT = true; + atEOT = atEOD = false; + + /* If the vtape is empty, start by writing a EOF */ + if (online && !read_fm(VT_READ_EOF)) { + weof(); + last_file = current_file=0; + } + + return fd; +} + +/* use this to track file usage */ +void vtape::update_pos() +{ + ASSERT(online); + struct stat statp; + if (fstat(fd, &statp) == 0) { + file_block = statp.st_blocks; + } + + Dmsg1(dbglevel+1, "update_pos=%i\n", file_block); + + if (file_block > max_block) { + atEOT = true; + } else { + atEOT = false; + } +} + +void vtape::dump() +{ + Dmsg0(dbglevel+1, "===================\n"); + Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block); + Dmsg1(dbglevel+1, "last_file=%i\n", last_file); + Dmsg1(dbglevel+1, "file_block=%i\n", file_block); + Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", + atEOF, atEOT, atEOD, atBOT); +} + diff --git a/bacula/src/stored/vtape.h b/bacula/src/stored/vtape.h new file mode 100644 index 0000000000..0bef45fe00 --- /dev/null +++ b/bacula/src/stored/vtape.h @@ -0,0 +1,109 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation, which is + listed in the file LICENSE. + + 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. + + Bacula® is a registered trademark of John Walker. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ +/* + * vtape.h - Emulate the Linux st (scsi tape) driver on file. + * for regression and bug hunting purpose + * + */ + +#ifndef VTAPE_H +#define VTAPE_H + +#include +#include +#include "bacula.h" + +#define FTAPE_MAX_DRIVE 50 + +/* + * Theses functions will replace open/read/write + */ +int vtape_open(const char *pathname, int flags, ...); +int vtape_read(int fd, void *buffer, unsigned int count); +int vtape_write(int fd, const void *buffer, unsigned int count); +int vtape_close(int fd); +int vtape_ioctl(int fd, unsigned long int request, ...); +void vtape_debug(int level); + +typedef enum { + VT_READ_EOF, /* Need to read the entire EOF struct */ + VT_SKIP_EOF /* Have already read the EOF byte */ +} VT_READ_FM_MODE; + +class vtape { +private: + int fd; /* Our file descriptor */ + + off_t file_block; /* size */ + off_t max_block; + + off_t last_FM; /* last file mark (last file) */ + off_t next_FM; /* next file mark (next file) */ + off_t cur_FM; /* current file mark */ + + bool atEOF; /* End of file */ + bool atEOT; /* End of media */ + bool atEOD; /* End of data */ + bool atBOT; /* Begin of tape */ + bool online; /* volume online */ + bool needEOF; /* check if last operation need eof */ + + int32_t last_file; /* last file of the volume */ + int32_t current_file; /* current position */ + int32_t current_block; /* current position */ + + void destroy(); + int offline(); + int truncate_file(); + void check_eof() { if(needEOF) weof();}; + void update_pos(); + bool read_fm(VT_READ_FM_MODE readfirst); + +public: + int fsf(); + int fsr(int count); + int weof(); + int bsf(); + int bsr(int count); + + vtape(); + ~vtape(); + + int get_fd(); + void dump(); + int open(const char *pathname, int flags); + int read(void *buffer, unsigned int count); + int write(const void *buffer, unsigned int count); + int close(); + + int tape_op(struct mtop *mt_com); + int tape_get(struct mtget *mt_com); + int tape_pos(struct mtpos *mt_com); +}; + +#endif /* !VTAPE_H */ diff --git a/bacula/technotes-2.5 b/bacula/technotes-2.5 index 11cd95b8d9..fb27fd1124 100644 --- a/bacula/technotes-2.5 +++ b/bacula/technotes-2.5 @@ -25,6 +25,7 @@ Add long term statistics job table General: 09Jun08 +ebl Rename faketape to vtape. ebl Update faketape driver. 08Jun08 ebl Modify faketape driver to avoid sparse file.