X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=oldrecover-src%2Fextract_list.c;fp=oldrecover-src%2Fextract_list.c;h=5c5d92accb034d7d7e8c37681968d6f86f3b00dc;hb=12179dea039515c06168c0037d048566a3f623de;hp=0000000000000000000000000000000000000000;hpb=94c03cae686e4196a345d72452fda2a5203768ce;p=debian%2Famanda diff --git a/oldrecover-src/extract_list.c b/oldrecover-src/extract_list.c new file mode 100644 index 0000000..5c5d92a --- /dev/null +++ b/oldrecover-src/extract_list.c @@ -0,0 +1,2247 @@ +/* + * Amanda, The Advanced Maryland Automatic Network Disk Archiver + * Copyright (c) 1991-1998, 2000 University of Maryland at College Park + * All Rights Reserved. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of U.M. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. U.M. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Authors: the Amanda Development Team. Its members are listed in a + * file named AUTHORS, in the root directory of this distribution. + */ +/* + * $Id: extract_list.c,v 1.6 2006/08/24 01:57:15 paddy_s Exp $ + * + * implements the "extract" command in amrecover + */ + +#include "amanda.h" +#include "version.h" +#include "amrecover.h" +#include "fileheader.h" +#include "dgram.h" +#include "stream.h" +#include "tapelist.h" +#ifdef SAMBA_CLIENT +#include "findpass.h" +#endif +#include "util.h" + +typedef struct EXTRACT_LIST_ITEM { + char *path; + + struct EXTRACT_LIST_ITEM *next; +} +EXTRACT_LIST_ITEM; + +typedef struct EXTRACT_LIST { + char *date; /* date tape created */ + int level; /* level of dump */ + char *tape; /* tape label */ + off_t fileno; /* fileno on tape */ + EXTRACT_LIST_ITEM *files; /* files to get off tape */ + + struct EXTRACT_LIST *next; +} +EXTRACT_LIST; + +#define SKIP_TAPE 2 +#define RETRY_TAPE 3 + +char *dump_device_name = NULL; + +extern char *localhost; + +/* global pid storage for interrupt handler */ +pid_t extract_restore_child_pid = -1; + +static EXTRACT_LIST *extract_list = NULL; +static int tape_control_sock = -1; +static int tape_data_sock = -1; + +#ifdef SAMBA_CLIENT +unsigned short samba_extract_method = SAMBA_TAR; +#endif /* SAMBA_CLIENT */ + +#define READ_TIMEOUT 240*60 + +EXTRACT_LIST *first_tape_list(void); +EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list); +int is_extract_list_nonempty(void); +int length_of_tape_list(EXTRACT_LIST *tape_list); +void add_file(char *path, char *regex); +void add_glob(char *glob); +void add_regex(char *regex); +void clear_extract_list(void); +void clean_tape_list(EXTRACT_LIST *tape_list); +void clean_extract_list(void); +void delete_file(char *path, char *regex); +void delete_glob(char *glob); +void delete_regex(char *regex); +void delete_tape_list(EXTRACT_LIST *tape_list); +void display_extract_list(char *file); +void extract_files(void); +void read_file_header(char *buffer, + dumpfile_t *file, + size_t buflen, + int tapedev); +void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist); +void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist); + +static int add_extract_item(DIR_ITEM *ditem); +static int delete_extract_item(DIR_ITEM *ditem); +static int extract_files_setup(char *label, off_t fsf); +static int okay_to_continue(int allow_tape, + int allow_skip, + int allow_retry); +static int okay_to_continue(int, int, int); +static ssize_t read_buffer(int datafd, + char *buffer, + size_t buflen, + long timeout_s); +static void clear_tape_list(EXTRACT_LIST *tape_list); +static void extract_files_child(int in_fd, EXTRACT_LIST *elist); +static void send_to_tape_server(int tss, char *cmd); + + +/* + * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s) + * + * Description: + * read data from input file desciptor waiting up to timeout_s + * seconds before returning data. + * + * Inputs: + * datafd - File descriptor to read from. + * buffer - Buffer to read into. + * buflen - Maximum number of bytes to read into buffer. + * timeout_s - Seconds to wait before returning what was already read. + * + * Returns: + * >0 - Number of data bytes in buffer. + * 0 - EOF + * -1 - errno == ETIMEDOUT if no data available in specified time. + * errno == ENFILE if datafd is invalid. + * otherwise errno is set by select or read.. + */ + +static ssize_t +read_buffer( + int datafd, + char * buffer, + size_t buflen, + long timeout_s) +{ + ssize_t size = 0; + fd_set readset; + struct timeval timeout; + char *dataptr; + ssize_t spaceleft; + int nfound; + + if(datafd < 0 || datafd >= (int)FD_SETSIZE) { + errno = EMFILE; /* out of range */ + return -1; + } + + dataptr = buffer; + spaceleft = (ssize_t)buflen; + + do { + FD_ZERO(&readset); + FD_SET(datafd, &readset); + timeout.tv_sec = timeout_s; + timeout.tv_usec = 0; + nfound = select(datafd+1, &readset, NULL, NULL, &timeout); + if(nfound < 0 ) { + /* Select returned an error. */ + fprintf(stderr,"select error: %s\n", strerror(errno)); + size = -1; + break; + } + + if (nfound == 0) { + /* Select timed out. */ + if (timeout_s != 0) { + /* Not polling: a real read timeout */ + fprintf(stderr,"timeout waiting for restore\n"); + fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"); + } + errno = ETIMEDOUT; + size = -1; + break; + } + + if(!FD_ISSET(datafd, &readset)) + continue; + + /* Select says data is available, so read it. */ + size = read(datafd, dataptr, (size_t)spaceleft); + if (size < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) { + continue; + } + if (errno != EPIPE) { + fprintf(stderr, "read_buffer: read error - %s", + strerror(errno)); + break; + } + size = 0; + } + spaceleft -= size; + dataptr += size; + } while ((size > 0) && (spaceleft > 0)); + + return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size); +} + + +EXTRACT_LIST * +first_tape_list(void) +{ + return extract_list; +} + +EXTRACT_LIST * +next_tape_list( + /*@keep@*/ EXTRACT_LIST *list) +{ + if (list == NULL) + return NULL; + return list->next; +} + +static void +clear_tape_list( + EXTRACT_LIST * tape_list) +{ + EXTRACT_LIST_ITEM *this, *next; + + + this = tape_list->files; + while (this != NULL) + { + next = this->next; + amfree(this->path); + amfree(this); + this = next; + } + tape_list->files = NULL; +} + + +/* remove a tape list from the extract list, clearing the tape list + beforehand if necessary */ +void +delete_tape_list( + EXTRACT_LIST * tape_list) +{ + EXTRACT_LIST *this, *prev; + + if (tape_list == NULL) + return; + + /* is it first on the list? */ + if (tape_list == extract_list) + { + extract_list = tape_list->next; + clear_tape_list(tape_list); + amfree(tape_list->date); + amfree(tape_list->tape); + amfree(tape_list); + return; + } + + /* so not first on list - find it and delete */ + prev = extract_list; + this = extract_list->next; + while (this != NULL) + { + if (this == tape_list) + { + prev->next = tape_list->next; + clear_tape_list(tape_list); + amfree(tape_list->date); + amfree(tape_list->tape); + amfree(tape_list); + return; + } + prev = this; + this = this->next; + } + /*NOTREACHED*/ +} + + +/* return the number of files on a tape's list */ +int +length_of_tape_list( + EXTRACT_LIST * tape_list) +{ + EXTRACT_LIST_ITEM *fn; + int n; + + n = 0; + for (fn = tape_list->files; fn != NULL; fn = fn->next) + n++; + + return n; +} + + +void +clear_extract_list(void) +{ + while (extract_list != NULL) + delete_tape_list(extract_list); +} + + +void +clean_tape_list( + EXTRACT_LIST *tape_list) +{ + EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1; + EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2; + int remove_fn1; + int remove_fn2; + + pfn1 = NULL; + fn1 = tape_list->files; + while (fn1 != NULL) { + remove_fn1 = 0; + + pfn2 = fn1; + fn2 = fn1->next; + while (fn2 != NULL && remove_fn1 == 0) { + remove_fn2 = 0; + if(strcmp(fn1->path, fn2->path) == 0) { + remove_fn2 = 1; + } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 && + ((strlen(fn2->path) > strlen(fn1->path) && + fn2->path[strlen(fn1->path)] == '/') || + (fn1->path[strlen(fn1->path)-1] == '/'))) { + remove_fn2 = 1; + } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 && + ((strlen(fn1->path) > strlen(fn2->path) && + fn1->path[strlen(fn2->path)] == '/') || + (fn2->path[strlen(fn2->path)-1] == '/'))) { + remove_fn1 = 1; + break; + } + + if (remove_fn2) { + dbprintf(("removing path %s, it is included in %s\n", + fn2->path, fn1->path)); + ofn2 = fn2; + fn2 = fn2->next; + amfree(ofn2->path); + amfree(ofn2); + pfn2->next = fn2; + } else if (remove_fn1 == 0) { + pfn2 = fn2; + fn2 = fn2->next; + } + } + + if(remove_fn1 != 0) { + /* fn2->path is always valid */ + /*@i@*/ dbprintf(("removing path %s, it is included in %s\n", + /*@i@*/ fn1->path, fn2->path)); + ofn1 = fn1; + fn1 = fn1->next; + amfree(ofn1->path); + if(pfn1 == NULL) { + amfree(tape_list->files); + tape_list->files = fn1; + } else { + amfree(pfn1->next); + pfn1->next = fn1; + } + } else { + pfn1 = fn1; + fn1 = fn1->next; + } + } +} + + +void +clean_extract_list(void) +{ + EXTRACT_LIST *this; + + for (this = extract_list; this != NULL; this = this->next) + clean_tape_list(this); +} + + +/* returns -1 if error */ +/* returns 0 on succes */ +/* returns 1 if already added */ +static int +add_extract_item( + DIR_ITEM * ditem) +{ + EXTRACT_LIST *this, *this1; + EXTRACT_LIST_ITEM *that, *curr; + char *ditem_path = NULL; + + ditem_path = stralloc(ditem->path); + clean_pathname(ditem_path); + + for (this = extract_list; this != NULL; this = this->next) + { + /* see if this is the list for the tape */ + if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0) + { + /* yes, so add to list */ + curr=this->files; + while(curr!=NULL) + { + if (strcmp(curr->path, ditem_path) == 0) { + amfree(ditem_path); + return 1; + } + curr=curr->next; + } + that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM)); + that->path = stralloc(ditem_path); + that->next = this->files; + this->files = that; /* add at front since easiest */ + amfree(ditem_path); + return 0; + } + } + + /* so this is the first time we have seen this tape */ + this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST)); + this->tape = stralloc(ditem->tape); + this->level = ditem->level; + this->fileno = ditem->fileno; + this->date = stralloc(ditem->date); + that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM)); + that->path = stralloc(ditem_path); + that->next = NULL; + this->files = that; + + /* add this in date increasing order */ + /* because restore must be done in this order */ + /* add at begining */ + if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) + { + this->next = extract_list; + extract_list = this; + amfree(ditem_path); + return 0; + } + for (this1 = extract_list; this1->next != NULL; this1 = this1->next) + { + /* add in the middle */ + if(strcmp(this->date,this1->next->date) < 0) + { + this->next = this1->next; + this1->next = this; + amfree(ditem_path); + return 0; + } + } + /* add at end */ + this->next = NULL; + this1->next = this; + amfree(ditem_path); + return 0; +} + + +/* returns -1 if error */ +/* returns 0 on deletion */ +/* returns 1 if not there */ +static int +delete_extract_item( + DIR_ITEM * ditem) +{ + EXTRACT_LIST *this; + EXTRACT_LIST_ITEM *that, *prev; + char *ditem_path = NULL; + + ditem_path = stralloc(ditem->path); + clean_pathname(ditem_path); + + for (this = extract_list; this != NULL; this = this->next) + { + /* see if this is the list for the tape */ + if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0) + { + /* yes, so find file on list */ + that = this->files; + if (strcmp(that->path, ditem_path) == 0) + { + /* first on list */ + this->files = that->next; + amfree(that->path); + amfree(that); + /* if list empty delete it */ + if (this->files == NULL) + delete_tape_list(this); + amfree(ditem_path); + return 0; + } + prev = that; + that = that->next; + while (that != NULL) + { + if (strcmp(that->path, ditem_path) == 0) + { + prev->next = that->next; + amfree(that->path); + amfree(that); + amfree(ditem_path); + return 0; + } + prev = that; + that = that->next; + } + amfree(ditem_path); + return 1; + } + } + + amfree(ditem_path); + return 1; +} + +void +add_glob( + char * glob) +{ + char *regex; + char *regex_path; + char *s; + char *uqglob = unquote_string(glob); + + regex = glob_to_regex(uqglob); + dbprintf(("add_glob (%s) -> %s\n", uqglob, regex)); + if ((s = validate_regexp(regex)) != NULL) { + printf("%s is not a valid shell wildcard pattern: ", glob); + puts(s); + } else { + /* + * glob_to_regex() anchors the beginning of the pattern with ^, + * but we will be tacking it onto the end of the current directory + * in add_file, so strip that off. Also, it anchors the end with + * $, but we need to match an optional trailing /, so tack that on + * the end. + */ + regex_path = stralloc(regex + 1); + regex_path[strlen(regex_path) - 1] = '\0'; + strappend(regex_path, "[/]*$"); + add_file(uqglob, regex_path); + amfree(regex_path); + } + amfree(regex); + amfree(uqglob); +} + +void +add_regex( + char * regex) +{ + char *s; + char *uqregex = unquote_string(regex); + + if ((s = validate_regexp(uqregex)) != NULL) { + printf("%s is not a valid regular expression: ", regex); + puts(s); + } else { + add_file(uqregex, regex); + } + amfree(uqregex); +} + +void add_file( + char * path, + char * regex) +{ + DIR_ITEM *ditem, lditem; + char *path_on_disk = NULL; + char *path_on_disk_slash = NULL; + char *cmd = NULL; + char *err = NULL; + int i; + ssize_t j; + char *dir, *dir_undo, dir_undo_ch = '\0'; + char *ditem_path = NULL; + char *l = NULL; + int added; + char *s, *fp, *quoted; + int ch; + int found_one; + int dir_entries; + + if (disk_path == NULL) { + printf("Must select directory before adding files\n"); + return; + } + memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */ + + dbprintf(("add_file: Looking for \"%s\"\n", regex)); + + if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */ + regex = "\\.[/]*$"; + } + else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */ + //regex = + regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$"; + } else { + /* remove "/" at end of path */ + j = (ssize_t)(strlen(regex) - 1); + while(j >= 0 && regex[j] == '/') + regex[j--] = '\0'; + } + + /* convert path (assumed in cwd) to one on disk */ + if (strcmp(disk_path, "/") == 0) { + if (*regex == '/') { + /* No mods needed if already starts with '/' */ + path_on_disk = stralloc(regex); + } else { + /* Prepend '/' */ + path_on_disk = stralloc2("/", regex); + } + } else { + char *clean_disk_path = clean_regex(disk_path); + path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL); + amfree(clean_disk_path); + } + + path_on_disk_slash = stralloc2(path_on_disk, "/"); + + dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n", + regex, path_on_disk)); + + found_one = 0; + dir_entries = 0; + for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem)) + { + dir_entries++; + quoted = quote_string(ditem->path); + dbprintf(("add_file: Pondering ditem->path=%s\n", quoted)); + amfree(quoted); + if (match(path_on_disk, ditem->path) + || match(path_on_disk_slash, ditem->path)) + { + found_one = 1; + j = (ssize_t)strlen(ditem->path); + if((j > 0 && ditem->path[j-1] == '/') + || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.')) + { /* It is a directory */ + ditem_path = newstralloc(ditem_path, ditem->path); + clean_pathname(ditem_path); + + cmd = stralloc2("ORLD ", ditem_path); + if(send_command(cmd) == -1) { + amfree(cmd); + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + exit(1); + } + amfree(cmd); + cmd = NULL; + /* skip preamble */ + if ((i = get_reply_line()) == -1) { + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + exit(1); + } + if(i==0) { /* assume something wrong */ + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + l = reply_line(); + printf("%s\n", l); + return; + } + dir_undo = NULL; + added=0; + lditem.path = newstralloc(lditem.path, ditem->path); + /* skip the last line -- duplicate of the preamble */ + + while ((i = get_reply_line()) != 0) { + if (i == -1) { + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + exit(1); + } + if(err) { + if(cmd == NULL) { + if(dir_undo) *dir_undo = dir_undo_ch; + dir_undo = NULL; + cmd = stralloc(l); /* save for error report */ + } + continue; /* throw the rest of the lines away */ + } + l=reply_line(); + if (!server_happy()) { + puts(l); + continue; + } +#define sc "201-" + if(strncmp(l, sc, sizeof(sc)-1) != 0) { + err = "bad reply: not 201-"; + continue; + } + + s = l + sizeof(sc)-1; + ch = *s++; +#undef sc + skip_whitespace(s, ch); + if(ch == '\0') { + err = "bad reply: missing date field"; + continue; + } + fp = s-1; + skip_non_whitespace(s, ch); + s[-1] = '\0'; + lditem.date = newstralloc(lditem.date, fp); + s[-1] = (char)ch; + + skip_whitespace(s, ch); + if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) { + err = "bad reply: cannot parse level field"; + continue; + } + skip_integer(s, ch); + + skip_whitespace(s, ch); + if(ch == '\0') { + err = "bad reply: missing tape field"; + continue; + } + fp = s-1; + skip_non_whitespace(s, ch); + s[-1] = '\0'; + lditem.tape = newstralloc(lditem.tape, fp); + s[-1] = (char)ch; + + if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) { + skip_whitespace(s, ch); + if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, + (OFF_T_FMT_TYPE *)&lditem.fileno) != 1) { + err = "bad reply: cannot parse fileno field"; + continue; + } + skip_integer(s, ch); + } + + skip_whitespace(s, ch); + if(ch == '\0') { + err = "bad reply: missing directory field"; + continue; + } + dir = s - 1; + skip_quoted_string(s, ch); + dir_undo = s - 1; + dir_undo_ch = *dir_undo; + *dir_undo = '\0'; + + switch(add_extract_item(&lditem)) { + case -1: + printf("System error\n"); + dbprintf(("add_file: (Failed) System error\n")); + break; + + case 0: + quoted = quote_string(lditem.path); + printf("Added dir %s at date %s\n", + quoted, lditem.date); + dbprintf(("add_file: (Successful) Added dir %s at date %s\n", + quoted, lditem.date)); + amfree(quoted); + added=1; + break; + + case 1: + break; + } + } + if(!server_happy()) { + puts(reply_line()); + } else if(err) { + if (*err) + puts(err); + if (cmd) + puts(cmd); + } else if(added == 0) { + quoted = quote_string(ditem_path); + printf("dir %s already added\n", quoted); + dbprintf(("add_file: dir %s already added\n", quoted)); + amfree(quoted); + } + } + else /* It is a file */ + { + switch(add_extract_item(ditem)) { + case -1: + printf("System error\n"); + dbprintf(("add_file: (Failed) System error\n")); + break; + + case 0: + quoted = quote_string(ditem->path); + printf("Added file %s\n", quoted); + dbprintf(("add_file: (Successful) Added %s\n", quoted)); + amfree(quoted); + break; + + case 1: + quoted = quote_string(ditem->path); + printf("File %s already added\n", quoted); + dbprintf(("add_file: file %s already added\n", quoted)); + amfree(quoted); + break; + } + } + } + } + if (cmd != NULL) + amfree(cmd); + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + + if(! found_one) { + quoted = quote_string(path); + printf("File %s doesn't exist in directory\n", quoted); + dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n", + quoted)); + amfree(quoted); + } +} + + +void +delete_glob( + char * glob) +{ + char *regex; + char *regex_path; + char *s; + char *uqglob = unquote_string(glob); + + regex = glob_to_regex(uqglob); + dbprintf(("delete_glob (%s) -> %s\n", uqglob, regex)); + if ((s = validate_regexp(regex)) != NULL) { + printf("\"%s\" is not a valid shell wildcard pattern: ", glob); + puts(s); + } else { + /* + * glob_to_regex() anchors the beginning of the pattern with ^, + * but we will be tacking it onto the end of the current directory + * in add_file, so strip that off. Also, it anchors the end with + * $, but we need to match an optional trailing /, so tack that on + * the end. + */ + regex_path = stralloc(regex + 1); + regex_path[strlen(regex_path) - 1] = '\0'; + strappend(regex_path, "[/]*$"); + delete_file(uqglob, regex_path); + amfree(regex_path); + } + amfree(regex); + amfree(uqglob); +} + +void +delete_regex( + char * regex) +{ + char *s; + char *uqregex = unquote_string(regex); + + if ((s = validate_regexp(regex)) != NULL) { + printf("\"%s\" is not a valid regular expression: ", regex); + puts(s); + } else { + delete_file(uqregex, uqregex); + } + amfree(uqregex); +} + +void +delete_file( + char * path, + char * regex) +{ + DIR_ITEM *ditem, lditem; + char *path_on_disk = NULL; + char *path_on_disk_slash = NULL; + char *cmd = NULL; + char *err = NULL; + int i; + ssize_t j; + char *date; + char *tape, *tape_undo, tape_undo_ch = '\0'; + char *dir_undo, dir_undo_ch = '\0'; + int level = 0; + off_t fileno; + char *ditem_path = NULL; + char *l = NULL; + int deleted; + char *s; + int ch; + int found_one; + char *quoted; + + if (disk_path == NULL) { + printf("Must select directory before deleting files\n"); + return; + } + memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */ + + dbprintf(("delete_file: Looking for \"%s\"\n", path)); + + if (strcmp(regex, "[^/]*[/]*$") == 0) { + /* Looking for * find everything but single . */ + regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$"; + } else { + /* remove "/" at end of path */ + j = (ssize_t)(strlen(regex) - 1); + while(j >= 0 && regex[j] == '/') regex[j--] = '\0'; + } + + /* convert path (assumed in cwd) to one on disk */ + if (strcmp(disk_path, "/") == 0) { + if (*regex == '/') { + if (strcmp(regex, "/[/]*$") == 0) { + /* We want "/" to match the directory itself: "/." */ + path_on_disk = stralloc("/\\.[/]*$"); + } else { + /* No mods needed if already starts with '/' */ + path_on_disk = stralloc(regex); + } + } else { + /* Prepend '/' */ + path_on_disk = stralloc2("/", regex); + } + } else { + char *clean_disk_path = clean_regex(disk_path); + path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL); + amfree(clean_disk_path); + } + + path_on_disk_slash = stralloc2(path_on_disk, "/"); + + dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n", + regex, path_on_disk)); + found_one = 0; + for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem)) + { + quoted = quote_string(ditem->path); + dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted)); + amfree(quoted); + if (match(path_on_disk, ditem->path) + || match(path_on_disk_slash, ditem->path)) + { + found_one = 1; + j = (ssize_t)strlen(ditem->path); + if((j > 0 && ditem->path[j-1] == '/') + || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.')) + { /* It is a directory */ + ditem_path = newstralloc(ditem_path, ditem->path); + clean_pathname(ditem_path); + + cmd = stralloc2("ORLD ", ditem_path); + if(send_command(cmd) == -1) { + amfree(cmd); + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + exit(1); + } + amfree(cmd); + /* skip preamble */ + if ((i = get_reply_line()) == -1) { + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + exit(1); + } + if(i==0) /* assume something wrong */ + { + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + l = reply_line(); + printf("%s\n", l); + return; + } + deleted=0; + lditem.path = newstralloc(lditem.path, ditem->path); + amfree(cmd); + tape_undo = dir_undo = NULL; + /* skip the last line -- duplicate of the preamble */ + while ((i = get_reply_line()) != 0) + { + if (i == -1) { + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + exit(1); + } + if(err) { + if(cmd == NULL) { + if(tape_undo) *tape_undo = tape_undo_ch; + if(dir_undo) *dir_undo = dir_undo_ch; + tape_undo = dir_undo = NULL; + cmd = stralloc(l); /* save for the error report */ + } + continue; /* throw the rest of the lines away */ + } + l=reply_line(); + if (!server_happy()) { + puts(l); + continue; + } +#define sc "201-" + if(strncmp(l, sc, sizeof(sc)-1) != 0) { + err = "bad reply: not 201-"; + continue; + } + s = l + sizeof(sc)-1; + ch = *s++; +#undef sc + skip_whitespace(s, ch); + if(ch == '\0') { + err = "bad reply: missing date field"; + continue; + } + date = s - 1; + skip_non_whitespace(s, ch); + *(s - 1) = '\0'; + + skip_whitespace(s, ch); + if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) { + err = "bad reply: cannot parse level field"; + continue; + } + skip_integer(s, ch); + + skip_whitespace(s, ch); + if(ch == '\0') { + err = "bad reply: missing tape field"; + continue; + } + tape = s - 1; + skip_non_whitespace(s, ch); + tape_undo = s - 1; + tape_undo_ch = *tape_undo; + *tape_undo = '\0'; + + if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) { + skip_whitespace(s, ch); + if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, + (OFF_T_FMT_TYPE *)&fileno) != 1) { + err = "bad reply: cannot parse fileno field"; + continue; + } + skip_integer(s, ch); + } + + skip_whitespace(s, ch); + if(ch == '\0') { + err = "bad reply: missing directory field"; + continue; + } + skip_non_whitespace(s, ch); + dir_undo = s - 1; + dir_undo_ch = *dir_undo; + *dir_undo = '\0'; + + lditem.date = newstralloc(lditem.date, date); + lditem.level=level; + lditem.tape = newstralloc(lditem.tape, tape); + switch(delete_extract_item(&lditem)) { + case -1: + printf("System error\n"); + dbprintf(("delete_file: (Failed) System error\n")); + break; + case 0: + printf("Deleted dir %s at date %s\n", ditem_path, date); + dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n", + ditem_path, date)); + deleted=1; + break; + case 1: + break; + } + } + if(!server_happy()) { + puts(reply_line()); + } else if(err) { + if (*err) + puts(err); + if (cmd) + puts(cmd); + } else if(deleted == 0) { + printf("Warning - dir '%s' not on tape list\n", + ditem_path); + dbprintf(("delete_file: dir '%s' not on tape list\n", + ditem_path)); + } + } + else + { + switch(delete_extract_item(ditem)) { + case -1: + printf("System error\n"); + dbprintf(("delete_file: (Failed) System error\n")); + break; + case 0: + printf("Deleted %s\n", ditem->path); + dbprintf(("delete_file: (Successful) Deleted %s\n", + ditem->path)); + break; + case 1: + printf("Warning - file '%s' not on tape list\n", + ditem->path); + dbprintf(("delete_file: file '%s' not on tape list\n", + ditem->path)); + break; + } + } + } + } + amfree(cmd); + amfree(ditem_path); + amfree(path_on_disk); + amfree(path_on_disk_slash); + + if(! found_one) { + printf("File %s doesn't exist in directory\n", path); + dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n", + path)); + } +} + + +/* print extract list into file. If NULL ptr passed print to screen */ +void +display_extract_list( + char * file) +{ + EXTRACT_LIST *this; + EXTRACT_LIST_ITEM *that; + FILE *fp; + char *pager; + char *pager_command; + char *uqfile; + + if (file == NULL) + { + if ((pager = getenv("PAGER")) == NULL) + { + pager = "more"; + } + /* + * Set up the pager command so if the pager is terminated, we do + * not get a SIGPIPE back. + */ + pager_command = stralloc2(pager, " ; /bin/cat > /dev/null"); + if ((fp = popen(pager_command, "w")) == NULL) + { + printf("Warning - can't pipe through %s\n", pager); + fp = stdout; + } + amfree(pager_command); + } + else + { + uqfile = unquote_string(file); + if ((fp = fopen(uqfile, "w")) == NULL) + { + printf("Can't open file %s to print extract list into\n", file); + amfree(uqfile); + return; + } + amfree(uqfile); + } + + for (this = extract_list; this != NULL; this = this->next) + { + fprintf(fp, "TAPE %s LEVEL %d DATE %s\n", + this->tape, this->level, this->date); + for (that = this->files; that != NULL; that = that->next) + fprintf(fp, "\t%s\n", that->path); + } + + if (file == NULL) { + apclose(fp); + } else { + printf("Extract list written to file %s\n", file); + afclose(fp); + } +} + + +/* returns 0 if extract list empty and 1 if it isn't */ +int +is_extract_list_nonempty(void) +{ + return (extract_list != NULL); +} + + +/* prints continue prompt and waits for response, + returns 0 if don't, non-0 if do */ +static int +okay_to_continue( + int allow_tape, + int allow_skip, + int allow_retry) +{ + int ch; + int ret = -1; + char *line = NULL; + char *s; + char *prompt; + int get_tape; + + get_tape = 0; + while (ret < 0) { + if (get_tape) { + prompt = "New tape device [?]: "; + } else if (allow_tape && allow_skip) { + prompt = "Continue [?/Y/n/s/t]? "; + } else if (allow_tape && !allow_skip) { + prompt = "Continue [?/Y/n/t]? "; + } else if (allow_retry) { + prompt = "Continue [?/Y/n/r]? "; + } else { + prompt = "Continue [?/Y/n]? "; + } + fputs(prompt, stdout); + fflush(stdout); fflush(stderr); + amfree(line); + if ((line = agets(stdin)) == NULL) { + putchar('\n'); + clearerr(stdin); + if (get_tape) { + get_tape = 0; + continue; + } + ret = 0; + break; + } + s = line; + while ((ch = *s++) != '\0' && isspace(ch)) { + (void)ch; /* Quiet empty loop body warning */ + } + if (ch == '?') { + if (get_tape) { + printf("Enter a new device ([host:]device) or \"default\"\n"); + } else { + printf("Enter \"y\"es to continue, \"n\"o to stop"); + if(allow_skip) { + printf(", \"s\"kip this tape"); + } + if(allow_retry) { + printf(" or \"r\"etry this tape"); + } + if (allow_tape) { + printf(" or \"t\"ape to change tape drives"); + } + putchar('\n'); + } + } else if (get_tape) { + set_tape(s - 1); + get_tape = 0; + } else if (ch == '\0' || ch == 'Y' || ch == 'y') { + ret = 1; + } else if (allow_tape && (ch == 'T' || ch == 't')) { + get_tape = 1; + } else if (ch == 'N' || ch == 'n') { + ret = 0; + } else if (allow_retry && (ch == 'R' || ch == 'r')) { + ret = RETRY_TAPE; + } else if (allow_skip && (ch == 'S' || ch == 's')) { + ret = SKIP_TAPE; + } + } + /*@ignore@*/ + amfree(line); + /*@end@*/ + return ret; +} + +static void +send_to_tape_server( + int tss, + char * cmd) +{ + char *msg = stralloc2(cmd, "\r\n"); + + if (fullwrite(tss, msg, strlen(msg)) < 0) + { + error("Error writing to tape server"); + /*NOTREACHED*/ + } + amfree(msg); +} + + +/* start up connection to tape server and set commands to initiate + transfer of dump image. + Return tape server socket on success, -1 on error. */ +static int +extract_files_setup( + char * label, + off_t fsf) +{ + struct servent *sp; + in_port_t my_port, my_data_port; + char *disk_regex = NULL; + char *host_regex = NULL; + char *service_name = NULL; + char *line = NULL; + char *clean_datestamp, *ch, *ch1; + char *our_feature_string = NULL; + char *tt = NULL; + + service_name = stralloc2("amidxtape", SERVICE_SUFFIX); + + /* get tape server details */ + if ((sp = getservbyname(service_name, "tcp")) == NULL) + { + printf("%s/tcp unknown protocol - config error?\n", service_name); + amfree(service_name); + return -1; + } + amfree(service_name); + seteuid(0); /* it either works ... */ + setegid(0); + tape_control_sock = stream_client_privileged(tape_server_name, + (in_port_t)ntohs((in_port_t)sp->s_port), + 0, + STREAM_BUFSIZE, + &my_port, + 0); + if (tape_control_sock < 0) + { + printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno)); + return -1; + } + if (my_port >= IPPORT_RESERVED) { + aclose(tape_control_sock); + printf("did not get a reserved port: %u\n", (unsigned)my_port); + return -1; + } + + setegid(getgid()); + seteuid(getuid()); /* put it back */ + + /* do the security thing */ + line = get_security(); + send_to_tape_server(tape_control_sock, line); + memset(line, '\0', strlen(line)); + amfree(line); + + disk_regex = alloc(strlen(disk_name) * 2 + 3); + + ch = disk_name; + ch1 = disk_regex; + + /* we want to force amrestore to only match disk_name exactly */ + *(ch1++) = '^'; + + /* We need to escape some characters first... NT compatibilty crap */ + for (; *ch != 0; ch++, ch1++) { + switch (*ch) { /* done this way in case there are more */ + case '$': + *(ch1++) = '\\'; + /* no break; we do want to fall through... */ + default: + *ch1 = *ch; + } + } + + /* we want to force amrestore to only match disk_name exactly */ + *(ch1++) = '$'; + + *ch1 = '\0'; + + host_regex = alloc(strlen(dump_hostname) * 2 + 3); + + ch = dump_hostname; + ch1 = host_regex; + + /* we want to force amrestore to only match dump_hostname exactly */ + *(ch1++) = '^'; + + /* We need to escape some characters first... NT compatibilty crap */ + for (; *ch != 0; ch++, ch1++) { + switch (*ch) { /* done this way in case there are more */ + case '$': + *(ch1++) = '\\'; + /* no break; we do want to fall through... */ + default: + *ch1 = *ch; + } + } + + /* we want to force amrestore to only match dump_hostname exactly */ + *(ch1++) = '$'; + + *ch1 = '\0'; + + clean_datestamp = stralloc(dump_datestamp); + for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) { + if(*ch1 != '-') { + *ch = *ch1; + ch++; + } + } + *ch = '\0'; + + /* push our feature list off to the tape server */ + /* XXX assumes that index server and tape server are equivalent, ew */ + if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){ + char buffer[32768] = "\0"; + + our_feature_string = am_feature_to_string(our_features); + tt = newstralloc2(tt, "FEATURES=", our_feature_string); + send_to_tape_server(tape_control_sock, tt); + if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) { + error("Could not read features from control socket\n"); + /*NOTREACHED*/ + } + tapesrv_features = am_string_to_feature(buffer); + amfree(our_feature_string); + } + + + if(am_has_feature(indexsrv_features, fe_amidxtaped_header) && + am_has_feature(indexsrv_features, fe_amidxtaped_device) && + am_has_feature(indexsrv_features, fe_amidxtaped_host) && + am_has_feature(indexsrv_features, fe_amidxtaped_disk) && + am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) { + + if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) { + tt = newstralloc2(tt, "CONFIG=", config); + send_to_tape_server(tape_control_sock, tt); + } + if(am_has_feature(indexsrv_features, fe_amidxtaped_label) && + label && label[0] != '/') { + tt = newstralloc2(tt,"LABEL=",label); + send_to_tape_server(tape_control_sock, tt); + } + if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) { + char v_fsf[100]; + snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)fsf); + tt = newstralloc2(tt, "FSF=",v_fsf); + send_to_tape_server(tape_control_sock, tt); + } + send_to_tape_server(tape_control_sock, "HEADER"); + tt = newstralloc2(tt, "DEVICE=", dump_device_name); + send_to_tape_server(tape_control_sock, tt); + tt = newstralloc2(tt, "HOST=", host_regex); + send_to_tape_server(tape_control_sock, tt); + tt = newstralloc2(tt, "DISK=", disk_regex); + send_to_tape_server(tape_control_sock, tt); + tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp); + send_to_tape_server(tape_control_sock, tt); + send_to_tape_server(tape_control_sock, "END"); + amfree(tt); + } + else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) { + /* send to the tape server what tape file we want */ + /* 6 args: + * "-h" + * "-p" + * "tape device" + * "hostname" + * "diskname" + * "datestamp" + */ + send_to_tape_server(tape_control_sock, "6"); + send_to_tape_server(tape_control_sock, "-h"); + send_to_tape_server(tape_control_sock, "-p"); + send_to_tape_server(tape_control_sock, dump_device_name); + send_to_tape_server(tape_control_sock, host_regex); + send_to_tape_server(tape_control_sock, disk_regex); + send_to_tape_server(tape_control_sock, clean_datestamp); + + dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n", + dump_device_name, host_regex, disk_regex, clean_datestamp)); + } + + /* + * split-restoring amidxtaped versions will expect to set up a data + * connection for dumpfile data, distinct from the socket we're already + * using for control data + */ + + if(am_has_feature(tapesrv_features, fe_recover_splits)){ + char buffer[32768]; + in_port_t data_port = (in_port_t)-1; + ssize_t nread; + + nread = read(tape_control_sock, buffer, sizeof(buffer)); + + if (nread <= 0) { + error("Could not read from control socket: %s\n", + strerror(errno)); + /*NOTREACHED*/ + } + + buffer[nread] = '\0'; + if (sscanf(buffer, "CONNECT %hu\n", + (unsigned short *)&data_port) != 1) { + error("Recieved invalid port number message from control socket: %s\n", + buffer); + /*NOTREACHED*/ + } + + tape_data_sock = stream_client_privileged(server_name, + data_port, + 0, + STREAM_BUFSIZE, + &my_data_port, + 0); + if(tape_data_sock == -1){ + error("Unable to make data connection to server: %s\n", + strerror(errno)); + /*NOTREACHED*/ + } + + amfree(our_feature_string); + + line = get_security(); + + send_to_tape_server(tape_data_sock, line); + memset(line, '\0', strlen(line)); + amfree(line); + } + + amfree(disk_regex); + amfree(host_regex); + amfree(clean_datestamp); + + return tape_control_sock; +} + + +/* + * Reads the first block of a tape file. + */ + +void +read_file_header( + char * buffer, + dumpfile_t *file, + size_t buflen, + int tapedev) +{ + ssize_t bytes_read; + + bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT); + if(bytes_read < 0) { + error("error reading header (%s), check amidxtaped.*.debug on server", + strerror(errno)); + /*NOTREACHED*/ + } + + if((size_t)bytes_read < buflen) { + fprintf(stderr, "%s: short block %d byte%s\n", + get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s"); + print_header(stdout, file); + error("Can't read file header"); + /*NOTREACHED*/ + } + + /* bytes_read == buflen */ + parse_file_header(buffer, file, (size_t)bytes_read); +} + +enum dumptypes { + IS_UNKNOWN, + IS_DUMP, + IS_GNUTAR, + IS_TAR, + IS_SAMBA, + IS_SAMBA_TAR +}; + +static void +extract_files_child( + int in_fd, + EXTRACT_LIST * elist) +{ + int save_errno; + int extra_params = 0; + int i,j=0; + char **restore_args = NULL; + int files_off_tape; + EXTRACT_LIST_ITEM *fn; + enum dumptypes dumptype = IS_UNKNOWN; + char buffer[DISK_BLOCK_BYTES]; + dumpfile_t file; + size_t len_program; + char *cmd = NULL; + int passwd_field = -1; +#ifdef SAMBA_CLIENT + char *domain = NULL, *smbpass = NULL; +#endif + + /* code executed by child to do extraction */ + /* never returns */ + + /* make in_fd be our stdin */ + if (dup2(in_fd, STDIN_FILENO) == -1) + { + error("dup2 failed in extract_files_child: %s", strerror(errno)); + /*NOTREACHED*/ + } + + /* read the file header */ + fh_init(&file); + read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO); + + if(file.type != F_DUMPFILE) { + print_header(stdout, &file); + error("bad header"); + /*NOTREACHED*/ + } + + if (file.program != NULL) { +#ifdef GNUTAR + if (strcmp(file.program, GNUTAR) == 0) + dumptype = IS_GNUTAR; +#endif + + if (dumptype == IS_UNKNOWN) { + len_program = strlen(file.program); + if(len_program >= 3 && + strcmp(&file.program[len_program-3],"tar") == 0) + dumptype = IS_TAR; + } + +#ifdef SAMBA_CLIENT + if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) { + if (samba_extract_method == SAMBA_TAR) + dumptype = IS_SAMBA_TAR; + else + dumptype = IS_SAMBA; + } +#endif + } + + /* form the arguments to restore */ + files_off_tape = length_of_tape_list(elist); + switch (dumptype) { + case IS_SAMBA: +#ifdef SAMBA_CLIENT + extra_params = 10; + break; +#endif + case IS_TAR: + case IS_GNUTAR: + extra_params = 4; + break; + case IS_SAMBA_TAR: + extra_params = 3; + break; + case IS_UNKNOWN: + case IS_DUMP: +#ifdef AIX_BACKUP + extra_params = 2; +#else +#if defined(XFSDUMP) + if (strcmp(file.program, XFSDUMP) == 0) { + extra_params = 4 + files_off_tape; + } else +#endif + { + extra_params = 4; + } +#endif + break; + } + + restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1) + * sizeof(char *))); + switch(dumptype) { + case IS_SAMBA: +#ifdef SAMBA_CLIENT + restore_args[j++] = stralloc("smbclient"); + smbpass = findpass(file.disk, &domain); + if (smbpass) { + restore_args[j++] = stralloc(file.disk); + passwd_field=j; + restore_args[j++] = stralloc("-U"); + restore_args[j++] = smbpass; + if (domain) { + restore_args[j++] = stralloc("-W"); + restore_args[j++] = stralloc(domain); + } else + extra_params -= 2; + } else + extra_params -= 6; + restore_args[j++] = stralloc("-d0"); + restore_args[j++] = stralloc("-Tx"); + restore_args[j++] = stralloc("-"); /* data on stdin */ + break; +#endif + case IS_TAR: + case IS_GNUTAR: + restore_args[j++] = stralloc("tar"); + restore_args[j++] = stralloc("--numeric-owner"); + restore_args[j++] = stralloc("-xpGvf"); + restore_args[j++] = stralloc("-"); /* data on stdin */ + break; + case IS_SAMBA_TAR: + restore_args[j++] = stralloc("tar"); + restore_args[j++] = stralloc("-xpvf"); + restore_args[j++] = stralloc("-"); /* data on stdin */ + break; + case IS_UNKNOWN: + case IS_DUMP: + restore_args[j++] = stralloc("restore"); +#ifdef AIX_BACKUP + restore_args[j++] = stralloc("-xB"); +#else +#if defined(XFSDUMP) + if (strcmp(file.program, XFSDUMP) == 0) { + restore_args[j++] = stralloc("-v"); + restore_args[j++] = stralloc("silent"); + } else +#endif +#if defined(VDUMP) + if (strcmp(file.program, VDUMP) == 0) { + restore_args[j++] = stralloc("xf"); + restore_args[j++] = stralloc("-"); /* data on stdin */ + } else +#endif + { + restore_args[j++] = stralloc("xbf"); + restore_args[j++] = stralloc("2"); /* read in units of 1K */ + restore_args[j++] = stralloc("-"); /* data on stdin */ + } +#endif + } + + for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next) + { + switch (dumptype) { + case IS_TAR: + case IS_GNUTAR: + case IS_SAMBA_TAR: + case IS_SAMBA: + if (strcmp(fn->path, "/") == 0) + restore_args[j++] = stralloc("."); + else + restore_args[j++] = stralloc2(".", fn->path); + break; + case IS_UNKNOWN: + case IS_DUMP: +#if defined(XFSDUMP) + if (strcmp(file.program, XFSDUMP) == 0) { + /* + * xfsrestore needs a -s option before each file to be + * restored, and also wants them to be relative paths. + */ + restore_args[j++] = stralloc("-s"); + restore_args[j++] = stralloc(fn->path + 1); + } else +#endif + { + restore_args[j++] = stralloc(fn->path); + } + } + } +#if defined(XFSDUMP) + if (strcmp(file.program, XFSDUMP) == 0) { + restore_args[j++] = stralloc("-"); + restore_args[j++] = stralloc("."); + } +#endif + restore_args[j] = NULL; + + switch (dumptype) { + case IS_SAMBA: +#ifdef SAMBA_CLIENT + cmd = stralloc(SAMBA_CLIENT); + break; +#else + /* fall through to ... */ +#endif + case IS_TAR: + case IS_GNUTAR: + case IS_SAMBA_TAR: +#ifndef GNUTAR + fprintf(stderr, "warning: GNUTAR program not available.\n"); + cmd = stralloc("tar"); +#else + cmd = stralloc(GNUTAR); +#endif + break; + case IS_UNKNOWN: + case IS_DUMP: + cmd = NULL; +#if defined(DUMP) + if (strcmp(file.program, DUMP) == 0) { + cmd = stralloc(RESTORE); + } +#endif +#if defined(VDUMP) + if (strcmp(file.program, VDUMP) == 0) { + cmd = stralloc(VRESTORE); + } +#endif +#if defined(VXDUMP) + if (strcmp(file.program, VXDUMP) == 0) { + cmd = stralloc(VXRESTORE); + } +#endif +#if defined(XFSDUMP) + if (strcmp(file.program, XFSDUMP) == 0) { + cmd = stralloc(XFSRESTORE); + } +#endif + if (cmd == NULL) { + fprintf(stderr, "warning: restore program for %s not available.\n", + file.program); + cmd = stralloc("restore"); + } + } + if (cmd) { + dbprintf(("Exec'ing %s with arguments:\n", cmd)); + for (i = 0; i < j; i++) { + if( i == passwd_field) + dbprintf(("\tXXXXX\n")); + else + dbprintf(("\t%s\n", restore_args[i])); + } + (void)execv(cmd, restore_args); + /* only get here if exec failed */ + save_errno = errno; + for (i = 0; i < j; i++) { + amfree(restore_args[i]); + } + amfree(restore_args); + errno = save_errno; + perror("amrecover couldn't exec"); + fprintf(stderr, " problem executing %s\n", cmd); + amfree(cmd); + } + exit(1); + /*NOT REACHED */ +} + +/* + * Interpose something between the process writing out the dump (writing it to + * some extraction program, really) and the socket from which we're reading, so + * that we can do things like prompt for human interaction for multiple tapes. + */ +void +writer_intermediary( + int ctl_fd, + int data_fd, + EXTRACT_LIST * elist) +{ + int child_pipe[2]; + pid_t pid; + char buffer[DISK_BLOCK_BYTES]; + ssize_t bytes_read; + amwait_t extractor_status; + int max_fd, nfound; + SELECT_ARG_TYPE readset, selectset; + struct timeval timeout; + + /* + * If there's no distinct data channel (such as if we're talking to an + * older server), don't bother doing anything complicated. Just run the + * extraction. + */ + if(data_fd == -1){ + extract_files_child(ctl_fd, elist); + /*NOTREACHED*/ + } + + if(pipe(child_pipe) == -1) { + error("extract_list - error setting up pipe to extractor: %s\n", + strerror(errno)); + /*NOTREACHED*/ + } + + /* okay, ready to extract. fork a child to do the actual work */ + if ((pid = fork()) == 0) { + /* this is the child process */ + /* never gets out of this clause */ + aclose(child_pipe[1]); + extract_files_child(child_pipe[0], elist); + /*NOTREACHED*/ + } + + /* This is the parent */ + if (pid == -1) { + error("writer_intermediary - error forking child"); + /*NOTREACHED*/ + } + + aclose(child_pipe[0]); + + if(data_fd > ctl_fd) max_fd = data_fd+1; + else max_fd = ctl_fd+1; + FD_ZERO(&readset); + FD_SET(data_fd, &readset); + FD_SET(ctl_fd, &readset); + + do { + timeout.tv_sec = READ_TIMEOUT; + timeout.tv_usec = 0; + FD_COPY(&readset, &selectset); + + nfound = select(max_fd, &selectset, NULL, NULL, + &timeout); + if(nfound < 0) { + fprintf(stderr,"select error: %s\n", strerror(errno)); + break; + } + + if (nfound == 0) { /* timeout */ + fprintf(stderr, "timeout waiting %d seconds for restore\n", + READ_TIMEOUT); + fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"); + break; + } + + if(FD_ISSET(ctl_fd, &selectset)) { + bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1); + switch(bytes_read) { + case -1: + if ((errno != EINTR) && (errno != EAGAIN)) { + if (errno != EPIPE) { + fprintf(stderr,"writer ctl fd read error: %s", + strerror(errno)); + } + FD_CLR(ctl_fd, &readset); + } + break; + + case 0: + FD_CLR(ctl_fd, &readset); + break; + + default: { + char desired_tape[MAX_TAPE_LABEL_BUF]; + + buffer[bytes_read] = '\0'; + /* if prompted for a tape, relay said prompt to the user */ + if(sscanf(buffer, "FEEDME %132s\n", desired_tape) == 1) { + int done = 0; + while (!done) { + char *input = NULL; + printf("Please insert tape %s. Continue? [Y|n]: ", + desired_tape); + fflush(stdout); + + input = agets(stdin); /* strips \n */ + if (strcasecmp("", input) == 0|| + strcasecmp("y", input) == 0|| + strcasecmp("yes", input) == 0) { + send_to_tape_server(tape_control_sock, "OK"); + done = 1; + } else if (strcasecmp("n", input) == 0|| + strcasecmp("no", input) == 0) { + send_to_tape_server(tape_control_sock, "ERROR"); + /* Abort! + We are the middle process, so just die. */ + exit(EXIT_FAILURE); + } + amfree(input); + } + } else { + fprintf(stderr, "Strange message from tape server: %s", buffer); + break; + } + } + } + } + + /* now read some dump data */ + if(FD_ISSET(data_fd, &selectset)) { + bytes_read = read(data_fd, buffer, sizeof(buffer)-1); + switch(bytes_read) { + case -1: + if ((errno != EINTR) && (errno != EAGAIN)) { + if (errno != EPIPE) { + fprintf(stderr,"writer data fd read error: %s", + strerror(errno)); + } + FD_CLR(data_fd, &readset); + } + break; + + case 0: + FD_CLR(data_fd, &readset); + break; + + default: + /* + * spit what we got from the server to the child + * process handling actual dumpfile extraction + */ + if(fullwrite(child_pipe[1], buffer, (size_t)bytes_read) < 0) { + if(errno == EPIPE) { + error("%s: pipe data reader has quit: %s\n", + get_pname(), strerror(errno)); + /* NOTREACHED */ + } + error("Write error to extract child: %s\n", + strerror(errno)); + /* NOTREACHED */ + } + break; + } + } + } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset)); + + aclose(child_pipe[1]); + + waitpid(pid, &extractor_status, 0); + if(WEXITSTATUS(extractor_status) != 0){ + int ret = WEXITSTATUS(extractor_status); + if(ret == 255) ret = -1; + error("Extractor child exited with status %d\n", ret); + /*NOTREACHED*/ + } + + exit(0); +} + +/* exec restore to do the actual restoration */ + +/* does the actual extraction of files */ +/* + * The original design had the dump image being returned exactly as it + * appears on the tape, and this routine getting from the index server + * whether or not it is compressed, on the assumption that the tape + * server may not know how to uncompress it. But + * - Amrestore can't do that. It returns either compressed or uncompressed + * (always). Amrestore assumes it can uncompress files. It is thus a good + * idea to run the tape server on a machine with gzip. + * - The information about compression in the disklist is really only + * for future dumps. It is possible to change compression on a drive + * so the information in the disklist may not necessarily relate to + * the dump image on the tape. + * Consequently the design was changed to assuming that amrestore can + * uncompress any dump image and have it return an uncompressed file + * always. + */ +void +extract_files(void) +{ + EXTRACT_LIST *elist; + pid_t pid; + amwait_t child_stat; + char buf[STR_SIZE]; + char *l; + int first; + int otc; + tapelist_t *tlist = NULL; + + if (!is_extract_list_nonempty()) + { + printf("Extract list empty - No files to extract!\n"); + return; + } + + clean_extract_list(); + + /* get tape device name from index server if none specified */ + if (tape_server_name == NULL) { + tape_server_name = newstralloc(tape_server_name, server_name); + } + if (tape_device_name == NULL) { + if (send_command("TAPE") == -1) + exit(1); + if (get_reply_line() == -1) + exit(1); + l = reply_line(); + if (!server_happy()) + { + printf("%s\n", l); + exit(1); + } + /* skip reply number */ + tape_device_name = newstralloc(tape_device_name, l+4); + } + + if (strcmp(tape_device_name, "/dev/null") == 0) + { + printf("amrecover: warning: using %s as the tape device will not work\n", + tape_device_name); + } + + first=1; + for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) + if(elist->tape[0]!='/') { + if(first) { + printf("\nExtracting files using tape drive %s on host %s.\n", + tape_device_name, tape_server_name); + printf("The following tapes are needed:"); + first=0; + } + else + printf(" "); + tlist = unmarshal_tapelist_str(elist->tape); + for( ; tlist != NULL; tlist = tlist->next) + printf(" %s", tlist->label); + printf("\n"); + amfree(tlist); + } + first=1; + for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) + { + if(elist->tape[0]=='/') { + if(first) { + printf("\nExtracting files from holding disk on host %s.\n", + tape_server_name); + printf("The following files are needed:"); + first=0; + } + else + printf(" "); + tlist = unmarshal_tapelist_str(elist->tape); + for( ; tlist != NULL; tlist = tlist->next) + printf(" %s", tlist->label); + printf("\n"); + amfree(tlist); + } + } + printf("\n"); + + if (getcwd(buf, sizeof(buf)) == NULL) { + perror("extract_list: Current working directory unavailable"); + exit(1); + } + + printf("Restoring files into directory %s\n", buf); +#ifdef SAMBA_CLIENT + if (samba_extract_method == SAMBA_SMBCLIENT) + printf("(unless it is a Samba backup, that will go through to the SMB server)\n"); +#endif + if (!okay_to_continue(0,0,0)) + return; + printf("\n"); + + while ((elist = first_tape_list()) != NULL) + { + if(elist->tape[0]=='/') { + dump_device_name = newstralloc(dump_device_name, elist->tape); + printf("Extracting from file "); + tlist = unmarshal_tapelist_str(dump_device_name); + for( ; tlist != NULL; tlist = tlist->next) + printf(" %s", tlist->label); + printf("\n"); + amfree(tlist); + } + else { + printf("Extracting files using tape drive %s on host %s.\n", + tape_device_name, tape_server_name); + tlist = unmarshal_tapelist_str(elist->tape); + printf("Load tape %s now\n", tlist->label); + amfree(tlist); + otc = okay_to_continue(1,1,0); + if (otc == 0) + return; + else if (otc == SKIP_TAPE) { + delete_tape_list(elist); /* skip this tape */ + continue; + } + dump_device_name = newstralloc(dump_device_name, tape_device_name); + } + dump_datestamp = newstralloc(dump_datestamp, elist->date); + + /* connect to the tape handler daemon on the tape drive server */ + if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1) + { + fprintf(stderr, "amrecover - can't talk to tape server\n"); + return; + } + + /* okay, ready to extract. fork a child to do the actual work */ + if ((pid = fork()) == 0) + { + /* this is the child process */ + /* never gets out of this clause */ + writer_intermediary(tape_control_sock, tape_data_sock, elist); + /*NOT REACHED*/ + } + /* this is the parent */ + if (pid == -1) + { + perror("extract_list - error forking child"); + aclose(tape_control_sock); + exit(1); + } + + /* store the child pid globally so that it can be killed on intr */ + extract_restore_child_pid = pid; + + /* wait for the child process to finish */ + if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1) + { + perror("extract_list - error waiting for child"); + exit(1); + } + + if(tape_data_sock != -1) { + aclose(tape_data_sock); + } + + if (pid == extract_restore_child_pid) + { + extract_restore_child_pid = -1; + } + else + { + fprintf(stderr, "extract list - unknown child terminated?\n"); + exit(1); + } + if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0)) + { + fprintf(stderr, + "extract_list - child returned non-zero status: %d\n", + WEXITSTATUS(child_stat)); + otc = okay_to_continue(0,0,1); + if(otc == 0) + return; + + if(otc == 1) { + delete_tape_list(elist); /* tape failed so delete from list */ + } + } + else { + delete_tape_list(elist); /* tape done so delete from list */ + } + } +}