From: Kern Sibbald Date: Wed, 9 Feb 2005 14:40:03 +0000 (+0000) Subject: - Modified ANSI label code to preserve any ANSI label X-Git-Tag: Release-1.38.0~645 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=a5fc60132e4ed7a74d74044730e173deae1a24d2;p=bacula%2Fbacula - Modified ANSI label code to preserve any ANSI label already found by skipping over it rather than rewriting it. - Split the ANSI label code into ansi_label.c - Do not let user relabel an ANSI labeled tape. - Applied a patch for the console help command supplied in a bug report. - Added some new dev methods. Most notably was set_eof(), which handles setting all the dev variables when an EOF is just read. This is now used most everywhere in the code. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@1820 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/examples/python/NewVolume.py b/bacula/examples/python/NewVolume.py index b6df07e6f5..3a1df0cf4f 100644 --- a/bacula/examples/python/NewVolume.py +++ b/bacula/examples/python/NewVolume.py @@ -7,6 +7,6 @@ def NewVolume(j): print "Client=" + client numvol = bacula.get(j, "NumVols"); print "NumVols=", numvol - bacula.set(jcr=j, JobReport="Python New Volume set for Job.\n") - bacula.set(jcr=j, VolumeName="TestA-001") +# bacula.set(jcr=j, JobReport="Python New Volume set for Job.\n") +# bacula.set(jcr=j, VolumeName="TestA-001") return 1 diff --git a/bacula/kernstodo b/bacula/kernstodo index 13d6315826..dade3954dd 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -30,6 +30,11 @@ Suggestions for Preben: - Optimized bootstrap. For 1.37: +- Make sure SD deletes spool files on error exit. +- Delete old spool files when SD starts. +- When Python creates a new label, the tape is immediately + recycled and no label created. This happens when using + autolabeling -- even when Python doesn't generate the name. - Add a restore directory-x - When labeling tapes, if you enter 000026, Bacula uses the tape index rather than the Volume name 000026. @@ -360,6 +365,10 @@ For 1.37 Testing/Documentation: - any actions should be interuptable with STRG+C - command-expansion would be pretty cool ==== +- When the replace Never option is set, new directory permissions + are not restored. See bug 213. To fix this requires creating a + list of newly restored directories so that those directory + permissions *can* be restored. - Compaction of Disk space by "migrating" Volumes that have pruned Jobs (what criteria? size, #jobs, time). - Add prune all command diff --git a/bacula/src/.cvsignore b/bacula/src/.cvsignore index a9c61f762d..463e48169e 100644 --- a/bacula/src/.cvsignore +++ b/bacula/src/.cvsignore @@ -6,3 +6,4 @@ config.h testprogs host.h perlgui +python diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index c2d2637b38..a912098ce6 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -34,6 +34,31 @@ */ +/* + Here is how database versions work. + + While I am working on a new release with database changes, the + update scripts are in the src/cats directory under the names + update_xxx_tables.in. Most of the time, I make database updates + in one go and immediately update the version, but not always. If + there are going to be several updates as is the case with version + 1.37, then I will often forgo changing the version until the last + update otherwise I will end up with too many versions and a lot + of confusion. + + When I am pretty sure there will be no more updates, I will + change the version from 8 to 9 (in the present case), and when I + am 100% sure there will be no more changes, the update script + will be copied to the updatedb directory with the correct name + (in the present case 8 to 9). + + Now, in principle, each of the different DB implementations + can have a different version, but in practice they are all + the same (simplifies things). The exception is the internal + database, which is no longer used, and hence, no longer changes. + */ + + #ifndef __SQL_H_ #define __SQL_H_ 1 diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 0c0644488a..273d1500e8 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -97,16 +97,16 @@ int quit_cmd(UAContext *ua, const char *cmd); struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; }; static struct cmdstruct commands[] = { { N_("add"), add_cmd, _("add media to a pool")}, - { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on/off] -- console messages")}, - { N_("automount"), automount_cmd, _("automount [on/off] -- after label")}, - { N_("cancel"), cancel_cmd, _("cancel job=nnn -- cancel a job")}, + { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")}, + { N_("automount"), automount_cmd, _("automount [on|off] -- after label")}, + { N_("cancel"), cancel_cmd, _("cancel -- cancel a job")}, { N_("create"), create_cmd, _("create DB Pool from resource")}, { N_("delete"), delete_cmd, _("delete [pool= | media volume=]")}, { N_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")}, { N_("exit"), quit_cmd, _("exit = quit")}, - { N_("gui"), gui_cmd, _("gui [on/off] -- non-interactive gui mode")}, + { N_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")}, { N_("help"), help_cmd, _("print this command")}, - { N_("list"), list_cmd, _("list [pools | jobs | jobtotals | media | files jobid=]; from catalog")}, + { N_("list"), list_cmd, _("list [pools | jobs | jobtotals | media | files ]; from catalog")}, { N_("label"), label_cmd, _("label a tape")}, { N_("llist"), llist_cmd, _("full or long list like list command")}, { N_("messages"), messagescmd, _("messages")}, diff --git a/bacula/src/stored/Makefile.in b/bacula/src/stored/Makefile.in index cbdfa2292f..782721c1f8 100644 --- a/bacula/src/stored/Makefile.in +++ b/bacula/src/stored/Makefile.in @@ -18,7 +18,8 @@ first_rule: all dummy: # bacula-sd -SVRSRCS = stored.c autochanger.c acquire.c append.c \ +SVRSRCS = stored.c ansi_label.c \ + autochanger.c acquire.c append.c \ askdir.c authenticate.c \ block.c butil.c dev.c \ device.c dircmd.c fd_cmds.c job.c \ @@ -26,7 +27,8 @@ SVRSRCS = stored.c autochanger.c acquire.c append.c \ python.c \ read.c read_record.c record.c \ spool.c status.c stored_conf.c -SVROBJS = stored.o autochanger.o acquire.o append.o \ +SVROBJS = stored.o ansi_label.o \ + autochanger.o acquire.o append.o \ askdir.o authenticate.o \ block.o butil.o dev.o \ device.o dircmd.o fd_cmds.o job.o \ @@ -37,29 +39,35 @@ SVROBJS = stored.o autochanger.o acquire.o append.o \ # btape TAPESRCS = btape.c block.c butil.c dev.c device.c label.c \ + ansi_label.c \ acquire.c mount.c record.c read_record.c \ stored_conf.c match_bsr.c parse_bsr.c spool.c TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o \ + ansi_label.o \ autochanger.o acquire.o mount.o record.o read_record.o \ stored_conf.o match_bsr.o parse_bsr.o spool.o # bls BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o \ + ansi_label.o \ autochanger.o acquire.o mount.o parse_bsr.o record.o \ read_record.o stored_conf.o spool.o # bextract BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o \ + ansi_label.o \ autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \ read_record.o stored_conf.o spool.o # bscan SCNOBJS = bscan.o block.o device.o dev.o label.o \ + ansi_label.o \ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \ butil.o read_record.o stored_conf.o spool.o # bcopy COPYOBJS = bcopy.o block.o device.o dev.o label.o \ + ansi_label.o \ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \ butil.o read_record.o stored_conf.o spool.o diff --git a/bacula/src/stored/ansi_label.c b/bacula/src/stored/ansi_label.c new file mode 100644 index 0000000000..0ee7ad0f8a --- /dev/null +++ b/bacula/src/stored/ansi_label.c @@ -0,0 +1,285 @@ +/* + * + * ansi_label.c routines to handle ANSI (and perhaps one day IBM) + * tape labels. + * + * Kern Sibbald, MMV + * + * + * + * Version $Id$ + */ +/* + Copyright (C) 2005 Kern Sibbald + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + */ + +#include "bacula.h" /* pull in global headers */ +#include "stored.h" /* pull in Storage Deamon headers */ + +/* Forward referenced functions */ +static char *ansi_date(time_t td, char *buf); +static bool same_label_names(char *bacula_name, char *ansi_name); + +/* + * We read an ANSI label and compare the Volume name. We require + * a VOL1 record of 80 characters followed by a HDR1 record containing + * BACULA.DATA in the filename field. We then read up to 3 more + * header records (they are not required) and an EOF, at which + * point, all is good. + * + * Returns: + * VOL_OK Volume name OK + * VOL_NO_LABEL No ANSI label on Volume + * VOL_IO_ERROR I/O error on read + * VOL_NAME_ERROR Wrong name in VOL1 record + * VOL_LABEL_ERROR Probably an ANSI label, but something wrong + * + */ +int read_ansi_ibm_label(DCR *dcr) +{ + DEVICE *dev = dcr->dev; + JCR *jcr = dcr->jcr; + char label[80]; /* tape label */ + int stat, i; + char *VolName = dcr->VolumeName; + + /* + * Read VOL1, HDR1, HDR2 labels, but ignore the data + * If tape read the following EOF mark, on disk do + * not read. + */ + Dmsg0(000, "Read ansi label.\n"); + if (!dev->is_tape()) { + return VOL_OK; + } + + dev->label_type = B_BACULA_LABEL; /* assume Bacula label */ + + /* Read a maximum of 5 records VOL1, HDR1, ... HDR4 */ + for (i=0; i < 6; i++) { + do { + stat = read(dev->fd, label, sizeof(label)); + } while (stat == -1 && errno == EINTR); + if (stat < 0) { + berrno be; + clrerror_dev(dev, -1); + Dmsg1(000, "Read device got: ERR=%s\n", be.strerror()); + Mmsg2(dev->errmsg, _("Read error on device %s in ANSI/IBM label. ERR=%s\n"), + dev->dev_name, be.strerror()); + Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg); + dev->VolCatInfo.VolCatErrors++; + return VOL_IO_ERROR; + } + if (stat == 0) { + if (dev->at_eof()) { + dev->state |= ST_EOT; + Dmsg0(000, "EOM on ANSI label\n"); + return VOL_LABEL_ERROR; /* at EOM this shouldn't happen */ + } else { + dev->set_eof(); + } + } + switch (i) { + case 0: /* Want VOL1 label */ + if (stat != 80 || strncmp("VOL1", label, 4) != 0) { + Dmsg0(000, "No VOL1 label\n"); + return VOL_NO_LABEL; /* No ANSI label */ + } + + dev->label_type = B_ANSI_LABEL; + + /* Compare Volume Names allow special wild card */ + if (VolName && *VolName && *VolName != '*') { + if (!same_label_names(VolName, &label[4])) { + char *p = &label[4]; + char *q = dev->VolHdr.VolName; + for (int i=0; *p != ' ' && i < 6; i++) { + *q++ = *p++; + } + *q = 0; + Dmsg2(000, "Wanted ANSI Vol %s got %6s\n", VolName, dev->VolHdr.VolName); + return VOL_NAME_ERROR; + } + } + break; + case 1: + if (stat != 80 || strncmp("HDR1", label, 4) != 0) { + Dmsg0(000, "No HDR1 label\n"); + return VOL_LABEL_ERROR; + } + if (strncmp("BACULA.DATA", &label[4], 11) != 0) { + Dmsg1(000, "HD1 not Bacula label. Wanted BACULA.DATA got %11s\n", + &label[4]); + return VOL_NAME_ERROR; /* Not a Bacula label */ + } + break; + case 2: + if (stat != 80 || strncmp("HDR2", label, 4) != 0) { + Dmsg0(000, "No HDR2 label\n"); + return VOL_LABEL_ERROR; + } + break; + default: + if (stat == 0) { + Dmsg0(000, "ANSI label OK\n"); + return VOL_OK; + } + if (stat != 80 || strncmp("HDR", label, 3) != 0) { + Dmsg0(000, "Unknown or bad ANSI label record.\n"); + return VOL_LABEL_ERROR; + } + break; + } + } + Dmsg0(000, "Too many records in ANSI label.\n"); + return VOL_LABEL_ERROR; +} + +/* + * Write an ANSI or IBM 80 character tape label + * Assume we are positioned at the beginning of the tape. + * Returns: true of OK + * false if error + */ +bool write_ansi_ibm_label(DCR *dcr, const char *VolName) +{ + DEVICE *dev = dcr->dev; + JCR *jcr = dcr->jcr; + char label[80]; /* tape label */ + char date[20]; /* ansi date buffer */ + time_t now; + int len, stat, label_type; + + /* + * If the Device requires a specific label type use it, + * otherwise, use the type requested by the Director + */ + if (dcr->device->label_type != B_BACULA_LABEL) { + label_type = dcr->device->label_type; /* force label type */ + } else { + label_type = dcr->VolCatInfo.LabelType; /* accept Dir type */ + } + + switch (label_type) { + case B_BACULA_LABEL: + return true; + case B_ANSI_LABEL: + case B_IBM_LABEL: + ser_declare; + Dmsg1(000, "Write ANSI label type=%d\n", label_type); + len = strlen(VolName); + if (len > 6) { + Jmsg1(jcr, M_FATAL, 0, _("ANSI Volume label name \"%s\" longer than 6 chars.\n"), + VolName); + return false; + } + memset(label, ' ', sizeof(label)); + ser_begin(label, sizeof(label)); + ser_bytes("VOL1", 4); + ser_bytes(VolName, len); + label[79] = '3'; /* ANSI label flag */ + /* Write VOL1 label */ + stat = write(dev->fd, label, sizeof(label)); + if (stat != sizeof(label)) { + berrno be; + Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI VOL1 label. ERR=%s\n"), + be.strerror()); + return false; + } + /* Now construct HDR1 label */ + ser_begin(label, sizeof(label)); + ser_bytes("HDR1", 4); + ser_bytes("BACULA.DATA", 11); /* Filename field */ + ser_begin(&label[21], sizeof(label)-21); /* fileset field */ + ser_bytes(VolName, len); /* write Vol Ser No. */ + ser_begin(&label[27], sizeof(label)-27); + ser_bytes("00010001000100", 14); /* File section, File seq no, Generation no */ + now = time(NULL); + ser_bytes(ansi_date(now, date), 6); /* current date */ + ser_bytes(ansi_date(now - 24 * 3600, date), 6); /* created yesterday */ + ser_bytes(" 000000Bacula ", 27); + /* Write HDR1 label */ + stat = write(dev->fd, label, sizeof(label)); + if (stat != sizeof(label)) { + berrno be; + Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"), + be.strerror()); + return false; + } + /* Now construct HDR2 label */ + memset(label, ' ', sizeof(label)); + ser_begin(label, sizeof(label)); + ser_bytes("HDR2F3200032000", 15); + /* Write HDR1 label */ + stat = write(dev->fd, label, sizeof(label)); + if (stat != sizeof(label)) { + berrno be; + Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"), + be.strerror()); + return false; + } + if (weof_dev(dev, 1) < 0) { + Jmsg(jcr, M_FATAL, 0, _("Error writing EOF to tape. ERR=%s"), dev->errmsg); + return false; + } + return true; + default: + Jmsg0(jcr, M_ABORT, 0, _("write_ansi_ibm_label called for non-ANSI/IBM type\n")); + return false; /* should not get here */ + } +} + +/* Check a Bacula Volume name against an ANSI Volume name */ +static bool same_label_names(char *bacula_name, char *ansi_name) +{ + char *a = ansi_name; + char *b = bacula_name; + /* Six characters max */ + for (int i=0; i < 6; i++) { + if (*a == *b) { + a++; + b++; + continue; + } + /* ANSI labels are blank filled, Bacula's are zero terminated */ + if (*a == ' ' && *b == 0) { + return true; + } + return false; + } + /* Reached 6 characters */ + b++; + if (*b == 0) { + return true; + } + return false; +} + + +static char *ansi_date(time_t td, char *buf) +{ + struct tm *tm; + + if (td == 0) { + td = time(NULL); + } + tm = gmtime(&td); + bsnprintf(buf, 10, " %05d ", 1000 * (tm->tm_year + 1900 - 2000) + tm->tm_yday); + return buf; +} diff --git a/bacula/src/stored/block.c b/bacula/src/stored/block.c index cc88da62ec..c1317da712 100644 --- a/bacula/src/stored/block.c +++ b/bacula/src/stored/block.c @@ -477,7 +477,7 @@ bool write_block_to_dev(DCR *dcr) (dev->file_size+block->binbuf) >= dev->max_file_size) { dev->file_size = 0; /* reset file size */ - if (dev_state(dev, ST_TAPE) && weof_dev(dev, 1) != 0) { /* write eof */ + if (dev->is_tape() && weof_dev(dev, 1) != 0) { /* write eof */ Dmsg0(190, "WEOF error in max file size.\n"); terminate_writing_volume(dcr); dev->dev_errno = ENOSPC; @@ -870,17 +870,15 @@ reread: Dmsg3(200, "Read device got %d bytes at %u:%u\n", stat, dev->file, dev->block_num); if (stat == 0) { /* Got EOF ! */ - dev->block_num = block->read_len = 0; + dev->block_num = 0; + block->read_len = 0; Mmsg3(dev->errmsg, _("Read zero bytes at %u:%u on device %s.\n"), dev->file, dev->block_num, dev->dev_name); if (dev->at_eof()) { /* EOF already read? */ dev->state |= ST_EOT; /* yes, 2 EOFs => EOT */ - block->read_len = 0; return 0; } - dev->file++; /* increment file */ - dev->state |= ST_EOF; /* set EOF read */ - block->read_len = 0; + dev->set_eof(); return false; /* return eof */ } /* Continue here for successful read */ diff --git a/bacula/src/stored/bscan.c b/bacula/src/stored/bscan.c index 291c3d5ba9..574c740916 100644 --- a/bacula/src/stored/bscan.c +++ b/bacula/src/stored/bscan.c @@ -367,7 +367,6 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) if (rec->data_len > 0) { mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */ if (showProgress) { - char ed1[50]; int pct = (mr.VolBytes * 100) / currentVolumeSize; if (pct != last_pct) { fprintf(stdout, "done: %d%%\n", pct); diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 666b86b426..6397ba7451 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -159,7 +159,6 @@ init_dev(DEVICE *dev, DEVRES *device) dev->vol_poll_interval = device->vol_poll_interval; dev->max_spool_size = device->max_spool_size; dev->drive_index = device->drive_index; - dev->label_type = device->label_type; if (tape) { /* No parts on tapes */ dev->max_part_size = 0; } @@ -328,6 +327,7 @@ open_dev(DEVICE *dev, char *VolName, int mode) Dmsg3(29, "open_dev: tape=%d dev_name=%s vol=%s\n", dev_is_tape(dev), dev->dev_name, dev->VolCatInfo.VolCatName); dev->state &= ~(ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF); + dev->label_type = B_BACULA_LABEL; if (dev->is_tape() || dev->is_fifo()) { dev->file_size = 0; int timeout; @@ -921,14 +921,6 @@ int open_first_part(DEVICE *dev) { } } -#ifdef debug_tracing -#undef rewind_dev -bool _rewind_dev(char *file, int line, DEVICE *dev) -{ - Dmsg2(100, "rewind_dev called from %s:%d\n", file, line); - return rewind_dev(dev); -} -#endif /* Protected version of lseek, which opens the right part if necessary */ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) @@ -1029,6 +1021,15 @@ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) } } +#ifdef debug_tracing +#undef rewind_dev +bool _rewind_dev(char *file, int line, DEVICE *dev) +{ + Dmsg2(100, "rewind_dev called from %s:%d\n", file, line); + return rewind_dev(dev); +} +#endif + /* * Rewind the device. * Returns: true on success @@ -1088,6 +1089,19 @@ bool rewind_dev(DEVICE *dev) return true; } +/* + * Called to indicate that we have just read an + * EOF from the device. + */ +void DEVICE::set_eof() +{ + state |= ST_EOF; + file++; + file_addr = 0; + file_size = 0; + block_num = 0; +} + /* * Position device to end of medium (end of data) * Returns: 1 on succes @@ -1102,7 +1116,7 @@ eod_dev(DEVICE *dev) off_t pos; Dmsg0(29, "eod_dev\n"); - if (dev->state & ST_EOT) { + if (dev->at_eot()) { return 1; } dev->state &= ~(ST_EOF); /* remove EOF flags */ @@ -1112,7 +1126,7 @@ eod_dev(DEVICE *dev) if (dev->state & (ST_FIFO | ST_PROG)) { return 1; } - if (!(dev->is_tape())) { + if (!dev->is_tape()) { pos = lseek_dev(dev, (off_t)0, SEEK_END); // Dmsg1(100, "====== Seek to %lld\n", pos); if (pos >= 0) { @@ -1173,6 +1187,7 @@ eod_dev(DEVICE *dev) return 0; } Dmsg2(100, "EOD file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); + dev->set_eof(); dev->file = mt_stat.mt_fileno; /* @@ -1189,7 +1204,7 @@ eod_dev(DEVICE *dev) * Move file by file to the end of the tape */ int file_num; - for (file_num=dev->file; !(dev->state & ST_EOT); file_num++) { + for (file_num=dev->file; !dev->at_eot(); file_num++) { Dmsg0(200, "eod_dev: doing fsf 1\n"); if (!fsf_dev(dev, 1)) { Dmsg0(200, "fsf_dev error.\n"); @@ -1205,6 +1220,7 @@ eod_dev(DEVICE *dev) if (dev_cap(dev, CAP_MTIOCGET) && ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) { Dmsg2(100, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno); + dev->set_eof(); dev->file = mt_stat.mt_fileno; } stat = 0; @@ -1521,10 +1537,8 @@ fsf_dev(DEVICE *dev, int num) return false; } Dmsg2(200, "fsf file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); + dev->set_eof(); dev->file = mt_stat.mt_fileno; - dev->state |= ST_EOF; /* just read EOF */ - dev->file_addr = 0; - dev->file_size = 0; return true; /* @@ -1572,10 +1586,7 @@ fsf_dev(DEVICE *dev, int num) Dmsg0(100, "Set ST_EOT\n"); break; } else { - dev->state |= ST_EOF; - dev->file++; - dev->file_addr = 0; - dev->file_size = 0; + dev->set_eof(); continue; } } else { /* Got data */ @@ -1594,10 +1605,7 @@ fsf_dev(DEVICE *dev, int num) Dmsg0(100, "Got < 0 for MTFSF\n"); Dmsg1(100, "%s", dev->errmsg); } else { - dev->state |= ST_EOF; /* just read EOF */ - dev->file++; - dev->file_addr = 0; - dev->file_size = 0; + dev->set_eof(); } } free_memory(rbuf); @@ -1717,11 +1725,7 @@ fsr_dev(DEVICE *dev, int num) if (dev->state & ST_EOF) { dev->state |= ST_EOT; } else { - dev->state |= ST_EOF; /* assume EOF */ - dev->file++; - dev->block_num = 0; - dev->file_addr = 0; - dev->file_size = 0; + dev->set_eof(); } } Mmsg2(dev->errmsg, _("ioctl MTFSR error on %s. ERR=%s.\n"), @@ -1861,6 +1865,12 @@ weof_dev(DEVICE *dev, int num) if (!dev->is_tape()) { return 0; } + if (!dev->can_append()) { + Mmsg0(dev->errmsg, _("Attempt to WEOF on non-appendable Volume\n")); + Emsg0(M_FATAL, 0, dev->errmsg); + return -1; + } + dev->state &= ~(ST_EOT | ST_EOF); /* remove EOF/EOT flags */ mt_com.mt_op = MTWEOF; mt_com.mt_count = num; @@ -2048,6 +2058,7 @@ static void do_close(DEVICE *dev) /* Clean up device packet so it can be reused */ dev->fd = -1; dev->state &= ~(ST_OPENED|ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF); + dev->label_type = B_BACULA_LABEL; dev->file = dev->block_num = 0; dev->file_size = 0; dev->file_addr = 0; diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index d63bf4cde0..abf48beb3f 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -248,6 +248,7 @@ public: int is_dvd() const; int is_open() const; int is_labeled() const; + int is_busy() const; /* either reading or writing */ int at_eof() const; int at_eom() const; int at_eot() const; @@ -255,6 +256,7 @@ public: int can_read() const; const char *strerror() const; const char *archive_name() const; + void set_eof(); }; /* Note, these return int not bool! */ @@ -264,6 +266,7 @@ inline int DEVICE::is_fifo() const { return state & ST_FIFO; } inline int DEVICE::is_dvd() const { return state & ST_DVD; } inline int DEVICE::is_open() const { return state & ST_OPENED; } inline int DEVICE::is_labeled() const { return state & ST_LABEL; } +inline int DEVICE::is_busy() const { return state & ST_READ || num_writers; } inline int DEVICE::at_eof() const { return state & ST_EOF; } inline int DEVICE::at_eom() const { return state & ST_EOT; } inline int DEVICE::at_eot() const { return state & ST_EOT; } @@ -272,9 +275,6 @@ inline int DEVICE::can_read() const { return state & ST_READ; } inline const char *DEVICE::strerror() const { return errmsg; } inline const char *DEVICE::archive_name() const { return dev_name; } - - - /* * Device Context (or Control) Record. * There is one of these records for each Job that is using diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index c4a9775725..4026e1c737 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -82,6 +82,7 @@ static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *oldname, char *newname, char *poolname, int Slot, int relabel); static bool try_autoload_device(JCR *jcr, int slot, const char *VolName); +static void send_dir_busy_message(BSOCK *dir, DEVICE *dev); struct s_cmds { const char *cmd; @@ -338,14 +339,8 @@ static bool do_label(JCR *jcr, int relabel) dev->dev_blocked == BST_WAITING_FOR_SYSOP || dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP)) { label_volume_if_ok(jcr, dev, oldname, newname, poolname, slot, relabel); - } else if (dev_state(dev, ST_READ) || dev->num_writers) { - if (dev_state(dev, ST_READ)) { - bnet_fsend(dir, _("3911 Device %s is busy with 1 reader.\n"), - dev_name(dev)); - } else { - bnet_fsend(dir, _("3912 Device %s is busy with %d writer(s).\n"), - dev_name(dev), dev->num_writers); - } + } else if (dev->is_busy()) { + send_dir_busy_message(dir, dev); } else { /* device not being used */ label_volume_if_ok(jcr, dev, oldname, newname, poolname, slot, relabel); } @@ -402,13 +397,17 @@ static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *oldname, case VOL_OK: if (!relabel) { bnet_fsend(dir, _( - "3911 Cannot label Volume because it is already labeled: \"%s\"\n"), + "3920 Cannot label Volume because it is already labeled: \"%s\"\n"), dev->VolHdr.VolName); break; } /* Relabel request. If oldname matches, continue */ if (strcmp(oldname, dev->VolHdr.VolName) != 0) { - bnet_fsend(dir, _("Wrong volume mounted.\n")); + bnet_fsend(dir, _("3921 Wrong volume mounted.\n")); + break; + } + if (dev->label_type != B_BACULA_LABEL) { + bnet_fsend(dir, _("3922 Cannot relabel an ANSI/IBM labeled Volume.\n")); break; } /* Fall through wanted! */ @@ -643,16 +642,8 @@ static bool unmount_cmd(JCR *jcr) } else if (dev->dev_blocked == BST_WRITING_LABEL) { bnet_fsend(dir, _("3903 Device \"%s\" is being labeled.\n"), dev_name(dev)); - } else if (dev_state(dev, ST_READ) || dev->num_writers) { - if (dev_state(dev, ST_READ)) { - Dmsg0(90, "Device in read mode\n"); - bnet_fsend(dir, _("3904 Device \"%s\" is busy reading.\n"), dev_name(dev)); - } else { - Dmsg1(90, "Device busy with %d writers\n", dev->num_writers); - bnet_fsend(dir, _("3905 Device %s is busy with %d writer(s).\n"), - dev_name(dev), dev->num_writers); - } - + } else if (dev->is_busy()) { + send_dir_busy_message(dir, dev); } else { /* device not being used */ Dmsg0(90, "Device not in use, unmounting\n"); /* On FreeBSD, I am having ASSERT() failures in block_device() @@ -714,16 +705,8 @@ static bool release_cmd(JCR *jcr) } else if (dev->dev_blocked == BST_WRITING_LABEL) { bnet_fsend(dir, _("3914 Device %s is being labeled.\n"), dev_name(dev)); - } else if (dev_state(dev, ST_READ) || dev->num_writers) { - if (dev_state(dev, ST_READ)) { - Dmsg0(90, "Device in read mode\n"); - bnet_fsend(dir, _("3915 Device %s is busy with 1 reader.\n"), dev_name(dev)); - } else { - Dmsg1(90, "Device busy with %d writers\n", dev->num_writers); - bnet_fsend(dir, _("3916 Device %s is busy with %d writer(s).\n"), - dev_name(dev), dev->num_writers); - } - + } else if (dev->is_busy()) { + send_dir_busy_message(dir, dev); } else { /* device not being used */ Dmsg0(90, "Device not in use, unmounting\n"); release_volume(jcr->dcr); @@ -769,13 +752,8 @@ static bool autochanger_cmd(JCR *jcr) dev->dev_blocked == BST_WAITING_FOR_SYSOP || dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP)) { autochanger_list(dcr, dir); - } else if (dev_state(dev, ST_READ) || dev->num_writers) { - if (dev_state(dev, ST_READ)) { - bnet_fsend(dir, _("3901 Device %s is busy with 1 reader.\n"), dev_name(dev)); - } else { - bnet_fsend(dir, _("3902 Device %s is busy with %d writer(s).\n"), - dev_name(dev), dev->num_writers); - } + } else if (dev->is_busy()) { + send_dir_busy_message(dir, dev); } else { /* device not being used */ autochanger_list(dcr, dir); } @@ -815,14 +793,8 @@ static bool readlabel_cmd(JCR *jcr) dev->dev_blocked == BST_WAITING_FOR_SYSOP || dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP)) { read_volume_label(jcr, dev, Slot); - } else if (dev_state(dev, ST_READ) || dev->num_writers) { - if (dev_state(dev, ST_READ)) { - bnet_fsend(dir, _("3911 Device %s is busy with 1 reader.\n"), - dev_name(dev)); - } else { - bnet_fsend(dir, _("3912 Device %s is busy with %d writer(s).\n"), - dev_name(dev), dev->num_writers); - } + } else if (dev->is_busy()) { + send_dir_busy_message(dir, dev); } else { /* device not being used */ read_volume_label(jcr, dev, Slot); } @@ -897,3 +869,14 @@ static bool try_autoload_device(JCR *jcr, int slot, const char *VolName) } return true; } + +static void send_dir_busy_message(BSOCK *dir, DEVICE *dev) +{ + if (dev->can_read()) { + bnet_fsend(dir, _("3911 Device \"%s\" is busy reading.\n"), + dev_name(dev)); + } else { + bnet_fsend(dir, _("3912 Device \"%s\" is busy with %d writer(s).\n"), + dev_name(dev), dev->num_writers); + } +} diff --git a/bacula/src/stored/label.c b/bacula/src/stored/label.c index cfda892467..1ae87ff3aa 100644 --- a/bacula/src/stored/label.c +++ b/bacula/src/stored/label.c @@ -32,8 +32,6 @@ /* Forward referenced functions */ static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec); -static int read_ansi_ibm_label(DCR *dcr); -static char *ansi_date(time_t td, char *buf); extern char my_name[]; extern int debug_level; @@ -49,14 +47,17 @@ extern int debug_level; * * Returns VOL_ code as defined in record.h * VOL_NOT_READ - * VOL_OK - * VOL_NO_LABEL - * VOL_IO_ERROR - * VOL_NAME_ERROR - * VOL_CREATE_ERROR - * VOL_VERSION_ERROR - * VOL_LABEL_ERROR - * VOL_NO_MEDIA + * VOL_OK good label found + * VOL_NO_LABEL volume not labeled + * VOL_IO_ERROR I/O error reading tape + * VOL_NAME_ERROR label has wrong name + * VOL_CREATE_ERROR Error creating label + * VOL_VERSION_ERROR label has wrong version + * VOL_LABEL_ERROR bad label type + * VOL_NO_MEDIA no media in drive + * + * The dcr block is emptied on return, and the Volume is + * rewound. */ int read_dev_volume_label(DCR *dcr) { @@ -67,7 +68,7 @@ int read_dev_volume_label(DCR *dcr) bool ok = false; DEV_BLOCK *block = dcr->block; int stat; - bool want_ansi_ibm_label; + bool want_ansi_label; Dmsg3(100, "Enter read_volume_label device=%s vol=%s dev_Vol=%s\n", dev_name(dev), VolName, dev->VolHdr.VolName); @@ -94,6 +95,7 @@ int read_dev_volume_label(DCR *dcr) } dev->state &= ~(ST_LABEL|ST_APPEND|ST_READ); /* set no label, no append */ + dev->label_type = B_BACULA_LABEL; if (!rewind_dev(dev)) { Mmsg(jcr->errmsg, _("Couldn't rewind device %s ERR=%s\n"), dev_name(dev), @@ -105,12 +107,22 @@ int read_dev_volume_label(DCR *dcr) /* Read ANSI/IBM label if so requested */ - want_ansi_ibm_label = dcr->VolCatInfo.LabelType != B_BACULA_LABEL || - dcr->device->label_type != B_BACULA_LABEL; - if (want_ansi_ibm_label || dev_cap(dev, CAP_CHECKLABELS)) { + want_ansi_label = dcr->VolCatInfo.LabelType != B_BACULA_LABEL || + dcr->device->label_type != B_BACULA_LABEL; + if (want_ansi_label || dev_cap(dev, CAP_CHECKLABELS)) { stat = read_ansi_ibm_label(dcr); /* If we want a label and didn't find it, return error */ - if (want_ansi_ibm_label && stat != VOL_OK) { + if (want_ansi_label && stat != VOL_OK) { + empty_block(block); + rewind_dev(dev); + return stat; + } + if (stat == VOL_NAME_ERROR || stat == VOL_LABEL_ERROR) { + Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"), + dev_name(dev), VolName, dev->VolHdr.VolName); + if (!dev->poll && jcr->label_errors++ > 100) { + Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg); + } empty_block(block); rewind_dev(dev); return stat; @@ -123,6 +135,7 @@ int read_dev_volume_label(DCR *dcr) /* Read the Bacula Volume label block */ record = new_record(); empty_block(block); + Dmsg0(90, "Big if statement in read_volume_label\n"); if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) { Mmsg(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bacula " @@ -143,22 +156,26 @@ int read_dev_volume_label(DCR *dcr) } else { ok = true; } + free_record(record); /* finished reading Volume record */ + empty_block(block); /* done with block */ + if (!ok) { - free_record(record); if (forge_on || jcr->ignore_label_errors) { dev->state |= ST_LABEL; /* set has Bacula label */ Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg); return VOL_OK; } - empty_block(block); rewind_dev(dev); return VOL_NO_LABEL; } - free_record(record); + /* At this point, we have read the first Bacula block, and + * then read the Bacula Volume label. Now we need to + * make sure we have the right Volume. + */ + /* If we are a streaming device, we only get one chance to read */ if (!dev_cap(dev, CAP_STREAM)) { - empty_block(block); rewind_dev(dev); } @@ -178,6 +195,9 @@ int read_dev_volume_label(DCR *dcr) Mmsg(jcr->errmsg, _("Volume on %s has bad Bacula label type: %x\n"), dev_name(dev), dev->VolHdr.LabelType); Dmsg1(30, "%s", jcr->errmsg); + if (!dev->poll && jcr->label_errors++ > 100) { + Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg); + } return VOL_LABEL_ERROR; } @@ -207,7 +227,7 @@ int read_dev_volume_label(DCR *dcr) return VOL_OK; } -/* Read the volume label by guessing the volume name. (only for mounted devices) +/* Read the volume label by guessing the volume name. (only for DVD devices) * write is true if we are reading the label before writing to the device. * * If the volume name cannot be guessed : @@ -235,14 +255,14 @@ int read_dev_volume_label_guess(DCR *dcr, bool write) /* For mounted devices, tries to guess the volume name, and read the label if possible. */ if (open_guess_name_dev(dev) < 0) { - if ((!write) || (dcr->VolCatInfo.VolCatParts > 0)) { + if (!write || dcr->VolCatInfo.VolCatParts > 0) { Mmsg2(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bacula labeled Volume."), dev_name(dev), dcr->VolumeName); Dmsg0(100, "Leave read_dev_volume_label_guess VOL_IO_ERROR (!open_guess_name_dev)\n"); return VOL_NO_LABEL; } - if (write && (dev->free_space_errno < 0)) { + if (write && dev->free_space_errno < 0) { Dmsg0(100, "Leave read_dev_volume_label_guess !free_space VOL_NO_MEDIA\n"); Mmsg2(jcr->errmsg, _("free_space error on %s. The current medium is probably not writable. ERR=%s.\n"), dev->dev_name, dev->errmsg); @@ -261,7 +281,7 @@ int read_dev_volume_label_guess(DCR *dcr, bool write) Dmsg0(100, "Leave read_dev_volume_label_guess !open_guess_name_dev\n"); return read_dev_volume_label(dcr); } else { - if (write && (dcr->dev->free_space_errno < 0)) { + if (write && dcr->dev->free_space_errno < 0) { Dmsg0(100, "Leave read_dev_volume_label_guess !free_space VOL_NO_MEDIA\n"); Mmsg2(jcr->errmsg, _("free_space error on %s. The current medium is probably not writable. ERR=%s.\n"), dev->dev_name, dev->errmsg); @@ -270,7 +290,7 @@ int read_dev_volume_label_guess(DCR *dcr, bool write) vol_label_status = read_dev_volume_label(dcr); - if ((!write) || (dcr->VolCatInfo.VolCatParts > 0)) { + if (!write || dcr->VolCatInfo.VolCatParts > 0) { Dmsg0(100, "Leave read_dev_volume_label_guess (open_guess_name_dev && (!write || dcr->VolCatInfo.VolCatParts > 0))\n"); return vol_label_status; } @@ -290,111 +310,224 @@ int read_dev_volume_label_guess(DCR *dcr, bool write) Dmsg0(100, "Leave read_dev_volume_label_guess (open_guess_name_dev && !VOL_NAME_ERROR)\n"); dev->state &= ~ST_LABEL; return read_dev_volume_label(dcr); - } - else { + } else { Dmsg0(100, "Leave read_dev_volume_label_guess (open_guess_name_dev && VOL_NAME_ERROR)\n"); return vol_label_status; } } } -/* unser_volume_label +/* + * Put a volume label into the block * - * Unserialize the Bacula Volume label into the device Volume_Label - * structure. + * Returns: false on failure + * true on success + */ +bool write_volume_label_to_block(DCR *dcr) +{ + DEV_RECORD rec; + DEVICE *dev = dcr->dev; + JCR *jcr = dcr->jcr; + DEV_BLOCK *block = dcr->block; + + Dmsg0(20, "write Label in write_volume_label_to_block()\n"); + memset(&rec, 0, sizeof(rec)); + rec.data = get_memory(SER_LENGTH_Volume_Label); + empty_block(block); /* Volume label always at beginning */ + + create_volume_label_record(dcr, &rec); + + block->BlockNumber = 0; + if (!write_record_to_block(block, &rec)) { + free_pool_memory(rec.data); + Jmsg1(jcr, M_FATAL, 0, _("Cannot write Volume label to block for device %s\n"), + dev_name(dev)); + return false; + } else { + Dmsg1(90, "Wrote label of %d bytes to block\n", rec.data_len); + } + free_pool_memory(rec.data); + return true; +} + + +/* + * Write a Volume Label + * !!! Note, this is ONLY used for writing + * a fresh volume label. Any data + * after the label will be destroyed, + * in fact, we write the label 5 times !!!! * - * Assumes that the record is already read. + * This routine expects that open_device() was previously called. * - * Returns: false on error - * true on success -*/ - -bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec) + * This routine should be used only when labeling a blank tape. + */ +bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName) { - ser_declare; + DEVICE *dev = dcr->dev; - if (rec->FileIndex != VOL_LABEL && rec->FileIndex != PRE_LABEL) { - Mmsg3(dev->errmsg, _("Expecting Volume Label, got FI=%s Stream=%s len=%d\n"), - FI_to_ascii(rec->FileIndex), - stream_to_ascii(rec->Stream, rec->FileIndex), - rec->data_len); + + Dmsg0(99, "write_volume_label()\n"); + empty_block(dcr->block); + + Dmsg1(000, "Label type=%d\n", dev->label_type); + if (!rewind_dev(dev)) { + memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); + Dmsg2(30, "Bad status on %s from rewind. ERR=%s\n", dev_name(dev), strerror_dev(dev)); if (!forge_on) { - return false; + goto bail_out; } } - dev->VolHdr.LabelType = rec->FileIndex; - dev->VolHdr.LabelSize = rec->data_len; + /* Create PRE_LABEL */ + create_volume_label(dev, VolName, PoolName); + /* + * If we have already detected an ANSI label, re-read it + * to skip past it. Otherwise, we write a new one if + * so requested. + */ + if (dev->label_type != B_BACULA_LABEL) { + if (read_ansi_ibm_label(dcr) != VOL_OK) { + rewind_dev(dev); + goto bail_out; + } + } else if (!write_ansi_ibm_label(dcr, VolName)) { + goto bail_out; + } - /* Unserialize the record into the Volume Header */ - rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label); - ser_begin(rec->data, SER_LENGTH_Volume_Label); - unser_string(dev->VolHdr.Id); - unser_uint32(dev->VolHdr.VerNum); + create_volume_label_record(dcr, dcr->rec); + dcr->rec->Stream = 0; - if (dev->VolHdr.VerNum >= 11) { - unser_btime(dev->VolHdr.label_btime); - unser_btime(dev->VolHdr.write_btime); - } else { /* old way */ - unser_float64(dev->VolHdr.label_date); - unser_float64(dev->VolHdr.label_time); + /* Temporarily mark in append state to enable writing */ + dev->state |= ST_APPEND; + if (!write_record_to_block(dcr->block, dcr->rec)) { + Dmsg2(30, "Bad Label write on %s. ERR=%s\n", dev_name(dev), strerror_dev(dev)); + goto bail_out; + } else { + Dmsg2(30, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev_name(dev)); } - unser_float64(dev->VolHdr.write_date); /* Unused with VerNum >= 11 */ - unser_float64(dev->VolHdr.write_time); /* Unused with VerNum >= 11 */ - unser_string(dev->VolHdr.VolName); - unser_string(dev->VolHdr.PrevVolName); - unser_string(dev->VolHdr.PoolName); - unser_string(dev->VolHdr.PoolType); - unser_string(dev->VolHdr.MediaType); + Dmsg0(99, "Call write_block_to_dev()\n"); + if (!write_block_to_dev(dcr)) { + Dmsg2(30, "Bad Label write on %s. ERR=%s\n", dev_name(dev), strerror_dev(dev)); + goto bail_out; + } + Dmsg0(99, " Wrote block to device\n"); - unser_string(dev->VolHdr.HostName); - unser_string(dev->VolHdr.LabelProg); - unser_string(dev->VolHdr.ProgVersion); - unser_string(dev->VolHdr.ProgDate); + if (weof_dev(dev, 1) == 0) { + dev->state |= ST_LABEL; + } - ser_end(rec->data, SER_LENGTH_Volume_Label); - Dmsg0(90, "ser_read_vol\n"); - if (debug_level >= 90) { + if (debug_level >= 20) { dump_volume_label(dev); } + dev->state &= ~ST_APPEND; /* remove append since this is PRE_LABEL */ return true; + +bail_out: + memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); + dev->state &= ~ST_APPEND; /* remove append since this is PRE_LABEL */ + return false; } /* - * Put a volume label into the block - * - * Returns: false on failure - * true on success + * Write a volume label. This is ONLY called if we have a valid Bacula + * label of type PRE_LABEL; + * Returns: true if OK + * false if unable to write it */ -bool write_volume_label_to_block(DCR *dcr) +bool rewrite_volume_label(DCR *dcr, bool recycle) { - DEV_RECORD rec; DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; - DEV_BLOCK *block = dcr->block; - Dmsg0(20, "write Label in write_volume_label_to_block()\n"); - memset(&rec, 0, sizeof(rec)); - rec.data = get_memory(SER_LENGTH_Volume_Label); - empty_block(block); /* Volume label always at beginning */ + Dmsg1(190, "set append found freshly labeled volume. dev=%x\n", dev); + dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */ + dev->state |= ST_APPEND; + if (!write_volume_label_to_block(dcr)) { + Dmsg0(200, "Error from write volume label.\n"); + return false; + } + /* + * If we are not dealing with a streaming device, + * write the block now to ensure we have write permission. + * It is better to find out now rather than later. + * We do not write the block now if this is an ANSI label. This + * avoids re-writing the ANSI label, which we do not want to do. + */ + if (!dev_cap(dev, CAP_STREAM)) { + if (!rewind_dev(dev)) { + Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device \"%s\". ERR=%s\n"), + dev_name(dev), strerror_dev(dev)); + } + if (recycle) { + if (!truncate_dev(dev)) { + Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device \"%s\". ERR=%s\n"), + dev_name(dev), strerror_dev(dev)); + } + } - create_volume_label_record(dcr, &rec); + /* + * If we have already detected an ANSI label, re-read it + * to skip past it. Otherwise, we write a new one if + * so requested. + */ + if (dev->label_type != B_BACULA_LABEL) { + if (read_ansi_ibm_label(dcr) != VOL_OK) { + rewind_dev(dev); + return false; + } + } else if (!write_ansi_ibm_label(dcr, dev->VolHdr.VolName)) { + return false; + } - block->BlockNumber = 0; - if (!write_record_to_block(block, &rec)) { - free_pool_memory(rec.data); - Jmsg1(jcr, M_FATAL, 0, _("Cannot write Volume label to block for device %s\n"), - dev_name(dev)); + /* Attempt write to check write permission */ + Dmsg0(200, "Attempt to write to device.\n"); + if (!write_block_to_dev(dcr)) { + Jmsg2(jcr, M_ERROR, 0, _("Unable to write device \"%s\". ERR=%s\n"), + dev_name(dev), strerror_dev(dev)); + Dmsg0(200, "===ERROR write block to dev\n"); + return false; + } + } + /* Set or reset Volume statistics */ + dev->VolCatInfo.VolCatJobs = 0; + dev->VolCatInfo.VolCatFiles = 0; + dev->VolCatInfo.VolCatBytes = 1; + dev->VolCatInfo.VolCatErrors = 0; + dev->VolCatInfo.VolCatBlocks = 0; + dev->VolCatInfo.VolCatRBytes = 0; + if (recycle) { + dev->VolCatInfo.VolCatMounts++; + dev->VolCatInfo.VolCatRecycles++; + } else { + dev->VolCatInfo.VolCatMounts = 1; + dev->VolCatInfo.VolCatRecycles = 0; + dev->VolCatInfo.VolCatWrites = 1; + dev->VolCatInfo.VolCatReads = 1; + } + Dmsg0(100, "dir_update_vol_info. Set Append\n"); + bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus)); + if (!dir_update_volume_info(dcr, true)) { /* indicate doing relabel */ return false; + } + if (recycle) { + Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device \"%s\", all previous data lost.\n"), + dcr->VolumeName, dev_name(dev)); } else { - Dmsg1(90, "Wrote label of %d bytes to block\n", rec.data_len); + Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device \"%s\"\n"), + dcr->VolumeName, dev_name(dev)); } - free_pool_memory(rec.data); + /* + * End writing real Volume label (from pre-labeled tape), or recycling + * the volume. + */ + Dmsg0(200, "OK from rewite vol label.\n"); return true; } + /* * create_volume_label_record * Serialize label (from dev->VolHdr structure) into device record. @@ -493,78 +626,6 @@ void create_volume_label(DEVICE *dev, const char *VolName, const char *PoolName) } } -/* - * Write a Volume Label - * !!! Note, this is ONLY used for writing - * a fresh volume label. Any data - * after the label will be destroyed, - * in fact, we write the label 5 times !!!! - * - * This routine expects that open_device() was previously called. - * - * This routine should be used only when labeling a blank tape. - */ -bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName) -{ - bool ok = false; - DEVICE *dev = dcr->dev; - - - Dmsg0(99, "write_volume_label()\n"); - empty_block(dcr->block); - /* Create PRE_LABEL */ - create_volume_label(dev, VolName, PoolName); - - if (!rewind_dev(dev)) { - memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - Dmsg2(30, "Bad status on %s from rewind. ERR=%s\n", dev_name(dev), strerror_dev(dev)); - if (!forge_on) { - return false; - } - } - - /* Write ANSI/IBM label if so requested */ - if (!write_ansi_ibm_label(dcr, VolName)) { - memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - goto bail_out; - } - - create_volume_label_record(dcr, dcr->rec); - dcr->rec->Stream = 0; - - /* Temporarily mark in append state to enable writing */ - dev->state |= ST_APPEND; - if (!write_record_to_block(dcr->block, dcr->rec)) { - Dmsg2(30, "Bad Label write on %s. ERR=%s\n", dev_name(dev), strerror_dev(dev)); - memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - goto bail_out; - } else { - Dmsg2(30, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev_name(dev)); - } - - Dmsg0(99, "Call write_block_to_dev()\n"); - if (!write_block_to_dev(dcr)) { - memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - Dmsg2(30, "Bad Label write on %s. ERR=%s\n", dev_name(dev), strerror_dev(dev)); - goto bail_out; - } - Dmsg0(99, " Wrote block to device\n"); - - if (weof_dev(dev, 1) == 0) { - dev->state |= ST_LABEL; - ok = true; - } - - if (debug_level >= 20) { - dump_volume_label(dev); - } - -bail_out: - dev->state &= ~ST_APPEND; /* remove append since this is PRE_LABEL */ - return ok; -} - - /* * Create session label * The pool memory must be released by the calling program @@ -694,6 +755,118 @@ bool write_session_label(DCR *dcr, int label) return true; } +/* unser_volume_label + * + * Unserialize the Bacula Volume label into the device Volume_Label + * structure. + * + * Assumes that the record is already read. + * + * Returns: false on error + * true on success +*/ + +bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec) +{ + ser_declare; + + if (rec->FileIndex != VOL_LABEL && rec->FileIndex != PRE_LABEL) { + Mmsg3(dev->errmsg, _("Expecting Volume Label, got FI=%s Stream=%s len=%d\n"), + FI_to_ascii(rec->FileIndex), + stream_to_ascii(rec->Stream, rec->FileIndex), + rec->data_len); + if (!forge_on) { + return false; + } + } + + dev->VolHdr.LabelType = rec->FileIndex; + dev->VolHdr.LabelSize = rec->data_len; + + + /* Unserialize the record into the Volume Header */ + rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label); + ser_begin(rec->data, SER_LENGTH_Volume_Label); + unser_string(dev->VolHdr.Id); + unser_uint32(dev->VolHdr.VerNum); + + if (dev->VolHdr.VerNum >= 11) { + unser_btime(dev->VolHdr.label_btime); + unser_btime(dev->VolHdr.write_btime); + } else { /* old way */ + unser_float64(dev->VolHdr.label_date); + unser_float64(dev->VolHdr.label_time); + } + unser_float64(dev->VolHdr.write_date); /* Unused with VerNum >= 11 */ + unser_float64(dev->VolHdr.write_time); /* Unused with VerNum >= 11 */ + + unser_string(dev->VolHdr.VolName); + unser_string(dev->VolHdr.PrevVolName); + unser_string(dev->VolHdr.PoolName); + unser_string(dev->VolHdr.PoolType); + unser_string(dev->VolHdr.MediaType); + + unser_string(dev->VolHdr.HostName); + unser_string(dev->VolHdr.LabelProg); + unser_string(dev->VolHdr.ProgVersion); + unser_string(dev->VolHdr.ProgDate); + + ser_end(rec->data, SER_LENGTH_Volume_Label); + Dmsg0(90, "ser_read_vol\n"); + if (debug_level >= 90) { + dump_volume_label(dev); + } + return true; +} + + +bool unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec) +{ + ser_declare; + + rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label); + unser_begin(rec->data, SER_LENGTH_Session_Label); + unser_string(label->Id); + unser_uint32(label->VerNum); + unser_uint32(label->JobId); + if (label->VerNum >= 11) { + unser_btime(label->write_btime); + } else { + unser_float64(label->write_date); + } + unser_float64(label->write_time); + unser_string(label->PoolName); + unser_string(label->PoolType); + unser_string(label->JobName); + unser_string(label->ClientName); + if (label->VerNum >= 10) { + unser_string(label->Job); /* Unique name of this Job */ + unser_string(label->FileSetName); + unser_uint32(label->JobType); + unser_uint32(label->JobLevel); + } + if (label->VerNum >= 11) { + unser_string(label->FileSetMD5); + } else { + label->FileSetMD5[0] = 0; + } + if (rec->FileIndex == EOS_LABEL) { + unser_uint32(label->JobFiles); + unser_uint64(label->JobBytes); + unser_uint32(label->StartBlock); + unser_uint32(label->EndBlock); + unser_uint32(label->StartFile); + unser_uint32(label->EndFile); + unser_uint32(label->JobErrors); + if (label->VerNum >= 11) { + unser_uint32(label->JobStatus); + } else { + label->JobStatus = JS_Terminated; /* kludge */ + } + } + return true; +} + void dump_volume_label(DEVICE *dev) { int dbl = debug_level; @@ -765,53 +938,6 @@ bail_out: debug_level = dbl; } -bool unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec) -{ - ser_declare; - - rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label); - unser_begin(rec->data, SER_LENGTH_Session_Label); - unser_string(label->Id); - unser_uint32(label->VerNum); - unser_uint32(label->JobId); - if (label->VerNum >= 11) { - unser_btime(label->write_btime); - } else { - unser_float64(label->write_date); - } - unser_float64(label->write_time); - unser_string(label->PoolName); - unser_string(label->PoolType); - unser_string(label->JobName); - unser_string(label->ClientName); - if (label->VerNum >= 10) { - unser_string(label->Job); /* Unique name of this Job */ - unser_string(label->FileSetName); - unser_uint32(label->JobType); - unser_uint32(label->JobLevel); - } - if (label->VerNum >= 11) { - unser_string(label->FileSetMD5); - } else { - label->FileSetMD5[0] = 0; - } - if (rec->FileIndex == EOS_LABEL) { - unser_uint32(label->JobFiles); - unser_uint64(label->JobBytes); - unser_uint32(label->StartBlock); - unser_uint32(label->EndBlock); - unser_uint32(label->StartFile); - unser_uint32(label->EndFile); - unser_uint32(label->JobErrors); - if (label->VerNum >= 11) { - unser_uint32(label->JobStatus); - } else { - label->JobStatus = JS_Terminated; /* kludge */ - } - } - return true; -} - static void dump_session_label(DEV_RECORD *rec, const char *type) { @@ -968,187 +1094,3 @@ void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose) } debug_level = dbl; } - -/* - * Write an ANSI or IBM 80 character tape label - * Assume we are positioned at the beginning of the tape. - * Returns: true of OK - * false if error - */ -bool write_ansi_ibm_label(DCR *dcr, const char *VolName) -{ - DEVICE *dev = dcr->dev; - JCR *jcr = dcr->jcr; - char label[80]; /* tape label */ - char date[20]; /* ansi date buffer */ - time_t now; - int len, stat, label_type; - - /* - * If the Device requires a specific label type use it, - * otherwise, use the type requested by the Director - */ - if (dcr->device->label_type != B_BACULA_LABEL) { - label_type = dcr->device->label_type; /* force label type */ - } else { - label_type = dcr->VolCatInfo.LabelType; /* accept Dir type */ - } - - switch (label_type) { - case B_BACULA_LABEL: - return true; - case B_ANSI_LABEL: - case B_IBM_LABEL: - ser_declare; - Dmsg1(000, "Write ANSI label type=%d\n", label_type); - len = strlen(VolName); - if (len > 6) { - Jmsg1(jcr, M_FATAL, 0, _("ANSI Volume label name \"%s\" longer than 6 chars.\n"), - VolName); - return false; - } - memset(label, ' ', sizeof(label)); - ser_begin(label, sizeof(label)); - ser_bytes("VOL1", 4); - ser_bytes(VolName, len); - label[79] = '3'; /* ANSI label flag */ - /* Write VOL1 label */ - stat = write(dev->fd, label, sizeof(label)); - if (stat != sizeof(label)) { - berrno be; - Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI VOL1 label. ERR=%s\n"), - be.strerror()); - return false; - } - /* Now construct HDR1 label */ - ser_begin(label, sizeof(label)); - ser_bytes("HDR1", 4); - ser_bytes("BACULA.DATA", 11); /* Filename field */ - ser_begin(&label[21], sizeof(label)-21); /* fileset field */ - ser_bytes(VolName, len); /* write Vol Ser No. */ - ser_begin(&label[27], sizeof(label)-27); - ser_bytes("00010001000100", 14); /* File section, File seq no, Generation no */ - now = time(NULL); - ser_bytes(ansi_date(now, date), 6); /* current date */ - ser_bytes(ansi_date(now - 24 * 3600, date), 6); /* created yesterday */ - ser_bytes(" 000000Bacula ", 27); - /* Write HDR1 label */ - stat = write(dev->fd, label, sizeof(label)); - if (stat != sizeof(label)) { - berrno be; - Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"), - be.strerror()); - return false; - } - /* Now construct HDR2 label */ - memset(label, ' ', sizeof(label)); - ser_begin(label, sizeof(label)); - ser_bytes("HDR2F3200032000", 15); - /* Write HDR1 label */ - stat = write(dev->fd, label, sizeof(label)); - if (stat != sizeof(label)) { - berrno be; - Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"), - be.strerror()); - return false; - } - if (weof_dev(dev, 1) < 0) { - Jmsg(jcr, M_FATAL, 0, _("Error writing EOF to tape. ERR=%s"), dev->errmsg); - return false; - } - return true; - default: - Jmsg0(jcr, M_ABORT, 0, _("write_ansi_ibm_label called for non-ANSI/IBM type\n")); - return false; /* should not get here */ - } -} - -static int read_ansi_ibm_label(DCR *dcr) -{ - DEVICE *dev = dcr->dev; - JCR *jcr = dcr->jcr; - char label[80]; /* tape label */ - int stat, i, num_rec; - - /* - * Read VOL1, HDR1, HDR2 labels, but ignore the data - * If tape read the following EOF mark, on disk do - * not read. - */ - Dmsg0(000, "Read ansi label.\n"); - if (dev->is_tape()) { - num_rec = 4; - } else { - num_rec = 3; - } - for (i=0; i < num_rec; i++) { - do { - stat = read(dev->fd, label, sizeof(label)); - } while (stat == -1 && errno == EINTR); - if (stat < 0) { - berrno be; - clrerror_dev(dev, -1); - Dmsg1(000, "Read device got: ERR=%s\n", be.strerror()); - Mmsg2(dev->errmsg, _("Read error on device %s in ANSI/IBM label. ERR=%s\n"), - dev->dev_name, be.strerror()); - Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg); - dev->VolCatInfo.VolCatErrors++; - return VOL_IO_ERROR; - } - if (stat == 0) { - if (dev->at_eof()) { - dev->state |= ST_EOT; - return VOL_NO_LABEL; - } else { - dev->state |= ST_EOF; - } - } - switch (i) { - case 0: /* Want VOL1 label */ - if (stat != 80 || strncmp("VOL1", label, 4) != 0) { - Dmsg0(000, "No VOL1 label\n"); - return VOL_NO_LABEL; - } - break; - case 1: - if (stat != 80 || strncmp("HDR1", label, 4) != 0) { - Dmsg0(000, "No HDR1 label\n"); - return VOL_NO_LABEL; - } - if (strncmp("BACULA.DATA", &label[4], 11) != 0) { - Dmsg0(000, "HD1 not Bacula label\n"); - return VOL_NAME_ERROR; - } - break; - case 2: - if (stat != 80 || strncmp("HDR2", label, 4) != 0) { - Dmsg0(000, "No HDR2 label\n"); - return VOL_NO_LABEL; - } - break; - case 3: /* Should get EOF here */ - if (stat != 0) { - Dmsg0(000, "No EOF\n"); - return VOL_IO_ERROR; - } - break; - } - } - /* ***FIXME*** add detection of IBM labels */ - dev->label_type = B_ANSI_LABEL; - Dmsg0(000, "ANSI label OK\n"); - return VOL_OK; -} - - -static char *ansi_date(time_t td, char *buf) -{ - struct tm *tm; - - if (td == 0) { - td = time(NULL); - } - tm = gmtime(&td); - bsnprintf(buf, 10, " %05d ", 1000 * (tm->tm_year + 1900 - 2000) + tm->tm_yday); - return buf; -} diff --git a/bacula/src/stored/mount.c b/bacula/src/stored/mount.c index cf678ae0a6..fdda8d2f01 100644 --- a/bacula/src/stored/mount.c +++ b/bacula/src/stored/mount.c @@ -30,8 +30,6 @@ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ -static bool rewrite_volume_label(DCR *dcr, bool recycle); - /* * If release is set, we rewind the current volume, @@ -375,86 +373,6 @@ read_volume: return true; } -/* - * Write a volume label. - * Returns: true if OK - * false if unable to write it - */ -static bool rewrite_volume_label(DCR *dcr, bool recycle) -{ - DEVICE *dev = dcr->dev; - JCR *jcr = dcr->jcr; - - Dmsg1(190, "set append found freshly labeled volume. dev=%x\n", dev); - dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */ - dev->state |= ST_APPEND; - if (!write_volume_label_to_block(dcr)) { - Dmsg0(200, "Error from write volume label.\n"); - return false; - } - /* - * If we are not dealing with a streaming device, - * write the block now to ensure we have write permission. - * It is better to find out now rather than later. - */ - if (!dev_cap(dev, CAP_STREAM)) { - if (!rewind_dev(dev)) { - Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device \"%s\". ERR=%s\n"), - dev_name(dev), strerror_dev(dev)); - } - if (recycle) { - if (!truncate_dev(dev)) { - Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device \"%s\". ERR=%s\n"), - dev_name(dev), strerror_dev(dev)); - } - } - /* Attempt write to check write permission */ - Dmsg0(200, "Attempt to write to device.\n"); - if (!write_ansi_ibm_label(dcr, dev->VolHdr.VolName)) { - return false; - } - if (!write_block_to_dev(dcr)) { - Jmsg2(jcr, M_ERROR, 0, _("Unable to write device \"%s\". ERR=%s\n"), - dev_name(dev), strerror_dev(dev)); - Dmsg0(200, "===ERROR write block to dev\n"); - return false; - } - } - /* Set or reset Volume statistics */ - dev->VolCatInfo.VolCatJobs = 0; - dev->VolCatInfo.VolCatFiles = 0; - dev->VolCatInfo.VolCatBytes = 1; - dev->VolCatInfo.VolCatErrors = 0; - dev->VolCatInfo.VolCatBlocks = 0; - dev->VolCatInfo.VolCatRBytes = 0; - if (recycle) { - dev->VolCatInfo.VolCatMounts++; - dev->VolCatInfo.VolCatRecycles++; - } else { - dev->VolCatInfo.VolCatMounts = 1; - dev->VolCatInfo.VolCatRecycles = 0; - dev->VolCatInfo.VolCatWrites = 1; - dev->VolCatInfo.VolCatReads = 1; - } - Dmsg0(100, "dir_update_vol_info. Set Append\n"); - bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus)); - if (!dir_update_volume_info(dcr, true)) { /* indicate doing relabel */ - return false; - } - if (recycle) { - Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device \"%s\", all previous data lost.\n"), - dcr->VolumeName, dev_name(dev)); - } else { - Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device \"%s\"\n"), - dcr->VolumeName, dev_name(dev)); - } - /* - * End writing real Volume label (from pre-labeled tape), or recycling - * the volume. - */ - Dmsg0(200, "OK from rewite vol label.\n"); - return true; -} /* @@ -518,6 +436,7 @@ void release_volume(DCR *dcr) memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); /* Force re-read of label */ dev->state &= ~(ST_LABEL|ST_READ|ST_APPEND); + dev->label_type = B_BACULA_LABEL; dcr->VolumeName[0] = 0; if (dev->is_open() && (!dev->is_tape() || !dev_cap(dev, CAP_ALWAYSOPEN))) { diff --git a/bacula/src/stored/protos.h b/bacula/src/stored/protos.h index f5ab5defe5..b94363a825 100644 --- a/bacula/src/stored/protos.h +++ b/bacula/src/stored/protos.h @@ -163,8 +163,10 @@ void create_session_label(DCR *dcr, DEV_RECORD *rec, int label); void create_volume_label(DEVICE *dev, const char *VolName, const char *PoolName); bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName); bool write_ansi_ibm_label(DCR *dcr, const char *VolName); +int read_ansi_ibm_label(DCR *dcr); bool write_session_label(DCR *dcr, int label); bool write_volume_label_to_block(DCR *dcr); +bool rewrite_volume_label(DCR *dcr, bool recycle); void dump_volume_label(DEVICE *dev); void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose); bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec); diff --git a/bacula/src/stored/record.h b/bacula/src/stored/record.h index 2d6f5a4dc5..b586a7635e 100644 --- a/bacula/src/stored/record.h +++ b/bacula/src/stored/record.h @@ -8,7 +8,7 @@ * */ /* - Copyright (C) 2000-2004 Kern Sibbald and John Walker + Copyright (C) 2000-2005 Kern Sibbald This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/bacula/src/version.h b/bacula/src/version.h index 6175c6a35e..bbde218477 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #undef VERSION #define VERSION "1.37.3" -#define BDATE "07 February 2005" -#define LSMDATE "07Feb05" +#define BDATE "09 February 2005" +#define LSMDATE "09Feb05" /* Debug flags */ #undef DEBUG