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