static void use_device_impl( XferSourceRecovery *xdtself, Device *device) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(xdtself); g_assert(self->paused); /* short-circuit if nothing is changing */ if (self->device == device) return; if (self->device) g_object_unref(self->device); self->device = NULL; /* if we already have a connection, then make this device use it */ if (self->conn) { if (!device_use_connection(device, self->conn)) { /* queue up an error for later, and set device_bad. * start_part will see this and fail silently */ self->device_bad = TRUE; xfer_cancel_with_error(XFER_ELEMENT(self), _("Cannot continue onto new volume: %s"), device_error_or_status(device)); return; } } self->device = device; g_object_ref(device); }
static gpointer directtcp_listen_thread( gpointer data) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(data); XferElement *elt = XFER_ELEMENT(self); int result; DBG(1, "(this is directtcp_listen_thread)"); /* we need to make an outgoing connection to downstream; we do this while * holding the start_part_mutex, so that a part doesn't get started until * we're finished with the device */ g_mutex_lock(self->start_part_mutex); if (elt->cancelled) { g_mutex_unlock(self->start_part_mutex); goto send_done; } g_assert(self->device != NULL); /* have a device */ g_assert(elt->downstream->input_listen_addrs != NULL); /* downstream listening */ DBG(2, "making DirectTCP connection on device %s", self->device->device_name); result = device_connect(self->device, FALSE, elt->downstream->input_listen_addrs, &self->conn, &elt->cancelled, self->start_part_mutex, self->abort_cond); if (result == 1 && !elt->cancelled) { xfer_cancel_with_error(elt, _("error making DirectTCP connection: %s"), device_error_or_status(self->device)); g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto send_done; } else if (result == 2 || elt->cancelled) { g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto send_done; } DBG(2, "DirectTCP connect succeeded"); return directtcp_common_thread(self); send_done: xfer_queue_message(elt->xfer, xmsg_new(elt, XMSG_DONE, 0)); return NULL; }
static gboolean do_block( XferDestDevice *self, guint size, gpointer data) { XferElement *elt = XFER_ELEMENT(self); if (device_write_block(self->device, size, data) != WRITE_SUCCEED) { xfer_cancel_with_error(elt, "%s: %s", self->device->device_name, device_error_or_status(self->device)); wait_until_xfer_cancelled(elt->xfer); return FALSE; } /* check for LEOM */ if (self->cancel_at_leom && self->device->is_eom) { xfer_cancel_with_error(elt, "%s: LEOM detected", self->device->device_name); wait_until_xfer_cancelled(elt->xfer); return FALSE; } return TRUE; }
static gboolean setup_impl( XferElement *elt) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(elt); if (elt->output_mech == XFER_MECH_DIRECTTCP_CONNECT) { g_assert(self->device != NULL); DBG(2, "listening for DirectTCP connection on device %s", self->device->device_name); if (!device_listen(self->device, FALSE, &elt->output_listen_addrs)) { xfer_cancel_with_error(elt, _("error listening for DirectTCP connection: %s"), device_error_or_status(self->device)); return FALSE; } self->listen_ok = TRUE; } else { /* no output_listen_addrs for either XFER_MECH_DIRECTTCP_LISTEN or * XFER_MECH_PULL_BUFFER */ elt->output_listen_addrs = NULL; } return TRUE; }
/* This function checks the label of a single tape, which may or may not * have been loaded by the changer. With the addition of char *dev, and *slot, * it has the same interface as taper_scan. slot should be the slot where * this tape is found, or NULL if no changer is in use. * Return value is the same as taper_scan. */ int scan_read_label( char *dev, char *slot, char *desired_label, char** label, char** timestamp, char** error_message) { Device * device; char *labelstr; DeviceStatusFlags device_status; char *new_label_errmsg; g_return_val_if_fail(dev != NULL, -1); if (*error_message == NULL) *error_message = stralloc(""); *label = *timestamp = NULL; device = device_open(dev); g_assert(device != NULL); if (device->status != DEVICE_STATUS_SUCCESS ) { *error_message = newvstrallocf(*error_message, _("%sError opening device %s: %s.\n"), *error_message, dev, device_error_or_status(device)); g_object_unref(device); amfree(*timestamp); amfree(*label); return -1; } if (!device_configure(device, TRUE)) { *error_message = newvstrallocf(*error_message, _("%sError configuring device %s: %s.\n"), *error_message, dev, device_error_or_status(device)); g_object_unref(device); amfree(*timestamp); amfree(*label); return -1; } device_status = device_read_label(device); if (device_status == DEVICE_STATUS_SUCCESS && device->volume_label != NULL) { *label = g_strdup(device->volume_label); *timestamp = strdup(device->volume_time); } else if (device_status & DEVICE_STATUS_VOLUME_UNLABELED) { if (!getconf_seen(CNF_LABEL_NEW_TAPES)) { *error_message = newvstrallocf(*error_message, _("%sFound an empty or non-amanda tape.\n"), *error_message); g_object_unref(device); return -1; } /* If we got a header, but the Device doesn't think it's labeled, then this * tape probably has some data on it, so refuse to automatically label it */ if (device->volume_header && device->volume_header->type != F_EMPTY) { *error_message = newvstrallocf(*error_message, _("%sFound a non-amanda tape; check and relabel it with 'amlabel -f'\n"), *error_message); g_object_unref(device); return -1; } g_object_unref(device); *label = find_brand_new_tape_label(&new_label_errmsg); if (*label != NULL) { *timestamp = stralloc("X"); *error_message = newvstrallocf(*error_message, _("%sFound an empty tape, will label it `%s'.\n"), *error_message, *label); return 3; } *error_message = newvstrallocf(*error_message, _("%s%s.\n"), *error_message, new_label_errmsg); return -1; } else { char * label_errstr; label_errstr = g_strdup_printf(_("Error reading label: %s.\n"), device_error_or_status(device)); *error_message = newvstralloc(*error_message, *error_message, label_errstr, NULL); g_free(label_errstr); return -1; } g_assert(*label != NULL && *timestamp != NULL); g_object_unref(device); *error_message = newvstrallocf(*error_message, _("%sread label `%s', date `%s'.\n"), *error_message, *label, *timestamp); /* Register this with the barcode database, even if its not ours. */ if (slot != NULL) { changer_label(slot, *label); } if (desired_label != NULL && strcmp(*label, desired_label) == 0) { /* Got desired label. */ return 1; } /* Is this actually an acceptable tape? */ labelstr = getconf_str(CNF_LABELSTR); if(!match(labelstr, *label)) { *error_message = newvstrallocf(*error_message, _("%slabel \"%s\" doesn't match \"%s\".\n"), *error_message, *label, labelstr); return -1; } else { tape_t *tp; if (strcmp(*timestamp, "X") == 0) { /* new, labeled tape. */ return 1; } tp = lookup_tapelabel(*label); if(tp == NULL) { *error_message = newvstrallocf(*error_message, _("%slabel \"%s\" matches labelstr but it is" " not listed in the tapelist file.\n"), *error_message, *label); return -1; } else if(tp != NULL && !reusable_tape(tp)) { *error_message = newvstrallocf(*error_message, _("%sTape with label %s is still active" " and cannot be overwritten.\n"), *error_message, *label); return -1; } } /* Yay! We got a good tape! */ return 2; }
static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) { char *old_volume_name = NULL; char *old_volume_time = NULL; tape_search_request_t request; gboolean search_result; DeviceStatusFlags status; /* If we got here, it means that we have found a tape to label and * have gotten permission from the driver to write it. But we * still can say NO-NEW-TAPE if a problem shows up, and must still * say NEW-TAPE if one doesn't. */ amfree(state->last_errmsg); state->device = device_open(state->next_tape_device); g_assert(state->device != NULL); amfree(state->next_tape_device); if (state->device->status != DEVICE_STATUS_SUCCESS) goto skip_volume; if (!device_configure(state->device, TRUE)) goto skip_volume; /* if we have an error, and are sure it isn't just an unlabeled volume, * then skip this volume */ status = device_read_label(state->device); if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && !(status & DEVICE_STATUS_VOLUME_UNLABELED)) goto skip_volume; old_volume_name = g_strdup(state->device->volume_label); old_volume_time = g_strdup(state->device->volume_time); if (!device_start(state->device, ACCESS_WRITE, state->next_tape_label, state->driver_start_time)) { gboolean tape_used; /* Something broke, see if we can tell if the volume was erased or * not. */ g_fprintf(stderr, "taper: Error writing label %s to device %s: %s.\n", state->next_tape_label, state->device->device_name, device_error_or_status(state->device)); if (!device_finish(state->device)) goto request_new_volume; /* This time, if we can't read the label, assume we've overwritten * the volume or otherwise corrupted it */ status = device_read_label(state->device); if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && !(status & DEVICE_STATUS_VOLUME_UNLABELED)) goto request_new_volume; tape_used = check_volume_changed(state->device, old_volume_name, old_volume_time); if (tape_used) goto request_new_volume; else goto skip_volume; } amfree(old_volume_name); amfree(old_volume_time); amfree(state->next_tape_label); update_tapelist(state); state->cur_tape++; if (state->have_changer && changer_label("UNKNOWN", state->device->volume_label) != 0) { error(_("couldn't update barcode database")); /*NOTREACHED*/ } log_add(L_START, "datestamp %s label %s tape %d", state->driver_start_time, state->device->volume_label, state->cur_tape); putresult(NEW_TAPE, "%s %s\n", dump_info->handle, state->device->volume_label); return TRUE; request_new_volume: /* Tell the driver we overwrote this volume, even if it was empty, and request * a new volume. */ if (state->device) state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); else state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); putresult(NEW_TAPE, "%s %s\n", dump_info->handle, state->next_tape_label); if (old_volume_name) { log_add(L_WARNING, "Problem writing label '%s' to volume %s " "(volume may be erased): %s\n", state->next_tape_label, old_volume_name, state->last_errmsg); } else { log_add(L_WARNING, "Problem writing label '%s' to new volume " "(volume may be erased): %s\n", state->next_tape_label, state->last_errmsg); } if (state->device) { g_object_unref(state->device); state->device = NULL; } amfree(state->next_tape_label); amfree(old_volume_name); amfree(old_volume_time); return find_and_label_new_tape(state, dump_info); skip_volume: /* grab a new volume without talking to the driver again -- we do this if we're * confident we didn't overwrite the last tape we got. */ if (state->device) state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); else state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); if (old_volume_name) { log_add(L_WARNING, "Problem writing label '%s' to volume '%s' " "(old volume data intact): %s\n", state->next_tape_label, old_volume_name, state->last_errmsg); } else { log_add(L_WARNING, "Problem writing label '%s' to new volume " "(old volume data intact): %s\n", state->next_tape_label, state->last_errmsg); } if (state->device) { g_object_unref(state->device); state->device = NULL; } amfree(state->next_tape_label); amfree(old_volume_name); amfree(old_volume_time); request.state = state; request.prolong = TRUE; request.errmsg = NULL; search_result = GPOINTER_TO_INT(tape_search_thread(&request)); if (search_result) { amfree(request.errmsg); return label_new_tape(state, dump_info); } else { /* Problem finding a new tape! */ log_taper_scan_errmsg(request.errmsg); putresult(NO_NEW_TAPE, "%s\n", dump_info->handle); return FALSE; } }
static gpointer pull_buffer_impl( XferElement *elt, size_t *size) { XferSourceDevice *self = (XferSourceDevice *)elt; gpointer buf = NULL; int result; int devsize; int max_block; /* indicate EOF on an cancel */ if (elt->cancelled) { *size = 0; return NULL; } /* get the device block size */ if (self->block_size == 0) { self->block_size = self->device->block_size; } do { buf = g_try_malloc(self->block_size); if (buf == NULL) { xfer_cancel_with_error(elt, _("%s: cannot allocate memory"), self->device->device_name); wait_until_xfer_cancelled(elt->xfer); return NULL; } devsize = (int)self->block_size; if (elt->size < 0) max_block = -1; else max_block = (elt->size+self->block_size-1)/self->block_size; result = device_read_block(self->device, buf, &devsize, max_block); *size = devsize; /* if the buffer was too small, loop around again */ if (result == 0) { g_assert(*size > self->block_size); self->block_size = devsize; amfree(buf); } } while (result == 0); if (result < 0) { amfree(buf); /* if we're not at EOF, it's an error */ if (!self->device->is_eof) { xfer_cancel_with_error(elt, _("error reading from %s: %s"), self->device->device_name, device_error_or_status(self->device)); wait_until_xfer_cancelled(elt->xfer); } *size = 0; return NULL; } return buf; }
static gpointer pull_buffer_impl( XferElement *elt, size_t *size) { XferSourceRecovery *self = XFER_SOURCE_RECOVERY(elt); gpointer buf = NULL; int result; int devsize; XMsg *msg; g_assert(elt->output_mech == XFER_MECH_PULL_BUFFER); g_mutex_lock(self->start_part_mutex); if (elt->size == 0) { if (elt->offset == 0 && elt->orig_size == 0) { self->paused = TRUE; } else { DBG(2, "xfer-source-recovery sending XMSG_CRC message"); DBG(2, "xfer-source-recovery CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(XFER_ELEMENT(self), XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); /* the device has signalled EOF (really end-of-part), so clean up instance * variables and report the EOP to the caller in the form of an xmsg */ DBG(2, "pull_buffer hit EOF; sending XMSG_SEGMENT_DONE"); msg = xmsg_new(XFER_ELEMENT(self), XMSG_SEGMENT_DONE, 0); msg->size = self->part_size; if (self->part_timer) { msg->duration = g_timer_elapsed(self->part_timer, NULL); g_timer_destroy(self->part_timer); self->part_timer = NULL; } msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; self->paused = TRUE; device_clear_bytes_read(self->device); self->bytes_read += self->part_size; self->part_size = 0; self->block_size = 0; /* don't queue the XMSG_PART_DONE until we've adjusted all of our * instance variables appropriately */ xfer_queue_message(elt->xfer, msg); if (self->device->is_eof) { DBG(2, "pull_buffer hit EOF; sending XMSG_PART_DONE"); msg = xmsg_new(XFER_ELEMENT(self), XMSG_PART_DONE, 0); msg->size = self->part_size; if (self->part_timer) { msg->duration = g_timer_elapsed(self->part_timer, NULL); g_timer_destroy(self->part_timer); self->part_timer = NULL; } msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; xfer_queue_message(elt->xfer, msg); } } } while (1) { /* make sure we have a device */ while (self->paused && !elt->cancelled) g_cond_wait(self->start_part_cond, self->start_part_mutex); /* indicate EOF on an cancel or when there are no more parts */ if (elt->cancelled) { goto error; } if (self->done) goto error; /* start the timer if this is the first pull_buffer of this part */ if (!self->part_timer) { DBG(2, "first pull_buffer of new part"); self->part_timer = g_timer_new(); } if (elt->size == 0) { result = -1; } else { /* loop until we read a full block, in case the blocks are larger * than expected */ if (self->block_size == 0) self->block_size = (size_t)self->device->block_size; do { int max_block; buf = g_malloc(self->block_size); if (buf == NULL) { xfer_cancel_with_error(elt, _("%s: cannot allocate memory"), self->device->device_name); g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto error_unlocked; } devsize = (int)self->block_size; if (elt->size < 0) max_block = -1; else max_block = (elt->size+self->block_size-1)/self->block_size; result = device_read_block(self->device, buf, &devsize, max_block); *size = devsize; if (result == 0) { g_assert(*size > self->block_size); self->block_size = devsize; amfree(buf); } } while (result == 0); if (result > 0 && (elt->offset || (elt->size > 0 && (long long unsigned)elt->size < *size))) { gpointer buf1 = g_malloc(self->block_size); if ((long long unsigned)elt->offset > *size) { g_debug("offset > *size"); } else if ((long long unsigned)elt->offset == *size) { g_debug("offset == *size"); } *size -= elt->offset; if (elt->size > 0 && (size_t)elt->size < *size) *size = elt->size; memmove(buf1, buf + elt->offset, *size); elt->offset = 0; g_free(buf); buf = buf1; } if (result > 0) elt->size -= *size; } /* if this block was successful, return it */ if (result > 0) { self->part_size += *size; break; } if (result < 0) { amfree(buf); /* if we're not at EOF, it's an error */ if (!self->device->is_eof && elt->size != 0) { xfer_cancel_with_error(elt, _("error reading from %s: %s"), self->device->device_name, device_error_or_status(self->device)); g_mutex_unlock(self->start_part_mutex); wait_until_xfer_cancelled(elt->xfer); goto error_unlocked; } DBG(2, "xfer-source-recovery sending XMSG_CRC message"); DBG(2, "xfer-source-recovery CRC: %08x size %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(XFER_ELEMENT(self), XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); /* the device has signalled EOF (really end-of-part), so clean up instance * variables and report the EOP to the caller in the form of an xmsg */ DBG(2, "pull_buffer hit EOF; sending XMSG_PART_DONE"); msg = xmsg_new(XFER_ELEMENT(self), XMSG_PART_DONE, 0); msg->size = self->part_size; msg->duration = g_timer_elapsed(self->part_timer, NULL); msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; self->paused = TRUE; self->bytes_read += self->part_size; device_clear_bytes_read(self->device); self->part_size = 0; self->block_size = 0; if (self->part_timer) { g_timer_destroy(self->part_timer); self->part_timer = NULL; } /* don't queue the XMSG_PART_DONE until we've adjusted all of our * instance variables appropriately */ xfer_queue_message(elt->xfer, msg); if (elt->size == 0) { g_mutex_unlock(self->start_part_mutex); return NULL; } } } g_mutex_unlock(self->start_part_mutex); if (buf) { crc32_add(buf, *size, &elt->crc); } return buf; error: g_mutex_unlock(self->start_part_mutex); error_unlocked: *size = 0; return NULL; }
/* common code for both directtcp_listen_thread and directtcp_connect_thread; * this is called after self->conn is filled in and carries out the data * transfer over that connection. NOTE: start_part_mutex is HELD when this * function begins */ static gpointer directtcp_common_thread( XferSourceRecovery *self) { XferElement *elt = XFER_ELEMENT(self); char *errmsg = NULL; int result; /* send XMSG_READY to indicate it's OK to call start_part now */ DBG(2, "directtcp_common_thread sending XMSG_READY"); xfer_queue_message(elt->xfer, xmsg_new(elt, XMSG_READY, 0)); /* now we sit around waiting for signals to write a part */ while (1) { guint64 actual_size; XMsg *msg; while (self->paused && !elt->cancelled) { DBG(9, "directtcp_common_thread waiting to be un-paused"); g_cond_wait(self->start_part_cond, self->start_part_mutex); } DBG(9, "directtcp_common_thread done waiting"); if (elt->cancelled) { g_mutex_unlock(self->start_part_mutex); goto close_conn_and_send_done; } /* if the device is NULL, we're done */ if (!self->device) break; /* read the part */ self->part_timer = g_timer_new(); while (1) { DBG(2, "directtcp_common_thread reading part from %s", self->device->device_name); result = device_read_to_connection(self->device, G_MAXUINT64, &actual_size, &elt->cancelled, self->start_part_mutex, self->abort_cond); if (result == 1 && !elt->cancelled) { xfer_cancel_with_error(elt, _("error reading from device: %s"), device_error_or_status(self->device)); g_mutex_unlock(self->start_part_mutex); goto close_conn_and_send_done; } else if (result == 2 || elt->cancelled) { g_mutex_unlock(self->start_part_mutex); goto close_conn_and_send_done; } /* break on EOF; otherwise do another read_to_connection */ if (self->device->is_eof) { break; } } DBG(2, "done reading part; sending XMSG_PART_DONE"); /* the device has signalled EOF (really end-of-part), so clean up instance * variables and report the EOP to the caller in the form of an xmsg */ msg = xmsg_new(XFER_ELEMENT(self), XMSG_PART_DONE, 0); msg->size = actual_size; msg->duration = g_timer_elapsed(self->part_timer, NULL); msg->partnum = 0; msg->fileno = self->device->file; msg->successful = TRUE; msg->eof = FALSE; self->paused = TRUE; g_object_unref(self->device); self->device = NULL; self->part_size = 0; self->block_size = 0; g_timer_destroy(self->part_timer); self->part_timer = NULL; xfer_queue_message(elt->xfer, msg); } g_mutex_unlock(self->start_part_mutex); close_conn_and_send_done: if (self->conn) { errmsg = directtcp_connection_close(self->conn); g_object_unref(self->conn); self->conn = NULL; if (errmsg) { xfer_cancel_with_error(elt, _("error closing DirectTCP connection: %s"), errmsg); wait_until_xfer_cancelled(elt->xfer); } } xfer_queue_message(elt->xfer, xmsg_new(elt, XMSG_DONE, 0)); return NULL; }