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