]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
- Rename "crypto_recipients" to "crypto_session" to more accurate describe the crypto...
[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       mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
325       if (!create_jobmedia_record(db, mjcr)) {
326          Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
327             dev->VolCatInfo.VolCatName, mjcr->Job);
328       }
329    }
330    /* Now let common read routine get up next tape. Note,
331     * we call mount_next... with bscan's jcr because that is where we
332     * have the Volume list, but we get attached.
333     */
334    bool stat = mount_next_read_volume(dcr);
335
336    if (showProgress) {
337       char ed1[50];
338       struct stat sb;
339       fstat(dev->fd, &sb);
340       currentVolumeSize = sb.st_size;
341       Pmsg1(000, _("First Volume Size = %sn"), 
342          edit_uint64(currentVolumeSize, ed1));
343    }
344    return stat;
345 }
346
347 static void do_scan()
348 {
349    attr = new_attr();
350
351    memset(&ar, 0, sizeof(ar));
352    memset(&pr, 0, sizeof(pr));
353    memset(&jr, 0, sizeof(jr));
354    memset(&cr, 0, sizeof(cr));
355    memset(&fsr, 0, sizeof(fsr));
356    memset(&fr, 0, sizeof(fr));
357
358    /* Detach bscan's jcr as we are not a real Job on the tape */
359
360    read_records(bjcr->read_dcr, record_cb, bscan_mount_next_read_volume);
361
362    free_attr(attr);
363 }
364
365 /*
366  * Returns: true  if OK
367  *          false if error
368  */
369 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
370 {
371    JCR *mjcr;
372    char ec1[30];
373    DEVICE *dev = dcr->dev;
374    JCR *bjcr = dcr->jcr;
375    DEV_BLOCK *block = dcr->block;
376    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
377
378    if (rec->data_len > 0) {
379       mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
380       if (showProgress && currentVolumeSize > 0) {
381          int pct = (mr.VolBytes * 100) / currentVolumeSize;
382          if (pct != last_pct) {
383             fprintf(stdout, _("done: %d%%\n"), pct);
384             fflush(stdout);
385             last_pct = pct;
386          }
387       }
388    }
389
390    if (list_records) {
391       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
392             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
393             rec->Stream, rec->data_len);
394    }
395    /*
396     * Check for Start or End of Session Record
397     *
398     */
399    if (rec->FileIndex < 0) {
400       bool save_update_db = update_db;
401
402       if (verbose > 1) {
403          dump_label_record(dev, rec, 1);
404       }
405       switch (rec->FileIndex) {
406       case PRE_LABEL:
407          Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
408          return false;
409          break;
410
411       case VOL_LABEL:
412          unser_volume_label(dev, rec);
413          /* Check Pool info */
414          bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
415          bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
416          num_pools++;
417          if (db_get_pool_record(bjcr, db, &pr)) {
418             if (verbose) {
419                Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
420             }
421          } else {
422             if (!update_db) {
423                Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
424                   pr.Name);
425             }
426             create_pool_record(db, &pr);
427          }
428          if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
429             Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
430                pr.PoolType, dev->VolHdr.PoolType);
431             return true;
432          } else if (verbose) {
433             Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
434          }
435
436          /* Check Media Info */
437          memset(&mr, 0, sizeof(mr));
438          bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
439          mr.PoolId = pr.PoolId;
440          num_media++;
441          if (db_get_media_record(bjcr, db, &mr)) {
442             if (verbose) {
443                Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
444             }
445             /* Clear out some volume statistics that will be updated */
446             mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
447             mr.VolBytes = rec->data_len + 20;
448          } else {
449             if (!update_db) {
450                Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
451                   mr.VolumeName);
452             }
453             bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
454             create_media_record(db, &mr, &dev->VolHdr);
455          }
456          if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
457             Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
458                mr.MediaType, dev->VolHdr.MediaType);
459             return true;              /* ignore error */
460          } else if (verbose) {
461             Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
462          }
463          /* Reset some DCR variables */
464          foreach_dlist(dcr, dev->attached_dcrs) {
465             dcr->VolFirstIndex = dcr->FileIndex = 0;
466             dcr->StartBlock = dcr->EndBlock = 0;
467             dcr->StartFile = dcr->EndFile = 0;
468          }
469
470          Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
471          break;
472
473       case SOS_LABEL:
474          mr.VolJobs++;
475          num_jobs++;
476          if (ignored_msgs > 0) {
477             Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
478                   ignored_msgs);
479             ignored_msgs = 0;
480          }
481          unser_session_label(&label, rec);
482          memset(&jr, 0, sizeof(jr));
483          bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
484          if (db_get_job_record(bjcr, db, &jr)) {
485             /* Job record already exists in DB */
486             update_db = false;  /* don't change db in create_job_record */
487             if (verbose) {
488                Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
489             }
490          } else {
491             /* Must create a Job record in DB */
492             if (!update_db) {
493                Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
494                   jr.JobId);
495             }
496          }
497          /* Create Client record if not already there */
498             bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
499             create_client_record(db, &cr);
500             jr.ClientId = cr.ClientId;
501
502          /* process label, if Job record exists don't update db */
503          mjcr = create_job_record(db, &jr, &label, rec);
504          dcr = mjcr->read_dcr;
505          update_db = save_update_db;
506
507          jr.PoolId = pr.PoolId;
508 #ifdef xxx
509          /* Set start positions into JCR */
510          if (dev->is_tape()) {
511             /*
512              * Note, we have already advanced past current block,
513              *  so the correct number is block_num - 1
514              */
515             dcr->StartBlock = dev->block_num - 1;
516             dcr->StartFile = dev->file;
517          } else {
518             dcr->StartBlock = (uint32_t)dev->file_addr;
519             dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
520          }
521 #endif
522          mjcr->start_time = jr.StartTime;
523          mjcr->JobLevel = jr.JobLevel;
524
525          mjcr->client_name = get_pool_memory(PM_FNAME);
526          pm_strcpy(mjcr->client_name, label.ClientName);
527          mjcr->fileset_name = get_pool_memory(PM_FNAME);
528          pm_strcpy(mjcr->fileset_name, label.FileSetName);
529          bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
530          bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
531
532          if (rec->VolSessionId != jr.VolSessionId) {
533             Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
534                jr.JobId,
535                jr.VolSessionId, rec->VolSessionId);
536             return true;              /* ignore error */
537          }
538          if (rec->VolSessionTime != jr.VolSessionTime) {
539             Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
540                jr.JobId,
541                jr.VolSessionTime, rec->VolSessionTime);
542             return true;              /* ignore error */
543          }
544          if (jr.PoolId != pr.PoolId) {
545             Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
546                jr.JobId,
547                jr.PoolId, pr.PoolId);
548             return true;              /* ignore error */
549          }
550          break;
551
552       case EOS_LABEL:
553          unser_session_label(&elabel, rec);
554
555          /* Create FileSet record */
556          bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
557          bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
558          create_fileset_record(db, &fsr);
559          jr.FileSetId = fsr.FileSetId;
560
561          mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
562          if (!mjcr) {
563             Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
564                   rec->VolSessionId, rec->VolSessionTime);
565             break;
566          }
567
568          /* Do the final update to the Job record */
569          update_job_record(db, &jr, &elabel, rec);
570
571          mjcr->end_time = jr.EndTime;
572          mjcr->JobStatus = JS_Terminated;
573
574          /* Create JobMedia record */
575          mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
576          create_jobmedia_record(db, mjcr);
577          dev->attached_dcrs->remove(mjcr->read_dcr);
578          free_jcr(mjcr);
579
580          break;
581
582       case EOM_LABEL:
583          break;
584
585       case EOT_LABEL:              /* end of all tapes */
586          /*
587           * Wiffle through all jobs still open and close
588           *   them.
589           */
590          if (update_db) {
591             DCR *mdcr;
592             foreach_dlist(mdcr, dev->attached_dcrs) {
593                JCR *mjcr = mdcr->jcr;
594                if (!mjcr || mjcr->JobId == 0) {
595                   continue;
596                }
597                jr.JobId = mjcr->JobId;
598                /* Mark Job as Error Terimined */
599                jr.JobStatus = JS_ErrorTerminated;
600                jr.JobFiles = mjcr->JobFiles;
601                jr.JobBytes = mjcr->JobBytes;
602                jr.VolSessionId = mjcr->VolSessionId;
603                jr.VolSessionTime = mjcr->VolSessionTime;
604                jr.JobTDate = (utime_t)mjcr->start_time;
605                jr.ClientId = mjcr->ClientId;
606                if (!db_update_job_end_record(bjcr, db, &jr)) {
607                   Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
608                }
609                mjcr->read_dcr = NULL;
610                free_jcr(mjcr);
611             }
612          }
613          mr.VolFiles = rec->File;
614          mr.VolBlocks = rec->Block;
615          mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
616          mr.VolMounts++;
617          update_media_record(db, &mr);
618          Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
619                     mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
620          break;
621       default:
622          break;
623       } /* end switch */
624       return true;
625    }
626
627    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
628    if (!mjcr) {
629       if (mr.VolJobs > 0) {
630          Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
631                       rec->VolSessionId, rec->VolSessionTime);
632       } else {
633          ignored_msgs++;
634       }
635       return true;
636    }
637    dcr = mjcr->read_dcr;
638    if (dcr->VolFirstIndex == 0) {
639       dcr->VolFirstIndex = block->FirstIndex;
640    }
641
642    /* File Attributes stream */
643    switch (rec->Stream) {
644    case STREAM_UNIX_ATTRIBUTES:
645    case STREAM_UNIX_ATTRIBUTES_EX:
646
647       if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
648          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
649       }
650
651       if (attr->file_index != rec->FileIndex) {
652          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
653             rec->FileIndex, attr->file_index);
654       }
655
656       if (verbose > 1) {
657          decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
658          build_attr_output_fnames(bjcr, attr);
659          print_ls_output(bjcr, attr);
660       }
661       fr.JobId = mjcr->JobId;
662       fr.FileId = 0;
663       num_files++;
664       if (verbose && (num_files & 0x7FFF) == 0) {
665          char ed1[30], ed2[30], ed3[30], ed4[30];
666          Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
667                      edit_uint64_with_commas(num_files, ed1),
668                      edit_uint64_with_commas(rec->File, ed2),
669                      edit_uint64_with_commas(rec->Block, ed3),
670                      edit_uint64_with_commas(mr.VolBytes, ed4));
671       }
672       create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
673             attr->type, attr->attr, rec);
674       free_jcr(mjcr);
675       break;
676
677    /* Data stream */
678    case STREAM_WIN32_DATA:
679    case STREAM_FILE_DATA:
680    case STREAM_SPARSE_DATA:
681       mjcr->JobBytes += rec->data_len;
682       if (rec->Stream == STREAM_SPARSE_DATA) {
683          mjcr->JobBytes -= sizeof(uint64_t);
684       }
685
686       free_jcr(mjcr);                 /* done using JCR */
687       break;
688
689    case STREAM_GZIP_DATA:
690       mjcr->JobBytes += rec->data_len; /* No correct, we should expand it */
691       free_jcr(mjcr);                 /* done using JCR */
692       break;
693
694    case STREAM_SPARSE_GZIP_DATA:
695       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
696       free_jcr(mjcr);                 /* done using JCR */
697       break;
698
699    /* Win32 GZIP stream */
700    case STREAM_WIN32_GZIP_DATA:
701       mjcr->JobBytes += rec->data_len;
702       free_jcr(mjcr);                 /* done using JCR */
703       break;
704
705    case STREAM_MD5_DIGEST:
706       bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE);
707       if (verbose > 1) {
708          Pmsg1(000, _("Got MD5 record: %s\n"), digest);
709       }
710       update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
711       break;
712
713    case STREAM_SHA1_DIGEST:
714       bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE);
715       if (verbose > 1) {
716          Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
717       }
718       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
719       break;
720
721    case STREAM_SHA256_DIGEST:
722       bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE);
723       if (verbose > 1) {
724          Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
725       }
726       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
727       break;
728
729    case STREAM_SHA512_DIGEST:
730       bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE);
731       if (verbose > 1) {
732          Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
733       }
734       update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
735       break;
736
737    case STREAM_ENCRYPTED_SESSION_DATA:
738       // TODO landonf: Investigate crypto support in bscan
739       if (verbose > 1) {
740          Pmsg0(000, _("Got signed digest record\n"));
741       }
742       break;
743
744    case STREAM_SIGNED_DIGEST:
745       // TODO landonf: Investigate crypto support in bscan
746       if (verbose > 1) {
747          Pmsg0(000, _("Got signed digest record\n"));
748       }
749       break;
750
751    case STREAM_PROGRAM_NAMES:
752       if (verbose) {
753          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
754       }
755       break;
756
757    case STREAM_PROGRAM_DATA:
758       if (verbose > 1) {
759          Pmsg0(000, _("Got Prog Data Stream record.\n"));
760       }
761       break;
762    default:
763       Pmsg2(0, _("Unknown stream type!!! stream=%d data=%s\n"), rec->Stream, rec->data);
764       break;
765    }
766    return true;
767 }
768
769 /*
770  * Free the Job Control Record if no one is still using it.
771  *  Called from main free_jcr() routine in src/lib/jcr.c so
772  *  that we can do our Director specific cleanup of the jcr.
773  */
774 static void bscan_free_jcr(JCR *jcr)
775 {
776    Dmsg0(200, "Start bscan free_jcr\n");
777
778    if (jcr->file_bsock) {
779       Dmsg0(200, "Close File bsock\n");
780       bnet_close(jcr->file_bsock);
781    }
782    if (jcr->store_bsock) {
783       Dmsg0(200, "Close Store bsock\n");
784       bnet_close(jcr->store_bsock);
785    }
786    if (jcr->RestoreBootstrap) {
787       free(jcr->RestoreBootstrap);
788    }
789    if (jcr->dcr) {
790       free_dcr(jcr->dcr);
791       jcr->dcr = NULL;
792    }
793    if (jcr->read_dcr) {
794       free_dcr(jcr->read_dcr);
795       jcr->read_dcr = NULL;
796    }
797    Dmsg0(200, "End bscan free_jcr\n");
798 }
799
800 /*
801  * We got a File Attributes record on the tape.  Now, lookup the Job
802  *   record, and then create the attributes record.
803  */
804 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
805                                char *fname, char *lname, int type,
806                                char *ap, DEV_RECORD *rec)
807 {
808    DCR *dcr = mjcr->read_dcr;
809    ar.fname = fname;
810    ar.link = lname;
811    ar.ClientId = mjcr->ClientId;
812    ar.JobId = mjcr->JobId;
813    ar.Stream = rec->Stream;
814    ar.FileIndex = rec->FileIndex;
815    ar.attr = ap;
816    if (dcr->VolFirstIndex == 0) {
817       dcr->VolFirstIndex = rec->FileIndex;
818    }
819    dcr->FileIndex = rec->FileIndex;
820    mjcr->JobFiles++;
821
822    if (!update_db) {
823       return 1;
824    }
825
826    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
827       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
828       return 0;
829    }
830    mjcr->FileId = ar.FileId;
831
832    if (verbose > 1) {
833       Pmsg1(000, _("Created File record: %s\n"), fname);
834    }
835    return 1;
836 }
837
838 /*
839  * For each Volume we see, we create a Medium record
840  */
841 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
842 {
843    struct date_time dt;
844    struct tm tm;
845
846    /* We mark Vols as Archive to keep them from being re-written */
847    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
848    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
849    if (vl->VerNum >= 11) {
850       mr->FirstWritten = btime_to_utime(vl->write_btime);
851       mr->LabelDate    = btime_to_utime(vl->label_btime);
852    } else {
853       /* DEPRECATED DO NOT USE */
854       dt.julian_day_number = vl->write_date;
855       dt.julian_day_fraction = vl->write_time;
856       tm_decode(&dt, &tm);
857       mr->FirstWritten = mktime(&tm);
858       dt.julian_day_number = vl->label_date;
859       dt.julian_day_fraction = vl->label_time;
860       tm_decode(&dt, &tm);
861       mr->LabelDate = mktime(&tm);
862    }
863    lasttime = mr->LabelDate;
864
865    if (!update_db) {
866       return 1;
867    }
868
869    if (!db_create_media_record(bjcr, db, mr)) {
870       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
871       return 0;
872    }
873    if (!db_update_media_record(bjcr, db, mr)) {
874       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
875       return 0;
876    }
877    if (verbose) {
878       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
879    }
880    return 1;
881
882 }
883
884 /*
885  * Called at end of media to update it
886  */
887 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
888 {
889    if (!update_db && !update_vol_info) {
890       return true;
891    }
892
893    mr->LastWritten = lasttime;
894    if (!db_update_media_record(bjcr, db, mr)) {
895       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
896       return false;;
897    }
898    if (verbose) {
899       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
900    }
901    return true;
902
903 }
904
905
906 static int create_pool_record(B_DB *db, POOL_DBR *pr)
907 {
908    pr->NumVols++;
909    pr->UseCatalog = 1;
910    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
911
912    if (!update_db) {
913       return 1;
914    }
915    if (!db_create_pool_record(bjcr, db, pr)) {
916       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
917       return 0;
918    }
919    if (verbose) {
920       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
921    }
922    return 1;
923
924 }
925
926
927 /*
928  * Called from SOS to create a client for the current Job
929  */
930 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
931 {
932    if (!update_db) {
933       return 1;
934    }
935    if (!db_create_client_record(bjcr, db, cr)) {
936       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
937       return 0;
938    }
939    if (verbose) {
940       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
941    }
942    return 1;
943 }
944
945 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
946 {
947    if (!update_db) {
948       return 1;
949    }
950    fsr->FileSetId = 0;
951    if (fsr->MD5[0] == 0) {
952       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
953       fsr->MD5[1] = 0;
954    }
955    if (db_get_fileset_record(bjcr, db, fsr)) {
956       if (verbose) {
957          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
958       }
959    } else {
960       if (!db_create_fileset_record(bjcr, db, fsr)) {
961          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
962             fsr->FileSet, db_strerror(db));
963          return 0;
964       }
965       if (verbose) {
966          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
967       }
968    }
969    return 1;
970 }
971
972 /*
973  * Simulate the two calls on the database to create
974  *  the Job record and to update it when the Job actually
975  *  begins running.
976  */
977 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
978                              DEV_RECORD *rec)
979 {
980    JCR *mjcr;
981    struct date_time dt;
982    struct tm tm;
983
984    jr->JobId = label->JobId;
985    jr->JobType = label->JobType;
986    jr->JobLevel = label->JobLevel;
987    jr->JobStatus = JS_Created;
988    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
989    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
990    if (label->VerNum >= 11) {
991       jr->SchedTime = btime_to_unix(label->write_btime);
992    } else {
993       dt.julian_day_number = label->write_date;
994       dt.julian_day_fraction = label->write_time;
995       tm_decode(&dt, &tm);
996       jr->SchedTime = mktime(&tm);
997    }
998
999    jr->StartTime = jr->SchedTime;
1000    jr->JobTDate = (utime_t)jr->SchedTime;
1001    jr->VolSessionId = rec->VolSessionId;
1002    jr->VolSessionTime = rec->VolSessionTime;
1003
1004    /* Now create a JCR as if starting the Job */
1005    mjcr = create_jcr(jr, rec, label->JobId);
1006
1007    if (!update_db) {
1008       return mjcr;
1009    }
1010
1011    /* This creates the bare essentials */
1012    if (!db_create_job_record(bjcr, db, jr)) {
1013       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1014       return mjcr;
1015    }
1016
1017    /* This adds the client, StartTime, JobTDate, ... */
1018    if (!db_update_job_start_record(bjcr, db, jr)) {
1019       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1020       return mjcr;
1021    }
1022    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1023          label->JobId);
1024    mjcr->JobId = jr->JobId;           /* set new JobId */
1025    return mjcr;
1026 }
1027
1028 /*
1029  * Simulate the database call that updates the Job
1030  *  at Job termination time.
1031  */
1032 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1033                               DEV_RECORD *rec)
1034 {
1035    struct date_time dt;
1036    struct tm tm;
1037    JCR *mjcr;
1038
1039    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1040    if (!mjcr) {
1041       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1042                    rec->VolSessionId, rec->VolSessionTime);
1043       return 0;
1044    }
1045    if (elabel->VerNum >= 11) {
1046       jr->EndTime = btime_to_unix(elabel->write_btime);
1047    } else {
1048       dt.julian_day_number = elabel->write_date;
1049       dt.julian_day_fraction = elabel->write_time;
1050       tm_decode(&dt, &tm);
1051       jr->EndTime = mktime(&tm);
1052    }
1053    lasttime = jr->EndTime;
1054    mjcr->end_time = jr->EndTime;
1055
1056    jr->JobId = mjcr->JobId;
1057    jr->JobStatus = elabel->JobStatus;
1058    mjcr->JobStatus = elabel->JobStatus;
1059    jr->JobFiles = elabel->JobFiles;
1060    jr->JobBytes = elabel->JobBytes;
1061    jr->VolSessionId = rec->VolSessionId;
1062    jr->VolSessionTime = rec->VolSessionTime;
1063    jr->JobTDate = (utime_t)mjcr->start_time;
1064    jr->ClientId = mjcr->ClientId;
1065
1066    if (!update_db) {
1067       free_jcr(mjcr);
1068       return 1;
1069    }
1070
1071    if (!db_update_job_end_record(bjcr, db, jr)) {
1072       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1073       free_jcr(mjcr);
1074       return 0;
1075    }
1076    if (verbose) {
1077       Pmsg2(000, _("Updated Job termination record for JobId=%u TermStat=%c\n"), jr->JobId,
1078          jr->JobStatus);
1079    }
1080    if (verbose > 1) {
1081       const char *term_msg;
1082       static char term_code[70];
1083       char sdt[50], edt[50];
1084       char ec1[30], ec2[30], ec3[30];
1085
1086       switch (mjcr->JobStatus) {
1087       case JS_Terminated:
1088          term_msg = _("Backup OK");
1089          break;
1090       case JS_FatalError:
1091       case JS_ErrorTerminated:
1092          term_msg = _("*** Backup Error ***");
1093          break;
1094       case JS_Canceled:
1095          term_msg = _("Backup Canceled");
1096          break;
1097       default:
1098          term_msg = term_code;
1099          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1100          break;
1101       }
1102       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1103       bstrftime(edt, sizeof(edt), mjcr->end_time);
1104       Pmsg14(000,  _("%s\n"
1105 "JobId:                  %d\n"
1106 "Job:                    %s\n"
1107 "FileSet:                %s\n"
1108 "Backup Level:           %s\n"
1109 "Client:                 %s\n"
1110 "Start time:             %s\n"
1111 "End time:               %s\n"
1112 "Files Written:          %s\n"
1113 "Bytes Written:          %s\n"
1114 "Volume Session Id:      %d\n"
1115 "Volume Session Time:    %d\n"
1116 "Last Volume Bytes:      %s\n"
1117 "Termination:            %s\n\n"),
1118         edt,
1119         mjcr->JobId,
1120         mjcr->Job,
1121         mjcr->fileset_name,
1122         job_level_to_str(mjcr->JobLevel),
1123         mjcr->client_name,
1124         sdt,
1125         edt,
1126         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1127         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1128         mjcr->VolSessionId,
1129         mjcr->VolSessionTime,
1130         edit_uint64_with_commas(mr.VolBytes, ec3),
1131         term_msg);
1132    }
1133    free_jcr(mjcr);
1134    return 1;
1135 }
1136
1137 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1138 {
1139    JOBMEDIA_DBR jmr;
1140    DCR *dcr = mjcr->read_dcr;
1141
1142    if (dev->is_tape()) {
1143       dcr->EndBlock = dev->EndBlock;
1144       dcr->EndFile  = dev->EndFile;
1145 #ifdef needed
1146    } else {
1147       dcr->EndBlock = (uint32_t)dev->file_addr;
1148       dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1149 #endif
1150    } 
1151
1152    memset(&jmr, 0, sizeof(jmr));
1153    jmr.JobId = mjcr->JobId;
1154    jmr.MediaId = mr.MediaId;
1155    jmr.FirstIndex = dcr->VolFirstIndex;
1156    jmr.LastIndex = dcr->VolLastIndex;
1157    jmr.StartFile = dcr->StartFile;
1158    jmr.EndFile = dcr->EndFile;
1159    jmr.StartBlock = dcr->StartBlock;
1160    jmr.EndBlock = dcr->EndBlock;
1161
1162
1163    if (!update_db) {
1164       return 1;
1165    }
1166
1167    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1168       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1169       return 0;
1170    }
1171    if (verbose) {
1172       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1173                 jmr.JobId, jmr.MediaId);
1174    }
1175    return 1;
1176 }
1177
1178 /*
1179  * Simulate the database call that updates the MD5/SHA1 record
1180  */
1181 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1182 {
1183    JCR *mjcr;
1184
1185    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1186    if (!mjcr) {
1187       if (mr.VolJobs > 0) {
1188          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1189                       rec->VolSessionId, rec->VolSessionTime);
1190       } else {
1191          ignored_msgs++;
1192       }
1193       return 0;
1194    }
1195
1196    if (!update_db || mjcr->FileId == 0) {
1197       free_jcr(mjcr);
1198       return 1;
1199    }
1200
1201    if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1202       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1203       free_jcr(mjcr);
1204       return 0;
1205    }
1206    if (verbose > 1) {
1207       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1208    }
1209    free_jcr(mjcr);
1210    return 1;
1211 }
1212
1213
1214 /*
1215  * Create a JCR as if we are really starting the job
1216  */
1217 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1218 {
1219    JCR *jobjcr;
1220    /*
1221     * Transfer as much as possible to the Job JCR. Most important is
1222     *   the JobId and the ClientId.
1223     */
1224    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1225    jobjcr->JobType = jr->JobType;
1226    jobjcr->JobLevel = jr->JobLevel;
1227    jobjcr->JobStatus = jr->JobStatus;
1228    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1229    jobjcr->JobId = JobId;      /* this is JobId on tape */
1230    jobjcr->sched_time = jr->SchedTime;
1231    jobjcr->start_time = jr->StartTime;
1232    jobjcr->VolSessionId = rec->VolSessionId;
1233    jobjcr->VolSessionTime = rec->VolSessionTime;
1234    jobjcr->ClientId = jr->ClientId;
1235    jobjcr->read_dcr = new_dcr(jobjcr, dev);
1236
1237    return jobjcr;
1238 }
1239
1240 /* Dummies to replace askdir.c */
1241 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
1242 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1243 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1244 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
1245 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1246 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1247 bool    dir_send_job_status(JCR *jcr) {return 1;}
1248 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1249 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
1250 bool    free_volume(DEVICE *dev) { return true; }
1251 void    free_unused_volume(DCR *dcr) { }
1252
1253 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1254 {
1255    DEVICE *dev = dcr->dev;
1256    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1257    /* Close device so user can use autochanger if desired */
1258    if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
1259       offline_dev(dev);
1260    }
1261    force_close_device(dev);
1262    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1263          dcr->VolumeName, dev->print_name());
1264    getchar();
1265    return true;
1266 }