2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
30 * implements the "extract" command in amrecover
35 #include "amrecover.h"
36 #include "fileheader.h"
47 #include "client_util.h"
49 #include "pipespawn.h"
51 typedef struct EXTRACT_LIST_ITEM {
54 struct EXTRACT_LIST_ITEM *next;
58 typedef struct EXTRACT_LIST {
59 char *date; /* date tape created */
60 int level; /* level of dump */
61 char *tape; /* tape label */
62 off_t fileno; /* fileno on tape */
63 EXTRACT_LIST_ITEM *files; /* files to get off tape */
65 struct EXTRACT_LIST *next;
69 typedef struct ctl_data_s {
75 data_path_t data_path;
77 backup_support_option_t *bsu;
86 security_stream_t *fd;
87 } amidxtaped_streams[] = {
93 #define NSTREAMS (int)(sizeof(amidxtaped_streams) / sizeof(amidxtaped_streams[0]))
96 static void amidxtaped_response(void *, pkt_t *, security_handle_t *);
97 static void stop_amidxtaped(void);
98 static char *dump_device_name = NULL;
100 static char *amidxtaped_line = NULL;
101 extern char *localhost;
102 static char header_buf[32768];
103 static int header_size = 0;
106 /* global pid storage for interrupt handler */
107 pid_t extract_restore_child_pid = -1;
109 static EXTRACT_LIST *extract_list = NULL;
110 static const security_driver_t *amidxtaped_secdrv;
112 unsigned short samba_extract_method = SAMBA_TAR;
114 #define READ_TIMEOUT 240*60
116 EXTRACT_LIST *first_tape_list(void);
117 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
118 static int is_empty_dir(char *fname);
119 int is_extract_list_nonempty(void);
120 int length_of_tape_list(EXTRACT_LIST *tape_list);
121 void add_file(char *path, char *regex);
122 void add_glob(char *glob);
123 void add_regex(char *regex);
124 void clear_extract_list(void);
125 void clean_tape_list(EXTRACT_LIST *tape_list);
126 void clean_extract_list(void);
127 void check_file_overwrite(char *filename);
128 void delete_file(char *path, char *regex);
129 void delete_glob(char *glob);
130 void delete_regex(char *regex);
131 void delete_tape_list(EXTRACT_LIST *tape_list);
132 void display_extract_list(char *file);
133 void extract_files(void);
134 void read_file_header(char *buffer,
138 static int add_extract_item(DIR_ITEM *ditem);
139 static int delete_extract_item(DIR_ITEM *ditem);
140 static int extract_files_setup(char *label, off_t fsf);
141 static int okay_to_continue(int allow_tape,
144 static ssize_t read_buffer(int datafd,
148 static void clear_tape_list(EXTRACT_LIST *tape_list);
149 static void extract_files_child(ctl_data_t *ctl_data);
150 static void send_to_tape_server(security_stream_t *stream, char *cmd);
151 int writer_intermediary(EXTRACT_LIST *elist);
152 int get_amidxtaped_line(void);
153 static void read_amidxtaped_data(void *, void *, ssize_t);
154 static char *merge_path(char *path1, char *path2);
155 static gboolean ask_file_overwrite(ctl_data_t *ctl_data);
156 static void start_processing_data(ctl_data_t *ctl_data);
159 * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
162 * read data from input file desciptor waiting up to timeout_s
163 * seconds before returning data.
166 * datafd - File descriptor to read from.
167 * buffer - Buffer to read into.
168 * buflen - Maximum number of bytes to read into buffer.
169 * timeout_s - Seconds to wait before returning what was already read.
172 * >0 - Number of data bytes in buffer.
174 * -1 - errno == ETIMEDOUT if no data available in specified time.
175 * errno == ENFILE if datafd is invalid.
176 * otherwise errno is set by select or read..
187 SELECT_ARG_TYPE readset;
188 struct timeval timeout;
193 if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
194 errno = EMFILE; /* out of range */
199 spaceleft = (ssize_t)buflen;
203 FD_SET(datafd, &readset);
204 timeout.tv_sec = timeout_s;
206 nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
208 /* Select returned an error. */
209 g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
215 /* Select timed out. */
216 if (timeout_s != 0) {
217 /* Not polling: a real read timeout */
218 g_fprintf(stderr,_("timeout waiting for restore\n"));
219 g_fprintf(stderr,_("increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"));
226 if(!FD_ISSET(datafd, &readset))
229 /* Select says data is available, so read it. */
230 size = read(datafd, dataptr, (size_t)spaceleft);
232 if ((errno == EINTR) || (errno == EAGAIN)) {
235 if (errno != EPIPE) {
236 g_fprintf(stderr, _("read_buffer: read error - %s"),
244 } while ((size > 0) && (spaceleft > 0));
246 return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
251 first_tape_list(void)
258 /*@keep@*/EXTRACT_LIST *list)
267 EXTRACT_LIST * tape_list)
269 EXTRACT_LIST_ITEM *this, *next;
272 this = tape_list->files;
281 tape_list->files = NULL;
285 /* remove a tape list from the extract list, clearing the tape list
286 beforehand if necessary */
289 EXTRACT_LIST *tape_list)
291 EXTRACT_LIST *this, *prev;
293 if (tape_list == NULL)
296 /* is it first on the list? */
297 if (tape_list == extract_list)
299 extract_list = tape_list->next;
300 clear_tape_list(tape_list);
301 amfree(tape_list->date);
302 amfree(tape_list->tape);
307 /* so not first on list - find it and delete */
309 this = extract_list->next;
312 if (this == tape_list)
314 prev->next = tape_list->next;
315 clear_tape_list(tape_list);
316 amfree(tape_list->date);
317 amfree(tape_list->tape);
328 /* return the number of files on a tape's list */
331 EXTRACT_LIST *tape_list)
333 EXTRACT_LIST_ITEM *fn;
337 for (fn = tape_list->files; fn != NULL; fn = fn->next)
345 clear_extract_list(void)
347 while (extract_list != NULL)
348 delete_tape_list(extract_list);
354 EXTRACT_LIST *tape_list)
356 EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
357 EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
362 fn1 = tape_list->files;
363 while (fn1 != NULL) {
368 while (fn2 != NULL && remove_fn1 == 0) {
370 if(strcmp(fn1->path, fn2->path) == 0) {
372 } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
373 ((strlen(fn2->path) > strlen(fn1->path) &&
374 fn2->path[strlen(fn1->path)] == '/') ||
375 (fn1->path[strlen(fn1->path)-1] == '/'))) {
377 } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
378 ((strlen(fn1->path) > strlen(fn2->path) &&
379 fn1->path[strlen(fn2->path)] == '/') ||
380 (fn2->path[strlen(fn2->path)-1] == '/'))) {
386 dbprintf(_("removing path %s, it is included in %s\n"),
387 fn2->path, fn1->path);
394 } else if (remove_fn1 == 0) {
400 if(remove_fn1 != 0) {
401 /* fn2->path is always valid */
402 /*@i@*/ dbprintf(_("removing path %s, it is included in %s\n"),
403 /*@i@*/ fn1->tpath, fn2->tpath);
409 amfree(tape_list->files);
410 tape_list->files = fn1;
428 char *npath = g_path_get_basename(path);
429 *dir = g_path_get_dirname(path);
430 if (strcmp(*dir, ".") == 0) {
437 clean_extract_list(void)
441 for (this = extract_list; this != NULL; this = this->next)
442 clean_tape_list(this);
446 int add_to_unlink_list(char *path);
447 int do_unlink_list(void);
448 void free_unlink_list(void);
450 typedef struct s_unlink_list {
452 struct s_unlink_list *next;
454 t_unlink_list *unlink_list = NULL;
463 unlink_list = alloc(SIZEOF(*unlink_list));
464 unlink_list->path = stralloc(path);
465 unlink_list->next = NULL;
467 for (ul = unlink_list; ul != NULL; ul = ul->next) {
468 if (strcmp(ul->path, path) == 0)
471 ul = alloc(SIZEOF(*ul));
472 ul->path = stralloc(path);
473 ul->next = unlink_list;
485 for (ul = unlink_list; ul != NULL; ul = ul->next) {
486 if (unlink(ul->path) < 0) {
487 g_fprintf(stderr,_("Can't unlink %s: %s\n"), ul->path, strerror(errno));
496 free_unlink_list(void)
498 t_unlink_list *ul, *ul1;
500 for (ul = unlink_list; ul != NULL; ul = ul1) {
512 check_file_overwrite(
516 EXTRACT_LIST_ITEM *fn;
517 struct stat stat_buf;
521 for (this = extract_list; this != NULL; this = this->next) {
522 for (fn = this->files; fn != NULL ; fn = fn->next) {
524 /* Check path component of fn->path */
526 path = stralloc2(dir, fn->path);
527 if (path[strlen(path)-1] == '/') {
528 path[strlen(path)-1] = '\0';
531 s = path + strlen(dir) + 1;
532 while((s = strchr(s, '/'))) {
534 if (lstat(path, &stat_buf) == 0) {
535 if(!S_ISDIR(stat_buf.st_mode)) {
536 if (add_to_unlink_list(path)) {
537 g_printf(_("WARNING: %s is not a directory, "
538 "it will be deleted.\n"),
543 else if (errno != ENOENT) {
544 g_printf(_("Can't stat %s: %s\n"), path, strerror(errno));
553 filename = stralloc2(dir, fn->path);
554 if (filename[strlen(filename)-1] == '/') {
555 filename[strlen(filename)-1] = '\0';
558 if (lstat(filename, &stat_buf) == 0) {
559 if(S_ISDIR(stat_buf.st_mode)) {
560 if(!is_empty_dir(filename)) {
561 g_printf(_("WARNING: All existing files in %s "
562 "will be deleted.\n"), filename);
564 } else if(S_ISREG(stat_buf.st_mode)) {
565 g_printf(_("WARNING: Existing file %s will be overwritten\n"),
568 if (add_to_unlink_list(filename)) {
569 g_printf(_("WARNING: Existing entry %s will be deleted\n"),
573 } else if (errno != ENOENT) {
574 g_printf(_("Can't stat %s: %s\n"), filename, strerror(errno));
582 /* returns -1 if error */
583 /* returns 0 on succes */
584 /* returns 1 if already added */
589 EXTRACT_LIST *this, *this1;
590 EXTRACT_LIST_ITEM *that, *curr;
593 ditem_path = stralloc(ditem->path);
594 clean_pathname(ditem_path);
596 for (this = extract_list; this != NULL; this = this->next)
598 /* see if this is the list for the tape */
599 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
601 /* yes, so add to list */
605 if (strcmp(curr->path,ditem_path) == 0) {
611 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
612 that->path = ditem_path;
613 that->tpath = clean_pathname(g_strdup(ditem->tpath));
614 that->next = this->files;
615 this->files = that; /* add at front since easiest */
620 /* so this is the first time we have seen this tape */
621 this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
622 this->tape = stralloc(ditem->tape);
623 this->level = ditem->level;
624 this->fileno = ditem->fileno;
625 this->date = stralloc(ditem->date);
626 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
627 that->path = ditem_path;
628 that->tpath = clean_pathname(g_strdup(ditem->tpath));
632 /* add this in date increasing order */
633 /* because restore must be done in this order */
634 /* add at begining */
635 if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
637 this->next = extract_list;
641 for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
643 /* add in the middle */
644 if(strcmp(this->date,this1->next->date) < 0)
646 this->next = this1->next;
658 /* returns -1 if error */
659 /* returns 0 on deletion */
660 /* returns 1 if not there */
666 EXTRACT_LIST_ITEM *that, *prev;
667 char *ditem_path = NULL;
669 ditem_path = stralloc(ditem->path);
670 clean_pathname(ditem_path);
672 for (this = extract_list; this != NULL; this = this->next)
674 /* see if this is the list for the tape */
675 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
677 /* yes, so find file on list */
679 if (strcmp(that->path, ditem_path) == 0)
682 this->files = that->next;
686 /* if list empty delete it */
687 if (this->files == NULL)
688 delete_tape_list(this);
696 if (strcmp(that->path, ditem_path) == 0)
698 prev->next = that->next;
723 int len = strlen(path1);
724 if (path1[len-1] == '/' && path2[0] == '/') {
725 result = stralloc2(path1, path2+1);
726 } else if (path1[len-1] != '/' && path2[0] != '/') {
727 result = vstralloc(path1, "/", path2, NULL);
729 result = stralloc2(path1, path2);
746 if (disk_path == NULL) {
747 g_printf(_("Must select directory before adding files\n"));
751 uqglob = unquote_string(glob);
752 glob = file_of_path(uqglob, &dir);
754 sdir = merge_path(mount_point, disk_path);
755 result = cd_glob(dir, 0);
759 regex = glob_to_regex(glob);
760 dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
761 if ((s = validate_regexp(regex)) != NULL) {
762 g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
766 * glob_to_regex() anchors the beginning of the pattern with ^,
767 * but we will be tacking it onto the end of the current directory
768 * in add_file, so strip that off. Also, it anchors the end with
769 * $, but we need to match an optional trailing /, so tack that on
772 regex_path = stralloc(regex + 1);
773 regex_path[strlen(regex_path) - 1] = '\0';
774 strappend(regex_path, "[/]*$");
775 add_file(uqglob, regex_path);
779 set_directory(sdir, 0);
799 if (disk_path == NULL) {
800 g_printf(_("Must select directory before adding files\n"));
804 uqregex = unquote_string(regex);
805 newregex = file_of_path(uqregex, &dir);
807 sdir = merge_path(mount_point, disk_path);
808 result = cd_regex(dir, 0);
813 if ((s = validate_regexp(newregex)) != NULL) {
814 g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
817 add_file(uqregex, newregex);
820 set_directory(sdir, 0);
833 DIR_ITEM *ditem, lditem;
834 char *tpath_on_disk = NULL;
839 char *dir_undo, dir_undo_ch = '\0';
840 char *ditem_path = NULL;
841 char *qditem_path = NULL;
844 char *s, *fp, *quoted;
849 if (disk_path == NULL) {
850 g_printf(_("Must select directory before adding files\n"));
853 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
855 dbprintf(_("add_file: Looking for \"%s\"\n"), regex);
857 if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */
860 else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */
861 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
863 /* remove "/" at end of path */
864 j = (ssize_t)(strlen(regex) - 1);
865 while(j >= 0 && regex[j] == '/')
869 /* convert path (assumed in cwd) to one on disk */
870 if (strcmp(disk_path, "/") == 0) {
872 /* No mods needed if already starts with '/' */
873 tpath_on_disk = g_strdup(regex);
876 tpath_on_disk = g_strconcat("/", regex, NULL);
879 char *clean_disk_tpath = clean_regex(disk_tpath, 0);
880 tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
881 amfree(clean_disk_tpath);
884 dbprintf(_("add_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
885 regex, tpath_on_disk);
889 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
892 quoted = quote_string(ditem->tpath);
893 dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted);
895 if (match(tpath_on_disk, ditem->tpath))
898 j = (ssize_t)strlen(ditem->tpath);
899 if((j > 0 && ditem->tpath[j-1] == '/')
900 || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
901 { /* It is a directory */
902 ditem_path = newstralloc(ditem_path, ditem->path);
903 clean_pathname(ditem_path);
905 qditem_path = quote_string(ditem_path);
906 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
908 if(send_command(cmd) == -1) {
911 amfree(tpath_on_disk);
917 if ((i = get_reply_line()) == -1) {
919 amfree(tpath_on_disk);
922 if(i==0) { /* assume something wrong */
924 amfree(tpath_on_disk);
932 g_free(lditem.tpath);
933 lditem.path = g_strdup(ditem->path);
934 lditem.tpath = g_strdup(ditem->tpath);
935 /* skip the last line -- duplicate of the preamble */
937 while ((i = get_reply_line()) != 0) {
940 amfree(tpath_on_disk);
945 if(dir_undo) *dir_undo = dir_undo_ch;
947 cmd = stralloc(l); /* save for error report */
949 continue; /* throw the rest of the lines away */
952 if (!server_happy()) {
958 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
959 err = _("bad reply: not 201-");
964 skip_whitespace(s, ch);
966 err = _("bad reply: missing date field");
970 skip_non_whitespace(s, ch);
972 lditem.date = newstralloc(lditem.date, fp);
975 skip_whitespace(s, ch);
976 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
977 err = _("bad reply: cannot parse level field");
982 skip_whitespace(s, ch);
984 err = _("bad reply: missing tape field");
988 skip_quoted_string(s, ch);
991 lditem.tape = unquote_string(fp);
994 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
995 long long fileno_ = (long long)0;
996 skip_whitespace(s, ch);
998 sscanf(s - 1, "%lld", &fileno_) != 1) {
999 err = _("bad reply: cannot parse fileno field");
1002 lditem.fileno = (off_t)fileno_;
1003 skip_integer(s, ch);
1006 skip_whitespace(s, ch);
1008 err = _("bad reply: missing directory field");
1011 skip_quoted_string(s, ch);
1013 dir_undo_ch = *dir_undo;
1016 switch(add_extract_item(&lditem)) {
1018 g_printf(_("System error\n"));
1019 dbprintf(_("add_file: (Failed) System error\n"));
1023 quoted = quote_string(lditem.tpath);
1024 g_printf(_("Added dir %s at date %s\n"),
1025 quoted, lditem.date);
1026 dbprintf(_("add_file: (Successful) Added dir %s at date %s\n"),
1027 quoted, lditem.date);
1036 if(!server_happy()) {
1043 } else if(added == 0) {
1044 quoted = quote_string(ditem_path);
1045 g_printf(_("dir %s already added\n"), quoted);
1046 dbprintf(_("add_file: dir %s already added\n"), quoted);
1050 else /* It is a file */
1052 switch(add_extract_item(ditem)) {
1054 g_printf(_("System error\n"));
1055 dbprintf(_("add_file: (Failed) System error\n"));
1059 quoted = quote_string(ditem->tpath);
1060 g_printf(_("Added file %s\n"), quoted);
1061 dbprintf(_("add_file: (Successful) Added %s\n"), quoted);
1066 quoted = quote_string(ditem->tpath);
1067 g_printf(_("File %s already added\n"), quoted);
1068 dbprintf(_("add_file: file %s already added\n"), quoted);
1077 amfree(tpath_on_disk);
1079 amfree(lditem.path);
1080 amfree(lditem.tpath);
1081 amfree(lditem.date);
1082 amfree(lditem.tape);
1085 quoted = quote_string(path);
1086 g_printf(_("File %s doesn't exist in directory\n"), quoted);
1087 dbprintf(_("add_file: (Failed) File %s doesn't exist in directory\n"),
1107 if (disk_path == NULL) {
1108 g_printf(_("Must select directory before adding files\n"));
1112 uqglob = unquote_string(glob);
1113 newglob = file_of_path(uqglob, &dir);
1115 sdir = merge_path(mount_point, disk_path);
1116 result = cd_glob(dir, 0);
1120 regex = glob_to_regex(newglob);
1121 dbprintf(_("delete_glob (%s) -> %s\n"), newglob, regex);
1122 if ((s = validate_regexp(regex)) != NULL) {
1123 g_printf(_("\"%s\" is not a valid shell wildcard pattern: "),
1128 * glob_to_regex() anchors the beginning of the pattern with ^,
1129 * but we will be tacking it onto the end of the current directory
1130 * in add_file, so strip that off. Also, it anchors the end with
1131 * $, but we need to match an optional trailing /, so tack that on
1134 regex_path = stralloc(regex + 1);
1135 regex_path[strlen(regex_path) - 1] = '\0';
1136 strappend(regex_path, "[/]*$");
1137 delete_file(uqglob, regex_path);
1141 set_directory(sdir, 0);
1161 if (disk_path == NULL) {
1162 g_printf(_("Must select directory before adding files\n"));
1166 uqregex = unquote_string(regex);
1167 newregex = file_of_path(uqregex, &dir);
1169 sdir = merge_path(mount_point, disk_path);
1170 result = cd_regex(dir, 0);
1175 if ((s = validate_regexp(newregex)) != NULL) {
1176 g_printf(_("\"%s\" is not a valid regular expression: "), newregex);
1179 delete_file(newregex, regex);
1182 set_directory(sdir, 0);
1195 DIR_ITEM *ditem, lditem;
1196 char *tpath_on_disk = NULL;
1202 char *tape, *tape_undo, tape_undo_ch = '\0';
1203 char *dir_undo, dir_undo_ch = '\0';
1205 char *ditem_path = NULL;
1206 char *ditem_tpath = NULL;
1215 if (disk_path == NULL) {
1216 g_printf(_("Must select directory before deleting files\n"));
1219 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
1221 dbprintf(_("delete_file: Looking for \"%s\"\n"), tpath);
1223 if (strcmp(regex, "[^/]*[/]*$") == 0) {
1224 /* Looking for * find everything but single . */
1225 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
1227 /* remove "/" at end of path */
1228 j = (ssize_t)(strlen(regex) - 1);
1229 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
1232 /* convert path (assumed in cwd) to one on disk */
1233 if (strcmp(disk_path, "/") == 0) {
1234 if (*regex == '/') {
1235 if (strcmp(regex, "/[/]*$") == 0) {
1236 /* We want "/" to match the directory itself: "/." */
1237 tpath_on_disk = stralloc("/\\.[/]*$");
1239 /* No mods needed if already starts with '/' */
1240 tpath_on_disk = stralloc(regex);
1244 tpath_on_disk = g_strconcat("/", regex, NULL);
1247 char *clean_disk_tpath = clean_regex(disk_tpath, 0);
1248 tpath_on_disk = g_strjoin(NULL, clean_disk_tpath, "/", regex, NULL);
1249 amfree(clean_disk_tpath);
1252 dbprintf(_("delete_file: Converted path=\"%s\" to tpath_on_disk=\"%s\"\n"),
1253 regex, tpath_on_disk);
1255 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
1257 quoted = quote_string(ditem->tpath);
1258 dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted);
1260 if (match(tpath_on_disk, ditem->tpath))
1263 j = (ssize_t)strlen(ditem->tpath);
1264 if((j > 0 && ditem->tpath[j-1] == '/')
1265 || (j > 1 && ditem->tpath[j-2] == '/' && ditem->tpath[j-1] == '.'))
1266 { /* It is a directory */
1267 ditem_path = newstralloc(ditem_path, ditem->path);
1268 ditem_tpath = newstralloc(ditem_tpath, ditem->tpath);
1269 clean_pathname(ditem_path);
1270 clean_pathname(ditem_tpath);
1272 qditem_path = quote_string(ditem_path);
1273 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
1274 amfree(qditem_path);
1275 if(send_command(cmd) == -1) {
1278 amfree(tpath_on_disk);
1283 if ((i = get_reply_line()) == -1) {
1285 amfree(tpath_on_disk);
1288 if(i==0) /* assume something wrong */
1291 amfree(tpath_on_disk);
1293 g_printf("%s\n", l);
1297 lditem.path = newstralloc(lditem.path, ditem->path);
1298 lditem.tpath = newstralloc(lditem.tpath, ditem->tpath);
1300 tape_undo = dir_undo = NULL;
1301 /* skip the last line -- duplicate of the preamble */
1302 while ((i = get_reply_line()) != 0)
1306 amfree(tpath_on_disk);
1311 if(tape_undo) *tape_undo = tape_undo_ch;
1312 if(dir_undo) *dir_undo = dir_undo_ch;
1313 tape_undo = dir_undo = NULL;
1314 cmd = stralloc(l); /* save for the error report */
1316 continue; /* throw the rest of the lines away */
1319 if (!server_happy()) {
1325 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1326 err = _("bad reply: not 201-");
1331 skip_whitespace(s, ch);
1333 err = _("bad reply: missing date field");
1337 skip_non_whitespace(s, ch);
1340 skip_whitespace(s, ch);
1341 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1342 err = _("bad reply: cannot parse level field");
1345 skip_integer(s, ch);
1347 skip_whitespace(s, ch);
1349 err = _("bad reply: missing tape field");
1353 skip_non_whitespace(s, ch);
1355 tape_undo_ch = *tape_undo;
1358 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1359 long long fileno_ = (long long)0;
1360 skip_whitespace(s, ch);
1362 sscanf(s - 1, "%lld", &fileno_) != 1) {
1363 err = _("bad reply: cannot parse fileno field");
1366 skip_integer(s, ch);
1369 skip_whitespace(s, ch);
1371 err = _("bad reply: missing directory field");
1374 skip_non_whitespace(s, ch);
1376 dir_undo_ch = *dir_undo;
1379 lditem.date = newstralloc(lditem.date, date);
1381 g_free(lditem.tape);
1382 lditem.tape = unquote_string(tape);
1383 switch(delete_extract_item(&lditem)) {
1385 g_printf(_("System error\n"));
1386 dbprintf(_("delete_file: (Failed) System error\n"));
1389 g_printf(_("Deleted dir %s at date %s\n"), ditem_tpath, date);
1390 dbprintf(_("delete_file: (Successful) Deleted dir %s at date %s\n"),
1398 if(!server_happy()) {
1405 } else if(deleted == 0) {
1406 g_printf(_("Warning - dir '%s' not on tape list\n"),
1408 dbprintf(_("delete_file: dir '%s' not on tape list\n"),
1414 switch(delete_extract_item(ditem)) {
1416 g_printf(_("System error\n"));
1417 dbprintf(_("delete_file: (Failed) System error\n"));
1420 g_printf(_("Deleted %s\n"), ditem->tpath);
1421 dbprintf(_("delete_file: (Successful) Deleted %s\n"),
1425 g_printf(_("Warning - file '%s' not on tape list\n"),
1427 dbprintf(_("delete_file: file '%s' not on tape list\n"),
1436 amfree(ditem_tpath);
1437 amfree(tpath_on_disk);
1440 g_printf(_("File %s doesn't exist in directory\n"), tpath);
1441 dbprintf(_("delete_file: (Failed) File %s doesn't exist in directory\n"),
1447 /* print extract list into file. If NULL ptr passed print to screen */
1449 display_extract_list(
1453 EXTRACT_LIST_ITEM *that;
1456 char *pager_command;
1461 if ((pager = getenv("PAGER")) == NULL)
1466 * Set up the pager command so if the pager is terminated, we do
1467 * not get a SIGPIPE back.
1469 pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1470 if ((fp = popen(pager_command, "w")) == NULL)
1472 g_printf(_("Warning - can't pipe through %s\n"), pager);
1475 amfree(pager_command);
1479 uqfile = unquote_string(file);
1480 if ((fp = fopen(uqfile, "w")) == NULL)
1482 g_printf(_("Can't open file %s to print extract list into\n"), file);
1489 for (this = extract_list; this != NULL; this = this->next)
1491 g_fprintf(fp, _("TAPE %s LEVEL %d DATE %s\n"),
1492 this->tape, this->level, this->date);
1493 for (that = this->files; that != NULL; that = that->next)
1494 g_fprintf(fp, "\t%s\n", that->tpath);
1500 g_printf(_("Extract list written to file %s\n"), file);
1511 struct dirent *entry;
1514 if((dir = opendir(fname)) == NULL)
1518 while(!gotentry && (entry = readdir(dir)) != NULL) {
1519 gotentry = !is_dot_or_dotdot(entry->d_name);
1527 /* returns 0 if extract list empty and 1 if it isn't */
1529 is_extract_list_nonempty(void)
1531 return (extract_list != NULL);
1535 /* prints continue prompt and waits for response,
1536 returns 0 if don't, non-0 if do */
1553 prompt = _("New device name [?]: ");
1554 } else if (allow_tape && allow_skip) {
1555 prompt = _("Continue [?/Y/n/s/d]? ");
1556 } else if (allow_tape && !allow_skip) {
1557 prompt = _("Continue [?/Y/n/d]? ");
1558 } else if (allow_retry) {
1559 prompt = _("Continue [?/Y/n/r]? ");
1561 prompt = _("Continue [?/Y/n]? ");
1563 fputs(prompt, stdout);
1564 fflush(stdout); fflush(stderr);
1566 if ((line = agets(stdin)) == NULL) {
1576 dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line);
1579 while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) {
1580 (void)ch; /* Quiet empty loop compiler warning */
1584 g_printf(_("Enter a new device name or \"default\"\n"));
1586 g_printf(_("Enter \"y\"es to continue, \"n\"o to stop"));
1588 g_printf(_(", \"s\"kip this tape"));
1591 g_printf(_(" or \"r\"etry this tape"));
1594 g_printf(_(" or \"d\" to change to a new device"));
1598 } else if (get_device) {
1599 char *tmp = stralloc(tape_server_name);
1601 if (strncmp_const(s - 1, "default") == 0) {
1602 set_device(tmp, NULL); /* default device, existing host */
1603 } else if (s[-1] != '\0') {
1604 set_device(tmp, s - 1); /* specified device, existing host */
1606 g_printf(_("No change.\n"));
1612 } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1614 } else if (allow_tape && (ch == 'D' || ch == 'd' || ch == 'T' || ch == 't')) {
1615 get_device = 1; /* ('T' and 't' are for backward-compatibility) */
1616 } else if (ch == 'N' || ch == 'n') {
1618 } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1620 } else if (allow_skip && (ch == 'S' || ch == 's')) {
1631 send_to_tape_server(
1632 security_stream_t * stream,
1635 char *msg = stralloc2(cmd, "\r\n");
1637 g_debug("send_to_tape_server: %s\n", cmd);
1638 if (security_stream_write(stream, msg, strlen(msg)) < 0)
1640 error(_("Error writing to tape server"));
1648 /* start up connection to tape server and set commands to initiate
1649 transfer of dump image.
1650 Return tape server socket on success, -1 on error. */
1652 extract_files_setup(
1656 char *disk_regex = NULL;
1657 char *host_regex = NULL;
1658 char *clean_datestamp, *ch, *ch1;
1663 amidxtaped_secdrv = security_getdriver(authopt);
1664 if (amidxtaped_secdrv == NULL) {
1665 error(_("no '%s' security driver available for host '%s'"),
1666 authopt, tape_server_name);
1669 /* We assume that amidxtaped support fe_amidxtaped_options_features */
1670 /* and fe_amidxtaped_options_auth */
1671 /* We should send a noop to really know */
1672 req = vstralloc("SERVICE amidxtaped\n",
1673 "OPTIONS ", "features=", our_features_string, ";",
1674 "auth=", authopt, ";",
1676 protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1677 generic_client_get_security_conf, req, STARTUP_TIMEOUT,
1678 amidxtaped_response, &response_error);
1681 if(response_error != 0) {
1685 disk_regex = make_exact_disk_expression(disk_name);
1686 host_regex = make_exact_host_expression(dump_hostname);
1688 clean_datestamp = stralloc(dump_datestamp);
1689 for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1696 /* push our feature list off to the tape server */
1697 /* XXX assumes that index server and tape server are equivalent, ew */
1699 if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1700 tt = newstralloc2(tt, "FEATURES=", our_features_string);
1701 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1702 get_amidxtaped_line();
1703 if (!amidxtaped_line) {
1704 g_fprintf(stderr, _("amrecover - amidxtaped closed the connection\n"));
1708 amfree(clean_datestamp);
1710 } else if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
1711 tapesrv_features = am_string_to_feature(amidxtaped_line+9);
1713 g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n"));
1717 amfree(clean_datestamp);
1721 *tapesrv_features = *indexsrv_features;
1725 if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1726 am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1727 am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1728 am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1729 am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1731 if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1732 tt = newstralloc2(tt, "CONFIG=", get_config_name());
1733 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1735 if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1736 label && label[0] != '/') {
1737 tt = newstralloc2(tt,"LABEL=",label);
1738 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1740 if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1742 g_snprintf(v_fsf, 99, "%lld", (long long)fsf);
1743 tt = newstralloc2(tt, "FSF=",v_fsf);
1744 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1746 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
1747 tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1748 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1749 tt = newstralloc2(tt, "HOST=", host_regex);
1750 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1751 tt = newstralloc2(tt, "DISK=", disk_regex);
1752 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1753 tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1754 send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1755 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
1758 else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1759 /* send to the tape server what tape file we want */
1768 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6");
1769 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h");
1770 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p");
1771 send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name);
1772 send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex);
1773 send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex);
1774 send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp);
1776 dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"),
1777 dump_device_name, host_regex, disk_regex, clean_datestamp);
1782 amfree(clean_datestamp);
1789 * Reads the first block of a tape file.
1800 bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1801 if(bytes_read < 0) {
1802 error(_("error reading header (%s), check amidxtaped.*.debug on server"),
1807 if((size_t)bytes_read < buflen) {
1808 g_fprintf(stderr, plural(_("%s: short block %d byte\n"),
1809 _("%s: short block %d bytes\n"), bytes_read),
1810 get_pname(), (int)bytes_read);
1811 print_header(stdout, file);
1812 error(_("Can't read file header"));
1816 /* bytes_read == buflen */
1817 parse_file_header(buffer, file, (size_t)bytes_read);
1831 extract_files_child(
1832 ctl_data_t *ctl_data)
1837 GPtrArray *argv_ptr = g_ptr_array_new();
1839 EXTRACT_LIST_ITEM *fn;
1840 enum dumptypes dumptype = IS_UNKNOWN;
1843 guint passwd_field = 999999999;
1845 char *domain = NULL, *smbpass = NULL;
1848 /* code executed by child to do extraction */
1851 /* make in_fd be our stdin */
1852 if (dup2(ctl_data->child_pipe[0], STDIN_FILENO) == -1)
1854 error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
1858 if(ctl_data->file.type != F_DUMPFILE) {
1859 dump_dumpfile_t(&ctl_data->file);
1860 error(_("bad header"));
1864 if (ctl_data->file.program[0] != '\0') {
1865 if (strcmp(ctl_data->file.program, "APPLICATION") == 0)
1866 dumptype = IS_APPLICATION_API;
1868 if (strcmp(ctl_data->file.program, GNUTAR) == 0)
1869 dumptype = IS_GNUTAR;
1872 if (dumptype == IS_UNKNOWN) {
1873 len_program = strlen(ctl_data->file.program);
1874 if(len_program >= 3 &&
1875 strcmp(&ctl_data->file.program[len_program-3],"tar") == 0)
1880 if (dumptype == IS_UNKNOWN && strcmp(ctl_data->file.program, SAMBA_CLIENT) ==0) {
1881 if (samba_extract_method == SAMBA_TAR)
1882 dumptype = IS_SAMBA_TAR;
1884 dumptype = IS_SAMBA;
1889 /* form the arguments to restore */
1890 files_off_tape = length_of_tape_list(ctl_data->elist);
1894 g_ptr_array_add(argv_ptr, stralloc("smbclient"));
1895 smbpass = findpass(ctl_data->file.disk, &domain);
1897 g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
1898 g_ptr_array_add(argv_ptr, stralloc("-U"));
1899 passwd_field = argv_ptr->len;
1900 g_ptr_array_add(argv_ptr, stralloc(smbpass));
1902 g_ptr_array_add(argv_ptr, stralloc("-W"));
1903 g_ptr_array_add(argv_ptr, stralloc(domain));
1906 g_ptr_array_add(argv_ptr, stralloc("-d0"));
1907 g_ptr_array_add(argv_ptr, stralloc("-Tx"));
1908 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1913 g_ptr_array_add(argv_ptr, stralloc("tar"));
1914 /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1915 g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1916 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1917 g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1918 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1921 g_ptr_array_add(argv_ptr, stralloc("tar"));
1922 g_ptr_array_add(argv_ptr, stralloc("-xpvf"));
1923 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1927 g_ptr_array_add(argv_ptr, stralloc("restore"));
1929 g_ptr_array_add(argv_ptr, stralloc("-xB"));
1931 #if defined(XFSDUMP)
1932 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
1933 g_ptr_array_add(argv_ptr, stralloc("-v"));
1934 g_ptr_array_add(argv_ptr, stralloc("silent"));
1938 if (strcmp(ctl_data->file.program, VDUMP) == 0) {
1939 g_ptr_array_add(argv_ptr, stralloc("xf"));
1940 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1944 g_ptr_array_add(argv_ptr, stralloc("xbf"));
1945 g_ptr_array_add(argv_ptr, stralloc("2")); /* read in units of 1K */
1946 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1950 case IS_APPLICATION_API:
1951 g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.application));
1952 g_ptr_array_add(argv_ptr, stralloc("restore"));
1953 g_ptr_array_add(argv_ptr, stralloc("--config"));
1954 g_ptr_array_add(argv_ptr, stralloc(get_config_name()));
1955 g_ptr_array_add(argv_ptr, stralloc("--disk"));
1956 g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk));
1957 if (dump_dle && dump_dle->device) {
1958 g_ptr_array_add(argv_ptr, stralloc("--device"));
1959 g_ptr_array_add(argv_ptr, stralloc(dump_dle->device));
1961 if (ctl_data->data_path == DATA_PATH_DIRECTTCP) {
1962 g_ptr_array_add(argv_ptr, stralloc("--data-path"));
1963 g_ptr_array_add(argv_ptr, stralloc("DIRECTTCP"));
1964 g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
1965 g_ptr_array_add(argv_ptr, stralloc(ctl_data->addrs));
1967 if (ctl_data->bsu && ctl_data->bsu->smb_recover_mode &&
1968 samba_extract_method == SAMBA_SMBCLIENT){
1969 g_ptr_array_add(argv_ptr, stralloc("--recover-mode"));
1970 g_ptr_array_add(argv_ptr, stralloc("smb"));
1972 g_ptr_array_add(argv_ptr, stralloc("--level"));
1973 g_ptr_array_add(argv_ptr, g_strdup_printf("%d", ctl_data->elist->level));
1978 merge_properties(dump_dle, NULL, dump_dle->application_property,
1980 application_property_add_to_argv(argv_ptr, dump_dle, NULL,
1982 for (scriptlist = dump_dle->scriptlist; scriptlist != NULL;
1983 scriptlist = scriptlist->next) {
1984 script = (script_t *)scriptlist->data;
1985 if (script->result && script->result->proplist) {
1986 property_add_to_argv(argv_ptr, script->result->proplist);
1990 } else if (proplist) {
1991 property_add_to_argv(argv_ptr, proplist);
1996 for (i = 0, fn = ctl_data->elist->files; i < files_off_tape;
2000 case IS_APPLICATION_API:
2005 if (strcmp(fn->path, "/") == 0)
2006 g_ptr_array_add(argv_ptr, stralloc("."));
2008 g_ptr_array_add(argv_ptr, stralloc2(".", fn->path));
2012 #if defined(XFSDUMP)
2013 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2015 * xfsrestore needs a -s option before each file to be
2016 * restored, and also wants them to be relative paths.
2018 g_ptr_array_add(argv_ptr, stralloc("-s"));
2019 g_ptr_array_add(argv_ptr, stralloc(fn->path + 1));
2023 g_ptr_array_add(argv_ptr, stralloc(fn->path));
2028 #if defined(XFSDUMP)
2029 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2030 g_ptr_array_add(argv_ptr, stralloc("-"));
2031 g_ptr_array_add(argv_ptr, stralloc("."));
2034 g_ptr_array_add(argv_ptr, NULL);
2039 cmd = stralloc(SAMBA_CLIENT);
2042 /* fall through to ... */
2048 g_fprintf(stderr, _("warning: GNUTAR program not available.\n"));
2049 cmd = stralloc("tar");
2051 cmd = stralloc(GNUTAR);
2058 if (strcmp(ctl_data->file.program, DUMP) == 0) {
2059 cmd = stralloc(RESTORE);
2063 if (strcmp(ctl_data->file.program, VDUMP) == 0) {
2064 cmd = stralloc(VRESTORE);
2068 if (strcmp(ctl_data->file.program, VXDUMP) == 0) {
2069 cmd = stralloc(VXRESTORE);
2072 #if defined(XFSDUMP)
2073 if (strcmp(ctl_data->file.program, XFSDUMP) == 0) {
2074 cmd = stralloc(XFSRESTORE);
2078 g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
2079 ctl_data->file.program);
2080 cmd = stralloc("restore");
2083 case IS_APPLICATION_API:
2084 cmd = vstralloc(APPLICATION_DIR, "/", ctl_data->file.application, NULL);
2088 dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
2089 for (j = 0; j < argv_ptr->len - 1; j++) {
2090 if (j == passwd_field)
2091 dbprintf("\tXXXXX\n");
2093 dbprintf(_("\t%s\n"), (char *)g_ptr_array_index(argv_ptr, j));
2096 (void)execv(cmd, (char **)argv_ptr->pdata);
2097 /* only get here if exec failed */
2099 g_ptr_array_free_full(argv_ptr);
2101 perror(_("amrecover couldn't exec"));
2102 g_fprintf(stderr, _(" problem executing %s\n"), cmd);
2110 * Interpose something between the process writing out the dump (writing it to
2111 * some extraction program, really) and the socket from which we're reading, so
2112 * that we can do things like prompt for human interaction for multiple tapes.
2115 writer_intermediary(
2116 EXTRACT_LIST * elist)
2118 ctl_data_t ctl_data;
2119 amwait_t extractor_status;
2121 ctl_data.header_done = 0;
2122 ctl_data.child_pipe[0] = -1;
2123 ctl_data.child_pipe[1] = -1;
2125 ctl_data.elist = elist;
2126 fh_init(&ctl_data.file);
2127 ctl_data.data_path = DATA_PATH_AMANDA;
2128 ctl_data.addrs = NULL;
2129 ctl_data.bsu = NULL;
2130 ctl_data.bytes_read = 0;
2133 security_stream_read(amidxtaped_streams[DATAFD].fd,
2134 read_amidxtaped_data, &ctl_data);
2136 while(get_amidxtaped_line() >= 0) {
2137 char desired_tape[MAX_TAPE_LABEL_BUF];
2138 g_debug("get amidxtaped line: %s", amidxtaped_line);
2140 /* if prompted for a tape, relay said prompt to the user */
2141 if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
2143 g_printf(_("Load tape %s now\n"), desired_tape);
2144 dbprintf(_("Requesting tape %s from user\n"), desired_tape);
2145 done = okay_to_continue(am_has_feature(indexsrv_features,
2146 fe_amrecover_feedme_tape),
2149 if (am_has_feature(indexsrv_features,
2150 fe_amrecover_feedme_tape)) {
2151 char *reply = stralloc2("TAPE ", tape_device_name);
2152 send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply);
2155 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2158 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2161 } else if (strncmp_const(amidxtaped_line, "USE-DATAPATH ") == 0) {
2162 if (strncmp_const(amidxtaped_line+13, "AMANDA") == 0) {
2163 ctl_data.data_path = DATA_PATH_AMANDA;
2164 g_debug("Using AMANDA data-path");
2165 } else if (strncmp_const(amidxtaped_line+13, "DIRECT-TCP") == 0) {
2166 ctl_data.data_path = DATA_PATH_DIRECTTCP;
2167 ctl_data.addrs = stralloc(amidxtaped_line+24);
2168 g_debug("Using DIRECT-TCP data-path with %s", ctl_data.addrs);
2170 start_processing_data(&ctl_data);
2171 } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
2172 g_printf("%s\n",&amidxtaped_line[8]);
2174 g_fprintf(stderr, _("Strange message from tape server: %s"),
2180 /* CTL might be close before DATA */
2182 dumpfile_free_data(&ctl_data.file);
2183 amfree(ctl_data.addrs);
2184 amfree(ctl_data.bsu);
2185 if (ctl_data.child_pipe[1] != -1)
2186 aclose(ctl_data.child_pipe[1]);
2188 if (ctl_data.header_done == 0) {
2189 g_printf(_("Got no header and data from server, check in amidxtaped.*.debug and amandad.*.debug files on server\n"));
2192 if (ctl_data.pid != -1) {
2193 waitpid(ctl_data.pid, &extractor_status, 0);
2194 if(WEXITSTATUS(extractor_status) != 0){
2195 int ret = WEXITSTATUS(extractor_status);
2196 if(ret == 255) ret = -1;
2197 g_printf(_("Extractor child exited with status %d\n"), ret);
2201 g_debug("bytes read: %jd", (intmax_t)ctl_data.bytes_read);
2205 /* exec restore to do the actual restoration */
2207 /* does the actual extraction of files */
2209 * The original design had the dump image being returned exactly as it
2210 * appears on the tape, and this routine getting from the index server
2211 * whether or not it is compressed, on the assumption that the tape
2212 * server may not know how to uncompress it. But
2213 * - Amrestore can't do that. It returns either compressed or uncompressed
2214 * (always). Amrestore assumes it can uncompress files. It is thus a good
2215 * idea to run the tape server on a machine with gzip.
2216 * - The information about compression in the disklist is really only
2217 * for future dumps. It is possible to change compression on a drive
2218 * so the information in the disklist may not necessarily relate to
2219 * the dump image on the tape.
2220 * Consequently the design was changed to assuming that amrestore can
2221 * uncompress any dump image and have it return an uncompressed file
2227 EXTRACT_LIST *elist;
2231 tapelist_t *tlist = NULL, *a_tlist;
2232 g_option_t g_options;
2233 levellist_t all_level = NULL;
2236 if (!is_extract_list_nonempty())
2238 g_printf(_("Extract list empty - No files to extract!\n"));
2242 clean_extract_list();
2244 /* get tape device name from index server if none specified */
2245 if (tape_server_name == NULL) {
2246 tape_server_name = newstralloc(tape_server_name, server_name);
2248 if (tape_device_name == NULL) {
2249 if (send_command("TAPE") == -1)
2251 if (get_reply_line() == -1)
2254 if (!server_happy())
2256 g_printf("%s\n", l);
2259 /* skip reply number */
2260 tape_device_name = newstralloc(tape_device_name, l+4);
2263 if (strcmp(tape_device_name, "/dev/null") == 0)
2265 g_printf(_("amrecover: warning: using %s as the tape device will not work\n"),
2270 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2271 if(elist->tape[0]!='/') {
2273 g_printf(_("\nExtracting files using tape drive %s on host %s.\n"),
2274 tape_device_name, tape_server_name);
2275 g_printf(_("The following tapes are needed:"));
2280 tlist = unmarshal_tapelist_str(elist->tape);
2281 for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
2282 g_printf(" %s", a_tlist->label);
2284 free_tapelist(tlist);
2288 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2289 if(elist->tape[0]=='/') {
2291 g_printf(_("\nExtracting files from holding disk on host %s.\n"),
2293 g_printf(_("The following files are needed:"));
2298 tlist = unmarshal_tapelist_str(elist->tape);
2299 for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2300 g_printf(" %s", a_tlist->label);
2302 free_tapelist(tlist);
2307 g_options.config = get_config_name();
2308 g_options.hostname = dump_hostname;
2309 for (elist = first_tape_list(); elist != NULL;
2310 elist = next_tape_list(elist)) {
2311 am_level_t *level = g_new0(am_level_t, 1);
2312 level->level = elist->level;
2313 all_level = g_slist_append(all_level, level);
2316 slist_free_full(dump_dle->levellist, g_free);
2317 dump_dle->levellist = all_level;
2318 run_client_scripts(EXECUTE_ON_PRE_RECOVER, &g_options, dump_dle,
2320 dump_dle->levellist = NULL;
2323 while ((elist = first_tape_list()) != NULL)
2325 if(elist->tape[0]=='/') {
2326 dump_device_name = newstralloc(dump_device_name, elist->tape);
2327 g_printf(_("Extracting from file "));
2328 tlist = unmarshal_tapelist_str(dump_device_name);
2329 for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2330 g_printf(" %s", a_tlist->label);
2332 free_tapelist(tlist);
2335 g_printf(_("Extracting files using tape drive %s on host %s.\n"),
2336 tape_device_name, tape_server_name);
2337 tlist = unmarshal_tapelist_str(elist->tape);
2338 g_printf(_("Load tape %s now\n"), tlist->label);
2339 dbprintf(_("Requesting tape %s from user\n"), tlist->label);
2340 free_tapelist(tlist);
2341 otc = okay_to_continue(1,1,0);
2344 else if (otc == SKIP_TAPE) {
2345 delete_tape_list(elist); /* skip this tape */
2348 dump_device_name = newstralloc(dump_device_name, tape_device_name);
2350 dump_datestamp = newstralloc(dump_datestamp, elist->date);
2352 if (last_level != -1 && dump_dle) {
2355 level = g_new0(am_level_t, 1);
2356 level->level = last_level;
2357 dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2359 level = g_new0(am_level_t, 1);
2360 level->level = elist->level;
2361 dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2362 run_client_scripts(EXECUTE_ON_INTER_LEVEL_RECOVER, &g_options,
2364 slist_free_full(dump_dle->levellist, g_free);
2365 dump_dle->levellist = NULL;
2368 /* connect to the tape handler daemon on the tape drive server */
2369 if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2371 g_fprintf(stderr, _("amrecover - can't talk to tape server: %s\n"),
2378 level = g_new0(am_level_t, 1);
2379 level->level = elist->level;
2380 dump_dle->levellist = g_slist_append(dump_dle->levellist, level);
2381 run_client_scripts(EXECUTE_ON_PRE_LEVEL_RECOVER, &g_options,
2384 last_level = elist->level;
2386 /* if the server have fe_amrecover_feedme_tape, it has asked for
2387 * the tape itself, even if the restore didn't succeed, we should
2390 if(writer_intermediary(elist) == 0 ||
2391 am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
2392 delete_tape_list(elist); /* tape done so delete from list */
2394 am_release_feature_set(tapesrv_features);
2398 run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
2400 slist_free_full(dump_dle->levellist, g_free);
2401 dump_dle->levellist = NULL;
2405 dump_dle->levellist = all_level;
2406 run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
2408 slist_free_full(dump_dle->levellist, g_free);
2410 dump_dle->levellist = NULL;
2415 amidxtaped_response(
2418 security_handle_t * sech)
2420 int ports[NSTREAMS], *response_error = datap, i;
2425 assert(response_error != NULL);
2426 assert(sech != NULL);
2427 memset(ports, -1, SIZEOF(ports));
2430 errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech));
2431 *response_error = 1;
2434 security_close_connection(sech, dump_hostname);
2436 if (pkt->type == P_NAK) {
2437 #if defined(PACKET_DEBUG)
2438 g_fprintf(stderr, _("got nak response:\n----\n%s\n----\n\n"), pkt->body);
2441 tok = strtok(pkt->body, " ");
2442 if (tok == NULL || strcmp(tok, "ERROR") != 0)
2445 tok = strtok(NULL, "\n");
2447 errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2448 *response_error = 1;
2451 errstr = newstralloc(errstr, "request NAK");
2452 *response_error = 2;
2457 if (pkt->type != P_REP) {
2458 errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
2459 pkt_type2str(pkt->type), pkt->body);
2460 *response_error = 1;
2464 #if defined(PACKET_DEBUG)
2465 g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
2468 for(i = 0; i < NSTREAMS; i++) {
2470 amidxtaped_streams[i].fd = NULL;
2474 while((tok = strtok(p, " \n")) != NULL) {
2478 * Error response packets have "ERROR" followed by the error message
2479 * followed by a newline.
2481 if (strcmp(tok, "ERROR") == 0) {
2482 tok = strtok(NULL, "\n");
2484 tok = _("[bogus error packet]");
2485 errstr = newstralloc(errstr, tok);
2486 *response_error = 2;
2492 * Regular packets have CONNECT followed by three streams
2494 if (strcmp(tok, "CONNECT") == 0) {
2497 * Parse the three stream specifiers out of the packet.
2499 for (i = 0; i < NSTREAMS; i++) {
2500 tok = strtok(NULL, " ");
2501 if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) {
2502 extra = vstrallocf(_("CONNECT token is \"%s\": expected \"%s\""),
2503 tok ? tok : "(null)",
2504 amidxtaped_streams[i].name);
2507 tok = strtok(NULL, " \n");
2508 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
2509 extra = vstrallocf(_("CONNECT %s token is \"%s\": expected a port number"),
2510 amidxtaped_streams[i].name,
2511 tok ? tok : "(null)");
2519 * OPTIONS [options string] '\n'
2521 if (strcmp(tok, "OPTIONS") == 0) {
2522 tok = strtok(NULL, "\n");
2524 extra = stralloc(_("OPTIONS token is missing"));
2528 while((p = strchr(tok, ';')) != NULL) {
2530 if(strncmp_const(tok, "features=") == 0) {
2531 tok += sizeof("features=") - 1;
2532 am_release_feature_set(their_features);
2533 if((their_features = am_string_to_feature(tok)) == NULL) {
2534 errstr = newvstralloc(errstr,
2535 _("OPTIONS: bad features value: "),
2547 extra = vstrallocf("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""),
2548 tok ? tok : _("(null)"));
2554 * Connect the streams to their remote ports
2556 for (i = 0; i < NSTREAMS; i++) {
2559 amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]);
2560 dbprintf(_("amidxtaped_streams[%d].fd = %p\n"),i, amidxtaped_streams[i].fd);
2561 if (amidxtaped_streams[i].fd == NULL) {
2562 errstr = newvstrallocf(errstr,\
2563 _("[could not connect %s stream: %s]"),
2564 amidxtaped_streams[i].name,
2565 security_geterror(sech));
2570 * Authenticate the streams
2572 for (i = 0; i < NSTREAMS; i++) {
2573 if (amidxtaped_streams[i].fd == NULL)
2575 if (security_stream_auth(amidxtaped_streams[i].fd) < 0) {
2576 errstr = newvstrallocf(errstr,
2577 _("[could not authenticate %s stream: %s]"),
2578 amidxtaped_streams[i].name,
2579 security_stream_geterror(amidxtaped_streams[i].fd));
2585 * The CTLFD and DATAFD streams are mandatory. If we didn't get
2588 if (amidxtaped_streams[CTLFD].fd == NULL) {
2589 errstr = newvstrallocf(errstr, _("[couldn't open CTL streams]"));
2592 if (amidxtaped_streams[DATAFD].fd == NULL) {
2593 errstr = newvstrallocf(errstr, _("[couldn't open DATA streams]"));
2597 /* everything worked */
2598 *response_error = 0;
2603 errstr = newvstrallocf(errstr,
2604 _("[parse of reply message failed: %s]"), extra);
2606 errstr = newvstrallocf(errstr,
2607 _("[parse of reply message failed: (no additional information)"));
2610 *response_error = 2;
2615 *response_error = 1;
2619 * This is called when everything needs to shut down so event_loop()
2623 stop_amidxtaped(void)
2627 for (i = 0; i < NSTREAMS; i++) {
2628 if (amidxtaped_streams[i].fd != NULL) {
2629 security_stream_close(amidxtaped_streams[i].fd);
2630 amidxtaped_streams[i].fd = NULL;
2635 static char* ctl_buffer = NULL;
2636 /* gets a "line" from server and put in server_line */
2637 /* server_line is terminated with \0, \r\n is striped */
2638 /* returns -1 if error */
2641 get_amidxtaped_line(void)
2647 amfree(amidxtaped_line);
2649 ctl_buffer = stralloc("");
2651 while (!strstr(ctl_buffer,"\r\n")) {
2652 if (amidxtaped_streams[CTLFD].fd == NULL)
2655 size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2659 else if(size == 0) {
2662 newbuf = alloc(strlen(ctl_buffer)+size+1);
2663 strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1));
2664 memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size);
2665 newbuf[strlen(ctl_buffer)+size] = '\0';
2667 ctl_buffer = newbuf;
2671 s = strstr(ctl_buffer,"\r\n");
2673 newbuf = stralloc(s+2);
2674 amidxtaped_line = stralloc(ctl_buffer);
2676 ctl_buffer = newbuf;
2682 read_amidxtaped_data(
2687 ctl_data_t *ctl_data = (ctl_data_t *)cookie;
2688 assert(cookie != NULL);
2691 errstr = newstralloc2(errstr, _("amidxtaped read: "),
2692 security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2697 * EOF. Stop and return.
2700 security_stream_close(amidxtaped_streams[DATAFD].fd);
2701 amidxtaped_streams[DATAFD].fd = NULL;
2703 * If the mesg fd has also shut down, then we're done.
2708 assert(buf != NULL);
2710 if (ctl_data->header_done == 0) {
2711 GPtrArray *errarray;
2712 g_option_t g_options;
2713 data_path_t data_path_set = DATA_PATH_AMANDA;
2716 to_move = MIN(32768-header_size, size);
2717 memcpy(header_buf+header_size, buf, to_move);
2718 header_size += to_move;
2720 g_debug("read header %zd => %d", size, header_size);
2721 if (header_size < 32768) {
2722 /* wait to read more data */
2724 } else if (header_size > 32768) {
2725 error("header_size is %d\n", header_size);
2727 assert (to_move == size);
2728 security_stream_read_cancel(amidxtaped_streams[DATAFD].fd);
2729 /* parse the file header */
2730 fh_init(&ctl_data->file);
2731 parse_file_header(header_buf, &ctl_data->file, (size_t)header_size);
2733 /* call backup_support_option */
2734 g_options.config = get_config_name();
2735 g_options.hostname = dump_hostname;
2736 if (strcmp(ctl_data->file.program, "APPLICATION") == 0) {
2738 ctl_data->bsu = backup_support_option(ctl_data->file.application,
2740 ctl_data->file.disk,
2744 ctl_data->bsu = backup_support_option(ctl_data->file.application,
2746 ctl_data->file.disk, NULL,
2749 if (!ctl_data->bsu) {
2751 for (i=0; i < errarray->len; i++) {
2753 line = g_ptr_array_index(errarray, i);
2754 g_fprintf(stderr, "%s\n", line);
2756 g_ptr_array_free_full(errarray);
2759 data_path_set = ctl_data->bsu->data_path_set;
2761 /* handle backup_support_option failure */
2763 ctl_data->header_done = 1;
2764 if (!ask_file_overwrite(ctl_data)) {
2765 if (am_has_feature(tapesrv_features, fe_amidxtaped_abort)) {
2766 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ABORT");
2772 if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
2774 /* send DATA-PATH request */
2775 msg = stralloc("AVAIL-DATAPATH");
2776 if (data_path_set & DATA_PATH_AMANDA)
2777 vstrextend(&msg, " AMANDA", NULL);
2778 if (data_path_set & DATA_PATH_DIRECTTCP)
2779 vstrextend(&msg, " DIRECT-TCP", NULL);
2780 send_to_tape_server(amidxtaped_streams[CTLFD].fd, msg);
2783 start_processing_data(ctl_data);
2786 ctl_data->bytes_read += size;
2787 /* Only the data is sent to the child */
2789 * We ignore errors while writing to the index file.
2791 (void)full_write(ctl_data->child_pipe[1], buf, (size_t)size);
2797 ctl_data_t *ctl_data)
2799 char *restore_dir = NULL;
2801 if (ctl_data->file.dumplevel == 0) {
2802 property_t *property = g_hash_table_lookup(proplist, "directory");
2803 if (property && property->values && property->values->data) {
2804 /* take first property value */
2805 restore_dir = strdup(property->values->data);
2807 if (samba_extract_method == SAMBA_SMBCLIENT ||
2809 ctl_data->bsu->recover_path == RECOVER_PATH_REMOTE)) {
2811 restore_dir = g_strdup(ctl_data->file.disk);
2813 g_printf(_("Restoring files into target host %s\n"), restore_dir);
2816 restore_dir = g_get_current_dir();
2818 g_printf(_("Restoring files into directory %s\n"), restore_dir);
2821 /* Collect files to delete befause of a bug in gnutar */
2822 if (strcmp(ctl_data->file.program, "GNUTAR") == 0 ||
2823 (strcmp(ctl_data->file.program, "APPLICATION") == 0 &&
2824 strcmp(ctl_data->file.application, "amgtar") == 0)) {
2825 check_file_overwrite(restore_dir);
2827 g_printf(_("All existing files in %s can be deleted\n"),
2831 if (!okay_to_continue(0,0,0)) {
2833 amfree(restore_dir);
2838 /* delete the files for gnutar */
2840 if (!do_unlink_list()) {
2841 g_fprintf(stderr, _("Can't recover because I can't cleanup the restore directory (%s)\n"),
2844 amfree(restore_dir);
2849 amfree(restore_dir);
2855 start_processing_data(
2856 ctl_data_t *ctl_data)
2858 if (pipe(ctl_data->child_pipe) == -1) {
2859 error(_("extract_list - error setting up pipe to extractor: %s\n"),
2865 if (ctl_data->file.encrypted) {
2868 int errfd = fileno(stderr);
2870 g_debug("image is encrypted %s %s", ctl_data->file.clnt_encrypt, ctl_data->file.clnt_decrypt_opt);
2871 argv[0] = ctl_data->file.clnt_encrypt;
2872 argv[1] = ctl_data->file.clnt_decrypt_opt;
2874 pipespawnv(ctl_data->file.clnt_encrypt, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &crypt_out, &errfd, argv);
2875 ctl_data->child_pipe[0] = crypt_out;
2879 if (ctl_data->file.compressed) {
2882 int errfd = fileno(stderr);
2886 g_debug("image is compressed %s", ctl_data->file.clntcompprog);
2887 if (strlen(ctl_data->file.clntcompprog) > 0) {
2888 comp_prog = ctl_data->file.clntcompprog;
2891 comp_prog = UNCOMPRESS_PATH;
2892 comp_arg = UNCOMPRESS_OPT;
2894 argv[0] = comp_prog;
2897 pipespawnv(comp_prog, STDOUT_PIPE, 0, &ctl_data->child_pipe[0], &comp_out, &errfd, argv);
2898 ctl_data->child_pipe[0] = comp_out;
2901 /* okay, ready to extract. fork a child to do the actual work */
2902 if ((ctl_data->pid = fork()) == 0) {
2903 /* this is the child process */
2904 /* never gets out of this clause */
2905 aclose(ctl_data->child_pipe[1]);
2906 extract_files_child(ctl_data);
2910 if (ctl_data->pid == -1) {
2911 errstr = newstralloc(errstr, _("writer_intermediary - error forking child"));
2912 g_printf(_("writer_intermediary - error forking child"));
2915 aclose(ctl_data->child_pipe[0]);
2916 security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data,
2918 if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) {
2919 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "DATAPATH-OK");