]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
717cecae8bfe4b5a34a3fa85b5016e7a0b4e4436
[bacula/bacula] / bacula / src / stored / bextract.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *  Dumb program to extract files from a Bacula backup.
22  *
23  *   Kern E. Sibbald, MM
24  *
25  */
26
27 #include "bacula.h"
28 #include "stored.h"
29 #include "ch.h"
30 #include "findlib/find.h"
31
32 #ifdef HAVE_LZO
33 #include <lzo/lzoconf.h>
34 #include <lzo/lzo1x.h>
35 #endif
36
37 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
38
39 static void do_extract(char *fname);
40 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
41
42 static DEVICE *dev = NULL;
43 static DCR *dcr;
44 static BFILE bfd;
45 static JCR *jcr;
46 static FF_PKT *ff;
47 static BSR *bsr = NULL;
48 static bool extract = false;
49 static int non_support_data = 0;
50 static long total = 0;
51 static ATTR *attr;
52 static POOLMEM *curr_fname;
53 static char *where;
54 static uint64_t num_errors = 0;
55 static uint64_t num_records = 0;
56 static uint32_t num_files = 0;
57 static uint32_t compress_buf_size = 70000;
58 static POOLMEM *compress_buf;
59 static int prog_name_msg = 0;
60 static int win32_data_msg = 0;
61 static char *VolumeName = NULL;
62
63 static char *wbuf;                    /* write buffer address */
64 static uint32_t wsize;                /* write size */
65 static uint64_t fileAddr = 0;         /* file write address */
66
67 static CONFIG *config;
68 #define CONFIG_FILE "bacula-sd.conf"
69
70 void *start_heap;
71 char *configfile = NULL;
72 STORES *me = NULL;                    /* our Global resource */
73 bool forge_on = false;
74 bool skip_extract = false;
75 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
76 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
77
78 static void usage()
79 {
80    fprintf(stderr, _(
81 PROG_COPYRIGHT
82 "\n%sVersion: %s (%s)\n\n"
83 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
84 "       -b <file>       specify a bootstrap file\n"
85 "       -c <file>       specify a Storage configuration file\n"
86 "       -d <nn>         set debug level to <nn>\n"
87 "       -dt             print timestamp in debug output\n"
88 "       -T              send debug traces to trace file (stored in /tmp)\n"
89 "       -e <file>       exclude list\n"
90 "       -i <file>       include list\n"
91 "       -p              proceed inspite of I/O errors\n"
92 "       -t              read data from volume, do not write anything\n"
93 "       -v              verbose\n"
94 "       -V <volumes>    specify Volume names (separated by |)\n"
95 "       -?              print this message\n\n"), 2000, "", VERSION, BDATE);
96    exit(1);
97 }
98
99
100 int main (int argc, char *argv[])
101 {
102    int ch;
103    FILE *fd;
104    char line[1000];
105    bool got_inc = false;
106
107    setlocale(LC_ALL, "");
108    bindtextdomain("bacula", LOCALEDIR);
109    textdomain("bacula");
110    init_stack_dump();
111    lmgr_init_thread();
112
113    working_directory = "/tmp";
114    my_name_is(argc, argv, "bextract");
115    init_msg(NULL, NULL);              /* setup message handler */
116
117    OSDependentInit();
118
119    ff = init_find_files();
120    binit(&bfd);
121
122    while ((ch = getopt(argc, argv, "Ttb:c:d:e:i:pvV:?")) != -1) {
123       switch (ch) {
124       case 't':
125          skip_extract = true;
126          break;
127
128       case 'b':                    /* bootstrap file */
129          bsr = parse_bsr(NULL, optarg);
130 //       dump_bsr(bsr, true);
131          break;
132
133       case 'T':                 /* Send debug to trace file */
134          set_trace(1);
135          break;
136
137       case 'c':                    /* specify config file */
138          if (configfile != NULL) {
139             free(configfile);
140          }
141          configfile = bstrdup(optarg);
142          break;
143
144       case 'd':                    /* debug level */
145          if (*optarg == 't') {
146             dbg_timestamp = true;
147          } else {
148             debug_level = atoi(optarg);
149             if (debug_level <= 0) {
150                debug_level = 1;
151             }
152          }
153          break;
154
155       case 'e':                    /* exclude list */
156          if ((fd = fopen(optarg, "rb")) == NULL) {
157             berrno be;
158             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
159                optarg, be.bstrerror());
160             exit(1);
161          }
162          while (fgets(line, sizeof(line), fd) != NULL) {
163             strip_trailing_junk(line);
164             if (line[0] == 0) { /* skip blank lines */
165                continue;
166             }
167             Dmsg1(900, "add_exclude %s\n", line);
168             add_fname_to_exclude_list(ff, line);
169          }
170          fclose(fd);
171          break;
172
173       case 'i':                    /* include list */
174          if ((fd = fopen(optarg, "rb")) == NULL) {
175             berrno be;
176             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
177                optarg, be.bstrerror());
178             exit(1);
179          }
180          while (fgets(line, sizeof(line), fd) != NULL) {
181             strip_trailing_junk(line);
182             if (line[0] == 0) { /* skip blank lines */
183                continue;
184             }
185             Dmsg1(900, "add_include %s\n", line);
186             add_fname_to_include_list(ff, 0, line);
187          }
188          fclose(fd);
189          got_inc = true;
190          break;
191
192       case 'p':
193          forge_on = true;
194          break;
195
196       case 'v':
197          verbose++;
198          break;
199
200       case 'V':                    /* Volume name */
201          VolumeName = optarg;
202          break;
203
204       case '?':
205       default:
206          usage();
207
208       } /* end switch */
209    } /* end while */
210    argc -= optind;
211    argv += optind;
212
213    if (argc != 2) {
214       Pmsg0(0, _("Wrong number of arguments: \n"));
215       usage();
216    }
217
218    if (configfile == NULL) {
219       configfile = bstrdup(CONFIG_FILE);
220    }
221
222    config = new_config_parser();
223    parse_sd_config(config, configfile, M_ERROR_TERM);
224    setup_me();
225    load_sd_plugins(me->plugin_directory);
226
227    if (!got_inc) {                            /* If no include file, */
228       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
229    }
230
231    where = argv[1];
232    do_extract(argv[0]);
233
234    if (bsr) {
235       free_bsr(bsr);
236    }
237    if (prog_name_msg) {
238       Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
239          prog_name_msg);
240    }
241    if (win32_data_msg) {
242       Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
243          win32_data_msg);
244    }
245    term_include_exclude_files(ff);
246    term_find_files(ff);
247    return 0;
248 }
249
250 static void do_extract(char *devname)
251 {
252    char ed1[50];
253    struct stat statp;
254
255    enable_backup_privileges(NULL, 1);
256
257    jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ, false/*read dedup data*/);
258    if (!jcr) {
259       exit(1);
260    }
261    dev = jcr->read_dcr->dev;
262    if (!dev) {
263       exit(1);
264    }
265    dcr = jcr->read_dcr;
266
267    /* Make sure where directory exists and that it is a directory */
268    if (stat(where, &statp) < 0) {
269       berrno be;
270       Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
271          where, be.bstrerror());
272    }
273    if (!S_ISDIR(statp.st_mode)) {
274       Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
275    }
276
277    free(jcr->where);
278    jcr->where = bstrdup(where);
279    attr = new_attr(jcr);
280
281    compress_buf = get_memory(compress_buf_size);
282    curr_fname = get_pool_memory(PM_FNAME);
283    *curr_fname = 0;
284
285    read_records(dcr, record_cb, mount_next_read_volume);
286    /* If output file is still open, it was the last one in the
287     * archive since we just hit an end of file, so close the file.
288     */
289    if (is_bopen(&bfd)) {
290       set_attributes(jcr, attr, &bfd);
291    }
292    release_device(dcr);
293    free_attr(attr);
294    free_jcr(jcr);
295    dev->term();
296    free_pool_memory(curr_fname);
297
298    printf(_("%u files restored.\n"), num_files);
299    if (num_errors) {
300       printf(_("Found %s error%s\n"), edit_uint64(num_errors, ed1), num_errors>1? "s":"");
301    }
302    return;
303 }
304
305 static bool store_data(BFILE *bfd, char *data, const int32_t length)
306 {
307    if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
308       set_portable_backup(bfd);
309       if (!processWin32BackupAPIBlock(bfd, data, length)) {
310          berrno be;
311          Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
312                attr->ofname, be.bstrerror());
313          return false;
314       }
315    } else if (bwrite(bfd, data, length) != (ssize_t)length) {
316       berrno be;
317       Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
318             attr->ofname, be.bstrerror());
319       return false;
320    }
321
322    return true;
323 }
324
325 /*
326  * Called here for each record from read_records()
327  */
328 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
329 {
330    int stat, ret=true;
331    JCR *jcr = dcr->jcr;
332    char ed1[50];
333
334    bool     restoredatap = false;
335    POOLMEM *orgdata = NULL;
336    uint32_t orgdata_len = 0;
337
338    if (rec->FileIndex < 0) {
339       return true;                    /* we don't want labels */
340    }
341
342    /* In this mode, we do not create any file on disk, just read
343     * everything from the volume.
344     */
345    if (skip_extract) {
346       switch (rec->maskedStream) {
347       case STREAM_UNIX_ATTRIBUTES:
348       case STREAM_UNIX_ATTRIBUTES_EX:
349          if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
350             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
351          }
352          if (verbose) {
353             attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
354             build_attr_output_fnames(jcr, attr);
355             print_ls_output(jcr, attr);
356          }
357          pm_strcpy(curr_fname, attr->fname);
358          num_files++;
359          break;
360       }
361       num_records++;
362
363       /* We display some progress information if verbose not set or set to 2 */
364       if (verbose != 1 && (num_records % 200000) == 0L) {
365          fprintf(stderr, "\rfiles=%d records=%s\n", num_files, edit_uint64(num_records, ed1));
366       }
367       ret = true;
368       goto bail_out;
369    }
370
371    /* File Attributes stream */
372
373    switch (rec->maskedStream) {
374    case STREAM_UNIX_ATTRIBUTES:
375    case STREAM_UNIX_ATTRIBUTES_EX:
376
377       /* If extracting, it was from previous stream, so
378        * close the output file.
379        */
380       if (extract) {
381          if (!is_bopen(&bfd)) {
382             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
383          }
384          set_attributes(jcr, attr, &bfd);
385          extract = false;
386       }
387
388       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
389          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
390       }
391
392       /* Keep the name of the current file if we find a bad block */
393       pm_strcpy(curr_fname, attr->fname);
394
395       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
396          attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
397          if (!is_restore_stream_supported(attr->data_stream)) {
398             if (!non_support_data++) {
399                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
400                   stream_to_ascii(attr->data_stream));
401             }
402             extract = false;
403             goto bail_out;
404          }
405
406          build_attr_output_fnames(jcr, attr);
407
408          if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
409             Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
410             extract = false;
411             goto bail_out;
412          }
413
414          extract = false;
415          stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
416
417          switch (stat) {
418          case CF_ERROR:
419          case CF_SKIP:
420             break;
421          case CF_EXTRACT:
422             extract = true;
423             print_ls_output(jcr, attr);
424             num_files++;
425             fileAddr = 0;
426             break;
427          case CF_CREATED:
428             set_attributes(jcr, attr, &bfd);
429             print_ls_output(jcr, attr);
430             num_files++;
431             fileAddr = 0;
432             break;
433          }
434       }
435       break;
436
437    case STREAM_RESTORE_OBJECT:
438       /* nothing to do */
439       break;
440
441    /* Data stream and extracting */
442    case STREAM_FILE_DATA:
443    case STREAM_SPARSE_DATA:
444    case STREAM_WIN32_DATA:
445
446       if (extract) {
447          if (rec->maskedStream == STREAM_SPARSE_DATA) {
448             ser_declare;
449             uint64_t faddr;
450             wbuf = rec->data + OFFSET_FADDR_SIZE;
451             wsize = rec->data_len - OFFSET_FADDR_SIZE;
452             ser_begin(rec->data, OFFSET_FADDR_SIZE);
453             unser_uint64(faddr);
454             if (fileAddr != faddr) {
455                fileAddr = faddr;
456                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
457                   berrno be;
458                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
459                      attr->ofname, be.bstrerror());
460                }
461             }
462          } else {
463             wbuf = rec->data;
464             wsize = rec->data_len;
465          }
466          total += wsize;
467          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
468          store_data(&bfd, wbuf, wsize);
469          fileAddr += wsize;
470       }
471       break;
472
473    /* GZIP data stream */
474    case STREAM_GZIP_DATA:
475    case STREAM_SPARSE_GZIP_DATA:
476    case STREAM_WIN32_GZIP_DATA:
477 #ifdef HAVE_LIBZ
478       if (extract) {
479          uLong compress_len = compress_buf_size;
480          int stat = Z_BUF_ERROR;
481
482          if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
483             ser_declare;
484             uint64_t faddr;
485             char ec1[50];
486             wbuf = rec->data + OFFSET_FADDR_SIZE;
487             wsize = rec->data_len - OFFSET_FADDR_SIZE;
488             ser_begin(rec->data, OFFSET_FADDR_SIZE);
489             unser_uint64(faddr);
490             if (fileAddr != faddr) {
491                fileAddr = faddr;
492                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
493                   berrno be;
494                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
495                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
496                   extract = false;
497                   goto bail_out;
498                }
499             }
500          } else {
501             wbuf = rec->data;
502             wsize = rec->data_len;
503          }
504
505          while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
506                                  (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
507             /* The buffer size is too small, try with a bigger one */
508             compress_len = 2 * compress_len;
509             compress_buf = check_pool_memory_size(compress_buf,
510                                                   compress_len);
511          }
512          if (stat != Z_OK) {
513             Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
514             extract = false;
515             goto bail_out;
516          }
517
518          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
519          store_data(&bfd, compress_buf, compress_len);
520          total += compress_len;
521          fileAddr += compress_len;
522          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
523             compress_len);
524       }
525 #else
526       if (extract) {
527          Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
528          extract = false;
529          goto bail_out;
530       }
531 #endif
532       break;
533
534    /* Compressed data stream */
535    case STREAM_COMPRESSED_DATA:
536    case STREAM_SPARSE_COMPRESSED_DATA:
537    case STREAM_WIN32_COMPRESSED_DATA:
538       if (extract) {
539          uint32_t comp_magic, comp_len;
540          uint16_t comp_level, comp_version;
541 #ifdef HAVE_LZO
542          lzo_uint compress_len;
543          const unsigned char *cbuf;
544          int r, real_compress_len;
545 #endif
546
547          if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
548             ser_declare;
549             uint64_t faddr;
550             char ec1[50];
551             wbuf = rec->data + OFFSET_FADDR_SIZE;
552             wsize = rec->data_len - OFFSET_FADDR_SIZE;
553             ser_begin(rec->data, OFFSET_FADDR_SIZE);
554             unser_uint64(faddr);
555             if (fileAddr != faddr) {
556                fileAddr = faddr;
557                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
558                   berrno be;
559                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
560                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
561                   extract = false;
562                   goto bail_out;
563                }
564             }
565          } else {
566             wbuf = rec->data;
567             wsize = rec->data_len;
568          }
569
570          /* read compress header */
571          unser_declare;
572          unser_begin(wbuf, sizeof(comp_stream_header));
573          unser_uint32(comp_magic);
574          unser_uint32(comp_len);
575          unser_uint16(comp_level);
576          unser_uint16(comp_version);
577          Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
578                                  comp_level, comp_version);
579
580          /* version check */
581          if (comp_version != COMP_HEAD_VERSION) {
582             Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
583             ret = false;
584             goto bail_out;
585          }
586          /* size check */
587          if (comp_len + sizeof(comp_stream_header) != wsize) {
588             Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
589                  comp_len, wsize);
590             ret = false;
591             goto bail_out;
592          }
593
594           switch(comp_magic) {
595 #ifdef HAVE_LZO
596             case COMPRESS_LZO1X:
597                compress_len = compress_buf_size;
598                cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
599                real_compress_len = wsize - sizeof(comp_stream_header);
600                Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
601                while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
602                                                (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
603                {
604
605                   /* The buffer size is too small, try with a bigger one */
606                   compress_len = 2 * compress_len;
607                   compress_buf = check_pool_memory_size(compress_buf,
608                                                   compress_len);
609                }
610                if (r != LZO_E_OK) {
611                   Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
612                   extract = false;
613                   goto bail_out;
614                }
615                Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
616                store_data(&bfd, compress_buf, compress_len);
617                total += compress_len;
618                fileAddr += compress_len;
619                Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
620                break;
621 #endif
622             default:
623                Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
624                extract = false;
625                goto bail_out;
626          }
627
628       }
629       break;
630
631    case STREAM_MD5_DIGEST:
632    case STREAM_SHA1_DIGEST:
633    case STREAM_SHA256_DIGEST:
634    case STREAM_SHA512_DIGEST:
635       break;
636
637    case STREAM_SIGNED_DIGEST:
638    case STREAM_ENCRYPTED_SESSION_DATA:
639       // TODO landonf: Investigate crypto support in the storage daemon
640       break;
641
642    case STREAM_PROGRAM_NAMES:
643    case STREAM_PROGRAM_DATA:
644       if (!prog_name_msg) {
645          Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
646          prog_name_msg++;
647       }
648       break;
649
650    default:
651       /* If extracting, weird stream (not 1 or 2), close output file anyway */
652       if (extract) {
653          if (!is_bopen(&bfd)) {
654             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
655          }
656          set_attributes(jcr, attr, &bfd);
657          extract = false;
658       }
659       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
660          rec->Stream);
661       break;
662
663    } /* end switch */
664 bail_out:
665    if (restoredatap) {
666       rec->data = orgdata;
667       rec->data_len = orgdata_len;
668    }
669    return ret;
670 }
671
672 /* Dummies to replace askdir.c */
673 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
674 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
675 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
676 bool    flush_jobmedia_queue(JCR *jcr) { return true; }
677 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
678 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
679 bool    dir_send_job_status(JCR *jcr) {return 1;}
680
681
682 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
683 {
684    DEVICE *dev = dcr->dev;
685    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
686       dcr->VolumeName, dev->print_name());
687    dev->close();
688    getchar();
689    return true;
690 }
691
692 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
693 {
694    Dmsg0(100, "Fake dir_get_volume_info\n");
695    dcr->setVolCatName(dcr->VolumeName);
696    Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);
697    return 1;
698 }