+/* Add a volume to the seen tapes list. */
+static void record_seen_volume(seentapes_t ** list, char * label,
+ char * slotstr) {
+ seentapes_t * new_entry;
+
+ if (list == NULL)
+ return;
+
+ new_entry = malloc(sizeof(seentapes_t));
+ new_entry->label = stralloc(label);
+ if (slotstr == NULL) {
+ new_entry->slotstr = NULL;
+ } else {
+ new_entry->slotstr = stralloc(slotstr);
+ }
+ new_entry->files = NULL;
+ new_entry->next = *list;
+ *list = new_entry;
+}
+
+/* Record a specific dump on a volume. */
+static void record_seen_dump(seentapes_t * volume, dumpfile_t * header) {
+ dumplist_t * this_dump;
+
+ if (volume == NULL)
+ return;
+
+ this_dump = malloc(sizeof(*this_dump));
+ this_dump->file = g_memdup(header, sizeof(*header));
+ this_dump->next = NULL;
+ if (volume->files) {
+ dumplist_t * tmp_dump = volume->files;
+ while (tmp_dump->next != NULL) {
+ tmp_dump = tmp_dump->next;
+ }
+ tmp_dump->next = this_dump;
+ } else {
+ volume->files = this_dump;
+ }
+}
+
+static void print_tape_inventory(FILE * logstream, seentapes_t * tape_seen,
+ char * timestamp, char * label,
+ int tape_count) {
+ char * logline;
+ dumplist_t * fileentry;
+
+ logline = log_genstring(L_START, "taper",
+ "datestamp %s label %s tape %d",
+ timestamp, label, tape_count);
+ fputs(logline, logstream);
+ amfree(logline);
+ for(fileentry=tape_seen->files; fileentry; fileentry=fileentry->next){
+ switch (fileentry->file->type) {
+ case F_DUMPFILE:
+ logline = log_genstring(L_SUCCESS, "taper",
+ "%s %s %s %d [faked log entry]",
+ fileentry->file->name,
+ fileentry->file->disk,
+ fileentry->file->datestamp,
+ fileentry->file->dumplevel);
+ break;
+ case F_SPLIT_DUMPFILE:
+ logline = log_genstring(L_CHUNK, "taper",
+ "%s %s %s %d %d [faked log entry]",
+ fileentry->file->name,
+ fileentry->file->disk,
+ fileentry->file->datestamp,
+ fileentry->file->partnum,
+ fileentry->file->dumplevel);
+ break;
+ default:
+ break;
+ }
+ if(logline != NULL){
+ fputs(logline, logstream);
+ amfree(logline);
+ fflush(logstream);
+ }
+ }
+}
+
+/* Check if the given header matches the given dumpspecs. Returns
+ TRUE if dumpspecs is NULL and false if the header is NULL. Returns
+ true if the header matches the match list. */
+static gboolean run_dumpspecs(GSList * dumpspecs,
+ dumpfile_t * header) {
+ dumpspec_t *ds;
+
+ if (dumpspecs == NULL)
+ return TRUE;
+ if (header == NULL)
+ return FALSE;
+
+ while (dumpspecs) {
+ ds = (dumpspec_t *)dumpspecs->data;
+ if (disk_match(header, ds->datestamp, ds->host,
+ ds->disk, ds->level) != 0) {
+ return TRUE;
+ }
+ dumpspecs = dumpspecs->next;
+ }
+
+ return FALSE;
+}
+
+/* A wrapper around restore() above. This function does some extra
+ checking to seek to the file in question and ensure that we really,
+ really want to use it.
+
+ The next_file argument provides instruction on what to do if the
+ requested file does not exist on the volume: If next_file is NULL
+ then if the requested file is missing the function will return
+ RESTORE_STATUS_NEXT_FILE. If next_file is not NULL then the first
+ extant file whose number is equal to or greater than file_num will
+ be attempted. *next_file will be filled in with the number of the
+ file following the one that was attempted. */
+static RestoreFileStatus
+try_restore_single_file(Device * device, int file_num, int* next_file,
+ FILE * prompt_out,
+ rst_flags_t * flags,
+ am_feature_t * their_features,
+ dumpfile_t * first_restored_file,
+ GSList * dumpspecs,
+ seentapes_t * tape_seen) {
+ RestoreSource source;
+ source.u.device = device;
+ source.restore_mode = DEVICE_MODE;
+
+ source.header = device_seek_file(device, file_num);
+
+ if (source.header == NULL) {
+ /* This definitely indicates an error. */
+ send_message(prompt_out, flags, their_features,
+ "Could not seek device %s to file %d: %s.",
+ device->device_name, file_num,
+ device_error(device));
+ return RESTORE_STATUS_NEXT_TAPE;
+ } else if (source.header->type == F_TAPEEND) {
+ amfree(source.header);
+ return RESTORE_STATUS_NEXT_TAPE;
+ } else if (device->file != file_num) {
+ if (next_file == NULL) {
+ send_message(prompt_out, flags, their_features,
+ "Requested file %d does not exist.",
+ file_num);
+ return RESTORE_STATUS_NEXT_FILE;
+ } else {
+ send_message(prompt_out, flags, their_features,
+ "Skipped from file %d to file %d.",
+ file_num, device->file);
+ file_num = device->file;
+ }
+ }
+ if (!am_has_feature(their_features, fe_amrecover_dle_in_header)) {
+ source.header->dle_str = NULL;
+ }
+
+ if (next_file != NULL) {
+ *next_file = file_num + 1;
+ }
+
+ g_return_val_if_fail(source.header->type == F_DUMPFILE ||
+ source.header->type == F_CONT_DUMPFILE ||
+ source.header->type == F_SPLIT_DUMPFILE,
+ RESTORE_STATUS_NEXT_FILE);
+
+
+ if (!run_dumpspecs(dumpspecs, source.header)) {
+ if(!flags->amidxtaped) {
+ g_fprintf(prompt_out, "%s: %d: skipping ",
+ get_pname(), file_num);
+ print_header(prompt_out, source.header);
+ }
+ return RESTORE_STATUS_NEXT_FILE;
+ }
+
+ if (first_restored_file != NULL &&
+ first_restored_file->type != F_UNKNOWN &&
+ first_restored_file->type != F_EMPTY &&
+ !headers_equal(first_restored_file, source.header, 1) &&
+ (flags->pipe_to_fd == fileno(stdout))) {
+ return RESTORE_STATUS_STOP;
+ }
+
+ if (!flags->amidxtaped) {
+ g_fprintf(stderr, "%s: %d: restoring ",
+ get_pname(), file_num);
+ print_header(stderr, source.header);
+ }
+ record_seen_dump(tape_seen, source.header);
+ restore(&source, flags);
+ if (first_restored_file) {
+ memcpy(first_restored_file, source.header, sizeof(dumpfile_t));
+ }
+ return RESTORE_STATUS_NEXT_FILE;
+}
+
+/* This function handles processing of a particular tape or holding
+ disk file. It returns TRUE if it is useful to load another tape.*/
+
+gboolean
+search_a_tape(Device * device,
+ FILE *prompt_out, /* Where to send any prompts */
+ rst_flags_t *flags, /* Restore options. */
+ am_feature_t *their_features,
+ tapelist_t *desired_tape, /* A list of desired tape files */
+ GSList *dumpspecs, /* What disks to restore. */
+ seentapes_t **tape_seen, /* Where to record data on
+ this tape. */
+ /* May be NULL. If zeroed, will be filled in with the
+ first restored file. If already filled in, then we
+ may only restore other files from the same dump. */
+ dumpfile_t * first_restored_file,
+ int tape_count,
+ FILE * logstream) {
+ seentapes_t * tape_seen_head = NULL;
+ RestoreSource source;