2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: amrestore.c,v 1.28.2.4.4.3.2.8.2.3 2004/11/19 18:12:30 martinea Exp $
29 * retrieves files from an amanda tape
32 * Pulls all files from the tape that match the hostname, diskname and
33 * datestamp regular expressions.
35 * If the header is output, only up to DISK_BLOCK_BYTES worth of it is
36 * sent, regardless of the tape blocksize. This makes the disk image
37 * look like a holding disk image, and also makes it easier to remove
38 * the header (e.g. in amrecover) since it has a fixed size.
43 #include "fileheader.h"
46 #define CREAT_MODE 0640
50 int compflag, rawflag, pipeflag, headerflag;
51 int got_sigpipe, file_number;
52 pid_t compress_pid = -1;
53 char *compress_type = COMPRESS_FAST_OPT;
61 void errexit P((void));
62 void handle_sigpipe P((int sig));
63 int disk_match P((dumpfile_t *file, char *datestamp,
64 char *hostname, char *diskname));
65 char *make_filename P((dumpfile_t *file));
66 void read_file_header P((dumpfile_t *file, int isafile));
67 static int get_block P((int isafile));
68 void restore P((dumpfile_t *file, char *filename, int isafile));
70 int main P((int argc, char **argv));
74 * Do exit(2) after an error, rather than exit(1).
81 void handle_sigpipe(sig)
84 * Signal handler for the SIGPIPE signal. Just sets a flag and returns.
85 * The act of catching the signal causes the pipe write() to fail with
92 int disk_match(file, datestamp, hostname, diskname)
94 char *datestamp, *hostname, *diskname;
97 * Returns 1 if the current dump file matches the hostname and diskname
98 * regular expressions given on the command line, 0 otherwise. As a
99 * special case, empty regexs are considered equivalent to ".*": they
103 if(file->type != F_DUMPFILE) return 0;
105 if((*hostname == '\0' || match_host(hostname, file->name)) &&
106 (*diskname == '\0' || match_disk(diskname, file->disk)) &&
107 (*datestamp== '\0' || match_datestamp(datestamp, file->datestamp)))
114 char *make_filename(file)
117 char number[NUM_STR_SIZE];
121 ap_snprintf(number, sizeof(number), "%d", file->dumplevel);
122 sfn = sanitise_filename(file->disk);
123 fn = vstralloc(file->name,
136 static int get_block(isafile)
139 static int test_blocksize = 1;
143 * If this is the first call, set the blocksize if it was not on
144 * the command line. Allocate the I/O buffer in any case.
146 * For files, the blocksize is always DISK_BLOCK_BYTES. For tapes,
147 * we allocate a large buffer and set the size to the length of the
148 * first (successful) record.
154 blocksize = buflen = DISK_BLOCK_BYTES;
156 buflen = MAX_TAPE_BLOCK_BYTES;
159 buffer = newalloc(buffer, buflen);
162 bytes_read = fullread(tapedev, buffer, buflen);
164 bytes_read = tapefd_read(tapedev, buffer, buflen);
165 if(blocksize < 0 && bytes_read > 0 && bytes_read < buflen) {
168 blocksize = bytes_read;
169 new_buffer = alloc(blocksize);
170 memcpy(new_buffer, buffer, bytes_read);
182 void read_file_header(file, isafile)
186 * Reads the first block of a tape file.
189 bytes_read = get_block(isafile);
191 error("error reading file header: %s", strerror(errno));
192 } else if(bytes_read < blocksize) {
193 if(bytes_read == 0) {
194 fprintf(stderr, "%s: missing file header block\n", get_pname());
196 fprintf(stderr, "%s: short file header block: %d byte%s\n",
197 get_pname(), bytes_read, (bytes_read == 1) ? "" : "s");
199 file->type = F_UNKNOWN;
201 parse_file_header(buffer, file, bytes_read);
207 void restore(file, filename, isafile)
212 * Restore the current file from tape. Depending on the settings of
213 * the command line flags, the file might need to be compressed or
214 * uncompressed. If so, a pipe through compress or uncompress is set
215 * up. The final output usually goes to a file named host.disk.date.lev,
216 * but with the -p flag the output goes to stdout (and presumably is
220 int rc = 0, dest, out, outpipe[2];
223 int file_is_compressed;
225 /* adjust compression flag */
227 file_is_compressed = file->compressed;
228 if(!compflag && file_is_compressed && !known_compress_type(file)) {
230 "%s: unknown compression suffix %s, can't uncompress\n",
231 get_pname(), file->comp_suffix);
235 /* set up final destination file */
238 dest = 1; /* standard output */
240 char *filename_ext = NULL;
243 filename_ext = file_is_compressed ? file->comp_suffix
246 filename_ext = ".RAW";
250 filename_ext = stralloc2(filename, filename_ext);
252 if((dest = creat(filename, CREAT_MODE)) < 0)
253 error("could not create output file: %s", strerror(errno));
254 amfree(filename_ext);
260 * If -r or -h, write the header before compress or uncompress pipe.
261 * Only write DISK_BLOCK_BYTES, regardless of how much was read.
262 * This makes the output look like a holding disk image, and also
263 * makes it easier to remove the header (e.g. in amrecover) since
264 * it has a fixed size.
266 if(rawflag || headerflag) {
270 if(compflag && !file_is_compressed) {
271 file->compressed = 1;
272 ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
273 " %s %s |", UNCOMPRESS_PATH,
274 #ifdef UNCOMPRESS_OPT
280 strncpy(file->comp_suffix,
282 sizeof(file->comp_suffix)-1);
283 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
286 /* remove CONT_FILENAME from header */
287 cont_filename = stralloc(file->cont_filename);
288 memset(file->cont_filename,'\0',sizeof(file->cont_filename));
289 file->blocksize = DISK_BLOCK_BYTES;
290 build_header(buffer, file, bytes_read);
292 if((w = fullwrite(out, buffer, DISK_BLOCK_BYTES)) != DISK_BLOCK_BYTES) {
294 error("write error: %s", strerror(errno));
296 error("write error: %d instead of %d", w, DISK_BLOCK_BYTES);
299 /* add CONT_FILENAME to header */
300 strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename));
303 /* if -c and file not compressed, insert compress pipe */
305 if(compflag && !file_is_compressed) {
306 if(pipe(outpipe) < 0) error("error [pipe: %s]", strerror(errno));
308 switch(compress_pid = fork()) {
309 case -1: error("could not fork for %s: %s",
310 COMPRESS_PATH, strerror(errno));
317 if(outpipe[0] != 0) {
318 if(dup2(outpipe[0], 0) == -1)
319 error("error [dup2 pipe: %s]", strerror(errno));
323 if(dup2(dest, 1) == -1)
324 error("error [dup2 dest: %s]", strerror(errno));
327 if (*compress_type == '\0') {
328 compress_type = NULL;
330 execlp(COMPRESS_PATH, COMPRESS_PATH, compress_type, (char *)0);
331 error("could not exec %s: %s", COMPRESS_PATH, strerror(errno));
335 /* if not -r or -c, and file is compressed, insert uncompress pipe */
337 else if(!rawflag && !compflag && file_is_compressed) {
339 * XXX for now we know that for the two compression types we
340 * understand, .Z and optionally .gz, UNCOMPRESS_PATH will take
341 * care of both. Later, we may need to reference a table of
342 * possible uncompress programs.
344 if(pipe(outpipe) < 0) error("error [pipe: %s]", strerror(errno));
346 switch(compress_pid = fork()) {
348 error("could not fork for %s: %s",
349 UNCOMPRESS_PATH, strerror(errno));
356 if(outpipe[0] != 0) {
357 if(dup2(outpipe[0], 0) < 0)
358 error("dup2 pipe: %s", strerror(errno));
362 if(dup2(dest, 1) < 0)
363 error("dup2 dest: %s", strerror(errno));
366 (void) execlp(UNCOMPRESS_PATH, UNCOMPRESS_PATH,
367 #ifdef UNCOMPRESS_OPT
371 error("could not exec %s: %s", UNCOMPRESS_PATH, strerror(errno));
376 /* copy the rest of the file from tape to the output */
380 bytes_read = get_block(isafile);
382 error("read error: %s", strerror(errno));
384 if(bytes_read == 0 && isafile) {
386 * See if we need to switch to the next file.
388 if(file->cont_filename[0] == '\0') {
389 break; /* no more files */
392 if((tapedev = open(file->cont_filename, O_RDONLY)) == -1) {
393 char *cont_filename = strrchr(file->cont_filename,'/');
396 if((tapedev = open(cont_filename,O_RDONLY)) == -1) {
397 error("can't open %s: %s", file->cont_filename,
401 fprintf(stderr, "cannot open %s: %s\n",
402 file->cont_filename, strerror(errno));
403 fprintf(stderr, "using %s\n",
408 error("can't open %s: %s", file->cont_filename,
412 read_file_header(file, isafile);
413 if(file->type != F_DUMPFILE && file->type != F_CONT_DUMPFILE) {
414 fprintf(stderr, "unexpected header type: ");
415 print_header(stderr, file);
420 for(l = 0; l < bytes_read; l += s) {
421 if((s = write(out, buffer + l, bytes_read - l)) < 0) {
423 fprintf(stderr,"Error %d (%s) offset %d+%d, wrote %d\n",
424 errno, strerror(errno), wc, bytes_read, rc);
426 "%s: pipe reader has quit in middle of file.\n",
429 perror("amrestore: write error");
435 } while (bytes_read > 0);
448 * Print usage message and terminate.
451 error("Usage: amrestore [-b blocksize] [-r|-c] [-p] [-h] [-f fileno] [-l label] tape-device|holdingfile [hostname [diskname [datestamp [hostname [diskname [datestamp ... ]]]]]]");
459 * Parses command line, then loops through all files on tape, restoring
460 * files that match the command line criteria.
467 struct stat stat_tape;
469 char *filename = NULL;
470 char *tapename = NULL;
475 struct match_list *next;
476 } *match_list = NULL, *me = NULL;
479 amwait_t compress_status;
487 for(fd = 3; fd < FD_SETSIZE; fd++) {
489 * Make sure nobody spoofs us with a lot of extra open files
490 * that would cause an open we do to get a very high file
491 * descriptor, which in turn might be used as an index into
492 * an array (e.g. an fd_set).
497 set_pname("amrestore");
499 erroutput_type = ERR_INTERACTIVE;
502 signal(SIGPIPE, handle_sigpipe);
505 while( (opt = getopt(argc, argv, "b:cCd:rpkhf:l:")) != -1) {
508 blocksize = strtol(optarg, &e, 10);
509 if(*e == 'k' || *e == 'K') {
511 } else if(*e == 'm' || *e == 'M') {
512 blocksize *= 1024 * 1024;
513 } else if(*e != '\0') {
514 error("invalid blocksize value \"%s\"", optarg);
516 if(blocksize < DISK_BLOCK_BYTES) {
517 error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
520 case 'c': compflag = 1; break;
521 case 'C': compflag = 1; compress_type = COMPRESS_BEST_OPT; break;
522 case 'r': rawflag = 1; break;
523 case 'p': pipeflag = 1; break;
524 case 'h': headerflag = 1; break;
526 filefsf = strtol(optarg, &e, 10);
528 error("invalid fileno value \"%s\"", optarg);
532 label = stralloc(optarg);
539 if(compflag && rawflag) {
541 "Cannot specify both -r (raw) and -c (compressed) output.\n");
546 fprintf(stderr, "%s: Must specify tape-device or holdingfile\n",
551 tapename = argv[optind++];
553 #define ARG_GET_HOST 0
554 #define ARG_GET_DISK 1
555 #define ARG_GET_DATE 2
557 arg_state = ARG_GET_HOST;
558 while(optind < argc) {
562 * This is a new host/disk/date triple, so allocate a match_list.
564 me = alloc(sizeof(*me));
565 me->hostname = argv[optind++];
568 me->next = match_list;
570 if(me->hostname[0] != '\0'
571 && (errstr=validate_regexp(me->hostname)) != NULL) {
572 fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
573 get_pname(), me->hostname, errstr);
576 arg_state = ARG_GET_DISK;
579 me->diskname = argv[optind++];
580 if(me->diskname[0] != '\0'
581 && (errstr=validate_regexp(me->diskname)) != NULL) {
582 fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
583 get_pname(), me->diskname, errstr);
586 arg_state = ARG_GET_DATE;
589 me->datestamp = argv[optind++];
590 if(me->datestamp[0] != '\0'
591 && (errstr=validate_regexp(me->datestamp)) != NULL) {
592 fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
593 get_pname(), me->datestamp, errstr);
596 arg_state = ARG_GET_HOST;
600 if(match_list == NULL) {
601 match_list = alloc(sizeof(*match_list));
602 match_list->hostname = "";
603 match_list->diskname = "";
604 match_list->datestamp = "";
605 match_list->next = NULL;
608 if(tape_stat(tapename,&stat_tape)!=0) {
609 error("could not stat %s: %s", tapename, strerror(errno));
611 isafile=S_ISREG((stat_tape.st_mode));
615 fprintf(stderr,"%s: ignoring -l flag when restoring from a file.\n",
619 if((err = tape_rewind(tapename)) != NULL) {
620 error("Could not rewind device '%s': %s", tapename, err);
622 tapedev = tape_open(tapename, 0);
623 read_file_header(&file, isafile);
624 if(file.type != F_TAPESTART) {
625 fprintf(stderr,"Not an amanda tape\n");
628 if(strcmp(label, file.name) != 0) {
629 fprintf(stderr,"Wrong label: '%s'\n", file.name);
632 tapefd_close(tapedev);
633 if((err = tape_rewind(tapename)) != NULL) {
634 error("Could not rewind device '%s': %s", tapename, err);
641 fprintf(stderr,"%s: ignoring -f flag when restoring from a file.\n",
645 if((err = tape_rewind(tapename)) != NULL) {
646 error("Could not rewind device '%s': %s", tapename, err);
648 if((err = tape_fsf(tapename,filefsf)) != NULL) {
649 error("Could not fsf device '%s': %s", tapename, err);
651 file_number = filefsf;
656 tapedev = open(tapename, 0);
658 tapedev = tape_open(tapename, 0);
661 error("could not open %s: %s", tapename, strerror(errno));
664 read_file_header(&file, isafile);
666 if(file.type != F_TAPESTART && !isafile && filefsf == -1) {
667 fprintf(stderr, "%s: WARNING: not at start of tape, file numbers will be offset\n",
672 while(count_error < 10) {
673 if(file.type == F_TAPEEND) break;
675 if(file.type == F_DUMPFILE) {
677 filename = make_filename(&file);
678 for(me = match_list; me; me = me->next) {
679 if(disk_match(&file,me->datestamp,me->hostname,me->diskname) != 0) {
684 fprintf(stderr, "%s: %3d: %s ",
687 found_match ? "restoring" : "skipping");
688 if(file.type != F_DUMPFILE) {
689 print_header(stderr, &file);
691 fprintf(stderr, "%s\n", filename);
695 restore(&file, filename, isafile);
696 if(compress_pid > 0) {
697 waitpid(compress_pid, &compress_status, 0);
701 file_number++; /* for the last message */
709 * Note that at this point we know we are working with a tape,
710 * not a holding disk file, so we can call the tape functions
713 if(bytes_read == 0) {
715 * If the last read got EOF, how to get to the next
716 * file depends on how the tape device driver is acting.
717 * If it is BSD-like, we do not really need to do anything.
718 * If it is Sys-V-like, we need to either fsf or close/open.
719 * The good news is, a close/open works in either case,
720 * so that's what we do.
722 tapefd_close(tapedev);
723 if((tapedev = tape_open(tapename, 0)) < 0) {
724 error("could not open %s: %s", tapename, strerror(errno));
729 * If the last read got something (even an error), we can
730 * do an fsf to get to the next file.
732 if(tapefd_fsf(tapedev, 1) < 0) {
733 error("could not fsf %s: %s", tapename, strerror(errno));
738 read_file_header(&file, isafile);
744 * See the notes above about advancing to the next file.
746 if(bytes_read == 0) {
747 tapefd_close(tapedev);
748 if((tapedev = tape_open(tapename, 0)) < 0) {
749 error("could not open %s: %s", tapename, strerror(errno));
752 if(tapefd_fsf(tapedev, 1) < 0) {
753 error("could not fsf %s: %s", tapename, strerror(errno));
756 tapefd_close(tapedev);
759 if((bytes_read <= 0 || file.type == F_TAPEEND) && !isafile) {
760 fprintf(stderr, "%s: %3d: reached ", get_pname(), file_number);
761 if(bytes_read <= 0) {
762 fprintf(stderr, "end of information\n");
764 print_header(stderr,&file);