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