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