]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
55c1fe13f3f068aea0f9c95ee9776ba382c16997
[bacula/bacula] / bacula / src / stored / bscan.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2008 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 (attr->file_index != rec->FileIndex) {
672          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
673             rec->FileIndex, attr->file_index);
674       }
675
676       if (verbose > 1) {
677          decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
678          build_attr_output_fnames(bjcr, attr);
679          print_ls_output(bjcr, attr);
680       }
681       fr.JobId = mjcr->JobId;
682       fr.FileId = 0;
683       num_files++;
684       if (verbose && (num_files & 0x7FFF) == 0) {
685          char ed1[30], ed2[30], ed3[30], ed4[30];
686          Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
687                      edit_uint64_with_commas(num_files, ed1),
688                      edit_uint64_with_commas(rec->File, ed2),
689                      edit_uint64_with_commas(rec->Block, ed3),
690                      edit_uint64_with_commas(mr.VolBytes, ed4));
691       }
692       create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
693             attr->type, attr->attr, rec);
694       free_jcr(mjcr);
695       break;
696
697    /* Data stream */
698    case STREAM_WIN32_DATA:
699    case STREAM_FILE_DATA:
700    case STREAM_SPARSE_DATA:
701    case STREAM_ENCRYPTED_FILE_DATA:
702    case STREAM_ENCRYPTED_WIN32_DATA:
703    case STREAM_ENCRYPTED_MACOS_FORK_DATA:
704       /*
705        * For encrypted stream, this is an approximation.
706        * The data must be decrypted to know the correct length.
707        */
708       mjcr->JobBytes += rec->data_len;
709       if (rec->Stream == STREAM_SPARSE_DATA) {
710          mjcr->JobBytes -= sizeof(uint64_t);
711       }
712
713       free_jcr(mjcr);                 /* done using JCR */
714       break;
715
716    case STREAM_GZIP_DATA:
717    case STREAM_ENCRYPTED_FILE_GZIP_DATA:
718    case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
719       /* No correct, we should (decrypt and) expand it 
720          done using JCR 
721       */
722       mjcr->JobBytes += rec->data_len;
723       free_jcr(mjcr);                 
724       break;
725
726    case STREAM_SPARSE_GZIP_DATA:
727       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
728       free_jcr(mjcr);                 /* done using JCR */
729       break;
730
731    /* Win32 GZIP stream */
732    case STREAM_WIN32_GZIP_DATA:
733       mjcr->JobBytes += rec->data_len;
734       free_jcr(mjcr);                 /* done using JCR */
735       break;
736
737    case STREAM_MD5_DIGEST:
738       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE, true);
739       if (verbose > 1) {
740          Pmsg1(000, _("Got MD5 record: %s\n"), digest);
741       }
742       update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
743       break;
744
745    case STREAM_SHA1_DIGEST:
746       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE, true);
747       if (verbose > 1) {
748          Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
749       }
750       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
751       break;
752
753    case STREAM_SHA256_DIGEST:
754       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE, true);
755       if (verbose > 1) {
756          Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
757       }
758       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
759       break;
760
761    case STREAM_SHA512_DIGEST:
762       bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE, true);
763       if (verbose > 1) {
764          Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
765       }
766       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
767       break;
768
769    case STREAM_ENCRYPTED_SESSION_DATA:
770       // TODO landonf: Investigate crypto support in bscan
771       if (verbose > 1) {
772          Pmsg0(000, _("Got signed digest record\n"));
773       }
774       break;
775
776    case STREAM_SIGNED_DIGEST:
777       // TODO landonf: Investigate crypto support in bscan
778       if (verbose > 1) {
779          Pmsg0(000, _("Got signed digest record\n"));
780       }
781       break;
782
783    case STREAM_PROGRAM_NAMES:
784       if (verbose) {
785          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
786       }
787       break;
788
789    case STREAM_PROGRAM_DATA:
790       if (verbose > 1) {
791          Pmsg0(000, _("Got Prog Data Stream record.\n"));
792       }
793       break;
794
795    case STREAM_UNIX_ACCESS_ACL:          /* Deprecated Standard ACL attributes on UNIX */
796    case STREAM_UNIX_DEFAULT_ACL:         /* Deprecated Default ACL attributes on UNIX */
797    case STREAM_ACL_AIX_TEXT:
798    case STREAM_ACL_DARWIN_ACCESS_ACL:
799    case STREAM_ACL_FREEBSD_DEFAULT_ACL:
800    case STREAM_ACL_FREEBSD_ACCESS_ACL:
801    case STREAM_ACL_HPUX_ACL_ENTRY:
802    case STREAM_ACL_IRIX_DEFAULT_ACL:
803    case STREAM_ACL_IRIX_ACCESS_ACL:
804    case STREAM_ACL_LINUX_DEFAULT_ACL:
805    case STREAM_ACL_LINUX_ACCESS_ACL:
806    case STREAM_ACL_TRU64_DEFAULT_ACL:
807    case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
808    case STREAM_ACL_TRU64_ACCESS_ACL:
809    case STREAM_ACL_SOLARIS_ACLENT:
810    case STREAM_ACL_SOLARIS_ACE:
811       /* Ignore Unix ACL attributes */
812       break;
813
814    case STREAM_XATTR_OPENBSD:
815    case STREAM_XATTR_SOLARIS_SYS:
816    case STREAM_XATTR_SOLARIS:
817    case STREAM_XATTR_DARWIN:
818    case STREAM_XATTR_FREEBSD:
819    case STREAM_XATTR_LINUX:
820    case STREAM_XATTR_NETBSD:
821       /* Ignore Unix Extended attributes */
822       break;
823
824    default:
825       Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream, rec->data_len);
826       break;
827    }
828    return true;
829 }
830
831 /*
832  * Free the Job Control Record if no one is still using it.
833  *  Called from main free_jcr() routine in src/lib/jcr.c so
834  *  that we can do our Director specific cleanup of the jcr.
835  */
836 static void bscan_free_jcr(JCR *jcr)
837 {
838    Dmsg0(200, "Start bscan free_jcr\n");
839
840    if (jcr->file_bsock) {
841       Dmsg0(200, "Close File bsock\n");
842       bnet_close(jcr->file_bsock);
843    }
844    if (jcr->store_bsock) {
845       Dmsg0(200, "Close Store bsock\n");
846       bnet_close(jcr->store_bsock);
847    }
848    if (jcr->RestoreBootstrap) {
849       free(jcr->RestoreBootstrap);
850    }
851    if (jcr->dcr) {
852       free_dcr(jcr->dcr);
853       jcr->dcr = NULL;
854    }
855    if (jcr->read_dcr) {
856       free_dcr(jcr->read_dcr);
857       jcr->read_dcr = NULL;
858    }
859    Dmsg0(200, "End bscan free_jcr\n");
860 }
861
862 /*
863  * We got a File Attributes record on the tape.  Now, lookup the Job
864  *   record, and then create the attributes record.
865  */
866 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
867                                char *fname, char *lname, int type,
868                                char *ap, DEV_RECORD *rec)
869 {
870    DCR *dcr = mjcr->read_dcr;
871    ar.fname = fname;
872    ar.link = lname;
873    ar.ClientId = mjcr->ClientId;
874    ar.JobId = mjcr->JobId;
875    ar.Stream = rec->Stream;
876    if (type == FT_DELETED) {
877       ar.FileIndex = 0;
878    } else {
879       ar.FileIndex = rec->FileIndex;
880    }
881    ar.attr = ap;
882    if (dcr->VolFirstIndex == 0) {
883       dcr->VolFirstIndex = rec->FileIndex;
884    }
885    dcr->FileIndex = rec->FileIndex;
886    mjcr->JobFiles++;
887
888    if (!update_db) {
889       return 1;
890    }
891
892    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
893       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
894       return 0;
895    }
896    mjcr->FileId = ar.FileId;
897
898    if (verbose > 1) {
899       Pmsg1(000, _("Created File record: %s\n"), fname);
900    }
901    return 1;
902 }
903
904 /*
905  * For each Volume we see, we create a Medium record
906  */
907 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
908 {
909    struct date_time dt;
910    struct tm tm;
911
912    /* We mark Vols as Archive to keep them from being re-written */
913    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
914    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
915    mr->Enabled = 1;
916    if (vl->VerNum >= 11) {
917       mr->FirstWritten = btime_to_utime(vl->write_btime);
918       mr->LabelDate    = btime_to_utime(vl->label_btime);
919    } else {
920       /* DEPRECATED DO NOT USE */
921       dt.julian_day_number = vl->write_date;
922       dt.julian_day_fraction = vl->write_time;
923       tm_decode(&dt, &tm);
924       mr->FirstWritten = mktime(&tm);
925       dt.julian_day_number = vl->label_date;
926       dt.julian_day_fraction = vl->label_time;
927       tm_decode(&dt, &tm);
928       mr->LabelDate = mktime(&tm);
929    }
930    lasttime = mr->LabelDate;
931    if (mr->VolJobs == 0) {
932       mr->VolJobs = 1;
933    }
934    if (mr->VolMounts == 0) {
935       mr->VolMounts = 1;
936    }
937
938    if (!update_db) {
939       return 1;
940    }
941
942    if (!db_create_media_record(bjcr, db, mr)) {
943       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
944       return 0;
945    }
946    if (!db_update_media_record(bjcr, db, mr)) {
947       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
948       return 0;
949    }
950    if (verbose) {
951       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
952    }
953    return 1;
954
955 }
956
957 /*
958  * Called at end of media to update it
959  */
960 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
961 {
962    if (!update_db && !update_vol_info) {
963       return true;
964    }
965
966    mr->LastWritten = lasttime;
967    if (!db_update_media_record(bjcr, db, mr)) {
968       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
969       return false;;
970    }
971    if (verbose) {
972       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
973    }
974    return true;
975
976 }
977
978
979 static int create_pool_record(B_DB *db, POOL_DBR *pr)
980 {
981    pr->NumVols++;
982    pr->UseCatalog = 1;
983    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
984
985    if (!update_db) {
986       return 1;
987    }
988    if (!db_create_pool_record(bjcr, db, pr)) {
989       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
990       return 0;
991    }
992    if (verbose) {
993       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
994    }
995    return 1;
996
997 }
998
999
1000 /*
1001  * Called from SOS to create a client for the current Job
1002  */
1003 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
1004 {
1005    /*
1006     * Note, update_db can temporarily be set false while 
1007     * updating the database, so we must ensure that ClientId is non-zero.
1008     */
1009    if (!update_db) {
1010       cr->ClientId = 0;
1011       if (!db_get_client_record(bjcr, db, cr)) {
1012         Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db_strerror(db));
1013         return 0;
1014       }
1015       return 1;
1016    }
1017    if (!db_create_client_record(bjcr, db, cr)) {
1018       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
1019       return 0;
1020    }
1021    if (verbose) {
1022       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
1023    }
1024    return 1;
1025 }
1026
1027 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
1028 {
1029    if (!update_db) {
1030       return 1;
1031    }
1032    fsr->FileSetId = 0;
1033    if (fsr->MD5[0] == 0) {
1034       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
1035       fsr->MD5[1] = 0;
1036    }
1037    if (db_get_fileset_record(bjcr, db, fsr)) {
1038       if (verbose) {
1039          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1040       }
1041    } else {
1042       if (!db_create_fileset_record(bjcr, db, fsr)) {
1043          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1044             fsr->FileSet, db_strerror(db));
1045          return 0;
1046       }
1047       if (verbose) {
1048          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1049       }
1050    }
1051    return 1;
1052 }
1053
1054 /*
1055  * Simulate the two calls on the database to create
1056  *  the Job record and to update it when the Job actually
1057  *  begins running.
1058  */
1059 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
1060                              DEV_RECORD *rec)
1061 {
1062    JCR *mjcr;
1063    struct date_time dt;
1064    struct tm tm;
1065
1066    jr->JobId = label->JobId;
1067    jr->JobType = label->JobType;
1068    jr->JobLevel = label->JobLevel;
1069    jr->JobStatus = JS_Created;
1070    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1071    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1072    if (label->VerNum >= 11) {
1073       jr->SchedTime = btime_to_unix(label->write_btime);
1074    } else {
1075       dt.julian_day_number = label->write_date;
1076       dt.julian_day_fraction = label->write_time;
1077       tm_decode(&dt, &tm);
1078       jr->SchedTime = mktime(&tm);
1079    }
1080
1081    jr->StartTime = jr->SchedTime;
1082    jr->JobTDate = (utime_t)jr->SchedTime;
1083    jr->VolSessionId = rec->VolSessionId;
1084    jr->VolSessionTime = rec->VolSessionTime;
1085
1086    /* Now create a JCR as if starting the Job */
1087    mjcr = create_jcr(jr, rec, label->JobId);
1088
1089    if (!update_db) {
1090       return mjcr;
1091    }
1092
1093    /* This creates the bare essentials */
1094    if (!db_create_job_record(bjcr, db, jr)) {
1095       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1096       return mjcr;
1097    }
1098
1099    /* This adds the client, StartTime, JobTDate, ... */
1100    if (!db_update_job_start_record(bjcr, db, jr)) {
1101       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1102       return mjcr;
1103    }
1104    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1105          label->JobId);
1106    mjcr->JobId = jr->JobId;           /* set new JobId */
1107    return mjcr;
1108 }
1109
1110 /*
1111  * Simulate the database call that updates the Job
1112  *  at Job termination time.
1113  */
1114 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1115                               DEV_RECORD *rec)
1116 {
1117    struct date_time dt;
1118    struct tm tm;
1119    JCR *mjcr;
1120
1121    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1122    if (!mjcr) {
1123       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1124                    rec->VolSessionId, rec->VolSessionTime);
1125       return 0;
1126    }
1127    if (elabel->VerNum >= 11) {
1128       jr->EndTime = btime_to_unix(elabel->write_btime);
1129    } else {
1130       dt.julian_day_number = elabel->write_date;
1131       dt.julian_day_fraction = elabel->write_time;
1132       tm_decode(&dt, &tm);
1133       jr->EndTime = mktime(&tm);
1134    }
1135    lasttime = jr->EndTime;
1136    mjcr->end_time = jr->EndTime;
1137
1138    jr->JobId = mjcr->JobId;
1139    jr->JobStatus = elabel->JobStatus;
1140    mjcr->JobStatus = elabel->JobStatus;
1141    jr->JobFiles = elabel->JobFiles;
1142    jr->JobBytes = elabel->JobBytes;
1143    jr->VolSessionId = rec->VolSessionId;
1144    jr->VolSessionTime = rec->VolSessionTime;
1145    jr->JobTDate = (utime_t)mjcr->start_time;
1146    jr->ClientId = mjcr->ClientId;
1147
1148    if (!update_db) {
1149       free_jcr(mjcr);
1150       return 1;
1151    }
1152
1153    if (!db_update_job_end_record(bjcr, db, jr)) {
1154       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1155       free_jcr(mjcr);
1156       return 0;
1157    }
1158    if (verbose) {
1159       Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"), 
1160          jr->JobId, job_level_to_str(mjcr->get_JobLevel()), jr->JobStatus);
1161    }
1162    if (verbose > 1) {
1163       const char *term_msg;
1164       static char term_code[70];
1165       char sdt[50], edt[50];
1166       char ec1[30], ec2[30], ec3[30];
1167
1168       switch (mjcr->JobStatus) {
1169       case JS_Terminated:
1170          term_msg = _("Backup OK");
1171          break;
1172       case JS_Warnings:
1173          term_msg = _("Backup OK -- with warnings");
1174          break;
1175       case JS_FatalError:
1176       case JS_ErrorTerminated:
1177          term_msg = _("*** Backup Error ***");
1178          break;
1179       case JS_Canceled:
1180          term_msg = _("Backup Canceled");
1181          break;
1182       default:
1183          term_msg = term_code;
1184          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1185          break;
1186       }
1187       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1188       bstrftime(edt, sizeof(edt), mjcr->end_time);
1189       Pmsg14(000,  _("%s\n"
1190 "JobId:                  %d\n"
1191 "Job:                    %s\n"
1192 "FileSet:                %s\n"
1193 "Backup Level:           %s\n"
1194 "Client:                 %s\n"
1195 "Start time:             %s\n"
1196 "End time:               %s\n"
1197 "Files Written:          %s\n"
1198 "Bytes Written:          %s\n"
1199 "Volume Session Id:      %d\n"
1200 "Volume Session Time:    %d\n"
1201 "Last Volume Bytes:      %s\n"
1202 "Termination:            %s\n\n"),
1203         edt,
1204         mjcr->JobId,
1205         mjcr->Job,
1206         mjcr->fileset_name,
1207         job_level_to_str(mjcr->get_JobLevel()),
1208         mjcr->client_name,
1209         sdt,
1210         edt,
1211         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1212         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1213         mjcr->VolSessionId,
1214         mjcr->VolSessionTime,
1215         edit_uint64_with_commas(mr.VolBytes, ec3),
1216         term_msg);
1217    }
1218    free_jcr(mjcr);
1219    return 1;
1220 }
1221
1222 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1223 {
1224    JOBMEDIA_DBR jmr;
1225    DCR *dcr = mjcr->read_dcr;
1226
1227    dcr->EndBlock = dev->EndBlock;
1228    dcr->EndFile  = dev->EndFile;
1229    dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1230
1231    memset(&jmr, 0, sizeof(jmr));
1232    jmr.JobId = mjcr->JobId;
1233    jmr.MediaId = mr.MediaId;
1234    jmr.FirstIndex = dcr->VolFirstIndex;
1235    jmr.LastIndex = dcr->VolLastIndex;
1236    jmr.StartFile = dcr->StartFile;
1237    jmr.EndFile = dcr->EndFile;
1238    jmr.StartBlock = dcr->StartBlock;
1239    jmr.EndBlock = dcr->EndBlock;
1240
1241
1242    if (!update_db) {
1243       return 1;
1244    }
1245
1246    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1247       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1248       return 0;
1249    }
1250    if (verbose) {
1251       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1252                 jmr.JobId, jmr.MediaId);
1253    }
1254    return 1;
1255 }
1256
1257 /*
1258  * Simulate the database call that updates the MD5/SHA1 record
1259  */
1260 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1261 {
1262    JCR *mjcr;
1263
1264    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1265    if (!mjcr) {
1266       if (mr.VolJobs > 0) {
1267          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1268                       rec->VolSessionId, rec->VolSessionTime);
1269       } else {
1270          ignored_msgs++;
1271       }
1272       return 0;
1273    }
1274
1275    if (!update_db || mjcr->FileId == 0) {
1276       free_jcr(mjcr);
1277       return 1;
1278    }
1279
1280    if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1281       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1282       free_jcr(mjcr);
1283       return 0;
1284    }
1285    if (verbose > 1) {
1286       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1287    }
1288    free_jcr(mjcr);
1289    return 1;
1290 }
1291
1292
1293 /*
1294  * Create a JCR as if we are really starting the job
1295  */
1296 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1297 {
1298    JCR *jobjcr;
1299    /*
1300     * Transfer as much as possible to the Job JCR. Most important is
1301     *   the JobId and the ClientId.
1302     */
1303    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1304    jobjcr->set_JobType(jr->JobType);
1305    jobjcr->set_JobLevel(jr->JobLevel);
1306    jobjcr->JobStatus = jr->JobStatus;
1307    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1308    jobjcr->JobId = JobId;      /* this is JobId on tape */
1309    jobjcr->sched_time = jr->SchedTime;
1310    jobjcr->start_time = jr->StartTime;
1311    jobjcr->VolSessionId = rec->VolSessionId;
1312    jobjcr->VolSessionTime = rec->VolSessionTime;
1313    jobjcr->ClientId = jr->ClientId;
1314    jobjcr->dcr = jobjcr->read_dcr = new_dcr(jobjcr, NULL, dev);
1315
1316    return jobjcr;
1317 }
1318
1319 /* Dummies to replace askdir.c */
1320 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1321 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
1322 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
1323 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1324 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1325 bool    dir_send_job_status(JCR *jcr) {return 1;}
1326 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1327
1328 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
1329 {
1330    DEVICE *dev = dcr->dev;
1331    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1332    /* Close device so user can use autochanger if desired */
1333    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1334          dcr->VolumeName, dev->print_name());
1335    dev->close();
1336    getchar();
1337    return true;
1338 }
1339
1340 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
1341 {
1342    Dmsg0(100, "Fake dir_get_volume_info\n");
1343    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
1344    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
1345    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
1346    return 1;
1347 }