ead4619c403222b1ce0b969e30356d1cd1dee38e
[debian/amanda] / common-src / fileheader.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: fileheader.c 6512 2007-05-24 17:00:24Z ian $
28  */
29
30 #include "amanda.h"
31 #include "fileheader.h"
32 #include <glib.h>
33 #include "util.h"
34
35 static const char *     filetype2str(filetype_t);
36 static filetype_t       str2filetype(const char *);
37 static void             strange_header(dumpfile_t *, const char *,
38                                 size_t, const char *, const char *);
39 static char *           strquotedstr(void);
40 static ssize_t          hexdump(const char *buffer, size_t len);
41
42 void
43 fh_init(
44     dumpfile_t *file)
45 {
46     memset(file, '\0', SIZEOF(*file));
47     file->blocksize = DISK_BLOCK_BYTES;
48 }
49
50 static void
51 strange_header(
52     dumpfile_t *file,
53     const char *buffer,
54     size_t      buflen,
55     const char *expected,
56     const char *actual)
57 {
58     if (actual == NULL)
59         actual = "<null>";
60     if (expected == NULL)
61         expected = "<null>";
62
63     g_fprintf(stderr, _("%s: strange amanda header: \"%.*s\"\n"), get_pname(),
64                 (int)buflen, buffer);
65
66     g_fprintf(stderr, _("%s: Expected: \"%s\"  Actual: \"%s\"\n"), get_pname(),
67                 expected, actual);
68
69     file->type = F_WEIRD;
70 }
71
72
73 void
74 parse_file_header(
75     const char *buffer,
76     dumpfile_t *file,
77     size_t      buflen)
78 {
79     char *buf, *line, *tok, *line1;
80     size_t lsize;
81     char *uqname;
82     int in_quotes;
83
84     /* put the buffer into a writable chunk of memory and nul-term it */
85     buf = alloc(buflen + 1);
86     memcpy(buf, buffer, buflen);
87     buf[buflen] = '\0';
88     fh_init(file); 
89
90     in_quotes = 0;
91     for (line = buf, lsize = 0; lsize < buflen; line++) {
92         if ((*line == '\n') && !in_quotes)
93             break;
94
95         if (*line == '"') {
96             in_quotes = !in_quotes;
97         } else if ((*line == '\\') && (*(line + 1) == '"')) {
98             line++;
99             lsize++;
100         }
101         lsize++;
102     }
103     *line = '\0';
104     line1 = alloc(lsize + 1);
105     strncpy(line1, buf, lsize);
106     line1[lsize] = '\0';
107     *line = '\n';
108
109     tok = strtok(line1, " ");
110     if (tok == NULL) {
111         g_fprintf(stderr, _("%s: Empty amanda header: buflen=%zu lsize=%zu\n"), get_pname(),
112             buflen, 
113             lsize);
114         hexdump(buffer, lsize);
115         strange_header(file, buffer, buflen, _("<Non-empty line>"), tok);
116         goto out;
117     }
118
119     if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
120         amfree(buf);
121         file->type = F_UNKNOWN;
122         amfree(line1);
123         return;
124     }
125
126     tok = strtok(NULL, " ");
127     if (tok == NULL) {
128         strange_header(file, buffer, buflen, _("<file type>"), tok);
129         goto out;
130     }
131     file->type = str2filetype(tok);
132     
133     switch (file->type) {
134     case F_TAPESTART:
135         tok = strtok(NULL, " ");
136         if ((tok == NULL) || (strcmp(tok, "DATE") != 0)) {
137             strange_header(file, buffer, buflen, "DATE", tok);
138             goto out;
139         }
140
141         tok = strtok(NULL, " ");
142         if (tok == NULL) {
143             strange_header(file, buffer, buflen, _("<date stamp>"), tok);
144             goto out;
145         }
146         strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
147
148         tok = strtok(NULL, " ");
149         if ((tok == NULL) || (strcmp(tok, "TAPE") != 0)) {
150             strange_header(file, buffer, buflen, "TAPE", tok);
151             goto out;
152         }
153
154         tok = strtok(NULL, " ");
155         if (tok == NULL) {
156             strange_header(file, buffer, buflen, _("<file type>"), tok);
157             goto out;
158         }
159         strncpy(file->name, tok, SIZEOF(file->name) - 1);
160         break;
161
162     case F_DUMPFILE:
163     case F_CONT_DUMPFILE:
164     case F_SPLIT_DUMPFILE:
165         tok = strtok(NULL, " ");
166         if (tok == NULL) {
167             strange_header(file, buffer, buflen, _("<date stamp>"), tok);
168             goto out;
169         }
170         strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
171
172         tok = strtok(NULL, " ");
173         if (tok == NULL) {
174             strange_header(file, buffer, buflen, _("<file name>"), tok);
175             goto out;
176         }
177         strncpy(file->name, tok, SIZEOF(file->name) - 1);
178
179         tok = strquotedstr();
180         if (tok == NULL) {
181             strange_header(file, buffer, buflen, _("<disk name>"), tok);
182             goto out;
183         }
184         uqname = unquote_string(tok);
185         strncpy(file->disk, uqname, SIZEOF(file->disk) - 1);
186         amfree(uqname);
187         
188         if(file->type == F_SPLIT_DUMPFILE) {
189             tok = strtok(NULL, " ");
190             if (tok == NULL || strcmp(tok, "part") != 0) {
191                 strange_header(file, buffer, buflen, "part", tok);
192                 goto out;
193             }
194
195             tok = strtok(NULL, "/");
196             if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) {
197                 strange_header(file, buffer, buflen, _("<part num param>"), tok);
198                 goto out;
199             }
200
201             /* If totalparts == -1, then the original dump was done in 
202                streaming mode (no holding disk), thus we don't know how 
203                many parts there are. */
204             tok = strtok(NULL, " ");
205             if((tok == NULL) || (sscanf(tok, "%d", &file->totalparts) != 1)) {
206                 strange_header(file, buffer, buflen, _("<total parts param>"), tok);
207                 goto out;
208             }
209         }
210
211         tok = strtok(NULL, " ");
212         if ((tok == NULL) || (strcmp(tok, "lev") != 0)) {
213             strange_header(file, buffer, buflen, "lev", tok);
214             goto out;
215         }
216
217         tok = strtok(NULL, " ");
218         if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) {
219             strange_header(file, buffer, buflen, _("<dump level param>"), tok);
220             goto out;
221         }
222
223         tok = strtok(NULL, " ");
224         if ((tok == NULL) || (strcmp(tok, "comp") != 0)) {
225             strange_header(file, buffer, buflen, "comp", tok);
226             goto out;
227         }
228
229         tok = strtok(NULL, " ");
230         if (tok == NULL) {
231             strange_header(file, buffer, buflen, _("<comp param>"), tok);
232             goto out;
233         }
234         strncpy(file->comp_suffix, tok, SIZEOF(file->comp_suffix) - 1);
235
236         file->compressed = strcmp(file->comp_suffix, "N");
237         /* compatibility with pre-2.2 amanda */
238         if (strcmp(file->comp_suffix, "C") == 0)
239             strncpy(file->comp_suffix, ".Z", SIZEOF(file->comp_suffix) - 1);
240                
241         tok = strtok(NULL, " ");
242         /* "program" is optional */
243         if (tok == NULL || strcmp(tok, "program") != 0) {
244             amfree(buf);
245             amfree(line1);
246             return;
247         }
248
249         tok = strtok(NULL, " ");
250         if (tok == NULL) {
251             strange_header(file, buffer, buflen, _("<program name>"), tok);
252             goto out;
253         }
254         strncpy(file->program, tok, SIZEOF(file->program) - 1);
255         if (file->program[0] == '\0')
256             strncpy(file->program, "RESTORE", SIZEOF(file->program) - 1);
257
258         if ((tok = strtok(NULL, " ")) == NULL)
259              break;          /* reached the end of the buffer */
260
261         /* "encryption" is optional */
262         if (BSTRNCMP(tok, "crypt") == 0) {
263             tok = strtok(NULL, " ");
264             if (tok == NULL) {
265                 strange_header(file, buffer, buflen, _("<crypt param>"), tok);
266                 goto out;
267             }
268             strncpy(file->encrypt_suffix, tok,
269                     SIZEOF(file->encrypt_suffix) - 1);
270             file->encrypted = BSTRNCMP(file->encrypt_suffix, "N");
271             if ((tok = strtok(NULL, " ")) == NULL)
272                 break;
273         }
274
275         /* "srvcompprog" is optional */
276         if (BSTRNCMP(tok, "server_custom_compress") == 0) {
277             tok = strtok(NULL, " ");
278             if (tok == NULL) {
279                 strange_header(file, buffer, buflen,
280                                 _("<server custom compress param>"), tok);
281                 goto out;
282             }
283             strncpy(file->srvcompprog, tok, SIZEOF(file->srvcompprog) - 1);
284             if ((tok = strtok(NULL, " ")) == NULL)
285                 break;      
286         }
287
288         /* "clntcompprog" is optional */
289         if (BSTRNCMP(tok, "client_custom_compress") == 0) {
290             tok = strtok(NULL, " ");
291             if (tok == NULL) {
292                 strange_header(file, buffer, buflen,
293                                 _("<client custom compress param>"), tok);
294                 goto out;
295             }
296             strncpy(file->clntcompprog, tok, SIZEOF(file->clntcompprog) - 1);
297             if ((tok = strtok(NULL, " ")) == NULL)
298                 break;
299         }
300
301         /* "srv_encrypt" is optional */
302         if (BSTRNCMP(tok, "server_encrypt") == 0) {
303             tok = strtok(NULL, " ");
304             if (tok == NULL) {
305                 strange_header(file, buffer, buflen,
306                                 _("<server encrypt param>"), tok);
307                 goto out;
308             }
309             strncpy(file->srv_encrypt, tok, SIZEOF(file->srv_encrypt) - 1);
310             if ((tok = strtok(NULL, " ")) == NULL) 
311                 break;
312         }
313
314         /* "clnt_encrypt" is optional */
315         if (BSTRNCMP(tok, "client_encrypt") == 0) {
316             tok = strtok(NULL, " ");
317             if (tok == NULL) {
318                 strange_header(file, buffer, buflen,
319                                 _("<client encrypt param>"), tok);
320                 goto out;
321             }
322             strncpy(file->clnt_encrypt, tok, SIZEOF(file->clnt_encrypt) - 1);
323             if ((tok = strtok(NULL, " ")) == NULL) 
324                 break;
325         }
326
327         /* "srv_decrypt_opt" is optional */
328         if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
329             tok = strtok(NULL, " ");
330             if (tok == NULL) {
331                 strange_header(file, buffer, buflen,
332                                 _("<server decrypt param>"), tok);
333                 goto out;
334             }
335             strncpy(file->srv_decrypt_opt, tok,
336                     SIZEOF(file->srv_decrypt_opt) - 1);
337             if ((tok = strtok(NULL, " ")) == NULL) 
338                 break;
339         }
340
341         /* "clnt_decrypt_opt" is optional */
342         if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
343             tok = strtok(NULL, " ");
344             if (tok == NULL) {
345                 strange_header(file, buffer, buflen,
346                                 _("<client decrypt param>"), tok);
347                 goto out;
348             }
349             strncpy(file->clnt_decrypt_opt, tok,
350                     SIZEOF(file->clnt_decrypt_opt) - 1);
351             if ((tok = strtok(NULL, " ")) == NULL) 
352                 break;
353         }
354       break;
355       
356
357     case F_TAPEEND:
358         tok = strtok(NULL, " ");
359         /* DATE is optional */
360         if (tok != NULL) {
361             if (strcmp(tok, "DATE") == 0) {
362                 tok = strtok(NULL, " ");
363                 if(tok == NULL)
364                     file->datestamp[0] = '\0';
365                 else
366                     strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
367             } else {
368                 strange_header(file, buffer, buflen, _("<DATE>"), tok);
369            }
370         } else {
371             file->datestamp[0] = '\0';
372         }
373         break;
374
375     default:
376         strange_header(file, buffer, buflen,
377                 _("TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND"), tok);
378         goto out;
379     }
380
381     (void)strtok(buf, "\n"); /* this is the first line */
382     /* iterate through the rest of the lines */
383     while ((line = strtok(NULL, "\n")) != NULL) {
384 #define SC "CONT_FILENAME="
385         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
386             line += SIZEOF(SC) - 1;
387             strncpy(file->cont_filename, line,
388                     SIZEOF(file->cont_filename) - 1);
389             continue;
390         }
391 #undef SC
392
393 #define SC "PARTIAL="
394         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
395             line += SIZEOF(SC) - 1;
396             file->is_partial = !strcasecmp(line, "yes");
397             continue;
398         }
399 #undef SC
400 #define SC "DUMPER="
401         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
402             line += SIZEOF(SC) - 1;
403             strncpy(file->dumper, line,
404                     SIZEOF(file->dumper) - 1);
405             continue;
406         }
407 #undef SC
408
409 #define SC _("To restore, position tape at start of file and run:")
410         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
411             continue;
412 #undef SC
413
414 #define SC "\tdd if=<tape> bs="
415         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
416             char *cmd1, *cmd2, *cmd3=NULL;
417
418             /* skip over dd command */
419             if ((cmd1 = strchr(line, '|')) == NULL) {
420
421                 strncpy(file->recover_cmd, "BUG",
422                         SIZEOF(file->recover_cmd) - 1);
423                 continue;
424             }
425             *cmd1++ = '\0';
426
427             /* block out first pipeline command */
428             if ((cmd2 = strchr(cmd1, '|')) != NULL) {
429               *cmd2++ = '\0';
430               if ((cmd3 = strchr(cmd2, '|')) != NULL)
431                 *cmd3++ = '\0';
432             }
433            
434             /* three cmds: decrypt    | uncompress | recover
435              * two   cmds: uncompress | recover
436              * XXX note that if there are two cmds, the first one 
437              * XXX could be either uncompress or decrypt. Since no
438              * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
439              * XXX for header information.
440              * one   cmds: recover
441              */
442
443             if ( cmd3 == NULL) {
444               if (cmd2 == NULL) {
445                 strncpy(file->recover_cmd, cmd1,
446                         SIZEOF(file->recover_cmd) - 1);
447               } else {
448                 g_snprintf(file->uncompress_cmd,
449                          SIZEOF(file->uncompress_cmd), "%s|", cmd1);
450                 strncpy(file->recover_cmd, cmd2,
451                         SIZEOF(file->recover_cmd) - 1);
452               }
453             } else {    /* cmd3 presents:  decrypt | uncompress | recover */
454               g_snprintf(file->decrypt_cmd,
455                        SIZEOF(file->decrypt_cmd), "%s|", cmd1);
456               g_snprintf(file->uncompress_cmd,
457                        SIZEOF(file->uncompress_cmd), "%s|", cmd2);
458               strncpy(file->recover_cmd, cmd3,
459                       SIZEOF(file->recover_cmd) - 1);
460             }
461             continue;
462         }
463 #undef SC
464         /* XXX complain about weird lines? */
465     }
466
467 out:
468     amfree(buf);
469     amfree(line1);
470 }
471
472 void
473 dump_dumpfile_t(
474     const dumpfile_t *file)
475 {
476         dbprintf(_("Contents of *(dumpfile_t *)%p:\n"), file);
477         dbprintf(_("    type             = %d (%s)\n"),
478                         file->type, filetype2str(file->type));
479         dbprintf(_("    datestamp        = '%s'\n"), file->datestamp);
480         dbprintf(_("    dumplevel        = %d\n"), file->dumplevel);
481         dbprintf(_("    compressed       = %d\n"), file->compressed);
482         dbprintf(_("    encrypted        = %d\n"), file->encrypted);
483         dbprintf(_("    comp_suffix      = '%s'\n"), file->comp_suffix);
484         dbprintf(_("    encrypt_suffix   = '%s'\n"), file->encrypt_suffix);
485         dbprintf(_("    name             = '%s'\n"), file->name);
486         dbprintf(_("    disk             = '%s'\n"), file->disk);
487         dbprintf(_("    program          = '%s'\n"), file->program);
488         dbprintf(_("    dumper           = '%s'\n"), file->dumper);
489         dbprintf(_("    srvcompprog      = '%s'\n"), file->srvcompprog);
490         dbprintf(_("    clntcompprog     = '%s'\n"), file->clntcompprog);
491         dbprintf(_("    srv_encrypt      = '%s'\n"), file->srv_encrypt);
492         dbprintf(_("    clnt_encrypt     = '%s'\n"), file->clnt_encrypt);
493         dbprintf(_("    recover_cmd      = '%s'\n"), file->recover_cmd);
494         dbprintf(_("    uncompress_cmd   = '%s'\n"), file->uncompress_cmd);
495         dbprintf(_("    encrypt_cmd      = '%s'\n"), file->encrypt_cmd);
496         dbprintf(_("    decrypt_cmd      = '%s'\n"), file->decrypt_cmd);
497         dbprintf(_("    srv_decrypt_opt  = '%s'\n"), file->srv_decrypt_opt);
498         dbprintf(_("    clnt_decrypt_opt = '%s'\n"), file->clnt_decrypt_opt);
499         dbprintf(_("    cont_filename    = '%s'\n"), file->cont_filename);
500         dbprintf(_("    is_partial       = %d\n"), file->is_partial);
501         dbprintf(_("    partnum          = %d\n"), file->partnum);
502         dbprintf(_("    totalparts       = %d\n"), file->totalparts);
503         dbprintf(_("    blocksize        = %zu\n"), file->blocksize);
504 }
505
506 static void
507 validate_name(
508     const char *name)
509 {
510         if (strlen(name) == 0) {
511             error(_("Invalid name '%s'\n"), name);
512             /*NOTREACHED*/
513         }
514 }
515
516 static void
517 validate_datestamp(
518     const char *datestamp)
519 {
520         if (strcmp(datestamp, "X") == 0) {
521             return;
522         }
523
524         if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
525             return;
526         }
527         if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
528             return;
529         }
530         error(_("Invalid datestamp '%s'\n"), datestamp);
531         /*NOTREACHED*/
532 }
533
534 static void
535 validate_parts(
536     const int partnum,
537     const int totalparts)
538 {
539         if (partnum < 1) {
540             error(_("Invalid partnum (%d)\n"), partnum);
541             /*NOTREACHED*/
542         }
543
544         if (partnum > totalparts && totalparts >= 0) {
545             error(_("Invalid partnum (%d) > totalparts (%d)\n"),
546                         partnum, totalparts);
547             /*NOTREACHED*/
548         }
549 }
550
551 char *
552 build_header(const dumpfile_t * file, size_t size)
553 {
554     GString *rval, *split_data;
555     char *qname;
556     char *program;
557
558     dbprintf(_("Building type %d (%s) header of size %zu using:\n"),
559                 file->type, filetype2str(file->type), size);
560     dump_dumpfile_t(file);
561
562     rval = g_string_sized_new(size);
563     split_data = g_string_sized_new(10);
564     
565     switch (file->type) {
566     case F_TAPESTART:
567         validate_name(file->name);
568         validate_datestamp(file->datestamp);
569         g_string_printf(rval, 
570                         "AMANDA: TAPESTART DATE %s TAPE %s\n014\n",
571                         file->datestamp, file->name);
572         break;
573         
574     case F_SPLIT_DUMPFILE:
575         validate_parts(file->partnum, file->totalparts);
576         g_string_printf(split_data,
577                         " part %d/%d ", file->partnum, file->totalparts);
578         /* FALLTHROUGH */
579         
580
581     case F_CONT_DUMPFILE:
582     case F_DUMPFILE :
583         validate_name(file->name);
584         validate_datestamp(file->datestamp);
585         qname = quote_string(file->disk);
586         program = stralloc(file->program);
587         if (match("^.*[.][Ee][Xx][Ee]$", program)) {
588                 /* Trim ".exe" from program name */
589                 program[strlen(program) - strlen(".exe")] = '\0';
590         }
591         g_string_printf(rval, 
592                         "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
593                         filetype2str(file->type),
594                         file->datestamp, file->name, qname,
595                         split_data->str,
596                         file->dumplevel, file->comp_suffix, program); 
597         amfree(program);
598         amfree(qname);
599
600         /* only output crypt if it's enabled */
601         if (strcmp(file->encrypt_suffix, "enc") == 0) {
602             g_string_append_printf(rval, " crypt %s", file->encrypt_suffix);
603         }
604
605         if (*file->srvcompprog) {
606             g_string_append_printf(rval, " server_custom_compress %s", 
607                                    file->srvcompprog);
608         } else if (*file->clntcompprog) {
609             g_string_append_printf(rval, " client_custom_compress %s",
610                                    file->clntcompprog);
611         } 
612         
613         if (*file->srv_encrypt) {
614             g_string_append_printf(rval, " server_encrypt %s",
615                                    file->srv_encrypt);
616         } else if (*file->clnt_encrypt) {
617             g_string_append_printf(rval, " client_encrypt %s",
618                                    file->clnt_encrypt);
619         } 
620         
621         if (*file->srv_decrypt_opt) {
622             g_string_append_printf(rval, " server_decrypt_option %s",
623                                    file->srv_decrypt_opt);
624         } else if (*file->clnt_decrypt_opt) {
625             g_string_append_printf(rval, " client_decrypt_option %s",
626                                    file->clnt_decrypt_opt);
627         } 
628         
629         g_string_append_printf(rval, "\n");
630         
631         if (file->cont_filename[0] != '\0') {
632             g_string_append_printf(rval, "CONT_FILENAME=%s\n",
633                                    file->cont_filename);
634         }
635         if (file->dumper[0] != '\0') {
636             g_string_append_printf(rval, "DUMPER=%s\n", file->dumper);
637         }
638         if (file->is_partial != 0) {
639             g_string_append_printf(rval, "PARTIAL=YES\n");
640         }
641         
642         g_string_append_printf(rval,
643             _("To restore, position tape at start of file and run:\n"));
644
645         /* \014 == ^L == form feed */
646         g_string_append_printf(rval,
647                                "\tdd if=<tape> bs=%zuk skip=1 |%s %s %s\n\014\n",
648                                file->blocksize / 1024,
649                                file->decrypt_cmd, file->uncompress_cmd,
650                                file->recover_cmd);
651         break;
652
653     case F_TAPEEND:
654         validate_datestamp(file->datestamp);
655         g_string_printf(rval, "AMANDA: TAPEEND DATE %s\n\014\n",
656                         file->datestamp);
657         break;
658
659     case F_UNKNOWN:
660     case F_EMPTY:
661     case F_WEIRD:
662     default:
663         error(_("Invalid header type: %d (%s)"),
664                 file->type, filetype2str(file->type));
665         /*NOTREACHED*/
666     }
667     
668     g_string_free(split_data, TRUE);
669
670     /* Since we don't return the length, it is an error for the header to be
671      * more than 'size' bytes */
672     assert(rval->len <= size);
673     /* Clear extra bytes. */
674     if (rval->len < size) {
675         bzero(rval->str + rval->len, rval->allocated_len - rval->len);
676     }
677     return g_string_free(rval, FALSE);
678 }
679
680 /*
681  * Prints the contents of the file structure.
682  */
683 void
684 print_header(
685     FILE *              outf,
686     const dumpfile_t *  file)
687 {
688     char *qdisk;
689     char number[NUM_STR_SIZE*2];
690
691     switch(file->type) {
692     case F_EMPTY:
693         g_fprintf(outf, _("EMPTY file\n"));
694         break;
695
696     case F_UNKNOWN:
697         g_fprintf(outf, _("UNKNOWN file\n"));
698         break;
699
700     case F_WEIRD:
701         g_fprintf(outf, _("WEIRD file\n"));
702         break;
703
704     case F_TAPESTART:
705         g_fprintf(outf, _("start of tape: date %s label %s\n"),
706                file->datestamp, file->name);
707         break;
708
709     case F_DUMPFILE:
710     case F_CONT_DUMPFILE:
711         qdisk = quote_string(file->disk);
712         g_fprintf(outf, "%s: date %s host %s disk %s lev %d comp %s",
713             filetype2str(file->type), file->datestamp, file->name,
714             qdisk, file->dumplevel, file->comp_suffix);
715         if (*file->program)
716             g_fprintf(outf, " program %s", file->program);
717         if (strcmp(file->encrypt_suffix, "enc") == 0)
718             g_fprintf(outf, " crypt %s", file->encrypt_suffix);
719         if (*file->srvcompprog)
720             g_fprintf(outf, " server_custom_compress %s", file->srvcompprog);
721         if (*file->clntcompprog)
722             g_fprintf(outf, " client_custom_compress %s", file->clntcompprog);
723         if (*file->srv_encrypt)
724             g_fprintf(outf, " server_encrypt %s", file->srv_encrypt);
725         if (*file->clnt_encrypt)
726             g_fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
727         if (*file->srv_decrypt_opt)
728             g_fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
729         if (*file->clnt_decrypt_opt)
730             g_fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
731         g_fprintf(outf, "\n");
732         amfree(qdisk);
733         break;
734
735     case F_SPLIT_DUMPFILE:
736         if(file->totalparts > 0){
737             g_snprintf(number, SIZEOF(number), "%d", file->totalparts);
738         }   
739         else g_snprintf(number, SIZEOF(number), "UNKNOWN");
740         qdisk = quote_string(file->disk);
741         g_fprintf(outf, "split dumpfile: date %s host %s disk %s part %d/%s lev %d comp %s",
742                       file->datestamp, file->name, qdisk, file->partnum,
743                       number, file->dumplevel, file->comp_suffix);
744         if (*file->program)
745             g_fprintf(outf, " program %s",file->program);
746         if (strcmp(file->encrypt_suffix, "enc") == 0)
747             g_fprintf(outf, " crypt %s", file->encrypt_suffix);
748         if (*file->srvcompprog)
749             g_fprintf(outf, " server_custom_compress %s", file->srvcompprog);
750         if (*file->clntcompprog)
751             g_fprintf(outf, " client_custom_compress %s", file->clntcompprog);
752         if (*file->srv_encrypt)
753             g_fprintf(outf, " server_encrypt %s", file->srv_encrypt);
754         if (*file->clnt_encrypt)
755             g_fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
756         if (*file->srv_decrypt_opt)
757             g_fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
758         if (*file->clnt_decrypt_opt)
759             g_fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
760         g_fprintf(outf, "\n");
761         amfree(qdisk);
762         break;
763
764     case F_TAPEEND:
765         g_fprintf(outf, "end of tape: date %s\n", file->datestamp);
766         break;
767     }
768 }
769
770 int
771 known_compress_type(
772     const dumpfile_t *  file)
773 {
774     if(strcmp(file->comp_suffix, ".Z") == 0)
775         return 1;
776 #ifdef HAVE_GZIP
777     if(strcmp(file->comp_suffix, ".gz") == 0)
778         return 1;
779 #endif
780     if(strcmp(file->comp_suffix, "cust") == 0)
781         return 1;
782     return 0;
783 }
784
785 static const struct {
786     filetype_t type;
787     const char *str;
788 } filetypetab[] = {
789     { F_UNKNOWN, "UNKNOWN" },
790     { F_WEIRD, "WEIRD" },
791     { F_TAPESTART, "TAPESTART" },
792     { F_TAPEEND,  "TAPEEND" },
793     { F_DUMPFILE, "FILE" },
794     { F_CONT_DUMPFILE, "CONT_FILE" },
795     { F_SPLIT_DUMPFILE, "SPLIT_FILE" }
796 };
797 #define NFILETYPES      (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
798
799 static const char *
800 filetype2str(
801     filetype_t  type)
802 {
803     int i;
804
805     for (i = 0; i < (int)NFILETYPES; i++)
806         if (filetypetab[i].type == type)
807             return (filetypetab[i].str);
808     return ("UNKNOWN");
809 }
810
811 static filetype_t
812 str2filetype(
813     const char *str)
814 {
815     int i;
816
817     for (i = 0; i < (int)NFILETYPES; i++)
818         if (strcmp(filetypetab[i].str, str) == 0)
819             return (filetypetab[i].type);
820     return (F_UNKNOWN);
821 }
822
823 gboolean headers_are_equal(dumpfile_t * a, dumpfile_t * b) {
824     if (a == NULL && b == NULL)
825         return TRUE;
826
827     if (a == NULL || b == NULL)
828         return FALSE;
829
830     return 0 == memcmp(a, b, sizeof(*a));
831 }
832
833 dumpfile_t * dumpfile_copy(dumpfile_t* source) {
834     gpointer rval = malloc(sizeof(dumpfile_t));
835     memcpy(rval, source, sizeof(dumpfile_t));
836     return rval;
837 }
838
839 /*
840  * This function modify strtok context.
841  */
842 static char *
843 strquotedstr(void)
844 {
845     char *  tok = strtok(NULL, " ");
846     size_t      len;
847
848     len = strlen(tok);
849     if ((tok != NULL) && (tok[0] == '"') &&
850         (len > 1 && (tok[len - 1] != '"') && (tok[len - 2] != '\\'))) {
851         char *  t;
852
853         do {
854             t = strtok(NULL, " ");
855             tok[len] = ' ';
856             len = strlen(tok);
857         } while ((t != NULL) &&
858                  (tok[len - 1] != '"') && (tok[len - 2] != '\\'));
859     }
860     return tok;
861 }
862
863 static ssize_t
864 hexdump(
865     const char *buffer,
866     size_t      len)
867 {
868     ssize_t rc = -1;
869
870     FILE *stream = popen("od -c -x -", "w");
871         
872     if (stream != NULL) {
873         fflush(stdout);
874         rc = (ssize_t)fwrite(buffer, len, 1, stream);
875         if (ferror(stream))
876             rc = -1;
877         pclose(stream);
878     }
879     return rc;
880 }
881