1 /*
2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
4 * Copyright (c) 2007-2013 Zmanda, Inc. All Rights Reserved.
5 * All Rights Reserved.
6 *
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.
16 *
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.
23 *
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
26 */
27 /*
28 * $Id: fileheader.c 6512 2007-05-24 17:00:24Z ian $
29 */
30
31 #include "amanda.h"
32 #include "fileheader.h"
33 #include "match.h"
34 #include <glib.h>
35 #include "util.h"
36
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,
43 const char *message);
44
45 void
fh_init(dumpfile_t * file)46 fh_init(
47 dumpfile_t *file)
48 {
49 memset(file, '\0', SIZEOF(*file));
50 file->type = F_EMPTY;
51 file->blocksize = 0;
52 }
53
54 static void
strange_header(dumpfile_t * file,const char * buffer,size_t buflen,const char * expected,const char * actual)55 strange_header(
56 dumpfile_t *file,
57 const char *buffer,
58 size_t buflen,
59 const char *expected,
60 const char *actual)
61 {
62 if (actual == NULL)
63 actual = "<null>";
64 if (expected == NULL)
65 expected = "<null>";
66
67 g_debug("strange amanda header: \"%.*s\"", (int)buflen, buffer);
68 g_debug("Expected: \"%s\" Actual: \"%s\"", expected, actual);
69
70 file->type = F_WEIRD;
71 }
72
73 /* chop whitespace off of a string, in place */
74 static void
chomp(char * str)75 chomp(char *str)
76 {
77 char *s = str;
78
79 if (!str)
80 return;
81
82 /* trim leading space */
83 while (g_ascii_isspace(*s)) { s++; }
84 if (s != str)
85 memmove(str, s, strlen(s)+1);
86
87 /* trim trailing space */
88 if (*str) {
89 for (s = str+strlen(str)-1; s >= str; s--) {
90 if (!g_ascii_isspace(*s))
91 break;
92 *s = '\0';
93 }
94 }
95 }
96
97 void
parse_file_header(const char * buffer,dumpfile_t * file,size_t buflen)98 parse_file_header(
99 const char *buffer,
100 dumpfile_t *file,
101 size_t buflen)
102 {
103 char *buf, *line, *tok, *line1;
104 size_t lsize;
105 char *uqname;
106 int in_quotes;
107 char *saveptr = NULL;
108
109 /* put the buffer into a writable chunk of memory and nul-term it */
110 buf = alloc(buflen + 1);
111 memcpy(buf, buffer, buflen);
112 buf[buflen] = '\0';
113 fh_init(file);
114
115 /* extract the first unquoted line */
116 in_quotes = 0;
117 for (line = buf, lsize = 0; lsize < buflen; line++) {
118 if ((*line == '\n') && !in_quotes)
119 break;
120
121 if (*line == '"') {
122 in_quotes = !in_quotes;
123 } else if ((*line == '\\') && (*(line + 1) == '"')) {
124 line++;
125 lsize++;
126 }
127 lsize++;
128 }
129 *line = '\0';
130 line1 = alloc(lsize + 1);
131 strncpy(line1, buf, lsize);
132 line1[lsize] = '\0';
133 *line = '\n';
134
135 tok = strtok_r(line1, " ", &saveptr);
136 if (tok == NULL) {
137 g_debug("Empty amanda header: buflen=%zu lsize=%zu buf='%s'", buflen, lsize, buf);
138 strange_header(file, buffer, buflen, _("<Non-empty line>"), tok);
139 goto out;
140 }
141
142 if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
143 amfree(buf);
144 file->type = F_WEIRD;
145 amfree(line1);
146 return;
147 }
148
149 tok = strtok_r(NULL, " ", &saveptr);
150 if (tok == NULL) {
151 strange_header(file, buffer, buflen, _("<file type>"), tok);
152 goto out;
153 }
154 file->type = str2filetype(tok);
155
156 switch (file->type) {
157 case F_TAPESTART:
158 tok = strtok_r(NULL, " ", &saveptr);
159 if ((tok == NULL) || (strcmp(tok, "DATE") != 0)) {
160 strange_header(file, buffer, buflen, "DATE", tok);
161 goto out;
162 }
163
164 tok = strtok_r(NULL, " ", &saveptr);
165 if (tok == NULL) {
166 strange_header(file, buffer, buflen, _("<date stamp>"), tok);
167 goto out;
168 }
169 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
170
171 tok = strtok_r(NULL, " ", &saveptr);
172 if ((tok == NULL) || (strcmp(tok, "TAPE") != 0)) {
173 strange_header(file, buffer, buflen, "TAPE", tok);
174 goto out;
175 }
176
177 tok = strtok_r(NULL, " ", &saveptr);
178 if (tok == NULL) {
179 strange_header(file, buffer, buflen, _("<file type>"), tok);
180 goto out;
181 }
182 strncpy(file->name, tok, SIZEOF(file->name) - 1);
183 break;
184
185 case F_DUMPFILE:
186 case F_CONT_DUMPFILE:
187 case F_SPLIT_DUMPFILE:
188 tok = strtok_r(NULL, " ", &saveptr);
189 if (tok == NULL) {
190 strange_header(file, buffer, buflen, _("<date stamp>"), tok);
191 goto out;
192 }
193 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
194
195 tok = strtok_r(NULL, " ", &saveptr);
196 if (tok == NULL) {
197 strange_header(file, buffer, buflen, _("<file name>"), tok);
198 goto out;
199 }
200 strncpy(file->name, tok, SIZEOF(file->name) - 1);
201
202 tok = strquotedstr(&saveptr);
203 if (tok == NULL) {
204 strange_header(file, buffer, buflen, _("<disk name>"), tok);
205 goto out;
206 }
207 uqname = unquote_string(tok);
208 strncpy(file->disk, uqname, SIZEOF(file->disk) - 1);
209 amfree(uqname);
210
211 if(file->type == F_SPLIT_DUMPFILE) {
212 tok = strtok_r(NULL, " ", &saveptr);
213 if (tok == NULL || strcmp(tok, "part") != 0) {
214 strange_header(file, buffer, buflen, "part", tok);
215 goto out;
216 }
217
218 tok = strtok_r(NULL, "/", &saveptr);
219 if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) {
220 strange_header(file, buffer, buflen, _("<part num param>"), tok);
221 goto out;
222 }
223
224 /* If totalparts == -1, then the original dump was done in
225 streaming mode (no holding disk), thus we don't know how
226 many parts there are. */
227 tok = strtok_r(NULL, " ", &saveptr);
228 if((tok == NULL) || (sscanf(tok, "%d", &file->totalparts) != 1)) {
229 strange_header(file, buffer, buflen, _("<total parts param>"), tok);
230 goto out;
231 }
232 } else if (file->type == F_DUMPFILE) {
233 /* only one part in this dump, so call it partnum 1 */
234 file->partnum = 1;
235 file->totalparts = 1;
236 } else {
237 file->partnum = 0;
238 file->totalparts = 0;
239 }
240
241 tok = strtok_r(NULL, " ", &saveptr);
242 if ((tok == NULL) || (strcmp(tok, "lev") != 0)) {
243 strange_header(file, buffer, buflen, "lev", tok);
244 goto out;
245 }
246
247 tok = strtok_r(NULL, " ", &saveptr);
248 if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) {
249 strange_header(file, buffer, buflen, _("<dump level param>"), tok);
250 goto out;
251 }
252
253 tok = strtok_r(NULL, " ", &saveptr);
254 if ((tok == NULL) || (strcmp(tok, "comp") != 0)) {
255 strange_header(file, buffer, buflen, "comp", tok);
256 goto out;
257 }
258
259 tok = strtok_r(NULL, " ", &saveptr);
260 if (tok == NULL) {
261 strange_header(file, buffer, buflen, _("<comp param>"), tok);
262 goto out;
263 }
264 strncpy(file->comp_suffix, tok, SIZEOF(file->comp_suffix) - 1);
265
266 file->compressed = (0 != strcmp(file->comp_suffix, "N"));
267 if (file->compressed) {
268 /* compatibility with pre-2.2 amanda */
269 if (strcmp(file->comp_suffix, "C") == 0)
270 strncpy(file->comp_suffix, ".Z", SIZEOF(file->comp_suffix) - 1);
271 } else {
272 strcpy(file->comp_suffix, "");
273 }
274
275 tok = strtok_r(NULL, " ", &saveptr);
276 /* "program" is optional */
277 if (tok == NULL || strcmp(tok, "program") != 0) {
278 break;
279 }
280
281 tok = strtok_r(NULL, " ", &saveptr);
282 if (tok == NULL) {
283 strange_header(file, buffer, buflen, _("<program name>"), tok);
284 goto out;
285 }
286 strncpy(file->program, tok, SIZEOF(file->program) - 1);
287
288 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
289 break; /* reached the end of the buffer */
290
291 /* encryption is optional */
292 if (BSTRNCMP(tok, "crypt") == 0) {
293 tok = strtok_r(NULL, " ", &saveptr);
294 if (tok == NULL) {
295 strange_header(file, buffer, buflen, _("<crypt param>"), tok);
296 goto out;
297 }
298 strncpy(file->encrypt_suffix, tok,
299 SIZEOF(file->encrypt_suffix) - 1);
300 file->encrypted = 1;
301
302 /* for compatibility with who-knows-what, allow "comp N" to be
303 * equivalent to no compression */
304 if (0 == BSTRNCMP(file->encrypt_suffix, "N")) {
305 file->encrypted = 0;
306 strcpy(file->encrypt_suffix, "");
307 }
308
309 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
310 break;
311 }
312
313 /* "srvcompprog" is optional */
314 if (BSTRNCMP(tok, "server_custom_compress") == 0) {
315 tok = strtok_r(NULL, " ", &saveptr);
316 if (tok == NULL) {
317 strange_header(file, buffer, buflen,
318 _("<server custom compress param>"), tok);
319 goto out;
320 }
321 strncpy(file->srvcompprog, tok, SIZEOF(file->srvcompprog) - 1);
322 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
323 break;
324 }
325
326 /* "clntcompprog" is optional */
327 if (BSTRNCMP(tok, "client_custom_compress") == 0) {
328 tok = strtok_r(NULL, " ", &saveptr);
329 if (tok == NULL) {
330 strange_header(file, buffer, buflen,
331 _("<client custom compress param>"), tok);
332 goto out;
333 }
334 strncpy(file->clntcompprog, tok, SIZEOF(file->clntcompprog) - 1);
335 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
336 break;
337 }
338
339 /* "srv_encrypt" is optional */
340 if (BSTRNCMP(tok, "server_encrypt") == 0) {
341 tok = strtok_r(NULL, " ", &saveptr);
342 if (tok == NULL) {
343 strange_header(file, buffer, buflen,
344 _("<server encrypt param>"), tok);
345 goto out;
346 }
347 strncpy(file->srv_encrypt, tok, SIZEOF(file->srv_encrypt) - 1);
348 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
349 break;
350 }
351
352 /* "clnt_encrypt" is optional */
353 if (BSTRNCMP(tok, "client_encrypt") == 0) {
354 tok = strtok_r(NULL, " ", &saveptr);
355 if (tok == NULL) {
356 strange_header(file, buffer, buflen,
357 _("<client encrypt param>"), tok);
358 goto out;
359 }
360 strncpy(file->clnt_encrypt, tok, SIZEOF(file->clnt_encrypt) - 1);
361 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
362 break;
363 }
364
365 /* "srv_decrypt_opt" is optional */
366 if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
367 tok = strtok_r(NULL, " ", &saveptr);
368 if (tok == NULL) {
369 strange_header(file, buffer, buflen,
370 _("<server decrypt param>"), tok);
371 goto out;
372 }
373 strncpy(file->srv_decrypt_opt, tok,
374 SIZEOF(file->srv_decrypt_opt) - 1);
375 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
376 break;
377 }
378
379 /* "clnt_decrypt_opt" is optional */
380 if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
381 tok = strtok_r(NULL, " ", &saveptr);
382 if (tok == NULL) {
383 strange_header(file, buffer, buflen,
384 _("<client decrypt param>"), tok);
385 goto out;
386 }
387 strncpy(file->clnt_decrypt_opt, tok,
388 SIZEOF(file->clnt_decrypt_opt) - 1);
389 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
390 break;
391 }
392 break;
393
394
395 case F_TAPEEND:
396 tok = strtok_r(NULL, " ", &saveptr);
397 /* DATE is optional */
398 if (tok != NULL) {
399 if (strcmp(tok, "DATE") == 0) {
400 tok = strtok_r(NULL, " ", &saveptr);
401 if(tok == NULL)
402 file->datestamp[0] = '\0';
403 else
404 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
405 } else {
406 strange_header(file, buffer, buflen, _("<DATE>"), tok);
407 }
408 } else {
409 file->datestamp[0] = '\0';
410 }
411 break;
412
413 case F_NOOP:
414 /* nothing follows */
415 break;
416
417 default:
418 strange_header(file, buffer, buflen,
419 _("TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND|NOOP"), tok);
420 goto out;
421 }
422
423 (void)strtok_r(buf, "\n", &saveptr); /* this is the first line */
424 /* iterate through the rest of the lines */
425 while ((line = strtok_r(NULL, "\n", &saveptr)) != NULL) {
426 #define SC "CONT_FILENAME="
427 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
428 line += SIZEOF(SC) - 1;
429 strncpy(file->cont_filename, line,
430 SIZEOF(file->cont_filename) - 1);
431 continue;
432 }
433 #undef SC
434
435 #define SC "PARTIAL="
436 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
437 line += SIZEOF(SC) - 1;
438 file->is_partial = !strcasecmp(line, "yes");
439 continue;
440 }
441 #undef SC
442 #define SC "APPLICATION="
443 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
444 line += SIZEOF(SC) - 1;
445 strncpy(file->application, line,
446 SIZEOF(file->application) - 1);
447 continue;
448 }
449 #undef SC
450
451 #define SC "ORIGSIZE="
452 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
453 line += SIZEOF(SC) - 1;
454 file->orig_size = OFF_T_ATOI(line);
455 }
456 #undef SC
457
458 #define SC "DLE="
459 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
460 line += SIZEOF(SC) - 1;
461 file->dle_str = parse_heredoc(line, &saveptr, buffer);
462 }
463 #undef SC
464
465 #define SC _("To restore, position tape at start of file and run:")
466 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
467 continue;
468 #undef SC
469
470 #define SC "\tdd if=<tape> "
471 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
472 char *cmd1, *cmd2, *cmd3=NULL;
473
474 /* skip over dd command */
475 if ((cmd1 = strchr(line, '|')) == NULL) {
476
477 strncpy(file->recover_cmd, "BUG",
478 SIZEOF(file->recover_cmd) - 1);
479 continue;
480 }
481 *cmd1++ = '\0';
482
483 /* block out first pipeline command */
484 if ((cmd2 = strchr(cmd1, '|')) != NULL) {
485 *cmd2++ = '\0';
486 if ((cmd3 = strchr(cmd2, '|')) != NULL)
487 *cmd3++ = '\0';
488 }
489
490 /* clean up some extra spaces in various fields */
491 chomp(cmd1);
492 chomp(cmd2);
493 chomp(cmd3);
494
495 /* three cmds: decrypt | uncompress | recover
496 * two cmds: uncompress | recover
497 * XXX note that if there are two cmds, the first one
498 * XXX could be either uncompress or decrypt. Since no
499 * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
500 * XXX for header information.
501 * one cmds: recover
502 */
503
504 if ( cmd3 == NULL) {
505 if (cmd2 == NULL) {
506 strncpy(file->recover_cmd, cmd1,
507 SIZEOF(file->recover_cmd) - 1);
508 } else {
509 g_snprintf(file->uncompress_cmd,
510 SIZEOF(file->uncompress_cmd), "%s |", cmd1);
511 strncpy(file->recover_cmd, cmd2,
512 SIZEOF(file->recover_cmd) - 1);
513 }
514 } else { /* cmd3 presents: decrypt | uncompress | recover */
515 g_snprintf(file->decrypt_cmd,
516 SIZEOF(file->decrypt_cmd), "%s |", cmd1);
517 g_snprintf(file->uncompress_cmd,
518 SIZEOF(file->uncompress_cmd), "%s |", cmd2);
519 strncpy(file->recover_cmd, cmd3,
520 SIZEOF(file->recover_cmd) - 1);
521 }
522 continue;
523 }
524 #undef SC
525 /* XXX complain about weird lines? */
526 }
527
528 out:
529 amfree(buf);
530 amfree(line1);
531 }
532
533 void
dump_dumpfile_t(const dumpfile_t * file)534 dump_dumpfile_t(
535 const dumpfile_t *file)
536 {
537 g_debug(_("Contents of *(dumpfile_t *)%p:"), file);
538 g_debug(_(" type = %d (%s)"),
539 file->type, filetype2str(file->type));
540 g_debug(_(" datestamp = '%s'"), file->datestamp);
541 g_debug(_(" dumplevel = %d"), file->dumplevel);
542 g_debug(_(" compressed = %d"), file->compressed);
543 g_debug(_(" encrypted = %d"), file->encrypted);
544 g_debug(_(" comp_suffix = '%s'"), file->comp_suffix);
545 g_debug(_(" encrypt_suffix = '%s'"), file->encrypt_suffix);
546 g_debug(_(" name = '%s'"), file->name);
547 g_debug(_(" disk = '%s'"), file->disk);
548 g_debug(_(" program = '%s'"), file->program);
549 g_debug(_(" application = '%s'"), file->application);
550 g_debug(_(" srvcompprog = '%s'"), file->srvcompprog);
551 g_debug(_(" clntcompprog = '%s'"), file->clntcompprog);
552 g_debug(_(" srv_encrypt = '%s'"), file->srv_encrypt);
553 g_debug(_(" clnt_encrypt = '%s'"), file->clnt_encrypt);
554 g_debug(_(" recover_cmd = '%s'"), file->recover_cmd);
555 g_debug(_(" uncompress_cmd = '%s'"), file->uncompress_cmd);
556 g_debug(_(" decrypt_cmd = '%s'"), file->decrypt_cmd);
557 g_debug(_(" srv_decrypt_opt = '%s'"), file->srv_decrypt_opt);
558 g_debug(_(" clnt_decrypt_opt = '%s'"), file->clnt_decrypt_opt);
559 g_debug(_(" cont_filename = '%s'"), file->cont_filename);
560 if (file->dle_str)
561 g_debug(_(" dle_str = %s"), file->dle_str);
562 else
563 g_debug(_(" dle_str = (null)"));
564 g_debug(_(" is_partial = %d"), file->is_partial);
565 g_debug(_(" partnum = %d"), file->partnum);
566 g_debug(_(" totalparts = %d"), file->totalparts);
567 if (file->blocksize)
568 g_debug(_(" blocksize = %zu"), file->blocksize);
569 }
570
571 static void
validate_nonempty_str(const char * val,const char * name)572 validate_nonempty_str(
573 const char *val,
574 const char *name)
575 {
576 if (strlen(val) == 0) {
577 error(_("Invalid %s '%s'\n"), name, val);
578 /*NOTREACHED*/
579 }
580 }
581
582 static void
validate_not_both(const char * val1,const char * val2,const char * name1,const char * name2)583 validate_not_both(
584 const char *val1, const char *val2,
585 const char *name1, const char *name2)
586 {
587 if (*val1 && *val2) {
588 error("cannot set both %s and %s\n", name1, name2);
589 }
590 }
591
592 static void
validate_no_space(const char * val,const char * name)593 validate_no_space(
594 const char *val,
595 const char *name)
596 {
597 if (strchr(val, ' ') != NULL) {
598 error(_("%s cannot contain spaces\n"), name);
599 /*NOTREACHED*/
600 }
601 }
602
603 static void
validate_pipe_cmd(const char * cmd,const char * name)604 validate_pipe_cmd(
605 const char *cmd,
606 const char *name)
607 {
608 if (strlen(cmd) && cmd[strlen(cmd)-1] != '|') {
609 error("invalid %s (must end with '|'): '%s'\n", name, cmd);
610 }
611 }
612
613 static void
validate_encrypt_suffix(int encrypted,const char * suff)614 validate_encrypt_suffix(
615 int encrypted,
616 const char *suff)
617 {
618 if (encrypted) {
619 if (!suff[0] || (0 == strcmp(suff, "N"))) {
620 error(_("Invalid encrypt_suffix '%s'\n"), suff);
621 }
622 } else {
623 if (suff[0] && (0 != strcmp(suff, "N"))) {
624 error(_("Invalid header: encrypt_suffix '%s' specified but not encrypted\n"), suff);
625 }
626 }
627 }
628
629 static void
validate_datestamp(const char * datestamp)630 validate_datestamp(
631 const char *datestamp)
632 {
633 if (strcmp(datestamp, "X") == 0) {
634 return;
635 }
636
637 if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
638 return;
639 }
640 if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
641 return;
642 }
643 error(_("Invalid datestamp '%s'\n"), datestamp);
644 /*NOTREACHED*/
645 }
646
647 static void
validate_parts(const int partnum,const int totalparts)648 validate_parts(
649 const int partnum,
650 const int totalparts)
651 {
652 if (partnum < 1) {
653 error(_("Invalid partnum (%d)\n"), partnum);
654 /*NOTREACHED*/
655 }
656
657 if (partnum > totalparts && totalparts >= 0) {
658 error(_("Invalid partnum (%d) > totalparts (%d)\n"),
659 partnum, totalparts);
660 /*NOTREACHED*/
661 }
662 }
663
664 char *
build_header(const dumpfile_t * file,size_t * size,size_t max_size)665 build_header(const dumpfile_t * file, size_t *size, size_t max_size)
666 {
667 GString *rval, *split_data;
668 char *qname;
669 char *program;
670 size_t min_size;
671
672 min_size = size? *size : max_size;
673 g_debug(_("Building type %s header of %zu-%zu bytes with name='%s' disk='%s' dumplevel=%d and blocksize=%zu"),
674 filetype2str(file->type), min_size, max_size,
675 file->name, file->disk, file->dumplevel, file->blocksize);
676
677 rval = g_string_sized_new(min_size);
678 split_data = g_string_sized_new(10);
679
680 switch (file->type) {
681 case F_TAPESTART:
682 validate_nonempty_str(file->name, "name");
683 validate_datestamp(file->datestamp);
684 g_string_printf(rval,
685 "AMANDA: TAPESTART DATE %s TAPE %s\n\014\n",
686 file->datestamp, file->name);
687 break;
688
689 case F_SPLIT_DUMPFILE:
690 validate_parts(file->partnum, file->totalparts);
691 g_string_printf(split_data,
692 " part %d/%d ", file->partnum, file->totalparts);
693 /* FALLTHROUGH */
694
695 case F_CONT_DUMPFILE:
696 case F_DUMPFILE :
697 validate_nonempty_str(file->name, "name");
698 validate_nonempty_str(file->program, "program");
699 validate_datestamp(file->datestamp);
700 validate_encrypt_suffix(file->encrypted, file->encrypt_suffix);
701 qname = quote_string(file->disk);
702 program = stralloc(file->program);
703 if (match("^.*[.][Ee][Xx][Ee]$", program)) {
704 /* Trim ".exe" from program name */
705 program[strlen(program) - strlen(".exe")] = '\0';
706 }
707 g_string_printf(rval,
708 "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
709 filetype2str(file->type),
710 file->datestamp, file->name, qname,
711 split_data->str,
712 file->dumplevel,
713 file->compressed? file->comp_suffix : "N",
714 program);
715 amfree(program);
716 amfree(qname);
717
718 /* only output crypt if it's enabled */
719 if (file->encrypted) {
720 g_string_append_printf(rval, " crypt %s", file->encrypt_suffix);
721 }
722
723 validate_not_both(file->srvcompprog, file->clntcompprog,
724 "srvcompprog", "clntcompprog");
725 if (*file->srvcompprog) {
726 validate_no_space(file->srvcompprog, "srvcompprog");
727 g_string_append_printf(rval, " server_custom_compress %s",
728 file->srvcompprog);
729 } else if (*file->clntcompprog) {
730 validate_no_space(file->clntcompprog, "clntcompprog");
731 g_string_append_printf(rval, " client_custom_compress %s",
732 file->clntcompprog);
733 }
734
735 validate_not_both(file->srv_encrypt, file->clnt_encrypt,
736 "srv_encrypt", "clnt_encrypt");
737 if (*file->srv_encrypt) {
738 validate_no_space(file->srv_encrypt, "srv_encrypt");
739 g_string_append_printf(rval, " server_encrypt %s",
740 file->srv_encrypt);
741 } else if (*file->clnt_encrypt) {
742 validate_no_space(file->clnt_encrypt, "clnt_encrypt");
743 g_string_append_printf(rval, " client_encrypt %s",
744 file->clnt_encrypt);
745 }
746
747 validate_not_both(file->srv_decrypt_opt, file->clnt_decrypt_opt,
748 "srv_decrypt_opt", "clnt_decrypt_opt");
749 if (*file->srv_decrypt_opt) {
750 validate_no_space(file->srv_decrypt_opt, "srv_decrypt_opt");
751 g_string_append_printf(rval, " server_decrypt_option %s",
752 file->srv_decrypt_opt);
753 } else if (*file->clnt_decrypt_opt) {
754 g_string_append_printf(rval, " client_decrypt_option %s",
755 file->clnt_decrypt_opt);
756 }
757
758 g_string_append_printf(rval, "\n");
759
760 if (file->cont_filename[0] != '\0') {
761 g_string_append_printf(rval, "CONT_FILENAME=%s\n",
762 file->cont_filename);
763 }
764 if (file->application[0] != '\0') {
765 g_string_append_printf(rval, "APPLICATION=%s\n", file->application);
766 }
767 if (file->is_partial != 0) {
768 g_string_append_printf(rval, "PARTIAL=YES\n");
769 }
770 if (file->orig_size > 0) {
771 g_string_append_printf(rval, "ORIGSIZE=%jd\n",
772 (intmax_t)file->orig_size);
773 }
774 if (file->dle_str && strlen(file->dle_str) < max_size-2048) {
775 char *heredoc = quote_heredoc(file->dle_str, "ENDDLE");
776 g_string_append_printf(rval, "DLE=%s\n", heredoc);
777 amfree(heredoc);
778 }
779
780 g_string_append_printf(rval,
781 _("To restore, position tape at start of file and run:\n"));
782
783 g_string_append_printf(rval, "\tdd if=<tape> ");
784 if (file->blocksize)
785 g_string_append_printf(rval, "bs=%zuk ",
786 file->blocksize / 1024);
787 g_string_append_printf(rval, "skip=1 | ");
788 if (*file->recover_cmd) {
789 if (*file->decrypt_cmd) {
790 validate_pipe_cmd(file->decrypt_cmd, "decrypt_cmd");
791 g_string_append_printf(rval, "%s ", file->decrypt_cmd);
792 }
793 if (*file->uncompress_cmd) {
794 validate_pipe_cmd(file->uncompress_cmd, "uncompress_cmd");
795 g_string_append_printf(rval, "%s ", file->uncompress_cmd);
796 }
797 g_string_append_printf(rval, "%s ", file->recover_cmd);
798 } else {
799 if (*file->uncompress_cmd || *file->decrypt_cmd)
800 error("cannot specify uncompress_cmd or decrypt_cmd without recover_cmd\n");
801 }
802 /* \014 == ^L == form feed */
803 g_string_append_printf(rval, "\n\014\n");
804 break;
805
806 case F_TAPEEND:
807 validate_datestamp(file->datestamp);
808 g_string_printf(rval, "AMANDA: TAPEEND DATE %s\n\014\n",
809 file->datestamp);
810 break;
811
812 case F_NOOP:
813 g_string_printf(rval, "AMANDA: NOOP\n\014\n");
814 break;
815
816 case F_UNKNOWN:
817 case F_EMPTY:
818 case F_WEIRD:
819 default:
820 error(_("Invalid header type: %d (%s)"),
821 file->type, filetype2str(file->type));
822 /*NOTREACHED*/
823 }
824
825 g_string_free(split_data, TRUE);
826
827 /* is it too big? */
828 if (rval->len > max_size) {
829 g_debug("header is larger than %zu bytes -- cannot create", max_size);
830 g_string_free(rval, TRUE);
831 return NULL;
832 }
833
834 /* Clear extra bytes. */
835 if (rval->len < min_size) {
836 bzero(rval->str + rval->len, rval->allocated_len - rval->len);
837 }
838 if (size) {
839 *size = MAX(min_size, (size_t)rval->len);
840 }
841 return g_string_free(rval, FALSE);
842 }
843
844 void
print_header(FILE * outf,const dumpfile_t * file)845 print_header(
846 FILE * outf,
847 const dumpfile_t * file)
848 {
849 char *summ = summarize_header(file);
850 g_fprintf(outf, "%s\n", summ);
851 g_free(summ);
852 }
853
854 /*
855 * Prints the contents of the file structure.
856 */
857 char *
summarize_header(const dumpfile_t * file)858 summarize_header(
859 const dumpfile_t * file)
860 {
861 char *qdisk;
862 GString *summ;
863
864 switch(file->type) {
865 case F_EMPTY:
866 return g_strdup(_("EMPTY file"));
867
868 case F_UNKNOWN:
869 return g_strdup(_("UNKNOWN file"));
870
871 default:
872 case F_WEIRD:
873 return g_strdup(_("WEIRD file"));
874
875 case F_TAPESTART:
876 return g_strdup_printf(_("start of tape: date %s label %s"),
877 file->datestamp, file->name);
878
879 case F_NOOP:
880 return g_strdup(_("NOOP file"));
881
882 case F_DUMPFILE:
883 case F_CONT_DUMPFILE:
884 qdisk = quote_string(file->disk);
885 summ = g_string_new("");
886 g_string_printf(summ, "%s: date %s host %s disk %s lev %d comp %s",
887 filetype2str(file->type), file->datestamp, file->name,
888 qdisk, file->dumplevel,
889 file->compressed? file->comp_suffix : "N");
890 amfree(qdisk);
891 goto add_suffixes;
892
893 case F_SPLIT_DUMPFILE: {
894 char totalparts[NUM_STR_SIZE*2];
895 if(file->totalparts > 0)
896 g_snprintf(totalparts, SIZEOF(totalparts), "%d", file->totalparts);
897 else
898 g_snprintf(totalparts, SIZEOF(totalparts), "UNKNOWN");
899 qdisk = quote_string(file->disk);
900 summ = g_string_new("");
901 g_string_printf(summ, "split dumpfile: date %s host %s disk %s"
902 " part %d/%s lev %d comp %s",
903 file->datestamp, file->name, qdisk, file->partnum,
904 totalparts, file->dumplevel,
905 file->compressed? file->comp_suffix : "N");
906 amfree(qdisk);
907 goto add_suffixes;
908 }
909
910 add_suffixes:
911 if (*file->program)
912 g_string_append_printf(summ, " program %s", file->program);
913 if (strcmp(file->encrypt_suffix, "enc") == 0)
914 g_string_append_printf(summ, " crypt %s", file->encrypt_suffix);
915 if (*file->srvcompprog)
916 g_string_append_printf(summ, " server_custom_compress %s", file->srvcompprog);
917 if (*file->clntcompprog)
918 g_string_append_printf(summ, " client_custom_compress %s", file->clntcompprog);
919 if (*file->srv_encrypt)
920 g_string_append_printf(summ, " server_encrypt %s", file->srv_encrypt);
921 if (*file->clnt_encrypt)
922 g_string_append_printf(summ, " client_encrypt %s", file->clnt_encrypt);
923 if (*file->srv_decrypt_opt)
924 g_string_append_printf(summ, " server_decrypt_option %s", file->srv_decrypt_opt);
925 if (*file->clnt_decrypt_opt)
926 g_string_append_printf(summ, " client_decrypt_option %s", file->clnt_decrypt_opt);
927 return g_string_free(summ, FALSE);
928
929 case F_TAPEEND:
930 return g_strdup_printf("end of tape: date %s", file->datestamp);
931 break;
932 }
933 }
934
935 int
known_compress_type(const dumpfile_t * file)936 known_compress_type(
937 const dumpfile_t * file)
938 {
939 if(strcmp(file->comp_suffix, ".Z") == 0)
940 return 1;
941 #ifdef HAVE_GZIP
942 if(strcmp(file->comp_suffix, ".gz") == 0)
943 return 1;
944 #endif
945 if(strcmp(file->comp_suffix, "cust") == 0)
946 return 1;
947 return 0;
948 }
949
950 static const struct {
951 filetype_t type;
952 const char *str;
953 } filetypetab[] = {
954 { F_UNKNOWN, "UNKNOWN" },
955 { F_WEIRD, "WEIRD" },
956 { F_TAPESTART, "TAPESTART" },
957 { F_TAPEEND, "TAPEEND" },
958 { F_DUMPFILE, "FILE" },
959 { F_CONT_DUMPFILE, "CONT_FILE" },
960 { F_SPLIT_DUMPFILE, "SPLIT_FILE" },
961 { F_NOOP, "NOOP" }
962 };
963 #define NFILETYPES (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
964
965 static const char *
filetype2str(filetype_t type)966 filetype2str(
967 filetype_t type)
968 {
969 int i;
970
971 for (i = 0; i < (int)NFILETYPES; i++)
972 if (filetypetab[i].type == type)
973 return (filetypetab[i].str);
974 return ("UNKNOWN");
975 }
976
977 static filetype_t
str2filetype(const char * str)978 str2filetype(
979 const char *str)
980 {
981 int i;
982
983 for (i = 0; i < (int)NFILETYPES; i++)
984 if (strcmp(filetypetab[i].str, str) == 0)
985 return (filetypetab[i].type);
986 return (F_UNKNOWN);
987 }
988
headers_are_equal(dumpfile_t * a,dumpfile_t * b)989 gboolean headers_are_equal(dumpfile_t * a, dumpfile_t * b) {
990 if (a == NULL && b == NULL)
991 return TRUE;
992
993 if (a == NULL || b == NULL)
994 return FALSE;
995
996 if (a->type != b->type) return FALSE;
997 if (strcmp(a->datestamp, b->datestamp)) return FALSE;
998 if (a->dumplevel != b->dumplevel) return FALSE;
999 if (a->compressed != b->compressed) return FALSE;
1000 if (a->encrypted != b->encrypted) return FALSE;
1001 if (strcmp(a->comp_suffix, b->comp_suffix)) return FALSE;
1002 if (strcmp(a->encrypt_suffix, b->encrypt_suffix)) return FALSE;
1003 if (strcmp(a->name, b->name)) return FALSE;
1004 if (strcmp(a->disk, b->disk)) return FALSE;
1005 if (strcmp(a->program, b->program)) return FALSE;
1006 if (strcmp(a->application, b->application)) return FALSE;
1007 if (strcmp(a->srvcompprog, b->srvcompprog)) return FALSE;
1008 if (strcmp(a->clntcompprog, b->clntcompprog)) return FALSE;
1009 if (strcmp(a->srv_encrypt, b->srv_encrypt)) return FALSE;
1010 if (strcmp(a->clnt_encrypt, b->clnt_encrypt)) return FALSE;
1011 if (strcmp(a->recover_cmd, b->recover_cmd)) return FALSE;
1012 if (strcmp(a->uncompress_cmd, b->uncompress_cmd)) return FALSE;
1013 if (strcmp(a->decrypt_cmd, b->decrypt_cmd)) return FALSE;
1014 if (strcmp(a->srv_decrypt_opt, b->srv_decrypt_opt)) return FALSE;
1015 if (strcmp(a->clnt_decrypt_opt, b->clnt_decrypt_opt)) return FALSE;
1016 if (strcmp(a->cont_filename, b->cont_filename)) return FALSE;
1017 if (a->dle_str != b->dle_str && a->dle_str && b->dle_str
1018 && strcmp(a->dle_str, b->dle_str)) return FALSE;
1019 if (a->is_partial != b->is_partial) return FALSE;
1020 if (a->partnum != b->partnum) return FALSE;
1021 if (a->totalparts != b->totalparts) return FALSE;
1022 if (a->blocksize != b->blocksize) return FALSE;
1023
1024 return TRUE; /* ok, they're the same */
1025 }
1026
dumpfile_copy(dumpfile_t * source)1027 dumpfile_t * dumpfile_copy(dumpfile_t* source) {
1028 dumpfile_t* rval = malloc(sizeof(dumpfile_t));
1029 memcpy(rval, source, sizeof(dumpfile_t));
1030 if (rval->dle_str) rval->dle_str = stralloc(rval->dle_str);
1031 return rval;
1032 }
1033
1034 void
dumpfile_copy_in_place(dumpfile_t * dest,dumpfile_t * source)1035 dumpfile_copy_in_place(
1036 dumpfile_t *dest,
1037 dumpfile_t* source)
1038 {
1039 memcpy(dest, source, sizeof(dumpfile_t));
1040 if (dest->dle_str) dest->dle_str = stralloc(dest->dle_str);
1041 }
1042
dumpfile_free_data(dumpfile_t * info)1043 void dumpfile_free_data(dumpfile_t* info) {
1044 if (info) {
1045 amfree(info->dle_str);
1046 }
1047 }
1048
dumpfile_free(dumpfile_t * info)1049 void dumpfile_free(dumpfile_t* info) {
1050 dumpfile_free_data(info);
1051 amfree(info);
1052 }
1053
quote_heredoc(char * text,char * delimiter_prefix)1054 static char *quote_heredoc(
1055 char *text,
1056 char *delimiter_prefix)
1057 {
1058 char *delimiter = stralloc(delimiter_prefix);
1059 int delimiter_n = 0;
1060 int delimiter_len = strlen(delimiter);
1061 char *quoted;
1062
1063 /* keep picking delimiters until we find one that's not a line in TEXT */
1064 while (1) {
1065 char *line = text;
1066 char *c = text;
1067 gboolean found_delimiter = FALSE;
1068
1069 while (1) {
1070 if (*c == '\n' || *c == '\0') {
1071 int linelen = c - line;
1072 if (linelen == delimiter_len && 0 == strncmp(line, delimiter, linelen)) {
1073 found_delimiter = TRUE;
1074 break;
1075 }
1076 line = c+1;
1077 }
1078 if (!*c) break;
1079 c++;
1080 }
1081
1082 if (!found_delimiter)
1083 break;
1084
1085 delimiter = newvstrallocf(delimiter, "%s%d", delimiter_prefix, ++delimiter_n);
1086 delimiter_len = strlen(delimiter);
1087 }
1088
1089 /* we have a delimiter .. now use it */
1090 quoted = vstrallocf("<<%s\n%s\n%s", delimiter, text, delimiter);
1091 amfree(delimiter);
1092 return quoted;
1093 }
1094
parse_heredoc(char * line,char ** saveptr,const char * message)1095 static char *parse_heredoc(
1096 char *line,
1097 char **saveptr,
1098 const char *message)
1099 {
1100 char *result = NULL;
1101
1102 if (strncmp(line, "<<", 2) == 0) {
1103 char *keyword = line+2;
1104 char *new_line;
1105
1106 while((new_line = strtok_r(NULL, "\n", saveptr)) != NULL &&
1107 strcmp(new_line, keyword) != 0) {
1108 result = vstrextend(&result, new_line, "\n", NULL);
1109 }
1110
1111 if (!new_line || !g_str_equal(new_line, keyword)) {
1112 g_debug("No end of heredoc: %s", keyword);
1113 if (message)
1114 g_debug("Message: %s", message);
1115 }
1116
1117 /* make sure we have something */
1118 if (!result)
1119 result = g_strdup("");
1120 /* remove latest '\n' */
1121 else if (strlen(result) > 0)
1122 result[strlen(result)-1] = '\0';
1123 } else {
1124 result = stralloc(line);
1125 }
1126 return result;
1127 }
1128