ede34793e14dd587bbf95051c86b35d857dc1266
[debian/amanda] / common-src / fileheader.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: fileheader.c,v 1.34 2006/03/09 16:51:41 martinea Exp $
28  */
29
30 #include "amanda.h"
31 #include "fileheader.h"
32
33 static const char *filetype2str P((filetype_t));
34 static filetype_t str2filetype P((const char *));
35
36 void
37 fh_init(file)
38     dumpfile_t *file;
39 {
40     memset(file, '\0', sizeof(*file));
41     file->blocksize = DISK_BLOCK_BYTES;
42 }
43
44 void
45 parse_file_header(buffer, file, buflen)
46     const char *buffer;
47     dumpfile_t *file;
48     size_t buflen;
49 {
50     char *buf, *line, *tok, *line1=NULL;
51     int lsize;
52     /* put the buffer into a writable chunk of memory and nul-term it */
53     buf = alloc(buflen + 1);
54     memcpy(buf, buffer, buflen);
55     buf[buflen] = '\0';
56
57     fh_init(file); 
58
59     for(line=buf,lsize=0; *line != '\n' && lsize < buflen; line++) {lsize++;};
60     *line = '\0';
61     line1 = alloc(lsize+1);
62     strncpy(line1,buf,lsize);
63     line1[lsize] = '\0';
64     *line = '\n';
65
66     tok = strtok(line1, " ");
67     if (tok == NULL)
68         goto weird_header;
69     if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
70         amfree(buf);
71         file->type = F_UNKNOWN;
72         amfree(line1);
73         return;
74     }
75
76     tok = strtok(NULL, " ");
77     if (tok == NULL)
78         goto weird_header;
79     file->type = str2filetype(tok);
80
81     switch (file->type) {
82     case F_TAPESTART:
83         tok = strtok(NULL, " ");
84         if (tok == NULL || strcmp(tok, "DATE") != 0)
85             goto weird_header;
86
87         tok = strtok(NULL, " ");
88         if (tok == NULL)
89             goto weird_header;
90         strncpy(file->datestamp, tok, sizeof(file->datestamp) - 1);
91
92         tok = strtok(NULL, " ");
93         if (tok == NULL || strcmp(tok, "TAPE") != 0)
94             goto weird_header;
95
96         tok = strtok(NULL, " ");
97         if (tok == NULL)
98             goto weird_header;
99         strncpy(file->name, tok, sizeof(file->name) - 1);
100         break;
101
102     case F_DUMPFILE:
103     case F_CONT_DUMPFILE:
104     case F_SPLIT_DUMPFILE:
105         tok = strtok(NULL, " ");
106         if (tok == NULL)
107             goto weird_header;
108         strncpy(file->datestamp, tok, sizeof(file->datestamp) - 1);
109
110         tok = strtok(NULL, " ");
111         if (tok == NULL)
112             goto weird_header;
113         strncpy(file->name, tok, sizeof(file->name) - 1);
114
115         tok = strtok(NULL, " ");
116         if (tok == NULL)
117             goto weird_header;
118         strncpy(file->disk, tok, sizeof(file->disk) - 1);
119         
120         if(file->type == F_SPLIT_DUMPFILE){
121             tok = strtok(NULL, " ");
122             if (tok == NULL || strcmp(tok, "part") != 0)
123                 goto weird_header;
124
125             tok = strtok(NULL, "/");
126             if (tok == NULL || sscanf(tok, "%d", &file->partnum) != 1)
127                 goto weird_header;
128
129             tok = strtok(NULL, " ");
130             if (tok == NULL)
131                 goto weird_header;
132             /* If totalparts == -1, then the original dump was done in 
133                streaming mode (no holding disk), thus we don't know how 
134                many parts there are. */
135             if(sscanf(tok, "%d", &file->totalparts) != 1){
136                 goto weird_header;
137             }
138         }
139         
140
141         tok = strtok(NULL, " ");
142         if (tok == NULL || strcmp(tok, "lev") != 0)
143             goto weird_header;
144
145         tok = strtok(NULL, " ");
146         if (tok == NULL || sscanf(tok, "%d", &file->dumplevel) != 1)
147             goto weird_header;
148
149         tok = strtok(NULL, " ");
150         if (tok == NULL || strcmp(tok, "comp") != 0)
151             goto weird_header;
152
153         tok = strtok(NULL, " ");
154         if (tok == NULL)
155             goto weird_header;
156         strncpy(file->comp_suffix, tok, sizeof(file->comp_suffix) - 1);
157
158         file->compressed = strcmp(file->comp_suffix, "N");
159         /* compatibility with pre-2.2 amanda */
160         if (strcmp(file->comp_suffix, "C") == 0)
161             strncpy(file->comp_suffix, ".Z", sizeof(file->comp_suffix) - 1);
162                
163         tok = strtok(NULL, " ");
164         /* "program" is optional */
165         if (tok == NULL || strcmp(tok, "program") != 0) {
166             amfree(buf);
167             amfree(line1);
168             return;
169         }
170
171         tok = strtok(NULL, " ");
172         if (tok == NULL)
173             goto weird_header;
174         strncpy(file->program, tok, sizeof(file->program) - 1);
175         if (file->program[0] == '\0')
176             strncpy(file->program, "RESTORE", sizeof(file->program) - 1);
177
178         if ((tok = strtok(NULL, " ")) == NULL)
179              break;          /* reach the end of the buf */
180
181         /* "encryption" is optional */
182         if (BSTRNCMP(tok, "crypt") == 0) {
183             tok = strtok(NULL, " ");
184             if (tok == NULL)
185                 goto weird_header;
186             strncpy(file->encrypt_suffix, tok,
187                     sizeof(file->encrypt_suffix) - 1);
188             file->encrypted = BSTRNCMP(file->encrypt_suffix, "N");
189             if ((tok = strtok(NULL, " ")) == NULL)
190                 break;
191         }
192
193         /* "srvcompprog" is optional */
194         if (BSTRNCMP(tok, "server_custom_compress") == 0) {
195             tok = strtok(NULL, " ");
196             if (tok == NULL)
197                 goto weird_header;
198             strncpy(file->srvcompprog, tok, sizeof(file->srvcompprog) - 1);
199             if ((tok = strtok(NULL, " ")) == NULL)
200                 break;      
201         }
202
203         /* "clntcompprog" is optional */
204         if (BSTRNCMP(tok, "client_custom_compress") == 0) {
205             tok = strtok(NULL, " ");
206             if (tok == NULL)
207                 goto weird_header;
208             strncpy(file->clntcompprog, tok, sizeof(file->clntcompprog) - 1);
209             if ((tok = strtok(NULL, " ")) == NULL)
210                 break;
211         }
212
213         /* "srv_encrypt" is optional */
214         if (BSTRNCMP(tok, "server_encrypt") == 0) {
215             tok = strtok(NULL, " ");
216             if (tok == NULL)
217                 goto weird_header;
218             strncpy(file->srv_encrypt, tok, sizeof(file->srv_encrypt) - 1);
219             if ((tok = strtok(NULL, " ")) == NULL) 
220                 break;
221         }
222
223         /* "clnt_encrypt" is optional */
224         if (BSTRNCMP(tok, "client_encrypt") == 0) {
225             tok = strtok(NULL, " ");
226             if (tok == NULL)
227                 goto weird_header;
228             strncpy(file->clnt_encrypt, tok, sizeof(file->clnt_encrypt) - 1);
229             if ((tok = strtok(NULL, " ")) == NULL) 
230                 break;
231         }
232
233         /* "srv_decrypt_opt" is optional */
234         if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
235             tok = strtok(NULL, " ");
236             if (tok == NULL)
237                 goto weird_header;
238             strncpy(file->srv_decrypt_opt, tok,
239                     sizeof(file->srv_decrypt_opt) - 1);
240             if ((tok = strtok(NULL, " ")) == NULL) 
241                 break;
242         }
243
244         /* "clnt_decrypt_opt" is optional */
245         if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
246             tok = strtok(NULL, " ");
247             if (tok == NULL)
248                 goto weird_header;
249             strncpy(file->clnt_decrypt_opt, tok,
250                     sizeof(file->clnt_decrypt_opt) - 1);
251             if ((tok = strtok(NULL, " ")) == NULL) 
252                 break;
253         }
254       break;
255       
256
257     case F_TAPEEND:
258         tok = strtok(NULL, " ");
259         /* DATE is optional */
260         if (tok == NULL || strcmp(tok, "DATE") != 0) {
261             amfree(buf);
262             amfree(line1);
263             return;
264         }
265         strncpy(file->datestamp, tok, sizeof(file->datestamp) - 1);
266         break;
267
268     default:
269         goto weird_header;
270     }
271
272     line = strtok(buf, "\n"); /* this is the first line */
273     /* iterate through the rest of the lines */
274     while ((line = strtok(NULL, "\n")) != NULL) {
275 #define SC "CONT_FILENAME="
276         if (strncmp(line, SC, sizeof(SC) - 1) == 0) {
277             line += sizeof(SC) - 1;
278             strncpy(file->cont_filename, line,
279                     sizeof(file->cont_filename) - 1);
280                     continue;
281         }
282 #undef SC
283
284 #define SC "PARTIAL="
285         if (strncmp(line, SC, sizeof(SC) - 1) == 0) {
286             line += sizeof(SC) - 1;
287             file->is_partial = !strcasecmp(line, "yes");
288             continue;
289         }
290 #undef SC
291
292 #define SC "To restore, position tape at start of file and run:"
293         if (strncmp(line, SC, sizeof(SC) - 1) == 0)
294             continue;
295 #undef SC
296
297 #define SC "\tdd if=<tape> bs="
298         if (strncmp(line, SC, sizeof(SC) - 1) == 0) {
299             char *cmd1=NULL, *cmd2=NULL, *cmd3=NULL;
300
301             /* skip over dd command */
302             if ((cmd1 = strchr(line, '|')) == NULL) {
303
304                 strncpy(file->recover_cmd, "BUG",
305                         sizeof(file->recover_cmd) - 1);
306                 continue;
307             }
308             *cmd1++ = '\0';
309
310             /* block out first pipeline command */
311             if ((cmd2 = strchr(cmd1, '|')) != NULL) {
312               *cmd2++ = '\0';
313               if ((cmd3 = strchr(cmd2, '|')) != NULL)
314                 *cmd3++ = '\0';
315             }
316            
317             /* three cmds: decrypt    | uncompress | recover
318              * two   cmds: uncompress | recover
319              * XXX note that if there are two cmds, the first one 
320              * XXX could be either uncompress or decrypt. Since no
321              * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
322              * XXX for header information.
323              * one   cmds: recover
324              */
325
326             if (cmd3 == NULL) {
327               if (cmd2 == NULL) {
328                 strncpy(file->recover_cmd, cmd1,
329                         sizeof(file->recover_cmd) - 1);
330               } else {
331                 snprintf(file->uncompress_cmd,
332                          sizeof(file->uncompress_cmd), "%s|", cmd1);
333                 strncpy(file->recover_cmd, cmd2,
334                         sizeof(file->recover_cmd) - 1);
335               }
336             } else {    /* cmd3 presents:  decrypt | uncompress | recover */
337               snprintf(file->decrypt_cmd,
338                        sizeof(file->decrypt_cmd), "%s|", cmd1);
339               snprintf(file->uncompress_cmd,
340                        sizeof(file->uncompress_cmd), "%s|", cmd2);
341               strncpy(file->recover_cmd, cmd3,
342                       sizeof(file->recover_cmd) - 1);
343             }
344             continue;
345         }
346 #undef SC
347         /* XXX complain about weird lines? */
348     }
349     amfree(buf);
350     amfree(line1);
351     return;
352
353 weird_header:
354     fprintf(stderr, "%s: strange amanda header: \"%.*s\"\n", get_pname(),
355         (int) buflen, buffer);
356     file->type = F_WEIRD;
357     amfree(buf);
358     amfree(line1);
359 }
360
361 void
362 build_header(buffer, file, buflen)
363     char *buffer;
364     const dumpfile_t *file;
365     size_t buflen;
366 {
367     int n;
368     char split_data[128] = "";
369
370     memset(buffer,'\0',buflen);
371
372     switch (file->type) {
373     case F_TAPESTART:
374         snprintf(buffer, buflen,
375             "AMANDA: TAPESTART DATE %s TAPE %s\n014\n",
376             file->datestamp, file->name);
377         break;
378
379     case F_SPLIT_DUMPFILE:
380         snprintf(split_data, sizeof(split_data),
381                  " part %d/%d ", file->partnum, file->totalparts);
382     /* FALLTHROUGH */
383         
384     case F_CONT_DUMPFILE:
385     case F_DUMPFILE :
386         n = snprintf(buffer, buflen,
387                      "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
388                          filetype2str(file->type),
389                          file->datestamp, file->name, file->disk,
390                          split_data,
391                          file->dumplevel, file->comp_suffix, file->program); 
392         if ( n ) {
393           buffer += n;
394           buflen -= n;
395           n = 0;
396         }
397      
398         if (strcmp(file->encrypt_suffix, "enc") == 0) {  /* only output crypt if it's enabled */
399           n = snprintf(buffer, buflen, " crypt %s", file->encrypt_suffix);
400         }
401         if ( n ) {
402           buffer += n;
403           buflen -= n;
404           n = 0;
405         }
406
407         if (*file->srvcompprog) {
408             n = snprintf(buffer, buflen, " server_custom_compress %s", file->srvcompprog);
409         } else if (*file->clntcompprog) {
410             n = snprintf(buffer, buflen, " client_custom_compress %s", file->clntcompprog);
411         } 
412
413         if ( n ) {
414           buffer += n;
415           buflen -= n;
416           n = 0;
417         }
418
419         if (*file->srv_encrypt) {
420             n = snprintf(buffer, buflen, " server_encrypt %s", file->srv_encrypt);
421         } else if (*file->clnt_encrypt) {
422             n = snprintf(buffer, buflen, " client_encrypt %s", file->clnt_encrypt);
423         } 
424
425         if ( n ) {
426           buffer += n;
427           buflen -= n;
428           n = 0;
429         }
430         
431         if (*file->srv_decrypt_opt) {
432             n = snprintf(buffer, buflen, " server_decrypt_option %s", file->srv_decrypt_opt);
433         } else if (*file->clnt_decrypt_opt) {
434             n = snprintf(buffer, buflen, " client_decrypt_option %s", file->clnt_decrypt_opt);
435         } 
436
437         if ( n ) {
438           buffer += n;
439           buflen -= n;
440           n = 0;
441         }
442
443         n = snprintf(buffer, buflen, "\n");
444         buffer += n;
445         buflen -= n;
446
447         if (file->cont_filename[0] != '\0') {
448             n = snprintf(buffer, buflen, "CONT_FILENAME=%s\n",
449                 file->cont_filename);
450             buffer += n;
451             buflen -= n;
452         }
453         if (file->is_partial != 0) {
454             n = snprintf(buffer, buflen, "PARTIAL=YES\n");
455             buffer += n;
456             buflen -= n;
457         }
458
459         n = snprintf(buffer, buflen, 
460             "To restore, position tape at start of file and run:\n");
461         buffer += n;
462         buflen -= n;
463
464         /* \014 == ^L */
465         n = snprintf(buffer, buflen,
466             "\tdd if=<tape> bs=%ldk skip=1 |%s %s %s\n\014\n",
467             file->blocksize / 1024, file->decrypt_cmd, file->uncompress_cmd, file->recover_cmd);
468         buffer += n;
469         buflen -= n;
470         break;
471
472     case F_TAPEEND:
473         snprintf(buffer, buflen, "AMANDA: TAPEEND DATE %s\n\014\n",
474             file->datestamp);
475         break;
476
477     case F_UNKNOWN:
478     case F_WEIRD:
479         break;
480     }
481 }
482
483 /*
484  * Prints the contents of the file structure.
485  */
486 void
487 print_header(outf, file)
488     FILE *outf;
489     const dumpfile_t *file;
490 {
491     char number[NUM_STR_SIZE*2];
492     switch(file->type) {
493     case F_UNKNOWN:
494         fprintf(outf, "UNKNOWN file\n");
495         break;
496     case F_WEIRD:
497         fprintf(outf, "WEIRD file\n");
498         break;
499     case F_TAPESTART:
500         fprintf(outf, "start of tape: date %s label %s\n",
501                file->datestamp, file->name);
502         break;
503     case F_DUMPFILE:
504     case F_CONT_DUMPFILE:
505         fprintf(outf, "%s: date %s host %s disk %s lev %d comp %s",
506             filetype2str(file->type), file->datestamp, file->name,
507             file->disk, file->dumplevel, file->comp_suffix);
508         if (*file->program)
509             fprintf(outf, " program %s",file->program);
510         if (strcmp(file->encrypt_suffix, "enc") == 0)
511             fprintf(outf, " crypt %s", file->encrypt_suffix);
512         if (*file->srvcompprog)
513             fprintf(outf, " server_custom_compress %s", file->srvcompprog);
514         if (*file->clntcompprog)
515             fprintf(outf, " client_custom_compress %s", file->clntcompprog);
516         if (*file->srv_encrypt)
517             fprintf(outf, " server_encrypt %s", file->srv_encrypt);
518         if (*file->clnt_encrypt)
519             fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
520         if (*file->srv_decrypt_opt)
521             fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
522         if (*file->clnt_decrypt_opt)
523             fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
524         fprintf(outf, "\n");
525         break;
526     case F_SPLIT_DUMPFILE:
527         if(file->totalparts > 0){
528             snprintf(number, sizeof(number), "%d", file->totalparts);
529         }   
530         else snprintf(number, sizeof(number), "UNKNOWN");
531         fprintf(outf, "split dumpfile: date %s host %s disk %s part %d/%s lev %d comp %s",
532                       file->datestamp, file->name, file->disk, file->partnum,
533                       number, file->dumplevel, file->comp_suffix);
534         if (*file->program)
535             fprintf(outf, " program %s",file->program);
536         if (strcmp(file->encrypt_suffix, "enc") == 0)
537             fprintf(outf, " crypt %s", file->encrypt_suffix);
538         if (*file->srvcompprog)
539             fprintf(outf, " server_custom_compress %s", file->srvcompprog);
540         if (*file->clntcompprog)
541             fprintf(outf, " client_custom_compress %s", file->clntcompprog);
542         if (*file->srv_encrypt)
543             fprintf(outf, " server_encrypt %s", file->srv_encrypt);
544         if (*file->clnt_encrypt)
545             fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
546         if (*file->srv_decrypt_opt)
547             fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
548         if (*file->clnt_decrypt_opt)
549             fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
550         fprintf(outf, "\n");
551         break;
552     case F_TAPEEND:
553         fprintf(outf, "end of tape: date %s\n", file->datestamp);
554         break;
555     }
556 }
557
558 int
559 known_compress_type(file)
560     const dumpfile_t *file;
561 {
562     if(strcmp(file->comp_suffix, ".Z") == 0)
563         return 1;
564 #ifdef HAVE_GZIP
565     if(strcmp(file->comp_suffix, ".gz") == 0)
566         return 1;
567 #endif
568     if(strcmp(file->comp_suffix, "cust") == 0)
569         return 1;
570     return 0;
571 }
572
573 static const struct {
574     filetype_t type;
575     const char *str;
576 } filetypetab[] = {
577     { F_UNKNOWN, "UNKNOWN" },
578     { F_WEIRD, "WEIRD" },
579     { F_TAPESTART, "TAPESTART" },
580     { F_TAPEEND,  "TAPEEND" },
581     { F_DUMPFILE, "FILE" },
582     { F_CONT_DUMPFILE, "CONT_FILE" },
583     { F_SPLIT_DUMPFILE, "SPLIT_FILE" }
584 };
585 #define NFILETYPES      (sizeof(filetypetab) / sizeof(filetypetab[0]))
586
587 static const char *
588 filetype2str(type)
589     filetype_t type;
590 {
591     int i;
592
593     for (i = 0; i < NFILETYPES; i++)
594         if (filetypetab[i].type == type)
595             return (filetypetab[i].str);
596     return ("UNKNOWN");
597 }
598
599 static filetype_t
600 str2filetype(str)
601     const char *str;
602 {
603     int i;
604
605     for (i = 0; i < NFILETYPES; i++)
606         if (strcmp(filetypetab[i].str, str) == 0)
607             return (filetypetab[i].type);
608     return (F_UNKNOWN);
609 }