1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero 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    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero 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 /*
24  * Kern E. Sibbald, MM
25  */
26 /**
27  * @file
28  * Dumb program to extract files from a Bareos backup.
29  */
30 
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "lib/crypto_cache.h"
35 #include "stored/acquire.h"
36 #include "stored/butil.h"
37 #include "stored/device_control_record.h"
38 #include "stored/jcr_private.h"
39 #include "stored/mount.h"
40 #include "stored/read_record.h"
41 #include "findlib/find.h"
42 #include "findlib/attribs.h"
43 #include "findlib/create_file.h"
44 #include "findlib/match.h"
45 #include "findlib/get_priv.h"
46 #include "lib/address_conf.h"
47 #include "lib/attribs.h"
48 #include "lib/berrno.h"
49 #include "lib/edit.h"
50 #include "lib/bsignal.h"
51 #include "lib/parse_bsr.h"
52 #include "lib/parse_conf.h"
53 #include "include/jcr.h"
54 
55 namespace storagedaemon {
56 extern bool ParseSdConfig(const char* configfile, int exit_code);
57 }
58 
59 using namespace storagedaemon;
60 
61 static void DoExtract(char* devname);
62 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec);
63 
64 static Device* dev = NULL;
65 static DeviceControlRecord* dcr;
66 static BareosWinFilePacket bfd;
67 static JobControlRecord* jcr;
68 static FindFilesPacket* ff;
69 static BootStrapRecord* bsr = NULL;
70 static bool extract = false;
71 static int non_support_data = 0;
72 static long total = 0;
73 static Attributes* attr;
74 static char* where;
75 static uint32_t num_files = 0;
76 static int prog_name_msg = 0;
77 static int win32_data_msg = 0;
78 static char* VolumeName = NULL;
79 static char* DirectorName = NULL;
80 static DirectorResource* director = NULL;
81 
82 static AclData acl_data;
83 static XattrData xattr_data;
84 static alist* delayed_streams = NULL;
85 
86 static char* wbuf;            /* write buffer address */
87 static uint32_t wsize;        /* write size */
88 static uint64_t fileAddr = 0; /* file write address */
89 
usage()90 static void usage()
91 {
92   kBareosVersionStrings.PrintCopyrightWithFsfAndPlanets(stderr, 2000);
93   fprintf(stderr,
94           _("Usage: bextract <options> <bareos-archive-device-name> "
95             "<directory-to-store-files>\n"
96             "       -b <file>       specify a bootstrap file\n"
97             "       -c <path>       specify a Storage configuration file or "
98             "directory\n"
99             "       -D <director>   specify a director name specified in the "
100             "Storage\n"
101             "                       configuration file for the Key Encryption "
102             "Key selection\n"
103             "       -d <nn>         set debug level to <nn>\n"
104             "       -dt             print timestamp in debug output\n"
105             "       -e <file>       exclude list\n"
106             "       -i <file>       include list\n"
107             "       -p              proceed inspite of I/O errors\n"
108             "       -v              verbose\n"
109             "       -V <volumes>    specify Volume names (separated by |)\n"
110             "       -?              print this message\n\n"));
111   exit(1);
112 }
113 
main(int argc,char * argv[])114 int main(int argc, char* argv[])
115 {
116   int ch;
117   FILE* fd;
118   char line[1000];
119   bool got_inc = false;
120 
121   setlocale(LC_ALL, "");
122   tzset();
123   bindtextdomain("bareos", LOCALEDIR);
124   textdomain("bareos");
125   InitStackDump();
126 
127   working_directory = "/tmp";
128   MyNameIs(argc, argv, "bextract");
129   InitMsg(NULL, NULL); /* setup message handler */
130 
131   OSDependentInit();
132 
133   ff = init_find_files();
134   binit(&bfd);
135 
136   while ((ch = getopt(argc, argv, "b:c:D:d:e:i:pvV:?")) != -1) {
137     switch (ch) {
138       case 'b': /* bootstrap file */
139         bsr = libbareos::parse_bsr(NULL, optarg);
140         //       DumpBsr(bsr, true);
141         break;
142 
143       case 'c': /* specify config file */
144         if (configfile != NULL) { free(configfile); }
145         configfile = strdup(optarg);
146         break;
147 
148       case 'D': /* specify director name */
149         if (DirectorName != NULL) { free(DirectorName); }
150         DirectorName = strdup(optarg);
151         break;
152 
153       case 'd': /* debug level */
154         if (*optarg == 't') {
155           dbg_timestamp = true;
156         } else {
157           debug_level = atoi(optarg);
158           if (debug_level <= 0) { debug_level = 1; }
159         }
160         break;
161 
162       case 'e': /* exclude list */
163         if ((fd = fopen(optarg, "rb")) == NULL) {
164           BErrNo be;
165           Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"), optarg,
166                 be.bstrerror());
167           exit(1);
168         }
169         while (fgets(line, sizeof(line), fd) != NULL) {
170           StripTrailingJunk(line);
171           Dmsg1(900, "add_exclude %s\n", line);
172           AddFnameToExcludeList(ff, line);
173         }
174         fclose(fd);
175         break;
176 
177       case 'i': /* include list */
178         if ((fd = fopen(optarg, "rb")) == NULL) {
179           BErrNo be;
180           Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"), optarg,
181                 be.bstrerror());
182           exit(1);
183         }
184         while (fgets(line, sizeof(line), fd) != NULL) {
185           StripTrailingJunk(line);
186           Dmsg1(900, "add_include %s\n", line);
187           AddFnameToIncludeList(ff, 0, line);
188         }
189         fclose(fd);
190         got_inc = true;
191         break;
192 
193       case 'p':
194         forge_on = true;
195         break;
196 
197       case 'v':
198         verbose++;
199         break;
200 
201       case 'V': /* Volume name */
202         VolumeName = optarg;
203         break;
204 
205       case '?':
206       default:
207         usage();
208 
209     } /* end switch */
210   }   /* end while */
211   argc -= optind;
212   argv += optind;
213 
214   if (argc != 2) {
215     Pmsg0(0,
216           _("Wrong number of arguments. Make sure the last two parameters are "
217             "<bareos-archive-device-name> <directory-to-store-files>\n"));
218     usage();
219   }
220 
221   my_config = InitSdConfig(configfile, M_ERROR_TERM);
222   ParseSdConfig(configfile, M_ERROR_TERM);
223 
224   if (DirectorName) {
225     foreach_res (director, R_DIRECTOR) {
226       if (bstrcmp(director->resource_name_, DirectorName)) { break; }
227     }
228     if (!director) {
229       Emsg2(
230           M_ERROR_TERM, 0,
231           _("No Director resource named %s defined in %s. Cannot continue.\n"),
232           DirectorName, configfile);
233     }
234   }
235 
236   LoadSdPlugins(me->plugin_directory, me->plugin_names);
237 
238   ReadCryptoCache(me->working_directory, "bareos-sd",
239                   GetFirstPortHostOrder(me->SDaddrs));
240 
241   if (!got_inc) {                      /* If no include file, */
242     AddFnameToIncludeList(ff, 0, "/"); /*   include everything */
243   }
244 
245   where = argv[1];
246   DoExtract(argv[0]);
247 
248   if (bsr) { libbareos::FreeBsr(bsr); }
249   if (prog_name_msg) {
250     Pmsg1(000,
251           _("%d Program Name and/or Program Data Stream records ignored.\n"),
252           prog_name_msg);
253   }
254   if (win32_data_msg) {
255     Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
256           win32_data_msg);
257   }
258   TermIncludeExcludeFiles(ff);
259   TermFindFiles(ff);
260   return 0;
261 }
262 
263 /*
264  * Cleanup of delayed restore stack with streams for later processing.
265  */
DropDelayedDataStreams()266 static inline void DropDelayedDataStreams()
267 {
268   DelayedDataStream* dds = nullptr;
269 
270   if (!delayed_streams || delayed_streams->empty()) { return; }
271 
272   foreach_alist (dds, delayed_streams) {
273     free(dds->content);
274   }
275 
276   delayed_streams->destroy();
277 }
278 
279 /*
280  * Push a data stream onto the delayed restore stack for later processing.
281  */
PushDelayedDataStream(int stream,char * content,uint32_t content_length)282 static inline void PushDelayedDataStream(int stream,
283                                          char* content,
284                                          uint32_t content_length)
285 {
286   DelayedDataStream* dds;
287 
288   if (!delayed_streams) { delayed_streams = new alist(10, owned_by_alist); }
289 
290   dds = (DelayedDataStream*)malloc(sizeof(DelayedDataStream));
291   dds->stream = stream;
292   dds->content = (char*)malloc(content_length);
293   memcpy(dds->content, content, content_length);
294   dds->content_length = content_length;
295 
296   delayed_streams->append(dds);
297 }
298 
299 /*
300  * Restore any data streams that are restored after the file
301  * is fully restored and has its attributes restored. Things
302  * like acls and xattr are restored after we set the file
303  * attributes otherwise we might clear some security flags
304  * by setting the attributes.
305  */
PopDelayedDataStreams()306 static inline void PopDelayedDataStreams()
307 {
308   DelayedDataStream* dds = nullptr;
309 
310   /*
311    * See if there is anything todo.
312    */
313   if (!delayed_streams || delayed_streams->empty()) { return; }
314 
315   /*
316    * Only process known delayed data streams here.
317    * If you start using more delayed data streams
318    * be sure to add them in this loop and add the
319    * proper calls here.
320    *
321    * Currently we support delayed data stream
322    * processing for the following type of streams:
323    * - *_ACL_*
324    * - *_XATTR_*
325    */
326   foreach_alist (dds, delayed_streams) {
327     switch (dds->stream) {
328       case STREAM_UNIX_ACCESS_ACL:
329       case STREAM_UNIX_DEFAULT_ACL:
330       case STREAM_ACL_AIX_TEXT:
331       case STREAM_ACL_DARWIN_ACCESS_ACL:
332       case STREAM_ACL_FREEBSD_DEFAULT_ACL:
333       case STREAM_ACL_FREEBSD_ACCESS_ACL:
334       case STREAM_ACL_HPUX_ACL_ENTRY:
335       case STREAM_ACL_IRIX_DEFAULT_ACL:
336       case STREAM_ACL_IRIX_ACCESS_ACL:
337       case STREAM_ACL_LINUX_DEFAULT_ACL:
338       case STREAM_ACL_LINUX_ACCESS_ACL:
339       case STREAM_ACL_TRU64_DEFAULT_ACL:
340       case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
341       case STREAM_ACL_TRU64_ACCESS_ACL:
342       case STREAM_ACL_SOLARIS_ACLENT:
343       case STREAM_ACL_SOLARIS_ACE:
344       case STREAM_ACL_AFS_TEXT:
345       case STREAM_ACL_AIX_AIXC:
346       case STREAM_ACL_AIX_NFS4:
347       case STREAM_ACL_FREEBSD_NFS4_ACL:
348       case STREAM_ACL_HURD_DEFAULT_ACL:
349       case STREAM_ACL_HURD_ACCESS_ACL:
350         parse_acl_streams(jcr, &acl_data, dds->stream, dds->content,
351                           dds->content_length);
352         free(dds->content);
353         break;
354       case STREAM_XATTR_HURD:
355       case STREAM_XATTR_IRIX:
356       case STREAM_XATTR_TRU64:
357       case STREAM_XATTR_AIX:
358       case STREAM_XATTR_OPENBSD:
359       case STREAM_XATTR_SOLARIS_SYS:
360       case STREAM_XATTR_DARWIN:
361       case STREAM_XATTR_FREEBSD:
362       case STREAM_XATTR_LINUX:
363       case STREAM_XATTR_NETBSD:
364         ParseXattrStreams(jcr, &xattr_data, dds->stream, dds->content,
365                           dds->content_length);
366         free(dds->content);
367         break;
368       default:
369         Jmsg(jcr, M_WARNING, 0,
370              _("Unknown stream=%d ignored. This shouldn't happen!\n"),
371              dds->stream);
372         break;
373     }
374   }
375 
376   /*
377    * We processed the stack so we can destroy it.
378    */
379   delayed_streams->destroy();
380 
381   /*
382    * (Re)Initialize the stack for a new use.
383    */
384   delayed_streams->init(10, owned_by_alist);
385 
386   return;
387 }
388 
ClosePreviousStream(void)389 static void ClosePreviousStream(void)
390 {
391   PopDelayedDataStreams();
392   SetAttributes(jcr, attr, &bfd);
393 }
394 
DoExtract(char * devname)395 static void DoExtract(char* devname)
396 {
397   struct stat statp;
398   uint32_t decompress_buf_size;
399 
400   EnableBackupPrivileges(NULL, 1);
401 
402   dcr = new DeviceControlRecord;
403   jcr = SetupJcr("bextract", devname, bsr, director, dcr, VolumeName,
404                  true); /* read device */
405   if (!jcr) { exit(1); }
406   dev = jcr->impl->read_dcr->dev;
407   if (!dev) { exit(1); }
408   dcr = jcr->impl->read_dcr;
409 
410   /*
411    * Make sure where directory exists and that it is a directory
412    */
413   if (stat(where, &statp) < 0) {
414     BErrNo be;
415     Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"), where,
416           be.bstrerror());
417   }
418   if (!S_ISDIR(statp.st_mode)) {
419     Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
420   }
421 
422   free(jcr->where);
423   jcr->where = strdup(where);
424   attr = new_attr(jcr);
425 
426   jcr->buf_size = DEFAULT_NETWORK_BUFFER_SIZE;
427   SetupDecompressionBuffers(jcr, &decompress_buf_size);
428 
429   if (decompress_buf_size > 0) {
430     jcr->compress = CompressionContext{};
431     jcr->compress.inflate_buffer = GetMemory(decompress_buf_size);
432     jcr->compress.inflate_buffer_size = decompress_buf_size;
433   }
434 
435   acl_data.last_fname = GetPoolMemory(PM_FNAME);
436   xattr_data.last_fname = GetPoolMemory(PM_FNAME);
437 
438   ReadRecords(dcr, RecordCb, MountNextReadVolume);
439 
440   /*
441    * If output file is still open, it was the last one in the
442    * archive since we just hit an end of file, so close the file.
443    */
444   if (IsBopen(&bfd)) { ClosePreviousStream(); }
445   FreeAttr(attr);
446 
447   FreePoolMemory(acl_data.last_fname);
448   FreePoolMemory(xattr_data.last_fname);
449 
450   if (delayed_streams) {
451     DropDelayedDataStreams();
452     delete delayed_streams;
453   }
454 
455   CleanupCompression(jcr);
456 
457   CleanDevice(jcr->impl->dcr);
458   delete dev;
459   FreeDeviceControlRecord(dcr);
460   FreeJcr(jcr);
461 
462   printf(_("%u files restored.\n"), num_files);
463 
464   return;
465 }
466 
StoreData(BareosWinFilePacket * bfd,char * data,const int32_t length)467 static bool StoreData(BareosWinFilePacket* bfd,
468                       char* data,
469                       const int32_t length)
470 {
471   if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
472     SetPortableBackup(bfd);
473     if (!processWin32BackupAPIBlock(bfd, data, length)) {
474       BErrNo be;
475       Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), attr->ofname,
476             be.bstrerror());
477       return false;
478     }
479   } else if (bwrite(bfd, data, length) != (ssize_t)length) {
480     BErrNo be;
481     Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), attr->ofname,
482           be.bstrerror());
483     return false;
484   }
485 
486   return true;
487 }
488 
489 /*
490  * Called here for each record from ReadRecords()
491  */
RecordCb(DeviceControlRecord * dcr,DeviceRecord * rec)492 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec)
493 {
494   int status;
495   JobControlRecord* jcr = dcr->jcr;
496 
497   if (rec->FileIndex < 0) { return true; /* we don't want labels */ }
498 
499   /* File Attributes stream */
500 
501   switch (rec->maskedStream) {
502     case STREAM_UNIX_ATTRIBUTES:
503     case STREAM_UNIX_ATTRIBUTES_EX:
504 
505       /* If extracting, it was from previous stream, so
506        * close the output file.
507        */
508       if (extract) {
509         if (!IsBopen(&bfd)) {
510           Emsg0(M_ERROR, 0,
511                 _("Logic error output file should be open but is not.\n"));
512         }
513         ClosePreviousStream();
514         extract = false;
515       }
516 
517       if (!UnpackAttributesRecord(jcr, rec->Stream, rec->data, rec->data_len,
518                                   attr)) {
519         Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
520       }
521 
522       if (FileIsIncluded(ff, attr->fname) && !FileIsExcluded(ff, attr->fname)) {
523         attr->data_stream = DecodeStat(attr->attr, &attr->statp,
524                                        sizeof(attr->statp), &attr->LinkFI);
525         if (!IsRestoreStreamSupported(attr->data_stream)) {
526           if (!non_support_data++) {
527             Jmsg(jcr, M_ERROR, 0,
528                  _("%s stream not supported on this Client.\n"),
529                  stream_to_ascii(attr->data_stream));
530           }
531           extract = false;
532           return true;
533         }
534 
535         BuildAttrOutputFnames(jcr, attr);
536 
537         if (attr->type
538             == FT_DELETED) { /* TODO: choose the right fname/ofname */
539           Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
540           extract = false;
541           return true;
542         }
543 
544         extract = false;
545         status = CreateFile(jcr, attr, &bfd, REPLACE_ALWAYS);
546         switch (status) {
547           case CF_ERROR:
548           case CF_SKIP:
549             break;
550           case CF_EXTRACT:
551             extract = true;
552             PrintLsOutput(jcr, attr);
553             num_files++;
554             fileAddr = 0;
555             break;
556           case CF_CREATED:
557             ClosePreviousStream();
558             PrintLsOutput(jcr, attr);
559             num_files++;
560             fileAddr = 0;
561             break;
562         }
563       }
564       break;
565 
566     case STREAM_RESTORE_OBJECT:
567       /* nothing to do */
568       break;
569 
570     /* Data stream and extracting */
571     case STREAM_FILE_DATA:
572     case STREAM_SPARSE_DATA:
573     case STREAM_WIN32_DATA:
574 
575       if (extract) {
576         if (rec->maskedStream == STREAM_SPARSE_DATA) {
577           ser_declare;
578           uint64_t faddr;
579           wbuf = rec->data + OFFSET_FADDR_SIZE;
580           wsize = rec->data_len - OFFSET_FADDR_SIZE;
581           SerBegin(rec->data, OFFSET_FADDR_SIZE);
582           unser_uint64(faddr);
583           if (fileAddr != faddr) {
584             fileAddr = faddr;
585             if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
586               BErrNo be;
587               Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"), attr->ofname,
588                     be.bstrerror());
589             }
590           }
591         } else {
592           wbuf = rec->data;
593           wsize = rec->data_len;
594         }
595         total += wsize;
596         Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
597         StoreData(&bfd, wbuf, wsize);
598         fileAddr += wsize;
599       }
600       break;
601 
602     /* GZIP data stream and Compressed data stream */
603     case STREAM_GZIP_DATA:
604     case STREAM_SPARSE_GZIP_DATA:
605     case STREAM_COMPRESSED_DATA:
606     case STREAM_SPARSE_COMPRESSED_DATA:
607     case STREAM_WIN32_COMPRESSED_DATA:
608       if (extract) {
609         if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA
610             || rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
611           ser_declare;
612           uint64_t faddr;
613           char ec1[50];
614 
615           wbuf = rec->data + OFFSET_FADDR_SIZE;
616           wsize = rec->data_len - OFFSET_FADDR_SIZE;
617 
618           SerBegin(rec->data, OFFSET_FADDR_SIZE);
619           unser_uint64(faddr);
620           SerEnd(rec->data, OFFSET_FADDR_SIZE);
621 
622           if (fileAddr != faddr) {
623             fileAddr = faddr;
624             if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
625               BErrNo be;
626 
627               Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
628                     edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
629               extract = false;
630               return true;
631             }
632           }
633         } else {
634           wbuf = rec->data;
635           wsize = rec->data_len;
636         }
637 
638         if (DecompressData(jcr, attr->ofname, rec->maskedStream, &wbuf, &wsize,
639                            false)) {
640           Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n",
641                 wsize, total);
642           StoreData(&bfd, wbuf, wsize);
643           total += wsize;
644           fileAddr += wsize;
645           Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, wsize);
646         } else {
647           extract = false;
648           return false;
649         }
650       }
651       break;
652 
653     case STREAM_MD5_DIGEST:
654     case STREAM_SHA1_DIGEST:
655     case STREAM_SHA256_DIGEST:
656     case STREAM_SHA512_DIGEST:
657       break;
658 
659     case STREAM_SIGNED_DIGEST:
660     case STREAM_ENCRYPTED_SESSION_DATA:
661       // TODO landonf: Investigate crypto support in the storage daemon
662       break;
663 
664     case STREAM_PROGRAM_NAMES:
665     case STREAM_PROGRAM_DATA:
666       if (!prog_name_msg) {
667         Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
668         prog_name_msg++;
669       }
670       break;
671 
672     case STREAM_UNIX_ACCESS_ACL:  /* Deprecated Standard ACL attributes on UNIX
673                                    */
674     case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX
675                                    */
676     case STREAM_ACL_AIX_TEXT:
677     case STREAM_ACL_DARWIN_ACCESS_ACL:
678     case STREAM_ACL_FREEBSD_DEFAULT_ACL:
679     case STREAM_ACL_FREEBSD_ACCESS_ACL:
680     case STREAM_ACL_HPUX_ACL_ENTRY:
681     case STREAM_ACL_IRIX_DEFAULT_ACL:
682     case STREAM_ACL_IRIX_ACCESS_ACL:
683     case STREAM_ACL_LINUX_DEFAULT_ACL:
684     case STREAM_ACL_LINUX_ACCESS_ACL:
685     case STREAM_ACL_TRU64_DEFAULT_ACL:
686     case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
687     case STREAM_ACL_TRU64_ACCESS_ACL:
688     case STREAM_ACL_SOLARIS_ACLENT:
689     case STREAM_ACL_SOLARIS_ACE:
690     case STREAM_ACL_AFS_TEXT:
691     case STREAM_ACL_AIX_AIXC:
692     case STREAM_ACL_AIX_NFS4:
693     case STREAM_ACL_FREEBSD_NFS4_ACL:
694     case STREAM_ACL_HURD_DEFAULT_ACL:
695     case STREAM_ACL_HURD_ACCESS_ACL:
696       if (extract) {
697         PmStrcpy(acl_data.last_fname, attr->fname);
698         PushDelayedDataStream(rec->maskedStream, rec->data, rec->data_len);
699       }
700       break;
701 
702     case STREAM_XATTR_HURD:
703     case STREAM_XATTR_IRIX:
704     case STREAM_XATTR_TRU64:
705     case STREAM_XATTR_AIX:
706     case STREAM_XATTR_OPENBSD:
707     case STREAM_XATTR_SOLARIS_SYS:
708     case STREAM_XATTR_SOLARIS:
709     case STREAM_XATTR_DARWIN:
710     case STREAM_XATTR_FREEBSD:
711     case STREAM_XATTR_LINUX:
712     case STREAM_XATTR_NETBSD:
713       if (extract) {
714         PmStrcpy(xattr_data.last_fname, attr->fname);
715         PushDelayedDataStream(rec->maskedStream, rec->data, rec->data_len);
716       }
717       break;
718 
719     case STREAM_NDMP_SEPARATOR:
720       break;
721 
722     default:
723       /*
724        * If extracting, weird stream (not 1 or 2), close output file anyway
725        */
726       if (extract) {
727         if (!IsBopen(&bfd)) {
728           Emsg0(M_ERROR, 0,
729                 _("Logic error output file should be open but is not.\n"));
730         }
731         ClosePreviousStream();
732         extract = false;
733       }
734       Jmsg(jcr, M_ERROR, 0,
735            _("Unknown stream=%d ignored. This shouldn't happen!\n"),
736            rec->Stream);
737       break;
738 
739   } /* end switch */
740   return true;
741 }
742