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