]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
Prevent a Volume that is being swapped from being freed from
[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:rsSt:u: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, false)) {
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    if (type == FT_DELETED) {
849       ar.FileIndex = 0;
850    } else {
851       ar.FileIndex = rec->FileIndex;
852    }
853    ar.attr = ap;
854    if (dcr->VolFirstIndex == 0) {
855       dcr->VolFirstIndex = rec->FileIndex;
856    }
857    dcr->FileIndex = rec->FileIndex;
858    mjcr->JobFiles++;
859
860    if (!update_db) {
861       return 1;
862    }
863
864    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
865       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
866       return 0;
867    }
868    mjcr->FileId = ar.FileId;
869
870    if (verbose > 1) {
871       Pmsg1(000, _("Created File record: %s\n"), fname);
872    }
873    return 1;
874 }
875
876 /*
877  * For each Volume we see, we create a Medium record
878  */
879 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
880 {
881    struct date_time dt;
882    struct tm tm;
883
884    /* We mark Vols as Archive to keep them from being re-written */
885    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
886    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
887    mr->Enabled = 1;
888    if (vl->VerNum >= 11) {
889       mr->FirstWritten = btime_to_utime(vl->write_btime);
890       mr->LabelDate    = btime_to_utime(vl->label_btime);
891    } else {
892       /* DEPRECATED DO NOT USE */
893       dt.julian_day_number = vl->write_date;
894       dt.julian_day_fraction = vl->write_time;
895       tm_decode(&dt, &tm);
896       mr->FirstWritten = mktime(&tm);
897       dt.julian_day_number = vl->label_date;
898       dt.julian_day_fraction = vl->label_time;
899       tm_decode(&dt, &tm);
900       mr->LabelDate = mktime(&tm);
901    }
902    lasttime = mr->LabelDate;
903    if (mr->VolJobs == 0) {
904       mr->VolJobs = 1;
905    }
906    if (mr->VolMounts == 0) {
907       mr->VolMounts = 1;
908    }
909
910    if (!update_db) {
911       return 1;
912    }
913
914    if (!db_create_media_record(bjcr, db, mr)) {
915       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
916       return 0;
917    }
918    if (!db_update_media_record(bjcr, db, mr)) {
919       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
920       return 0;
921    }
922    if (verbose) {
923       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
924    }
925    return 1;
926
927 }
928
929 /*
930  * Called at end of media to update it
931  */
932 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
933 {
934    if (!update_db && !update_vol_info) {
935       return true;
936    }
937
938    mr->LastWritten = lasttime;
939    if (!db_update_media_record(bjcr, db, mr)) {
940       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
941       return false;;
942    }
943    if (verbose) {
944       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
945    }
946    return true;
947
948 }
949
950
951 static int create_pool_record(B_DB *db, POOL_DBR *pr)
952 {
953    pr->NumVols++;
954    pr->UseCatalog = 1;
955    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
956
957    if (!update_db) {
958       return 1;
959    }
960    if (!db_create_pool_record(bjcr, db, pr)) {
961       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
962       return 0;
963    }
964    if (verbose) {
965       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
966    }
967    return 1;
968
969 }
970
971
972 /*
973  * Called from SOS to create a client for the current Job
974  */
975 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
976 {
977    /*
978     * Note, update_db can temporarily be set false while 
979     * updating the database, so we must ensure that ClientId is non-zero.
980     */
981    if (!update_db) {
982       cr->ClientId = 0;
983       if (!db_get_client_record(bjcr, db, cr)) {
984         Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db_strerror(db));
985         return 0;
986       }
987       return 1;
988    }
989    if (!db_create_client_record(bjcr, db, cr)) {
990       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
991       return 0;
992    }
993    if (verbose) {
994       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
995    }
996    return 1;
997 }
998
999 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
1000 {
1001    if (!update_db) {
1002       return 1;
1003    }
1004    fsr->FileSetId = 0;
1005    if (fsr->MD5[0] == 0) {
1006       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
1007       fsr->MD5[1] = 0;
1008    }
1009    if (db_get_fileset_record(bjcr, db, fsr)) {
1010       if (verbose) {
1011          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1012       }
1013    } else {
1014       if (!db_create_fileset_record(bjcr, db, fsr)) {
1015          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1016             fsr->FileSet, db_strerror(db));
1017          return 0;
1018       }
1019       if (verbose) {
1020          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1021       }
1022    }
1023    return 1;
1024 }
1025
1026 /*
1027  * Simulate the two calls on the database to create
1028  *  the Job record and to update it when the Job actually
1029  *  begins running.
1030  */
1031 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
1032                              DEV_RECORD *rec)
1033 {
1034    JCR *mjcr;
1035    struct date_time dt;
1036    struct tm tm;
1037
1038    jr->JobId = label->JobId;
1039    jr->JobType = label->JobType;
1040    jr->JobLevel = label->JobLevel;
1041    jr->JobStatus = JS_Created;
1042    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1043    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1044    if (label->VerNum >= 11) {
1045       jr->SchedTime = btime_to_unix(label->write_btime);
1046    } else {
1047       dt.julian_day_number = label->write_date;
1048       dt.julian_day_fraction = label->write_time;
1049       tm_decode(&dt, &tm);
1050       jr->SchedTime = mktime(&tm);
1051    }
1052
1053    jr->StartTime = jr->SchedTime;
1054    jr->JobTDate = (utime_t)jr->SchedTime;
1055    jr->VolSessionId = rec->VolSessionId;
1056    jr->VolSessionTime = rec->VolSessionTime;
1057
1058    /* Now create a JCR as if starting the Job */
1059    mjcr = create_jcr(jr, rec, label->JobId);
1060
1061    if (!update_db) {
1062       return mjcr;
1063    }
1064
1065    /* This creates the bare essentials */
1066    if (!db_create_job_record(bjcr, db, jr)) {
1067       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1068       return mjcr;
1069    }
1070
1071    /* This adds the client, StartTime, JobTDate, ... */
1072    if (!db_update_job_start_record(bjcr, db, jr)) {
1073       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1074       return mjcr;
1075    }
1076    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1077          label->JobId);
1078    mjcr->JobId = jr->JobId;           /* set new JobId */
1079    return mjcr;
1080 }
1081
1082 /*
1083  * Simulate the database call that updates the Job
1084  *  at Job termination time.
1085  */
1086 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1087                               DEV_RECORD *rec)
1088 {
1089    struct date_time dt;
1090    struct tm tm;
1091    JCR *mjcr;
1092
1093    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1094    if (!mjcr) {
1095       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1096                    rec->VolSessionId, rec->VolSessionTime);
1097       return 0;
1098    }
1099    if (elabel->VerNum >= 11) {
1100       jr->EndTime = btime_to_unix(elabel->write_btime);
1101    } else {
1102       dt.julian_day_number = elabel->write_date;
1103       dt.julian_day_fraction = elabel->write_time;
1104       tm_decode(&dt, &tm);
1105       jr->EndTime = mktime(&tm);
1106    }
1107    lasttime = jr->EndTime;
1108    mjcr->end_time = jr->EndTime;
1109
1110    jr->JobId = mjcr->JobId;
1111    jr->JobStatus = elabel->JobStatus;
1112    mjcr->JobStatus = elabel->JobStatus;
1113    jr->JobFiles = elabel->JobFiles;
1114    jr->JobBytes = elabel->JobBytes;
1115    jr->VolSessionId = rec->VolSessionId;
1116    jr->VolSessionTime = rec->VolSessionTime;
1117    jr->JobTDate = (utime_t)mjcr->start_time;
1118    jr->ClientId = mjcr->ClientId;
1119
1120    if (!update_db) {
1121       free_jcr(mjcr);
1122       return 1;
1123    }
1124
1125    if (!db_update_job_end_record(bjcr, db, jr, false)) {
1126       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1127       free_jcr(mjcr);
1128       return 0;
1129    }
1130    if (verbose) {
1131       Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"), 
1132          jr->JobId, job_level_to_str(mjcr->JobLevel), jr->JobStatus);
1133    }
1134    if (verbose > 1) {
1135       const char *term_msg;
1136       static char term_code[70];
1137       char sdt[50], edt[50];
1138       char ec1[30], ec2[30], ec3[30];
1139
1140       switch (mjcr->JobStatus) {
1141       case JS_Terminated:
1142          term_msg = _("Backup OK");
1143          break;
1144       case JS_FatalError:
1145       case JS_ErrorTerminated:
1146          term_msg = _("*** Backup Error ***");
1147          break;
1148       case JS_Canceled:
1149          term_msg = _("Backup Canceled");
1150          break;
1151       default:
1152          term_msg = term_code;
1153          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1154          break;
1155       }
1156       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1157       bstrftime(edt, sizeof(edt), mjcr->end_time);
1158       Pmsg14(000,  _("%s\n"
1159 "JobId:                  %d\n"
1160 "Job:                    %s\n"
1161 "FileSet:                %s\n"
1162 "Backup Level:           %s\n"
1163 "Client:                 %s\n"
1164 "Start time:             %s\n"
1165 "End time:               %s\n"
1166 "Files Written:          %s\n"
1167 "Bytes Written:          %s\n"
1168 "Volume Session Id:      %d\n"
1169 "Volume Session Time:    %d\n"
1170 "Last Volume Bytes:      %s\n"
1171 "Termination:            %s\n\n"),
1172         edt,
1173         mjcr->JobId,
1174         mjcr->Job,
1175         mjcr->fileset_name,
1176         job_level_to_str(mjcr->JobLevel),
1177         mjcr->client_name,
1178         sdt,
1179         edt,
1180         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1181         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1182         mjcr->VolSessionId,
1183         mjcr->VolSessionTime,
1184         edit_uint64_with_commas(mr.VolBytes, ec3),
1185         term_msg);
1186    }
1187    free_jcr(mjcr);
1188    return 1;
1189 }
1190
1191 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1192 {
1193    JOBMEDIA_DBR jmr;
1194    DCR *dcr = mjcr->read_dcr;
1195
1196    dcr->EndBlock = dev->EndBlock;
1197    dcr->EndFile  = dev->EndFile;
1198    dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1199
1200    memset(&jmr, 0, sizeof(jmr));
1201    jmr.JobId = mjcr->JobId;
1202    jmr.MediaId = mr.MediaId;
1203    jmr.FirstIndex = dcr->VolFirstIndex;
1204    jmr.LastIndex = dcr->VolLastIndex;
1205    jmr.StartFile = dcr->StartFile;
1206    jmr.EndFile = dcr->EndFile;
1207    jmr.StartBlock = dcr->StartBlock;
1208    jmr.EndBlock = dcr->EndBlock;
1209
1210
1211    if (!update_db) {
1212       return 1;
1213    }
1214
1215    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1216       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1217       return 0;
1218    }
1219    if (verbose) {
1220       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1221                 jmr.JobId, jmr.MediaId);
1222    }
1223    return 1;
1224 }
1225
1226 /*
1227  * Simulate the database call that updates the MD5/SHA1 record
1228  */
1229 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1230 {
1231    JCR *mjcr;
1232
1233    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1234    if (!mjcr) {
1235       if (mr.VolJobs > 0) {
1236          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1237                       rec->VolSessionId, rec->VolSessionTime);
1238       } else {
1239          ignored_msgs++;
1240       }
1241       return 0;
1242    }
1243
1244    if (!update_db || mjcr->FileId == 0) {
1245       free_jcr(mjcr);
1246       return 1;
1247    }
1248
1249    if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1250       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1251       free_jcr(mjcr);
1252       return 0;
1253    }
1254    if (verbose > 1) {
1255       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1256    }
1257    free_jcr(mjcr);
1258    return 1;
1259 }
1260
1261
1262 /*
1263  * Create a JCR as if we are really starting the job
1264  */
1265 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1266 {
1267    JCR *jobjcr;
1268    /*
1269     * Transfer as much as possible to the Job JCR. Most important is
1270     *   the JobId and the ClientId.
1271     */
1272    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1273    jobjcr->JobType = jr->JobType;
1274    jobjcr->JobLevel = jr->JobLevel;
1275    jobjcr->JobStatus = jr->JobStatus;
1276    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1277    jobjcr->JobId = JobId;      /* this is JobId on tape */
1278    jobjcr->sched_time = jr->SchedTime;
1279    jobjcr->start_time = jr->StartTime;
1280    jobjcr->VolSessionId = rec->VolSessionId;
1281    jobjcr->VolSessionTime = rec->VolSessionTime;
1282    jobjcr->ClientId = jr->ClientId;
1283    jobjcr->dcr = jobjcr->read_dcr = new_dcr(jobjcr, NULL, dev);
1284
1285    return jobjcr;
1286 }
1287
1288 /* Dummies to replace askdir.c */
1289 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1290 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
1291 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
1292 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1293 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1294 bool    dir_send_job_status(JCR *jcr) {return 1;}
1295 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1296
1297 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
1298 {
1299    DEVICE *dev = dcr->dev;
1300    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1301    /* Close device so user can use autochanger if desired */
1302    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1303          dcr->VolumeName, dev->print_name());
1304    dev->close();
1305    getchar();
1306    return true;
1307 }
1308
1309 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
1310 {
1311    Dmsg0(100, "Fake dir_get_volume_info\n");
1312    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
1313    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
1314    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
1315    return 1;
1316 }