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