#include "bacula.h" /* define 64bit file usage */
#include "stored.h"
-#ifdef USE_FAKETAPE
-#include "faketape.h"
+#include "vtape.h"
-static int dbglevel = 10;
+static int dbglevel = 100;
#define FILE_OFFSET 30
-faketape *ftape_list[FTAPE_MAX_DRIVE];
+vtape *ftape_list[FTAPE_MAX_DRIVE];
-static faketape *get_tape(int fd)
+static vtape *get_tape(int fd)
{
ASSERT(fd >= 0);
return ftape_list[fd];
}
-static bool put_tape(faketape *ftape)
+static bool put_tape(vtape *ftape)
{
ASSERT(ftape != NULL);
return true;
}
-void faketape_debug(int level)
+void vtape_debug(int level)
{
dbglevel = level;
}
/* theses function will replace open/read/write/close/ioctl
* in bacula core
*/
-int faketape_open(const char *pathname, int flags)
+int vtape_open(const char *pathname, int flags, ...)
{
ASSERT(pathname != NULL);
int fd;
- faketape *tape = new faketape();
+ vtape *tape = new vtape();
fd = tape->open(pathname, flags);
if (fd > 0) {
put_tape(tape);
return fd;
}
-int faketape_read(int fd, void *buffer, unsigned int count)
+int vtape_read(int fd, void *buffer, unsigned int count)
{
- faketape *tape = get_tape(fd);
+ vtape *tape = get_tape(fd);
ASSERT(tape != NULL);
return tape->read(buffer, count);
}
-int faketape_write(int fd, const void *buffer, unsigned int count)
+int vtape_write(int fd, const void *buffer, unsigned int count)
{
- faketape *tape = get_tape(fd);
+ vtape *tape = get_tape(fd);
ASSERT(tape != NULL);
return tape->write(buffer, count);
}
-int faketape_close(int fd)
+int vtape_close(int fd)
{
- faketape *tape = get_tape(fd);
+ vtape *tape = get_tape(fd);
ASSERT(tape != NULL);
tape->close();
delete tape;
return 0;
}
-int faketape_ioctl(int fd, unsigned long int request, ...)
+int vtape_ioctl(int fd, unsigned long int request, ...)
{
va_list argp;
int result=0;
- faketape *t = get_tape(fd);
+ vtape *t = get_tape(fd);
if (!t) {
errno = EBADF;
return -1;
/****************************************************************/
-int faketape::tape_op(struct mtop *mt_com)
+int vtape::tape_op(struct mtop *mt_com)
{
int result=0;
+ int count = mt_com->mt_count;
if (!online) {
errno = ENOMEDIUM;
break;
case MTFSF: /* Forward space over mt_count filemarks. */
- result = fsf(mt_com->mt_count);
+ do {
+ result = fsf();
+ } while (--count > 0 && result == 0);
break;
case MTBSF: /* Backward space over mt_count filemarks. */
- result = bsf(mt_com->mt_count);
+ do {
+ result = bsf();
+ } while (--count > 0 && result == 0);
break;
case MTFSR: /* Forward space over mt_count records (tape blocks). */
break;
case MTWEOF: /* Write mt_count filemarks. */
- result = weof(mt_com->mt_count);
+ do {
+ result = weof();
+ } while (result == 0 && --count > 0);
break;
case MTREW: /* Rewind. */
- Dmsg0(dbglevel, "rewind faketape\n");
+ Dmsg0(dbglevel, "rewind vtape\n");
+ check_eof();
atEOF = atEOD = false;
atBOT = true;
current_file = 0;
current_block = 0;
- seek_file();
+ lseek(fd, 0, SEEK_SET);
+ result = !read_fm(VT_READ_EOF);
break;
case MTOFFL: /* put tape offline */
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 */
- atBOT = false;
- atEOF = false;
- atEOD = true;
- atEOT = false;
-
- current_file = last_file;
- current_block = -1;
- seek_file();
break;
case MTERASE: /* not used by bacula */
current_file = 0;
current_block = -1;
- seek_file();
+ lseek(fd, 0, SEEK_SET);
+ read_fm(VT_READ_EOF);
truncate_file();
break;
return result == 0 ? 0 : -1;
}
-int faketape::tape_get(struct mtget *mt_get)
+int vtape::tape_get(struct mtget *mt_get)
{
int density = 1;
int block_size = 1024;
return 0;
}
-int faketape::tape_pos(struct mtpos *mt_pos)
+int vtape::tape_pos(struct mtpos *mt_pos)
{
if (current_block >= 0) {
mt_pos->mt_blkno = current_block;
* of a tape. When you wrote something, data after the
* current position are discarded.
*/
-int faketape::truncate_file()
+int vtape::truncate_file()
{
Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block);
ftruncate(fd, lseek(fd, 0, SEEK_CUR));
return 0;
}
-faketape::faketape()
+vtape::vtape()
{
fd = -1;
atEOT = false;
atEOD = false;
online = false;
- inplace = false;
needEOF = false;
file_block = 0;
current_file = 0;
current_block = -1;
- max_block = 2*1024*100; /* 100MB */
+ max_block = 2*1024*2048; /* 2GB */
}
-faketape::~faketape()
+vtape::~vtape()
{
}
-int faketape::get_fd()
+int vtape::get_fd()
{
return this->fd;
}
/*
* TODO: check if after a write op, and other tape op put a EOF
*/
-int faketape::write(const void *buffer, unsigned int count)
+int vtape::write(const void *buffer, unsigned int count)
{
ASSERT(online);
ASSERT(current_file >= 0);
ASSERT(buffer);
unsigned int nb;
- Dmsg3(dbglevel, "write len=%i %i:%i\n", count, current_file,current_block);
+ Dmsg3(dbglevel*2, "write len=%i %i:%i\n",
+ count, current_file,current_block);
if (atEOT) {
Dmsg0(dbglevel, "write nothing, EOT !\n");
return -1;
}
- if (!inplace) {
- seek_file();
- }
-
if (!atEOD) { /* if not at the end of the data */
truncate_file();
}
needEOF = true; /* next operation need EOF mark */
-// if ((count + file_size) > max_size) {
-// Dmsg2(dbglevel,
-// "EOT writing only %i of %i requested\n",
-// max_size - file_size, count);
-// count = max_size - file_size;
-// atEOT = true;
-// }
-
uint32_t size = count;
::write(fd, &size, sizeof(uint32_t));
nb = ::write(fd, buffer, count);
return nb;
}
-int faketape::weof(int count)
+/*
+ * +---+---------+---+------------------+---+-------------------+
+ * |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);
- Dmsg3(dbglevel, "Writing EOF %i:%i last=%i\n",
- current_file, current_block,last_file);
+
if (atEOT) {
errno = ENOSPC;
current_block = -1;
return -1;
}
- needEOF = false;
- truncate_file(); /* nothing after this point */
- /* TODO: check this */
- current_file += count;
- current_block = 0;
+ 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;
- seek_file();
- ::write(fd, &c, sizeof(uint32_t));
- seek_file();
+ ::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;
- update_pos();
+ 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;
}
-int faketape::fsf(int count)
+/*
+ * Go to next FM
+ */
+int vtape::fsf()
{
ASSERT(online);
ASSERT(current_file >= 0);
/*
* 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
*/
- check_eof();
- int ret;
- if (atEOT) {
+ int ret=0;
+ if (atEOT || atEOD) {
+ errno = EIO;
current_block = -1;
return -1;
}
- atBOT = atEOF = false;
- Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
+ atBOT = false;
+ Dmsg2(dbglevel+1, "fsf %i <= %i\n", current_file, last_file);
- if ((current_file + count) <= last_file) {
- current_file += count;
- current_block = 0;
+ 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 {
+
+ } 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;
}
- seek_file();
return ret;
}
-int faketape::fsr(int count)
+/* /------------\ /---------------\
+ * +---+------+---+---------------+-+
+ * |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);
int i,nb, ret=0;
off_t where=0;
uint32_t s;
- Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
+ Dmsg4(dbglevel, "fsr %i:%i EOF=%i c=%i\n",
+ current_file,current_block,atEOF,count);
check_eof();
current_file, current_block, nb,s);
errno = EIO;
ret = -1;
- if (current_file < last_file) {
- current_block = 0;
+ if (next_FM) {
current_file++;
- seek_file();
- }
+ read_fm(VT_SKIP_EOF);
+ }
atEOF = true; /* stop the loop */
}
}
- find_maxfile(); /* refresh stats */
-
return ret;
}
-int faketape::bsr(int count)
+/*
+ * BSR + EOF => begin of EOF + EIO
+ * BSR + BSR + EOF => last block
+ * current_block = -1
+ */
+int vtape::bsr(int count)
{
- Dmsg2(dbglevel, "bsr current_block=%i count=%i\n",
- current_block, count);
-
ASSERT(online);
ASSERT(current_file >= 0);
ASSERT(count == 1);
int orig_f = current_file;
int orig_b = current_block;
- current_block=0;
- seek_file();
+ 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;
+ last2 = last; /* keep track of the 2 last blocs position */
last = lseek(fd, 0, SEEK_CUR);
last_f = current_file;
last_b = current_block;
- Dmsg5(dbglevel, "EOF=%i last=%lli orig=%lli %i:%i\n",
- atEOF, last, orig, current_file, 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));
lseek(fd, last2, SEEK_SET);
current_file = last_f;
current_block = last_b - 1;
- Dmsg3(dbglevel, "set offset2=%lli %i:%i\n",
+ 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, "set offset=%lli %i:%i\n",
+ Dmsg3(dbglevel, "2 set offset=%lli %i:%i\n",
last, current_file, current_block);
} else {
lseek(fd, orig, SEEK_SET);
}
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;
}
-int faketape::bsf(int count)
+/* BSF => just before last EOF
+ * EOF + BSF => just before EOF
+ * file 0 + BSF => BOT + errno
+ */
+int vtape::bsf()
{
ASSERT(online);
ASSERT(current_file >= 0);
- Dmsg3(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block, count);
+ 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 - count < 0) {
+ 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 {
- current_file = current_file - count + 1;
- current_block = -1;
- seek_file();
+ Dmsg1(dbglevel, "bsf last=%lli\n", last_FM);
+ lseek(fd, cur_FM, SEEK_SET);
current_file--;
- /* go just before last EOF */
- lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
+ current_block=-1;
}
return ret;
}
/*
- * Put faketape in offline mode
+ * Put vtape in offline mode
*/
-int faketape::offline()
+int vtape::offline()
{
close();
atBOT = false; /* Begin of tape */
online = false;
- file_size = 0;
+ file_block = 0;
current_file = -1;
current_block = -1;
last_file = -1;
return 0;
}
-int faketape::close()
+/* 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;
}
+
/*
- **rb
- **status
- * EOF Bacula status: file=2 block=0
- * Device status: EOF ONLINE IM_REP_EN file=2 block=0
- **rb
- **status
- * EOD EOF Bacula status: file=2 block=0
- * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
- *
+ * 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 faketape::read(void *buffer, unsigned int count)
+int vtape::read(void *buffer, unsigned int count)
{
ASSERT(online);
ASSERT(current_file >= 0);
unsigned int nb;
uint32_t s;
- Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
+ Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block);
if (atEOT || atEOD) {
- return 0;
+ errno = EIO;
+ return -1;
}
if (atEOF) {
- current_file++;
- current_block=0;
- inplace = false;
- atEOF = false;
+ if (!next_FM) {
+ atEOD = true;
+ atEOF = false;
+ current_block=-1;
+ return 0;
+ }
+ atEOF=false;
}
- if (!inplace) {
- seek_file();
- }
+ check_eof();
atEOD = atBOT = false;
- current_block++;
+ /* reading size of data */
nb = ::read(fd, &s, sizeof(uint32_t));
if (nb <= 0) {
- atEOF = true;
+ 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;
- lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
+ if (read_fm(VT_SKIP_EOF)) {
+ current_file++;
+ }
+
return 0;
}
+
+ /* reading data itself */
nb = ::read(fd, buffer, s);
- if (s != nb) {
- atEOF = true;
- if (current_file == last_file) {
- atEOD = true;
- current_block = -1;
- }
- Dmsg0(dbglevel, "EOF during reading\n");
+ 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 faketape::open(const char *pathname, int uflags)
+int vtape::open(const char *pathname, int uflags)
{
- Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
+ Dmsg2(dbglevel, "vtape::open(%s, %i)\n", pathname, uflags);
online = true; /* assume that drive contains a tape */
return -1;
}
- /* open volume descriptor and get this->fd */
- find_maxfile();
-
file_block = 0;
current_block = 0;
current_file = 0;
+ cur_FM = next_FM = last_FM = 0;
needEOF = false;
- inplace = true;
atBOT = true;
atEOT = atEOD = false;
- return fd;
-}
-
-/*
- * read volume to get the last file number
- */
-int faketape::find_maxfile()
-{
- struct stat statp;
- if (fstat(fd, &statp) != 0) {
- return 0;
+ /* If the vtape is empty, start by writing a EOF */
+ if (online && !read_fm(VT_READ_EOF)) {
+ weof();
+ last_file = current_file=0;
}
- last_file = statp.st_size>>FILE_OFFSET;
-
- Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
- return last_file;
+ return fd;
}
-void faketape::update_pos()
+/* use this to track file usage */
+void vtape::update_pos()
{
ASSERT(online);
struct stat statp;
file_block = statp.st_blocks;
}
+ Dmsg1(dbglevel+1, "update_pos=%i\n", file_block);
+
if (file_block > max_block) {
atEOT = true;
} else {
}
}
-int faketape::seek_file()
-{
- ASSERT(online);
- ASSERT(current_file >= 0);
- Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
-
- off_t pos = ((off_t)current_file)<<FILE_OFFSET;
- if(lseek(fd, pos, SEEK_SET) == -1) {
- return -1;
- }
- last_file = MAX(last_file, current_file);
- if (current_block > 0) {
- fsr(current_block);
- }
- inplace = true;
-
- return 0;
-}
-
-void faketape::dump()
+void vtape::dump()
{
Dmsg0(dbglevel+1, "===================\n");
Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
atEOF, atEOT, atEOD, atBOT);
}
-#endif /* USE_FAKETAPE */