3 * label.c Bacula routines to handle labels
11 Copyright (C) 2000-2006 Kern Sibbald
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 version 2 as amended with additional clauses defined in the
16 file LICENSE in the main source directory.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 the file LICENSE for additional details.
25 #include "bacula.h" /* pull in global headers */
26 #include "stored.h" /* pull in Storage Deamon headers */
28 /* Forward referenced functions */
29 static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec);
31 extern char my_name[];
32 extern int debug_level;
35 * Read the volume label
37 * If dcr->VolumeName == NULL, we accept any Bacula Volume
38 * If dcr->VolumeName[0] == 0, we accept any Bacula Volume
39 * otherwise dcr->VolumeName must match the Volume.
41 * If VolName given, ensure that it matches
43 * Returns VOL_ code as defined in record.h
45 * VOL_OK good label found
46 * VOL_NO_LABEL volume not labeled
47 * VOL_IO_ERROR I/O error reading tape
48 * VOL_NAME_ERROR label has wrong name
49 * VOL_CREATE_ERROR Error creating label
50 * VOL_VERSION_ERROR label has wrong version
51 * VOL_LABEL_ERROR bad label type
52 * VOL_NO_MEDIA no media in drive
54 * The dcr block is emptied on return, and the Volume is
57 int read_dev_volume_label(DCR *dcr)
60 DEVICE *dev = dcr->dev;
61 char *VolName = dcr->VolumeName;
64 DEV_BLOCK *block = dcr->block;
67 bool have_ansi_label = false;
69 Dmsg4(100, "Enter read_volume_label res=%d device=%s vol=%s dev_Vol=%s\n",
70 dev->reserved_device, dev->print_name(), VolName,
71 dev->VolHdr.VolumeName[0]?dev->VolHdr.VolumeName:"*NULL*");
73 if (!dev->is_open()) {
74 if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
78 if (dev->is_labeled()) { /* did we already read label? */
79 /* Compare Volume Names allow special wild card */
80 if (VolName && *VolName && *VolName != '*' && strcmp(dev->VolHdr.VolumeName, VolName) != 0) {
81 Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
82 dev->print_name(), VolName, dev->VolHdr.VolumeName);
84 * Cancel Job if too many label errors
87 if (!dev->poll && jcr->label_errors++ > 100) {
88 Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
90 Dmsg0(150, "return VOL_NAME_ERROR\n");
91 stat = VOL_NAME_ERROR;
94 Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
95 return VOL_OK; /* label already read */
101 dev->label_type = B_BACULA_LABEL;
103 if (!dev->rewind(dcr)) {
104 Mmsg(jcr->errmsg, _("Couldn't rewind device %s: ERR=%s\n"),
105 dev->print_name(), dev->bstrerror());
106 Dmsg1(30, "return VOL_NO_MEDIA: %s", jcr->errmsg);
109 bstrncpy(dev->VolHdr.Id, "**error**", sizeof(dev->VolHdr.Id));
111 /* Read ANSI/IBM label if so requested */
113 want_ansi_label = dcr->VolCatInfo.LabelType != B_BACULA_LABEL ||
114 dcr->device->label_type != B_BACULA_LABEL;
115 if (want_ansi_label || dev_cap(dev, CAP_CHECKLABELS)) {
116 stat = read_ansi_ibm_label(dcr);
117 /* If we want a label and didn't find it, return error */
118 if (want_ansi_label && stat != VOL_OK) {
121 if (stat == VOL_NAME_ERROR || stat == VOL_LABEL_ERROR) {
122 Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
123 dev->print_name(), VolName, dev->VolHdr.VolumeName);
124 if (!dev->poll && jcr->label_errors++ > 100) {
125 Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
129 if (stat != VOL_OK) { /* Not an ANSI/IBM label, so re-read */
132 have_ansi_label = true;
136 /* Read the Bacula Volume label block */
137 record = new_record();
140 Dmsg0(90, "Big if statement in read_volume_label\n");
141 if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) {
142 Mmsg(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bacula "
143 "labeled Volume, because: ERR=%s"), NPRT(VolName),
144 dev->print_name(), dev->bstrerror());
145 Dmsg1(30, "%s", jcr->errmsg);
146 } else if (!read_record_from_block(block, record)) {
147 Mmsg(jcr->errmsg, _("Could not read Volume label from block.\n"));
148 Dmsg1(30, "%s", jcr->errmsg);
149 } else if (!unser_volume_label(dev, record)) {
150 Mmsg(jcr->errmsg, _("Could not unserialize Volume label: ERR=%s\n"),
152 Dmsg1(30, "%s", jcr->errmsg);
153 } else if (strcmp(dev->VolHdr.Id, BaculaId) != 0 &&
154 strcmp(dev->VolHdr.Id, OldBaculaId) != 0) {
155 Mmsg(jcr->errmsg, _("Volume Header Id bad: %s\n"), dev->VolHdr.Id);
156 Dmsg1(30, "%s", jcr->errmsg);
160 free_record(record); /* finished reading Volume record */
163 if (forge_on || jcr->ignore_label_errors) {
164 dev->set_labeled(); /* set has Bacula label */
165 Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg);
173 /* At this point, we have read the first Bacula block, and
174 * then read the Bacula Volume label. Now we need to
175 * make sure we have the right Volume.
179 if (dev->VolHdr.VerNum != BaculaTapeVersion &&
180 dev->VolHdr.VerNum != OldCompatibleBaculaTapeVersion1 &&
181 dev->VolHdr.VerNum != OldCompatibleBaculaTapeVersion2) {
182 Mmsg(jcr->errmsg, _("Volume on %s has wrong Bacula version. Wanted %d got %d\n"),
183 dev->print_name(), BaculaTapeVersion, dev->VolHdr.VerNum);
184 Dmsg1(30, "VOL_VERSION_ERROR: %s", jcr->errmsg);
185 stat = VOL_VERSION_ERROR;
189 /* We are looking for either an unused Bacula tape (PRE_LABEL) or
190 * a Bacula volume label (VOL_LABEL)
192 if (dev->VolHdr.LabelType != PRE_LABEL && dev->VolHdr.LabelType != VOL_LABEL) {
193 Mmsg(jcr->errmsg, _("Volume on %s has bad Bacula label type: %x\n"),
194 dev->print_name(), dev->VolHdr.LabelType);
195 Dmsg1(30, "%s", jcr->errmsg);
196 if (!dev->poll && jcr->label_errors++ > 100) {
197 Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
199 Dmsg0(150, "return VOL_LABEL_ERROR\n");
200 stat = VOL_LABEL_ERROR;
204 dev->set_labeled(); /* set has Bacula label */
205 new_volume(dcr, dev->VolHdr.VolumeName);
207 /* Compare Volume Names */
208 Dmsg2(30, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName);
209 if (VolName && *VolName && *VolName != '*' && strcmp(dev->VolHdr.VolumeName, VolName) != 0) {
210 Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
211 dev->print_name(), VolName, dev->VolHdr.VolumeName);
212 Dmsg1(30, "%s", jcr->errmsg);
214 * Cancel Job if too many label errors
215 * => we are in a loop
217 if (!dev->poll && jcr->label_errors++ > 100) {
218 Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
220 Dmsg0(150, "return VOL_NAME_ERROR\n");
221 stat = VOL_NAME_ERROR;
224 Dmsg1(30, "Copy vol_name=%s\n", dev->VolHdr.VolumeName);
226 if (debug_level >= 10) {
227 dump_volume_label(dev);
229 Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
230 /* If we are a streaming device, we only get one chance to read */
231 if (!dev_cap(dev, CAP_STREAM)) {
233 if (have_ansi_label) {
234 stat = read_ansi_ibm_label(dcr);
235 /* If we want a label and didn't find it, return error */
236 if (stat != VOL_OK) {
247 Dmsg1(150, "return %d\n", stat);
252 * Put a volume label into the block
254 * Returns: false on failure
257 bool write_volume_label_to_block(DCR *dcr)
260 DEVICE *dev = dcr->dev;
262 DEV_BLOCK *block = dcr->block;
264 Dmsg0(20, "write Label in write_volume_label_to_block()\n");
265 memset(&rec, 0, sizeof(rec));
266 rec.data = get_memory(SER_LENGTH_Volume_Label);
267 empty_block(block); /* Volume label always at beginning */
269 create_volume_label_record(dcr, &rec);
271 block->BlockNumber = 0;
272 if (!write_record_to_block(block, &rec)) {
273 free_pool_memory(rec.data);
274 Jmsg1(jcr, M_FATAL, 0, _("Cannot write Volume label to block for device %s\n"),
278 Dmsg1(90, "Wrote label of %d bytes to block\n", rec.data_len);
280 free_pool_memory(rec.data);
286 * Write a Volume Label
287 * !!! Note, this is ONLY used for writing
288 * a fresh volume label. Any data
289 * after the label will be destroyed,
290 * in fact, we write the label 5 times !!!!
292 * This routine should be used only when labeling a blank tape.
294 bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName)
296 DEVICE *dev = dcr->dev;
299 Dmsg0(99, "write_volume_label()\n");
300 empty_block(dcr->block);
302 if (dev->open(dcr, OPEN_READ_WRITE) < 0) {
303 /* If device is not tape, attempt to create it */
304 if (dev->is_tape() || dev->open(dcr, CREATE_READ_WRITE) < 0) {
308 Dmsg1(150, "Label type=%d\n", dev->label_type);
309 if (!dev->rewind(dcr)) {
311 memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
312 Dmsg2(30, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), dev->bstrerror());
318 /* Create PRE_LABEL */
319 create_volume_label(dev, VolName, PoolName);
322 * If we have already detected an ANSI label, re-read it
323 * to skip past it. Otherwise, we write a new one if
326 if (dev->label_type != B_BACULA_LABEL) {
327 if (read_ansi_ibm_label(dcr) != VOL_OK) {
331 } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, VolName)) {
335 create_volume_label_record(dcr, dcr->rec);
336 dcr->rec->Stream = 0;
338 /* Temporarily mark in append state to enable writing */
340 if (!write_record_to_block(dcr->block, dcr->rec)) {
341 Dmsg2(30, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->bstrerror());
344 Dmsg2(30, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev->print_name());
347 Dmsg0(99, "Call write_block_to_dev()\n");
348 if (!write_block_to_dev(dcr)) {
349 Dmsg2(30, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->bstrerror());
352 Dmsg0(99, " Wrote block to device\n");
356 write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
359 if (debug_level >= 20) {
360 dump_volume_label(dev);
362 new_volume(dcr, VolName);
363 dev->clear_append(); /* remove append since this is PRE_LABEL */
368 memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
369 dev->clear_append(); /* remove append since this is PRE_LABEL */
374 * Write a volume label. This is ONLY called if we have a valid Bacula
375 * label of type PRE_LABEL;
376 * Returns: true if OK
377 * false if unable to write it
379 bool rewrite_volume_label(DCR *dcr, bool recycle)
381 DEVICE *dev = dcr->dev;
384 if (dev->open(dcr, OPEN_READ_WRITE) < 0) {
387 Dmsg2(190, "set append found freshly labeled volume. fd=%d dev=%x\n", dev->fd, dev);
388 dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
390 if (!write_volume_label_to_block(dcr)) {
391 Dmsg0(200, "Error from write volume label.\n");
395 * If we are not dealing with a streaming device,
396 * write the block now to ensure we have write permission.
397 * It is better to find out now rather than later.
398 * We do not write the block now if this is an ANSI label. This
399 * avoids re-writing the ANSI label, which we do not want to do.
401 if (!dev_cap(dev, CAP_STREAM)) {
402 if (!dev->rewind(dcr)) {
403 Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s: ERR=%s\n"),
404 dev->print_name(), dev->bstrerror());
407 if (!dev->truncate(dcr)) {
408 Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s: ERR=%s\n"),
409 dev->print_name(), dev->bstrerror());
414 * If we have already detected an ANSI label, re-read it
415 * to skip past it. Otherwise, we write a new one if
418 if (dev->label_type != B_BACULA_LABEL) {
419 if (read_ansi_ibm_label(dcr) != VOL_OK) {
423 } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, dev->VolHdr.VolumeName)) {
427 /* Attempt write to check write permission */
428 Dmsg1(200, "Attempt to write to device fd=%d.\n", dev->fd);
429 if (!write_block_to_dev(dcr)) {
430 Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s: ERR=%s\n"),
431 dev->print_name(), dev->bstrerror());
432 Dmsg0(200, "===ERROR write block to dev\n");
436 /* Set or reset Volume statistics */
437 dev->VolCatInfo.VolCatJobs = 0;
438 dev->VolCatInfo.VolCatFiles = 0;
439 dev->VolCatInfo.VolCatBytes = 1;
440 dev->VolCatInfo.VolCatErrors = 0;
441 dev->VolCatInfo.VolCatBlocks = 0;
442 dev->VolCatInfo.VolCatRBytes = 0;
444 dev->VolCatInfo.VolCatMounts++;
445 dev->VolCatInfo.VolCatRecycles++;
447 dev->VolCatInfo.VolCatMounts = 1;
448 dev->VolCatInfo.VolCatRecycles = 0;
449 dev->VolCatInfo.VolCatWrites = 1;
450 dev->VolCatInfo.VolCatReads = 1;
452 Dmsg0(150, "dir_update_vol_info. Set Append\n");
453 bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus));
454 if (!dir_update_volume_info(dcr, true)) { /* indicate doing relabel */
458 Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device %s, all previous data lost.\n"),
459 dcr->VolumeName, dev->print_name());
461 Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device %s\n"),
462 dcr->VolumeName, dev->print_name());
465 * End writing real Volume label (from pre-labeled tape), or recycling
468 Dmsg0(200, "OK from rewite vol label.\n");
474 * create_volume_label_record
475 * Serialize label (from dev->VolHdr structure) into device record.
476 * Assumes that the dev->VolHdr structure is properly
479 static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec)
483 DEVICE *dev = dcr->dev;
487 /* Serialize the label into the device record. */
489 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label);
490 ser_begin(rec->data, SER_LENGTH_Volume_Label);
491 ser_string(dev->VolHdr.Id);
493 ser_uint32(dev->VolHdr.VerNum);
495 if (dev->VolHdr.VerNum >= 11) {
496 ser_btime(dev->VolHdr.label_btime);
497 dev->VolHdr.write_btime = get_current_btime();
498 ser_btime(dev->VolHdr.write_btime);
499 dev->VolHdr.write_date = 0;
500 dev->VolHdr.write_time = 0;
502 /* OLD WAY DEPRECATED */
503 ser_float64(dev->VolHdr.label_date);
504 ser_float64(dev->VolHdr.label_time);
505 get_current_time(&dt);
506 dev->VolHdr.write_date = dt.julian_day_number;
507 dev->VolHdr.write_time = dt.julian_day_fraction;
509 ser_float64(dev->VolHdr.write_date); /* 0 if VerNum >= 11 */
510 ser_float64(dev->VolHdr.write_time); /* 0 if VerNum >= 11 */
512 ser_string(dev->VolHdr.VolumeName);
513 ser_string(dev->VolHdr.PrevVolumeName);
514 ser_string(dev->VolHdr.PoolName);
515 ser_string(dev->VolHdr.PoolType);
516 ser_string(dev->VolHdr.MediaType);
518 ser_string(dev->VolHdr.HostName);
519 ser_string(dev->VolHdr.LabelProg);
520 ser_string(dev->VolHdr.ProgVersion);
521 ser_string(dev->VolHdr.ProgDate);
523 ser_end(rec->data, SER_LENGTH_Volume_Label);
524 rec->data_len = ser_length(rec->data);
525 rec->FileIndex = dev->VolHdr.LabelType;
526 rec->VolSessionId = jcr->VolSessionId;
527 rec->VolSessionTime = jcr->VolSessionTime;
528 rec->Stream = jcr->NumVolumes;
529 Dmsg2(150, "Created Vol label rec: FI=%s len=%d\n", FI_to_ascii(buf, rec->FileIndex),
535 * Create a volume label in memory
537 void create_volume_label(DEVICE *dev, const char *VolName, const char *PoolName)
539 DEVRES *device = (DEVRES *)dev->device;
541 Dmsg0(90, "Start create_volume_label()\n");
545 free_volume(dev); /* release any old volume */
546 memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
548 bstrncpy(dev->VolHdr.Id, BaculaId, sizeof(dev->VolHdr.Id));
549 dev->VolHdr.VerNum = BaculaTapeVersion;
550 dev->VolHdr.LabelType = PRE_LABEL; /* Mark tape as unused */
551 bstrncpy(dev->VolHdr.VolumeName, VolName, sizeof(dev->VolHdr.VolumeName));
552 bstrncpy(dev->VolHdr.PoolName, PoolName, sizeof(dev->VolHdr.PoolName));
553 bstrncpy(dev->VolHdr.MediaType, device->media_type, sizeof(dev->VolHdr.MediaType));
555 bstrncpy(dev->VolHdr.PoolType, "Backup", sizeof(dev->VolHdr.PoolType));
557 dev->VolHdr.label_btime = get_current_btime();
558 dev->VolHdr.label_date = 0;
559 dev->VolHdr.label_time = 0;
561 if (gethostname(dev->VolHdr.HostName, sizeof(dev->VolHdr.HostName)) != 0) {
562 dev->VolHdr.HostName[0] = 0;
564 bstrncpy(dev->VolHdr.LabelProg, my_name, sizeof(dev->VolHdr.LabelProg));
565 sprintf(dev->VolHdr.ProgVersion, "Ver. %s %s", VERSION, BDATE);
566 sprintf(dev->VolHdr.ProgDate, "Build %s %s", __DATE__, __TIME__);
567 dev->set_labeled(); /* set has Bacula label */
568 if (debug_level >= 90) {
569 dump_volume_label(dev);
574 * Create session label
575 * The pool memory must be released by the calling program
577 void create_session_label(DCR *dcr, DEV_RECORD *rec, int label)
582 rec->VolSessionId = jcr->VolSessionId;
583 rec->VolSessionTime = jcr->VolSessionTime;
584 rec->Stream = jcr->JobId;
586 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label);
587 ser_begin(rec->data, SER_LENGTH_Session_Label);
588 ser_string(BaculaId);
589 ser_uint32(BaculaTapeVersion);
591 ser_uint32(jcr->JobId);
593 /* Changed in VerNum 11 */
594 ser_btime(get_current_btime());
597 ser_string(dcr->pool_name);
598 ser_string(dcr->pool_type);
599 ser_string(jcr->job_name); /* base Job name */
600 ser_string(jcr->client_name);
602 /* Added in VerNum 10 */
603 ser_string(jcr->Job); /* Unique name of this Job */
604 ser_string(jcr->fileset_name);
605 ser_uint32(jcr->JobType);
606 ser_uint32(jcr->JobLevel);
607 /* Added in VerNum 11 */
608 ser_string(jcr->fileset_md5);
610 if (label == EOS_LABEL) {
611 ser_uint32(jcr->JobFiles);
612 ser_uint64(jcr->JobBytes);
613 ser_uint32(dcr->StartBlock);
614 ser_uint32(dcr->EndBlock);
615 ser_uint32(dcr->StartFile);
616 ser_uint32(dcr->EndFile);
617 ser_uint32(jcr->JobErrors);
619 /* Added in VerNum 11 */
620 ser_uint32(jcr->JobStatus);
622 ser_end(rec->data, SER_LENGTH_Session_Label);
623 rec->data_len = ser_length(rec->data);
626 /* Write session label
627 * Returns: false on failure
630 bool write_session_label(DCR *dcr, int label)
633 DEVICE *dev = dcr->dev;
635 DEV_BLOCK *block = dcr->block;
636 char buf1[100], buf2[100];
639 Dmsg1(90, "session_label record=%x\n", rec);
642 if (dev->is_tape()) {
643 dcr->StartBlock = dev->block_num;
644 dcr->StartFile = dev->file;
646 dcr->StartBlock = (uint32_t)dev->file_addr;
647 dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
651 if (dev->is_tape()) {
652 dcr->EndBlock = dev->EndBlock;
653 dcr->EndFile = dev->EndFile;
655 dcr->EndBlock = (uint32_t)dev->file_addr;
656 dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
660 Jmsg1(jcr, M_ABORT, 0, _("Bad session label = %d\n"), label);
663 create_session_label(dcr, rec, label);
664 rec->FileIndex = label;
667 * We guarantee that the session record can totally fit
668 * into a block. If not, write the block, and put it in
669 * the next block. Having the sesssion record totally in
670 * one block makes reading them much easier (no need to
671 * read the next block).
673 if (!can_write_record_to_block(block, rec)) {
674 Dmsg0(150, "Cannot write session label to block.\n");
675 if (!write_block_to_device(dcr)) {
676 Dmsg0(90, "Got session label write_block_to_dev error.\n");
677 /* ****FIXME***** errno is not set here */
678 Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
679 dev_vol_name(dev), strerror(errno));
684 if (!write_record_to_block(block, rec)) {
685 Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
686 dev_vol_name(dev), strerror(errno));
691 Dmsg6(20, "Write sesson_label record JobId=%d FI=%s SessId=%d Strm=%s len=%d "
692 "remainder=%d\n", jcr->JobId,
693 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
694 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
698 Dmsg2(20, "Leave write_session_label Block=%d File=%d\n",
699 dev->block_num, dev->file);
703 /* unser_volume_label
705 * Unserialize the Bacula Volume label into the device Volume_Label
708 * Assumes that the record is already read.
710 * Returns: false on error
714 bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec)
717 char buf1[100], buf2[100];
719 if (rec->FileIndex != VOL_LABEL && rec->FileIndex != PRE_LABEL) {
720 Mmsg3(dev->errmsg, _("Expecting Volume Label, got FI=%s Stream=%s len=%d\n"),
721 FI_to_ascii(buf1, rec->FileIndex),
722 stream_to_ascii(buf2, rec->Stream, rec->FileIndex),
729 dev->VolHdr.LabelType = rec->FileIndex;
730 dev->VolHdr.LabelSize = rec->data_len;
733 /* Unserialize the record into the Volume Header */
734 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label);
735 ser_begin(rec->data, SER_LENGTH_Volume_Label);
736 unser_string(dev->VolHdr.Id);
737 unser_uint32(dev->VolHdr.VerNum);
739 if (dev->VolHdr.VerNum >= 11) {
740 unser_btime(dev->VolHdr.label_btime);
741 unser_btime(dev->VolHdr.write_btime);
742 } else { /* old way */
743 unser_float64(dev->VolHdr.label_date);
744 unser_float64(dev->VolHdr.label_time);
746 unser_float64(dev->VolHdr.write_date); /* Unused with VerNum >= 11 */
747 unser_float64(dev->VolHdr.write_time); /* Unused with VerNum >= 11 */
749 unser_string(dev->VolHdr.VolumeName);
750 unser_string(dev->VolHdr.PrevVolumeName);
751 unser_string(dev->VolHdr.PoolName);
752 unser_string(dev->VolHdr.PoolType);
753 unser_string(dev->VolHdr.MediaType);
755 unser_string(dev->VolHdr.HostName);
756 unser_string(dev->VolHdr.LabelProg);
757 unser_string(dev->VolHdr.ProgVersion);
758 unser_string(dev->VolHdr.ProgDate);
760 ser_end(rec->data, SER_LENGTH_Volume_Label);
761 Dmsg0(190, "unser_vol_label\n");
762 if (debug_level >= 190) {
763 dump_volume_label(dev);
769 bool unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec)
773 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label);
774 unser_begin(rec->data, SER_LENGTH_Session_Label);
775 unser_string(label->Id);
776 unser_uint32(label->VerNum);
777 unser_uint32(label->JobId);
778 if (label->VerNum >= 11) {
779 unser_btime(label->write_btime);
781 unser_float64(label->write_date);
783 unser_float64(label->write_time);
784 unser_string(label->PoolName);
785 unser_string(label->PoolType);
786 unser_string(label->JobName);
787 unser_string(label->ClientName);
788 if (label->VerNum >= 10) {
789 unser_string(label->Job); /* Unique name of this Job */
790 unser_string(label->FileSetName);
791 unser_uint32(label->JobType);
792 unser_uint32(label->JobLevel);
794 if (label->VerNum >= 11) {
795 unser_string(label->FileSetMD5);
797 label->FileSetMD5[0] = 0;
799 if (rec->FileIndex == EOS_LABEL) {
800 unser_uint32(label->JobFiles);
801 unser_uint64(label->JobBytes);
802 unser_uint32(label->StartBlock);
803 unser_uint32(label->EndBlock);
804 unser_uint32(label->StartFile);
805 unser_uint32(label->EndFile);
806 unser_uint32(label->JobErrors);
807 if (label->VerNum >= 11) {
808 unser_uint32(label->JobStatus);
810 label->JobStatus = JS_Terminated; /* kludge */
816 void dump_volume_label(DEVICE *dev)
818 int dbl = debug_level;
820 const char *LabelType;
827 switch (dev->VolHdr.LabelType) {
829 LabelType = "PRE_LABEL";
832 LabelType = "VOL_LABEL";
835 LabelType = "EOM_LABEL";
838 LabelType = "SOS_LABEL";
841 LabelType = "EOS_LABEL";
847 sprintf(buf, _("Unknown %d"), dev->VolHdr.LabelType);
851 Pmsg11(-1, _("\nVolume Label:\n"
864 dev->VolHdr.Id, dev->VolHdr.VerNum,
865 dev->VolHdr.VolumeName, dev->VolHdr.PrevVolumeName,
866 File, LabelType, dev->VolHdr.LabelSize,
867 dev->VolHdr.PoolName, dev->VolHdr.MediaType,
868 dev->VolHdr.PoolType, dev->VolHdr.HostName);
870 if (dev->VolHdr.VerNum >= 11) {
872 bstrftime(dt, sizeof(dt), btime_to_unix(dev->VolHdr.label_btime));
873 Pmsg1(-1, _("Date label written: %s\n"), dt);
875 dt.julian_day_number = dev->VolHdr.label_date;
876 dt.julian_day_fraction = dev->VolHdr.label_time;
879 _("Date label written: %04d-%02d-%02d at %02d:%02d\n"),
880 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
888 static void dump_session_label(DEV_RECORD *rec, const char *type)
894 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], ec6[30], ec7[30];
896 unser_session_label(&label, rec);
899 Pmsg7(-1, _("\n%s Record:\n"
906 ""), type, label.JobId, label.VerNum,
907 label.PoolName, label.PoolType,
908 label.JobName, label.ClientName);
910 if (label.VerNum >= 10) {
912 "Job (unique name) : %s\n"
916 ""), label.Job, label.FileSetName, label.JobType, label.JobLevel);
919 if (rec->FileIndex == EOS_LABEL) {
930 edit_uint64_with_commas(label.JobFiles, ec1),
931 edit_uint64_with_commas(label.JobBytes, ec2),
932 edit_uint64_with_commas(label.StartBlock, ec3),
933 edit_uint64_with_commas(label.EndBlock, ec4),
934 edit_uint64_with_commas(label.StartFile, ec5),
935 edit_uint64_with_commas(label.EndFile, ec6),
936 edit_uint64_with_commas(label.JobErrors, ec7),
939 if (label.VerNum >= 11) {
941 bstrftime(dt, sizeof(dt), btime_to_unix(label.write_btime));
942 Pmsg1(-1, _("Date written : %s\n"), dt);
944 dt.julian_day_number = label.write_date;
945 dt.julian_day_fraction = label.write_time;
947 Pmsg5(-1, _("Date written : %04d-%02d-%02d at %02d:%02d\n"),
948 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
954 void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose)
959 if (rec->FileIndex == 0 && rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
964 switch (rec->FileIndex) {
966 type = _("Fresh Volume");
972 type = _("Begin Job Session");
975 type = _("End Job Session");
978 type = _("End of Media");
981 type = _("End of Tape");
988 switch (rec->FileIndex) {
991 unser_volume_label(dev, rec);
992 dump_volume_label(dev);
995 dump_session_label(rec, type);
998 dump_session_label(rec, type);
1001 Pmsg7(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n"),
1002 type, dev->file, dev->block_num, rec->VolSessionId,
1003 rec->VolSessionTime, rec->Stream, rec->data_len);
1006 Pmsg0(-1, _("End of physical tape.\n"));
1009 Pmsg7(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n"),
1010 type, dev->file, dev->block_num, rec->VolSessionId,
1011 rec->VolSessionTime, rec->Stream, rec->data_len);
1015 SESSION_LABEL label;
1017 switch (rec->FileIndex) {
1019 unser_session_label(&label, rec);
1020 bstrftimes(dt, sizeof(dt), btime_to_unix(label.write_btime));
1021 Pmsg6(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d\n"),
1022 type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, label.JobId);
1023 Pmsg4(-1, _(" Job=%s Date=%s Level=%c Type=%c\n"),
1024 label.Job, dt, label.JobLevel, label.JobType);
1027 char ed1[30], ed2[30];
1028 unser_session_label(&label, rec);
1029 bstrftimes(dt, sizeof(dt), btime_to_unix(label.write_btime));
1030 Pmsg6(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d\n"),
1031 type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, label.JobId);
1032 Pmsg7(-1, _(" Date=%s Level=%c Type=%c Files=%s Bytes=%s Errors=%d Status=%c\n"),
1033 dt, label.JobLevel, label.JobType,
1034 edit_uint64_with_commas(label.JobFiles, ed1),
1035 edit_uint64_with_commas(label.JobBytes, ed2),
1036 label.JobErrors, (char)label.JobStatus);
1042 Pmsg7(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n"),
1043 type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime,
1044 rec->Stream, rec->data_len);