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