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