2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 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.
29 * implements the "extract" command in amrecover
34 #include "amrecover.h"
35 #include "fileheader.h"
46 #include "client_util.h"
48 #include "pipespawn.h"
50 typedef struct EXTRACT_LIST_ITEM {
53 struct EXTRACT_LIST_ITEM *next;
57 typedef struct EXTRACT_LIST {
58 char *date; /* date tape created */
59 int level; /* level of dump */
60 char *tape; /* tape label */
61 off_t fileno; /* fileno on tape */
62 EXTRACT_LIST_ITEM *files; /* files to get off tape */
64 struct EXTRACT_LIST *next;
68 typedef struct ctl_data_s {
74 data_path_t data_path;
76 backup_support_option_t *bsu;
85 security_stream_t *fd;
86 } amidxtaped_streams[] = {
92 #define NSTREAMS (int)(sizeof(amidxtaped_streams) / sizeof(amidxtaped_streams[0]))
95 static void amidxtaped_response(void *, pkt_t *, security_handle_t *);
96 static void stop_amidxtaped(void);
97 static char *dump_device_name = NULL;
99 static char *amidxtaped_line = NULL;
100 extern char *localhost;
101 static char header_buf[32768];
102 static int header_size = 0;
105 /* global pid storage for interrupt handler */
106 pid_t extract_restore_child_pid = -1;
108 static EXTRACT_LIST *extract_list = NULL;
109 static const security_driver_t *amidxtaped_secdrv;
111 unsigned short samba_extract_method = SAMBA_TAR;
113 #define READ_TIMEOUT 240*60
115 EXTRACT_LIST *first_tape_list(void);
116 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
117 static int is_empty_dir(char *fname);
118 int is_extract_list_nonempty(void);
119 int length_of_tape_list(EXTRACT_LIST *tape_list);
120 void add_file(char *path, char *regex);
121 void add_glob(char *glob);
122 void add_regex(char *regex);
123 void clear_extract_list(void);
124 void clean_tape_list(EXTRACT_LIST *tape_list);
125 void clean_extract_list(void);
126 void check_file_overwrite(char *filename);
127 void delete_file(char *path, char *regex);
128 void delete_glob(char *glob);
129 void delete_regex(char *regex);
130 void delete_tape_list(EXTRACT_LIST *tape_list);
131 void display_extract_list(char *file);
132 void extract_files(void);
133 void read_file_header(char *buffer,
137 static int add_extract_item(DIR_ITEM *ditem);
138 static int delete_extract_item(DIR_ITEM *ditem);
139 static int extract_files_setup(char *label, off_t fsf);
140 static int okay_to_continue(int allow_tape,
143 static ssize_t read_buffer(int datafd,
147 static void clear_tape_list(EXTRACT_LIST *tape_list);
148 static void extract_files_child(ctl_data_t *ctl_data);
149 static void send_to_tape_server(security_stream_t *stream, char *cmd);
150 int writer_intermediary(EXTRACT_LIST *elist);
151 int get_amidxtaped_line(void);
152 static void read_amidxtaped_data(void *, void *, ssize_t);
153 static char *merge_path(char *path1, char *path2);
154 static gboolean ask_file_overwrite(ctl_data_t *ctl_data);
155 static void start_processing_data(ctl_data_t *ctl_data);
158 * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
161 * read data from input file desciptor waiting up to timeout_s
162 * seconds before returning data.
165 * datafd - File descriptor to read from.
166 * buffer - Buffer to read into.
167 * buflen - Maximum number of bytes to read into buffer.
168 * timeout_s - Seconds to wait before returning what was already read.
171 * >0 - Number of data bytes in buffer.
173 * -1 - errno == ETIMEDOUT if no data available in specified time.
174 * errno == ENFILE if datafd is invalid.
175 * otherwise errno is set by select or read..
186 SELECT_ARG_TYPE readset;
187 struct timeval timeout;
192 if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
193 errno = EMFILE; /* out of range */
198 spaceleft = (ssize_t)buflen;
202 FD_SET(datafd, &readset);
203 timeout.tv_sec = timeout_s;
205 nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
207 /* Select returned an error. */
208 g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
214 /* Select timed out. */
215 if (timeout_s != 0) {
216 /* Not polling: a real read timeout */
217 g_fprintf(stderr,_("timeout waiting for restore\n"));
218 g_fprintf(stderr,_("increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"));
225 if(!FD_ISSET(datafd, &readset))
228 /* Select says data is available, so read it. */
229 size = read(datafd, dataptr, (size_t)spaceleft);
231 if ((errno == EINTR) || (errno == EAGAIN)) {
234 if (errno != EPIPE) {
235 g_fprintf(stderr, _("read_buffer: read error - %s"),
243 } while ((size > 0) && (spaceleft > 0));
245 return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
250 first_tape_list(void)
257 /*@keep@*/EXTRACT_LIST *list)
266 EXTRACT_LIST * tape_list)
268 EXTRACT_LIST_ITEM *this, *next;
271 this = tape_list->files;
280 tape_list->files = NULL;
284 /* remove a tape list from the extract list, clearing the tape list
285 beforehand if necessary */
288 EXTRACT_LIST *tape_list)
290 EXTRACT_LIST *this, *prev;
292 if (tape_list == NULL)
295 /* is it first on the list? */
296 if (tape_list == extract_list)
298 extract_list = tape_list->next;
299 clear_tape_list(tape_list);
300 amfree(tape_list->date);
301 amfree(tape_list->tape);
306 /* so not first on list - find it and delete */
308 this = extract_list->next;
311 if (this == tape_list)
313 prev->next = tape_list->next;
314 clear_tape_list(tape_list);
315 amfree(tape_list->date);
316 amfree(tape_list->tape);
327 /* return the number of files on a tape's list */
330 EXTRACT_LIST *tape_list)
332 EXTRACT_LIST_ITEM *fn;
336 for (fn = tape_list->files; fn != NULL; fn = fn->next)
344 clear_extract_list(void)
346 while (extract_list != NULL)
347 delete_tape_list(extract_list);
353 EXTRACT_LIST *tape_list)
355 EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
356 EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
361 fn1 = tape_list->files;
362 while (fn1 != NULL) {
367 while (fn2 != NULL && remove_fn1 == 0) {
369 if(strcmp(fn1->path, fn2->path) == 0) {
371 } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
372 ((strlen(fn2->path) > strlen(fn1->path) &&
373 fn2->path[strlen(fn1->path)] == '/') ||
374 (fn1->path[strlen(fn1->path)-1] == '/'))) {
376 } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
377 ((strlen(fn1->path) > strlen(fn2->path) &&
378 fn1->path[strlen(fn2->path)] == '/') ||
379 (fn2->path[strlen(fn2->path)-1] == '/'))) {
385 dbprintf(_("removing path %s, it is included in %s\n"),
386 fn2->path, fn1->path);
393 } else if (remove_fn1 == 0) {
399 if(remove_fn1 != 0) {
400 /* fn2->path is always valid */
401 /*@i@*/ dbprintf(_("removing path %s, it is included in %s\n"),
402 /*@i@*/ fn1->tpath, fn2->tpath);
408 amfree(tape_list->files);
409 tape_list->files = fn1;
427 char *npath = g_path_get_basename(path);
428 *dir = g_path_get_dirname(path);
429 if (strcmp(*dir, ".") == 0) {
436 clean_extract_list(void)
440 for (this = extract_list; this != NULL; this = this->next)
441 clean_tape_list(this);
445 int add_to_unlink_list(char *path);
446 int do_unlink_list(void);
447 void free_unlink_list(void);
449 typedef struct s_unlink_list {
451 struct s_unlink_list *next;
453 t_unlink_list *unlink_list = NULL;
462 unlink_list = alloc(SIZEOF(*unlink_list));
463 unlink_list->path = stralloc(path);
464 unlink_list->next = NULL;
466 for (ul = unlink_list; ul != NULL; ul = ul->next) {
467 if (strcmp(ul->path, path) == 0)
470 ul = alloc(SIZEOF(*ul));
471 ul->path = stralloc(path);
472 ul->next = unlink_list;
484 for (ul = unlink_list; ul != NULL; ul = ul->next) {
485 if (unlink(ul->path) < 0) {
486 g_fprintf(stderr,_("Can't unlink %s: %s\n"), ul->path, strerror(errno));
495 free_unlink_list(void)
497 t_unlink_list *ul, *ul1;
499 for (ul = unlink_list; ul != NULL; ul = ul1) {
511 check_file_overwrite(
515 EXTRACT_LIST_ITEM *fn;
516 struct stat stat_buf;
520 for (this = extract_list; this != NULL; this = this->next) {
521 for (fn = this->files; fn != NULL ; fn = fn->next) {
523 /* Check path component of fn->path */
525 path = stralloc2(dir, fn->path);
526 if (path[strlen(path)-1] == '/') {
527 path[strlen(path)-1] = '\0';
530 s = path + strlen(dir) + 1;
531 while((s = strchr(s, '/'))) {
533 if (lstat(path, &stat_buf) == 0) {
534 if(!S_ISDIR(stat_buf.st_mode)) {
535 if (add_to_unlink_list(path)) {
536 g_printf(_("WARNING: %s is not a directory, "
537 "it will be deleted.\n"),
542 else if (errno != ENOENT) {
543 g_printf(_("Can't stat %s: %s\n"), path, strerror(errno));
552 filename = stralloc2(dir, fn->path);
553 if (filename[strlen(filename)-1] == '/') {
554 filename[strlen(filename)-1] = '\0';
557 if (lstat(filename, &stat_buf) == 0) {
558 if(S_ISDIR(stat_buf.st_mode)) {
559 if(!is_empty_dir(filename)) {
560 g_printf(_("WARNING: All existing files in %s "
561 "will be deleted.\n"), filename);
563 } else if(S_ISREG(stat_buf.st_mode)) {
564 g_printf(_("WARNING: Existing file %s will be overwritten\n"),
567 if (add_to_unlink_list(filename)) {
568 g_printf(_("WARNING: Existing entry %s will be deleted\n"),
572 } else if (errno != ENOENT) {
573 g_printf(_("Can't stat %s: %s\n"), filename, strerror(errno));
581 /* returns -1 if error */
582 /* returns 0 on succes */
583 /* returns 1 if already added */
588 EXTRACT_LIST *this, *this1;
589 EXTRACT_LIST_ITEM *that, *curr;
592 ditem_path = stralloc(ditem->path);
593 clean_pathname(ditem_path);
595 for (this = extract_list; this != NULL; this = this->next)
597 /* see if this is the list for the tape */
598 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
600 /* yes, so add to list */
604 if (strcmp(curr->path,ditem_path) == 0) {
610 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
611 that->path = ditem_path;
612 that->tpath = clean_pathname(g_strdup(ditem->tpath));
613 that->next = this->files;
614 this->files = that; /* add at front since easiest */
619 /* so this is the first time we have seen this tape */
620 this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
621 this->tape = stralloc(ditem->tape);
622 this->level = ditem->level;
623 this->fileno = ditem->fileno;
624 this->date = stralloc(ditem->date);
625 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
626 that->path = ditem_path;
627 that->tpath = clean_pathname(g_strdup(ditem->tpath));
631 /* add this in date increasing order */
632 /* because restore must be done in this order */
633 /* add at begining */
634 if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
636 this->next = extract_list;
640 for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
642 /* add in the middle */
643 if(strcmp(this->date,this1->next->date) < 0)
645 this->next = this1->next;
657 /* returns -1 if error */
658 /* returns 0 on deletion */
659 /* returns 1 if not there */
665 EXTRACT_LIST_ITEM *that, *prev;
666 char *ditem_path = NULL;
668 ditem_path = stralloc(ditem->path);
669 clean_pathname(ditem_path);
671 for (this = extract_list; this != NULL; this = this->next)
673 /* see if this is the list for the tape */
674 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
676 /* yes, so find file on list */
678 if (strcmp(that->path, ditem_path) == 0)
681 this->files = that->next;
685 /* if list empty delete it */
686 if (this->files == NULL)
687 delete_tape_list(this);
695 if (strcmp(that->path, ditem_path) == 0)
697 prev->next = that->next;
722 int len = strlen(path1);
723 if (path1[len-1] == '/' && path2[0] == '/') {
724 result = stralloc2(path1, path2+1);
725 } else if (path1[len-1] != '/' && path2[0] != '/') {
726 result = vstralloc(path1, "/", path2, NULL);
728 result = stralloc2(path1, path2);
745 if (disk_path == NULL) {
746 g_printf(_("Must select directory before adding files\n"));
750 uqglob = unquote_string(glob);
751 glob = file_of_path(uqglob, &dir);
753 sdir = merge_path(mount_point, disk_path);
754 result = cd_glob(dir, 0);
758 regex = glob_to_regex(glob);
759 dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
760 if ((s = validate_regexp(regex)) != NULL) {
761 g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
765 * glob_to_regex() anchors the beginning of the pattern with ^,
766 * but we will be tacking it onto the end of the current directory
767 * in add_file, so strip that off. Also, it anchors the end with
768 * $, but we need to match an optional trailing /, so tack that on
771 regex_path = stralloc(regex + 1);
772 regex_path[strlen(regex_path) - 1] = '\0';
773 strappend(regex_path, "[/]*$");
774 add_file(uqglob, regex_path);
778 set_directory(sdir, 0);
798 if (disk_path == NULL) {
799 g_printf(_("Must select directory before adding files\n"));
803 uqregex = unquote_string(regex);
804 newregex = file_of_path(uqregex, &dir);
806 sdir = merge_path(mount_point, disk_path);
807 result = cd_regex(dir, 0);
812 if ((s = validate_regexp(newregex)) != NULL) {
813 g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
816 add_file(uqregex, newregex);
819 set_directory(sdir, 0);
832 DIR_ITEM *ditem, lditem;
833 char *tpath_on_disk = NULL;
838 char *dir_undo, dir_undo_ch = '\0';
839 char *ditem_path = NULL;
840 char *qditem_path = NULL;
843 char *s, *fp, *quoted;
848 if (disk_path == NULL) {
849 g_printf(_("Must select directory before adding files\n"));
852 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
854 dbprintf(_("add_file: Looking for \"%s\"\n"), regex);
856 if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */
859 else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */
860 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
862 /* remove "/" at end of path */
863 j = (ssize_t)(strlen(regex) - 1);
864 while(j >= 0 && regex[j] == '/')
868 /* convert path (assumed in cwd) to one on disk */
869 if (strcmp(disk_path, "/") == 0) {
871 /* No mods needed if already starts with '/' */
872 tpath_on_disk = g_strdup(regex);
875 tpath_on_disk = g_strconcat("/", regex, NULL);
878 char *clean_disk_tpath = clean_regex(disk_tpath, 0);
879 tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
880 amfree(clean_disk_tpath);
883 dbprintf(_("add_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
884 regex, tpath_on_disk);
888 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
891 quoted = quote_string(ditem->tpath);
892 dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted);
894 if (match(tpath_on_disk, ditem->tpath))
897 j = (ssize_t)strlen(ditem->tpath);
898 if((j > 0 && ditem->tpath[j-1] == '/')
899 || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
900 { /* It is a directory */
901 ditem_path = newstralloc(ditem_path, ditem->path);
902 clean_pathname(ditem_path);
904 qditem_path = quote_string(ditem_path);
905 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
907 if(send_command(cmd) == -1) {
910 amfree(tpath_on_disk);
916 if ((i = get_reply_line()) == -1) {
918 amfree(tpath_on_disk);
921 if(i==0) { /* assume something wrong */
923 amfree(tpath_on_disk);
931 g_free(lditem.tpath);
932 lditem.path = g_strdup(ditem->path);
933 lditem.tpath = g_strdup(ditem->tpath);
934 /* skip the last line -- duplicate of the preamble */
936 while ((i = get_reply_line()) != 0) {
939 amfree(tpath_on_disk);
944 if(dir_undo) *dir_undo = dir_undo_ch;
946 cmd = stralloc(l); /* save for error report */
948 continue; /* throw the rest of the lines away */
951 if (!server_happy()) {
957 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
958 err = _("bad reply: not 201-");
963 skip_whitespace(s, ch);
965 err = _("bad reply: missing date field");
969 skip_non_whitespace(s, ch);
971 lditem.date = newstralloc(lditem.date, fp);
974 skip_whitespace(s, ch);
975 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
976 err = _("bad reply: cannot parse level field");
981 skip_whitespace(s, ch);
983 err = _("bad reply: missing tape field");
987 skip_quoted_string(s, ch);
990 lditem.tape = unquote_string(fp);
993 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
994 long long fileno_ = (long long)0;
995 skip_whitespace(s, ch);
997 sscanf(s - 1, "%lld", &fileno_) != 1) {
998 err = _("bad reply: cannot parse fileno field");
1001 lditem.fileno = (off_t)fileno_;
1002 skip_integer(s, ch);
1005 skip_whitespace(s, ch);
1007 err = _("bad reply: missing directory field");
1010 skip_quoted_string(s, ch);
1012 dir_undo_ch = *dir_undo;
1015 switch(add_extract_item(&lditem)) {
1017 g_printf(_("System error\n"));
1018 dbprintf(_("add_file: (Failed) System error\n"));
1022 quoted = quote_string(lditem.tpath);
1023 g_printf(_("Added dir %s at date %s\n"),
1024 quoted, lditem.date);
1025 dbprintf(_("add_file: (Successful) Added dir %s at date %s\n"),
1026 quoted, lditem.date);
1035 if(!server_happy()) {
1042 } else if(added == 0) {
1043 quoted = quote_string(ditem_path);
1044 g_printf(_("dir %s already added\n"), quoted);
1045 dbprintf(_("add_file: dir %s already added\n"), quoted);
1049 else /* It is a file */
1051 switch(add_extract_item(ditem)) {
1053 g_printf(_("System error\n"));
1054 dbprintf(_("add_file: (Failed) System error\n"));
1058 quoted = quote_string(ditem->tpath);
1059 g_printf(_("Added file %s\n"), quoted);
1060 dbprintf(_("add_file: (Successful) Added %s\n"), quoted);
1065 quoted = quote_string(ditem->tpath);
1066 g_printf(_("File %s already added\n"), quoted);
1067 dbprintf(_("add_file: file %s already added\n"), quoted);
1076 amfree(tpath_on_disk);
1078 amfree(lditem.path);
1079 amfree(lditem.tpath);
1080 amfree(lditem.date);
1081 amfree(lditem.tape);
1084 quoted = quote_string(path);
1085 g_printf(_("File %s doesn't exist in directory\n"), quoted);
1086 dbprintf(_("add_file: (Failed) File %s doesn't exist in directory\n"),
1106 if (disk_path == NULL) {
1107 g_printf(_("Must select directory before adding files\n"));
1111 uqglob = unquote_string(glob);
1112 newglob = file_of_path(uqglob, &dir);
1114 sdir = merge_path(mount_point, disk_path);
1115 result = cd_glob(dir, 0);
1119 regex = glob_to_regex(newglob);
1120 dbprintf(_("delete_glob (%s) -> %s\n"), newglob, regex);
1121 if ((s = validate_regexp(regex)) != NULL) {
1122 g_printf(_("\"%s\" is not a valid shell wildcard pattern: "),
1127 * glob_to_regex() anchors the beginning of the pattern with ^,
1128 * but we will be tacking it onto the end of the current directory
1129 * in add_file, so strip that off. Also, it anchors the end with
1130 * $, but we need to match an optional trailing /, so tack that on
1133 regex_path = stralloc(regex + 1);
1134 regex_path[strlen(regex_path) - 1] = '\0';
1135 strappend(regex_path, "[/]*$");
1136 delete_file(uqglob, regex_path);
1140 set_directory(sdir, 0);
1160 if (disk_path == NULL) {
1161 g_printf(_("Must select directory before adding files\n"));
1165 uqregex = unquote_string(regex);
1166 newregex = file_of_path(uqregex, &dir);
1168 sdir = merge_path(mount_point, disk_path);
1169 result = cd_regex(dir, 0);
1174 if ((s = validate_regexp(newregex)) != NULL) {
1175 g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
1178 delete_file(newregex, regex);
1181 set_directory(sdir, 0);
1194 DIR_ITEM *ditem, lditem;
1195 char *tpath_on_disk = NULL;
1201 char *tape, *tape_undo, tape_undo_ch = '\0';
1202 char *dir_undo, dir_undo_ch = '\0';
1204 char *ditem_path = NULL;
1205 char *ditem_tpath = NULL;
1214 if (disk_path == NULL) {
1215 g_printf(_("Must select directory before deleting files\n"));
1218 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
1220 dbprintf(_("delete_file: Looking for \"%s\"\n"), tpath);
1222 if (strcmp(regex, "[^/]*[/]*$") == 0) {
1223 /* Looking for * find everything but single . */
1224 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
1226 /* remove "/" at end of path */
1227 j = (ssize_t)(strlen(regex) - 1);
1228 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
1231 /* convert path (assumed in cwd) to one on disk */
1232 if (strcmp(disk_path, "/") == 0) {
1233 if (*regex == '/') {
1234 if (strcmp(regex, "/[/]*$") == 0) {
1235 /* We want "/" to match the directory itself: "/." */
1236 tpath_on_disk = stralloc("/\\.[/]*$");
1238 /* No mods needed if already starts with '/' */
1239 tpath_on_disk = stralloc(regex);
1243 tpath_on_disk = g_strconcat("/", regex, NULL);
1246 char *clean_disk_tpath = clean_regex(disk_tpath, 0);
1247 tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
1248 amfree(clean_disk_tpath);
1251 dbprintf(_("delete_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
1252 regex, tpath_on_disk);
1254 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
1256 quoted = quote_string(ditem->tpath);
1257 dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted);
1259 if (match(tpath_on_disk, ditem->tpath))
1262 j = (ssize_t)strlen(ditem->tpath);
1263 if((j > 0 && ditem->tpath[j-1] == '/')
1264 || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
1265 { /* It is a directory */
1266 ditem_path = newstralloc(ditem_path, ditem->path);
1267 ditem_tpath = newstralloc(ditem_tpath, ditem->tpath);
1268 clean_pathname(ditem_path);
1269 clean_pathname(ditem_tpath);
1271 qditem_path = quote_string(ditem_path);
1272 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
1273 amfree(qditem_path);
1274 if(send_command(cmd) == -1) {
1277 amfree(tpath_on_disk);
1282 if ((i = get_reply_line()) == -1) {
1284 amfree(tpath_on_disk);
1287 if(i==0) /* assume something wrong */
1290 amfree(tpath_on_disk);
1292 g_printf("%s\n", l);
1296 lditem.path = newstralloc(lditem.path, ditem->path);
1297 lditem.tpath = newstralloc(lditem.tpath, ditem->tpath);
1299 tape_undo = dir_undo = NULL;
1300 /* skip the last line -- duplicate of the preamble */
1301 while ((i = get_reply_line()) != 0)
1305 amfree(tpath_on_disk);
1310 if(tape_undo) *tape_undo = tape_undo_ch;
1311 if(dir_undo) *dir_undo = dir_undo_ch;
1312 tape_undo = dir_undo = NULL;
1313 cmd = stralloc(l); /* save for the error report */
1315 continue; /* throw the rest of the lines away */
1318 if (!server_happy()) {
1324 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1325 err = _("bad reply: not 201-");
1330 skip_whitespace(s, ch);
1332 err = _("bad reply: missing date field");
1336 skip_non_whitespace(s, ch);
1339 skip_whitespace(s, ch);
1340 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1341 err = _("bad reply: cannot parse level field");
1344 skip_integer(s, ch);
1346 skip_whitespace(s, ch);
1348 err = _("bad reply: missing tape field");
1352 skip_non_whitespace(s, ch);
1354 tape_undo_ch = *tape_undo;
1357 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1358 long long fileno_ = (long long)0;
1359 skip_whitespace(s, ch);
1361 sscanf(s - 1, "%lld", &fileno_) != 1) {
1362 err = _("bad reply: cannot parse fileno field");
1365 skip_integer(s, ch);
1368 skip_whitespace(s, ch);
1370 err = _("bad reply: missing directory field");
1373 skip_non_whitespace(s, ch);
1375 dir_undo_ch = *dir_undo;
1378 lditem.date = newstralloc(lditem.date, date);
1380 g_free(lditem.tape);
1381 lditem.tape = unquote_string(tape);
1382 switch(delete_extract_item(&lditem)) {
1384 g_printf(_("System error\n"));
1385 dbprintf(_("delete_file: (Failed) System error\n"));
1388 g_printf(_("Deleted dir %s at date %s\n"), ditem_tpath, date);
1389 dbprintf(_("delete_file: (Successful) Deleted dir %s at date %s\n"),
1397 if(!server_happy()) {
1404 } else if(deleted == 0) {
1405 g_printf(_("Warning - dir '%s' not on tape list\n"),
1407 dbprintf(_("delete_file: dir '%s' not on tape list\n"),
1413 switch(delete_extract_item(ditem)) {
1415 g_printf(_("System error\n"));
1416 dbprintf(_("delete_file: (Failed) System error\n"));
1419 g_printf(_("Deleted %s\n"), ditem->tpath);
1420 dbprintf(_("delete_file: (Successful) Deleted %s\n"),
1424 g_printf(_("Warning - file '%s' not on tape list\n"),
1426 dbprintf(_("delete_file: file '%s' not on tape list\n"),
1435 amfree(ditem_tpath);
1436 amfree(tpath_on_disk);
1439 g_printf(_("File %s doesn't exist in directory\n"), tpath);
1440 dbprintf(_("delete_file: (Failed) File %s doesn't exist in directory\n"),
1446 /* print extract list into file. If NULL ptr passed print to screen */
1448 display_extract_list(
1452 EXTRACT_LIST_ITEM *that;
1455 char *pager_command;
1460 if ((pager = getenv("PAGER")) == NULL)
1465 * Set up the pager command so if the pager is terminated, we do
1466 * not get a SIGPIPE back.
1468 pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1469 if ((fp = popen(pager_command, "w")) == NULL)
1471 g_printf(_("Warning - can't pipe through %s\n"), pager);
1474 amfree(pager_command);
1478 uqfile = unquote_string(file);
1479 if ((fp = fopen(uqfile, "w")) == NULL)
1481 g_printf(_("Can't open file %s to print extract list into\n"), file);
1488 for (this = extract_list; this != NULL; this = this->next)
1490 g_fprintf(fp, _("TAPE %s LEVEL %d DATE %s\n"),
1491 this->tape, this->level, this->date);
1492 for (that = this->files; that != NULL; that = that->next)
1493 g_fprintf(fp, "\t%s\n", that->tpath);
1499 g_printf(_("Extract list written to file %s\n"), file);
1510 struct dirent *entry;
1513 if((dir = opendir(fname)) == NULL)
1517 while(!gotentry && (entry = readdir(dir)) != NULL) {
1518 gotentry = !is_dot_or_dotdot(entry->d_name);
1526 /* returns 0 if extract list empty and 1 if it isn't */
1528 is_extract_list_nonempty(void)
1530 return (extract_list != NULL);
1534 /* prints continue prompt and waits for response,
1535 returns 0 if don't, non-0 if do */
1552 prompt = _("New device name [?]: ");
1553 } else if (allow_tape && allow_skip) {
1554 prompt = _("Continue [?/Y/n/s/d]? ");
1555 } else if (allow_tape && !allow_skip) {
1556 prompt = _("Continue [?/Y/n/d]? ");
1557 } else if (allow_retry) {
1558 prompt = _("Continue [?/Y/n/r]? ");
1560 prompt = _("Continue [?/Y/n]? ");
1562 fputs(prompt, stdout);
1563 fflush(stdout); fflush(stderr);
1565 if ((line = agets(stdin)) == NULL) {
1575 dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line);
1578 while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) {
1579 (void)ch; /* Quiet empty loop compiler warning */
1583 g_printf(_("Enter a new device name or \"default\"\n"));
1585 g_printf(_("Enter \"y\"es to continue, \"n\"o to stop"));
1587 g_printf(_(", \"s\"kip this tape"));
1590 g_printf(_(" or \"r\"etry this tape"));
1593 g_printf(_(" or \"d\" to change to a new device"));
1597 } else if (get_device) {
1598 char *tmp = stralloc(tape_server_name);
1600 if (strncmp_const(s - 1, "default") == 0) {
1601 set_device(tmp, NULL); /* default device, existing host */
1602 } else if (s[-1] != '\0') {
1603 set_device(tmp, s - 1); /* specified device, existing host */
1605 g_printf(_("No change.\n"));
1611 } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1613 } else if (allow_tape && (ch == 'D' || ch == 'd' || ch == 'T' || ch == 't')) {
1614 get_device = 1; /* ('T' and 't' are for backward-compatibility) */
1615 } else if (ch == 'N' || ch == 'n') {
1617 } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1619 } else if (allow_skip && (ch == 'S' || ch == 's')) {
1630 send_to_tape_server(
1631 security_stream_t * stream,
1634 char *msg = stralloc2(cmd, "\r\n");
1636 g_debug("send_to_tape_server: %s\n", cmd);
1637 if (security_stream_write(stream, msg, strlen(msg)) < 0)
1639 error(_("Error writing to tape server"));
1647 /* start up connection to tape server and set commands to initiate
1648 transfer of dump image.
1649 Return tape server socket on success, -1 on error. */
1651 extract_files_setup(
1655 char *disk_regex = NULL;
1656 char *host_regex = NULL;
1657 char *clean_datestamp, *ch, *ch1;
1662 amidxtaped_secdrv = security_getdriver(authopt);
1663 if (amidxtaped_secdrv == NULL) {
1664 error(_("no '%s' security driver available for host '%s'"),
1665 authopt, tape_server_name);
1668 /* We assume that amidxtaped support fe_amidxtaped_options_features */
1669 /* and fe_amidxtaped_options_auth */
1670 /* We should send a noop to really know */
1671 req = vstralloc("SERVICE amidxtaped\n",
1672 "OPTIONS ", "features=", our_features_string, ";",
1673 "auth=", authopt, ";",
1675 protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1676 generic_client_get_security_conf, req, STARTUP_TIMEOUT,
1677 amidxtaped_response, &response_error);
1680 if(response_error != 0) {
1684 disk_regex = make_exact_disk_expression(disk_name);
1685 host_regex = make_exact_host_expression(dump_hostname);
1687 clean_datestamp = stralloc(dump_datestamp);
1688 for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1695 /* push our feature list off to the tape server */
1696 /* XXX assumes that index server and tape server are equivalent, ew */
1698 if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1699 tt = newstralloc2(tt, "FEATURES=", our_features_string);
1700 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1701 get_amidxtaped_line();
1702 if (!amidxtaped_line) {
1703 g_fprintf(stderr, _("amrecover - amidxtaped closed the connection\n"));
1707 amfree(clean_datestamp);
1709 } else if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
1710 tapesrv_features = am_string_to_feature(amidxtaped_line+9);
1712 g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n"));
1716 amfree(clean_datestamp);
1720 *tapesrv_features = *indexsrv_features;
1724 if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1725 am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1726 am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1727 am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1728 am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1730 if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1731 tt = newstralloc2(tt, "CONFIG=", get_config_name());
1732 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1734 if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1735 label && label[0] != '/') {
1736 tt = newstralloc2(tt,"LABEL=",label);
1737 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1739 if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1741 g_snprintf(v_fsf, 99, "%lld", (long long)fsf);
1742 tt = newstralloc2(tt, "FSF=",v_fsf);
1743 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1745 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
1746 tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1747 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1748 tt = newstralloc2(tt, "HOST=", host_regex);
1749 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1750 tt = newstralloc2(tt, "DISK=", disk_regex);
1751 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1752 tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1753 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1754 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
1757 else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1758 /* send to the tape server what tape file we want */
1767 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6");
1768 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h");
1769 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p");
1770 send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name);
1771 send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex);
1772 send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex);
1773 send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp);
1775 dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"),
1776 dump_device_name, host_regex, disk_regex, clean_datestamp);
1781 amfree(clean_datestamp);
1788 * Reads the first block of a tape file.
1799 bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1800 if(bytes_read < 0) {
1801 error(_("error reading header (%s), check amidxtaped.*.debug on server"),
1806 if((size_t)bytes_read < buflen) {
1807 g_fprintf(stderr, plural(_("%s: short block %d byte\n"),
1808 _("%s: short block %d bytes\n"), bytes_read),
1809 get_pname(), (int)bytes_read);
1810 print_header(stdout, file);
1811 error(_("Can't read file header"));
1815 /* bytes_read == buflen */
1816 parse_file_header(buffer, file, (size_t)bytes_read);
1830 extract_files_child(
1831 ctl_data_t *ctl_data)
1836 GPtrArray *argv_ptr = g_ptr_array_new();
1838 EXTRACT_LIST_ITEM *fn;
1839 enum dumptypes dumptype = IS_UNKNOWN;
1842 guint passwd_field = 999999999;
1844 char *domain = NULL, *smbpass = NULL;
1847 /* code executed by child to do extraction */
1850 /* make in_fd be our stdin */
1851 if (dup2(ctl_data->child_pipe[0], STDIN_FILENO) == -1)
1853 error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
1857 if(ctl_data->file.type != F_DUMPFILE) {
1858 dump_dumpfile_t(&ctl_data->file);
1859 error(_("bad header"));
1863 if (ctl_data->file.program != NULL) {
1864 if (strcmp(ctl_data->file.program, "APPLICATION") == 0)
1865 dumptype = IS_APPLICATION_API;
1867 if (strcmp(ctl_data->file.program, GNUTAR) == 0)
1868 dumptype = IS_GNUTAR;
1871 if (dumptype == IS_UNKNOWN) {
1872 len_program = strlen(ctl_data->file.program);
1873 if(len_program >= 3 &&
1874 strcmp(&ctl_data->file.program[len_program-3],"tar") == 0)
1879 if (dumptype == IS_UNKNOWN && strcmp(ctl_data->file.program, SAMBA_CLIENT) ==0) {
1880 if (samba_extract_method == SAMBA_TAR)
1881 dumptype = IS_SAMBA_TAR;
1883 dumptype = IS_SAMBA;
1888 /* form the arguments to restore */
1889 files_off_tape = length_of_tape_list(ctl_data->elist);
1893 g_ptr_array_add(argv_ptr, stralloc("smbclient"));
1894 smbpass = findpass(ctl_data->file.disk, &domain);
1896 g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
1897 g_ptr_array_add(argv_ptr, stralloc("-U"));
1898 passwd_field = argv_ptr->len;
1899 g_ptr_array_add(argv_ptr, stralloc(smbpass));
1901 g_ptr_array_add(argv_ptr, stralloc("-W"));
1902 g_ptr_array_add(argv_ptr, stralloc(domain));
1905 g_ptr_array_add(argv_ptr, stralloc("-d0"));
1906 g_ptr_array_add(argv_ptr, stralloc("-Tx"));
1907 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1912 g_ptr_array_add(argv_ptr, stralloc("tar"));
1913 /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1914 g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1915 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1916 g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1917 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1920 g_ptr_array_add(argv_ptr, stralloc("tar"));
1921 g_ptr_array_add(argv_ptr, stralloc("-xpvf"));
1922 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1926 g_ptr_array_add(argv_ptr, stralloc("restore"));
1928 g_ptr_array_add(argv_ptr, stralloc("-xB"));
1930 #if defined(XFSDUMP)
1931 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
1932 g_ptr_array_add(argv_ptr, stralloc("-v"));
1933 g_ptr_array_add(argv_ptr, stralloc("silent"));
1937 if (strcmp(ctl_data->file.program, VDUMP) == 0) {
1938 g_ptr_array_add(argv_ptr, stralloc("xf"));
1939 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1943 g_ptr_array_add(argv_ptr, stralloc("xbf"));
1944 g_ptr_array_add(argv_ptr, stralloc("2")); /* read in units of 1K */
1945 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1949 case IS_APPLICATION_API:
1950 g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.application));
1951 g_ptr_array_add(argv_ptr, stralloc("restore"));
1952 g_ptr_array_add(argv_ptr, stralloc("--config"));
1953 g_ptr_array_add(argv_ptr, stralloc(get_config_name()));
1954 g_ptr_array_add(argv_ptr, stralloc("--disk"));
1955 g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
1956 if (dump_dle && dump_dle->device) {
1957 g_ptr_array_add(argv_ptr, stralloc("--device"));
1958 g_ptr_array_add(argv_ptr, stralloc(dump_dle->device));
1960 if (ctl_data->data_path == DATA_PATH_DIRECTTCP) {
1961 g_ptr_array_add(argv_ptr, stralloc("--data-path"));
1962 g_ptr_array_add(argv_ptr, stralloc("DIRECTTCP"));
1963 g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
1964 g_ptr_array_add(argv_ptr, stralloc(ctl_data->addrs));
1966 if (ctl_data->bsu && ctl_data->bsu->smb_recover_mode &&
1967 samba_extract_method == SAMBA_SMBCLIENT){
1968 g_ptr_array_add(argv_ptr, stralloc("--recover-mode"));
1969 g_ptr_array_add(argv_ptr, stralloc("smb"));
1971 g_ptr_array_add(argv_ptr, stralloc("--level"));
1972 g_ptr_array_add(argv_ptr, g_strdup_printf("%d", ctl_data->elist->level));
1977 merge_properties(dump_dle, NULL, dump_dle->application_property,
1979 application_property_add_to_argv(argv_ptr, dump_dle, NULL,
1981 for (scriptlist = dump_dle->scriptlist; scriptlist != NULL;
1982 scriptlist = scriptlist->next) {
1983 script = (script_t *)scriptlist->data;
1984 if (script->result && script->result->proplist) {
1985 property_add_to_argv(argv_ptr, script->result->proplist);
1989 } else if (proplist) {
1990 property_add_to_argv(argv_ptr, proplist);
1995 for (i = 0, fn = ctl_data->elist->files; i < files_off_tape;
1999 case IS_APPLICATION_API:
2004 if (strcmp(fn->path, "/") == 0)
2005 g_ptr_array_add(argv_ptr, stralloc("."));
2007 g_ptr_array_add(argv_ptr, stralloc2(".", fn->path));
2011 #if defined(XFSDUMP)
2012 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2014 * xfsrestore needs a -s option before each file to be
2015 * restored, and also wants them to be relative paths.
2017 g_ptr_array_add(argv_ptr, stralloc("-s"));
2018 g_ptr_array_add(argv_ptr, stralloc(fn->path + 1));
2022 g_ptr_array_add(argv_ptr, stralloc(fn->path));
2027 #if defined(XFSDUMP)
2028 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2029 g_ptr_array_add(argv_ptr, stralloc("-"));
2030 g_ptr_array_add(argv_ptr, stralloc("."));
2033 g_ptr_array_add(argv_ptr, NULL);
2038 cmd = stralloc(SAMBA_CLIENT);
2041 /* fall through to ... */
2047 g_fprintf(stderr, _("warning: GNUTAR program not available.\n"));
2048 cmd = stralloc("tar");
2050 cmd = stralloc(GNUTAR);
2057 if (strcmp(ctl_data->file.program, DUMP) == 0) {
2058 cmd = stralloc(RESTORE);
2062 if (strcmp(ctl_data->file.program, VDUMP) == 0) {
2063 cmd = stralloc(VRESTORE);
2067 if (strcmp(ctl_data->file.program, VXDUMP) == 0) {
2068 cmd = stralloc(VXRESTORE);
2071 #if defined(XFSDUMP)
2072 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2073 cmd = stralloc(XFSRESTORE);
2077 g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
2078 ctl_data->file.program);
2079 cmd = stralloc("restore");
2082 case IS_APPLICATION_API:
2083 cmd = vstralloc(APPLICATION_DIR, "/", ctl_data->file.application, NULL);
2087 dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
2088 for (j = 0; j < argv_ptr->len - 1; j++) {
2089 if (j == passwd_field)
2090 dbprintf("\tXXXXX\n");
2092 dbprintf(_("\t%s\n"), (char *)g_ptr_array_index(argv_ptr, j));
2095 (void)execv(cmd, (char **)argv_ptr->pdata);
2096 /* only get here if exec failed */
2098 g_ptr_array_free_full(argv_ptr);
2100 perror(_("amrecover couldn't exec"));
2101 g_fprintf(stderr, _(" problem executing %s\n"), cmd);
2109 * Interpose something between the process writing out the dump (writing it to
2110 * some extraction program, really) and the socket from which we're reading, so
2111 * that we can do things like prompt for human interaction for multiple tapes.
2114 writer_intermediary(
2115 EXTRACT_LIST * elist)
2117 ctl_data_t ctl_data;
2118 amwait_t extractor_status;
2120 ctl_data.header_done = 0;
2121 ctl_data.child_pipe[0] = -1;
2122 ctl_data.child_pipe[1] = -1;
2124 ctl_data.elist = elist;
2125 fh_init(&ctl_data.file);
2126 ctl_data.data_path = DATA_PATH_AMANDA;
2127 ctl_data.addrs = NULL;
2128 ctl_data.bsu = NULL;
2129 ctl_data.bytes_read = 0;
2132 security_stream_read(amidxtaped_streams[DATAFD].fd,
2133 read_amidxtaped_data, &ctl_data);
2135 while(get_amidxtaped_line() >= 0) {
2136 char desired_tape[MAX_TAPE_LABEL_BUF];
2137 g_debug("get amidxtaped line: %s", amidxtaped_line);
2139 /* if prompted for a tape, relay said prompt to the user */
2140 if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
2142 g_printf(_("Load tape %s now\n"), desired_tape);
2143 dbprintf(_("Requesting tape %s from user\n"), desired_tape);
2144 done = okay_to_continue(am_has_feature(indexsrv_features,
2145 fe_amrecover_feedme_tape),
2148 if (am_has_feature(indexsrv_features,
2149 fe_amrecover_feedme_tape)) {
2150 char *reply = stralloc2("TAPE ", tape_device_name);
2151 send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply);
2154 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2157 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2160 } else if (strncmp_const(amidxtaped_line, "USE-DATAPATH ") == 0) {
2161 if (strncmp_const(amidxtaped_line+13, "AMANDA") == 0) {
2162 ctl_data.data_path = DATA_PATH_AMANDA;
2163 g_debug("Using AMANDA data-path");
2164 } else if (strncmp_const(amidxtaped_line+13, "DIRECT-TCP") == 0) {
2165 ctl_data.data_path = DATA_PATH_DIRECTTCP;
2166 ctl_data.addrs = stralloc(amidxtaped_line+24);
2167 g_debug("Using DIRECT-TCP data-path with %s", ctl_data.addrs);
2169 start_processing_data(&ctl_data);
2170 } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
2171 g_printf("%s\n",&amidxtaped_line[8]);
2173 g_fprintf(stderr, _("Strange message from tape server: %s"),
2179 /* CTL might be close before DATA */
2181 dumpfile_free_data(&ctl_data.file);
2182 amfree(ctl_data.addrs);
2183 amfree(ctl_data.bsu);
2184 if (ctl_data.child_pipe[1] != -1)
2185 aclose(ctl_data.child_pipe[1]);
2187 if (ctl_data.header_done == 0) {
2188 g_printf(_("Got no header and data from server, check in amidxtaped.*.debug and amandad.*.debug files on server\n"));
2191 if (ctl_data.pid != -1) {
2192 waitpid(ctl_data.pid, &extractor_status, 0);
2193 if(WEXITSTATUS(extractor_status) != 0){
2194 int ret = WEXITSTATUS(extractor_status);
2195 if(ret == 255) ret = -1;
2196 g_printf(_("Extractor child exited with status %d\n"), ret);
2200 g_debug("bytes read: %jd", (intmax_t)ctl_data.bytes_read);
2204 /* exec restore to do the actual restoration */
2206 /* does the actual extraction of files */
2208 * The original design had the dump image being returned exactly as it
2209 * appears on the tape, and this routine getting from the index server
2210 * whether or not it is compressed, on the assumption that the tape
2211 * server may not know how to uncompress it. But
2212 * - Amrestore can't do that. It returns either compressed or uncompressed
2213 * (always). Amrestore assumes it can uncompress files. It is thus a good
2214 * idea to run the tape server on a machine with gzip.
2215 * - The information about compression in the disklist is really only
2216 * for future dumps. It is possible to change compression on a drive
2217 * so the information in the disklist may not necessarily relate to
2218 * the dump image on the tape.
2219 * Consequently the design was changed to assuming that amrestore can
2220 * uncompress any dump image and have it return an uncompressed file
2226 EXTRACT_LIST *elist;
2230 tapelist_t *tlist = NULL, *a_tlist;
2231 g_option_t g_options;
2232 levellist_t all_level = NULL;
2235 if (!is_extract_list_nonempty())
2237 g_printf(_("Extract list empty - No files to extract!\n"));
2241 clean_extract_list();
2243 /* get tape device name from index server if none specified */
2244 if (tape_server_name == NULL) {
2245 tape_server_name = newstralloc(tape_server_name, server_name);
2247 if (tape_device_name == NULL) {
2248 if (send_command("TAPE") == -1)
2250 if (get_reply_line() == -1)
2253 if (!server_happy())
2255 g_printf("%s\n", l);
2258 /* skip reply number */
2259 tape_device_name = newstralloc(tape_device_name, l+4);
2262 if (strcmp(tape_device_name, "/dev/null") == 0)
2264 g_printf(_("amrecover: warning: using %s as the tape device will not work\n"),
2269 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2270 if(elist->tape[0]!='/') {
2272 g_printf(_("\nExtracting files using tape drive %s on host %s.\n"),
2273 tape_device_name, tape_server_name);
2274 g_printf(_("The following tapes are needed:"));
2279 tlist = unmarshal_tapelist_str(elist->tape);
2280 for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
2281 g_printf(" %s", a_tlist->label);
2283 free_tapelist(tlist);
2287 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2288 if(elist->tape[0]=='/') {
2290 g_printf(_("\nExtracting files from holding disk on host %s.\n"),
2292 g_printf(_("The following files are needed:"));
2297 tlist = unmarshal_tapelist_str(elist->tape);
2298 for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2299 g_printf(" %s", a_tlist->label);
2301 free_tapelist(tlist);
2306 g_options.config = get_config_name();
2307 g_options.hostname = dump_hostname;
2308 for (elist = first_tape_list(); elist != NULL;
2309 elist = next_tape_list(elist)) {
2310 am_level_t *level = g_new0(am_level_t, 1);
2311 level->level = elist->level;
2312 all_level = g_slist_append(all_level, level);
2315 slist_free_full(dump_dle->levellist, g_free);
2316 dump_dle->levellist = all_level;
2317 run_client_scripts(EXECUTE_ON_PRE_RECOVER, &g_options, dump_dle,
2319 dump_dle->levellist = NULL;
2322 while ((elist = first_tape_list()) != NULL)
2324 if(elist->tape[0]=='/') {
2325 dump_device_name = newstralloc(dump_device_name, elist->tape);
2326 g_printf(_("Extracting from file "));
2327 tlist = unmarshal_tapelist_str(dump_device_name);
2328 for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2329 g_printf(" %s", a_tlist->label);
2331 free_tapelist(tlist);
2334 g_printf(_("Extracting files using tape drive %s on host %s.\n"),
2335 tape_device_name, tape_server_name);
2336 tlist = unmarshal_tapelist_str(elist->tape);
2337 g_printf(_("Load tape %s now\n"), tlist->label);
2338 dbprintf(_("Requesting tape %s from user\n"), tlist->label);
2339 free_tapelist(tlist);
2340 otc = okay_to_continue(1,1,0);
2343 else if (otc == SKIP_TAPE) {
2344 delete_tape_list(elist); /* skip this tape */
2347 dump_device_name = newstralloc(dump_device_name, tape_device_name);
2349 dump_datestamp = newstralloc(dump_datestamp, elist->date);
2351 if (last_level != -1 && dump_dle) {
2354 level = g_new0(am_level_t, 1);
2355 level->level = last_level;
2356 dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2358 level = g_new0(am_level_t, 1);
2359 level->level = elist->level;
2360 dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2361 run_client_scripts(EXECUTE_ON_INTER_LEVEL_RECOVER, &g_options,
2363 slist_free_full(dump_dle->levellist, g_free);
2364 dump_dle->levellist = NULL;
2367 /* connect to the tape handler daemon on the tape drive server */
2368 if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2370 g_fprintf(stderr, _("amrecover - can't talk to tape server: %s\n"),
2377 level = g_new0(am_level_t, 1);
2378 level->level = elist->level;
2379 dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2380 run_client_scripts(EXECUTE_ON_PRE_LEVEL_RECOVER, &g_options,
2383 last_level = elist->level;
2385 /* if the server have fe_amrecover_feedme_tape, it has asked for
2386 * the tape itself, even if the restore didn't succeed, we should
2389 if(writer_intermediary(elist) == 0 ||
2390 am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
2391 delete_tape_list(elist); /* tape done so delete from list */
2393 am_release_feature_set(tapesrv_features);
2397 run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
2399 slist_free_full(dump_dle->levellist, g_free);
2400 dump_dle->levellist = NULL;
2404 dump_dle->levellist = all_level;
2405 run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
2407 slist_free_full(dump_dle->levellist, g_free);
2409 dump_dle->levellist = NULL;
2414 amidxtaped_response(
2417 security_handle_t * sech)
2419 int ports[NSTREAMS], *response_error = datap, i;
2424 assert(response_error != NULL);
2425 assert(sech != NULL);
2426 memset(ports, -1, SIZEOF(ports));
2429 errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech));
2430 *response_error = 1;
2433 security_close_connection(sech, dump_hostname);
2435 if (pkt->type == P_NAK) {
2436 #if defined(PACKET_DEBUG)
2437 g_fprintf(stderr, _("got nak response:\n----\n%s\n----\n\n"), pkt->body);
2440 tok = strtok(pkt->body, " ");
2441 if (tok == NULL || strcmp(tok, "ERROR") != 0)
2444 tok = strtok(NULL, "\n");
2446 errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2447 *response_error = 1;
2450 errstr = newstralloc(errstr, "request NAK");
2451 *response_error = 2;
2456 if (pkt->type != P_REP) {
2457 errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
2458 pkt_type2str(pkt->type), pkt->body);
2459 *response_error = 1;
2463 #if defined(PACKET_DEBUG)
2464 g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
2467 for(i = 0; i < NSTREAMS; i++) {
2469 amidxtaped_streams[i].fd = NULL;
2473 while((tok = strtok(p, " \n")) != NULL) {
2477 * Error response packets have "ERROR" followed by the error message
2478 * followed by a newline.
2480 if (strcmp(tok, "ERROR") == 0) {
2481 tok = strtok(NULL, "\n");
2483 tok = _("[bogus error packet]");
2484 errstr = newstralloc(errstr, tok);
2485 *response_error = 2;
2491 * Regular packets have CONNECT followed by three streams
2493 if (strcmp(tok, "CONNECT") == 0) {
2496 * Parse the three stream specifiers out of the packet.
2498 for (i = 0; i < NSTREAMS; i++) {
2499 tok = strtok(NULL, " ");
2500 if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) {
2501 extra = vstrallocf(_("CONNECT token is \"%s\": expected \"%s\""),
2502 tok ? tok : "(null)",
2503 amidxtaped_streams[i].name);
2506 tok = strtok(NULL, " \n");
2507 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
2508 extra = vstrallocf(_("CONNECT %s token is \"%s\": expected a port number"),
2509 amidxtaped_streams[i].name,
2510 tok ? tok : "(null)");
2518 * OPTIONS [options string] '\n'
2520 if (strcmp(tok, "OPTIONS") == 0) {
2521 tok = strtok(NULL, "\n");
2523 extra = stralloc(_("OPTIONS token is missing"));
2527 while((p = strchr(tok, ';')) != NULL) {
2529 if(strncmp_const(tok, "features=") == 0) {
2530 tok += sizeof("features=") - 1;
2531 am_release_feature_set(their_features);
2532 if((their_features = am_string_to_feature(tok)) == NULL) {
2533 errstr = newvstralloc(errstr,
2534 _("OPTIONS: bad features value: "),
2546 extra = vstrallocf("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""),
2547 tok ? tok : _("(null)"));
2553 * Connect the streams to their remote ports
2555 for (i = 0; i < NSTREAMS; i++) {
2558 amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]);
2559 dbprintf(_("amidxtaped_streams[%d].fd = %p\n"),i, amidxtaped_streams[i].fd);
2560 if (amidxtaped_streams[i].fd == NULL) {
2561 errstr = newvstrallocf(errstr,\
2562 _("[could not connect %s stream: %s]"),
2563 amidxtaped_streams[i].name,
2564 security_geterror(sech));
2569 * Authenticate the streams
2571 for (i = 0; i < NSTREAMS; i++) {
2572 if (amidxtaped_streams[i].fd == NULL)
2574 if (security_stream_auth(amidxtaped_streams[i].fd) < 0) {
2575 errstr = newvstrallocf(errstr,
2576 _("[could not authenticate %s stream: %s]"),
2577 amidxtaped_streams[i].name,
2578 security_stream_geterror(amidxtaped_streams[i].fd));
2584 * The CTLFD and DATAFD streams are mandatory. If we didn't get
2587 if (amidxtaped_streams[CTLFD].fd == NULL) {
2588 errstr = newvstrallocf(errstr, _("[couldn't open CTL streams]"));
2591 if (amidxtaped_streams[DATAFD].fd == NULL) {
2592 errstr = newvstrallocf(errstr, _("[couldn't open DATA streams]"));
2596 /* everything worked */
2597 *response_error = 0;
2602 errstr = newvstrallocf(errstr,
2603 _("[parse of reply message failed: %s]"), extra);
2605 errstr = newvstrallocf(errstr,
2606 _("[parse of reply message failed: (no additional information)"));
2609 *response_error = 2;
2614 *response_error = 1;
2618 * This is called when everything needs to shut down so event_loop()
2622 stop_amidxtaped(void)
2626 for (i = 0; i < NSTREAMS; i++) {
2627 if (amidxtaped_streams[i].fd != NULL) {
2628 security_stream_close(amidxtaped_streams[i].fd);
2629 amidxtaped_streams[i].fd = NULL;
2634 static char* ctl_buffer = NULL;
2635 /* gets a "line" from server and put in server_line */
2636 /* server_line is terminated with \0, \r\n is striped */
2637 /* returns -1 if error */
2640 get_amidxtaped_line(void)
2646 amfree(amidxtaped_line);
2648 ctl_buffer = stralloc("");
2650 while (!strstr(ctl_buffer,"\r\n")) {
2651 if (amidxtaped_streams[CTLFD].fd == NULL)
2654 size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2658 else if(size == 0) {
2661 newbuf = alloc(strlen(ctl_buffer)+size+1);
2662 strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1));
2663 memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size);
2664 newbuf[strlen(ctl_buffer)+size] = '\0';
2666 ctl_buffer = newbuf;
2670 s = strstr(ctl_buffer,"\r\n");
2672 newbuf = stralloc(s+2);
2673 amidxtaped_line = stralloc(ctl_buffer);
2675 ctl_buffer = newbuf;
2681 read_amidxtaped_data(
2686 ctl_data_t *ctl_data = (ctl_data_t *)cookie;
2687 assert(cookie != NULL);
2690 errstr = newstralloc2(errstr, _("amidxtaped read: "),
2691 security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2696 * EOF. Stop and return.
2699 security_stream_close(amidxtaped_streams[DATAFD].fd);
2700 amidxtaped_streams[DATAFD].fd = NULL;
2702 * If the mesg fd has also shut down, then we're done.
2707 assert(buf != NULL);
2709 if (ctl_data->header_done == 0) {
2710 GPtrArray *errarray;
2711 g_option_t g_options;
2712 data_path_t data_path_set = DATA_PATH_AMANDA;
2715 to_move = MIN(32768-header_size, size);
2716 memcpy(header_buf+header_size, buf, to_move);
2717 header_size += to_move;
2719 g_debug("read header %zd => %d", size, header_size);
2720 if (header_size < 32768) {
2721 security_stream_read(amidxtaped_streams[DATAFD].fd,
2722 read_amidxtaped_data, cookie);
2724 } else if (header_size > 32768) {
2725 error("header_size is %d\n", header_size);
2727 assert (to_move == size);
2728 /* parse the file header */
2729 fh_init(&ctl_data->file);
2730 parse_file_header(header_buf, &ctl_data->file, (size_t)header_size);
2732 /* call backup_support_option */
2733 g_options.config = get_config_name();
2734 g_options.hostname = dump_hostname;
2735 if (strcmp(ctl_data->file.program, "APPLICATION") == 0) {
2737 ctl_data->bsu = backup_support_option(ctl_data->file.application,
2739 ctl_data->file.disk,
2743 ctl_data->bsu = backup_support_option(ctl_data->file.application,
2745 ctl_data->file.disk, NULL,
2748 if (!ctl_data->bsu) {
2750 for (i=0; i < errarray->len; i++) {
2752 line = g_ptr_array_index(errarray, i);
2753 g_fprintf(stderr, "%s\n", line);
2755 g_ptr_array_free_full(errarray);
2758 data_path_set = ctl_data->bsu->data_path_set;
2760 /* handle backup_support_option failure */
2762 ctl_data->header_done = 1;
2763 if (!ask_file_overwrite(ctl_data)) {
2764 if (am_has_feature(tapesrv_features, fe_amidxtaped_abort)) {
2765 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ABORT");
2771 if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
2773 /* send DATA-PATH request */
2774 msg = stralloc("AVAIL-DATAPATH");
2775 if (data_path_set & DATA_PATH_AMANDA)
2776 vstrextend(&msg, " AMANDA", NULL);
2777 if (data_path_set & DATA_PATH_DIRECTTCP)
2778 vstrextend(&msg, " DIRECT-TCP", NULL);
2779 send_to_tape_server(amidxtaped_streams[CTLFD].fd, msg);
2782 start_processing_data(ctl_data);
2785 ctl_data->bytes_read += size;
2786 /* Only the data is sent to the child */
2788 * We ignore errors while writing to the index file.
2790 (void)full_write(ctl_data->child_pipe[1], buf, (size_t)size);
2791 security_stream_read(amidxtaped_streams[DATAFD].fd,
2792 read_amidxtaped_data, cookie);
2798 ctl_data_t *ctl_data)
2800 char *restore_dir = NULL;
2802 if (ctl_data->file.dumplevel == 0) {
2803 property_t *property = g_hash_table_lookup(proplist, "directory");
2804 if (property && property->values && property->values->data) {
2805 /* take first property value */
2806 restore_dir = strdup(property->values->data);
2808 if (samba_extract_method == SAMBA_SMBCLIENT ||
2810 ctl_data->bsu->recover_path == RECOVER_PATH_REMOTE)) {
2812 restore_dir = g_strdup(ctl_data->file.disk);
2814 g_printf(_("Restoring files into target host %s\n"), restore_dir);
2817 restore_dir = g_get_current_dir();
2819 g_printf(_("Restoring files into directory %s\n"), restore_dir);
2822 /* Collect files to delete befause of a bug in gnutar */
2823 if (strcmp(ctl_data->file.program, "GNUTAR") == 0 ||
2824 (strcmp(ctl_data->file.program, "APPLICATION") == 0 &&
2825 strcmp(ctl_data->file.application, "amgtar") == 0)) {
2826 check_file_overwrite(restore_dir);
2828 g_printf(_("All existing files in %s can be deleted\n"),
2832 if (!okay_to_continue(0,0,0)) {
2834 amfree(restore_dir);
2839 /* delete the files for gnutar */
2841 if (!do_unlink_list()) {
2842 g_fprintf(stderr, _("Can't recover because I can't cleanup the restore directory (%s)\n"),
2845 amfree(restore_dir);
2850 amfree(restore_dir);
2856 start_processing_data(
2857 ctl_data_t *ctl_data)
2859 if (pipe(ctl_data->child_pipe) == -1) {
2860 error(_("extract_list - error setting up pipe to extractor: %s\n"),
2866 if (ctl_data->file.encrypted) {
2869 int errfd = fileno(stderr);
2871 g_debug("image is encrypted %s %s", ctl_data->file.clnt_encrypt, ctl_data->file.clnt_decrypt_opt);
2872 argv[0] = ctl_data->file.clnt_encrypt;
2873 argv[1] = ctl_data->file.clnt_decrypt_opt;
2875 pipespawnv(ctl_data->file.clnt_encrypt, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &crypt_out, &errfd, argv);
2876 ctl_data->child_pipe[0] = crypt_out;
2880 if (ctl_data->file.compressed) {
2883 int errfd = fileno(stderr);
2887 g_debug("image is compressed %s", ctl_data->file.clntcompprog);
2888 if (strlen(ctl_data->file.clntcompprog) > 0) {
2889 comp_prog = ctl_data->file.clntcompprog;
2892 comp_prog = UNCOMPRESS_PATH;
2893 comp_arg = UNCOMPRESS_OPT;
2895 argv[0] = comp_prog;
2898 pipespawnv(comp_prog, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &comp_out, &errfd, argv);
2899 ctl_data->child_pipe[0] = comp_out;
2902 /* okay, ready to extract. fork a child to do the actual work */
2903 if ((ctl_data->pid = fork()) == 0) {
2904 /* this is the child process */
2905 /* never gets out of this clause */
2906 aclose(ctl_data->child_pipe[1]);
2907 extract_files_child(ctl_data);
2911 if (ctl_data->pid == -1) {
2912 errstr = newstralloc(errstr, _("writer_intermediary - error forking child"));
2913 g_printf(_("writer_intermediary - error forking child"));
2916 aclose(ctl_data->child_pipe[0]);
2917 security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data,
2919 if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
2920 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "DATAPATH-OK");