2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Program to scan a Bacula Volume and compare it with
23 * the catalog and optionally synchronize the catalog
26 * Kern E. Sibbald, December 2001
32 #include "findlib/find.h"
33 #include "cats/cats.h"
35 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
37 /* Forward referenced functions */
38 static void do_scan(void);
39 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
40 static int create_file_attributes_record(BDB *db, JCR *mjcr,
41 char *fname, char *lname, int type,
42 char *ap, DEV_RECORD *rec);
43 static int create_media_record(BDB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
44 static bool update_media_record(BDB *db, MEDIA_DBR *mr);
45 static int create_pool_record(BDB *db, POOL_DBR *pr);
46 static JCR *create_job_record(BDB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
47 static int update_job_record(BDB *db, JOB_DBR *mr, SESSION_LABEL *elabel,
49 static int create_client_record(BDB *db, CLIENT_DBR *cr);
50 static int create_fileset_record(BDB *db, FILESET_DBR *fsr);
51 static int create_jobmedia_record(BDB *db, JCR *jcr);
52 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
53 static int update_digest_record(BDB *db, char *digest, DEV_RECORD *rec, int type);
57 static DEVICE *dev = NULL;
59 static JCR *bjcr; /* jcr for bscan */
60 static BSR *bsr = NULL;
65 static FILESET_DBR fsr;
68 static SESSION_LABEL label;
69 static SESSION_LABEL elabel;
72 static time_t lasttime = 0;
74 static const char *db_driver = "NULL";
75 static const char *db_name = "bacula";
76 static const char *db_user = "bacula";
77 static const char *db_password = "";
78 static const char *db_host = NULL;
79 static const char *db_ssl_key = NULL;
80 static const char *db_ssl_cert = NULL;
81 static const char *db_ssl_ca = NULL;
82 static const char *db_ssl_capath = NULL;
83 static const char *db_ssl_cipher = NULL;
84 static int db_port = 0;
85 static const char *wd = NULL;
86 static bool update_db = false;
87 static bool update_vol_info = false;
88 static bool list_records = false;
89 static int ignored_msgs = 0;
91 static uint64_t currentVolumeSize;
92 static int last_pct = -1;
93 static bool showProgress = false;
94 static int num_jobs = 0;
95 static int num_pools = 0;
96 static int num_media = 0;
97 static int num_files = 0;
99 static CONFIG *config;
100 #define CONFIG_FILE "bacula-sd.conf"
103 char *configfile = NULL;
104 STORES *me = NULL; /* our Global resource */
105 bool forge_on = false; /* proceed inspite of I/O errors */
106 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
107 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
113 "\n%sVersion: %s (%s)\n\n"
114 "Usage: bscan [ options ] <bacula-archive>\n"
115 " -b bootstrap specify a bootstrap file\n"
116 " -c <file> specify configuration file\n"
117 " -d <nn> set debug level to <nn>\n"
118 " -dt print timestamp in debug output\n"
119 " -m update media info in database\n"
120 " -D <driver name> specify the driver database name (default NULL)\n"
121 " -n <name> specify the database name (default bacula)\n"
122 " -u <user> specify database user name (default bacula)\n"
123 " -P <password> specify database password (default none)\n"
124 " -h <host> specify database host (default NULL)\n"
125 " -k <sslkey> path name to the key file (default NULL)\n"
126 " -e <sslcert> path name to the certificate file (default NULL)\n"
127 " -a <sslca> path name to the CA certificate file (default NULL)\n"
128 " -t <port> specify database port (default 0)\n"
129 " -p proceed inspite of I/O errors\n"
131 " -s synchronize or store in database\n"
132 " -S show scan progress periodically\n"
134 " -V <Volumes> specify Volume names (separated by |)\n"
135 " -w <dir> specify working directory (default from conf file)\n"
136 " -? print this message\n\n"),
137 2001, "", VERSION, BDATE);
141 int main (int argc, char *argv[])
144 struct stat stat_buf;
145 char *VolumeName = NULL;
147 setlocale(LC_ALL, "");
148 bindtextdomain("bacula", LOCALEDIR);
149 textdomain("bacula");
153 my_name_is(argc, argv, "bscan");
154 init_msg(NULL, NULL);
158 while ((ch = getopt(argc, argv, "b:c:d:D:h:k:e:a:p:mn:pP:rsSt:u:vV:w:?")) != -1) {
164 bsr = parse_bsr(NULL, optarg);
167 case 'c': /* specify config file */
168 if (configfile != NULL) {
171 configfile = bstrdup(optarg);
178 case 'd': /* debug level */
179 if (*optarg == 't') {
180 dbg_timestamp = true;
182 debug_level = atoi(optarg);
183 if (debug_level <= 0) {
198 db_ssl_cert = optarg;
206 db_port = atoi(optarg);
210 update_vol_info = true;
222 db_password = optarg;
241 case 'V': /* Volume name */
259 Pmsg0(0, _("Wrong number of arguments: \n"));
263 if (configfile == NULL) {
264 configfile = bstrdup(CONFIG_FILE);
267 config = new_config_parser();
268 parse_sd_config(config, configfile, M_ERROR_TERM);
270 load_sd_plugins(me->plugin_directory);
272 /* Check if -w option given, otherwise use resource for working directory */
274 working_directory = wd;
275 } else if (!me->working_directory) {
276 Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
279 working_directory = me->working_directory;
282 /* Check that working directory is good */
283 if (stat(working_directory, &stat_buf) != 0) {
284 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
287 if (!S_ISDIR(stat_buf.st_mode)) {
288 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
292 bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, SD_READ);
296 dev = bjcr->read_dcr->dev;
300 fstat(dev->fd(), &sb);
301 currentVolumeSize = sb.st_size;
302 Pmsg1(000, _("First Volume Size = %s\n"),
303 edit_uint64(currentVolumeSize, ed1));
306 db = db_init_database(NULL, db_driver, db_name, db_user, db_password,
307 db_host, db_port, NULL,
308 db_ssl_key, db_ssl_cert, db_ssl_ca,
309 db_ssl_capath, db_ssl_cipher,
311 if (!db || !db_open_database(NULL, db)) {
312 Pmsg2(000, _("Could not open Catalog \"%s\", database \"%s\".\n"),
315 Jmsg(NULL, M_FATAL, 0, _("%s"), db_strerror(db));
316 Pmsg1(000, "%s", db_strerror(db));
317 db_close_database(NULL, db);
319 Jmsg(NULL, M_ERROR_TERM, 0, _("Could not open Catalog \"%s\", database \"%s\".\n"),
322 Dmsg0(200, "Database opened\n");
324 Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
329 printf("Records added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
330 num_media, num_pools, num_jobs, num_files);
332 printf("Records would have been added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
333 num_media, num_pools, num_jobs, num_files);
342 * We are at the end of reading a tape. Now, we simulate handling
343 * the end of writing a tape by wiffling through the attached
344 * jcrs creating jobmedia records.
346 static bool bscan_mount_next_read_volume(DCR *dcr)
348 DEVICE *dev = dcr->dev;
350 Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->getVolCatName());
351 foreach_dlist(mdcr, dev->attached_dcrs) {
352 JCR *mjcr = mdcr->jcr;
353 Dmsg1(100, "========== JobId=%u ========\n", mjcr->JobId);
354 if (mjcr->JobId == 0) {
358 Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
360 mdcr->StartBlock = dcr->StartBlock;
361 mdcr->StartFile = dcr->StartFile;
362 mdcr->EndBlock = dcr->EndBlock;
363 mdcr->EndFile = dcr->EndFile;
364 mdcr->VolMediaId = dcr->VolMediaId;
365 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
366 if( mjcr->bscan_insert_jobmedia_records ) {
367 if (!create_jobmedia_record(db, mjcr)) {
368 Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
369 dev->getVolCatName(), mjcr->Job);
374 update_media_record(db, &mr);
376 /* Now let common read routine get up next tape. Note,
377 * we call mount_next... with bscan's jcr because that is where we
378 * have the Volume list, but we get attached.
380 bool stat = mount_next_read_volume(dcr);
385 fstat(dev->fd(), &sb);
386 currentVolumeSize = sb.st_size;
387 Pmsg1(000, _("First Volume Size = %s\n"),
388 edit_uint64(currentVolumeSize, ed1));
393 static void do_scan()
395 attr = new_attr(bjcr);
397 memset(&ar, 0, sizeof(ar));
398 memset(&pr, 0, sizeof(pr));
399 memset(&jr, 0, sizeof(jr));
400 memset(&cr, 0, sizeof(cr));
401 memset(&fsr, 0, sizeof(fsr));
402 memset(&fr, 0, sizeof(fr));
404 /* Detach bscan's jcr as we are not a real Job on the tape */
406 read_records(bjcr->read_dcr, record_cb, bscan_mount_next_read_volume);
409 db_write_batch_file_records(bjcr); /* used by bulk batch file insert */
415 * Returns: true if OK
418 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
422 DEVICE *dev = dcr->dev;
423 JCR *bjcr = dcr->jcr;
424 DEV_BLOCK *block = dcr->block;
426 db_int64_ctx jmr_count;
428 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
430 if (rec->data_len > 0) {
431 mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
432 if (showProgress && currentVolumeSize > 0) {
433 int pct = (mr.VolBytes * 100) / currentVolumeSize;
434 if (pct != last_pct) {
435 fprintf(stdout, _("done: %d%%\n"), pct);
443 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
444 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
445 rec->Stream, rec->data_len);
448 * Check for Start or End of Session Record
451 if (rec->FileIndex < 0) {
452 bool save_update_db = update_db;
455 dump_label_record(dev, rec, 1, false);
457 switch (rec->FileIndex) {
459 Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
464 unser_volume_label(dev, rec);
465 /* Check Pool info */
466 bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
467 bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
469 if (db_get_pool_numvols(bjcr, db, &pr)) {
471 Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
475 Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
478 create_pool_record(db, &pr);
480 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
481 Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
482 pr.PoolType, dev->VolHdr.PoolType);
484 } else if (verbose) {
485 Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
488 /* Check Media Info */
489 memset(&mr, 0, sizeof(mr));
490 bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
491 mr.PoolId = pr.PoolId;
493 if (db_get_media_record(bjcr, db, &mr)) {
495 Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
497 /* Clear out some volume statistics that will be updated */
498 mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
499 mr.VolBytes = rec->data_len + 20;
502 Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
505 bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
506 create_media_record(db, &mr, &dev->VolHdr);
508 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
509 Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
510 mr.MediaType, dev->VolHdr.MediaType);
511 return true; /* ignore error */
512 } else if (verbose) {
513 Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
515 /* Reset some DCR variables */
516 foreach_dlist(dcr, dev->attached_dcrs) {
517 dcr->VolFirstIndex = dcr->FileIndex = 0;
518 dcr->StartBlock = dcr->EndBlock = 0;
519 dcr->StartFile = dcr->EndFile = 0;
523 Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
529 if (ignored_msgs > 0) {
530 Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
534 unser_session_label(&label, rec);
535 memset(&jr, 0, sizeof(jr));
536 bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
537 if (db_get_job_record(bjcr, db, &jr)) {
538 /* Job record already exists in DB */
539 update_db = false; /* don't change db in create_job_record */
541 Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
544 /* Must create a Job record in DB */
546 Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
551 /* Create Client record if not already there */
552 bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
553 create_client_record(db, &cr);
554 jr.ClientId = cr.ClientId;
556 /* process label, if Job record exists don't update db */
557 mjcr = create_job_record(db, &jr, &label, rec);
558 dcr = mjcr->read_dcr;
559 update_db = save_update_db;
561 jr.PoolId = pr.PoolId;
562 mjcr->start_time = jr.StartTime;
563 mjcr->setJobLevel(jr.JobLevel);
565 mjcr->client_name = get_pool_memory(PM_FNAME);
566 pm_strcpy(mjcr->client_name, label.ClientName);
567 mjcr->fileset_name = get_pool_memory(PM_FNAME);
568 pm_strcpy(mjcr->fileset_name, label.FileSetName);
569 bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
570 bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
572 /* Look for existing Job Media records for this job. If there are
573 any, no new ones need be created. This may occur if File
574 Retention has expired before Job Retention, or if the volume
575 has already been bscan'd */
576 Mmsg(sql_buffer, "SELECT count(*) from JobMedia where JobId=%d", jr.JobId);
577 db_sql_query(db, sql_buffer.c_str(), db_int64_handler, &jmr_count);
578 if( jmr_count.value > 0 ) {
579 //FIELD NAME TO BE DEFINED/CONFIRMED (maybe a struct?)
580 mjcr->bscan_insert_jobmedia_records = false;
582 mjcr->bscan_insert_jobmedia_records = true;
585 if (rec->VolSessionId != jr.VolSessionId) {
586 Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
588 jr.VolSessionId, rec->VolSessionId);
589 return true; /* ignore error */
591 if (rec->VolSessionTime != jr.VolSessionTime) {
592 Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
594 jr.VolSessionTime, rec->VolSessionTime);
595 return true; /* ignore error */
597 if (jr.PoolId != pr.PoolId) {
598 Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
600 jr.PoolId, pr.PoolId);
601 return true; /* ignore error */
606 unser_session_label(&elabel, rec);
608 /* Create FileSet record */
609 bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
610 bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
611 create_fileset_record(db, &fsr);
612 jr.FileSetId = fsr.FileSetId;
614 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
616 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
617 rec->VolSessionId, rec->VolSessionTime);
621 /* Do the final update to the Job record */
622 update_job_record(db, &jr, &elabel, rec);
624 mjcr->end_time = jr.EndTime;
625 mjcr->JobStatus = JS_Terminated;
627 /* Create JobMedia record */
628 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
629 if( mjcr->bscan_insert_jobmedia_records ) {
630 create_jobmedia_record(db, mjcr);
632 free_dcr(mjcr->read_dcr);
640 case EOT_LABEL: /* end of all tapes */
642 * Wiffle through all jobs still open and close
647 foreach_dlist(mdcr, dev->attached_dcrs) {
648 JCR *mjcr = mdcr->jcr;
649 if (!mjcr || mjcr->JobId == 0) {
652 jr.JobId = mjcr->JobId;
653 /* Mark Job as Error Terimined */
654 jr.JobStatus = JS_ErrorTerminated;
655 jr.JobFiles = mjcr->JobFiles;
656 jr.JobBytes = mjcr->JobBytes;
657 jr.VolSessionId = mjcr->VolSessionId;
658 jr.VolSessionTime = mjcr->VolSessionTime;
659 jr.JobTDate = (utime_t)mjcr->start_time;
660 jr.ClientId = mjcr->ClientId;
661 if (!db_update_job_end_record(bjcr, db, &jr)) {
662 Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
664 mjcr->read_dcr = NULL;
668 mr.VolFiles = rec->File;
669 mr.VolBlocks = rec->Block;
670 mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
672 update_media_record(db, &mr);
673 Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
674 mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
682 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
684 if (mr.VolJobs > 0) {
685 Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
686 rec->VolSessionId, rec->VolSessionTime);
692 dcr = mjcr->read_dcr;
693 if (dcr->VolFirstIndex == 0) {
694 dcr->VolFirstIndex = block->FirstIndex;
697 /* File Attributes stream */
698 switch (rec->maskedStream) {
699 case STREAM_UNIX_ATTRIBUTES:
700 case STREAM_UNIX_ATTRIBUTES_EX:
702 if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, rec->data_len, attr)) {
703 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
707 decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
708 build_attr_output_fnames(bjcr, attr);
709 print_ls_output(bjcr, attr);
711 fr.JobId = mjcr->JobId;
714 if (verbose && (num_files & 0x7FFF) == 0) {
715 char ed1[30], ed2[30], ed3[30], ed4[30];
716 Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
717 edit_uint64_with_commas(num_files, ed1),
718 edit_uint64_with_commas(rec->File, ed2),
719 edit_uint64_with_commas(rec->Block, ed3),
720 edit_uint64_with_commas(mr.VolBytes, ed4));
722 create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
723 attr->type, attr->attr, rec);
727 case STREAM_RESTORE_OBJECT:
729 /* Implement putting into catalog */
733 case STREAM_WIN32_DATA:
734 case STREAM_FILE_DATA:
735 case STREAM_SPARSE_DATA:
736 case STREAM_MACOS_FORK_DATA:
737 case STREAM_ENCRYPTED_FILE_DATA:
738 case STREAM_ENCRYPTED_WIN32_DATA:
739 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
741 * For encrypted stream, this is an approximation.
742 * The data must be decrypted to know the correct length.
744 mjcr->JobBytes += rec->data_len;
745 if (rec->maskedStream == STREAM_SPARSE_DATA) {
746 mjcr->JobBytes -= sizeof(uint64_t);
749 free_jcr(mjcr); /* done using JCR */
752 case STREAM_GZIP_DATA:
753 case STREAM_COMPRESSED_DATA:
754 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
755 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
756 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
757 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
758 /* No correct, we should (decrypt and) expand it
761 mjcr->JobBytes += rec->data_len;
765 case STREAM_SPARSE_GZIP_DATA:
766 case STREAM_SPARSE_COMPRESSED_DATA:
767 mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
768 free_jcr(mjcr); /* done using JCR */
771 /* Win32 GZIP stream */
772 case STREAM_WIN32_GZIP_DATA:
773 case STREAM_WIN32_COMPRESSED_DATA:
774 mjcr->JobBytes += rec->data_len;
775 free_jcr(mjcr); /* done using JCR */
778 case STREAM_MD5_DIGEST:
779 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE, true);
781 Pmsg1(000, _("Got MD5 record: %s\n"), digest);
783 update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
786 case STREAM_SHA1_DIGEST:
787 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE, true);
789 Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
791 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
794 case STREAM_SHA256_DIGEST:
795 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE, true);
797 Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
799 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
802 case STREAM_SHA512_DIGEST:
803 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE, true);
805 Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
807 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
810 case STREAM_ENCRYPTED_SESSION_DATA:
811 // TODO landonf: Investigate crypto support in bscan
813 Pmsg0(000, _("Got signed digest record\n"));
817 case STREAM_SIGNED_DIGEST:
818 // TODO landonf: Investigate crypto support in bscan
820 Pmsg0(000, _("Got signed digest record\n"));
824 case STREAM_PROGRAM_NAMES:
826 Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
830 case STREAM_PROGRAM_DATA:
832 Pmsg0(000, _("Got Prog Data Stream record.\n"));
836 case STREAM_UNIX_ACCESS_ACL: /* Deprecated Standard ACL attributes on UNIX */
837 case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX */
838 case STREAM_HFSPLUS_ATTRIBUTES:
839 case STREAM_ACL_AIX_TEXT:
840 case STREAM_ACL_DARWIN_ACCESS:
841 case STREAM_ACL_FREEBSD_DEFAULT:
842 case STREAM_ACL_FREEBSD_ACCESS:
843 case STREAM_ACL_HPUX_ACL_ENTRY:
844 case STREAM_ACL_IRIX_DEFAULT:
845 case STREAM_ACL_IRIX_ACCESS:
846 case STREAM_ACL_LINUX_DEFAULT:
847 case STREAM_ACL_LINUX_ACCESS:
848 case STREAM_ACL_TRU64_DEFAULT:
849 case STREAM_ACL_TRU64_DEFAULT_DIR:
850 case STREAM_ACL_TRU64_ACCESS:
851 case STREAM_ACL_SOLARIS_POSIX:
852 case STREAM_ACL_SOLARIS_NFS4:
853 case STREAM_ACL_AFS_TEXT:
854 case STREAM_ACL_AIX_AIXC:
855 case STREAM_ACL_AIX_NFS4:
856 case STREAM_ACL_FREEBSD_NFS4:
857 case STREAM_ACL_HURD_DEFAULT:
858 case STREAM_ACL_HURD_ACCESS:
859 /* Ignore Unix ACL attributes */
862 case STREAM_XATTR_HURD:
863 case STREAM_XATTR_IRIX:
864 case STREAM_XATTR_TRU64:
865 case STREAM_XATTR_AIX:
866 case STREAM_XATTR_OPENBSD:
867 case STREAM_XATTR_SOLARIS_SYS:
868 case STREAM_XATTR_SOLARIS:
869 case STREAM_XATTR_DARWIN:
870 case STREAM_XATTR_FREEBSD:
871 case STREAM_XATTR_LINUX:
872 case STREAM_XATTR_NETBSD:
873 /* Ignore Unix Extended attributes */
877 Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream, rec->data_len);
884 * Free the Job Control Record if no one is still using it.
885 * Called from main free_jcr() routine in src/lib/jcr.c so
886 * that we can do our Director specific cleanup of the jcr.
888 static void bscan_free_jcr(JCR *jcr)
890 Dmsg0(200, "Start bscan free_jcr\n");
892 free_bsock(jcr->file_bsock);
893 free_bsock(jcr->store_bsock);
894 if (jcr->RestoreBootstrap) {
895 free(jcr->RestoreBootstrap);
902 free_dcr(jcr->read_dcr);
903 jcr->read_dcr = NULL;
905 Dmsg0(200, "End bscan free_jcr\n");
909 * We got a File Attributes record on the tape. Now, lookup the Job
910 * record, and then create the attributes record.
912 static int create_file_attributes_record(BDB *db, JCR *mjcr,
913 char *fname, char *lname, int type,
914 char *ap, DEV_RECORD *rec)
916 DCR *dcr = mjcr->read_dcr;
919 ar.ClientId = mjcr->ClientId;
920 ar.JobId = mjcr->JobId;
921 ar.Stream = rec->Stream;
922 if (type == FT_DELETED) {
925 ar.FileIndex = rec->FileIndex;
928 if (dcr->VolFirstIndex == 0) {
929 dcr->VolFirstIndex = rec->FileIndex;
931 dcr->FileIndex = rec->FileIndex;
938 if (!db_create_file_attributes_record(bjcr, db, &ar)) {
939 Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
942 mjcr->FileId = ar.FileId;
945 Pmsg1(000, _("Created File record: %s\n"), fname);
951 * For each Volume we see, we create a Medium record
953 static int create_media_record(BDB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
958 /* We mark Vols as Archive to keep them from being re-written */
959 bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
960 mr->VolRetention = 365 * 3600 * 24; /* 1 year */
962 if (vl->VerNum >= 11) {
963 mr->set_first_written = true; /* Save FirstWritten during update_media */
964 mr->FirstWritten = btime_to_utime(vl->write_btime);
965 mr->LabelDate = btime_to_utime(vl->label_btime);
967 /* DEPRECATED DO NOT USE */
968 dt.julian_day_number = vl->write_date;
969 dt.julian_day_fraction = vl->write_time;
971 mr->FirstWritten = mktime(&tm);
972 dt.julian_day_number = vl->label_date;
973 dt.julian_day_fraction = vl->label_time;
975 mr->LabelDate = mktime(&tm);
977 lasttime = mr->LabelDate;
978 if (mr->VolJobs == 0) {
981 if (mr->VolMounts == 0) {
989 if (!db_create_media_record(bjcr, db, mr)) {
990 Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
993 if (!db_update_media_record(bjcr, db, mr)) {
994 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
998 Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
1005 * Called at end of media to update it
1007 static bool update_media_record(BDB *db, MEDIA_DBR *mr)
1009 if (!update_db && !update_vol_info) {
1013 mr->LastWritten = lasttime;
1014 if (!db_update_media_record(bjcr, db, mr)) {
1015 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
1019 Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
1026 static int create_pool_record(BDB *db, POOL_DBR *pr)
1030 pr->VolRetention = 355 * 3600 * 24; /* 1 year */
1035 if (!db_create_pool_record(bjcr, db, pr)) {
1036 Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
1040 Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
1048 * Called from SOS to create a client for the current Job
1050 static int create_client_record(BDB *db, CLIENT_DBR *cr)
1053 * Note, update_db can temporarily be set false while
1054 * updating the database, so we must ensure that ClientId is non-zero.
1058 if (!db_get_client_record(bjcr, db, cr)) {
1059 Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db_strerror(db));
1064 if (!db_create_client_record(bjcr, db, cr)) {
1065 Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
1069 Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
1074 static int create_fileset_record(BDB *db, FILESET_DBR *fsr)
1080 if (fsr->MD5[0] == 0) {
1081 fsr->MD5[0] = ' '; /* Equivalent to nothing */
1084 if (db_get_fileset_record(bjcr, db, fsr)) {
1086 Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1089 if (!db_create_fileset_record(bjcr, db, fsr)) {
1090 Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1091 fsr->FileSet, db_strerror(db));
1095 Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1102 * Simulate the two calls on the database to create
1103 * the Job record and to update it when the Job actually
1106 static JCR *create_job_record(BDB *db, JOB_DBR *jr, SESSION_LABEL *label,
1110 struct date_time dt;
1113 jr->JobId = label->JobId;
1114 jr->JobType = label->JobType;
1115 jr->JobLevel = label->JobLevel;
1116 jr->JobStatus = JS_Created;
1117 bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1118 bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1119 if (label->VerNum >= 11) {
1120 jr->SchedTime = btime_to_unix(label->write_btime);
1122 dt.julian_day_number = label->write_date;
1123 dt.julian_day_fraction = label->write_time;
1124 tm_decode(&dt, &tm);
1125 jr->SchedTime = mktime(&tm);
1128 jr->StartTime = jr->SchedTime;
1129 jr->JobTDate = (utime_t)jr->SchedTime;
1130 jr->VolSessionId = rec->VolSessionId;
1131 jr->VolSessionTime = rec->VolSessionTime;
1133 /* Now create a JCR as if starting the Job */
1134 mjcr = create_jcr(jr, rec, label->JobId);
1140 /* This creates the bare essentials */
1141 if (!db_create_job_record(bjcr, db, jr)) {
1142 Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1146 /* This adds the client, StartTime, JobTDate, ... */
1147 if (!db_update_job_start_record(bjcr, db, jr)) {
1148 Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1151 Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1153 mjcr->JobId = jr->JobId; /* set new JobId */
1158 * Simulate the database call that updates the Job
1159 * at Job termination time.
1161 static int update_job_record(BDB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1164 struct date_time dt;
1168 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1170 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1171 rec->VolSessionId, rec->VolSessionTime);
1174 if (elabel->VerNum >= 11) {
1175 jr->EndTime = btime_to_unix(elabel->write_btime);
1177 dt.julian_day_number = elabel->write_date;
1178 dt.julian_day_fraction = elabel->write_time;
1179 tm_decode(&dt, &tm);
1180 jr->EndTime = mktime(&tm);
1182 lasttime = jr->EndTime;
1183 mjcr->end_time = jr->EndTime;
1185 jr->JobId = mjcr->JobId;
1186 jr->JobStatus = elabel->JobStatus;
1187 mjcr->JobStatus = elabel->JobStatus;
1188 jr->JobFiles = elabel->JobFiles;
1189 if (jr->JobFiles > 0) { /* If we found files, force PurgedFiles */
1190 jr->PurgedFiles = 0;
1192 jr->JobBytes = elabel->JobBytes;
1193 jr->VolSessionId = rec->VolSessionId;
1194 jr->VolSessionTime = rec->VolSessionTime;
1195 jr->JobTDate = (utime_t)mjcr->start_time;
1196 jr->ClientId = mjcr->ClientId;
1203 if (!db_update_job_end_record(bjcr, db, jr)) {
1204 Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId, db_strerror(db));
1209 Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1210 jr->JobId, job_level_to_str(mjcr->getJobLevel()), jr->JobStatus);
1213 const char *term_msg;
1214 static char term_code[70];
1215 char sdt[50], edt[50];
1216 char ec1[30], ec2[30], ec3[30];
1218 switch (mjcr->JobStatus) {
1220 term_msg = _("Backup OK");
1223 term_msg = _("Backup OK -- with warnings");
1226 case JS_ErrorTerminated:
1227 term_msg = _("*** Backup Error ***");
1230 term_msg = _("Backup Canceled");
1233 term_msg = term_code;
1234 sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1237 bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1238 bstrftime(edt, sizeof(edt), mjcr->end_time);
1239 Pmsg14(000, _("%s\n"
1243 "Backup Level: %s\n"
1247 "Files Written: %s\n"
1248 "Bytes Written: %s\n"
1249 "Volume Session Id: %d\n"
1250 "Volume Session Time: %d\n"
1251 "Last Volume Bytes: %s\n"
1252 "Termination: %s\n\n"),
1257 job_level_to_str(mjcr->getJobLevel()),
1261 edit_uint64_with_commas(mjcr->JobFiles, ec1),
1262 edit_uint64_with_commas(mjcr->JobBytes, ec2),
1264 mjcr->VolSessionTime,
1265 edit_uint64_with_commas(mr.VolBytes, ec3),
1272 static int create_jobmedia_record(BDB *db, JCR *mjcr)
1275 DCR *dcr = mjcr->read_dcr;
1277 dcr->EndBlock = dev->EndBlock;
1278 dcr->EndFile = dev->EndFile;
1279 dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1281 memset(&jmr, 0, sizeof(jmr));
1282 jmr.JobId = mjcr->JobId;
1283 jmr.MediaId = mr.MediaId;
1284 jmr.FirstIndex = dcr->VolFirstIndex;
1285 jmr.LastIndex = dcr->VolLastIndex;
1286 jmr.StartFile = dcr->StartFile;
1287 jmr.EndFile = dcr->EndFile;
1288 jmr.StartBlock = dcr->StartBlock;
1289 jmr.EndBlock = dcr->EndBlock;
1296 if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1297 Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1301 Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1302 jmr.JobId, jmr.MediaId);
1308 * Simulate the database call that updates the MD5/SHA1 record
1310 static int update_digest_record(BDB *db, char *digest, DEV_RECORD *rec, int type)
1314 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1316 if (mr.VolJobs > 0) {
1317 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1318 rec->VolSessionId, rec->VolSessionTime);
1325 if (!update_db || mjcr->FileId == 0) {
1330 if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1331 Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1336 Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1344 * Create a JCR as if we are really starting the job
1346 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1350 * Transfer as much as possible to the Job JCR. Most important is
1351 * the JobId and the ClientId.
1353 jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1354 jobjcr->setJobType(jr->JobType);
1355 jobjcr->setJobLevel(jr->JobLevel);
1356 jobjcr->JobStatus = jr->JobStatus;
1357 bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1358 jobjcr->JobId = JobId; /* this is JobId on tape */
1359 jobjcr->sched_time = jr->SchedTime;
1360 jobjcr->start_time = jr->StartTime;
1361 jobjcr->VolSessionId = rec->VolSessionId;
1362 jobjcr->VolSessionTime = rec->VolSessionTime;
1363 jobjcr->ClientId = jr->ClientId;
1364 jobjcr->dcr = jobjcr->read_dcr = new_dcr(jobjcr, NULL, dev, SD_READ);
1369 /* Dummies to replace askdir.c */
1370 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1371 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
1372 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
1373 bool flush_jobmedia_queue(JCR *jcr) { return true; }
1374 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1375 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1376 bool dir_send_job_status(JCR *jcr) {return 1;}
1377 int generate_job_event(JCR *jcr, const char *event) { return 1; }
1379 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
1381 DEVICE *dev = dcr->dev;
1382 Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1383 /* Close device so user can use autochanger if desired */
1384 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1385 dcr->VolumeName, dev->print_name());
1391 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
1393 Dmsg0(100, "Fake dir_get_volume_info\n");
1394 dcr->setVolCatName(dcr->VolumeName);
1395 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);