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