2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Please note!!! The VTAPE device is for testing only.
22 * It simulates a tape drive, which is useful for testing
23 * without a real drive, but is inefficient for writing
24 * disk volumes. In addition, we do not test for every
25 * possible error condition, so please do not use this
32 Maximum File Size = 800M
33 Maximum Volume Size = 3G
35 Archive Device = /tmp/fake
37 AutomaticMount = yes; # when device opened, read it
59 #include "bacula.h" /* define 64bit file usage */
65 static int dbglevel = 100;
66 #define FILE_OFFSET 30
68 void vtape_debug(int level)
73 /* DEVICE virtual that we redefine. */
74 int vtape::d_ioctl(int fd, ioctl_req_t request, char *op)
78 if (request == MTIOCTOP) {
79 result = tape_op((mtop *)op);
80 } else if (request == MTIOCGET) {
81 result = tape_get((mtget *)op);
82 } else if (request == MTIOCPOS) {
83 result = tape_pos((mtpos *)op);
92 int vtape::tape_op(struct mtop *mt_com)
95 int count = mt_com->mt_count;
102 switch (mt_com->mt_op)
118 case MTFSF: /* Forward space over mt_count filemarks. */
121 } while (--count > 0 && result == 0);
124 case MTBSF: /* Backward space over mt_count filemarks. */
127 } while (--count > 0 && result == 0);
130 case MTFSR: /* Forward space over mt_count records (tape blocks). */
138 mt: /dev/lto2: Erreur d'entree/sortie
143 /* tester si on se trouve a la fin du fichier */
144 result = fsr(mt_com->mt_count);
147 case MTBSR: /* Backward space over mt_count records (tape blocks). */
148 result = bsr(mt_com->mt_count);
151 case MTWEOF: /* Write mt_count filemarks. */
154 } while (result == 0 && --count > 0);
157 case MTREW: /* Rewind. */
158 Dmsg0(dbglevel, "rewind vtape\n");
160 atEOF = atEOD = false;
164 lseek(fd, 0, SEEK_SET);
165 result = !read_fm(VT_READ_EOF);
168 case MTOFFL: /* put tape offline */
169 result = offline(NULL) ? 0 : -1;
172 case MTRETEN: /* Re-tension tape. */
176 case MTBSFM: /* not used by bacula */
181 case MTFSFM: /* not used by bacula */
186 case MTEOM:/* Go to the end of the recorded media (for appending files). */
188 lseek(fd, next_FM, SEEK_SET);
189 if (read_fm(VT_READ_EOF)) {
194 while (::read(fd, &l, sizeof(l)) > 0) {
196 lseek(fd, l, SEEK_CUR);
200 Dmsg0(dbglevel, "skip 1 block\n");
213 case MTERASE: /* not used by bacula */
220 lseek(fd, 0, SEEK_SET);
221 read_fm(VT_READ_EOF);
265 return result == 0 ? 0 : -1;
268 int vtape::tape_get(struct mtget *mt_get)
271 int block_size = 1024;
273 mt_get->mt_type = MT_ISSCSI2;
274 mt_get->mt_blkno = current_block;
275 mt_get->mt_fileno = current_file;
277 mt_get->mt_resid = -1;
278 // pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
282 ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
283 ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
286 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
289 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
293 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
296 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
300 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
303 if (0) { //WriteProtected) {
304 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
308 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
310 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
312 mt_get->mt_erreg = 0;
317 int vtape::tape_pos(struct mtpos *mt_pos)
319 if (current_block >= 0) {
320 mt_pos->mt_blkno = current_block;
328 * This function try to emulate the append only behavior
329 * of a tape. When you wrote something, data after the
330 * current position are discarded.
332 int vtape::truncate_file()
334 Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block);
335 ftruncate(fd, lseek(fd, 0, SEEK_CUR));
336 last_file = current_file;
364 max_block = VTAPE_MAX_BLOCK;
373 * DEVICE virtual that we redefine.
375 * Write a variable block of count size.
376 * block = vtape_header + data
377 * vtape_header = sizeof(data)
378 * if vtape_header == 0, this is a EOF
380 ssize_t vtape::d_write(int, const void *buffer, size_t count)
383 ASSERT(current_file >= 0);
388 Dmsg3(dbglevel*2, "write len=%i %i:%i\n",
389 count, current_file,current_block);
392 Dmsg0(dbglevel, "write nothing, EOT !\n");
397 if (!atEOD) { /* if not at the end of the data */
401 if (current_block != -1) {
407 atEOD = true; /* End of data */
409 needEOF = true; /* next operation need EOF mark */
411 uint32_t size = count;
412 ::write(fd, &size, sizeof(uint32_t));
413 nb = ::write(fd, buffer, count);
415 if (nb != (ssize_t)count) {
418 "Not enough space writing only %i of %i requested\n",
428 * +---+---------+---+------------------+---+-------------------+
429 * |00N| DATA |0LN| DATA |0LC| DATA |
430 * +---+---------+---+------------------+---+-------------------+
433 * L : Last FileMark offset
434 * N : Next FileMark offset
435 * C : Current FileMark Offset
440 ASSERT(current_file >= 0);
451 truncate_file(); /* nothing after this point */
455 cur_FM = lseek(fd, 0, SEEK_CUR); // current position
457 /* update previous next_FM */
458 lseek(fd, last_FM + sizeof(uint32_t)+sizeof(boffset_t), SEEK_SET);
459 ::write(fd, &cur_FM, sizeof(boffset_t));
460 lseek(fd, cur_FM, SEEK_SET);
465 ::write(fd, &c, sizeof(uint32_t)); // EOF
466 ::write(fd, &last_FM, sizeof(last_FM)); // F-1
467 ::write(fd, &next_FM, sizeof(next_FM)); // F (will be updated next time)
477 last_file = MAX(current_file, last_file);
479 Dmsg4(dbglevel, "Writing EOF %i:%i last=%lli cur=%lli next=0\n",
480 current_file, current_block, last_FM, cur_FM);
491 ASSERT(current_file >= 0);
494 * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
498 if (atEOT || atEOD) {
505 Dmsg2(dbglevel+1, "fsf %i <= %i\n", current_file, last_file);
507 if (next_FM > cur_FM) { /* not the last file */
508 lseek(fd, next_FM, SEEK_SET);
509 read_fm(VT_READ_EOF);
514 } else if (atEOF) { /* last file mark */
520 } else { /* last file, but no at the end */
523 Dmsg0(dbglevel, "Try to FSF after EOT\n");
525 current_file = last_file ;
533 /* /------------\ /---------------\
534 * +---+------+---+---------------+-+
536 * +---+------+---+---------------+-+
539 bool vtape::read_fm(VT_READ_FM_MODE read_all)
543 if (read_all == VT_READ_EOF) {
544 ::read(fd, &c, sizeof(c));
546 lseek(fd, cur_FM, SEEK_SET);
551 cur_FM = lseek(fd, 0, SEEK_CUR) - sizeof(c);
553 ::read(fd, &last_FM, sizeof(last_FM));
554 ret = ::read(fd, &next_FM, sizeof(next_FM));
558 Dmsg3(dbglevel, "Read FM cur=%lli last=%lli next=%lli\n",
559 cur_FM, last_FM, next_FM);
561 return (ret == sizeof(next_FM));
565 * TODO: Check fsr with EOF
567 int vtape::fsr(int count)
570 ASSERT(current_file >= 0);
574 // boffset_t where=0;
576 Dmsg4(dbglevel, "fsr %i:%i EOF=%i c=%i\n",
577 current_file,current_block,atEOF,count);
592 atBOT = atEOF = false;
594 /* check all block record */
595 for(i=0; (i < count) && !atEOF ; i++) {
596 nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
597 if (nb == sizeof(uint32_t) && s) {
599 lseek(fd, s, SEEK_CUR); /* seek after this block */
601 Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
602 current_file, current_block, nb,s);
607 read_fm(VT_SKIP_EOF);
609 atEOF = true; /* stop the loop */
617 * BSR + EOF => begin of EOF + EIO
618 * BSR + BSR + EOF => last block
621 int vtape::bsr(int count)
624 ASSERT(current_file >= 0);
638 boffset_t last=-1, last2=-1;
639 boffset_t orig = lseek(fd, 0, SEEK_CUR);
640 int orig_f = current_file;
641 int orig_b = current_block;
643 Dmsg4(dbglevel, "bsr(%i) cur_blk=%i orig=%lli cur_FM=%lli\n",
644 count, current_block, orig, cur_FM);
646 /* begin of tape, do nothing */
652 /* at EOF 0:-1 BOT=0 EOD=0 EOF=0 ERR: Input/output error */
654 lseek(fd, cur_FM, SEEK_SET);
656 if (current_file > 0) {
665 * First, go to cur/last_FM and read all blocks to find the good one
667 if (cur_FM == orig) { /* already just before EOF */
668 lseek(fd, last_FM, SEEK_SET);
671 lseek(fd, cur_FM, SEEK_SET);
674 ret = read_fm(VT_READ_EOF);
678 last2 = last; /* keep track of the 2 last blocs position */
679 last = lseek(fd, 0, SEEK_CUR);
680 last_f = current_file;
681 last_b = current_block;
682 Dmsg6(dbglevel, "EOF=%i last2=%lli last=%lli < orig=%lli %i:%i\n",
683 atEOF, last2, last, orig, current_file, current_block);
686 } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
688 if (last2 > 0 && atEOF) { /* we take the previous position */
689 lseek(fd, last2, SEEK_SET);
690 current_file = last_f;
691 current_block = last_b - 1;
692 Dmsg3(dbglevel, "1 set offset2=%lli %i:%i\n",
693 last, current_file, current_block);
695 } else if (last > 0) {
696 lseek(fd, last, SEEK_SET);
697 current_file = last_f;
698 current_block = last_b;
699 Dmsg3(dbglevel, "2 set offset=%lli %i:%i\n",
700 last, current_file, current_block);
702 lseek(fd, orig, SEEK_SET);
703 current_file = orig_f;
704 current_block = orig_b;
708 Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
710 atEOT = atEOF = atEOD = false;
711 atBOT = (lseek(fd, 0, SEEK_CUR) - (sizeof(uint32_t)+2*sizeof(boffset_t))) == 0;
714 current_block = orig_b;
720 /* BSF => just before last EOF
721 * EOF + BSF => just before EOF
722 * file 0 + BSF => BOT + errno
727 ASSERT(current_file >= 0);
728 Dmsg2(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block);
733 atBOT = atEOF = atEOT = atEOD = false;
735 if (current_file == 0) {/* BOT + errno */
736 lseek(fd, 0, SEEK_SET);
737 read_fm(VT_READ_EOF);
744 Dmsg1(dbglevel, "bsf last=%lli\n", last_FM);
745 lseek(fd, cur_FM, SEEK_SET);
753 * DEVICE virtual that we redefine.
755 * Put vtape in offline mode
757 bool vtape::offline(DCR *dcr)
761 atEOF = false; /* End of file */
762 atEOT = false; /* End of tape */
763 atEOD = false; /* End of data */
764 atBOT = false; /* Begin of tape */
775 * DEVICE virtual that we redefine.
777 * A filemark is automatically written to tape if the last tape operation
778 * before close was a write.
780 int vtape::d_close(int)
787 lock.l_type = F_UNLCK;
789 lock.l_whence = SEEK_SET;
791 lock.l_pid = getpid();
793 ASSERT(fcntl(fd, F_SETLK, &lock) != -1);
804 * DEVICE virtual that we redefine.
806 * When a filemark is encountered while reading, the following happens. If
807 * there are data remaining in the buffer when the filemark is found, the
808 * buffered data is returned. The next read returns zero bytes. The following
809 * read returns data from the next file. The end of recorded data is signaled
810 * by returning zero bytes for two consecutive read calls. The third read
813 ssize_t vtape::d_read(int, void *buffer, size_t count)
816 ASSERT(current_file >= 0);
820 Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block);
822 if (atEOT || atEOD) {
839 atEOD = atBOT = false;
841 /* reading size of data */
842 nb = ::read(fd, &s, sizeof(uint32_t));
844 atEOF = true; /* TODO: check this */
848 if (s > count) { /* not enough buffer to read block */
849 Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
850 lseek(fd, s, SEEK_CUR);
857 if (read_fm(VT_SKIP_EOF)) {
864 /* reading data itself */
865 nb = ::read(fd, buffer, s);
866 if (nb != (ssize_t)s) { /* read error */
870 Dmsg0(dbglevel, "EOT during reading\n");
874 if (current_block >= 0) {
881 /* Redefine DEVICE virtual function */
882 int vtape::d_open(const char *pathname, int uflags)
884 Dmsg2(dbglevel, "vtape::d_open(%s, %i)\n", pathname, uflags);
886 online = true; /* assume that drive contains a tape */
890 ASSERT(!m_shstore || (m_shstore_lock && m_shstore_register));
892 if (stat(pathname, &statp) != 0) {
894 Dmsg1(dbglevel, "Can't stat on %s\n", pathname);
895 if (uflags & O_NONBLOCK) {
897 fd = ::open("/dev/null", O_RDWR | O_LARGEFILE, 0600);
900 fd = ::open(pathname, O_RDWR | O_LARGEFILE | O_CLOEXEC, 0600);
905 Dmsg2(0, "Unable to open vtape device %s ERR=%s\n", pathname, be.bstrerror());
910 lockfile = (char *)malloc(strlen(pathname) + 3);
911 strcpy(lockfile, pathname);
912 strcat(lockfile, ".l");
914 lockfd = ::open(lockfile, O_CREAT | O_RDWR | O_LARGEFILE | O_CLOEXEC, 0600);
917 Dmsg2(0, "Unable to open vtape device lock %s ERR=%s\n", lockfile, be.bstrerror());
920 lock.l_type = F_WRLCK;
922 lock.l_whence = SEEK_SET;
924 lock.l_pid = getpid();
926 ASSERT(fcntl(lockfd, F_SETLK, &lock) != -1);
932 cur_FM = next_FM = last_FM = 0;
935 atEOT = atEOD = false;
937 /* If the vtape is empty, start by writing a EOF */
938 if (online && !read_fm(VT_READ_EOF)) {
939 lseek(fd, 0, SEEK_SET); /* rewind */
940 cur_FM = next_FM = last_FM = 0; /* reset */
941 weof(); /* write the first EOF */
942 last_file = current_file=0;
948 /* use this to track file usage */
949 void vtape::update_pos()
953 if (fstat(fd, &statp) == 0) {
954 file_block = statp.st_blocks;
957 Dmsg1(dbglevel*2, "update_pos=%i\n", file_block);
959 if (file_block > max_block) {
968 Dmsg0(dbglevel+1, "===================\n");
969 Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
970 Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
971 Dmsg1(dbglevel+1, "file_block=%i\n", file_block);
972 Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n",
973 atEOF, atEOT, atEOD, atBOT);
976 const char *vtape::print_type()
981 #else /*!USE_VTAPE */
983 /* faked constructor and destructor to avoid undefined reference to vtable */
992 /* dummy implementation */
993 const char *vtape::print_type()
998 #endif /* ! USE_VTAPE */