+static gboolean
+s3_device_set_password(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->password);
+ self->password = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_tenant_id(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->tenant_id);
+ self->tenant_id = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_tenant_name(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->tenant_name);
+ self->tenant_name = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_host_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->host);
+ self->host = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_service_path_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->service_path);
+ self->service_path = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_user_token_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->user_token);
+ self->user_token = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_bucket_location_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ char *str_val = g_value_dup_string(val);
+
+ if (str_val[0] && self->use_ssl && !s3_curl_location_compat()) {
+ device_set_error(p_self, stralloc(_(
+ "Location constraint given for Amazon S3 bucket, "
+ "but libcurl is too old support wildcard certificates.")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ goto fail;
+ }
+
+ if (str_val[0] && !s3_bucket_location_compat(self->bucket)) {
+ device_set_error(p_self, g_strdup_printf(_(
+ "Location constraint given for Amazon S3 bucket, "
+ "but the bucket name (%s) is not usable as a subdomain."),
+ self->bucket),
+ DEVICE_STATUS_DEVICE_ERROR);
+ goto fail;
+ }
+
+ amfree(self->bucket_location);
+ self->bucket_location = str_val;
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+fail:
+ g_free(str_val);
+ return FALSE;
+}
+
+static gboolean
+s3_device_set_storage_class_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ char *str_val = g_value_dup_string(val);
+
+ amfree(self->storage_class);
+ self->storage_class = str_val;
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_server_side_encryption_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ char *str_val = g_value_dup_string(val);
+
+ amfree(self->server_side_encryption);
+ self->server_side_encryption = str_val;
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_proxy_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ char *str_val = g_value_dup_string(val);
+
+ amfree(self->proxy);
+ self->proxy = str_val;
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_ca_info_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->ca_info);
+ self->ca_info = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_verbose_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ int thread;
+
+ self->verbose = g_value_get_boolean(val);
+ /* Our S3 handle may not yet have been instantiated; if so, it will
+ * get the proper verbose setting when it is created */
+ if (self->s3t) {
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ if (self->s3t[thread].s3)
+ s3_verbose(self->s3t[thread].s3, self->verbose);
+ }
+ }
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_create_bucket_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ int thread;
+
+ self->create_bucket = g_value_get_boolean(val);
+ /* Our S3 handle may not yet have been instantiated; if so, it will
+ * get the proper verbose setting when it is created */
+ if (self->s3t) {
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ if (self->s3t[thread].s3)
+ s3_verbose(self->s3t[thread].s3, self->verbose);
+ }
+ }
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_storage_api(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ const char *storage_api = g_value_get_string(val);
+ if (g_str_equal(storage_api, "S3")) {
+ self->s3_api = S3_API_S3;
+ } else if (g_str_equal(storage_api, "SWIFT-1.0")) {
+ self->s3_api = S3_API_SWIFT_1;
+ } else if (g_str_equal(storage_api, "SWIFT-2.0")) {
+ self->s3_api = S3_API_SWIFT_2;
+ } else if (g_str_equal(storage_api, "OAUTH2")) {
+ self->s3_api = S3_API_OAUTH2;
+ } else if (g_str_equal(storage_api, "CASTOR")) {
+#if LIBCURL_VERSION_NUM >= 0x071301
+ curl_version_info_data *info;
+ /* check the runtime version too */
+ info = curl_version_info(CURLVERSION_NOW);
+ if (info->version_num >= 0x071301) {
+ self->s3_api = S3_API_CASTOR;
+ } else {
+ device_set_error(p_self, g_strdup_printf(_(
+ "Error setting STORAGE-API to castor "
+ "(You must install libcurl 7.19.1 or newer)")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+#else
+ device_set_error(p_self, g_strdup_printf(_(
+ "Error setting STORAGE-API to castor "
+ "This amanda is compiled with a too old libcurl, you must compile with libcurl 7.19.1 or newer")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+#endif
+ } else {
+ g_debug("Invalid STORAGE_API, using \"S3\".");
+ self->s3_api = S3_API_S3;
+ }
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_openstack_swift_api_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+
+ const gboolean openstack_swift_api = g_value_get_boolean(val);
+ if (openstack_swift_api) {
+ GValue storage_api_val;
+ g_value_init(&storage_api_val, G_TYPE_STRING);
+ g_value_set_static_string(&storage_api_val, "SWIFT-1.0");
+ return s3_device_set_storage_api(p_self, base, &storage_api_val,
+ surety, source);
+ }
+ return TRUE;
+}
+
+static gboolean
+s3_device_set_s3_multi_delete_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ self->use_s3_multi_delete = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_ssl_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ gboolean new_val;
+ int thread;
+
+ new_val = g_value_get_boolean(val);
+ /* Our S3 handle may not yet have been instantiated; if so, it will
+ * get the proper use_ssl setting when it is created */
+ if (self->s3t) {
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ if (self->s3t[thread].s3 && !s3_use_ssl(self->s3t[thread].s3, new_val)) {
+ device_set_error(p_self, g_strdup_printf(_(
+ "Error setting S3 SSL/TLS use "
+ "(tried to enable SSL/TLS for S3, but curl doesn't support it?)")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+ }
+ self->use_ssl = new_val;
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_reuse_connection_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ self->reuse_connection = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_max_send_speed_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ guint64 new_val;
+ int thread;
+
+ new_val = g_value_get_uint64(val);
+ if (self->s3t) {
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ if (self->s3t[thread].s3 && !s3_set_max_send_speed(self->s3t[thread].s3, new_val)) {
+ device_set_error(p_self,
+ g_strdup("Could not set S3 maximum send speed"),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+ }
+ self->max_send_speed = new_val;
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_max_recv_speed_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ guint64 new_val;
+ int thread;
+
+ new_val = g_value_get_uint64(val);
+ if (self->s3t) {
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ if (self->s3t[thread].s3 &&
+ !s3_set_max_recv_speed(self->s3t[thread].s3, new_val)) {
+ device_set_error(p_self,
+ g_strdup("Could not set S3 maximum recv speed"),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+ }
+ self->max_recv_speed = new_val;
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_nb_threads_backup(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ guint64 new_val;
+
+ new_val = g_value_get_uint64(val);
+ self->nb_threads_backup = new_val;
+ if (self->nb_threads_backup > self->nb_threads) {
+ self->nb_threads = self->nb_threads_backup;
+ }
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_nb_threads_recovery(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+ guint64 new_val;
+
+ new_val = g_value_get_uint64(val);
+ self->nb_threads_recovery = new_val;
+ if (self->nb_threads_recovery > self->nb_threads) {
+ self->nb_threads = self->nb_threads_recovery;
+ }
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_max_volume_usage_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ self->volume_limit = g_value_get_uint64(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+
+}
+
+static gboolean
+s3_device_set_enforce_max_volume_usage_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ self->enforce_volume_limit = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+
+}
+
+static gboolean
+s3_device_set_use_subdomain_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ self->use_subdomain = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+property_set_leom_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ self->leom = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_client_id_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->client_id);
+ self->client_id = g_value_dup_string(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_client_secret_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->client_secret);
+ self->client_secret = g_value_dup_string(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_refresh_token_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->refresh_token);
+ self->refresh_token = g_value_dup_string(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_project_id_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->project_id);
+ self->project_id = g_value_dup_string(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_reps_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->reps);
+ self->reps = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+s3_device_set_reps_bucket_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety surety, PropertySource source)
+{
+ S3Device *self = S3_DEVICE(p_self);
+
+ amfree(self->reps_bucket);
+ self->reps_bucket = g_value_dup_string(val);
+ device_clear_volume_details(p_self);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static Device*
+s3_device_factory(char * device_name, char * device_type, char * device_node)
+{
+ Device *rval;
+ g_assert(0 == strcmp(device_type, S3_DEVICE_NAME));
+ rval = DEVICE(g_object_new(TYPE_S3_DEVICE, NULL));
+
+ device_open_device(rval, device_name, device_type, device_node);
+ return rval;
+}
+
+/*
+ * Virtual function overrides
+ */
+
+static void
+s3_device_open_device(Device *pself, char *device_name,
+ char * device_type, char * device_node)
+{
+ S3Device *self = S3_DEVICE(pself);
+ char * name_colon;
+ GValue tmp_value;
+
+ pself->min_block_size = S3_DEVICE_MIN_BLOCK_SIZE;
+ pself->max_block_size = S3_DEVICE_MAX_BLOCK_SIZE;
+ pself->block_size = S3_DEVICE_DEFAULT_BLOCK_SIZE;
+
+ /* Device name may be bucket/prefix, to support multiple volumes in a
+ * single bucket. */
+ name_colon = strchr(device_node, '/');
+ if (name_colon == NULL) {
+ self->bucket = g_strdup(device_node);
+ self->prefix = g_strdup("");
+ } else {
+ self->bucket = g_strndup(device_node, name_colon - device_node);
+ self->prefix = g_strdup(name_colon + 1);
+ }
+
+ if (self->bucket == NULL || self->bucket[0] == '\0') {
+ device_set_error(pself,
+ vstrallocf(_("Empty bucket name in device %s"), device_name),
+ DEVICE_STATUS_DEVICE_ERROR);
+ amfree(self->bucket);
+ amfree(self->prefix);
+ return;
+ }
+
+ if (self->reps == NULL) {
+ self->reps = g_strdup(S3_DEVICE_REPS_DEFAULT);
+ }
+
+ if (self->reps_bucket == NULL) {
+ self->reps_bucket = g_strdup(S3_DEVICE_REPS_BUCKET_DEFAULT);
+ }
+
+ g_debug(_("S3 driver using bucket '%s', prefix '%s'"), self->bucket, self->prefix);
+
+ /* default values */
+ self->verbose = FALSE;
+ self->s3_api = S3_API_S3;
+
+ /* use SSL if available */
+ self->use_ssl = s3_curl_supports_ssl();
+ bzero(&tmp_value, sizeof(GValue));
+ g_value_init(&tmp_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&tmp_value, self->use_ssl);
+ device_set_simple_property(pself, device_property_s3_ssl.ID,
+ &tmp_value, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
+
+ /* reuse connection */
+ self->reuse_connection = TRUE;
+ bzero(&tmp_value, sizeof(GValue));
+ g_value_init(&tmp_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&tmp_value, self->reuse_connection);
+ device_set_simple_property(pself, device_property_reuse_connection.ID,
+ &tmp_value, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
+
+ /* Set default create_bucket */
+ self->create_bucket = TRUE;
+ bzero(&tmp_value, sizeof(GValue));
+ g_value_init(&tmp_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&tmp_value, self->create_bucket);
+ device_set_simple_property(pself, device_property_create_bucket.ID,
+ &tmp_value, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
+
+ if (parent_class->open_device) {
+ parent_class->open_device(pself, device_name, device_type, device_node);
+ }
+}
+
+static void s3_device_finalize(GObject * obj_self) {
+ S3Device *self = S3_DEVICE (obj_self);
+ int thread;
+
+ if(G_OBJECT_CLASS(parent_class)->finalize)
+ (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
+
+ if (self->thread_pool_delete) {
+ g_thread_pool_free(self->thread_pool_delete, 1, 1);
+ self->thread_pool_delete = NULL;
+ }
+ if (self->thread_pool_write) {
+ g_thread_pool_free(self->thread_pool_write, 1, 1);
+ self->thread_pool_write = NULL;
+ }
+ if (self->thread_pool_read) {
+ g_thread_pool_free(self->thread_pool_read, 1, 1);
+ self->thread_pool_read = NULL;
+ }
+ if (self->thread_idle_mutex) {
+ g_mutex_free(self->thread_idle_mutex);
+ self->thread_idle_mutex = NULL;
+ }
+ if (self->thread_idle_cond) {
+ g_cond_free(self->thread_idle_cond);
+ self->thread_idle_cond = NULL;
+ }
+ if (self->s3t) {
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ g_mutex_free(self->s3t[thread].now_mutex);
+ if(self->s3t[thread].s3) s3_free(self->s3t[thread].s3);
+ g_free(self->s3t[thread].curl_buffer.buffer);
+ }
+ g_free(self->s3t);
+ }
+ if(self->bucket) g_free(self->bucket);
+ if(self->prefix) g_free(self->prefix);
+ if(self->access_key) g_free(self->access_key);
+ if(self->secret_key) g_free(self->secret_key);
+ if(self->swift_account_id) g_free(self->swift_account_id);
+ if(self->swift_access_key) g_free(self->swift_access_key);
+ if(self->username) g_free(self->username);
+ if(self->password) g_free(self->password);
+ if(self->tenant_id) g_free(self->tenant_id);
+ if(self->tenant_name) g_free(self->tenant_name);
+ if(self->host) g_free(self->host);
+ if(self->service_path) g_free(self->service_path);
+ if(self->user_token) g_free(self->user_token);
+ if(self->bucket_location) g_free(self->bucket_location);
+ if(self->storage_class) g_free(self->storage_class);
+ if(self->server_side_encryption) g_free(self->server_side_encryption);
+ if(self->proxy) g_free(self->proxy);
+ if(self->ca_info) g_free(self->ca_info);
+ if(self->reps) g_free(self->reps);
+ if(self->reps_bucket) g_free(self->reps_bucket);
+}
+
+static gboolean setup_handle(S3Device * self) {
+ Device *d_self = DEVICE(self);
+ int thread;
+ guint response_code;
+ s3_error_code_t s3_error_code;
+ CURLcode curl_code;
+
+ if (self->s3t == NULL) {
+ if (self->s3_api == S3_API_S3) {
+ if (self->access_key == NULL || self->access_key[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("No Amazon access key specified")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ if (self->secret_key == NULL || self->secret_key[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("No Amazon secret key specified")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ } else if (self->s3_api == S3_API_SWIFT_1) {
+ if (self->swift_account_id == NULL ||
+ self->swift_account_id[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("No Swift account id specified")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ if (self->swift_access_key == NULL ||
+ self->swift_access_key[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("No Swift access key specified")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ } else if (self->s3_api == S3_API_SWIFT_2) {
+ if (!((self->username && self->password && self->tenant_id) ||
+ (self->username && self->password && self->tenant_name) ||
+ (self->access_key && self->secret_key && self->tenant_id) ||
+ (self->access_key && self->secret_key && self->tenant_name))) {
+ device_set_error(d_self,
+ g_strdup(_("Missing authorization properties")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ } else if (self->s3_api == S3_API_OAUTH2) {
+ if (self->client_id == NULL ||
+ self->client_id[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("Missing client_id properties")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ if (self->client_secret == NULL ||
+ self->client_secret[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("Missing client_secret properties")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ if (self->refresh_token == NULL ||
+ self->refresh_token[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("Missing refresh_token properties")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ if (self->project_id == NULL ||
+ self->project_id[0] == '\0') {
+ device_set_error(d_self,
+ g_strdup(_("Missing project_id properties")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ } else if (self->s3_api == S3_API_CASTOR) {
+ self->use_s3_multi_delete = 0;
+ self->use_subdomain = FALSE;
+ if(self->service_path) {
+ g_free(self->service_path);
+ self->service_path = NULL;
+ }
+ }
+
+ self->s3t = g_new0(S3_by_thread, self->nb_threads);
+ if (self->s3t == NULL) {
+ device_set_error(d_self,
+ g_strdup(_("Can't allocate S3Handle array")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ self->thread_idle_cond = g_cond_new();
+ self->thread_idle_mutex = g_mutex_new();
+
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ self->s3t[thread].idle = 1;
+ self->s3t[thread].done = 1;
+ self->s3t[thread].eof = FALSE;
+ self->s3t[thread].errflags = DEVICE_STATUS_SUCCESS;
+ self->s3t[thread].errmsg = NULL;
+ self->s3t[thread].filename = NULL;
+ self->s3t[thread].curl_buffer.buffer = NULL;
+ self->s3t[thread].curl_buffer.buffer_len = 0;
+ self->s3t[thread].now_mutex = g_mutex_new();
+ self->s3t[thread].s3 = s3_open(self->access_key, self->secret_key,
+ self->swift_account_id,
+ self->swift_access_key,
+ self->host, self->service_path,
+ self->use_subdomain,
+ self->user_token, self->bucket_location,
+ self->storage_class, self->ca_info,
+ self->server_side_encryption,
+ self->proxy,
+ self->s3_api,
+ self->username,
+ self->password,
+ self->tenant_id,
+ self->tenant_name,
+ self->client_id,
+ self->client_secret,
+ self->refresh_token,
+ self->reuse_connection,
+ self->reps, self->reps_bucket);
+ if (self->s3t[thread].s3 == NULL) {
+ device_set_error(d_self,
+ stralloc(_("Internal error creating S3 handle")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ self->nb_threads = thread+1;
+ return FALSE;
+ }
+ }
+
+ g_debug("Create %d threads", self->nb_threads);
+ self->thread_pool_delete = g_thread_pool_new(s3_thread_delete_block,
+ self, self->nb_threads, 0,
+ NULL);
+ self->thread_pool_write = g_thread_pool_new(s3_thread_write_block, self,
+ self->nb_threads, 0, NULL);
+ self->thread_pool_read = g_thread_pool_new(s3_thread_read_block, self,
+ self->nb_threads, 0, NULL);
+
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ s3_verbose(self->s3t[thread].s3, self->verbose);
+
+ if (!s3_use_ssl(self->s3t[thread].s3, self->use_ssl)) {
+ device_set_error(d_self, g_strdup_printf(_(
+ "Error setting S3 SSL/TLS use "
+ "(tried to enable SSL/TLS for S3, but curl doesn't support it?)")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ if (self->max_send_speed &&
+ !s3_set_max_send_speed(self->s3t[thread].s3,
+ self->max_send_speed)) {
+ device_set_error(d_self,
+ g_strdup("Could not set S3 maximum send speed"),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ if (self->max_recv_speed &&
+ !s3_set_max_recv_speed(self->s3t[thread].s3,
+ self->max_recv_speed)) {
+ device_set_error(d_self,
+ g_strdup("Could not set S3 maximum recv speed"),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+
+ for (thread = 0; thread < self->nb_threads; thread++) {
+ if (!s3_open2(self->s3t[thread].s3)) {
+ if (self->s3_api == S3_API_SWIFT_1 ||
+ self->s3_api == S3_API_SWIFT_2) {
+ s3_error(self->s3t[0].s3, NULL, &response_code,
+ &s3_error_code, NULL, &curl_code, NULL);
+ device_set_error(d_self,
+ g_strdup_printf(_("s3_open2 failed: %s"),
+ s3_strerror(self->s3t[0].s3)),
+ DEVICE_STATUS_DEVICE_ERROR);
+ self->nb_threads = thread+1;
+ return FALSE;
+ } else {
+ device_set_error(d_self,
+ g_strdup("s3_open2 failed"),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+make_bucket(
+ Device * pself)
+{
+ S3Device *self = S3_DEVICE(pself);
+ guint response_code;
+ s3_error_code_t s3_error_code;
+ CURLcode curl_code;
+
+ if (s3_is_bucket_exists(self->s3t[0].s3, self->bucket, self->project_id)) {
+ return TRUE;
+ }
+
+ s3_error(self->s3t[0].s3, NULL, &response_code, &s3_error_code, NULL, &curl_code, NULL);
+
+ if (response_code == 0 && s3_error_code == 0 &&
+ (curl_code == CURLE_COULDNT_CONNECT ||
+ curl_code == CURLE_COULDNT_RESOLVE_HOST)) {
+ device_set_error(pself,
+ g_strdup_printf(_("While connecting to S3 bucket: %s"),
+ s3_strerror(self->s3t[0].s3)),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ if (!self->create_bucket) {
+ device_set_error(pself,
+ g_strdup_printf(_("Can't list bucket: %s"),
+ s3_strerror(self->s3t[0].s3)),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ if (!s3_make_bucket(self->s3t[0].s3, self->bucket, self->project_id)) {
+ s3_error(self->s3t[0].s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
+
+ /* if it isn't an expected error (bucket already exists),
+ * return FALSE */
+ if (response_code != 409 ||
+ (s3_error_code != S3_ERROR_BucketAlreadyExists &&
+ s3_error_code != S3_ERROR_BucketAlreadyOwnedByYou)) {
+ device_set_error(pself,
+ g_strdup_printf(_("While creating new S3 bucket: %s"), s3_strerror(self->s3t[0].s3)),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int progress_func(
+ void *thread_data,
+ double dltotal G_GNUC_UNUSED,
+ double dlnow,
+ double ultotal G_GNUC_UNUSED,
+ double ulnow)
+{
+ S3_by_thread *s3t = (S3_by_thread *)thread_data;
+
+ g_mutex_lock(s3t->now_mutex);
+ s3t->dlnow = dlnow;
+ s3t->ulnow = ulnow;
+ g_mutex_unlock(s3t->now_mutex);
+
+ return 0;
+}
+
+static DeviceStatusFlags
+s3_device_read_label(Device *pself) {
+ S3Device *self = S3_DEVICE(pself);
+ char *key;
+ CurlBuffer buf = {NULL, 0, 0, S3_DEVICE_MAX_BLOCK_SIZE};
+ dumpfile_t *amanda_header;
+ /* note that this may be called from s3_device_start, when
+ * self->access_mode is not ACCESS_NULL */
+
+ amfree(pself->volume_label);
+ amfree(pself->volume_time);
+ dumpfile_free(pself->volume_header);
+ pself->volume_header = NULL;
+
+ if (device_in_error(self)) return pself->status;
+
+ if (!setup_handle(self)) {
+ /* setup_handle already set our error message */
+ return pself->status;
+ }
+ reset_thread(self);