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