]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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 do an "ls" of a Bacula 1.0 mortal file.
22  *
23  *  Kern Sibbald, MM
24  */
25
26 #include "bacula.h"
27 #include "stored.h"
28 #include "findlib/find.h"
29 #include "lib/cmd_parser.h"
30
31 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
32
33 static void do_blocks(char *infname);
34 static void do_jobs(char *infname);
35 static void do_ls(char *fname);
36 static void do_close(JCR *jcr);
37 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
38 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
39
40 static DEVICE *dev;
41 static DCR *dcr;
42 static bool dump_label = false;
43 static bool list_blocks = false;
44 static bool list_jobs = false;
45 static DEV_RECORD *rec;
46 static JCR *jcr;
47 static SESSION_LABEL sessrec;
48 static uint32_t num_files = 0;
49 static ATTR *attr;
50 static CONFIG *config;
51
52 void *start_heap;
53 #define CONFIG_FILE "bacula-sd.conf"
54 char *configfile = NULL;
55 bool detect_errors = false;
56 int  errors = 0;
57
58 static FF_PKT *ff;
59
60 static BSR *bsr = NULL;
61
62 static void usage()
63 {
64    fprintf(stderr, _(
65 PROG_COPYRIGHT
66 "\n%sVersion: %s (%s)\n\n"
67 "Usage: bls [options] <device-name>\n"
68 "     -b <file>          specify a bootstrap file\n"
69 "     -c <file>          specify a Storage configuration file\n"
70 "     -d <nn>            set debug level to <nn>\n"
71 "     -dt                print timestamp in debug output\n"
72 "     -e <file>          exclude list\n"
73 "     -i <file>          include list\n"
74 "     -j                 list jobs\n"
75 "     -k                 list blocks\n"
76 "  (no j or k option)    list saved files\n"
77 "     -L                 dump label\n"
78 "     -p                 proceed inspite of errors\n"
79 "     -V                 specify Volume names (separated by |)\n"
80 "     -E                 Check records to detect errors\n"
81 "     -v                 be verbose\n"
82 "     -?                 print this message\n\n"), 2000, "", VERSION, BDATE);
83    exit(1);
84 }
85
86
87 int main (int argc, char *argv[])
88 {
89    int i, ch;
90    FILE *fd;
91    char line[1000];
92    char *VolumeName= NULL;
93    char *bsrName = NULL;
94    bool ignore_label_errors = false;
95    BtoolsAskDirHandler askdir_handler;
96
97    init_askdir_handler(&askdir_handler);
98    setlocale(LC_ALL, "");
99    bindtextdomain("bacula", LOCALEDIR);
100    textdomain("bacula");
101    init_stack_dump();
102    lmgr_init_thread();
103
104    working_directory = "/tmp";
105    my_name_is(argc, argv, "bls");
106    init_msg(NULL, NULL);              /* initialize message handler */
107
108    OSDependentInit();
109
110    ff = init_find_files();
111
112    while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?EDF:")) != -1) {
113       switch (ch) {
114       case 'b':
115          bsrName = optarg;
116          break;
117
118       case 'E':
119          detect_errors = true;
120          break;
121
122       case 'c':                    /* specify config file */
123          if (configfile != NULL) {
124             free(configfile);
125          }
126          configfile = bstrdup(optarg);
127          break;
128
129       case 'd':                    /* debug level */
130          if (*optarg == 't') {
131             dbg_timestamp = true;
132          } else {
133             char *p;
134             /* We probably find a tag list -d 10,sql,bvfs */
135             if ((p = strchr(optarg, ',')) != NULL) {
136                *p = 0;
137             }
138             debug_level = atoi(optarg);
139             if (debug_level <= 0) {
140                debug_level = 1;
141             }
142             if (p) {
143                debug_parse_tags(p+1, &debug_level_tags);
144             }
145          }
146          break;
147
148       case 'e':                    /* exclude list */
149          if ((fd = fopen(optarg, "rb")) == NULL) {
150             berrno be;
151             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
152                optarg, be.bstrerror());
153             exit(1);
154          }
155          while (fgets(line, sizeof(line), fd) != NULL) {
156             strip_trailing_junk(line);
157             Dmsg1(100, "add_exclude %s\n", line);
158             add_fname_to_exclude_list(ff, line);
159          }
160          fclose(fd);
161          break;
162
163       case 'i':                    /* include list */
164          if ((fd = fopen(optarg, "rb")) == NULL) {
165             berrno be;
166             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
167                optarg, be.bstrerror());
168             exit(1);
169          }
170          while (fgets(line, sizeof(line), fd) != NULL) {
171             strip_trailing_junk(line);
172             Dmsg1(100, "add_include %s\n", line);
173             add_fname_to_include_list(ff, 0, line);
174          }
175          fclose(fd);
176          break;
177
178       case 'j':
179          list_jobs = true;
180          break;
181
182       case 'k':
183          list_blocks = true;
184          break;
185
186       case 'L':
187          dump_label = true;
188          break;
189
190       case 'p':
191          ignore_label_errors = true;
192          forge_on = true;
193          break;
194
195       case 'v':
196          verbose++;
197          break;
198
199       case 'V':                    /* Volume name */
200          VolumeName = optarg;
201          break;
202
203       case '?':
204       default:
205          usage();
206
207       } /* end switch */
208    } /* end while */
209    argc -= optind;
210    argv += optind;
211
212    if (!argc) {
213       Pmsg0(0, _("No archive name specified\n"));
214       usage();
215    }
216
217    if (configfile == NULL) {
218       configfile = bstrdup(CONFIG_FILE);
219    }
220
221    config = New(CONFIG());
222    parse_sd_config(config, configfile, M_ERROR_TERM);
223    setup_me();
224    load_sd_plugins(me->plugin_directory);
225
226    if (ff->included_files_list == NULL) {
227       add_fname_to_include_list(ff, 0, "/");
228    }
229
230    for (i=0; i < argc; i++) {
231       if (bsrName) {
232          bsr = parse_bsr(NULL, bsrName);
233       }
234       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, SD_READ);
235       if (!jcr) {
236          exit(1);
237       }
238       jcr->ignore_label_errors = ignore_label_errors;
239       dev = jcr->dcr->dev;
240       if (!dev) {
241          exit(1);
242       }
243       dcr = jcr->dcr;
244       rec = new_record();
245       attr = new_attr(jcr);
246       /*
247        * Assume that we have already read the volume label.
248        * If on second or subsequent volume, adjust buffer pointer
249        */
250       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
251          Pmsg1(0, _("\n"
252                     "Warning, this Volume is a continuation of Volume %s\n"),
253                 dev->VolHdr.PrevVolumeName);
254       }
255
256       if (list_blocks) {
257          do_blocks(argv[i]);
258       } else if (list_jobs) {
259          do_jobs(argv[i]);
260       } else {
261          do_ls(argv[i]);
262       }
263       do_close(jcr);
264    }
265    if (bsr) {
266       free_bsr(bsr);
267    }
268    term_include_exclude_files(ff);
269    term_find_files(ff);
270
271    if (detect_errors) {
272       return (errors > 0)? 1 : 0;
273    }
274    return 0;
275 }
276
277 static void do_close(JCR *jcr)
278 {
279    release_device(jcr->dcr);
280    free_attr(attr);
281    free_record(rec);
282    free_jcr(jcr);
283    dev->term(NULL);
284 }
285
286
287 /* List just block information */
288 static void do_blocks(char *infname)
289 {
290    DEV_BLOCK *block = dcr->block;
291    char buf1[100], buf2[100];
292    for ( ;; ) {
293       if (!dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK)) {
294          Dmsg1(100, "!read_block(): ERR=%s\n", dev->print_errmsg());
295          if (dev->at_eot()) {
296             if (!mount_next_read_volume(dcr)) {
297                Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
298                   dev->file, dev->print_name(), dcr->VolumeName);
299                break;
300             }
301             /* Read and discard Volume label */
302             DEV_RECORD *record;
303             record = new_record();
304             dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
305             read_record_from_block(dcr, record);
306             get_session_record(dev, record, &sessrec);
307             free_record(record);
308             Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
309          } else if (dev->at_eof()) {
310             Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
311                dev->file, dev->print_name(), dcr->VolumeName);
312             Dmsg0(20, "read_record got eof. try again\n");
313             continue;
314          } else if (dev->is_short_block()) {
315             Jmsg(jcr, M_INFO, 0, "%s", dev->print_errmsg());
316             continue;
317          } else {
318             /* I/O error */
319             errors++;
320             display_tape_error_status(jcr, dev);
321             break;
322          }
323       }
324       if (!match_bsr_block(bsr, block)) {
325          Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
326             block->BlockNumber, block->block_len, block->BlockVer,
327             block->VolSessionId, block->VolSessionTime);
328          continue;
329       }
330       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
331         block->BlockNumber, block->block_len, block->BlockVer,
332         block->VolSessionId, block->VolSessionTime);
333       if (verbose == 1) {
334          read_record_from_block(dcr, rec);
335          Pmsg8(-1, "Addr=%llu blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n",
336               dev->get_full_addr(),
337               block->BlockNumber, block->block_len,
338               FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
339               stream_to_ascii_ex(buf2, rec->Stream, rec->FileIndex), rec->data_len);
340          rec->remainder = 0;
341       } else if (verbose > 1) {   /* detailed block dump */
342          Pmsg5(-1, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
343            block->BlockNumber, block->block_len, block->BlockVer,
344            block->VolSessionId, block->VolSessionTime);
345          dump_block(dcr->dev, block, "", true);
346       } else {
347          printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
348       }
349
350    }
351    return;
352 }
353
354 /*
355  * We are only looking for labels or in particular Job Session records
356  */
357 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
358 {
359    if (rec->FileIndex < 0) {
360       dump_label_record(dcr->dev, rec, verbose, detect_errors);
361    }
362    rec->remainder = 0;
363    return true;
364 }
365
366 /* Do list job records */
367 static void do_jobs(char *infname)
368 {
369    if (!read_records(dcr, jobs_cb, mount_next_read_volume)) {
370       errors++;
371    }
372 }
373
374 /* Do an ls type listing of an archive */
375 static void do_ls(char *infname)
376 {
377    if (dump_label) {
378       dev->dump_volume_label();
379       return;
380    }
381    if (!read_records(dcr, record_cb, mount_next_read_volume)) {
382       errors++;
383    }
384    printf("%u files found.\n", num_files);
385 }
386
387
388 /*
389  * Called here for each record from read_records()
390  */
391 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
392 {
393    if (verbose && rec->FileIndex < 0) {
394       dump_label_record(dcr->dev, rec, verbose, false);
395       return true;
396    }
397
398    /* File Attributes stream */
399    if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
400        rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
401       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
402          if (!forge_on) {
403             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
404          } else {
405             Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
406          }
407          num_files++;
408          return true;
409       }
410
411       attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
412       build_attr_output_fnames(jcr, attr);
413
414       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
415          if (verbose) {
416             Pmsg5(000, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
417                   rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
418          }
419          print_ls_output(jcr, attr);
420          num_files++;
421       }
422    } else if (rec->maskedStream == STREAM_PLUGIN_NAME) {
423       char data[100];
424       int len = MIN(rec->data_len+1, sizeof(data));
425       bstrncpy(data, rec->data, len);
426       Dmsg1(100, "Plugin data: %s\n", data);
427    } else if (rec->maskedStream == STREAM_RESTORE_OBJECT) {
428       Dmsg0(100, "Restore Object record\n");
429    } else if (rec->maskedStream == STREAM_ADATA_BLOCK_HEADER) {
430       Dmsg0(000, "Adata block header\n");
431    } else if (rec->maskedStream == STREAM_ADATA_RECORD_HEADER) {
432       Dmsg0(000, "Adata record header\n");
433    }
434
435    return true;
436 }
437
438
439 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
440 {
441    const char *rtype;
442    memset(sessrec, 0, sizeof(SESSION_LABEL));
443    jcr->JobId = 0;
444    switch (rec->FileIndex) {
445    case PRE_LABEL:
446       rtype = _("Fresh Volume Label");
447       break;
448    case VOL_LABEL:
449       rtype = _("Volume Label");
450       unser_volume_label(dev, rec);
451       break;
452    case SOS_LABEL:
453       rtype = _("Begin Job Session");
454       unser_session_label(sessrec, rec);
455       jcr->JobId = sessrec->JobId;
456       break;
457    case EOS_LABEL:
458       rtype = _("End Job Session");
459       break;
460    case 0:
461    case EOM_LABEL:
462       rtype = _("End of Medium");
463       break;
464    case EOT_LABEL:
465       rtype = _("End of Physical Medium");
466       break;
467    case SOB_LABEL:
468       rtype = _("Start of object");
469       break;
470    case EOB_LABEL:
471       rtype = _("End of object");
472       break;
473    default:
474       rtype = _("Unknown");
475       Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);
476       break;
477    }
478    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
479          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
480    if (verbose) {
481       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
482             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
483    }
484 }