Switzerland, email:ftf@fsfeurope.org.
*/
+/*
+ * Maximum File Size = 800M
+ *
+ */
+
#include "faketape.h"
#include <dirent.h>
#include <sys/mtio.h>
#include <ctype.h>
-static int tape_get(int fd, struct mtget *mt_get);
-static int tape_op(int fd, struct mtop *mt_com);
-static int tape_pos(int fd, struct mtpos *mt_pos);
-
-static int dbglevel = 0;
-
-
+static int dbglevel = 10;
+#define FILE_OFFSET 30
faketape *ftape_list[FTAPE_MAX_DRIVE];
static faketape *get_tape(int fd)
return true;
}
-int faketape_open(const char *pathname, int flags, int mode)
+void faketape_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)
{
ASSERT(pathname != NULL);
int fd;
faketape *tape = new faketape();
- fd = tape->open(pathname, flags, mode);
+ fd = tape->open(pathname, flags);
if (fd > 0) {
put_tape(tape);
}
va_start(argp, request);
-// switch (request) {
-// case MTIOCTOP:
- result = t->tape_op(va_arg(argp, mtop *));
-// break;
-
-// case MTIOCGET:
-// result = tape_get(fd, va_arg(argp, mtget *));
-// break;
+ if (request == MTIOCTOP) {
+ result = t->tape_op(va_arg(argp, mtop *));
+ } else if (request == MTIOCGET) {
+ result = t->tape_get(va_arg(argp, mtget *));
+ }
//
// case MTIOCPOS:
// result = tape_pos(fd, va_arg(argp, mtpos *));
// break;
//
-// default:
-// errno = ENOTTY;
-// result = -1;
-// break;
-// }
-
+ else {
+ errno = ENOTTY;
+ result = -1;
+ }
va_end(argp);
return result;
}
+/****************************************************************/
+
int faketape::tape_op(struct mtop *mt_com)
{
int result=0;
break;
case MTFSF: /* Forward space over mt_count filemarks. */
+ result = fsf(mt_com->mt_count);
break;
case MTBSF: /* Backward space over mt_count filemarks. */
- current_file = current_file - mt_com->mt_count;
- if (current_file < 0) {
- current_file = 0;
- errno = EIO;
- result = -1;
- }
- atEOD = false;
- atEOF = false;
- atBOT = false;
- open_file();
+ result = bsf(mt_com->mt_count);
break;
case MTFSR: /* Forward space over mt_count records (tape blocks). */
block number = 0
*/
/* tester si on se trouve a la fin du fichier */
- lseek(cur_fd, SEEK_CUR, tape_info.block_size*mt_com->mt_count);
+ result = fsr(mt_com->mt_count);
break;
case MTBSR: /* Backward space over mt_count records (tape blocks). */
- result = -1;
+ result = bsr(mt_com->mt_count);
break;
case MTWEOF: /* Write mt_count filemarks. */
- weof(mt_com->mt_count);
+ result = weof(mt_com->mt_count);
break;
case MTREW: /* Rewind. */
- close_file();
+ Dmsg0(dbglevel, "rewind faketape\n");
atEOF = atEOD = false;
atBOT = true;
current_file = 0;
current_block = 0;
+ seek_file();
break;
case MTOFFL: /* put tape offline */
block number = -1
*/
/* Can be at EOM */
- atEOF = true;
- current_block = -1;
- /* Ne pas creer le fichier si on est a la fin */
+ atBOT = false;
+ atEOF = false;
+ atEOD = true;
+ atEOT = false;
+ current_file = last_file;
+ current_block = -1;
+ seek_file();
break;
- case MTERASE:
+ case MTERASE: /* not used by bacula */
atEOD = true;
atEOF = false;
atEOT = false;
+
current_file = 0;
current_block = -1;
- delete_files(current_file);
+ seek_file();
+ truncate_file();
break;
case MTSETBLK:
int faketape::tape_get(struct mtget *mt_get)
{
- return 0;
-}
+ int density = 1;
+ int block_size = 1024;
-int faketape::tape_pos(struct mtpos *mt_pos)
-{
- return 0;
-}
+ mt_get->mt_type = MT_ISSCSI2;
+ mt_get->mt_blkno = current_block;
+ mt_get->mt_fileno = current_file;
-int faketape::delete_files(int startfile)
-{
- int cur,max=0;
- char *p;
- POOL_MEM tmp;
- DIR *fp_dir;
- struct dirent *dir;
- struct stat statp;
+ mt_get->mt_resid = -1;
+// pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
- Dmsg1(dbglevel, "delete_files %i\n", startfile);
+ /* TODO */
+ mt_get->mt_dsreg =
+ ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
+ ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
- fp_dir = opendir(this->volume);
- if (!fp_dir) {
- this->last_file=0;
- this->size = 0;
- return -1;
+
+ mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
+
+ if (atEOF) {
+ mt_get->mt_gstat |= 0x80000000; // GMT_EOF
}
- this->size = 0;
+ if (atBOT) {
+ mt_get->mt_gstat |= 0x40000000; // GMT_BOT
+ }
+ if (atEOT) {
+ mt_get->mt_gstat |= 0x20000000; // GMT_EOT
+ }
- /* search for all digit files
- * and we remove all ones that are greater than
- * startfile
- */
- while ((dir = readdir (fp_dir)) != NULL)
- {
- Mmsg(tmp, "%s/%s", this->volume, dir->d_name);
- cur = 0;
- /* check if d_name contains only digits */
- for(p = dir->d_name; *p && isdigit(*p); p++)
- {
- cur *= 10;
- cur += *p - '0';
- }
- if (!*p && cur > 0) {
- if (cur >= startfile) { /* remove it */
- unlink(tmp.c_str());
- } else {
- if (lstat(tmp.c_str(), &statp) == 0) {
- this->size += statp.st_size;
- }
- max = (max > cur)?max:cur;
- }
- }
+ if (atEOD) {
+ mt_get->mt_gstat |= 0x08000000; // GMT_EOD
}
- closedir(fp_dir);
- this->last_file = max;
- return max;
+ 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 faketape::tape_pos(struct mtpos *mt_pos)
+{
+ return 0;
+}
+
+/*
+ * This function try to emulate the append only behavior
+ * of a tape. When you wrote something, data after the
+ * current position are discarded.
+ */
+int faketape::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;
+ return 0;
}
faketape::faketape()
{
fd = -1;
- cur_fd = -1;
atEOF = false;
atBOT = false;
atEOT = false;
atEOD = false;
online = false;
-
- size = 0;
+ inplace = false;
+ needEOF = false;
+
+ file_size = 0;
last_file = 0;
current_file = 0;
current_block = -1;
- max_size = 10000000;
+ current_pos = 0;
+
+ max_block = 1024*1024*1024*1024*8;
volume = get_pool_memory(PM_NAME);
- cur_file = get_pool_memory(PM_NAME);
- cur_info = get_pool_memory(PM_NAME);
}
faketape::~faketape()
{
free_pool_memory(volume);
- free_pool_memory(cur_file);
- free_pool_memory(cur_info);
}
int faketape::get_fd()
return this->fd;
}
+/*
+ * TODO: regarder si apres un write une operation x pose un EOF
+ */
int faketape::write(const void *buffer, unsigned int count)
{
+ ASSERT(current_file >= 0);
+ ASSERT(count > 0);
+ ASSERT(buffer);
+
unsigned int nb;
- check_file();
+ Dmsg3(dbglevel, "write len=%i %i:%i\n", count, current_file,current_block);
if (atEOT) {
- return 0;
+ Dmsg0(dbglevel, "write nothing, EOT !\n");
+ errno = ENOSPC;
+ return -1;
}
- current_block++;
+ if (!inplace) {
+ seek_file();
+ }
+
+ 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 */
- /* TODO: remove all files > current_file and
- * remove blocks > current_block
- */
- if (count + size > max_size) {
- Dmsg2(dbglevel,
- "EOT writing only %i of %i requested\n",
- max_size - size, count);
- count = max_size - size;
- atEOT = true;
- }
+ 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;
+// }
- nb = ::write(cur_fd, buffer, count);
+ off_t size = count;
+ ::write(fd, &size, sizeof(off_t));
+ nb = ::write(fd, buffer, count);
+ file_size += sizeof(off_t) + nb;
+
if (nb != count) {
atEOT = true;
Dmsg2(dbglevel,
int faketape::weof(int count)
{
+ ASSERT(current_file >= 0);
+ Dmsg3(dbglevel, "Writing EOF %i:%i last=%i\n",
+ current_file, current_block,last_file);
if (atEOT) {
current_block = -1;
return -1;
}
+ needEOF = false;
+ truncate_file(); /* nothing after this point */
- count--; /* end this file */
- ftruncate(cur_fd, lseek(cur_fd, 0, SEEK_CUR));
- close_file();
- current_file++;
+ /* TODO: check this */
+ current_file += count;
+ current_block = 0;
- /* we erase all previous information */
- if (last_file > current_file) {
- delete_files(current_file);
- }
+ off_t c=0;
+ seek_file();
+ ::write(fd, &c, sizeof(off_t));
+ seek_file();
- for (; count>0 ; count--) {
- current_file++;
- open_file();
- }
- current_block=0;
- atEOF = true;
atEOD = false;
+ atBOT = false;
+ atEOF = true;
+
return 0;
}
int faketape::fsf(int count)
-{
-
+{
+ ASSERT(current_file >= 0);
+ ASSERT(fd >= 0);
/*
* 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
*/
+ check_eof();
+ int ret;
if (atEOT) {
current_block = -1;
return -1;
}
- close_file();
+ atBOT = atEOF = false;
+ Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
- if (current_file + count <= last_file) {
+ if ((current_file + count) <= last_file) {
current_file += count;
current_block = 0;
- return 0;
+ ret = 0;
} else {
- current_file = last_file;
+ Dmsg0(dbglevel, "Try to FSF after EOT\n");
+ current_file = last_file ;
+ current_block = -1;
+ atEOD=true;
+ ret = -1;
+ }
+ seek_file();
+// read_eof();
+ return ret;
+}
+
+int faketape::fsr(int count)
+{
+ ASSERT(current_file >= 0);
+ ASSERT(fd >= 0);
+
+ int i,nb, ret=0;
+ off_t where=0, s;
+ Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
+
+ check_eof();
+
+ if (atEOT) {
+ errno = EIO;
current_block = -1;
- atEOT=true;
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(off_t)); /* get size of next block */
+ if (nb == sizeof(off_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 (current_file < last_file) {
+ current_block = 0;
+ current_file++;
+ seek_file();
+ }
+ atEOF = true; /* stop the loop */
+ }
+ }
+
+ find_maxfile(); /* refresh stats */
+
+ if (where == file_size) {
+ atEOD = true;
+ }
+ return ret;
}
+// TODO: Make it working, at this time we get only the EOF position...
+int faketape::bsr(int count)
+{
+ Dmsg2(dbglevel, "bsr current_block=%i count=%i\n",
+ current_block, count);
+
+ ASSERT(current_file >= 0);
+ ASSERT(fd >= 0);
+
+ check_eof();
+
+ ASSERT(count == 1);
+
+ if (!count) {
+ return 0;
+ }
+
+ int ret=0;
+ int last_f=0;
+ int last_b=0;
+
+ off_t last=-1;
+ off_t orig = lseek(fd, 0, SEEK_CUR);
+
+ current_block=0;
+ seek_file();
+
+ do {
+ if (!atEOF) {
+ 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);
+ }
+ ret = fsr(1);
+ } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
+
+ if (last > 0) {
+ lseek(fd, last, SEEK_SET);
+ current_file = last_f;
+ current_block = last_b;
+ Dmsg3(dbglevel, "set offset=%lli %i:%i\n",
+ last, current_file, current_block);
+ }
+
+ Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
+ atEOT = atEOF = atEOD = false;
+
+ return 0;
+}
+
+//int faketape::read_eof()
+//{
+// int s, nb;
+// off_t old = lseek(fd, 0, SEEK_CUR);
+// nb = ::read(fd, &s, sizeof(s));
+// if (nb >= 0 && (nb != sizeof(s) || !s)) { /* EOF */
+// atEOF = true;
+// }
+// lseek(fd, old, SEEK_SET);
+// return 0;
+//}
+
int faketape::bsf(int count)
{
+ ASSERT(current_file >= 0);
+ Dmsg3(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block, count);
+ int ret = 0;
-/*
- * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
- */
- close_file();
- atEOT = atEOD = false;
+ check_eof();
+ atBOT = atEOF = atEOT = atEOD = false;
if (current_file - count < 0) {
current_file = 0;
current_block = 0;
atBOT = true;
- return -1;
- }
-
- current_file = current_file - count;
- current_block = 0;
- if (!current_file) {
- atBOT = true;
+ errno = EIO;
+ ret = -1;
+ } else {
+ current_file = current_file - count + 1;
+ current_block = -1;
+ seek_file();
+ current_file--;
+ /* go just before last EOF */
+ lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(off_t), SEEK_SET);
}
-
- return 1;
+ return ret;
}
/*
*/
int faketape::offline()
{
- ASSERT(cur_fd > 0);
-
- close_file();
-
- cur_fd = -1;
- atEOF = false;
- atEOT = false;
- atEOD = false;
- atBOT = false;
+ check_eof();
+ close();
+
+ atEOF = false; /* End of file */
+ atEOT = false; /* End of tape */
+ atEOD = false; /* End of data */
+ atBOT = false; /* Begin of tape */
current_file = -1;
current_block = -1;
- last_file = 0;
- return 0;
-}
-
-int faketape::close_file()
-{
- if (cur_fd > 0) {
- ::close(cur_fd);
- cur_fd = -1;
- }
+ last_file = -1;
return 0;
}
int faketape::close()
{
- close_file();
+ check_eof();
::close(fd);
-
+ fd = -1;
return 0;
}
/*
int faketape::read(void *buffer, unsigned int count)
{
+ ASSERT(current_file >= 0);
unsigned int nb;
- check_file();
+ off_t s;
if (atEOT || atEOD) {
return 0;
}
+ if (!inplace) {
+ seek_file();
+ }
+
+ Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
+
atBOT = false;
+ current_block++;
- nb = ::read(cur_fd, buffer, count);
- if (count != nb) {
+ nb = ::read(fd, &s, sizeof(off_t));
+ if (nb <= 0) {
+ atEOF = true;
+ return 0;
+ }
+ if (s > count) { /* not enough buffer to read block */
+ lseek(fd, s, SEEK_CUR);
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!s) { /* EOF */
+ atEOF = true;
+ lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(off_t), SEEK_SET);
+ return 0;
+ }
+ nb = ::read(fd, buffer, s);
+ if (s != nb) {
atEOF = true;
if (current_file == last_file) {
atEOD = true;
return nb;
}
-int faketape::read_volinfo()
+int faketape::open(const char *pathname, int uflags)
{
- struct stat statp;
- memset(&tape_info, 0, sizeof(FTAPE_FORMAT));
-
- Dmsg2(0, "read_volinfo %p %p\n", cur_info, volume);
- Mmsg(cur_info, "%s/info", volume);
- fd = ::open(cur_info, O_CREAT | O_RDWR | O_BINARY, 0640);
-
- if (fd < 0) {
- return -1;
- }
-
- fstat(fd, &statp);
-
- /* read volume info */
- int nb = ::read(fd, &tape_info, sizeof(FTAPE_FORMAT));
- if (nb != sizeof(FTAPE_FORMAT)) { /* new tape ? */
- Dmsg1(dbglevel, "Initialize %s\n", volume);
- tape_info.version = 1;
- tape_info.block_max = 2000000;
- tape_info.block_size = statp.st_blksize;
- tape_info.max_file_mark = 2000;
-
- lseek(fd, SEEK_SET, 0);
- nb = ::write(fd, &tape_info, sizeof(FTAPE_FORMAT));
-
- if (nb != sizeof(FTAPE_FORMAT)) {
- ::close(fd);
- return -1;
- }
- }
-
- Dmsg0(dbglevel, "read_volinfo OK\n");
- find_maxfile();
-
- return fd;
-}
-
-int faketape::open(const char *pathname, int uflags, int umode)
-{
- Dmsg3(dbglevel, "faketape::open(%s, %i, %i)\n", pathname, uflags, umode);
+ Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
pm_strcpy(volume, pathname);
struct stat statp;
- if (lstat(volume, &statp) != 0) {
+ if (stat(volume, &statp) != 0) {
Dmsg1(dbglevel, "Can't stat on %s\n", volume);
return -1;
}
- if (!S_ISDIR(statp.st_mode)) {
- Dmsg1(dbglevel, "%s is not a directory\n", volume);
- errno = EACCES;
+ fd = ::open(pathname, O_CREAT | O_RDWR, 0700);
+ if (fd < 0) {
return -1;
}
/* open volume descriptor and get this->fd */
- if (read_volinfo() < 0) {
+ if (find_maxfile() < 0) {
return -1;
}
- current_block=-1;
+ current_block = 0;
+ current_file = 0;
+ needEOF = false;
+ online = inplace = true;
+ atBOT = true;
+ atEOT = atEOD = false;
return fd;
}
+/*
+ * read volume to get the last file number
+ */
int faketape::find_maxfile()
{
- int max=0;
- int cur;
- char *p;
- POOL_MEM tmp;
- DIR *fp_dir;
- struct dirent *dir;
struct stat statp;
-
- fp_dir = opendir(this->volume);
- if (!fp_dir) {
- last_file=0;
- return -1;
- }
-
- this->size = 0;
-
- /* search for all digit file */
- while ((dir = readdir (fp_dir)) != NULL)
- {
- Mmsg(tmp, "%s/%s", this->volume, dir->d_name);
- if (lstat(tmp.c_str(), &statp) == 0) {
- this->size += statp.st_size;
- } else {
- Dmsg1(dbglevel, "Can't stat %s\n", tmp.c_str());
- }
- cur = 0;
- /* TODO: compute size */
- for(p = dir->d_name; *p && isdigit(*p); p++)
- {
- cur *= 10;
- cur += *p;
- }
- if (!*p && cur) {
- max = (max > cur)?max:cur;
- }
+ if (fstat(fd, &statp) != 0) {
+ return 0;
}
-
- closedir(fp_dir);
- this->last_file = max;
- return max;
+ last_file = statp.st_size>>FILE_OFFSET;
+ file_size = statp.st_size;
+
+ current_pos = lseek(fd, 0, SEEK_CUR); /* get current position */
+ Dmsg3(dbglevel+1, "last_file=%i file_size=%u current_pos=%i\n",
+ last_file, file_size, current_pos);
+
+ return last_file;
}
-int faketape::open_file()
+int faketape::seek_file()
{
ASSERT(current_file >= 0);
- if (cur_fd > 0) {
- ::close(cur_fd);
- }
+ Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
- Mmsg(cur_file, "%s/%i", volume, current_file);
- cur_fd = ::open(cur_file, O_CREAT | O_RDWR | O_BINARY, 0640);
- if (cur_fd < 0) {
+ off_t pos = ((off_t)current_file)<<FILE_OFFSET;
+ if(lseek(fd, pos, SEEK_SET) == -1) {
return -1;
}
- current_block = 0;
+ if (current_block > 0) {
+ fsr(current_block);
+ }
last_file = (last_file > current_file)?last_file:current_file;
+ inplace = true;
- Dmsg1(dbglevel, "open_file %s %i\n", cur_file);
-
- return cur_fd;
-
+ return 0;
}
void faketape::dump()
{
- Dmsg0(dbglevel, "===================\n");
- Dmsg1(dbglevel, "last_file=%i\n", last_file);
- Dmsg1(dbglevel, "current_file=%i\n", current_file);
- Dmsg1(dbglevel, "volume=%s\n", volume);
- Dmsg1(dbglevel, "cur_file=%s\n", cur_file);
- Dmsg1(dbglevel, "size=%i\n", size);
- Dmsg1(dbglevel, "EOF=%i\n", atEOF);
- Dmsg1(dbglevel, "EOT=%i\n", atEOT);
- Dmsg1(dbglevel, "EOD=%i\n", atEOD);
+ 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, "volume=%s\n", volume);
+ Dmsg1(dbglevel+1, "file_size=%i\n", file_size);
+ Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n",
+ atEOF, atEOT, atEOD, atBOT);
}
/****************************************************************
int main()
{
int fd;
+ char buf[500];
printf("Starting FakeTape\n");
- mkdir("/tmp/fake", 0777);
- fd = faketape_open("/tmp/fake", O_CREAT | O_RDWR | O_BINARY, 0666);
- if (fd < 0) {
- perror("open ERR");
- return 1;
- }
+ mkdir("/tmp/fake", 0700);
+
+
- faketape *tape = get_tape(fd);
- tape->write("test", strlen("test")+1);
- tape->write("test", strlen("test")+1);
- tape->dump();
-
- tape->weof(1);
- tape->write("test", strlen("test")+1);
- tape->write("test", strlen("test")+1);
- tape->dump();
- tape->fsf(2);
- tape->dump();
- faketape_close(fd);
return 0;
}