f6786a891cb128ee7ab5dbb01f9c94167ac30b2b
[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
398 #define SC "To restore, position tape at start of file and run:"
399         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
400             continue;
401 #undef SC
402
403 #define SC "\tdd if=<tape> bs="
404         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
405             char *cmd1, *cmd2, *cmd3=NULL;
406
407             /* skip over dd command */
408             if ((cmd1 = strchr(line, '|')) == NULL) {
409
410                 strncpy(file->recover_cmd, "BUG",
411                         SIZEOF(file->recover_cmd) - 1);
412                 continue;
413             }
414             *cmd1++ = '\0';
415
416             /* block out first pipeline command */
417             if ((cmd2 = strchr(cmd1, '|')) != NULL) {
418               *cmd2++ = '\0';
419               if ((cmd3 = strchr(cmd2, '|')) != NULL)
420                 *cmd3++ = '\0';
421             }
422            
423             /* three cmds: decrypt    | uncompress | recover
424              * two   cmds: uncompress | recover
425              * XXX note that if there are two cmds, the first one 
426              * XXX could be either uncompress or decrypt. Since no
427              * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
428              * XXX for header information.
429              * one   cmds: recover
430              */
431
432             if ( cmd3 == NULL) {
433               if (cmd2 == NULL) {
434                 strncpy(file->recover_cmd, cmd1,
435                         SIZEOF(file->recover_cmd) - 1);
436               } else {
437                 snprintf(file->uncompress_cmd,
438                          SIZEOF(file->uncompress_cmd), "%s|", cmd1);
439                 strncpy(file->recover_cmd, cmd2,
440                         SIZEOF(file->recover_cmd) - 1);
441               }
442             } else {    /* cmd3 presents:  decrypt | uncompress | recover */
443               snprintf(file->decrypt_cmd,
444                        SIZEOF(file->decrypt_cmd), "%s|", cmd1);
445               snprintf(file->uncompress_cmd,
446                        SIZEOF(file->uncompress_cmd), "%s|", cmd2);
447               strncpy(file->recover_cmd, cmd3,
448                       SIZEOF(file->recover_cmd) - 1);
449             }
450             continue;
451         }
452 #undef SC
453         /* XXX complain about weird lines? */
454     }
455
456 out:
457     amfree(buf);
458     amfree(line1);
459 }
460
461 void
462 dump_dumpfile_t(
463     const dumpfile_t *file)
464 {
465         const char *pname = get_pname();
466
467         dbprintf(("%s: Contents of *(dumpfile_t *)%p:\n", pname, file));
468         dbprintf(("%s:     type             = %d (%s)\n", pname,
469                         file->type, filetype2str(file->type)));
470         dbprintf(("%s:     datestamp        = '%s'\n", pname,
471                         file->datestamp));
472         dbprintf(("%s:     dumplevel        = %d\n", pname, file->dumplevel));
473         dbprintf(("%s:     compressed       = %d\n", pname, file->compressed));
474         dbprintf(("%s:     encrypted        = %d\n", pname, file->encrypted));
475         dbprintf(("%s:     comp_suffix      = '%s'\n", pname,
476                         file->comp_suffix));
477         dbprintf(("%s:     encrypt_suffix   = '%s'\n", pname,
478                         file->encrypt_suffix));
479         dbprintf(("%s:     name             = '%s'\n", pname, file->name));
480         dbprintf(("%s:     disk             = '%s'\n", pname, file->disk));
481         dbprintf(("%s:     program          = '%s'\n", pname, file->program));
482         dbprintf(("%s:     srvcompprog      = '%s'\n", pname,
483                         file->srvcompprog));
484         dbprintf(("%s:     clntcompprog     = '%s'\n", pname,
485                         file->clntcompprog));
486         dbprintf(("%s:     srv_encrypt      = '%s'\n", pname,
487                         file->srv_encrypt));
488         dbprintf(("%s:     clnt_encrypt     = '%s'\n", pname,
489                         file->clnt_encrypt));
490         dbprintf(("%s:     recover_cmd      = '%s'\n", pname,
491                         file->recover_cmd));
492         dbprintf(("%s:     uncompress_cmd   = '%s'\n", pname,
493                         file->uncompress_cmd));
494         dbprintf(("%s:     encrypt_cmd      = '%s'\n", pname,
495                         file->encrypt_cmd));
496         dbprintf(("%s:     decrypt_cmd      = '%s'\n", pname,
497                         file->decrypt_cmd));
498         dbprintf(("%s:     srv_decrypt_opt  = '%s'\n", pname,
499                         file->srv_decrypt_opt));
500         dbprintf(("%s:     clnt_decrypt_opt = '%s'\n", pname,
501                         file->clnt_decrypt_opt));
502         dbprintf(("%s:     cont_filename    = '%s'\n", pname,
503                         file->cont_filename));
504         dbprintf(("%s:     is_partial       = %d\n", pname, file->is_partial));
505         dbprintf(("%s:     partnum          = %d\n", pname, file->partnum));
506         dbprintf(("%s:     totalparts       = %d\n", pname, file->totalparts));
507         dbprintf(("%s:     blocksize        = " SIZE_T_FMT "\n", pname,
508                         (SIZE_T_FMT_TYPE)file->blocksize));
509 }
510
511 static void
512 validate_name(
513     const char *name)
514 {
515         if (strlen(name) == 0) {
516             error("Invalid name '%s'\n", name);
517             /*NOTREACHED*/
518         }
519 }
520
521 static void
522 validate_datestamp(
523     const char *datestamp)
524 {
525         if (strcmp(datestamp, "X") == 0) {
526             return;
527         }
528
529         if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
530             return;
531         }
532         if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
533             return;
534         }
535         error("Invalid datestamp '%s'\n", datestamp);
536         /*NOTREACHED*/
537 }
538
539 static void
540 validate_parts(
541     const int partnum,
542     const int totalparts)
543 {
544         if (partnum < 1) {
545             error("Invalid partnum (%d)\n", partnum);
546             /*NOTREACHED*/
547         }
548
549         if (partnum > totalparts && totalparts >= 0) {
550             error("Invalid partnum (%d) > totalparts (%d)\n",
551                         partnum, totalparts);
552             /*NOTREACHED*/
553         }
554 }
555
556 void
557 build_header(
558     char *              buffer,
559     const dumpfile_t *  file,
560     size_t              buflen)
561 {
562     int n;
563     char *qname;
564     char split_data[128] = "";
565
566     dbprintf(("%s: Building type %d (%s) header of size " SIZE_T_FMT " using:\n",
567                 get_pname(), file->type, filetype2str(file->type),
568                 (SIZE_T_FMT_TYPE)buflen));
569     dump_dumpfile_t(file);
570
571     memset(buffer,'\0',buflen);
572
573     switch (file->type) {
574     case F_TAPESTART:
575         validate_name(file->name);
576         validate_datestamp(file->datestamp);
577         snprintf(buffer, buflen,
578             "AMANDA: TAPESTART DATE %s TAPE %s\n014\n",
579             file->datestamp, file->name);
580         break;
581
582     case F_SPLIT_DUMPFILE:
583         validate_parts(file->partnum, file->totalparts);
584         snprintf(split_data, SIZEOF(split_data),
585                  " part %d/%d ", file->partnum, file->totalparts);
586         /*FALLTHROUGH*/
587         
588     case F_CONT_DUMPFILE:
589     case F_DUMPFILE :
590         validate_name(file->name);
591         validate_datestamp(file->datestamp);
592         qname = quote_string(file->disk);
593         n = snprintf(buffer, buflen,
594                      "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
595                          filetype2str(file->type),
596                          file->datestamp, file->name, qname,
597                          split_data,
598                          file->dumplevel, file->comp_suffix, file->program); 
599         amfree(qname);
600         if ( n ) {
601           buffer += n;
602           buflen -= n;
603           n = 0;
604         }
605      
606         if (strcmp(file->encrypt_suffix, "enc") == 0) {  /* only output crypt if it's enabled */
607           n = snprintf(buffer, buflen, " crypt %s", file->encrypt_suffix);
608         }
609         if ( n ) {
610           buffer += n;
611           buflen -= n;
612           n = 0;
613         }
614
615         if (*file->srvcompprog) {
616             n = snprintf(buffer, buflen, " server_custom_compress %s", file->srvcompprog);
617         } else if (*file->clntcompprog) {
618             n = snprintf(buffer, buflen, " client_custom_compress %s", file->clntcompprog);
619         } 
620
621         if ( n ) {
622           buffer += n;
623           buflen -= n;
624           n = 0;
625         }
626
627         if (*file->srv_encrypt) {
628             n = snprintf(buffer, buflen, " server_encrypt %s", file->srv_encrypt);
629         } else if (*file->clnt_encrypt) {
630             n = snprintf(buffer, buflen, " client_encrypt %s", file->clnt_encrypt);
631         } 
632
633         if ( n ) {
634           buffer += n;
635           buflen -= n;
636           n = 0;
637         }
638         
639         if (*file->srv_decrypt_opt) {
640             n = snprintf(buffer, buflen, " server_decrypt_option %s", file->srv_decrypt_opt);
641         } else if (*file->clnt_decrypt_opt) {
642             n = snprintf(buffer, buflen, " client_decrypt_option %s", file->clnt_decrypt_opt);
643         } 
644
645         if ( n ) {
646           buffer += n;
647           buflen -= n;
648           n = 0;
649         }
650
651         n = snprintf(buffer, buflen, "\n");
652         buffer += n;
653         buflen -= n;
654
655         if (file->cont_filename[0] != '\0') {
656             n = snprintf(buffer, buflen, "CONT_FILENAME=%s\n",
657                 file->cont_filename);
658             buffer += n;
659             buflen -= n;
660         }
661         if (file->is_partial != 0) {
662             n = snprintf(buffer, buflen, "PARTIAL=YES\n");
663             buffer += n;
664             buflen -= n;
665         }
666
667         n = snprintf(buffer, buflen, 
668             "To restore, position tape at start of file and run:\n");
669         buffer += n;
670         buflen -= n;
671
672         /* \014 == ^L == form feed */
673         n = snprintf(buffer, buflen,
674             "\tdd if=<tape> bs=" SIZE_T_FMT "k skip=1 | %s %s %s\n\014\n",
675             (SIZE_T_FMT_TYPE)file->blocksize / 1024, file->decrypt_cmd,
676             file->uncompress_cmd, file->recover_cmd);
677         break;
678
679     case F_TAPEEND:
680         validate_datestamp(file->datestamp);
681         snprintf(buffer, buflen, "AMANDA: TAPEEND DATE %s\n\014\n",
682             file->datestamp);
683         break;
684
685     case F_UNKNOWN:
686     case F_EMPTY:
687     case F_WEIRD:
688     default:
689         error("Invalid header type: %d (%s)",
690                 file->type, filetype2str(file->type));
691         /*NOTREACHED*/
692     }
693 }
694
695 /*
696  * Prints the contents of the file structure.
697  */
698 void
699 print_header(
700     FILE *              outf,
701     const dumpfile_t *  file)
702 {
703     char *qdisk;
704     char number[NUM_STR_SIZE*2];
705
706     switch(file->type) {
707     case F_EMPTY:
708         fprintf(outf, "EMPTY file\n");
709         break;
710
711     case F_UNKNOWN:
712         fprintf(outf, "UNKNOWN file\n");
713         break;
714
715     case F_WEIRD:
716         fprintf(outf, "WEIRD file\n");
717         break;
718
719     case F_TAPESTART:
720         fprintf(outf, "start of tape: date %s label %s\n",
721                file->datestamp, file->name);
722         break;
723
724     case F_DUMPFILE:
725     case F_CONT_DUMPFILE:
726         qdisk = quote_string(file->disk);
727         fprintf(outf, "%s: date %s host %s disk %s lev %d comp %s",
728             filetype2str(file->type), file->datestamp, file->name,
729             qdisk, file->dumplevel, file->comp_suffix);
730         if (*file->program)
731             fprintf(outf, " program %s",file->program);
732         if (strcmp(file->encrypt_suffix, "enc") == 0)
733             fprintf(outf, " crypt %s", file->encrypt_suffix);
734         if (*file->srvcompprog)
735             fprintf(outf, " server_custom_compress %s", file->srvcompprog);
736         if (*file->clntcompprog)
737             fprintf(outf, " client_custom_compress %s", file->clntcompprog);
738         if (*file->srv_encrypt)
739             fprintf(outf, " server_encrypt %s", file->srv_encrypt);
740         if (*file->clnt_encrypt)
741             fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
742         if (*file->srv_decrypt_opt)
743             fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
744         if (*file->clnt_decrypt_opt)
745             fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
746         fprintf(outf, "\n");
747         amfree(qdisk);
748         break;
749
750     case F_SPLIT_DUMPFILE:
751         if(file->totalparts > 0){
752             snprintf(number, SIZEOF(number), "%d", file->totalparts);
753         }   
754         else snprintf(number, SIZEOF(number), "UNKNOWN");
755         qdisk = quote_string(file->disk);
756         fprintf(outf, "split dumpfile: date %s host %s disk %s part %d/%s lev %d comp %s",
757                       file->datestamp, file->name, qdisk, file->partnum,
758                       number, file->dumplevel, file->comp_suffix);
759         if (*file->program)
760             fprintf(outf, " program %s",file->program);
761         if (strcmp(file->encrypt_suffix, "enc") == 0)
762             fprintf(outf, " crypt %s", file->encrypt_suffix);
763         if (*file->srvcompprog)
764             fprintf(outf, " server_custom_compress %s", file->srvcompprog);
765         if (*file->clntcompprog)
766             fprintf(outf, " client_custom_compress %s", file->clntcompprog);
767         if (*file->srv_encrypt)
768             fprintf(outf, " server_encrypt %s", file->srv_encrypt);
769         if (*file->clnt_encrypt)
770             fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
771         if (*file->srv_decrypt_opt)
772             fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
773         if (*file->clnt_decrypt_opt)
774             fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
775         fprintf(outf, "\n");
776         amfree(qdisk);
777         break;
778
779     case F_TAPEEND:
780         fprintf(outf, "end of tape: date %s\n", file->datestamp);
781         break;
782     }
783 }
784
785 int
786 known_compress_type(
787     const dumpfile_t *  file)
788 {
789     if(strcmp(file->comp_suffix, ".Z") == 0)
790         return 1;
791 #ifdef HAVE_GZIP
792     if(strcmp(file->comp_suffix, ".gz") == 0)
793         return 1;
794 #endif
795     if(strcmp(file->comp_suffix, "cust") == 0)
796         return 1;
797     return 0;
798 }
799
800 static const struct {
801     filetype_t type;
802     const char *str;
803 } filetypetab[] = {
804     { F_UNKNOWN, "UNKNOWN" },
805     { F_WEIRD, "WEIRD" },
806     { F_TAPESTART, "TAPESTART" },
807     { F_TAPEEND,  "TAPEEND" },
808     { F_DUMPFILE, "FILE" },
809     { F_CONT_DUMPFILE, "CONT_FILE" },
810     { F_SPLIT_DUMPFILE, "SPLIT_FILE" }
811 };
812 #define NFILETYPES      (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
813
814 static const char *
815 filetype2str(
816     filetype_t  type)
817 {
818     int i;
819
820     for (i = 0; i < (int)NFILETYPES; i++)
821         if (filetypetab[i].type == type)
822             return (filetypetab[i].str);
823     return ("UNKNOWN");
824 }
825
826 static filetype_t
827 str2filetype(
828     const char *str)
829 {
830     int i;
831
832     for (i = 0; i < (int)NFILETYPES; i++)
833         if (strcmp(filetypetab[i].str, str) == 0)
834             return (filetypetab[i].type);
835     return (F_UNKNOWN);
836 }