]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
Backport from BEE
[bacula/bacula] / bacula / src / stored / bscan.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
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.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *  Program to scan a Bacula Volume and compare it with
19  *    the catalog and optionally synchronize the catalog
20  *    with the tape.
21  *
22  *   Kern E. Sibbald, December 2001
23  *
24  */
25
26 #include "bacula.h"
27 #include "stored.h"
28 #include "findlib/find.h"
29 #include "cats/cats.h"
30 #include "cats/sql_glue.h"
31
32 /* Dummy functions */
33 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
34 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
35
36 /* Forward referenced functions */
37 static void do_scan(void);
38 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
39 static int  create_file_attributes_record(B_DB *db, JCR *mjcr,
40                                char *fname, char *lname, int type,
41                                char *ap, DEV_RECORD *rec);
42 static int  create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
43 static bool update_media_record(B_DB *db, MEDIA_DBR *mr);
44 static int  create_pool_record(B_DB *db, POOL_DBR *pr);
45 static JCR *create_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
46 static int  update_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *elabel,
47                               DEV_RECORD *rec);
48 static int  create_client_record(B_DB *db, CLIENT_DBR *cr);
49 static int  create_fileset_record(B_DB *db, FILESET_DBR *fsr);
50 static int  create_jobmedia_record(B_DB *db, JCR *jcr);
51 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
52 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type);
53
54
55 /* Local variables */
56 static DEVICE *dev = NULL;
57 static B_DB *db;
58 static JCR *bjcr;                     /* jcr for bscan */
59 static BSR *bsr = NULL;
60 static MEDIA_DBR mr;
61 static POOL_DBR pr;
62 static JOB_DBR jr;
63 static CLIENT_DBR cr;
64 static FILESET_DBR fsr;
65 static ATTR_DBR ar;
66 static FILE_DBR fr;
67 static SESSION_LABEL label;
68 static SESSION_LABEL elabel;
69 static ATTR *attr;
70
71 static time_t lasttime = 0;
72
73 static const char *db_driver = "NULL";
74 static const char *db_name = "bacula";
75 static const char *db_user = "bacula";
76 static const char *db_password = "";
77 static const char *db_host = NULL;
78 static int db_port = 0;
79 static const char *wd = NULL;
80 static bool update_db = false;
81 static bool update_vol_info = false;
82 static bool list_records = false;
83 static int ignored_msgs = 0;
84
85 static uint64_t currentVolumeSize;
86 static int last_pct = -1;
87 static bool showProgress = false;
88 static int num_jobs = 0;
89 static int num_pools = 0;
90 static int num_media = 0;
91 static int num_files = 0;
92
93 static CONFIG *config;
94 #define CONFIG_FILE "bacula-sd.conf"
95
96 void *start_heap;
97 char *configfile = NULL;
98 STORES *me = NULL;                    /* our Global resource */
99 bool forge_on = false;                /* proceed inspite of I/O errors */
100 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
101 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
102
103
104 static void usage()
105 {
106    fprintf(stderr, _(
107 PROG_COPYRIGHT
108 "\nVersion: %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"
122 "       -r                list records\n"
123 "       -s                synchronize or store in database\n"
124 "       -S                show scan progress periodically\n"
125 "       -v                verbose\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);
130    exit(1);
131 }
132
133 int main (int argc, char *argv[])
134 {
135    int ch;
136    struct stat stat_buf;
137    char *VolumeName = NULL;
138
139    setlocale(LC_ALL, "");
140    bindtextdomain("bacula", LOCALEDIR);
141    textdomain("bacula");
142    init_stack_dump();
143    lmgr_init_thread();
144
145    my_name_is(argc, argv, "bscan");
146    init_msg(NULL, NULL);
147
148    OSDependentInit();
149
150    while ((ch = getopt(argc, argv, "b:c:d:D:h:p:mn:pP:rsSt:u:vV:w:?")) != -1) {
151       switch (ch) {
152       case 'S' :
153          showProgress = true;
154          break;
155       case 'b':
156          bsr = parse_bsr(NULL, optarg);
157          break;
158
159       case 'c':                    /* specify config file */
160          if (configfile != NULL) {
161             free(configfile);
162          }
163          configfile = bstrdup(optarg);
164          break;
165
166       case 'D':
167          db_driver = optarg;
168          break;
169
170       case 'd':                    /* debug level */
171          if (*optarg == 't') {
172             dbg_timestamp = true;
173          } else {
174             debug_level = atoi(optarg);
175             if (debug_level <= 0) {
176                debug_level = 1;
177             }
178          }
179          break;
180
181       case 'h':
182          db_host = optarg;
183          break;
184
185       case 't':
186          db_port = atoi(optarg);
187          break;
188
189       case 'm':
190          update_vol_info = true;
191          break;
192
193       case 'n':
194          db_name = optarg;
195          break;
196
197       case 'u':
198          db_user = optarg;
199          break;
200
201       case 'P':
202          db_password = optarg;
203          break;
204
205       case 'p':
206          forge_on = true;
207          break;
208
209       case 'r':
210          list_records = true;
211          break;
212
213       case 's':
214          update_db = true;
215          break;
216
217       case 'v':
218          verbose++;
219          break;
220
221       case 'V':                    /* Volume name */
222          VolumeName = optarg;
223          break;
224
225       case 'w':
226          wd = optarg;
227          break;
228
229       case '?':
230       default:
231          usage();
232
233       }
234    }
235    argc -= optind;
236    argv += optind;
237
238    if (argc != 1) {
239       Pmsg0(0, _("Wrong number of arguments: \n"));
240       usage();
241    }
242
243    if (configfile == NULL) {
244       configfile = bstrdup(CONFIG_FILE);
245    }
246
247    config = new_config_parser();
248    parse_sd_config(config, configfile, M_ERROR_TERM);
249    setup_me();
250    load_sd_plugins(me->plugin_directory);
251
252    /* Check if -w option given, otherwise use resource for working directory */
253    if (wd) {
254       working_directory = wd;
255    } else if (!me->working_directory) {
256       Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
257          configfile);
258    } else {
259       working_directory = me->working_directory;
260    }
261
262    /* Check that working directory is good */
263    if (stat(working_directory, &stat_buf) != 0) {
264       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
265          working_directory);
266    }
267    if (!S_ISDIR(stat_buf.st_mode)) {
268       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
269          working_directory);
270    }
271
272    bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, SD_READ);
273    if (!bjcr) {
274       exit(1);
275    }
276    dev = bjcr->read_dcr->dev;
277    if (showProgress) {
278       char ed1[50];
279       struct stat sb;
280       fstat(dev->fd(), &sb);
281       currentVolumeSize = sb.st_size;
282       Pmsg1(000, _("First Volume Size = %s\n"),
283          edit_uint64(currentVolumeSize, ed1));
284    }
285
286    if ((db = db_init_database(NULL, db_driver, db_name, db_user, db_password,
287                               db_host, db_port, NULL, false, false)) == NULL) {
288       Emsg0(M_ERROR_TERM, 0, _("Could not init Bacula database\n"));
289    }
290    if (!db_open_database(NULL, db)) {
291       Emsg0(M_ERROR_TERM, 0, db_strerror(db));
292    }
293    Dmsg0(200, "Database opened\n");
294    if (verbose) {
295       Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
296    }
297
298    do_scan();
299    if (update_db) {
300       printf("Records added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
301          num_media, num_pools, num_jobs, num_files);
302    } else {
303       printf("Records would have been added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
304          num_media, num_pools, num_jobs, num_files);
305    }
306
307    free_jcr(bjcr);
308    dev->term();
309    return 0;
310 }
311
312 /*
313  * We are at the end of reading a tape. Now, we simulate handling
314  *   the end of writing a tape by wiffling through the attached
315  *   jcrs creating jobmedia records.
316  */
317 static bool bscan_mount_next_read_volume(DCR *dcr)
318 {
319    DEVICE *dev = dcr->dev;
320    DCR *mdcr;
321    Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->getVolCatName());
322    foreach_dlist(mdcr, dev->attached_dcrs) {
323       JCR *mjcr = mdcr->jcr;
324       Dmsg1(100, "========== JobId=%u ========\n", mjcr->JobId);
325       if (mjcr->JobId == 0) {
326          continue;
327       }
328       if (verbose) {
329          Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
330       }
331       mdcr->StartBlock = dcr->StartBlock;
332       mdcr->StartFile = dcr->StartFile;
333       mdcr->EndBlock = dcr->EndBlock;
334       mdcr->EndFile = dcr->EndFile;
335       mdcr->VolMediaId = dcr->VolMediaId;
336       mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
337       if( mjcr->bscan_insert_jobmedia_records ) {
338          if (!create_jobmedia_record(db, mjcr)) {
339             Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
340                dev->getVolCatName(), mjcr->Job);
341          }
342       }
343    }
344
345    update_media_record(db, &mr);
346
347    /* Now let common read routine get up next tape. Note,
348     * we call mount_next... with bscan's jcr because that is where we
349     * have the Volume list, but we get attached.
350     */
351    bool stat = mount_next_read_volume(dcr);
352
353    if (showProgress) {
354       char ed1[50];
355       struct stat sb;
356       fstat(dev->fd(), &sb);
357       currentVolumeSize = sb.st_size;
358       Pmsg1(000, _("First Volume Size = %s\n"),
359          edit_uint64(currentVolumeSize, ed1));
360    }
361    return stat;
362 }
363
364 static void do_scan()
365 {
366    attr = new_attr(bjcr);
367
368    memset(&ar, 0, sizeof(ar));
369    memset(&pr, 0, sizeof(pr));
370    memset(&jr, 0, sizeof(jr));
371    memset(&cr, 0, sizeof(cr));
372    memset(&fsr, 0, sizeof(fsr));
373    memset(&fr, 0, sizeof(fr));
374
375    /* Detach bscan's jcr as we are not a real Job on the tape */
376
377    read_records(bjcr->read_dcr, record_cb, bscan_mount_next_read_volume);
378
379    if (update_db) {
380       db_write_batch_file_records(bjcr); /* used by bulk batch file insert */
381    }
382    free_attr(attr);
383 }
384
385 /*
386  * Returns: true  if OK
387  *          false if error
388  */
389 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
390 {
391    JCR *mjcr;
392    char ec1[30];
393    DEVICE *dev = dcr->dev;
394    JCR *bjcr = dcr->jcr;
395    DEV_BLOCK *block = dcr->block;
396    POOL_MEM sql_buffer;
397    db_int64_ctx jmr_count;
398
399    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
400
401    if (rec->data_len > 0) {
402       mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
403       if (showProgress && currentVolumeSize > 0) {
404          int pct = (mr.VolBytes * 100) / currentVolumeSize;
405          if (pct != last_pct) {
406             fprintf(stdout, _("done: %d%%\n"), pct);
407             fflush(stdout);
408             last_pct = pct;
409          }
410       }
411    }
412
413    if (list_records) {
414       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
415             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
416             rec->Stream, rec->data_len);
417    }
418    /*
419     * Check for Start or End of Session Record
420     *
421     */
422    if (rec->FileIndex < 0) {
423       bool save_update_db = update_db;
424
425       if (verbose > 1) {
426          dump_label_record(dev, rec, 1);
427       }
428       switch (rec->FileIndex) {
429       case PRE_LABEL:
430          Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
431          return false;
432          break;
433
434       case VOL_LABEL:
435          unser_volume_label(dev, rec);
436          /* Check Pool info */
437          bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
438          bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
439          num_pools++;
440          if (db_get_pool_numvols(bjcr, db, &pr)) {
441             if (verbose) {
442                Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
443             }
444          } else {
445             if (!update_db) {
446                Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
447                   pr.Name);
448             }
449             create_pool_record(db, &pr);
450          }
451          if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
452             Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
453                pr.PoolType, dev->VolHdr.PoolType);
454             return true;
455          } else if (verbose) {
456             Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
457          }
458
459          /* Check Media Info */
460          memset(&mr, 0, sizeof(mr));
461          bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
462          mr.PoolId = pr.PoolId;
463          num_media++;
464          if (db_get_media_record(bjcr, db, &mr)) {
465             if (verbose) {
466                Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
467             }
468             /* Clear out some volume statistics that will be updated */
469             mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
470             mr.VolBytes = rec->data_len + 20;
471          } else {
472             if (!update_db) {
473                Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
474                   mr.VolumeName);
475             }
476             bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
477             create_media_record(db, &mr, &dev->VolHdr);
478          }
479          if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
480             Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
481                mr.MediaType, dev->VolHdr.MediaType);
482             return true;              /* ignore error */
483          } else if (verbose) {
484             Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
485          }
486          /* Reset some DCR variables */
487          foreach_dlist(dcr, dev->attached_dcrs) {
488             dcr->VolFirstIndex = dcr->FileIndex = 0;
489             dcr->StartBlock = dcr->EndBlock = 0;
490             dcr->StartFile = dcr->EndFile = 0;
491             dcr->VolMediaId = 0;
492          }
493
494          Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
495          break;
496
497       case SOS_LABEL:
498          mr.VolJobs++;
499          num_jobs++;
500          if (ignored_msgs > 0) {
501             Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
502                   ignored_msgs);
503             ignored_msgs = 0;
504          }
505          unser_session_label(&label, rec);
506          memset(&jr, 0, sizeof(jr));
507          bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
508          if (db_get_job_record(bjcr, db, &jr)) {
509             /* Job record already exists in DB */
510             update_db = false;  /* don't change db in create_job_record */
511             if (verbose) {
512                Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
513             }
514          } else {
515             /* Must create a Job record in DB */
516             if (!update_db) {
517                Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
518                   jr.JobId);
519             }
520          }
521
522          /* Create Client record if not already there */
523          bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
524          create_client_record(db, &cr);
525          jr.ClientId = cr.ClientId;
526
527          /* process label, if Job record exists don't update db */
528          mjcr = create_job_record(db, &jr, &label, rec);
529          dcr = mjcr->read_dcr;
530          update_db = save_update_db;
531
532          jr.PoolId = pr.PoolId;
533          mjcr->start_time = jr.StartTime;
534          mjcr->setJobLevel(jr.JobLevel);
535
536          mjcr->client_name = get_pool_memory(PM_FNAME);
537          pm_strcpy(mjcr->client_name, label.ClientName);
538          mjcr->fileset_name = get_pool_memory(PM_FNAME);
539          pm_strcpy(mjcr->fileset_name, label.FileSetName);
540          bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
541          bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
542
543          /* Look for existing Job Media records for this job.  If there are
544             any, no new ones need be created.  This may occur if File
545             Retention has expired before Job Retention, or if the volume
546             has already been bscan'd */
547          Mmsg(sql_buffer, "SELECT count(*) from JobMedia where JobId=%d", jr.JobId);
548          db_sql_query(db, sql_buffer.c_str(), db_int64_handler, &jmr_count);
549          if( jmr_count.value > 0 ) {
550             //FIELD NAME TO BE DEFINED/CONFIRMED (maybe a struct?)
551             mjcr->bscan_insert_jobmedia_records = false;
552          } else {
553             mjcr->bscan_insert_jobmedia_records = true;
554          }
555
556          if (rec->VolSessionId != jr.VolSessionId) {
557             Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
558                jr.JobId,
559                jr.VolSessionId, rec->VolSessionId);
560             return true;              /* ignore error */
561          }
562          if (rec->VolSessionTime != jr.VolSessionTime) {
563             Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
564                jr.JobId,
565                jr.VolSessionTime, rec->VolSessionTime);
566             return true;              /* ignore error */
567          }
568          if (jr.PoolId != pr.PoolId) {
569             Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
570                jr.JobId,
571                jr.PoolId, pr.PoolId);
572             return true;              /* ignore error */
573          }
574          break;
575
576       case EOS_LABEL:
577          unser_session_label(&elabel, rec);
578
579          /* Create FileSet record */
580          bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
581          bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
582          create_fileset_record(db, &fsr);
583          jr.FileSetId = fsr.FileSetId;
584
585          mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
586          if (!mjcr) {
587             Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
588                   rec->VolSessionId, rec->VolSessionTime);
589             break;
590          }
591
592          /* Do the final update to the Job record */
593          update_job_record(db, &jr, &elabel, rec);
594
595          mjcr->end_time = jr.EndTime;
596          mjcr->JobStatus = JS_Terminated;
597
598          /* Create JobMedia record */
599          mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
600          if( mjcr->bscan_insert_jobmedia_records ) {
601             create_jobmedia_record(db, mjcr);
602          }
603          free_dcr(mjcr->read_dcr);
604          free_jcr(mjcr);
605
606          break;
607
608       case EOM_LABEL:
609          break;
610
611       case EOT_LABEL:              /* end of all tapes */
612          /*
613           * Wiffle through all jobs still open and close
614           *   them.
615           */
616          if (update_db) {
617             DCR *mdcr;
618             foreach_dlist(mdcr, dev->attached_dcrs) {
619                JCR *mjcr = mdcr->jcr;
620                if (!mjcr || mjcr->JobId == 0) {
621                   continue;
622                }
623                jr.JobId = mjcr->JobId;
624                /* Mark Job as Error Terimined */
625                jr.JobStatus = JS_ErrorTerminated;
626                jr.JobFiles = mjcr->JobFiles;
627                jr.JobBytes = mjcr->JobBytes;
628                jr.VolSessionId = mjcr->VolSessionId;
629                jr.VolSessionTime = mjcr->VolSessionTime;
630                jr.JobTDate = (utime_t)mjcr->start_time;
631                jr.ClientId = mjcr->ClientId;
632                if (!db_update_job_end_record(bjcr, db, &jr)) {
633                   Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
634                }
635                mjcr->read_dcr = NULL;
636                free_jcr(mjcr);
637             }
638          }
639          mr.VolFiles = rec->File;
640          mr.VolBlocks = rec->Block;
641          mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
642          mr.VolMounts++;
643          update_media_record(db, &mr);
644          Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
645                     mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
646          break;
647       default:
648          break;
649       } /* end switch */
650       return true;
651    }
652
653    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
654    if (!mjcr) {
655       if (mr.VolJobs > 0) {
656          Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
657                       rec->VolSessionId, rec->VolSessionTime);
658       } else {
659          ignored_msgs++;
660       }
661       return true;
662    }
663    dcr = mjcr->read_dcr;
664    if (dcr->VolFirstIndex == 0) {
665       dcr->VolFirstIndex = block->FirstIndex;
666    }
667
668    /* File Attributes stream */
669    switch (rec->maskedStream) {
670    case STREAM_UNIX_ATTRIBUTES:
671    case STREAM_UNIX_ATTRIBUTES_EX:
672
673       if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, rec->data_len, attr)) {
674          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
675       }
676
677       if (verbose > 1) {
678          decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
679          build_attr_output_fnames(bjcr, attr);
680          print_ls_output(bjcr, attr);
681       }
682       fr.JobId = mjcr->JobId;
683       fr.FileId = 0;
684       num_files++;
685       if (verbose && (num_files & 0x7FFF) == 0) {
686          char ed1[30], ed2[30], ed3[30], ed4[30];
687          Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
688                      edit_uint64_with_commas(num_files, ed1),
689                      edit_uint64_with_commas(rec->File, ed2),
690                      edit_uint64_with_commas(rec->Block, ed3),
691                      edit_uint64_with_commas(mr.VolBytes, ed4));
692       }
693       create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
694             attr->type, attr->attr, rec);
695       free_jcr(mjcr);
696       break;
697
698    case STREAM_RESTORE_OBJECT:
699    /* ****FIXME*****/
700       /* Implement putting into catalog */
701       break;
702
703    /* Data stream */
704    case STREAM_WIN32_DATA:
705    case STREAM_FILE_DATA:
706    case STREAM_SPARSE_DATA:
707    case STREAM_MACOS_FORK_DATA:
708    case STREAM_ENCRYPTED_FILE_DATA:
709    case STREAM_ENCRYPTED_WIN32_DATA:
710    case STREAM_ENCRYPTED_MACOS_FORK_DATA:
711       /*
712        * For encrypted stream, this is an approximation.
713        * The data must be decrypted to know the correct length.
714        */
715       mjcr->JobBytes += rec->data_len;
716       if (rec->maskedStream == STREAM_SPARSE_DATA) {
717          mjcr->JobBytes -= sizeof(uint64_t);
718       }
719
720       free_jcr(mjcr);                 /* done using JCR */
721       break;
722
723    case STREAM_GZIP_DATA:
724    case STREAM_COMPRESSED_DATA:
725    case STREAM_ENCRYPTED_FILE_GZIP_DATA:
726    case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
727    case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
728    case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
729       /* No correct, we should (decrypt and) expand it
730          done using JCR
731       */
732       mjcr->JobBytes += rec->data_len;
733       free_jcr(mjcr);
734       break;
735
736    case STREAM_SPARSE_GZIP_DATA:
737    case STREAM_SPARSE_COMPRESSED_DATA:
738       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
739       free_jcr(mjcr);                 /* done using JCR */
740       break;
741
742    /* Win32 GZIP stream */
743    case STREAM_WIN32_GZIP_DATA:
744    case STREAM_WIN32_COMPRESSED_DATA:
745       mjcr->JobBytes += rec->data_len;
746       free_jcr(mjcr);                 /* done using JCR */
747       break;
748
749    case STREAM_MD5_DIGEST:
750       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE, true);
751       if (verbose > 1) {
752          Pmsg1(000, _("Got MD5 record: %s\n"), digest);
753       }
754       update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
755       break;
756
757    case STREAM_SHA1_DIGEST:
758       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE, true);
759       if (verbose > 1) {
760          Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
761       }
762       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
763       break;
764
765    case STREAM_SHA256_DIGEST:
766       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE, true);
767       if (verbose > 1) {
768          Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
769       }
770       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
771       break;
772
773    case STREAM_SHA512_DIGEST:
774       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE, true);
775       if (verbose > 1) {
776          Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
777       }
778       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
779       break;
780
781    case STREAM_ENCRYPTED_SESSION_DATA:
782       // TODO landonf: Investigate crypto support in bscan
783       if (verbose > 1) {
784          Pmsg0(000, _("Got signed digest record\n"));
785       }
786       break;
787
788    case STREAM_SIGNED_DIGEST:
789       // TODO landonf: Investigate crypto support in bscan
790       if (verbose > 1) {
791          Pmsg0(000, _("Got signed digest record\n"));
792       }
793       break;
794
795    case STREAM_PROGRAM_NAMES:
796       if (verbose) {
797          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
798       }
799       break;
800
801    case STREAM_PROGRAM_DATA:
802       if (verbose > 1) {
803          Pmsg0(000, _("Got Prog Data Stream record.\n"));
804       }
805       break;
806
807    case STREAM_UNIX_ACCESS_ACL:          /* Deprecated Standard ACL attributes on UNIX */
808    case STREAM_UNIX_DEFAULT_ACL:         /* Deprecated Default ACL attributes on UNIX */
809    case STREAM_HFSPLUS_ATTRIBUTES:
810    case STREAM_ACL_AIX_TEXT:
811    case STREAM_ACL_DARWIN_ACCESS_ACL:
812    case STREAM_ACL_FREEBSD_DEFAULT_ACL:
813    case STREAM_ACL_FREEBSD_ACCESS_ACL:
814    case STREAM_ACL_HPUX_ACL_ENTRY:
815    case STREAM_ACL_IRIX_DEFAULT_ACL:
816    case STREAM_ACL_IRIX_ACCESS_ACL:
817    case STREAM_ACL_LINUX_DEFAULT_ACL:
818    case STREAM_ACL_LINUX_ACCESS_ACL:
819    case STREAM_ACL_TRU64_DEFAULT_ACL:
820    case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
821    case STREAM_ACL_TRU64_ACCESS_ACL:
822    case STREAM_ACL_SOLARIS_ACLENT:
823    case STREAM_ACL_SOLARIS_ACE:
824    case STREAM_ACL_AFS_TEXT:
825    case STREAM_ACL_AIX_AIXC:
826    case STREAM_ACL_AIX_NFS4:
827    case STREAM_ACL_FREEBSD_NFS4_ACL:
828    case STREAM_ACL_HURD_DEFAULT_ACL:
829    case STREAM_ACL_HURD_ACCESS_ACL:
830       /* Ignore Unix ACL attributes */
831       break;
832
833    case STREAM_XATTR_HURD:
834    case STREAM_XATTR_IRIX:
835    case STREAM_XATTR_TRU64:
836    case STREAM_XATTR_AIX:
837    case STREAM_XATTR_OPENBSD:
838    case STREAM_XATTR_SOLARIS_SYS:
839    case STREAM_XATTR_SOLARIS:
840    case STREAM_XATTR_DARWIN:
841    case STREAM_XATTR_FREEBSD:
842    case STREAM_XATTR_LINUX:
843    case STREAM_XATTR_NETBSD:
844       /* Ignore Unix Extended attributes */
845       break;
846
847    default:
848       Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream, rec->data_len);
849       break;
850    }
851    return true;
852 }
853
854 /*
855  * Free the Job Control Record if no one is still using it.
856  *  Called from main free_jcr() routine in src/lib/jcr.c so
857  *  that we can do our Director specific cleanup of the jcr.
858  */
859 static void bscan_free_jcr(JCR *jcr)
860 {
861    Dmsg0(200, "Start bscan free_jcr\n");
862
863    free_bsock(jcr->file_bsock);
864    free_bsock(jcr->store_bsock);
865    if (jcr->RestoreBootstrap) {
866       free(jcr->RestoreBootstrap);
867    }
868    if (jcr->dcr) {
869       free_dcr(jcr->dcr);
870       jcr->dcr = NULL;
871    }
872    if (jcr->read_dcr) {
873       free_dcr(jcr->read_dcr);
874       jcr->read_dcr = NULL;
875    }
876    Dmsg0(200, "End bscan free_jcr\n");
877 }
878
879 /*
880  * We got a File Attributes record on the tape.  Now, lookup the Job
881  *   record, and then create the attributes record.
882  */
883 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
884                                char *fname, char *lname, int type,
885                                char *ap, DEV_RECORD *rec)
886 {
887    DCR *dcr = mjcr->read_dcr;
888    ar.fname = fname;
889    ar.link = lname;
890    ar.ClientId = mjcr->ClientId;
891    ar.JobId = mjcr->JobId;
892    ar.Stream = rec->Stream;
893    if (type == FT_DELETED) {
894       ar.FileIndex = 0;
895    } else {
896       ar.FileIndex = rec->FileIndex;
897    }
898    ar.attr = ap;
899    if (dcr->VolFirstIndex == 0) {
900       dcr->VolFirstIndex = rec->FileIndex;
901    }
902    dcr->FileIndex = rec->FileIndex;
903    mjcr->JobFiles++;
904
905    if (!update_db) {
906       return 1;
907    }
908
909    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
910       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
911       return 0;
912    }
913    mjcr->FileId = ar.FileId;
914
915    if (verbose > 1) {
916       Pmsg1(000, _("Created File record: %s\n"), fname);
917    }
918    return 1;
919 }
920
921 /*
922  * For each Volume we see, we create a Medium record
923  */
924 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
925 {
926    struct date_time dt;
927    struct tm tm;
928
929    /* We mark Vols as Archive to keep them from being re-written */
930    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
931    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
932    mr->Enabled = 1;
933    if (vl->VerNum >= 11) {
934       mr->set_first_written = true; /* Save FirstWritten during update_media */
935       mr->FirstWritten = btime_to_utime(vl->write_btime);
936       mr->LabelDate    = btime_to_utime(vl->label_btime);
937    } else {
938       /* DEPRECATED DO NOT USE */
939       dt.julian_day_number = vl->write_date;
940       dt.julian_day_fraction = vl->write_time;
941       tm_decode(&dt, &tm);
942       mr->FirstWritten = mktime(&tm);
943       dt.julian_day_number = vl->label_date;
944       dt.julian_day_fraction = vl->label_time;
945       tm_decode(&dt, &tm);
946       mr->LabelDate = mktime(&tm);
947    }
948    lasttime = mr->LabelDate;
949    if (mr->VolJobs == 0) {
950       mr->VolJobs = 1;
951    }
952    if (mr->VolMounts == 0) {
953       mr->VolMounts = 1;
954    }
955
956    if (!update_db) {
957       return 1;
958    }
959
960    if (!db_create_media_record(bjcr, db, mr)) {
961       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
962       return 0;
963    }
964    if (!db_update_media_record(bjcr, db, mr)) {
965       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
966       return 0;
967    }
968    if (verbose) {
969       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
970    }
971    return 1;
972
973 }
974
975 /*
976  * Called at end of media to update it
977  */
978 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
979 {
980    if (!update_db && !update_vol_info) {
981       return true;
982    }
983
984    mr->LastWritten = lasttime;
985    if (!db_update_media_record(bjcr, db, mr)) {
986       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
987       return false;;
988    }
989    if (verbose) {
990       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
991    }
992    return true;
993
994 }
995
996
997 static int create_pool_record(B_DB *db, POOL_DBR *pr)
998 {
999    pr->NumVols++;
1000    pr->UseCatalog = 1;
1001    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
1002
1003    if (!update_db) {
1004       return 1;
1005    }
1006    if (!db_create_pool_record(bjcr, db, pr)) {
1007       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
1008       return 0;
1009    }
1010    if (verbose) {
1011       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
1012    }
1013    return 1;
1014
1015 }
1016
1017
1018 /*
1019  * Called from SOS to create a client for the current Job
1020  */
1021 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
1022 {
1023    /*
1024     * Note, update_db can temporarily be set false while
1025     * updating the database, so we must ensure that ClientId is non-zero.
1026     */
1027    if (!update_db) {
1028       cr->ClientId = 0;
1029       if (!db_get_client_record(bjcr, db, cr)) {
1030         Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db_strerror(db));
1031         return 0;
1032       }
1033       return 1;
1034    }
1035    if (!db_create_client_record(bjcr, db, cr)) {
1036       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
1037       return 0;
1038    }
1039    if (verbose) {
1040       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
1041    }
1042    return 1;
1043 }
1044
1045 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
1046 {
1047    if (!update_db) {
1048       return 1;
1049    }
1050    fsr->FileSetId = 0;
1051    if (fsr->MD5[0] == 0) {
1052       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
1053       fsr->MD5[1] = 0;
1054    }
1055    if (db_get_fileset_record(bjcr, db, fsr)) {
1056       if (verbose) {
1057          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1058       }
1059    } else {
1060       if (!db_create_fileset_record(bjcr, db, fsr)) {
1061          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1062             fsr->FileSet, db_strerror(db));
1063          return 0;
1064       }
1065       if (verbose) {
1066          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1067       }
1068    }
1069    return 1;
1070 }
1071
1072 /*
1073  * Simulate the two calls on the database to create
1074  *  the Job record and to update it when the Job actually
1075  *  begins running.
1076  */
1077 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
1078                              DEV_RECORD *rec)
1079 {
1080    JCR *mjcr;
1081    struct date_time dt;
1082    struct tm tm;
1083
1084    jr->JobId = label->JobId;
1085    jr->JobType = label->JobType;
1086    jr->JobLevel = label->JobLevel;
1087    jr->JobStatus = JS_Created;
1088    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1089    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1090    if (label->VerNum >= 11) {
1091       jr->SchedTime = btime_to_unix(label->write_btime);
1092    } else {
1093       dt.julian_day_number = label->write_date;
1094       dt.julian_day_fraction = label->write_time;
1095       tm_decode(&dt, &tm);
1096       jr->SchedTime = mktime(&tm);
1097    }
1098
1099    jr->StartTime = jr->SchedTime;
1100    jr->JobTDate = (utime_t)jr->SchedTime;
1101    jr->VolSessionId = rec->VolSessionId;
1102    jr->VolSessionTime = rec->VolSessionTime;
1103
1104    /* Now create a JCR as if starting the Job */
1105    mjcr = create_jcr(jr, rec, label->JobId);
1106
1107    if (!update_db) {
1108       return mjcr;
1109    }
1110
1111    /* This creates the bare essentials */
1112    if (!db_create_job_record(bjcr, db, jr)) {
1113       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1114       return mjcr;
1115    }
1116
1117    /* This adds the client, StartTime, JobTDate, ... */
1118    if (!db_update_job_start_record(bjcr, db, jr)) {
1119       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1120       return mjcr;
1121    }
1122    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1123          label->JobId);
1124    mjcr->JobId = jr->JobId;           /* set new JobId */
1125    return mjcr;
1126 }
1127
1128 /*
1129  * Simulate the database call that updates the Job
1130  *  at Job termination time.
1131  */
1132 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1133                               DEV_RECORD *rec)
1134 {
1135    struct date_time dt;
1136    struct tm tm;
1137    JCR *mjcr;
1138
1139    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1140    if (!mjcr) {
1141       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1142                    rec->VolSessionId, rec->VolSessionTime);
1143       return 0;
1144    }
1145    if (elabel->VerNum >= 11) {
1146       jr->EndTime = btime_to_unix(elabel->write_btime);
1147    } else {
1148       dt.julian_day_number = elabel->write_date;
1149       dt.julian_day_fraction = elabel->write_time;
1150       tm_decode(&dt, &tm);
1151       jr->EndTime = mktime(&tm);
1152    }
1153    lasttime = jr->EndTime;
1154    mjcr->end_time = jr->EndTime;
1155
1156    jr->JobId = mjcr->JobId;
1157    jr->JobStatus = elabel->JobStatus;
1158    mjcr->JobStatus = elabel->JobStatus;
1159    jr->JobFiles = elabel->JobFiles;
1160    if (jr->JobFiles > 0) {  /* If we found files, force PurgedFiles */
1161       jr->PurgedFiles = 0;
1162    }
1163    jr->JobBytes = elabel->JobBytes;
1164    jr->VolSessionId = rec->VolSessionId;
1165    jr->VolSessionTime = rec->VolSessionTime;
1166    jr->JobTDate = (utime_t)mjcr->start_time;
1167    jr->ClientId = mjcr->ClientId;
1168
1169    if (!update_db) {
1170       free_jcr(mjcr);
1171       return 1;
1172    }
1173
1174    if (!db_update_job_end_record(bjcr, db, jr)) {
1175       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1176       free_jcr(mjcr);
1177       return 0;
1178    }
1179    if (verbose) {
1180       Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1181          jr->JobId, job_level_to_str(mjcr->getJobLevel()), jr->JobStatus);
1182    }
1183    if (verbose > 1) {
1184       const char *term_msg;
1185       static char term_code[70];
1186       char sdt[50], edt[50];
1187       char ec1[30], ec2[30], ec3[30];
1188
1189       switch (mjcr->JobStatus) {
1190       case JS_Terminated:
1191          term_msg = _("Backup OK");
1192          break;
1193       case JS_Warnings:
1194          term_msg = _("Backup OK -- with warnings");
1195          break;
1196       case JS_FatalError:
1197       case JS_ErrorTerminated:
1198          term_msg = _("*** Backup Error ***");
1199          break;
1200       case JS_Canceled:
1201          term_msg = _("Backup Canceled");
1202          break;
1203       default:
1204          term_msg = term_code;
1205          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1206          break;
1207       }
1208       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1209       bstrftime(edt, sizeof(edt), mjcr->end_time);
1210       Pmsg14(000,  _("%s\n"
1211 "JobId:                  %d\n"
1212 "Job:                    %s\n"
1213 "FileSet:                %s\n"
1214 "Backup Level:           %s\n"
1215 "Client:                 %s\n"
1216 "Start time:             %s\n"
1217 "End time:               %s\n"
1218 "Files Written:          %s\n"
1219 "Bytes Written:          %s\n"
1220 "Volume Session Id:      %d\n"
1221 "Volume Session Time:    %d\n"
1222 "Last Volume Bytes:      %s\n"
1223 "Termination:            %s\n\n"),
1224         edt,
1225         mjcr->JobId,
1226         mjcr->Job,
1227         mjcr->fileset_name,
1228         job_level_to_str(mjcr->getJobLevel()),
1229         mjcr->client_name,
1230         sdt,
1231         edt,
1232         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1233         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1234         mjcr->VolSessionId,
1235         mjcr->VolSessionTime,
1236         edit_uint64_with_commas(mr.VolBytes, ec3),
1237         term_msg);
1238    }
1239    free_jcr(mjcr);
1240    return 1;
1241 }
1242
1243 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1244 {
1245    JOBMEDIA_DBR jmr;
1246    DCR *dcr = mjcr->read_dcr;
1247
1248    dcr->EndBlock = dev->EndBlock;
1249    dcr->EndFile  = dev->EndFile;
1250    dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1251
1252    memset(&jmr, 0, sizeof(jmr));
1253    jmr.JobId = mjcr->JobId;
1254    jmr.MediaId = mr.MediaId;
1255    jmr.FirstIndex = dcr->VolFirstIndex;
1256    jmr.LastIndex = dcr->VolLastIndex;
1257    jmr.StartFile = dcr->StartFile;
1258    jmr.EndFile = dcr->EndFile;
1259    jmr.StartBlock = dcr->StartBlock;
1260    jmr.EndBlock = dcr->EndBlock;
1261
1262
1263    if (!update_db) {
1264       return 1;
1265    }
1266
1267    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1268       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1269       return 0;
1270    }
1271    if (verbose) {
1272       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1273                 jmr.JobId, jmr.MediaId);
1274    }
1275    return 1;
1276 }
1277
1278 /*
1279  * Simulate the database call that updates the MD5/SHA1 record
1280  */
1281 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1282 {
1283    JCR *mjcr;
1284
1285    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1286    if (!mjcr) {
1287       if (mr.VolJobs > 0) {
1288          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1289                       rec->VolSessionId, rec->VolSessionTime);
1290       } else {
1291          ignored_msgs++;
1292       }
1293       return 0;
1294    }
1295
1296    if (!update_db || mjcr->FileId == 0) {
1297       free_jcr(mjcr);
1298       return 1;
1299    }
1300
1301    if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1302       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1303       free_jcr(mjcr);
1304       return 0;
1305    }
1306    if (verbose > 1) {
1307       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1308    }
1309    free_jcr(mjcr);
1310    return 1;
1311 }
1312
1313
1314 /*
1315  * Create a JCR as if we are really starting the job
1316  */
1317 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1318 {
1319    JCR *jobjcr;
1320    /*
1321     * Transfer as much as possible to the Job JCR. Most important is
1322     *   the JobId and the ClientId.
1323     */
1324    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1325    jobjcr->setJobType(jr->JobType);
1326    jobjcr->setJobLevel(jr->JobLevel);
1327    jobjcr->JobStatus = jr->JobStatus;
1328    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1329    jobjcr->JobId = JobId;      /* this is JobId on tape */
1330    jobjcr->sched_time = jr->SchedTime;
1331    jobjcr->start_time = jr->StartTime;
1332    jobjcr->VolSessionId = rec->VolSessionId;
1333    jobjcr->VolSessionTime = rec->VolSessionTime;
1334    jobjcr->ClientId = jr->ClientId;
1335    jobjcr->dcr = jobjcr->read_dcr = new_dcr(jobjcr, NULL, dev, SD_READ);
1336
1337    return jobjcr;
1338 }
1339
1340 /* Dummies to replace askdir.c */
1341 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1342 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
1343 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
1344 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1345 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1346 bool    dir_send_job_status(JCR *jcr) {return 1;}
1347 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1348
1349 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
1350 {
1351    DEVICE *dev = dcr->dev;
1352    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1353    /* Close device so user can use autochanger if desired */
1354    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1355          dcr->VolumeName, dev->print_name());
1356    dev->close();
1357    getchar();
1358    return true;
1359 }
1360
1361 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
1362 {
1363    Dmsg0(100, "Fake dir_get_volume_info\n");
1364    dcr->setVolCatName(dcr->VolumeName);
1365 #ifdef BUILD_DVD
1366    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
1367 #endif
1368    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);
1369    return 1;
1370 }