535e4a6068c5b9528460ba3c0413359d15e77393
[debian/amanda] / restore-src / amrestore.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 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: amrestore.c,v 1.56.2.1 2006/04/07 10:52:17 martinea Exp $
28  *
29  * retrieves files from an amanda tape
30  */
31 /*
32  * Pulls all files from the tape that match the hostname, diskname and
33  * datestamp regular expressions.
34  *
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.
39  */
40
41 #include "amanda.h"
42 #include "util.h"
43 #include "tapeio.h"
44 #include "fileheader.h"
45 #include "restore.h"
46
47 #define CREAT_MODE      0640
48
49 static int file_number;
50 static pid_t comp_enc_pid = -1;
51 static int tapedev;
52 static long filefsf = -1;
53
54 /* local functions */
55
56 static void errexit P((void));
57 static void usage P((void));
58 int main P((int argc, char **argv));
59
60 static void errexit()
61 /*
62  * Do exit(2) after an error, rather than exit(1).
63  */
64 {
65     exit(2);
66 }
67
68
69 static void usage()
70 /*
71  * Print usage message and terminate.
72  */
73 {
74     error("Usage: amrestore [-b blocksize] [-r|-c] [-p] [-h] [-f fileno] [-l label] tape-device|holdingfile [hostname [diskname [datestamp [hostname [diskname [datestamp ... ]]]]]]");
75 }
76
77 int main(argc, argv)
78 int argc;
79 char **argv;
80 /*
81  * Parses command line, then loops through all files on tape, restoring
82  * files that match the command line criteria.
83  */
84 {
85     extern int optind;
86     int opt;
87     char *errstr;
88     int isafile;
89     struct stat stat_tape;
90     dumpfile_t file;
91     char *filename = NULL;
92     char *tapename = NULL;
93     struct match_list {
94         char *hostname;
95         char *diskname;
96         char *datestamp;
97         struct match_list *next;
98     } *match_list = NULL, *me = NULL;
99     int found_match;
100     int arg_state;
101     amwait_t compress_status;
102     int r = 0;
103     char *e;
104     char *err;
105     char *label = NULL;
106     rst_flags_t *rst_flags;
107     int count_error;
108     size_t read_result;
109
110     safe_fd(-1, 0);
111
112     set_pname("amrestore");
113
114     /* Don't die when child closes pipe */
115     signal(SIGPIPE, SIG_IGN);
116
117     erroutput_type = ERR_INTERACTIVE;
118
119     onerror(errexit);
120
121     rst_flags = new_rst_flags();
122     rst_flags->inline_assemble = 0;
123
124     /* handle options */
125     while( (opt = getopt(argc, argv, "b:cCd:rphf:l:")) != -1) {
126         switch(opt) {
127         case 'b':
128             rst_flags->blocksize = strtol(optarg, &e, 10);
129             if(*e == 'k' || *e == 'K') {
130                 rst_flags->blocksize *= 1024;
131             } else if(*e == 'm' || *e == 'M') {
132                 rst_flags->blocksize *= 1024 * 1024;
133             } else if(*e != '\0') {
134                 error("invalid rst_flags->blocksize value \"%s\"", optarg);
135             }
136             if(rst_flags->blocksize < DISK_BLOCK_BYTES) {
137                 error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
138             }
139             break;
140         case 'c': rst_flags->compress = 1; break;
141         case 'C':
142             rst_flags->compress = 1;
143             rst_flags->comp_type = COMPRESS_BEST_OPT;
144             break;
145         case 'r': rst_flags->raw = 1; break;
146         case 'p': rst_flags->pipe_to_fd = fileno(stdout); break;
147         case 'h': rst_flags->headers = 1; break;
148         case 'f':
149             filefsf = strtol(optarg, &e, 10);
150             if(*e != '\0') {
151                 error("invalid fileno value \"%s\"", optarg);
152             }
153             break;
154         case 'l':
155             label = stralloc(optarg);
156             break;
157         default:
158             usage();
159         }
160     }
161
162     if(rst_flags->compress && rst_flags->raw) {
163         fprintf(stderr, 
164                 "Cannot specify both -r (raw) and -c (compressed) output.\n");
165         usage();
166     }
167
168     if(optind >= argc) {
169         fprintf(stderr, "%s: Must specify tape-device or holdingfile\n",
170                         get_pname());
171         usage();
172     }
173
174     tapename = argv[optind++];
175
176 #define ARG_GET_HOST 0
177 #define ARG_GET_DISK 1
178 #define ARG_GET_DATE 2
179
180     arg_state = ARG_GET_HOST;
181     while(optind < argc) {
182         switch(arg_state) {
183         case ARG_GET_HOST:
184             /*
185              * This is a new host/disk/date triple, so allocate a match_list.
186              */
187             me = alloc(sizeof(*me));
188             me->hostname = argv[optind++];
189             me->diskname = "";
190             me->datestamp = "";
191             me->next = match_list;
192             match_list = me;
193             if(me->hostname[0] != '\0'
194                && (errstr=validate_regexp(me->hostname)) != NULL) {
195                 fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
196                         get_pname(), me->hostname, errstr);
197                 usage();
198             }
199             arg_state = ARG_GET_DISK;
200             break;
201         case ARG_GET_DISK:
202             me->diskname = argv[optind++];
203             if(me->diskname[0] != '\0'
204                && (errstr=validate_regexp(me->diskname)) != NULL) {
205                 fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
206                         get_pname(), me->diskname, errstr);
207                 usage();
208             }
209             arg_state = ARG_GET_DATE;
210             break;
211         case ARG_GET_DATE:
212             me->datestamp = argv[optind++];
213             if(me->datestamp[0] != '\0'
214                && (errstr=validate_regexp(me->datestamp)) != NULL) {
215                 fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
216                         get_pname(), me->datestamp, errstr);
217                 usage();
218             }
219             arg_state = ARG_GET_HOST;
220             break;
221         }
222     }
223     if(match_list == NULL) {
224         match_list = alloc(sizeof(*match_list));
225         match_list->hostname = "";
226         match_list->diskname = "";
227         match_list->datestamp = "";
228         match_list->next = NULL;
229     }
230
231     if(tape_stat(tapename,&stat_tape)!=0) {
232         error("could not stat %s: %s", tapename, strerror(errno));
233     }
234     isafile=S_ISREG((stat_tape.st_mode));
235
236     if(label) {
237         if(isafile) {
238             fprintf(stderr,"%s: ignoring -l flag when restoring from a file.\n",
239                     get_pname());
240         }
241         else {
242             if((err = tape_rewind(tapename)) != NULL) {
243                 error("Could not rewind device '%s': %s", tapename, err);
244             }
245             if ((tapedev = tape_open(tapename, 0)) == -1) {;
246                 error("Could not open device '%s': %s", tapename, err);
247             }
248             read_file_header(&file, tapedev, isafile, rst_flags);
249             if(file.type != F_TAPESTART) {
250                 fprintf(stderr,"Not an amanda tape\n");
251                 exit (1);
252             }
253             if(strcmp(label, file.name) != 0) {
254                 fprintf(stderr,"Wrong label: '%s'\n", file.name);
255                 exit (1);
256             }
257             tapefd_close(tapedev);
258             if((err = tape_rewind(tapename)) != NULL) {
259                 error("Could not rewind device '%s': %s", tapename, err);
260             }
261         }
262     }
263     file_number = 0;
264     if(filefsf != -1) {
265         if(isafile) {
266             fprintf(stderr,"%s: ignoring -f flag when restoring from a file.\n",
267                     get_pname());
268         }
269         else {
270             if((err = tape_rewind(tapename)) != NULL) {
271                 error("Could not rewind device '%s': %s", tapename, err);
272             }
273             if((err = tape_fsf(tapename,filefsf)) != NULL) {
274                 error("Could not fsf device '%s': %s", tapename, err);
275             }
276             file_number = filefsf;
277         }
278     }
279
280     if(isafile) {
281         tapedev = open(tapename, O_RDWR);
282     } else {
283         tapedev = tape_open(tapename, 0);
284     }
285     if(tapedev < 0) {
286         error("could not open %s: %s", tapename, strerror(errno));
287     }
288
289     read_file_header(&file, tapedev, isafile, rst_flags);
290
291     if(file.type != F_TAPESTART && !isafile && filefsf == -1) {
292         fprintf(stderr, "%s: WARNING: not at start of tape, file numbers will be offset\n",
293                         get_pname());
294     }
295
296     count_error=0;
297     read_result = 0;
298     while(count_error < 10) {
299         if(file.type == F_TAPEEND) break;
300         found_match = 0;
301         if(file.type == F_DUMPFILE || file.type == F_SPLIT_DUMPFILE) {
302             amfree(filename);
303             filename = make_filename(&file);
304             for(me = match_list; me; me = me->next) {
305                 if(disk_match(&file,me->datestamp,me->hostname,me->diskname,"") != 0) {
306                     found_match = 1;
307                     break;
308                 }
309             }
310             fprintf(stderr, "%s: %3d: %s ",
311                             get_pname(),
312                             file_number,
313                             found_match ? "restoring" : "skipping");
314             if(file.type != F_DUMPFILE  && file.type != F_SPLIT_DUMPFILE) {
315                 print_header(stderr, &file);
316             } else {
317                 fprintf(stderr, "%s\n", filename);
318             }
319         }
320         if(found_match) {
321             count_error=0;
322             read_result = restore(&file, filename,
323                 tapedev, isafile, rst_flags);
324             if(comp_enc_pid > 0) {
325                 waitpid(comp_enc_pid, &compress_status, 0);
326                 comp_enc_pid = -1;
327             }
328             if(rst_flags->pipe_to_fd != -1) {
329                 file_number++;                  /* for the last message */
330                 break;
331             }
332         }
333         if(isafile) {
334             break;
335         }
336         /*
337          * Note that at this point we know we are working with a tape,
338          * not a holding disk file, so we can call the tape functions
339          * without checking.
340          */
341         if(read_result == 0) {
342             /*
343              * If the last read got EOF, how to get to the next
344              * file depends on how the tape device driver is acting.
345              * If it is BSD-like, we do not really need to do anything.
346              * If it is Sys-V-like, we need to either fsf or close/open.
347              * The good news is, a close/open works in either case,
348              * so that's what we do.
349              */
350             tapefd_close(tapedev);
351             if((tapedev = tape_open(tapename, 0)) < 0) {
352                 error("could not open %s: %s", tapename, strerror(errno));
353             }
354             count_error++;
355         } else {
356             /*
357              * If the last read got something (even an error), we can
358              * do an fsf to get to the next file.
359              */
360             if(tapefd_fsf(tapedev, 1) < 0) {
361                 error("could not fsf %s: %s", tapename, strerror(errno));
362             }
363             count_error=0;
364         }
365         file_number++;
366         read_file_header(&file, tapedev, isafile, rst_flags);
367     }
368     if(isafile) {
369         close(tapedev);
370     } else {
371         /*
372          * See the notes above about advancing to the next file.
373          */
374         if(read_result == 0) {
375             tapefd_close(tapedev);
376             if((tapedev = tape_open(tapename, 0)) < 0) {
377                 error("could not open %s: %s", tapename, strerror(errno));
378             }
379         } else {
380             if(tapefd_fsf(tapedev, 1) < 0) {
381                 error("could not fsf %s: %s", tapename, strerror(errno));
382             }
383         }
384         tapefd_close(tapedev);
385     }
386
387     if((read_result <= 0 || file.type == F_TAPEEND) && !isafile) {
388         fprintf(stderr, "%s: %3d: reached ", get_pname(), file_number);
389         if(read_result <= 0) {
390             fprintf(stderr, "end of information\n");
391         } else {
392             print_header(stderr,&file);
393         }
394         r = 1;
395     }
396     return r;
397 }