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);
32 * Read the volume label
34 * If dcr->VolumeName == NULL, we accept any Bacula Volume
35 * If dcr->VolumeName[0] == 0, we accept any Bacula Volume
36 * otherwise dcr->VolumeName must match the Volume.
38 * If VolName given, ensure that it matches
40 * Returns VOL_ code as defined in record.h
42 * VOL_OK good label found
43 * VOL_NO_LABEL volume not labeled
44 * VOL_IO_ERROR I/O error reading tape
45 * VOL_NAME_ERROR label has wrong name
46 * VOL_CREATE_ERROR Error creating label
47 * VOL_VERSION_ERROR label has wrong version
48 * VOL_LABEL_ERROR bad label type
49 * VOL_NO_MEDIA no media in drive
51 * The dcr block is emptied on return, and the Volume is
54 int read_dev_volume_label(DCR *dcr)
57 DEVICE *dev = dcr->dev;
58 char *VolName = dcr->VolumeName;
61 DEV_BLOCK *block = dcr->block;
64 bool have_ansi_label = false;
66 Dmsg4(100, "Enter read_volume_label res=%d device=%s vol=%s dev_Vol=%s\n",
67 dev->reserved_device, dev->print_name(), VolName,
68 dev->VolHdr.VolumeName[0]?dev->VolHdr.VolumeName:"*NULL*");
70 if (!dev->is_open()) {
71 if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
75 if (dev->is_labeled()) { /* did we already read label? */
76 /* Compare Volume Names allow special wild card */
77 if (VolName && *VolName && *VolName != '*' && strcmp(dev->VolHdr.VolumeName, VolName) != 0) {
78 Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
79 dev->print_name(), VolName, dev->VolHdr.VolumeName);
81 * Cancel Job if too many label errors
84 if (!dev->poll && jcr->label_errors++ > 100) {
85 Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
87 Dmsg0(150, "return VOL_NAME_ERROR\n");
88 stat = VOL_NAME_ERROR;
91 Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
92 return VOL_OK; /* label already read */
98 dev->label_type = B_BACULA_LABEL;
100 if (!dev->rewind(dcr)) {
101 Mmsg(jcr->errmsg, _("Couldn't rewind device %s: ERR=%s\n"),
102 dev->print_name(), dev->bstrerror());
103 Dmsg1(30, "return VOL_NO_MEDIA: %s", jcr->errmsg);
106 bstrncpy(dev->VolHdr.Id, "**error**", sizeof(dev->VolHdr.Id));
108 /* Read ANSI/IBM label if so requested */
110 want_ansi_label = dcr->VolCatInfo.LabelType != B_BACULA_LABEL ||
111 dcr->device->label_type != B_BACULA_LABEL;
112 if (want_ansi_label || dev_cap(dev, CAP_CHECKLABELS)) {
113 stat = read_ansi_ibm_label(dcr);
114 /* If we want a label and didn't find it, return error */
115 if (want_ansi_label && stat != VOL_OK) {
118 if (stat == VOL_NAME_ERROR || stat == VOL_LABEL_ERROR) {
119 Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
120 dev->print_name(), VolName, dev->VolHdr.VolumeName);
121 if (!dev->poll && jcr->label_errors++ > 100) {
122 Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
126 if (stat != VOL_OK) { /* Not an ANSI/IBM label, so re-read */
129 have_ansi_label = true;
133 /* Read the Bacula Volume label block */
134 record = new_record();
137 Dmsg0(90, "Big if statement in read_volume_label\n");
138 if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) {
139 Mmsg(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bacula "
140 "labeled Volume, because: ERR=%s"), NPRT(VolName),
141 dev->print_name(), dev->bstrerror());
142 Dmsg1(30, "%s", jcr->errmsg);
143 } else if (!read_record_from_block(block, record)) {
144 Mmsg(jcr->errmsg, _("Could not read Volume label from block.\n"));
145 Dmsg1(30, "%s", jcr->errmsg);
146 } else if (!unser_volume_label(dev, record)) {
147 Mmsg(jcr->errmsg, _("Could not unserialize Volume label: ERR=%s\n"),
149 Dmsg1(30, "%s", jcr->errmsg);
150 } else if (strcmp(dev->VolHdr.Id, BaculaId) != 0 &&
151 strcmp(dev->VolHdr.Id, OldBaculaId) != 0) {
152 Mmsg(jcr->errmsg, _("Volume Header Id bad: %s\n"), dev->VolHdr.Id);
153 Dmsg1(30, "%s", jcr->errmsg);
157 free_record(record); /* finished reading Volume record */
160 if (forge_on || jcr->ignore_label_errors) {
161 dev->set_labeled(); /* set has Bacula label */
162 Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg);
170 /* At this point, we have read the first Bacula block, and
171 * then read the Bacula Volume label. Now we need to
172 * make sure we have the right Volume.
176 if (dev->VolHdr.VerNum != BaculaTapeVersion &&
177 dev->VolHdr.VerNum != OldCompatibleBaculaTapeVersion1 &&
178 dev->VolHdr.VerNum != OldCompatibleBaculaTapeVersion2) {
179 Mmsg(jcr->errmsg, _("Volume on %s has wrong Bacula version. Wanted %d got %d\n"),
180 dev->print_name(), BaculaTapeVersion, dev->VolHdr.VerNum);
181 Dmsg1(30, "VOL_VERSION_ERROR: %s", jcr->errmsg);
182 stat = VOL_VERSION_ERROR;
186 /* We are looking for either an unused Bacula tape (PRE_LABEL) or
187 * a Bacula volume label (VOL_LABEL)
189 if (dev->VolHdr.LabelType != PRE_LABEL && dev->VolHdr.LabelType != VOL_LABEL) {
190 Mmsg(jcr->errmsg, _("Volume on %s has bad Bacula label type: %x\n"),
191 dev->print_name(), dev->VolHdr.LabelType);
192 Dmsg1(30, "%s", jcr->errmsg);
193 if (!dev->poll && jcr->label_errors++ > 100) {
194 Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
196 Dmsg0(150, "return VOL_LABEL_ERROR\n");
197 stat = VOL_LABEL_ERROR;
201 dev->set_labeled(); /* set has Bacula label */
202 new_volume(dcr, dev->VolHdr.VolumeName);
204 /* Compare Volume Names */
205 Dmsg2(30, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName);
206 if (VolName && *VolName && *VolName != '*' && strcmp(dev->VolHdr.VolumeName, VolName) != 0) {
207 Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
208 dev->print_name(), VolName, dev->VolHdr.VolumeName);
209 Dmsg1(30, "%s", jcr->errmsg);
211 * Cancel Job if too many label errors
212 * => we are in a loop
214 if (!dev->poll && jcr->label_errors++ > 100) {
215 Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
217 Dmsg0(150, "return VOL_NAME_ERROR\n");
218 stat = VOL_NAME_ERROR;
221 Dmsg1(30, "Copy vol_name=%s\n", dev->VolHdr.VolumeName);
223 if (debug_level >= 10) {
224 dump_volume_label(dev);
226 Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
227 /* If we are a streaming device, we only get one chance to read */
228 if (!dev_cap(dev, CAP_STREAM)) {
230 if (have_ansi_label) {
231 stat = read_ansi_ibm_label(dcr);
232 /* If we want a label and didn't find it, return error */
233 if (stat != VOL_OK) {
244 Dmsg1(150, "return %d\n", stat);
249 * Put a volume label into the block
251 * Returns: false on failure
254 bool write_volume_label_to_block(DCR *dcr)
257 DEVICE *dev = dcr->dev;
259 DEV_BLOCK *block = dcr->block;
261 Dmsg0(20, "write Label in write_volume_label_to_block()\n");
262 memset(&rec, 0, sizeof(rec));
263 rec.data = get_memory(SER_LENGTH_Volume_Label);
264 empty_block(block); /* Volume label always at beginning */
266 create_volume_label_record(dcr, &rec);
268 block->BlockNumber = 0;
269 if (!write_record_to_block(block, &rec)) {
270 free_pool_memory(rec.data);
271 Jmsg1(jcr, M_FATAL, 0, _("Cannot write Volume label to block for device %s\n"),
275 Dmsg1(90, "Wrote label of %d bytes to block\n", rec.data_len);
277 free_pool_memory(rec.data);
283 * Write a Volume Label
284 * !!! Note, this is ONLY used for writing
285 * a fresh volume label. Any data
286 * after the label will be destroyed,
287 * in fact, we write the label 5 times !!!!
289 * This routine should be used only when labeling a blank tape.
291 bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName)
293 DEVICE *dev = dcr->dev;
296 Dmsg0(99, "write_volume_label()\n");
297 empty_block(dcr->block);
299 if (dev->open(dcr, OPEN_READ_WRITE) < 0) {
300 /* If device is not tape, attempt to create it */
301 if (dev->is_tape() || dev->open(dcr, CREATE_READ_WRITE) < 0) {
305 Dmsg1(150, "Label type=%d\n", dev->label_type);
306 if (!dev->rewind(dcr)) {
308 memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
309 Dmsg2(30, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), dev->bstrerror());
315 /* Create PRE_LABEL */
316 create_volume_label(dev, VolName, PoolName);
319 * If we have already detected an ANSI label, re-read it
320 * to skip past it. Otherwise, we write a new one if
323 if (dev->label_type != B_BACULA_LABEL) {
324 if (read_ansi_ibm_label(dcr) != VOL_OK) {
328 } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, VolName)) {
332 create_volume_label_record(dcr, dcr->rec);
333 dcr->rec->Stream = 0;
335 /* Temporarily mark in append state to enable writing */
337 if (!write_record_to_block(dcr->block, dcr->rec)) {
338 Dmsg2(30, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->bstrerror());
341 Dmsg2(30, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev->print_name());
344 Dmsg0(99, "Call write_block_to_dev()\n");
345 if (!write_block_to_dev(dcr)) {
346 Dmsg2(30, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->bstrerror());
349 Dmsg0(99, " Wrote block to device\n");
353 write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
356 if (debug_level >= 20) {
357 dump_volume_label(dev);
359 new_volume(dcr, VolName);
360 dev->clear_append(); /* remove append since this is PRE_LABEL */
365 memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
366 dev->clear_append(); /* remove append since this is PRE_LABEL */
371 * Write a volume label. This is ONLY called if we have a valid Bacula
372 * label of type PRE_LABEL;
373 * Returns: true if OK
374 * false if unable to write it
376 bool rewrite_volume_label(DCR *dcr, bool recycle)
378 DEVICE *dev = dcr->dev;
380 bool can_write = true;
382 if (dev->open(dcr, OPEN_READ_WRITE) < 0) {
383 /* If device is DVD, attempt to create it */
384 if (!dev->is_dvd()) {
387 if (dev->open(dcr, CREATE_READ_WRITE) < 0) {
388 /* We forge on for a DVD but don't do any writing */
392 Dmsg2(190, "set append found freshly labeled volume. fd=%d dev=%x\n", dev->fd, dev);
393 dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
396 if (!write_volume_label_to_block(dcr)) {
397 Dmsg0(200, "Error from write volume label.\n");
402 * If we are not dealing with a streaming device,
403 * write the block now to ensure we have write permission.
404 * It is better to find out now rather than later.
405 * We do not write the block now if this is an ANSI label. This
406 * avoids re-writing the ANSI label, which we do not want to do.
408 if (can_write && !dev_cap(dev, CAP_STREAM)) {
409 if (!dev->rewind(dcr)) {
410 Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s: ERR=%s\n"),
411 dev->print_name(), dev->bstrerror());
414 if (!dev->truncate(dcr)) {
415 Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s: ERR=%s\n"),
416 dev->print_name(), dev->bstrerror());
421 * If we have already detected an ANSI label, re-read it
422 * to skip past it. Otherwise, we write a new one if
425 if (dev->label_type != B_BACULA_LABEL) {
426 if (read_ansi_ibm_label(dcr) != VOL_OK) {
430 } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, dev->VolHdr.VolumeName)) {
434 /* Attempt write to check write permission */
435 Dmsg1(200, "Attempt to write to device fd=%d.\n", dev->fd);
436 if (!write_block_to_dev(dcr)) {
437 Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s: ERR=%s\n"),
438 dev->print_name(), dev->bstrerror());
439 Dmsg0(200, "===ERROR write block to dev\n");
443 /* Set or reset Volume statistics */
444 dev->VolCatInfo.VolCatJobs = 0;
445 dev->VolCatInfo.VolCatFiles = 0;
446 dev->VolCatInfo.VolCatBytes = 1;
447 dev->VolCatInfo.VolCatErrors = 0;
448 dev->VolCatInfo.VolCatBlocks = 0;
449 dev->VolCatInfo.VolCatRBytes = 0;
451 dev->VolCatInfo.VolCatMounts++;
452 dev->VolCatInfo.VolCatRecycles++;
454 dev->VolCatInfo.VolCatMounts = 1;
455 dev->VolCatInfo.VolCatRecycles = 0;
456 dev->VolCatInfo.VolCatWrites = 1;
457 dev->VolCatInfo.VolCatReads = 1;
459 Dmsg0(150, "dir_update_vol_info. Set Append\n");
460 bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus));
461 if (!dir_update_volume_info(dcr, true)) { /* indicate doing relabel */
465 Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device %s, all previous data lost.\n"),
466 dcr->VolumeName, dev->print_name());
468 Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device %s\n"),
469 dcr->VolumeName, dev->print_name());
472 * End writing real Volume label (from pre-labeled tape), or recycling
475 Dmsg0(200, "OK from rewite vol label.\n");
481 * create_volume_label_record
482 * Serialize label (from dev->VolHdr structure) into device record.
483 * Assumes that the dev->VolHdr structure is properly
486 static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec)
490 DEVICE *dev = dcr->dev;
494 /* Serialize the label into the device record. */
496 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label);
497 ser_begin(rec->data, SER_LENGTH_Volume_Label);
498 ser_string(dev->VolHdr.Id);
500 ser_uint32(dev->VolHdr.VerNum);
502 if (dev->VolHdr.VerNum >= 11) {
503 ser_btime(dev->VolHdr.label_btime);
504 dev->VolHdr.write_btime = get_current_btime();
505 ser_btime(dev->VolHdr.write_btime);
506 dev->VolHdr.write_date = 0;
507 dev->VolHdr.write_time = 0;
509 /* OLD WAY DEPRECATED */
510 ser_float64(dev->VolHdr.label_date);
511 ser_float64(dev->VolHdr.label_time);
512 get_current_time(&dt);
513 dev->VolHdr.write_date = dt.julian_day_number;
514 dev->VolHdr.write_time = dt.julian_day_fraction;
516 ser_float64(dev->VolHdr.write_date); /* 0 if VerNum >= 11 */
517 ser_float64(dev->VolHdr.write_time); /* 0 if VerNum >= 11 */
519 ser_string(dev->VolHdr.VolumeName);
520 ser_string(dev->VolHdr.PrevVolumeName);
521 ser_string(dev->VolHdr.PoolName);
522 ser_string(dev->VolHdr.PoolType);
523 ser_string(dev->VolHdr.MediaType);
525 ser_string(dev->VolHdr.HostName);
526 ser_string(dev->VolHdr.LabelProg);
527 ser_string(dev->VolHdr.ProgVersion);
528 ser_string(dev->VolHdr.ProgDate);
530 ser_end(rec->data, SER_LENGTH_Volume_Label);
531 rec->data_len = ser_length(rec->data);
532 rec->FileIndex = dev->VolHdr.LabelType;
533 rec->VolSessionId = jcr->VolSessionId;
534 rec->VolSessionTime = jcr->VolSessionTime;
535 rec->Stream = jcr->NumVolumes;
536 Dmsg2(150, "Created Vol label rec: FI=%s len=%d\n", FI_to_ascii(buf, rec->FileIndex),
542 * Create a volume label in memory
544 void create_volume_label(DEVICE *dev, const char *VolName, const char *PoolName)
546 DEVRES *device = (DEVRES *)dev->device;
548 Dmsg0(90, "Start create_volume_label()\n");
552 free_volume(dev); /* release any old volume */
553 memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
555 bstrncpy(dev->VolHdr.Id, BaculaId, sizeof(dev->VolHdr.Id));
556 dev->VolHdr.VerNum = BaculaTapeVersion;
557 dev->VolHdr.LabelType = PRE_LABEL; /* Mark tape as unused */
558 bstrncpy(dev->VolHdr.VolumeName, VolName, sizeof(dev->VolHdr.VolumeName));
559 bstrncpy(dev->VolHdr.PoolName, PoolName, sizeof(dev->VolHdr.PoolName));
560 bstrncpy(dev->VolHdr.MediaType, device->media_type, sizeof(dev->VolHdr.MediaType));
562 bstrncpy(dev->VolHdr.PoolType, "Backup", sizeof(dev->VolHdr.PoolType));
564 dev->VolHdr.label_btime = get_current_btime();
565 dev->VolHdr.label_date = 0;
566 dev->VolHdr.label_time = 0;
568 if (gethostname(dev->VolHdr.HostName, sizeof(dev->VolHdr.HostName)) != 0) {
569 dev->VolHdr.HostName[0] = 0;
571 bstrncpy(dev->VolHdr.LabelProg, my_name, sizeof(dev->VolHdr.LabelProg));
572 sprintf(dev->VolHdr.ProgVersion, "Ver. %s %s", VERSION, BDATE);
573 sprintf(dev->VolHdr.ProgDate, "Build %s %s", __DATE__, __TIME__);
574 dev->set_labeled(); /* set has Bacula label */
575 if (debug_level >= 90) {
576 dump_volume_label(dev);
581 * Create session label
582 * The pool memory must be released by the calling program
584 void create_session_label(DCR *dcr, DEV_RECORD *rec, int label)
589 rec->VolSessionId = jcr->VolSessionId;
590 rec->VolSessionTime = jcr->VolSessionTime;
591 rec->Stream = jcr->JobId;
593 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label);
594 ser_begin(rec->data, SER_LENGTH_Session_Label);
595 ser_string(BaculaId);
596 ser_uint32(BaculaTapeVersion);
598 ser_uint32(jcr->JobId);
600 /* Changed in VerNum 11 */
601 ser_btime(get_current_btime());
604 ser_string(dcr->pool_name);
605 ser_string(dcr->pool_type);
606 ser_string(jcr->job_name); /* base Job name */
607 ser_string(jcr->client_name);
609 /* Added in VerNum 10 */
610 ser_string(jcr->Job); /* Unique name of this Job */
611 ser_string(jcr->fileset_name);
612 ser_uint32(jcr->JobType);
613 ser_uint32(jcr->JobLevel);
614 /* Added in VerNum 11 */
615 ser_string(jcr->fileset_md5);
617 if (label == EOS_LABEL) {
618 ser_uint32(jcr->JobFiles);
619 ser_uint64(jcr->JobBytes);
620 ser_uint32(dcr->StartBlock);
621 ser_uint32(dcr->EndBlock);
622 ser_uint32(dcr->StartFile);
623 ser_uint32(dcr->EndFile);
624 ser_uint32(jcr->JobErrors);
626 /* Added in VerNum 11 */
627 ser_uint32(jcr->JobStatus);
629 ser_end(rec->data, SER_LENGTH_Session_Label);
630 rec->data_len = ser_length(rec->data);
633 /* Write session label
634 * Returns: false on failure
637 bool write_session_label(DCR *dcr, int label)
640 DEVICE *dev = dcr->dev;
642 DEV_BLOCK *block = dcr->block;
643 char buf1[100], buf2[100];
646 Dmsg1(90, "session_label record=%x\n", rec);
649 if (dev->is_tape()) {
650 dcr->StartBlock = dev->block_num;
651 dcr->StartFile = dev->file;
653 dcr->StartBlock = (uint32_t)dev->file_addr;
654 dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
658 if (dev->is_tape()) {
659 dcr->EndBlock = dev->EndBlock;
660 dcr->EndFile = dev->EndFile;
662 dcr->EndBlock = (uint32_t)dev->file_addr;
663 dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
667 Jmsg1(jcr, M_ABORT, 0, _("Bad session label = %d\n"), label);
670 create_session_label(dcr, rec, label);
671 rec->FileIndex = label;
674 * We guarantee that the session record can totally fit
675 * into a block. If not, write the block, and put it in
676 * the next block. Having the sesssion record totally in
677 * one block makes reading them much easier (no need to
678 * read the next block).
680 if (!can_write_record_to_block(block, rec)) {
681 Dmsg0(150, "Cannot write session label to block.\n");
682 if (!write_block_to_device(dcr)) {
683 Dmsg0(90, "Got session label write_block_to_dev error.\n");
684 /* ****FIXME***** errno is not set here */
685 Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
686 dev_vol_name(dev), strerror(errno));
691 if (!write_record_to_block(block, rec)) {
692 Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
693 dev_vol_name(dev), strerror(errno));
698 Dmsg6(20, "Write sesson_label record JobId=%d FI=%s SessId=%d Strm=%s len=%d "
699 "remainder=%d\n", jcr->JobId,
700 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
701 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
705 Dmsg2(20, "Leave write_session_label Block=%d File=%d\n",
706 dev->block_num, dev->file);
710 /* unser_volume_label
712 * Unserialize the Bacula Volume label into the device Volume_Label
715 * Assumes that the record is already read.
717 * Returns: false on error
721 bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec)
724 char buf1[100], buf2[100];
726 if (rec->FileIndex != VOL_LABEL && rec->FileIndex != PRE_LABEL) {
727 Mmsg3(dev->errmsg, _("Expecting Volume Label, got FI=%s Stream=%s len=%d\n"),
728 FI_to_ascii(buf1, rec->FileIndex),
729 stream_to_ascii(buf2, rec->Stream, rec->FileIndex),
736 dev->VolHdr.LabelType = rec->FileIndex;
737 dev->VolHdr.LabelSize = rec->data_len;
740 /* Unserialize the record into the Volume Header */
741 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label);
742 ser_begin(rec->data, SER_LENGTH_Volume_Label);
743 unser_string(dev->VolHdr.Id);
744 unser_uint32(dev->VolHdr.VerNum);
746 if (dev->VolHdr.VerNum >= 11) {
747 unser_btime(dev->VolHdr.label_btime);
748 unser_btime(dev->VolHdr.write_btime);
749 } else { /* old way */
750 unser_float64(dev->VolHdr.label_date);
751 unser_float64(dev->VolHdr.label_time);
753 unser_float64(dev->VolHdr.write_date); /* Unused with VerNum >= 11 */
754 unser_float64(dev->VolHdr.write_time); /* Unused with VerNum >= 11 */
756 unser_string(dev->VolHdr.VolumeName);
757 unser_string(dev->VolHdr.PrevVolumeName);
758 unser_string(dev->VolHdr.PoolName);
759 unser_string(dev->VolHdr.PoolType);
760 unser_string(dev->VolHdr.MediaType);
762 unser_string(dev->VolHdr.HostName);
763 unser_string(dev->VolHdr.LabelProg);
764 unser_string(dev->VolHdr.ProgVersion);
765 unser_string(dev->VolHdr.ProgDate);
767 ser_end(rec->data, SER_LENGTH_Volume_Label);
768 Dmsg0(190, "unser_vol_label\n");
769 if (debug_level >= 190) {
770 dump_volume_label(dev);
776 bool unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec)
780 rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label);
781 unser_begin(rec->data, SER_LENGTH_Session_Label);
782 unser_string(label->Id);
783 unser_uint32(label->VerNum);
784 unser_uint32(label->JobId);
785 if (label->VerNum >= 11) {
786 unser_btime(label->write_btime);
788 unser_float64(label->write_date);
790 unser_float64(label->write_time);
791 unser_string(label->PoolName);
792 unser_string(label->PoolType);
793 unser_string(label->JobName);
794 unser_string(label->ClientName);
795 if (label->VerNum >= 10) {
796 unser_string(label->Job); /* Unique name of this Job */
797 unser_string(label->FileSetName);
798 unser_uint32(label->JobType);
799 unser_uint32(label->JobLevel);
801 if (label->VerNum >= 11) {
802 unser_string(label->FileSetMD5);
804 label->FileSetMD5[0] = 0;
806 if (rec->FileIndex == EOS_LABEL) {
807 unser_uint32(label->JobFiles);
808 unser_uint64(label->JobBytes);
809 unser_uint32(label->StartBlock);
810 unser_uint32(label->EndBlock);
811 unser_uint32(label->StartFile);
812 unser_uint32(label->EndFile);
813 unser_uint32(label->JobErrors);
814 if (label->VerNum >= 11) {
815 unser_uint32(label->JobStatus);
817 label->JobStatus = JS_Terminated; /* kludge */
823 void dump_volume_label(DEVICE *dev)
825 int dbl = debug_level;
827 const char *LabelType;
834 switch (dev->VolHdr.LabelType) {
836 LabelType = "PRE_LABEL";
839 LabelType = "VOL_LABEL";
842 LabelType = "EOM_LABEL";
845 LabelType = "SOS_LABEL";
848 LabelType = "EOS_LABEL";
854 sprintf(buf, _("Unknown %d"), dev->VolHdr.LabelType);
858 Pmsg11(-1, _("\nVolume Label:\n"
871 dev->VolHdr.Id, dev->VolHdr.VerNum,
872 dev->VolHdr.VolumeName, dev->VolHdr.PrevVolumeName,
873 File, LabelType, dev->VolHdr.LabelSize,
874 dev->VolHdr.PoolName, dev->VolHdr.MediaType,
875 dev->VolHdr.PoolType, dev->VolHdr.HostName);
877 if (dev->VolHdr.VerNum >= 11) {
879 bstrftime(dt, sizeof(dt), btime_to_unix(dev->VolHdr.label_btime));
880 Pmsg1(-1, _("Date label written: %s\n"), dt);
882 dt.julian_day_number = dev->VolHdr.label_date;
883 dt.julian_day_fraction = dev->VolHdr.label_time;
886 _("Date label written: %04d-%02d-%02d at %02d:%02d\n"),
887 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
895 static void dump_session_label(DEV_RECORD *rec, const char *type)
901 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], ec6[30], ec7[30];
903 unser_session_label(&label, rec);
906 Pmsg7(-1, _("\n%s Record:\n"
913 ""), type, label.JobId, label.VerNum,
914 label.PoolName, label.PoolType,
915 label.JobName, label.ClientName);
917 if (label.VerNum >= 10) {
919 "Job (unique name) : %s\n"
923 ""), label.Job, label.FileSetName, label.JobType, label.JobLevel);
926 if (rec->FileIndex == EOS_LABEL) {
937 edit_uint64_with_commas(label.JobFiles, ec1),
938 edit_uint64_with_commas(label.JobBytes, ec2),
939 edit_uint64_with_commas(label.StartBlock, ec3),
940 edit_uint64_with_commas(label.EndBlock, ec4),
941 edit_uint64_with_commas(label.StartFile, ec5),
942 edit_uint64_with_commas(label.EndFile, ec6),
943 edit_uint64_with_commas(label.JobErrors, ec7),
946 if (label.VerNum >= 11) {
948 bstrftime(dt, sizeof(dt), btime_to_unix(label.write_btime));
949 Pmsg1(-1, _("Date written : %s\n"), dt);
951 dt.julian_day_number = label.write_date;
952 dt.julian_day_fraction = label.write_time;
954 Pmsg5(-1, _("Date written : %04d-%02d-%02d at %02d:%02d\n"),
955 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
961 void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose)
966 if (rec->FileIndex == 0 && rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
971 switch (rec->FileIndex) {
973 type = _("Fresh Volume");
979 type = _("Begin Job Session");
982 type = _("End Job Session");
985 type = _("End of Media");
988 type = _("End of Tape");
995 switch (rec->FileIndex) {
998 unser_volume_label(dev, rec);
999 dump_volume_label(dev);
1002 dump_session_label(rec, type);
1005 dump_session_label(rec, type);
1008 Pmsg7(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n"),
1009 type, dev->file, dev->block_num, rec->VolSessionId,
1010 rec->VolSessionTime, rec->Stream, rec->data_len);
1013 Pmsg0(-1, _("End of physical tape.\n"));
1016 Pmsg7(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n"),
1017 type, dev->file, dev->block_num, rec->VolSessionId,
1018 rec->VolSessionTime, rec->Stream, rec->data_len);
1022 SESSION_LABEL label;
1024 switch (rec->FileIndex) {
1026 unser_session_label(&label, rec);
1027 bstrftimes(dt, sizeof(dt), btime_to_unix(label.write_btime));
1028 Pmsg6(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d\n"),
1029 type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, label.JobId);
1030 Pmsg4(-1, _(" Job=%s Date=%s Level=%c Type=%c\n"),
1031 label.Job, dt, label.JobLevel, label.JobType);
1034 char ed1[30], ed2[30];
1035 unser_session_label(&label, rec);
1036 bstrftimes(dt, sizeof(dt), btime_to_unix(label.write_btime));
1037 Pmsg6(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d\n"),
1038 type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, label.JobId);
1039 Pmsg7(-1, _(" Date=%s Level=%c Type=%c Files=%s Bytes=%s Errors=%d Status=%c\n"),
1040 dt, label.JobLevel, label.JobType,
1041 edit_uint64_with_commas(label.JobFiles, ed1),
1042 edit_uint64_with_commas(label.JobBytes, ed2),
1043 label.JobErrors, (char)label.JobStatus);
1049 Pmsg7(-1, _("%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n"),
1050 type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime,
1051 rec->Stream, rec->data_len);