]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
- Merge changes made to 1.38.3 into 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       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) {
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_SIGNED_DIGEST:
738       // TODO landonf: Investigate signed digest support in bscan
739       if (verbose > 1) {
740          Pmsg0(000, _("Got signed digest record\n"));
741       }
742       break;
743
744    case STREAM_PROGRAM_NAMES:
745       if (verbose) {
746          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
747       }
748       break;
749
750    case STREAM_PROGRAM_DATA:
751       if (verbose > 1) {
752          Pmsg0(000, _("Got Prog Data Stream record.\n"));
753       }
754       break;
755    default:
756       Pmsg2(0, _("Unknown stream type!!! stream=%d data=%s\n"), rec->Stream, rec->data);
757       break;
758    }
759    return true;
760 }
761
762 /*
763  * Free the Job Control Record if no one is still using it.
764  *  Called from main free_jcr() routine in src/lib/jcr.c so
765  *  that we can do our Director specific cleanup of the jcr.
766  */
767 static void bscan_free_jcr(JCR *jcr)
768 {
769    Dmsg0(200, "Start bscan free_jcr\n");
770
771    if (jcr->file_bsock) {
772       Dmsg0(200, "Close File bsock\n");
773       bnet_close(jcr->file_bsock);
774    }
775    if (jcr->store_bsock) {
776       Dmsg0(200, "Close Store bsock\n");
777       bnet_close(jcr->store_bsock);
778    }
779    if (jcr->RestoreBootstrap) {
780       free(jcr->RestoreBootstrap);
781    }
782    if (jcr->dcr) {
783       free_dcr(jcr->dcr);
784       jcr->dcr = NULL;
785    }
786    if (jcr->read_dcr) {
787       free_dcr(jcr->read_dcr);
788       jcr->read_dcr = NULL;
789    }
790    Dmsg0(200, "End bscan free_jcr\n");
791 }
792
793 /*
794  * We got a File Attributes record on the tape.  Now, lookup the Job
795  *   record, and then create the attributes record.
796  */
797 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
798                                char *fname, char *lname, int type,
799                                char *ap, DEV_RECORD *rec)
800 {
801    DCR *dcr = mjcr->read_dcr;
802    ar.fname = fname;
803    ar.link = lname;
804    ar.ClientId = mjcr->ClientId;
805    ar.JobId = mjcr->JobId;
806    ar.Stream = rec->Stream;
807    ar.FileIndex = rec->FileIndex;
808    ar.attr = ap;
809    if (dcr->VolFirstIndex == 0) {
810       dcr->VolFirstIndex = rec->FileIndex;
811    }
812    dcr->FileIndex = rec->FileIndex;
813    mjcr->JobFiles++;
814
815    if (!update_db) {
816       return 1;
817    }
818
819    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
820       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
821       return 0;
822    }
823    mjcr->FileId = ar.FileId;
824
825    if (verbose > 1) {
826       Pmsg1(000, _("Created File record: %s\n"), fname);
827    }
828    return 1;
829 }
830
831 /*
832  * For each Volume we see, we create a Medium record
833  */
834 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
835 {
836    struct date_time dt;
837    struct tm tm;
838
839    /* We mark Vols as Archive to keep them from being re-written */
840    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
841    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
842    if (vl->VerNum >= 11) {
843       mr->FirstWritten = btime_to_utime(vl->write_btime);
844       mr->LabelDate    = btime_to_utime(vl->label_btime);
845    } else {
846       /* DEPRECATED DO NOT USE */
847       dt.julian_day_number = vl->write_date;
848       dt.julian_day_fraction = vl->write_time;
849       tm_decode(&dt, &tm);
850       mr->FirstWritten = mktime(&tm);
851       dt.julian_day_number = vl->label_date;
852       dt.julian_day_fraction = vl->label_time;
853       tm_decode(&dt, &tm);
854       mr->LabelDate = mktime(&tm);
855    }
856    lasttime = mr->LabelDate;
857
858    if (!update_db) {
859       return 1;
860    }
861
862    if (!db_create_media_record(bjcr, db, mr)) {
863       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
864       return 0;
865    }
866    if (!db_update_media_record(bjcr, db, mr)) {
867       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
868       return 0;
869    }
870    if (verbose) {
871       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
872    }
873    return 1;
874
875 }
876
877 /*
878  * Called at end of media to update it
879  */
880 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
881 {
882    if (!update_db && !update_vol_info) {
883       return true;
884    }
885
886    mr->LastWritten = lasttime;
887    if (!db_update_media_record(bjcr, db, mr)) {
888       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
889       return false;;
890    }
891    if (verbose) {
892       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
893    }
894    return true;
895
896 }
897
898
899 static int create_pool_record(B_DB *db, POOL_DBR *pr)
900 {
901    pr->NumVols++;
902    pr->UseCatalog = 1;
903    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
904
905    if (!update_db) {
906       return 1;
907    }
908    if (!db_create_pool_record(bjcr, db, pr)) {
909       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
910       return 0;
911    }
912    if (verbose) {
913       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
914    }
915    return 1;
916
917 }
918
919
920 /*
921  * Called from SOS to create a client for the current Job
922  */
923 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
924 {
925    if (!update_db) {
926       return 1;
927    }
928    if (!db_create_client_record(bjcr, db, cr)) {
929       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
930       return 0;
931    }
932    if (verbose) {
933       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
934    }
935    return 1;
936 }
937
938 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
939 {
940    if (!update_db) {
941       return 1;
942    }
943    fsr->FileSetId = 0;
944    if (fsr->MD5[0] == 0) {
945       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
946       fsr->MD5[1] = 0;
947    }
948    if (db_get_fileset_record(bjcr, db, fsr)) {
949       if (verbose) {
950          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
951       }
952    } else {
953       if (!db_create_fileset_record(bjcr, db, fsr)) {
954          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
955             fsr->FileSet, db_strerror(db));
956          return 0;
957       }
958       if (verbose) {
959          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
960       }
961    }
962    return 1;
963 }
964
965 /*
966  * Simulate the two calls on the database to create
967  *  the Job record and to update it when the Job actually
968  *  begins running.
969  */
970 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
971                              DEV_RECORD *rec)
972 {
973    JCR *mjcr;
974    struct date_time dt;
975    struct tm tm;
976
977    jr->JobId = label->JobId;
978    jr->JobType = label->JobType;
979    jr->JobLevel = label->JobLevel;
980    jr->JobStatus = JS_Created;
981    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
982    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
983    if (label->VerNum >= 11) {
984       jr->SchedTime = btime_to_unix(label->write_btime);
985    } else {
986       dt.julian_day_number = label->write_date;
987       dt.julian_day_fraction = label->write_time;
988       tm_decode(&dt, &tm);
989       jr->SchedTime = mktime(&tm);
990    }
991
992    jr->StartTime = jr->SchedTime;
993    jr->JobTDate = (utime_t)jr->SchedTime;
994    jr->VolSessionId = rec->VolSessionId;
995    jr->VolSessionTime = rec->VolSessionTime;
996
997    /* Now create a JCR as if starting the Job */
998    mjcr = create_jcr(jr, rec, label->JobId);
999
1000    if (!update_db) {
1001       return mjcr;
1002    }
1003
1004    /* This creates the bare essentials */
1005    if (!db_create_job_record(bjcr, db, jr)) {
1006       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1007       return mjcr;
1008    }
1009
1010    /* This adds the client, StartTime, JobTDate, ... */
1011    if (!db_update_job_start_record(bjcr, db, jr)) {
1012       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1013       return mjcr;
1014    }
1015    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1016          label->JobId);
1017    mjcr->JobId = jr->JobId;           /* set new JobId */
1018    return mjcr;
1019 }
1020
1021 /*
1022  * Simulate the database call that updates the Job
1023  *  at Job termination time.
1024  */
1025 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1026                               DEV_RECORD *rec)
1027 {
1028    struct date_time dt;
1029    struct tm tm;
1030    JCR *mjcr;
1031
1032    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1033    if (!mjcr) {
1034       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1035                    rec->VolSessionId, rec->VolSessionTime);
1036       return 0;
1037    }
1038    if (elabel->VerNum >= 11) {
1039       jr->EndTime = btime_to_unix(elabel->write_btime);
1040    } else {
1041       dt.julian_day_number = elabel->write_date;
1042       dt.julian_day_fraction = elabel->write_time;
1043       tm_decode(&dt, &tm);
1044       jr->EndTime = mktime(&tm);
1045    }
1046    lasttime = jr->EndTime;
1047    mjcr->end_time = jr->EndTime;
1048
1049    jr->JobId = mjcr->JobId;
1050    jr->JobStatus = elabel->JobStatus;
1051    mjcr->JobStatus = elabel->JobStatus;
1052    jr->JobFiles = elabel->JobFiles;
1053    jr->JobBytes = elabel->JobBytes;
1054    jr->VolSessionId = rec->VolSessionId;
1055    jr->VolSessionTime = rec->VolSessionTime;
1056    jr->JobTDate = (utime_t)mjcr->start_time;
1057    jr->ClientId = mjcr->ClientId;
1058
1059    if (!update_db) {
1060       free_jcr(mjcr);
1061       return 1;
1062    }
1063
1064    if (!db_update_job_end_record(bjcr, db, jr)) {
1065       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1066       free_jcr(mjcr);
1067       return 0;
1068    }
1069    if (verbose) {
1070       Pmsg2(000, _("Updated Job termination record for JobId=%u TermStat=%c\n"), jr->JobId,
1071          jr->JobStatus);
1072    }
1073    if (verbose > 1) {
1074       const char *term_msg;
1075       static char term_code[70];
1076       char sdt[50], edt[50];
1077       char ec1[30], ec2[30], ec3[30];
1078
1079       switch (mjcr->JobStatus) {
1080       case JS_Terminated:
1081          term_msg = _("Backup OK");
1082          break;
1083       case JS_FatalError:
1084       case JS_ErrorTerminated:
1085          term_msg = _("*** Backup Error ***");
1086          break;
1087       case JS_Canceled:
1088          term_msg = _("Backup Canceled");
1089          break;
1090       default:
1091          term_msg = term_code;
1092          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1093          break;
1094       }
1095       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1096       bstrftime(edt, sizeof(edt), mjcr->end_time);
1097       Pmsg14(000,  _("%s\n"
1098 "JobId:                  %d\n"
1099 "Job:                    %s\n"
1100 "FileSet:                %s\n"
1101 "Backup Level:           %s\n"
1102 "Client:                 %s\n"
1103 "Start time:             %s\n"
1104 "End time:               %s\n"
1105 "Files Written:          %s\n"
1106 "Bytes Written:          %s\n"
1107 "Volume Session Id:      %d\n"
1108 "Volume Session Time:    %d\n"
1109 "Last Volume Bytes:      %s\n"
1110 "Termination:            %s\n\n"),
1111         edt,
1112         mjcr->JobId,
1113         mjcr->Job,
1114         mjcr->fileset_name,
1115         job_level_to_str(mjcr->JobLevel),
1116         mjcr->client_name,
1117         sdt,
1118         edt,
1119         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1120         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1121         mjcr->VolSessionId,
1122         mjcr->VolSessionTime,
1123         edit_uint64_with_commas(mr.VolBytes, ec3),
1124         term_msg);
1125    }
1126    free_jcr(mjcr);
1127    return 1;
1128 }
1129
1130 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1131 {
1132    JOBMEDIA_DBR jmr;
1133    DCR *dcr = mjcr->read_dcr;
1134
1135    if (dev->is_tape()) {
1136       dcr->EndBlock = dev->EndBlock;
1137       dcr->EndFile  = dev->EndFile;
1138 #ifdef needed
1139    } else {
1140       dcr->EndBlock = (uint32_t)dev->file_addr;
1141       dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1142 #endif
1143    } 
1144
1145    memset(&jmr, 0, sizeof(jmr));
1146    jmr.JobId = mjcr->JobId;
1147    jmr.MediaId = mr.MediaId;
1148    jmr.FirstIndex = dcr->VolFirstIndex;
1149    jmr.LastIndex = dcr->VolLastIndex;
1150    jmr.StartFile = dcr->StartFile;
1151    jmr.EndFile = dcr->EndFile;
1152    jmr.StartBlock = dcr->StartBlock;
1153    jmr.EndBlock = dcr->EndBlock;
1154
1155
1156    if (!update_db) {
1157       return 1;
1158    }
1159
1160    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1161       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1162       return 0;
1163    }
1164    if (verbose) {
1165       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1166                 jmr.JobId, jmr.MediaId);
1167    }
1168    return 1;
1169 }
1170
1171 /*
1172  * Simulate the database call that updates the MD5/SHA1 record
1173  */
1174 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1175 {
1176    JCR *mjcr;
1177
1178    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1179    if (!mjcr) {
1180       if (mr.VolJobs > 0) {
1181          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1182                       rec->VolSessionId, rec->VolSessionTime);
1183       } else {
1184          ignored_msgs++;
1185       }
1186       return 0;
1187    }
1188
1189    if (!update_db || mjcr->FileId == 0) {
1190       free_jcr(mjcr);
1191       return 1;
1192    }
1193
1194    if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1195       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1196       free_jcr(mjcr);
1197       return 0;
1198    }
1199    if (verbose > 1) {
1200       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1201    }
1202    free_jcr(mjcr);
1203    return 1;
1204 }
1205
1206
1207 /*
1208  * Create a JCR as if we are really starting the job
1209  */
1210 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1211 {
1212    JCR *jobjcr;
1213    /*
1214     * Transfer as much as possible to the Job JCR. Most important is
1215     *   the JobId and the ClientId.
1216     */
1217    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1218    jobjcr->JobType = jr->JobType;
1219    jobjcr->JobLevel = jr->JobLevel;
1220    jobjcr->JobStatus = jr->JobStatus;
1221    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1222    jobjcr->JobId = JobId;      /* this is JobId on tape */
1223    jobjcr->sched_time = jr->SchedTime;
1224    jobjcr->start_time = jr->StartTime;
1225    jobjcr->VolSessionId = rec->VolSessionId;
1226    jobjcr->VolSessionTime = rec->VolSessionTime;
1227    jobjcr->ClientId = jr->ClientId;
1228    jobjcr->read_dcr = new_dcr(jobjcr, dev);
1229
1230    return jobjcr;
1231 }
1232
1233 /* Dummies to replace askdir.c */
1234 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
1235 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1236 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1237 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
1238 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1239 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1240 bool    dir_send_job_status(JCR *jcr) {return 1;}
1241 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1242 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
1243 bool    free_volume(DEVICE *dev) { return true; }
1244 void    free_unused_volume(DCR *dcr) { }
1245
1246 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1247 {
1248    DEVICE *dev = dcr->dev;
1249    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1250    /* Close device so user can use autochanger if desired */
1251    if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
1252       offline_dev(dev);
1253    }
1254    force_close_device(dev);
1255    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1256          dcr->VolumeName, dev->print_name());
1257    getchar();
1258    return true;
1259 }