2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: fileheader.c 6512 2007-05-24 17:00:24Z ian $
32 #include "fileheader.h"
37 static const char * filetype2str(filetype_t);
38 static filetype_t str2filetype(const char *);
39 static void strange_header(dumpfile_t *, const char *,
40 size_t, const char *, const char *);
41 static char *quote_heredoc(char *text, char *delimiter_prefix);
42 static char *parse_heredoc(char *line, char **saveptr);
48 memset(file, '\0', SIZEOF(*file));
66 g_debug("strange amanda header: \"%.*s\"", (int)buflen, buffer);
67 g_debug("Expected: \"%s\" Actual: \"%s\"", expected, actual);
72 /* chop whitespace off of a string, in place */
81 /* trim leading space */
82 while (g_ascii_isspace(*s)) { s++; }
84 memmove(str, s, strlen(s)+1);
86 /* trim trailing space */
88 for (s = str+strlen(str)-1; s >= str; s--) {
89 if (!g_ascii_isspace(*s))
102 char *buf, *line, *tok, *line1;
106 char *saveptr = NULL;
108 /* put the buffer into a writable chunk of memory and nul-term it */
109 buf = alloc(buflen + 1);
110 memcpy(buf, buffer, buflen);
114 /* extract the first unquoted line */
116 for (line = buf, lsize = 0; lsize < buflen; line++) {
117 if ((*line == '\n') && !in_quotes)
121 in_quotes = !in_quotes;
122 } else if ((*line == '\\') && (*(line + 1) == '"')) {
129 line1 = alloc(lsize + 1);
130 strncpy(line1, buf, lsize);
134 tok = strtok_r(line1, " ", &saveptr);
136 g_debug("Empty amanda header: buflen=%zu lsize=%zu buf='%s'", buflen, lsize, buf);
137 strange_header(file, buffer, buflen, _("<Non-empty line>"), tok);
141 if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
143 file->type = F_WEIRD;
148 tok = strtok_r(NULL, " ", &saveptr);
150 strange_header(file, buffer, buflen, _("<file type>"), tok);
153 file->type = str2filetype(tok);
155 switch (file->type) {
157 tok = strtok_r(NULL, " ", &saveptr);
158 if ((tok == NULL) || (strcmp(tok, "DATE") != 0)) {
159 strange_header(file, buffer, buflen, "DATE", tok);
163 tok = strtok_r(NULL, " ", &saveptr);
165 strange_header(file, buffer, buflen, _("<date stamp>"), tok);
168 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
170 tok = strtok_r(NULL, " ", &saveptr);
171 if ((tok == NULL) || (strcmp(tok, "TAPE") != 0)) {
172 strange_header(file, buffer, buflen, "TAPE", tok);
176 tok = strtok_r(NULL, " ", &saveptr);
178 strange_header(file, buffer, buflen, _("<file type>"), tok);
181 strncpy(file->name, tok, SIZEOF(file->name) - 1);
185 case F_CONT_DUMPFILE:
186 case F_SPLIT_DUMPFILE:
187 tok = strtok_r(NULL, " ", &saveptr);
189 strange_header(file, buffer, buflen, _("<date stamp>"), tok);
192 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
194 tok = strtok_r(NULL, " ", &saveptr);
196 strange_header(file, buffer, buflen, _("<file name>"), tok);
199 strncpy(file->name, tok, SIZEOF(file->name) - 1);
201 tok = strquotedstr(&saveptr);
203 strange_header(file, buffer, buflen, _("<disk name>"), tok);
206 uqname = unquote_string(tok);
207 strncpy(file->disk, uqname, SIZEOF(file->disk) - 1);
210 if(file->type == F_SPLIT_DUMPFILE) {
211 tok = strtok_r(NULL, " ", &saveptr);
212 if (tok == NULL || strcmp(tok, "part") != 0) {
213 strange_header(file, buffer, buflen, "part", tok);
217 tok = strtok_r(NULL, "/", &saveptr);
218 if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) {
219 strange_header(file, buffer, buflen, _("<part num param>"), tok);
223 /* If totalparts == -1, then the original dump was done in
224 streaming mode (no holding disk), thus we don't know how
225 many parts there are. */
226 tok = strtok_r(NULL, " ", &saveptr);
227 if((tok == NULL) || (sscanf(tok, "%d", &file->totalparts) != 1)) {
228 strange_header(file, buffer, buflen, _("<total parts param>"), tok);
231 } else if (file->type == F_DUMPFILE) {
232 /* only one part in this dump, so call it partnum 1 */
234 file->totalparts = 1;
237 file->totalparts = 0;
240 tok = strtok_r(NULL, " ", &saveptr);
241 if ((tok == NULL) || (strcmp(tok, "lev") != 0)) {
242 strange_header(file, buffer, buflen, "lev", tok);
246 tok = strtok_r(NULL, " ", &saveptr);
247 if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) {
248 strange_header(file, buffer, buflen, _("<dump level param>"), tok);
252 tok = strtok_r(NULL, " ", &saveptr);
253 if ((tok == NULL) || (strcmp(tok, "comp") != 0)) {
254 strange_header(file, buffer, buflen, "comp", tok);
258 tok = strtok_r(NULL, " ", &saveptr);
260 strange_header(file, buffer, buflen, _("<comp param>"), tok);
263 strncpy(file->comp_suffix, tok, SIZEOF(file->comp_suffix) - 1);
265 file->compressed = (0 != strcmp(file->comp_suffix, "N"));
266 if (file->compressed) {
267 /* compatibility with pre-2.2 amanda */
268 if (strcmp(file->comp_suffix, "C") == 0)
269 strncpy(file->comp_suffix, ".Z", SIZEOF(file->comp_suffix) - 1);
271 strcpy(file->comp_suffix, "");
274 tok = strtok_r(NULL, " ", &saveptr);
275 /* "program" is optional */
276 if (tok == NULL || strcmp(tok, "program") != 0) {
280 tok = strtok_r(NULL, " ", &saveptr);
282 strange_header(file, buffer, buflen, _("<program name>"), tok);
285 strncpy(file->program, tok, SIZEOF(file->program) - 1);
287 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
288 break; /* reached the end of the buffer */
290 /* encryption is optional */
291 if (BSTRNCMP(tok, "crypt") == 0) {
292 tok = strtok_r(NULL, " ", &saveptr);
294 strange_header(file, buffer, buflen, _("<crypt param>"), tok);
297 strncpy(file->encrypt_suffix, tok,
298 SIZEOF(file->encrypt_suffix) - 1);
301 /* for compatibility with who-knows-what, allow "comp N" to be
302 * equivalent to no compression */
303 if (0 == BSTRNCMP(file->encrypt_suffix, "N")) {
305 strcpy(file->encrypt_suffix, "");
308 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
312 /* "srvcompprog" is optional */
313 if (BSTRNCMP(tok, "server_custom_compress") == 0) {
314 tok = strtok_r(NULL, " ", &saveptr);
316 strange_header(file, buffer, buflen,
317 _("<server custom compress param>"), tok);
320 strncpy(file->srvcompprog, tok, SIZEOF(file->srvcompprog) - 1);
321 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
325 /* "clntcompprog" is optional */
326 if (BSTRNCMP(tok, "client_custom_compress") == 0) {
327 tok = strtok_r(NULL, " ", &saveptr);
329 strange_header(file, buffer, buflen,
330 _("<client custom compress param>"), tok);
333 strncpy(file->clntcompprog, tok, SIZEOF(file->clntcompprog) - 1);
334 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
338 /* "srv_encrypt" is optional */
339 if (BSTRNCMP(tok, "server_encrypt") == 0) {
340 tok = strtok_r(NULL, " ", &saveptr);
342 strange_header(file, buffer, buflen,
343 _("<server encrypt param>"), tok);
346 strncpy(file->srv_encrypt, tok, SIZEOF(file->srv_encrypt) - 1);
347 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
351 /* "clnt_encrypt" is optional */
352 if (BSTRNCMP(tok, "client_encrypt") == 0) {
353 tok = strtok_r(NULL, " ", &saveptr);
355 strange_header(file, buffer, buflen,
356 _("<client encrypt param>"), tok);
359 strncpy(file->clnt_encrypt, tok, SIZEOF(file->clnt_encrypt) - 1);
360 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
364 /* "srv_decrypt_opt" is optional */
365 if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
366 tok = strtok_r(NULL, " ", &saveptr);
368 strange_header(file, buffer, buflen,
369 _("<server decrypt param>"), tok);
372 strncpy(file->srv_decrypt_opt, tok,
373 SIZEOF(file->srv_decrypt_opt) - 1);
374 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
378 /* "clnt_decrypt_opt" is optional */
379 if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
380 tok = strtok_r(NULL, " ", &saveptr);
382 strange_header(file, buffer, buflen,
383 _("<client decrypt param>"), tok);
386 strncpy(file->clnt_decrypt_opt, tok,
387 SIZEOF(file->clnt_decrypt_opt) - 1);
388 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
395 tok = strtok_r(NULL, " ", &saveptr);
396 /* DATE is optional */
398 if (strcmp(tok, "DATE") == 0) {
399 tok = strtok_r(NULL, " ", &saveptr);
401 file->datestamp[0] = '\0';
403 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
405 strange_header(file, buffer, buflen, _("<DATE>"), tok);
408 file->datestamp[0] = '\0';
413 /* nothing follows */
417 strange_header(file, buffer, buflen,
418 _("TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND|NOOP"), tok);
422 (void)strtok_r(buf, "\n", &saveptr); /* this is the first line */
423 /* iterate through the rest of the lines */
424 while ((line = strtok_r(NULL, "\n", &saveptr)) != NULL) {
425 #define SC "CONT_FILENAME="
426 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
427 line += SIZEOF(SC) - 1;
428 strncpy(file->cont_filename, line,
429 SIZEOF(file->cont_filename) - 1);
434 #define SC "PARTIAL="
435 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
436 line += SIZEOF(SC) - 1;
437 file->is_partial = !strcasecmp(line, "yes");
441 #define SC "APPLICATION="
442 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
443 line += SIZEOF(SC) - 1;
444 strncpy(file->application, line,
445 SIZEOF(file->application) - 1);
450 #define SC "ORIGSIZE="
451 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
452 line += SIZEOF(SC) - 1;
453 file->orig_size = OFF_T_ATOI(line);
458 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
459 line += SIZEOF(SC) - 1;
460 file->dle_str = parse_heredoc(line, &saveptr);
464 #define SC _("To restore, position tape at start of file and run:")
465 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
469 #define SC "\tdd if=<tape> "
470 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
471 char *cmd1, *cmd2, *cmd3=NULL;
473 /* skip over dd command */
474 if ((cmd1 = strchr(line, '|')) == NULL) {
476 strncpy(file->recover_cmd, "BUG",
477 SIZEOF(file->recover_cmd) - 1);
482 /* block out first pipeline command */
483 if ((cmd2 = strchr(cmd1, '|')) != NULL) {
485 if ((cmd3 = strchr(cmd2, '|')) != NULL)
489 /* clean up some extra spaces in various fields */
494 /* three cmds: decrypt | uncompress | recover
495 * two cmds: uncompress | recover
496 * XXX note that if there are two cmds, the first one
497 * XXX could be either uncompress or decrypt. Since no
498 * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
499 * XXX for header information.
505 strncpy(file->recover_cmd, cmd1,
506 SIZEOF(file->recover_cmd) - 1);
508 g_snprintf(file->uncompress_cmd,
509 SIZEOF(file->uncompress_cmd), "%s |", cmd1);
510 strncpy(file->recover_cmd, cmd2,
511 SIZEOF(file->recover_cmd) - 1);
513 } else { /* cmd3 presents: decrypt | uncompress | recover */
514 g_snprintf(file->decrypt_cmd,
515 SIZEOF(file->decrypt_cmd), "%s |", cmd1);
516 g_snprintf(file->uncompress_cmd,
517 SIZEOF(file->uncompress_cmd), "%s |", cmd2);
518 strncpy(file->recover_cmd, cmd3,
519 SIZEOF(file->recover_cmd) - 1);
524 /* XXX complain about weird lines? */
534 const dumpfile_t *file)
536 g_debug(_("Contents of *(dumpfile_t *)%p:"), file);
537 g_debug(_(" type = %d (%s)"),
538 file->type, filetype2str(file->type));
539 g_debug(_(" datestamp = '%s'"), file->datestamp);
540 g_debug(_(" dumplevel = %d"), file->dumplevel);
541 g_debug(_(" compressed = %d"), file->compressed);
542 g_debug(_(" encrypted = %d"), file->encrypted);
543 g_debug(_(" comp_suffix = '%s'"), file->comp_suffix);
544 g_debug(_(" encrypt_suffix = '%s'"), file->encrypt_suffix);
545 g_debug(_(" name = '%s'"), file->name);
546 g_debug(_(" disk = '%s'"), file->disk);
547 g_debug(_(" program = '%s'"), file->program);
548 g_debug(_(" application = '%s'"), file->application);
549 g_debug(_(" srvcompprog = '%s'"), file->srvcompprog);
550 g_debug(_(" clntcompprog = '%s'"), file->clntcompprog);
551 g_debug(_(" srv_encrypt = '%s'"), file->srv_encrypt);
552 g_debug(_(" clnt_encrypt = '%s'"), file->clnt_encrypt);
553 g_debug(_(" recover_cmd = '%s'"), file->recover_cmd);
554 g_debug(_(" uncompress_cmd = '%s'"), file->uncompress_cmd);
555 g_debug(_(" decrypt_cmd = '%s'"), file->decrypt_cmd);
556 g_debug(_(" srv_decrypt_opt = '%s'"), file->srv_decrypt_opt);
557 g_debug(_(" clnt_decrypt_opt = '%s'"), file->clnt_decrypt_opt);
558 g_debug(_(" cont_filename = '%s'"), file->cont_filename);
560 g_debug(_(" dle_str = %s"), file->dle_str);
562 g_debug(_(" dle_str = (null)"));
563 g_debug(_(" is_partial = %d"), file->is_partial);
564 g_debug(_(" partnum = %d"), file->partnum);
565 g_debug(_(" totalparts = %d"), file->totalparts);
567 g_debug(_(" blocksize = %zu"), file->blocksize);
571 validate_nonempty_str(
575 if (strlen(val) == 0) {
576 error(_("Invalid %s '%s'\n"), name, val);
583 const char *val1, const char *val2,
584 const char *name1, const char *name2)
586 if (*val1 && *val2) {
587 error("cannot set both %s and %s\n", name1, name2);
596 if (strchr(val, ' ') != NULL) {
597 error(_("%s cannot contain spaces\n"), name);
607 if (strlen(cmd) && cmd[strlen(cmd)-1] != '|') {
608 error("invalid %s (must end with '|'): '%s'\n", name, cmd);
613 validate_encrypt_suffix(
618 if (!suff[0] || (0 == strcmp(suff, "N"))) {
619 error(_("Invalid encrypt_suffix '%s'\n"), suff);
622 if (suff[0] && (0 != strcmp(suff, "N"))) {
623 error(_("Invalid header: encrypt_suffix '%s' specified but not encrypted\n"), suff);
630 const char *datestamp)
632 if (strcmp(datestamp, "X") == 0) {
636 if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
639 if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
642 error(_("Invalid datestamp '%s'\n"), datestamp);
649 const int totalparts)
652 error(_("Invalid partnum (%d)\n"), partnum);
656 if (partnum > totalparts && totalparts >= 0) {
657 error(_("Invalid partnum (%d) > totalparts (%d)\n"),
658 partnum, totalparts);
664 build_header(const dumpfile_t * file, size_t *size, size_t max_size)
666 GString *rval, *split_data;
671 min_size = size? *size : max_size;
672 g_debug(_("Building type %s header of %zu-%zu bytes with name='%s' disk='%s' dumplevel=%d and blocksize=%zu"),
673 filetype2str(file->type), min_size, max_size,
674 file->name, file->disk, file->dumplevel, file->blocksize);
676 rval = g_string_sized_new(min_size);
677 split_data = g_string_sized_new(10);
679 switch (file->type) {
681 validate_nonempty_str(file->name, "name");
682 validate_datestamp(file->datestamp);
683 g_string_printf(rval,
684 "AMANDA: TAPESTART DATE %s TAPE %s\n\014\n",
685 file->datestamp, file->name);
688 case F_SPLIT_DUMPFILE:
689 validate_parts(file->partnum, file->totalparts);
690 g_string_printf(split_data,
691 " part %d/%d ", file->partnum, file->totalparts);
694 case F_CONT_DUMPFILE:
696 validate_nonempty_str(file->name, "name");
697 validate_nonempty_str(file->program, "program");
698 validate_datestamp(file->datestamp);
699 validate_encrypt_suffix(file->encrypted, file->encrypt_suffix);
700 qname = quote_string(file->disk);
701 program = stralloc(file->program);
702 if (match("^.*[.][Ee][Xx][Ee]$", program)) {
703 /* Trim ".exe" from program name */
704 program[strlen(program) - strlen(".exe")] = '\0';
706 g_string_printf(rval,
707 "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
708 filetype2str(file->type),
709 file->datestamp, file->name, qname,
712 file->compressed? file->comp_suffix : "N",
717 /* only output crypt if it's enabled */
718 if (file->encrypted) {
719 g_string_append_printf(rval, " crypt %s", file->encrypt_suffix);
722 validate_not_both(file->srvcompprog, file->clntcompprog,
723 "srvcompprog", "clntcompprog");
724 if (*file->srvcompprog) {
725 validate_no_space(file->srvcompprog, "srvcompprog");
726 g_string_append_printf(rval, " server_custom_compress %s",
728 } else if (*file->clntcompprog) {
729 validate_no_space(file->clntcompprog, "clntcompprog");
730 g_string_append_printf(rval, " client_custom_compress %s",
734 validate_not_both(file->srv_encrypt, file->clnt_encrypt,
735 "srv_encrypt", "clnt_encrypt");
736 if (*file->srv_encrypt) {
737 validate_no_space(file->srv_encrypt, "srv_encrypt");
738 g_string_append_printf(rval, " server_encrypt %s",
740 } else if (*file->clnt_encrypt) {
741 validate_no_space(file->clnt_encrypt, "clnt_encrypt");
742 g_string_append_printf(rval, " client_encrypt %s",
746 validate_not_both(file->srv_decrypt_opt, file->clnt_decrypt_opt,
747 "srv_decrypt_opt", "clnt_decrypt_opt");
748 if (*file->srv_decrypt_opt) {
749 validate_no_space(file->srv_decrypt_opt, "srv_decrypt_opt");
750 g_string_append_printf(rval, " server_decrypt_option %s",
751 file->srv_decrypt_opt);
752 } else if (*file->clnt_decrypt_opt) {
753 g_string_append_printf(rval, " client_decrypt_option %s",
754 file->clnt_decrypt_opt);
757 g_string_append_printf(rval, "\n");
759 if (file->cont_filename[0] != '\0') {
760 g_string_append_printf(rval, "CONT_FILENAME=%s\n",
761 file->cont_filename);
763 if (file->application[0] != '\0') {
764 g_string_append_printf(rval, "APPLICATION=%s\n", file->application);
766 if (file->is_partial != 0) {
767 g_string_append_printf(rval, "PARTIAL=YES\n");
769 if (file->orig_size > 0) {
770 g_string_append_printf(rval, "ORIGSIZE=%jd\n",
771 (intmax_t)file->orig_size);
773 if (file->dle_str && strlen(file->dle_str) < max_size-2048) {
774 char *heredoc = quote_heredoc(file->dle_str, "ENDDLE");
775 g_string_append_printf(rval, "DLE=%s\n", heredoc);
779 g_string_append_printf(rval,
780 _("To restore, position tape at start of file and run:\n"));
782 g_string_append_printf(rval, "\tdd if=<tape> ");
784 g_string_append_printf(rval, "bs=%zuk ",
785 file->blocksize / 1024);
786 g_string_append_printf(rval, "skip=1 | ");
787 if (*file->recover_cmd) {
788 if (*file->decrypt_cmd) {
789 validate_pipe_cmd(file->decrypt_cmd, "decrypt_cmd");
790 g_string_append_printf(rval, "%s ", file->decrypt_cmd);
792 if (*file->uncompress_cmd) {
793 validate_pipe_cmd(file->uncompress_cmd, "uncompress_cmd");
794 g_string_append_printf(rval, "%s ", file->uncompress_cmd);
796 g_string_append_printf(rval, "%s ", file->recover_cmd);
798 if (*file->uncompress_cmd || *file->decrypt_cmd)
799 error("cannot specify uncompress_cmd or decrypt_cmd without recover_cmd\n");
801 /* \014 == ^L == form feed */
802 g_string_append_printf(rval, "\n\014\n");
806 validate_datestamp(file->datestamp);
807 g_string_printf(rval, "AMANDA: TAPEEND DATE %s\n\014\n",
812 g_string_printf(rval, "AMANDA: NOOP\n\014\n");
819 error(_("Invalid header type: %d (%s)"),
820 file->type, filetype2str(file->type));
824 g_string_free(split_data, TRUE);
827 if (rval->len > max_size) {
828 g_debug("header is larger than %zu bytes -- cannot create", max_size);
829 g_string_free(rval, TRUE);
833 /* Clear extra bytes. */
834 if (rval->len < min_size) {
835 bzero(rval->str + rval->len, rval->allocated_len - rval->len);
838 *size = MAX(min_size, (size_t)rval->len);
840 return g_string_free(rval, FALSE);
846 const dumpfile_t * file)
848 char *summ = summarize_header(file);
849 g_fprintf(outf, "%s\n", summ);
854 * Prints the contents of the file structure.
858 const dumpfile_t * file)
865 return g_strdup(_("EMPTY file"));
868 return g_strdup(_("UNKNOWN file"));
872 return g_strdup(_("WEIRD file"));
875 return g_strdup_printf(_("start of tape: date %s label %s"),
876 file->datestamp, file->name);
879 return g_strdup(_("NOOP file"));
882 case F_CONT_DUMPFILE:
883 qdisk = quote_string(file->disk);
884 summ = g_string_new("");
885 g_string_printf(summ, "%s: date %s host %s disk %s lev %d comp %s",
886 filetype2str(file->type), file->datestamp, file->name,
887 qdisk, file->dumplevel,
888 file->compressed? file->comp_suffix : "N");
892 case F_SPLIT_DUMPFILE: {
893 char totalparts[NUM_STR_SIZE*2];
894 if(file->totalparts > 0)
895 g_snprintf(totalparts, SIZEOF(totalparts), "%d", file->totalparts);
897 g_snprintf(totalparts, SIZEOF(totalparts), "UNKNOWN");
898 qdisk = quote_string(file->disk);
899 summ = g_string_new("");
900 g_string_printf(summ, "split dumpfile: date %s host %s disk %s"
901 " part %d/%s lev %d comp %s",
902 file->datestamp, file->name, qdisk, file->partnum,
903 totalparts, file->dumplevel,
904 file->compressed? file->comp_suffix : "N");
911 g_string_append_printf(summ, " program %s", file->program);
912 if (strcmp(file->encrypt_suffix, "enc") == 0)
913 g_string_append_printf(summ, " crypt %s", file->encrypt_suffix);
914 if (*file->srvcompprog)
915 g_string_append_printf(summ, " server_custom_compress %s", file->srvcompprog);
916 if (*file->clntcompprog)
917 g_string_append_printf(summ, " client_custom_compress %s", file->clntcompprog);
918 if (*file->srv_encrypt)
919 g_string_append_printf(summ, " server_encrypt %s", file->srv_encrypt);
920 if (*file->clnt_encrypt)
921 g_string_append_printf(summ, " client_encrypt %s", file->clnt_encrypt);
922 if (*file->srv_decrypt_opt)
923 g_string_append_printf(summ, " server_decrypt_option %s", file->srv_decrypt_opt);
924 if (*file->clnt_decrypt_opt)
925 g_string_append_printf(summ, " client_decrypt_option %s", file->clnt_decrypt_opt);
926 return g_string_free(summ, FALSE);
929 return g_strdup_printf("end of tape: date %s", file->datestamp);
936 const dumpfile_t * file)
938 if(strcmp(file->comp_suffix, ".Z") == 0)
941 if(strcmp(file->comp_suffix, ".gz") == 0)
944 if(strcmp(file->comp_suffix, "cust") == 0)
949 static const struct {
953 { F_UNKNOWN, "UNKNOWN" },
954 { F_WEIRD, "WEIRD" },
955 { F_TAPESTART, "TAPESTART" },
956 { F_TAPEEND, "TAPEEND" },
957 { F_DUMPFILE, "FILE" },
958 { F_CONT_DUMPFILE, "CONT_FILE" },
959 { F_SPLIT_DUMPFILE, "SPLIT_FILE" },
962 #define NFILETYPES (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
970 for (i = 0; i < (int)NFILETYPES; i++)
971 if (filetypetab[i].type == type)
972 return (filetypetab[i].str);
982 for (i = 0; i < (int)NFILETYPES; i++)
983 if (strcmp(filetypetab[i].str, str) == 0)
984 return (filetypetab[i].type);
988 gboolean headers_are_equal(dumpfile_t * a, dumpfile_t * b) {
989 if (a == NULL && b == NULL)
992 if (a == NULL || b == NULL)
995 if (a->type != b->type) return FALSE;
996 if (strcmp(a->datestamp, b->datestamp)) return FALSE;
997 if (a->dumplevel != b->dumplevel) return FALSE;
998 if (a->compressed != b->compressed) return FALSE;
999 if (a->encrypted != b->encrypted) return FALSE;
1000 if (strcmp(a->comp_suffix, b->comp_suffix)) return FALSE;
1001 if (strcmp(a->encrypt_suffix, b->encrypt_suffix)) return FALSE;
1002 if (strcmp(a->name, b->name)) return FALSE;
1003 if (strcmp(a->disk, b->disk)) return FALSE;
1004 if (strcmp(a->program, b->program)) return FALSE;
1005 if (strcmp(a->application, b->application)) return FALSE;
1006 if (strcmp(a->srvcompprog, b->srvcompprog)) return FALSE;
1007 if (strcmp(a->clntcompprog, b->clntcompprog)) return FALSE;
1008 if (strcmp(a->srv_encrypt, b->srv_encrypt)) return FALSE;
1009 if (strcmp(a->clnt_encrypt, b->clnt_encrypt)) return FALSE;
1010 if (strcmp(a->recover_cmd, b->recover_cmd)) return FALSE;
1011 if (strcmp(a->uncompress_cmd, b->uncompress_cmd)) return FALSE;
1012 if (strcmp(a->decrypt_cmd, b->decrypt_cmd)) return FALSE;
1013 if (strcmp(a->srv_decrypt_opt, b->srv_decrypt_opt)) return FALSE;
1014 if (strcmp(a->clnt_decrypt_opt, b->clnt_decrypt_opt)) return FALSE;
1015 if (strcmp(a->cont_filename, b->cont_filename)) return FALSE;
1016 if (a->dle_str != b->dle_str && a->dle_str && b->dle_str
1017 && strcmp(a->dle_str, b->dle_str)) return FALSE;
1018 if (a->is_partial != b->is_partial) return FALSE;
1019 if (a->partnum != b->partnum) return FALSE;
1020 if (a->totalparts != b->totalparts) return FALSE;
1021 if (a->blocksize != b->blocksize) return FALSE;
1023 return TRUE; /* ok, they're the same */
1026 dumpfile_t * dumpfile_copy(dumpfile_t* source) {
1027 dumpfile_t* rval = malloc(sizeof(dumpfile_t));
1028 memcpy(rval, source, sizeof(dumpfile_t));
1029 if (rval->dle_str) rval->dle_str = stralloc(rval->dle_str);
1034 dumpfile_copy_in_place(
1038 memcpy(dest, source, sizeof(dumpfile_t));
1039 if (dest->dle_str) dest->dle_str = stralloc(dest->dle_str);
1042 void dumpfile_free_data(dumpfile_t* info) {
1044 amfree(info->dle_str);
1048 void dumpfile_free(dumpfile_t* info) {
1049 dumpfile_free_data(info);
1053 static char *quote_heredoc(
1055 char *delimiter_prefix)
1057 char *delimiter = stralloc(delimiter_prefix);
1058 int delimiter_n = 0;
1059 int delimiter_len = strlen(delimiter);
1062 /* keep picking delimiters until we find one that's not a line in TEXT */
1066 gboolean found_delimiter = FALSE;
1069 if (*c == '\n' || *c == '\0') {
1070 int linelen = c - line;
1071 if (linelen == delimiter_len && 0 == strncmp(line, delimiter, linelen)) {
1072 found_delimiter = TRUE;
1081 if (!found_delimiter)
1084 delimiter = newvstrallocf(delimiter, "%s%d", delimiter_prefix, ++delimiter_n);
1085 delimiter_len = strlen(delimiter);
1088 /* we have a delimiter .. now use it */
1089 quoted = vstrallocf("<<%s\n%s\n%s", delimiter, text, delimiter);
1094 static char *parse_heredoc(
1098 char *result = NULL;
1100 if (strncmp(line, "<<", 2) == 0) {
1101 char *keyword = line+2;
1104 while((new_line = strtok_r(NULL, "\n", saveptr)) != NULL &&
1105 strcmp(new_line, keyword) != 0) {
1106 result = vstrextend(&result, new_line, "\n", NULL);
1108 /* make sure we have something */
1110 result = g_strdup("");
1111 /* remove latest '\n' */
1112 else if (strlen(result) > 0)
1113 result[strlen(result)-1] = '\0';
1115 result = stralloc(line);