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