2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: fileheader.c,v 1.40 2006/07/01 00:10:38 paddy_s Exp $
31 #include "fileheader.h"
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 *);
42 memset(file, '\0', SIZEOF(*file));
43 file->blocksize = DISK_BLOCK_BYTES;
59 fprintf(stderr, "%s: strange amanda header: \"%.*s\"\n", get_pname(),
62 fprintf(stderr, "%s: Expected: \"%s\" Actual: \"%s\"\n", get_pname(),
75 char *buf, *line, *tok, *line1;
80 /* put the buffer into a writable chunk of memory and nul-term it */
81 buf = alloc(buflen + 1);
82 memcpy(buf, buffer, buflen);
87 for (line = buf, lsize = 0; lsize < buflen; line++) {
88 if ((*line == '\n') && !in_quotes)
92 in_quotes = !in_quotes;
93 } else if ((*line == '\\') && (*(line + 1) == '"')) {
100 line1 = alloc(lsize + 1);
101 strncpy(line1, buf, lsize);
105 tok = strtok(line1, " ");
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);
116 if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
118 file->type = F_UNKNOWN;
123 tok = strtok(NULL, " ");
125 strange_header(file, buffer, buflen, "<file type>", tok);
128 file->type = str2filetype(tok);
130 switch (file->type) {
132 tok = strtok(NULL, " ");
133 if ((tok == NULL) || (strcmp(tok, "DATE") != 0)) {
134 strange_header(file, buffer, buflen, "DATE", tok);
138 tok = strtok(NULL, " ");
140 strange_header(file, buffer, buflen, "<date stamp>", tok);
143 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
145 tok = strtok(NULL, " ");
146 if ((tok == NULL) || (strcmp(tok, "TAPE") != 0)) {
147 strange_header(file, buffer, buflen, "TAPE", tok);
151 tok = strtok(NULL, " ");
153 strange_header(file, buffer, buflen, "<file type>", tok);
156 strncpy(file->name, tok, SIZEOF(file->name) - 1);
160 case F_CONT_DUMPFILE:
161 case F_SPLIT_DUMPFILE:
162 tok = strtok(NULL, " ");
164 strange_header(file, buffer, buflen, "<date stamp>", tok);
167 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
169 tok = strtok(NULL, " ");
171 strange_header(file, buffer, buflen, "<file name>", tok);
174 strncpy(file->name, tok, SIZEOF(file->name) - 1);
176 tok = strquotedstr();
178 strange_header(file, buffer, buflen, "<disk name>", tok);
181 uqname = unquote_string(tok);
182 strncpy(file->disk, uqname, SIZEOF(file->disk) - 1);
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);
192 tok = strtok(NULL, "/");
193 if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) {
194 strange_header(file, buffer, buflen, "<part num param>", tok);
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);
208 tok = strtok(NULL, " ");
209 if ((tok == NULL) || (strcmp(tok, "lev") != 0)) {
210 strange_header(file, buffer, buflen, "lev", tok);
214 tok = strtok(NULL, " ");
215 if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) {
216 strange_header(file, buffer, buflen, "<dump level param>", tok);
220 tok = strtok(NULL, " ");
221 if ((tok == NULL) || (strcmp(tok, "comp") != 0)) {
222 strange_header(file, buffer, buflen, "comp", tok);
226 tok = strtok(NULL, " ");
228 strange_header(file, buffer, buflen, "<comp param>", tok);
231 strncpy(file->comp_suffix, tok, SIZEOF(file->comp_suffix) - 1);
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);
238 tok = strtok(NULL, " ");
239 /* "program" is optional */
240 if (tok == NULL || strcmp(tok, "program") != 0) {
246 tok = strtok(NULL, " ");
248 strange_header(file, buffer, buflen, "<program name>", tok);
251 strncpy(file->program, tok, SIZEOF(file->program) - 1);
252 if (file->program[0] == '\0')
253 strncpy(file->program, "RESTORE", SIZEOF(file->program) - 1);
255 if ((tok = strtok(NULL, " ")) == NULL)
256 break; /* reached the end of the buffer */
258 /* "encryption" is optional */
259 if (BSTRNCMP(tok, "crypt") == 0) {
260 tok = strtok(NULL, " ");
262 strange_header(file, buffer, buflen, "<crypt param>", tok);
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)
272 /* "srvcompprog" is optional */
273 if (BSTRNCMP(tok, "server_custom_compress") == 0) {
274 tok = strtok(NULL, " ");
276 strange_header(file, buffer, buflen,
277 "<server custom compress param>", tok);
280 strncpy(file->srvcompprog, tok, SIZEOF(file->srvcompprog) - 1);
281 if ((tok = strtok(NULL, " ")) == NULL)
285 /* "clntcompprog" is optional */
286 if (BSTRNCMP(tok, "client_custom_compress") == 0) {
287 tok = strtok(NULL, " ");
289 strange_header(file, buffer, buflen,
290 "<client custom compress param>", tok);
293 strncpy(file->clntcompprog, tok, SIZEOF(file->clntcompprog) - 1);
294 if ((tok = strtok(NULL, " ")) == NULL)
298 /* "srv_encrypt" is optional */
299 if (BSTRNCMP(tok, "server_encrypt") == 0) {
300 tok = strtok(NULL, " ");
302 strange_header(file, buffer, buflen,
303 "<server encrypt param>", tok);
306 strncpy(file->srv_encrypt, tok, SIZEOF(file->srv_encrypt) - 1);
307 if ((tok = strtok(NULL, " ")) == NULL)
311 /* "clnt_encrypt" is optional */
312 if (BSTRNCMP(tok, "client_encrypt") == 0) {
313 tok = strtok(NULL, " ");
315 strange_header(file, buffer, buflen,
316 "<client encrypt param>", tok);
319 strncpy(file->clnt_encrypt, tok, SIZEOF(file->clnt_encrypt) - 1);
320 if ((tok = strtok(NULL, " ")) == NULL)
324 /* "srv_decrypt_opt" is optional */
325 if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
326 tok = strtok(NULL, " ");
328 strange_header(file, buffer, buflen,
329 "<server decrypt param>", tok);
332 strncpy(file->srv_decrypt_opt, tok,
333 SIZEOF(file->srv_decrypt_opt) - 1);
334 if ((tok = strtok(NULL, " ")) == NULL)
338 /* "clnt_decrypt_opt" is optional */
339 if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
340 tok = strtok(NULL, " ");
342 strange_header(file, buffer, buflen,
343 "<client decrypt param>", tok);
346 strncpy(file->clnt_decrypt_opt, tok,
347 SIZEOF(file->clnt_decrypt_opt) - 1);
348 if ((tok = strtok(NULL, " ")) == NULL)
355 tok = strtok(NULL, " ");
356 /* DATE is optional */
358 if (strcmp(tok, "DATE") == 0) {
359 tok = strtok(NULL, " ");
361 file->datestamp[0] = '\0';
363 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
365 strange_header(file, buffer, buflen, "<DATE>", tok);
368 file->datestamp[0] = '\0';
373 strange_header(file, buffer, buflen,
374 "TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND", tok);
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);
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");
398 #define SC "To restore, position tape at start of file and run:"
399 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
403 #define SC "\tdd if=<tape> bs="
404 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
405 char *cmd1, *cmd2, *cmd3=NULL;
407 /* skip over dd command */
408 if ((cmd1 = strchr(line, '|')) == NULL) {
410 strncpy(file->recover_cmd, "BUG",
411 SIZEOF(file->recover_cmd) - 1);
416 /* block out first pipeline command */
417 if ((cmd2 = strchr(cmd1, '|')) != NULL) {
419 if ((cmd3 = strchr(cmd2, '|')) != NULL)
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.
434 strncpy(file->recover_cmd, cmd1,
435 SIZEOF(file->recover_cmd) - 1);
437 snprintf(file->uncompress_cmd,
438 SIZEOF(file->uncompress_cmd), "%s|", cmd1);
439 strncpy(file->recover_cmd, cmd2,
440 SIZEOF(file->recover_cmd) - 1);
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);
453 /* XXX complain about weird lines? */
463 const dumpfile_t *file)
465 const char *pname = get_pname();
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,
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,
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,
484 dbprintf(("%s: clntcompprog = '%s'\n", pname,
485 file->clntcompprog));
486 dbprintf(("%s: srv_encrypt = '%s'\n", pname,
488 dbprintf(("%s: clnt_encrypt = '%s'\n", pname,
489 file->clnt_encrypt));
490 dbprintf(("%s: recover_cmd = '%s'\n", pname,
492 dbprintf(("%s: uncompress_cmd = '%s'\n", pname,
493 file->uncompress_cmd));
494 dbprintf(("%s: encrypt_cmd = '%s'\n", pname,
496 dbprintf(("%s: decrypt_cmd = '%s'\n", pname,
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));
515 if (strlen(name) == 0) {
516 error("Invalid name '%s'\n", name);
523 const char *datestamp)
525 if (strcmp(datestamp, "X") == 0) {
529 if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
532 if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
535 error("Invalid datestamp '%s'\n", datestamp);
542 const int totalparts)
545 error("Invalid partnum (%d)\n", partnum);
549 if (partnum > totalparts && totalparts >= 0) {
550 error("Invalid partnum (%d) > totalparts (%d)\n",
551 partnum, totalparts);
559 const dumpfile_t * file,
564 char split_data[128] = "";
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);
571 memset(buffer,'\0',buflen);
573 switch (file->type) {
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);
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);
588 case F_CONT_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,
598 file->dumplevel, file->comp_suffix, file->program);
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);
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);
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);
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);
651 n = snprintf(buffer, buflen, "\n");
655 if (file->cont_filename[0] != '\0') {
656 n = snprintf(buffer, buflen, "CONT_FILENAME=%s\n",
657 file->cont_filename);
661 if (file->is_partial != 0) {
662 n = snprintf(buffer, buflen, "PARTIAL=YES\n");
667 n = snprintf(buffer, buflen,
668 "To restore, position tape at start of file and run:\n");
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);
680 validate_datestamp(file->datestamp);
681 snprintf(buffer, buflen, "AMANDA: TAPEEND DATE %s\n\014\n",
689 error("Invalid header type: %d (%s)",
690 file->type, filetype2str(file->type));
696 * Prints the contents of the file structure.
701 const dumpfile_t * file)
704 char number[NUM_STR_SIZE*2];
708 fprintf(outf, "EMPTY file\n");
712 fprintf(outf, "UNKNOWN file\n");
716 fprintf(outf, "WEIRD file\n");
720 fprintf(outf, "start of tape: date %s label %s\n",
721 file->datestamp, file->name);
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);
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);
750 case F_SPLIT_DUMPFILE:
751 if(file->totalparts > 0){
752 snprintf(number, SIZEOF(number), "%d", file->totalparts);
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);
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);
780 fprintf(outf, "end of tape: date %s\n", file->datestamp);
787 const dumpfile_t * file)
789 if(strcmp(file->comp_suffix, ".Z") == 0)
792 if(strcmp(file->comp_suffix, ".gz") == 0)
795 if(strcmp(file->comp_suffix, "cust") == 0)
800 static const struct {
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" }
812 #define NFILETYPES (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
820 for (i = 0; i < (int)NFILETYPES; i++)
821 if (filetypetab[i].type == type)
822 return (filetypetab[i].str);
832 for (i = 0; i < (int)NFILETYPES; i++)
833 if (strcmp(filetypetab[i].str, str) == 0)
834 return (filetypetab[i].type);