2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Program to scan a Bacula Volume and compare it with
22 * the catalog and optionally synchronize the catalog
25 * Kern E. Sibbald, December 2001
30 #include "findlib/find.h"
31 #include "cats/cats.h"
33 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
35 /* Forward referenced functions */
36 static void do_scan(void);
37 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
38 static int create_file_attributes_record(BDB *db, JCR *mjcr,
39 char *fname, char *lname, int type,
40 char *ap, DEV_RECORD *rec);
41 static int create_media_record(BDB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
42 static bool update_media_record(BDB *db, MEDIA_DBR *mr);
43 static int create_pool_record(BDB *db, POOL_DBR *pr);
44 static JCR *create_job_record(BDB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
45 static int update_job_record(BDB *db, JOB_DBR *mr, SESSION_LABEL *elabel,
47 static int create_client_record(BDB *db, CLIENT_DBR *cr);
48 static int create_fileset_record(BDB *db, FILESET_DBR *fsr);
49 static int create_jobmedia_record(BDB *db, JCR *jcr);
50 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
51 static int update_digest_record(BDB *db, char *digest, DEV_RECORD *rec, int type);
55 static DEVICE *dev = NULL;
57 static JCR *bjcr; /* jcr for bscan */
58 static BSR *bsr = NULL;
63 static FILESET_DBR fsr;
66 static SESSION_LABEL label;
67 static SESSION_LABEL elabel;
70 static time_t lasttime = 0;
72 static const char *db_driver = "NULL";
73 static const char *db_name = "bacula";
74 static const char *db_user = "bacula";
75 static const char *db_password = "";
76 static const char *db_host = NULL;
77 static const char *db_ssl_mode = NULL;
78 static const char *db_ssl_key = NULL;
79 static const char *db_ssl_cert = NULL;
80 static const char *db_ssl_ca = NULL;
81 static const char *db_ssl_capath = NULL;
82 static const char *db_ssl_cipher = NULL;
83 static int db_port = 0;
84 static const char *wd = NULL;
85 static bool update_db = false;
86 static bool update_vol_info = false;
87 static bool list_records = false;
88 static int ignored_msgs = 0;
90 static uint64_t currentVolumeSize;
91 static int last_pct = -1;
92 static bool showProgress = false;
93 static int num_jobs = 0;
94 static int num_pools = 0;
95 static int num_media = 0;
96 static int num_files = 0;
98 static CONFIG *config;
99 #define CONFIG_FILE "bacula-sd.conf"
102 char *configfile = NULL;
108 "\n%sVersion: %s (%s)\n\n"
109 "Usage: bscan [ options ] <bacula-archive>\n"
110 " -b bootstrap specify a bootstrap file\n"
111 " -c <file> specify configuration file\n"
112 " -d <nn> set debug level to <nn>\n"
113 " -dt print timestamp in debug output\n"
114 " -m update media info in database\n"
115 " -D <driver name> specify the driver database name (default NULL)\n"
116 " -n <name> specify the database name (default bacula)\n"
117 " -u <user> specify database user name (default bacula)\n"
118 " -P <password> specify database password (default none)\n"
119 " -h <host> specify database host (default NULL)\n"
120 " -t <port> specify database port (default 0)\n"
121 " -p proceed inspite of I/O errors\n"
123 " -s synchronize or store in database\n"
124 " -S show scan progress periodically\n"
126 " -V <Volumes> specify Volume names (separated by |)\n"
127 " -w <dir> specify working directory (default from conf file)\n"
128 " -? print this message\n\n"),
129 2001, "", VERSION, BDATE);
133 int main (int argc, char *argv[])
136 struct stat stat_buf;
137 char *VolumeName = NULL;
138 BtoolsAskDirHandler askdir_handler;
140 init_askdir_handler(&askdir_handler);
141 setlocale(LC_ALL, "");
142 bindtextdomain("bacula", LOCALEDIR);
143 textdomain("bacula");
147 my_name_is(argc, argv, "bscan");
148 init_msg(NULL, NULL);
152 while ((ch = getopt(argc, argv, "b:c:d:D:h:o:k:e:a:p:mn:pP:rsSt:u:vV:w:?")) != -1) {
158 bsr = parse_bsr(NULL, optarg);
161 case 'c': /* specify config file */
162 if (configfile != NULL) {
165 configfile = bstrdup(optarg);
172 case 'd': /* debug level */
173 if (*optarg == 't') {
174 dbg_timestamp = true;
176 debug_level = atoi(optarg);
177 if (debug_level <= 0) {
188 db_ssl_mode = optarg;
196 db_ssl_cert = optarg;
204 db_port = atoi(optarg);
208 update_vol_info = true;
220 db_password = optarg;
239 case 'V': /* Volume name */
257 Pmsg0(0, _("Wrong number of arguments: \n"));
261 if (configfile == NULL) {
262 configfile = bstrdup(CONFIG_FILE);
265 config = New(CONFIG());
266 parse_sd_config(config, configfile, M_ERROR_TERM);
268 load_sd_plugins(me->plugin_directory);
270 /* Check if -w option given, otherwise use resource for working directory */
272 working_directory = wd;
273 } else if (!me->working_directory) {
274 Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
277 working_directory = me->working_directory;
280 /* Check that working directory is good */
281 if (stat(working_directory, &stat_buf) != 0) {
282 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
285 if (!S_ISDIR(stat_buf.st_mode)) {
286 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
290 bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, SD_READ);
294 dev = bjcr->read_dcr->dev;
298 fstat(dev->fd(), &sb);
299 currentVolumeSize = sb.st_size;
300 Pmsg1(000, _("First Volume Size = %s\n"),
301 edit_uint64(currentVolumeSize, ed1));
304 db = db_init_database(NULL, db_driver, db_name, db_user, db_password,
305 db_host, db_port, NULL,
306 db_ssl_mode, db_ssl_key,
307 db_ssl_cert, db_ssl_ca,
308 db_ssl_capath, db_ssl_cipher,
310 if (!db || !db_open_database(NULL, db)) {
311 Pmsg2(000, _("Could not open Catalog \"%s\", database \"%s\".\n"),
314 Jmsg(NULL, M_FATAL, 0, _("%s"), db_strerror(db));
315 Pmsg1(000, "%s", db_strerror(db));
316 db_close_database(NULL, db);
318 Jmsg(NULL, M_ERROR_TERM, 0, _("Could not open Catalog \"%s\", database \"%s\".\n"),
321 Dmsg0(200, "Database opened\n");
323 Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
328 printf("Records added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
329 num_media, num_pools, num_jobs, num_files);
331 printf("Records would have been added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
332 num_media, num_pools, num_jobs, num_files);
341 * We are at the end of reading a Volume. Now, we simulate handling
342 * the end of writing a Volume by wiffling through the attached
343 * jcrs creating jobmedia records.
345 static bool bscan_mount_next_read_volume(DCR *dcr)
347 DEVICE *dev = dcr->dev;
349 Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->getVolCatName());
350 foreach_dlist(mdcr, dev->attached_dcrs) {
351 JCR *mjcr = mdcr->jcr;
352 Dmsg1(100, "========== JobId=%u ========\n", mjcr->JobId);
353 if (mjcr->JobId == 0) {
357 Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
359 mdcr->StartAddr = dcr->StartAddr;
360 mdcr->EndAddr = dcr->EndAddr;
361 mdcr->VolMediaId = dcr->VolMediaId;
362 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
363 if( mjcr->bscan_insert_jobmedia_records ) {
364 if (!create_jobmedia_record(db, mjcr)) {
365 Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
366 dev->getVolCatName(), mjcr->Job);
371 update_media_record(db, &mr);
373 /* Now let common read routine get up next tape. Note,
374 * we call mount_next... with bscan's jcr because that is where we
375 * have the Volume list, but we get attached.
377 bool stat = mount_next_read_volume(dcr);
382 fstat(dev->fd(), &sb);
383 currentVolumeSize = sb.st_size;
384 Pmsg1(000, _("First Volume Size = %s\n"),
385 edit_uint64(currentVolumeSize, ed1));
390 static void do_scan()
392 attr = new_attr(bjcr);
394 memset(&ar, 0, sizeof(ar));
395 memset(&pr, 0, sizeof(pr));
396 memset(&jr, 0, sizeof(jr));
397 memset(&cr, 0, sizeof(cr));
398 memset(&fsr, 0, sizeof(fsr));
399 memset(&fr, 0, sizeof(fr));
401 /* Detach bscan's jcr as we are not a real Job on the tape */
403 read_records(bjcr->read_dcr, record_cb, bscan_mount_next_read_volume);
406 db_write_batch_file_records(bjcr); /* used by bulk batch file insert */
412 * Returns: true if OK
415 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
419 DEVICE *dev = dcr->dev;
420 JCR *bjcr = dcr->jcr;
421 DEV_BLOCK *block = dcr->block;
423 db_int64_ctx jmr_count;
425 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
427 if (rec->data_len > 0) {
428 mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
429 if (showProgress && currentVolumeSize > 0) {
430 int pct = (mr.VolBytes * 100) / currentVolumeSize;
431 if (pct != last_pct) {
432 fprintf(stdout, _("done: %d%%\n"), pct);
440 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
441 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
442 rec->Stream, rec->data_len);
445 * Check for Start or End of Session Record
448 if (rec->FileIndex < 0) {
449 bool save_update_db = update_db;
452 dump_label_record(dev, rec, 1, false);
454 switch (rec->FileIndex) {
456 Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
461 unser_volume_label(dev, rec);
462 /* Check Pool info */
463 bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
464 bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
466 if (db_get_pool_numvols(bjcr, db, &pr)) {
468 Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
472 Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
475 create_pool_record(db, &pr);
477 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
478 Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
479 pr.PoolType, dev->VolHdr.PoolType);
481 } else if (verbose) {
482 Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
485 /* Check Media Info */
486 memset(&mr, 0, sizeof(mr));
487 bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
488 mr.PoolId = pr.PoolId;
490 if (db_get_media_record(bjcr, db, &mr)) {
492 Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
494 /* Clear out some volume statistics that will be updated */
495 mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
496 mr.VolBytes = rec->data_len + 20;
499 Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
502 bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
503 create_media_record(db, &mr, &dev->VolHdr);
505 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
506 Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
507 mr.MediaType, dev->VolHdr.MediaType);
508 return true; /* ignore error */
509 } else if (verbose) {
510 Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
512 /* Reset some DCR variables */
513 foreach_dlist(dcr, dev->attached_dcrs) {
514 dcr->VolFirstIndex = dcr->FileIndex = 0;
515 dcr->StartAddr = dcr->EndAddr = 0;
519 Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
525 if (ignored_msgs > 0) {
526 Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
530 unser_session_label(&label, rec);
531 memset(&jr, 0, sizeof(jr));
532 bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
533 if (db_get_job_record(bjcr, db, &jr)) {
534 /* Job record already exists in DB */
535 update_db = false; /* don't change db in create_job_record */
537 Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
540 /* Must create a Job record in DB */
542 Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
547 /* Create Client record if not already there */
548 bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
549 create_client_record(db, &cr);
550 jr.ClientId = cr.ClientId;
552 /* process label, if Job record exists don't update db */
553 mjcr = create_job_record(db, &jr, &label, rec);
554 dcr = mjcr->read_dcr;
555 update_db = save_update_db;
557 jr.PoolId = pr.PoolId;
558 mjcr->start_time = jr.StartTime;
559 mjcr->setJobLevel(jr.JobLevel);
561 mjcr->client_name = get_pool_memory(PM_FNAME);
562 pm_strcpy(mjcr->client_name, label.ClientName);
563 mjcr->fileset_name = get_pool_memory(PM_FNAME);
564 pm_strcpy(mjcr->fileset_name, label.FileSetName);
565 bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
566 bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
568 /* Look for existing Job Media records for this job. If there are
569 any, no new ones need be created. This may occur if File
570 Retention has expired before Job Retention, or if the volume
571 has already been bscan'd */
572 Mmsg(sql_buffer, "SELECT count(*) from JobMedia where JobId=%d", jr.JobId);
573 db_sql_query(db, sql_buffer.c_str(), db_int64_handler, &jmr_count);
574 if( jmr_count.value > 0 ) {
575 //FIELD NAME TO BE DEFINED/CONFIRMED (maybe a struct?)
576 mjcr->bscan_insert_jobmedia_records = false;
578 mjcr->bscan_insert_jobmedia_records = true;
581 if (rec->VolSessionId != jr.VolSessionId) {
582 Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
584 jr.VolSessionId, rec->VolSessionId);
585 return true; /* ignore error */
587 if (rec->VolSessionTime != jr.VolSessionTime) {
588 Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
590 jr.VolSessionTime, rec->VolSessionTime);
591 return true; /* ignore error */
593 if (jr.PoolId != pr.PoolId) {
594 Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
596 jr.PoolId, pr.PoolId);
597 return true; /* ignore error */
602 unser_session_label(&elabel, rec);
604 /* Create FileSet record */
605 bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
606 bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
607 create_fileset_record(db, &fsr);
608 jr.FileSetId = fsr.FileSetId;
610 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
612 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
613 rec->VolSessionId, rec->VolSessionTime);
617 /* Do the final update to the Job record */
618 update_job_record(db, &jr, &elabel, rec);
620 mjcr->end_time = jr.EndTime;
621 mjcr->JobStatus = JS_Terminated;
623 /* Create JobMedia record */
624 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
625 if( mjcr->bscan_insert_jobmedia_records ) {
626 create_jobmedia_record(db, mjcr);
628 free_dcr(mjcr->read_dcr);
636 case EOT_LABEL: /* end of all tapes */
638 * Wiffle through all jobs still open and close
643 foreach_dlist(mdcr, dev->attached_dcrs) {
644 JCR *mjcr = mdcr->jcr;
645 if (!mjcr || mjcr->JobId == 0) {
648 jr.JobId = mjcr->JobId;
649 /* Mark Job as Error Terimined */
650 jr.JobStatus = JS_ErrorTerminated;
651 jr.JobFiles = mjcr->JobFiles;
652 jr.JobBytes = mjcr->JobBytes;
653 jr.VolSessionId = mjcr->VolSessionId;
654 jr.VolSessionTime = mjcr->VolSessionTime;
655 jr.JobTDate = (utime_t)mjcr->start_time;
656 jr.ClientId = mjcr->ClientId;
657 if (!db_update_job_end_record(bjcr, db, &jr)) {
658 Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
660 mjcr->read_dcr = NULL;
664 mr.VolFiles = (uint32_t)(rec->Addr >> 32);
665 mr.VolBlocks = (uint32_t)rec->Addr;
666 mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
668 update_media_record(db, &mr);
669 Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
670 mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
678 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
680 if (mr.VolJobs > 0) {
681 Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
682 rec->VolSessionId, rec->VolSessionTime);
688 dcr = mjcr->read_dcr;
689 if (dcr->VolFirstIndex == 0) {
690 dcr->VolFirstIndex = block->FirstIndex;
693 /* File Attributes stream */
694 switch (rec->maskedStream) {
695 case STREAM_UNIX_ATTRIBUTES:
696 case STREAM_UNIX_ATTRIBUTES_EX:
698 if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, rec->data_len, attr)) {
699 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
703 decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
704 build_attr_output_fnames(bjcr, attr);
705 print_ls_output(bjcr, attr);
707 fr.JobId = mjcr->JobId;
710 if (verbose && (num_files & 0x7FFF) == 0) {
711 char ed1[30], ed2[30], ed3[30];
712 Pmsg3(000, _("%s file records. At addr=%s bytes=%s\n"),
713 edit_uint64_with_commas(num_files, ed1),
714 edit_uint64_with_commas(rec->Addr, ed2),
715 edit_uint64_with_commas(mr.VolBytes, ed3));
717 create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
718 attr->type, attr->attr, rec);
722 case STREAM_RESTORE_OBJECT:
724 /* Implement putting into catalog */
728 case STREAM_WIN32_DATA:
729 case STREAM_FILE_DATA:
730 case STREAM_SPARSE_DATA:
731 case STREAM_MACOS_FORK_DATA:
732 case STREAM_ENCRYPTED_FILE_DATA:
733 case STREAM_ENCRYPTED_WIN32_DATA:
734 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
736 * For encrypted stream, this is an approximation.
737 * The data must be decrypted to know the correct length.
739 mjcr->JobBytes += rec->data_len;
740 if (rec->maskedStream == STREAM_SPARSE_DATA) {
741 mjcr->JobBytes -= sizeof(uint64_t);
744 free_jcr(mjcr); /* done using JCR */
747 case STREAM_GZIP_DATA:
748 case STREAM_COMPRESSED_DATA:
749 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
750 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
751 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
752 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
753 /* No correct, we should (decrypt and) expand it
756 mjcr->JobBytes += rec->data_len;
760 case STREAM_SPARSE_GZIP_DATA:
761 case STREAM_SPARSE_COMPRESSED_DATA:
762 mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
763 free_jcr(mjcr); /* done using JCR */
766 /* Win32 GZIP stream */
767 case STREAM_WIN32_GZIP_DATA:
768 case STREAM_WIN32_COMPRESSED_DATA:
769 mjcr->JobBytes += rec->data_len;
770 free_jcr(mjcr); /* done using JCR */
773 case STREAM_MD5_DIGEST:
774 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE, true);
776 Pmsg1(000, _("Got MD5 record: %s\n"), digest);
778 update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
781 case STREAM_SHA1_DIGEST:
782 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE, true);
784 Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
786 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
789 case STREAM_SHA256_DIGEST:
790 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE, true);
792 Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
794 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
797 case STREAM_SHA512_DIGEST:
798 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE, true);
800 Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
802 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
805 case STREAM_ENCRYPTED_SESSION_DATA:
806 // TODO landonf: Investigate crypto support in bscan
808 Pmsg0(000, _("Got signed digest record\n"));
812 case STREAM_SIGNED_DIGEST:
813 // TODO landonf: Investigate crypto support in bscan
815 Pmsg0(000, _("Got signed digest record\n"));
819 case STREAM_PROGRAM_NAMES:
821 Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
825 case STREAM_PROGRAM_DATA:
827 Pmsg0(000, _("Got Prog Data Stream record.\n"));
831 case STREAM_UNIX_ACCESS_ACL: /* Deprecated Standard ACL attributes on UNIX */
832 case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX */
833 case STREAM_HFSPLUS_ATTRIBUTES:
834 case STREAM_XACL_AIX_TEXT:
835 case STREAM_XACL_DARWIN_ACCESS:
836 case STREAM_XACL_FREEBSD_DEFAULT:
837 case STREAM_XACL_FREEBSD_ACCESS:
838 case STREAM_XACL_HPUX_ACL_ENTRY:
839 case STREAM_XACL_IRIX_DEFAULT:
840 case STREAM_XACL_IRIX_ACCESS:
841 case STREAM_XACL_LINUX_DEFAULT:
842 case STREAM_XACL_LINUX_ACCESS:
843 case STREAM_XACL_TRU64_DEFAULT:
844 case STREAM_XACL_TRU64_DEFAULT_DIR:
845 case STREAM_XACL_TRU64_ACCESS:
846 case STREAM_XACL_SOLARIS_POSIX:
847 case STREAM_XACL_SOLARIS_NFS4:
848 case STREAM_XACL_AFS_TEXT:
849 case STREAM_XACL_AIX_AIXC:
850 case STREAM_XACL_AIX_NFS4:
851 case STREAM_XACL_FREEBSD_NFS4:
852 case STREAM_XACL_HURD_DEFAULT:
853 case STREAM_XACL_HURD_ACCESS:
854 /* Ignore Unix ACL attributes */
857 case STREAM_XACL_HURD_XATTR:
858 case STREAM_XACL_IRIX_XATTR:
859 case STREAM_XACL_TRU64_XATTR:
860 case STREAM_XACL_AIX_XATTR:
861 case STREAM_XACL_OPENBSD_XATTR:
862 case STREAM_XACL_SOLARIS_SYS_XATTR:
863 case STREAM_XACL_SOLARIS_XATTR:
864 case STREAM_XACL_DARWIN_XATTR:
865 case STREAM_XACL_FREEBSD_XATTR:
866 case STREAM_XACL_LINUX_XATTR:
867 case STREAM_XACL_NETBSD_XATTR:
868 /* Ignore Unix Extended attributes */
872 Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream, rec->data_len);
879 * Free the Job Control Record if no one is still using it.
880 * Called from main free_jcr() routine in src/lib/jcr.c so
881 * that we can do our Director specific cleanup of the jcr.
883 static void bscan_free_jcr(JCR *jcr)
885 Dmsg0(200, "Start bscan free_jcr\n");
887 free_bsock(jcr->file_bsock);
888 free_bsock(jcr->store_bsock);
889 if (jcr->RestoreBootstrap) {
890 free(jcr->RestoreBootstrap);
897 free_dcr(jcr->read_dcr);
898 jcr->read_dcr = NULL;
900 Dmsg0(200, "End bscan free_jcr\n");
904 * We got a File Attributes record on the tape. Now, lookup the Job
905 * record, and then create the attributes record.
907 static int create_file_attributes_record(BDB *db, JCR *mjcr,
908 char *fname, char *lname, int type,
909 char *ap, DEV_RECORD *rec)
911 DCR *dcr = mjcr->read_dcr;
914 ar.ClientId = mjcr->ClientId;
915 ar.JobId = mjcr->JobId;
916 ar.Stream = rec->Stream;
917 if (type == FT_DELETED) {
920 ar.FileIndex = rec->FileIndex;
923 if (dcr->VolFirstIndex == 0) {
924 dcr->VolFirstIndex = rec->FileIndex;
926 dcr->FileIndex = rec->FileIndex;
933 if (!db_create_file_attributes_record(bjcr, db, &ar)) {
934 Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
937 mjcr->FileId = ar.FileId;
940 Pmsg1(000, _("Created File record: %s\n"), fname);
946 * For each Volume we see, we create a Medium record
948 static int create_media_record(BDB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
953 /* We mark Vols as Archive to keep them from being re-written */
954 bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
955 mr->VolRetention = 365 * 3600 * 24; /* 1 year */
957 if (vl->VerNum >= 11) {
958 mr->set_first_written = true; /* Save FirstWritten during update_media */
959 mr->FirstWritten = btime_to_utime(vl->write_btime);
960 mr->LabelDate = btime_to_utime(vl->label_btime);
962 /* DEPRECATED DO NOT USE */
963 dt.julian_day_number = vl->write_date;
964 dt.julian_day_fraction = vl->write_time;
966 mr->FirstWritten = mktime(&tm);
967 dt.julian_day_number = vl->label_date;
968 dt.julian_day_fraction = vl->label_time;
970 mr->LabelDate = mktime(&tm);
972 lasttime = mr->LabelDate;
973 if (mr->VolJobs == 0) {
976 if (mr->VolMounts == 0) {
984 if (!db_create_media_record(bjcr, db, mr)) {
985 Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
988 if (!db_update_media_record(bjcr, db, mr)) {
989 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
993 Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
1000 * Called at end of media to update it
1002 static bool update_media_record(BDB *db, MEDIA_DBR *mr)
1004 if (!update_db && !update_vol_info) {
1008 mr->LastWritten = lasttime;
1009 if (!db_update_media_record(bjcr, db, mr)) {
1010 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
1014 Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
1021 static int create_pool_record(BDB *db, POOL_DBR *pr)
1025 pr->VolRetention = 355 * 3600 * 24; /* 1 year */
1030 if (!db_create_pool_record(bjcr, db, pr)) {
1031 Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
1035 Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
1043 * Called from SOS to create a client for the current Job
1045 static int create_client_record(BDB *db, CLIENT_DBR *cr)
1048 * Note, update_db can temporarily be set false while
1049 * updating the database, so we must ensure that ClientId is non-zero.
1053 if (!db_get_client_record(bjcr, db, cr)) {
1054 Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db_strerror(db));
1059 if (!db_create_client_record(bjcr, db, cr)) {
1060 Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
1064 Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
1069 static int create_fileset_record(BDB *db, FILESET_DBR *fsr)
1075 if (fsr->MD5[0] == 0) {
1076 fsr->MD5[0] = ' '; /* Equivalent to nothing */
1079 if (db_get_fileset_record(bjcr, db, fsr)) {
1081 Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1084 if (!db_create_fileset_record(bjcr, db, fsr)) {
1085 Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1086 fsr->FileSet, db_strerror(db));
1090 Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1097 * Simulate the two calls on the database to create
1098 * the Job record and to update it when the Job actually
1101 static JCR *create_job_record(BDB *db, JOB_DBR *jr, SESSION_LABEL *label,
1105 struct date_time dt;
1108 jr->JobId = label->JobId;
1109 jr->JobType = label->JobType;
1110 jr->JobLevel = label->JobLevel;
1111 jr->JobStatus = JS_Created;
1112 bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1113 bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1114 if (label->VerNum >= 11) {
1115 jr->SchedTime = btime_to_unix(label->write_btime);
1117 dt.julian_day_number = label->write_date;
1118 dt.julian_day_fraction = label->write_time;
1119 tm_decode(&dt, &tm);
1120 jr->SchedTime = mktime(&tm);
1123 jr->StartTime = jr->SchedTime;
1124 jr->JobTDate = (utime_t)jr->SchedTime;
1125 jr->VolSessionId = rec->VolSessionId;
1126 jr->VolSessionTime = rec->VolSessionTime;
1128 /* Now create a JCR as if starting the Job */
1129 mjcr = create_jcr(jr, rec, label->JobId);
1135 /* This creates the bare essentials */
1136 if (!db_create_job_record(bjcr, db, jr)) {
1137 Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1141 /* This adds the client, StartTime, JobTDate, ... */
1142 if (!db_update_job_start_record(bjcr, db, jr)) {
1143 Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1146 Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1148 mjcr->JobId = jr->JobId; /* set new JobId */
1153 * Simulate the database call that updates the Job
1154 * at Job termination time.
1156 static int update_job_record(BDB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1159 struct date_time dt;
1163 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1165 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1166 rec->VolSessionId, rec->VolSessionTime);
1169 if (elabel->VerNum >= 11) {
1170 jr->EndTime = btime_to_unix(elabel->write_btime);
1172 dt.julian_day_number = elabel->write_date;
1173 dt.julian_day_fraction = elabel->write_time;
1174 tm_decode(&dt, &tm);
1175 jr->EndTime = mktime(&tm);
1177 lasttime = jr->EndTime;
1178 mjcr->end_time = jr->EndTime;
1180 jr->JobId = mjcr->JobId;
1182 /* The JobStatus can't be 0 */
1183 if (elabel->JobStatus == 0) {
1184 Pmsg2(000, _("Could not find JobStatus for SessId=%d SessTime=%d in EOS record.\n"),
1185 rec->VolSessionId, rec->VolSessionTime);
1187 mjcr->JobStatus = jr->JobStatus =
1188 elabel->JobStatus ? elabel->JobStatus : JS_ErrorTerminated;
1190 jr->JobFiles = elabel->JobFiles;
1191 if (jr->JobFiles > 0) { /* If we found files, force PurgedFiles */
1192 jr->PurgedFiles = 0;
1194 jr->JobBytes = elabel->JobBytes;
1195 jr->VolSessionId = rec->VolSessionId;
1196 jr->VolSessionTime = rec->VolSessionTime;
1197 jr->JobTDate = (utime_t)mjcr->start_time;
1198 jr->ClientId = mjcr->ClientId;
1205 if (!db_update_job_end_record(bjcr, db, jr)) {
1206 Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId, db_strerror(db));
1211 Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1212 jr->JobId, job_level_to_str(mjcr->getJobLevel()), jr->JobStatus);
1215 const char *term_msg;
1216 static char term_code[70];
1217 char sdt[50], edt[50];
1218 char ec1[30], ec2[30], ec3[30];
1220 switch (mjcr->JobStatus) {
1222 term_msg = _("Backup OK");
1225 term_msg = _("Backup OK -- with warnings");
1228 case JS_ErrorTerminated:
1229 term_msg = _("*** Backup Error ***");
1232 term_msg = _("Backup Canceled");
1235 term_msg = term_code;
1236 sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1239 bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1240 bstrftime(edt, sizeof(edt), mjcr->end_time);
1241 Pmsg14(000, _("%s\n"
1245 "Backup Level: %s\n"
1249 "Files Written: %s\n"
1250 "Bytes Written: %s\n"
1251 "Volume Session Id: %d\n"
1252 "Volume Session Time: %d\n"
1253 "Last Volume Bytes: %s\n"
1254 "Termination: %s\n\n"),
1259 job_level_to_str(mjcr->getJobLevel()),
1263 edit_uint64_with_commas(mjcr->JobFiles, ec1),
1264 edit_uint64_with_commas(mjcr->JobBytes, ec2),
1266 mjcr->VolSessionTime,
1267 edit_uint64_with_commas(mr.VolBytes, ec3),
1274 static int create_jobmedia_record(BDB *db, JCR *mjcr)
1277 DCR *dcr = mjcr->read_dcr;
1279 dcr->EndAddr = dev->EndAddr;
1280 dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1282 memset(&jmr, 0, sizeof(jmr));
1283 jmr.JobId = mjcr->JobId;
1284 jmr.MediaId = mr.MediaId;
1285 jmr.FirstIndex = dcr->VolFirstIndex;
1286 jmr.LastIndex = dcr->VolLastIndex;
1287 jmr.StartBlock = (uint32_t)dcr->StartAddr;
1288 jmr.StartFile = (uint32_t)(dcr->StartAddr >> 32);
1289 jmr.EndBlock = (uint32_t)dcr->EndAddr;
1290 jmr.EndFile = (uint32_t)(dcr->EndAddr >> 32);
1295 if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1296 Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1300 Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1301 jmr.JobId, jmr.MediaId);
1307 * Simulate the database call that updates the MD5/SHA1 record
1309 static int update_digest_record(BDB *db, char *digest, DEV_RECORD *rec, int type)
1313 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1315 if (mr.VolJobs > 0) {
1316 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1317 rec->VolSessionId, rec->VolSessionTime);
1324 if (!update_db || mjcr->FileId == 0) {
1329 if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1330 Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1335 Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1343 * Create a JCR as if we are really starting the job
1345 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1349 * Transfer as much as possible to the Job JCR. Most important is
1350 * the JobId and the ClientId.
1352 jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1353 jobjcr->setJobType(jr->JobType);
1354 jobjcr->setJobLevel(jr->JobLevel);
1355 jobjcr->JobStatus = jr->JobStatus;
1356 bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1357 jobjcr->JobId = JobId; /* this is JobId on tape */
1358 jobjcr->sched_time = jr->SchedTime;
1359 jobjcr->start_time = jr->StartTime;
1360 jobjcr->VolSessionId = rec->VolSessionId;
1361 jobjcr->VolSessionTime = rec->VolSessionTime;
1362 jobjcr->ClientId = jr->ClientId;
1363 jobjcr->dcr = jobjcr->read_dcr = new_dcr(jobjcr, NULL, dev, SD_READ);