// we can only get md5 of an object containing 1 range // XXX: move code to separate thread gboolean cache_mng_get_md5 (CacheMng *cmng, fuse_ino_t ino, gchar **md5str) { struct _CacheEntry *entry; unsigned char digest[MD5_DIGEST_LENGTH]; MD5_CTX md5ctx; ssize_t bytes; unsigned char data[1024]; char path[PATH_MAX]; size_t i; gchar *out; FILE *in; entry = g_hash_table_lookup (cmng->h_entries, GUINT_TO_POINTER (ino)); if (!entry) return FALSE; if (range_count (entry->avail_range) != 1) { LOG_debug (CMNG_LOG, INO_H"Entry contains more than 1 range, can't take MD5 sum of such object !", INO_T (ino)); return FALSE; } cache_mng_file_name (cmng, path, sizeof (path), ino); in = fopen (path, "rb"); if (in == NULL) { LOG_debug (CMNG_LOG, INO_H"Can't open file for reading: %s", INO_T (ino), path); return FALSE; } MD5_Init (&md5ctx); while ((bytes = fread (data, 1, 1024, in)) != 0) MD5_Update (&md5ctx, data, bytes); MD5_Final (digest, &md5ctx); fclose (in); out = g_malloc (33); for (i = 0; i < 16; ++i) sprintf (&out[i*2], "%02x", (unsigned int)digest[i]); *md5str = out; return TRUE; }
/*{{{ GET request */ static void fileio_read_on_get_cb (HttpConnection *con, void *ctx, gboolean success, const gchar *buf, size_t buf_len, G_GNUC_UNUSED struct evkeyvalq *headers) { FileReadData *rdata = (FileReadData *) ctx; const char *versioning_header = NULL; // release HttpConnection http_connection_release (con); if (!success) { LOG_err (FIO_LOG, INO_CON_H"Failed to get file from server !", INO_T (rdata->ino), con); rdata->on_buffer_read_cb (rdata->ctx, FALSE, NULL, 0); g_free (rdata); return; } // store it in the local cache cache_mng_store_file_buf (application_get_cache_mng (rdata->fop->app), rdata->ino, buf_len, rdata->request_offset, (unsigned char *) buf, NULL, NULL); // update version ID versioning_header = http_find_header (headers, "x-amz-version-id"); if (versioning_header) { cache_mng_update_version_id (application_get_cache_mng (rdata->fop->app), rdata->ino, versioning_header); } LOG_debug (FIO_LOG, INO_H"Storing [%"G_GUINT64_FORMAT" %zu]", INO_T(rdata->ino), rdata->request_offset, buf_len); // and read it fileio_read_get_buf (rdata); }
static void fileio_read_get_buf (FileReadData *rdata) { if ((guint64)rdata->off >= rdata->fop->file_size) { // requested range is outsize the file size LOG_debug (FIO_LOG, INO_H"requested size is beyond the file size!", INO_T (rdata->ino)); fileio_read_on_cache_cb (NULL, 0, TRUE, rdata); return; } // set new request size: // 1. file must have some size // 2. offset must be less than the file size // 3. offset + size is greater than the file size if (rdata->fop->file_size > 0 && rdata->off >= 0 && rdata->fop->file_size > (guint64)rdata->off && (guint64) (rdata->off + rdata->size) > rdata->fop->file_size) { rdata->size = rdata->fop->file_size - rdata->off; // special case, when zero-size file is requested } else if (rdata->fop->file_size == 0) { rdata->size = 0; } else { // request size and offset are ok } LOG_debug (FIO_LOG, INO_H"requesting [%"OFF_FMT": %"G_GUINT64_FORMAT"], file size: %"G_GUINT64_FORMAT, INO_T (rdata->ino), rdata->off, rdata->size, rdata->fop->file_size); cache_mng_retrieve_file_buf (application_get_cache_mng (rdata->fop->app), rdata->ino, rdata->size, rdata->off, fileio_read_on_cache_cb, rdata); }
// multipart is sent static void fileio_release_on_complete_cb (HttpConnection *con, void *ctx, gboolean success, G_GNUC_UNUSED const gchar *buf, G_GNUC_UNUSED size_t buf_len, G_GNUC_UNUSED struct evkeyvalq *headers) { FileIO *fop = (FileIO *) ctx; const gchar *versioning_header; http_connection_release (con); if (!success) { LOG_err (FIO_LOG, INO_CON_H"Failed to send Multipart data to the server !", INO_T (fop->ino), con); fileio_destroy (fop); return; } versioning_header = http_find_header (headers, "x-amz-version-id"); if (versioning_header) { cache_mng_update_version_id (application_get_cache_mng (fop->app), fop->ino, versioning_header); } // done LOG_debug (FIO_LOG, INO_CON_H"Multipart Upload is done !", INO_T (fop->ino), con); // fileio_destroy (fop); fileio_release_update_headers (fop); }
void fileio_write_buffer (FileIO *fop, const char *buf, size_t buf_size, off_t off, fuse_ino_t ino, FileIO_on_buffer_written_cb on_buffer_written_cb, gpointer ctx) { FileWriteData *wdata; // XXX: allow only sequentially write // current written bytes should be always match offset if (off >= 0 && fop->current_size != (guint64)off) { LOG_err (FIO_LOG, INO_H"Write call with offset %"OFF_FMT" is not allowed !", INO_T (ino), off); on_buffer_written_cb (fop, ctx, FALSE, 0); return; } // add data to output buffer evbuffer_add (fop->write_buf, buf, buf_size); fop->current_size += buf_size; LOG_debug (FIO_LOG, INO_H"Write buf size: %zd", INO_T (ino), evbuffer_get_length (fop->write_buf)); // CacheMng cache_mng_store_file_buf (application_get_cache_mng (fop->app), ino, buf_size, off, (unsigned char *) buf, NULL, NULL); // if current write buffer exceeds "part_size" - this is a multipart upload if (evbuffer_get_length (fop->write_buf) >= conf_get_uint (application_get_conf (fop->app), "s3.part_size")) { // init helper struct wdata = g_new0 (FileWriteData, 1); wdata->fop = fop; wdata->buf_size = buf_size; wdata->off = off; wdata->ino = ino; wdata->on_buffer_written_cb = on_buffer_written_cb; wdata->ctx = ctx; // init multipart upload if (!fop->multipart_initiated) { fileio_write_init_multipart (wdata); // else send the current part } else { fileio_write_send_part (wdata); } // or just notify client that we are ready for more data } else { on_buffer_written_cb (fop, ctx, TRUE, buf_size); } }
static void fileio_release_complete_multipart (FileIO *fop) { if (!fop->uploadid) { LOG_err (FIO_LOG, INO_H"UploadID is not set, aborting operation !", INO_T (fop->ino)); fileio_destroy (fop); return; } if (!client_pool_get_client (application_get_write_client_pool (fop->app), fileio_release_on_complete_con_cb, fop)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (fop->ino)); fileio_destroy (fop); return; } }
static void fileio_release_update_headers (FileIO *fop) { // update MD5 headers only if versioning is disabled if (conf_get_boolean (application_get_conf (fop->app), "s3.versioning")) { LOG_debug (FIO_LOG, INO_H"File uploaded !", INO_T (fop->ino)); fileio_destroy (fop); } else { if (!client_pool_get_client (application_get_write_client_pool (fop->app), fileio_release_on_update_headers_con_cb, fop)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (fop->ino)); fileio_destroy (fop); return; } } }
static void fileio_write_on_multipart_init_cb (HttpConnection *con, void *ctx, gboolean success, const gchar *buf, size_t buf_len, G_GNUC_UNUSED struct evkeyvalq *headers) { FileWriteData *wdata = (FileWriteData *) ctx; gchar *uploadid; http_connection_release (con); wdata->fop->multipart_initiated = TRUE; if (!success || !buf_len) { LOG_err (FIO_LOG, INO_CON_H"Failed to get multipart init data from the server !", INO_T (wdata->ino), con); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } uploadid = get_uploadid (buf, buf_len); if (!uploadid) { LOG_err (FIO_LOG, INO_CON_H"Failed to parse multipart init data!", INO_T (wdata->ino), con); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } wdata->fop->uploadid = g_strdup (uploadid); xmlFree (uploadid); // done, resume uploading part wdata->fop->part_number = 1; fileio_write_send_part (wdata); }
// buffer is sent static void fileio_write_on_send_cb (HttpConnection *con, void *ctx, gboolean success, G_GNUC_UNUSED const gchar *buf, G_GNUC_UNUSED size_t buf_len, G_GNUC_UNUSED struct evkeyvalq *headers) { FileWriteData *wdata = (FileWriteData *) ctx; const char *versioning_header; http_connection_release (con); if (!success) { LOG_err (FIO_LOG, INO_CON_H"Failed to send bufer to server !", INO_T (wdata->ino), con); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } versioning_header = http_find_header (headers, "x-amz-version-id"); if (versioning_header) { cache_mng_update_version_id (application_get_cache_mng (wdata->fop->app), wdata->ino, versioning_header); } // empty part buffer evbuffer_drain (wdata->fop->write_buf, -1); // done sending part wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, TRUE, wdata->buf_size); g_free (wdata); }
// if it's the first fuse read() request - send HEAD request to server // else try to get data from local cache, otherwise download from the server void fileio_read_buffer (FileIO *fop, size_t size, off_t off, fuse_ino_t ino, FileIO_on_buffer_read_cb on_buffer_read_cb, gpointer ctx) { FileReadData *rdata; rdata = g_new0 (FileReadData, 1); rdata->fop = fop; rdata->size = size; rdata->off = off; rdata->ino = ino; rdata->on_buffer_read_cb = on_buffer_read_cb; rdata->ctx = ctx; rdata->request_offset = off; // send HEAD request first if (!rdata->fop->head_req_sent) { // get HTTP connection to download manifest or a full file if (!client_pool_get_client (application_get_read_client_pool (rdata->fop->app), fileio_read_on_head_con_cb, rdata)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (rdata->ino)); rdata->on_buffer_read_cb (rdata->ctx, FALSE, NULL, 0); g_free (rdata); } // HEAD is sent, try to get data from cache } else { fileio_read_get_buf (rdata); } }
// file is sent static void fileio_release_on_part_sent_cb (HttpConnection *con, void *ctx, gboolean success, G_GNUC_UNUSED const gchar *buf, G_GNUC_UNUSED size_t buf_len, G_GNUC_UNUSED struct evkeyvalq *headers) { FileIO *fop = (FileIO *) ctx; const gchar *versioning_header; http_connection_release (con); if (!success) { LOG_err (FIO_LOG, INO_CON_H"Failed to send bufer to server !", INO_T (fop->ino), con); fileio_destroy (fop); return; } versioning_header = http_find_header (headers, "x-amz-version-id"); if (versioning_header) { cache_mng_update_version_id (application_get_cache_mng (fop->app), fop->ino, versioning_header); } // if it's a multi part upload - Complete Multipart Upload if (fop->multipart_initiated) { fileio_release_complete_multipart (fop); // or we are done } else { fileio_release_update_headers (fop); //fileio_destroy (fop); } }
// got HttpConnection object static void fileio_write_on_multipart_init_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileWriteData *wdata = (FileWriteData *) ctx; gboolean res; gchar *path; http_connection_acquire (con); path = g_strdup_printf ("%s?uploads", wdata->fop->fname); // send storage class with the init request http_connection_add_output_header (con, "x-amz-storage-class", conf_get_string (application_get_conf (con->app), "s3.storage_type")); res = http_connection_make_request (con, path, "POST", NULL, TRUE, NULL, fileio_write_on_multipart_init_cb, wdata ); g_free (path); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (wdata->ino), con); http_connection_release (con); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } }
// got HttpConnection object static void fileio_release_on_update_headers_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileIO *fop = (FileIO *) ctx; gchar *path; gchar *cpy_path; gboolean res; unsigned char digest[16]; gchar *md5str; size_t i; LOG_debug (FIO_LOG, INO_CON_H"Updating object's headers..", INO_T (fop->ino), con); http_connection_acquire (con); if (fop->content_type) http_connection_add_output_header (con, "Content-Type", fop->content_type); http_connection_add_output_header (con, "x-amz-metadata-directive", "REPLACE"); http_connection_add_output_header (con, "x-amz-storage-class", conf_get_string (application_get_conf (con->app), "s3.storage_type")); MD5_Final (digest, &fop->md5); md5str = g_malloc (33); for (i = 0; i < 16; ++i) sprintf(&md5str[i*2], "%02x", (unsigned int)digest[i]); http_connection_add_output_header (con, "x-amz-meta-md5", md5str); g_free (md5str); cpy_path = g_strdup_printf ("%s%s", conf_get_string (application_get_conf (fop->app), "s3.bucket_name"), fop->fname); http_connection_add_output_header (con, "x-amz-copy-source", cpy_path); g_free (cpy_path); path = g_strdup_printf ("%s", fop->fname); res = http_connection_make_request (con, path, "PUT", NULL, TRUE, NULL, fileio_release_on_update_header_cb, fop ); g_free (path); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (fop->ino), con); http_connection_release (con); fileio_destroy (fop); return; } }
static void fileio_write_send_part (FileWriteData *wdata) { if (!wdata->fop->uploadid) { LOG_err (FIO_LOG, INO_H"UploadID is not set, aborting operation !", INO_T (wdata->ino)); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } if (!client_pool_get_client (application_get_write_client_pool (wdata->fop->app), fileio_write_on_send_con_cb, wdata)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (wdata->ino)); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } }
// removes file from local storage void cache_mng_remove_file (CacheMng *cmng, fuse_ino_t ino) { struct _CacheEntry *entry; char path[PATH_MAX]; entry = g_hash_table_lookup (cmng->h_entries, GUINT_TO_POINTER (ino)); if (entry) { cmng->size -= range_length (entry->avail_range); g_queue_delete_link (cmng->q_lru, entry->ll_lru); g_hash_table_remove (cmng->h_entries, GUINT_TO_POINTER (ino)); cache_mng_file_name (cmng, path, sizeof (path), ino); unlink (path); LOG_debug (CMNG_LOG, INO_H"Entry is removed", INO_T (ino)); } else { LOG_debug (CMNG_LOG, INO_H"Entry not found", INO_T (ino)); } }
/*{{{ update headers on uploaded object */ static void fileio_release_on_update_header_cb (HttpConnection *con, void *ctx, gboolean success, G_GNUC_UNUSED const gchar *buf, G_GNUC_UNUSED size_t buf_len, G_GNUC_UNUSED struct evkeyvalq *headers) { FileIO *fop = (FileIO *) ctx; http_connection_release (con); if (!success) { LOG_err (FIO_LOG, INO_CON_H"Failed to update headers on the server !", INO_T (fop->ino), con); fileio_destroy (fop); return; } // done LOG_debug (FIO_LOG, INO_CON_H"Headers are updated !", INO_T (fop->ino), con); fileio_destroy (fop); }
static void fileio_read_on_cache_cb (unsigned char *buf, size_t size, gboolean success, void *ctx) { FileReadData *rdata = (FileReadData *) ctx; // we got data from the cache if (success) { LOG_debug (FIO_LOG, INO_H"Reading from cache", INO_T (rdata->ino)); rdata->on_buffer_read_cb (rdata->ctx, TRUE, (char *)buf, size); g_free (rdata); } else { LOG_debug (FIO_LOG, INO_H"Reading from server !", INO_T (rdata->ino)); if (!client_pool_get_client (application_get_read_client_pool (rdata->fop->app), fileio_read_on_con_cb, rdata)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (rdata->ino)); rdata->on_buffer_read_cb (rdata->ctx, FALSE, NULL, 0); g_free (rdata); return; } } }
static void fileio_write_init_multipart (FileWriteData *wdata) { if (!client_pool_get_client (application_get_write_client_pool (wdata->fop->app), fileio_write_on_multipart_init_con_cb, wdata)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (wdata->ino)); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } }
// got HttpConnection object static void fileio_release_on_complete_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileIO *fop = (FileIO *) ctx; gchar *path; gboolean res; struct evbuffer *xml_buf; GList *l; xml_buf = evbuffer_new (); evbuffer_add_printf (xml_buf, "%s", "<CompleteMultipartUpload>"); for (l = g_list_first (fop->l_parts); l; l = g_list_next (l)) { FileIOPart *part = (FileIOPart *) l->data; evbuffer_add_printf (xml_buf, "<Part><PartNumber>%u</PartNumber><ETag>\"%s\"</ETag></Part>", part->part_number, part->md5str); } evbuffer_add_printf (xml_buf, "%s", "</CompleteMultipartUpload>"); LOG_debug (FIO_LOG, INO_CON_H"Sending Multipart Final part..", INO_T (fop->ino), con); http_connection_acquire (con); path = g_strdup_printf ("%s?uploadId=%s", fop->fname, fop->uploadid); res = http_connection_make_request (con, path, "POST", xml_buf, TRUE, NULL, fileio_release_on_complete_cb, fop ); g_free (path); evbuffer_free (xml_buf); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (fop->ino), con); http_connection_release (con); fileio_destroy (fop); return; } }
// got HttpConnection object static void fileio_write_on_send_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileWriteData *wdata = (FileWriteData *) ctx; gchar *path; gboolean res; FileIOPart *part; size_t buf_len; const gchar *buf; http_connection_acquire (con); // add part information to the list part = g_new0 (FileIOPart, 1); part->part_number = wdata->fop->part_number; buf_len = evbuffer_get_length (wdata->fop->write_buf); buf = (const gchar *) evbuffer_pullup (wdata->fop->write_buf, buf_len); // XXX: move to separate thread // 1. calculate MD5 of a part. get_md5_sum (buf, buf_len, &part->md5str, &part->md5b); // 2. calculate MD5 of multiple message blocks MD5_Update (&wdata->fop->md5, buf, buf_len); wdata->fop->l_parts = g_list_append (wdata->fop->l_parts, part); path = g_strdup_printf ("%s?partNumber=%u&uploadId=%s", wdata->fop->fname, wdata->fop->part_number, wdata->fop->uploadid); // increase part number wdata->fop->part_number++; // XXX: check that part_number does not exceeds 10000 // add output headers http_connection_add_output_header (con, "Content-MD5", part->md5b); res = http_connection_make_request (con, path, "PUT", wdata->fop->write_buf, TRUE, NULL, fileio_write_on_send_cb, wdata ); g_free (path); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (wdata->ino), con); http_connection_release (con); wdata->on_buffer_written_cb (wdata->fop, wdata->ctx, FALSE, 0); g_free (wdata); return; } }
// got HttpConnection object static void fileio_read_on_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileReadData *rdata = (FileReadData *) ctx; gboolean res; guint64 part_size; http_connection_acquire (con); part_size = conf_get_uint (application_get_conf (rdata->fop->app), "s3.part_size"); // small file - get the whole file at once if (rdata->fop->file_size < part_size) rdata->request_offset = 0; // calculate offset else { gchar *range_hdr; if (part_size < rdata->size) part_size = rdata->size; rdata->request_offset = rdata->off; range_hdr = g_strdup_printf ("bytes=%"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT, (gint64)rdata->request_offset, (gint64)(rdata->request_offset + part_size)); http_connection_add_output_header (con, "Range", range_hdr); g_free (range_hdr); } res = http_connection_make_request (con, rdata->fop->fname, "GET", NULL, TRUE, NULL, fileio_read_on_get_cb, rdata ); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (rdata->ino), con); http_connection_release (con); rdata->on_buffer_read_cb (rdata->ctx, FALSE, NULL, 0); g_free (rdata); return; } }
// file is released, finish all operations void fileio_release (FileIO *fop) { // if write buffer has some data left - send it to the server // or an empty file was created if (evbuffer_get_length (fop->write_buf) || fop->assume_new) { if (!client_pool_get_client (application_get_write_client_pool (fop->app), fileio_release_on_part_con_cb, fop)) { LOG_err (FIO_LOG, INO_H"Failed to get HTTP client !", INO_T (fop->ino)); fileio_destroy (fop); return; } } else { // if it's a multi part upload - Complete Multipart Upload if (fop->multipart_initiated) { fileio_release_complete_multipart (fop); // just a "small" file } else fileio_destroy (fop); } }
// got HttpConnection object static void fileio_read_on_head_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileReadData *rdata = (FileReadData *) ctx; gboolean res; http_connection_acquire (con); res = http_connection_make_request (con, rdata->fop->fname, "HEAD", NULL, FALSE, NULL, fileio_read_on_head_cb, rdata ); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (rdata->ino), con); http_connection_release (con); rdata->on_buffer_read_cb (rdata->ctx, FALSE, NULL, 0); g_free (rdata); return; } }
// store file buffer into local storage // if success == TRUE then "buf" successfuly stored on disc void cache_mng_store_file_buf (CacheMng *cmng, fuse_ino_t ino, size_t size, off_t off, unsigned char *buf, cache_mng_on_store_file_buf_cb on_store_file_buf_cb, void *ctx) { struct _CacheContext *context; struct _CacheEntry *entry; ssize_t res; int fd; char path[PATH_MAX]; guint64 old_length, new_length; guint64 range_size; time_t now; range_size = (guint64)(off + size); // limit the number of cache checks now = time (NULL); if (cmng->check_time < now && now - cmng->check_time >= 10) { // remove data until we have at least size bytes of max_size left while (cmng->max_size < cmng->size + size && g_queue_peek_tail (cmng->q_lru)) { entry = (struct _CacheEntry *) g_queue_peek_tail (cmng->q_lru); cache_mng_remove_file (cmng, entry->ino); } cmng->check_time = now; } context = cache_context_create (size, ctx); context->cb.store_cb = on_store_file_buf_cb; cache_mng_file_name (cmng, path, sizeof (path), ino); fd = open (path, O_WRONLY|O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { LOG_err (CMNG_LOG, INO_H"Failed to create / open file for writing! Path: %s", INO_T (ino), path); if (context->cb.store_cb) context->cb.store_cb (FALSE, context->user_ctx); cache_context_destroy (context); return; } res = pwrite(fd, buf, size, off); close (fd); entry = g_hash_table_lookup (cmng->h_entries, GUINT_TO_POINTER (ino)); if (!entry) { entry = cache_entry_create (ino); g_queue_push_head (cmng->q_lru, entry); entry->ll_lru = g_queue_peek_head_link (cmng->q_lru); g_hash_table_insert (cmng->h_entries, GUINT_TO_POINTER (ino), entry); } old_length = range_length (entry->avail_range); range_add (entry->avail_range, off, range_size); new_length = range_length (entry->avail_range); if (new_length >= old_length) cmng->size += new_length - old_length; else { LOG_err (CMNG_LOG, INO_H"New length is less than the old length !: %"G_GUINT64_FORMAT" <= %"G_GUINT64_FORMAT, INO_T (ino), new_length, old_length); } // update modification time entry->modification_time = time (NULL); context->success = (res == (ssize_t) size); LOG_debug (CMNG_LOG, INO_H"Written [%"OFF_FMT":%zu] bytes, result: %s", INO_T (ino), off, size, context->success ? "OK" : "Failed"); context->ev = event_new (application_get_evbase (cmng->app), -1, 0, cache_write_cb, context); // fire this event at once event_active (context->ev, 0, 0); event_add (context->ev, NULL); }
// retrieve file buffer from local storage // if success == TRUE then "buf" contains "size" bytes of data void cache_mng_retrieve_file_buf (CacheMng *cmng, fuse_ino_t ino, size_t size, off_t off, cache_mng_on_retrieve_file_buf_cb on_retrieve_file_buf_cb, void *ctx) { struct _CacheContext *context; struct _CacheEntry *entry; context = cache_context_create (size, ctx); context->cb.retrieve_cb = on_retrieve_file_buf_cb; entry = g_hash_table_lookup (cmng->h_entries, GUINT_TO_POINTER (ino)); if (entry && range_contain (entry->avail_range, off, off + size)) { int fd; ssize_t res; char path[PATH_MAX]; if (ino != entry->ino) { LOG_err (CMNG_LOG, INO_H"Requested inode doesn't match hashed key!", INO_T (ino)); if (context->cb.retrieve_cb) context->cb.retrieve_cb (NULL, 0, FALSE, context->user_ctx); cache_context_destroy (context); cmng->cache_miss++; return; } cache_mng_file_name (cmng, path, sizeof (path), ino); fd = open (path, O_RDONLY); if (fd < 0) { LOG_err (CMNG_LOG, INO_H"Failed to open file for reading! Path: %s", INO_T (ino), path); if (context->cb.retrieve_cb) context->cb.retrieve_cb (NULL, 0, FALSE, context->user_ctx); cache_context_destroy (context); cmng->cache_miss++; return; } context->buf = g_malloc (size); res = pread (fd, context->buf, size, off); close (fd); context->success = (res == (ssize_t) size); LOG_debug (CMNG_LOG, INO_H"Read [%"OFF_FMT":%zu] bytes, result: %s", INO_T (ino), off, size, context->success ? "OK" : "Failed"); if (!context->success) { g_free (context->buf); context->buf = NULL; cmng->cache_miss++; } else cmng->cache_hits++; // move entry to the front of q_lru g_queue_unlink (cmng->q_lru, entry->ll_lru); g_queue_push_head_link (cmng->q_lru, entry->ll_lru); } else { LOG_debug (CMNG_LOG, INO_H"Entry isn't found or doesn't contain requested range: [%"OFF_FMT": %"OFF_FMT"]", INO_T (ino), off, off + size); cmng->cache_miss++; } context->ev = event_new (application_get_evbase (cmng->app), -1, 0, cache_read_cb, context); // fire this event at once event_active (context->ev, 0, 0); event_add (context->ev, NULL); }
// got HttpConnection object static void fileio_release_on_part_con_cb (gpointer client, gpointer ctx) { HttpConnection *con = (HttpConnection *) client; FileIO *fop = (FileIO *) ctx; gchar *path; gboolean res; FileIOPart *part; size_t buf_len; const gchar *buf; LOG_debug (FIO_LOG, INO_CON_H"Releasing fop. Size: %zu", INO_T (fop->ino), con, evbuffer_get_length (fop->write_buf)); // add part information to the list part = g_new0 (FileIOPart, 1); part->part_number = fop->part_number; buf_len = evbuffer_get_length (fop->write_buf); buf = (const gchar *)evbuffer_pullup (fop->write_buf, buf_len); // XXX: move to separate thread // 1. calculate MD5 of a part. get_md5_sum (buf, buf_len, &part->md5str, &part->md5b); // 2. calculate MD5 of multiple message blocks MD5_Update (&fop->md5, buf, buf_len); fop->l_parts = g_list_append (fop->l_parts, part); // if this is a multipart if (fop->multipart_initiated) { if (!fop->uploadid) { LOG_err (FIO_LOG, INO_CON_H"UploadID is not set, aborting operation !", INO_T (fop->ino), con); fileio_destroy (fop); return; } path = g_strdup_printf ("%s?partNumber=%u&uploadId=%s", fop->fname, fop->part_number, fop->uploadid); fop->part_number++; } else { path = g_strdup (fop->fname); } #ifdef MAGIC_ENABLED // guess MIME type gchar *mime_type = magic_buffer (application_get_magic_ctx (fop->app), buf, buf_len); if (mime_type) { LOG_debug (FIO_LOG, "Guessed MIME type of %s as %s", path, mime_type); fop->content_type = g_strdup (mime_type); } else { LOG_err (FIO_LOG, "Failed to guess MIME type of %s !", path); } #endif http_connection_acquire (con); // add output headers http_connection_add_output_header (con, "Content-MD5", part->md5b); if (fop->content_type) http_connection_add_output_header (con, "Content-Type", fop->content_type); // if this is the full file if (!fop->multipart_initiated) { time_t t; gchar time_str[50]; // Add current time t = time (NULL); if (strftime (time_str, sizeof (time_str), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t))) { http_connection_add_output_header (con, "x-amz-meta-date", time_str); } http_connection_add_output_header (con, "x-amz-storage-class", conf_get_string (application_get_conf (con->app), "s3.storage_type")); } res = http_connection_make_request (con, path, "PUT", fop->write_buf, TRUE, NULL, fileio_release_on_part_sent_cb, fop ); g_free (path); if (!res) { LOG_err (FIO_LOG, INO_CON_H"Failed to create HTTP request !", INO_T (fop->ino), con); http_connection_release (con); fileio_destroy (fop); return; } }
static void fileio_read_on_head_cb (HttpConnection *con, void *ctx, gboolean success, G_GNUC_UNUSED const gchar *buf, G_GNUC_UNUSED size_t buf_len, struct evkeyvalq *headers) { FileReadData *rdata = (FileReadData *) ctx; const char *content_len_header; DirTree *dtree; // release HttpConnection http_connection_release (con); if (!success) { LOG_err (FIO_LOG, INO_CON_H"Failed to get HEAD from server !", INO_T (rdata->ino), con); rdata->on_buffer_read_cb (rdata->ctx, FALSE, NULL, 0); g_free (rdata); return; } rdata->fop->head_req_sent = TRUE; // update DirTree dtree = application_get_dir_tree (rdata->fop->app); dir_tree_set_entry_exist (dtree, rdata->ino); // consistency checking: // 1. check local and remote file sizes content_len_header = http_find_header (headers, "Content-Length"); if (content_len_header) { guint64 local_size = 0; gint64 size = 0; size = strtoll ((char *)content_len_header, NULL, 10); if (size < 0) { LOG_err (FIO_LOG, INO_CON_H"Header contains incorrect file size!", INO_T (rdata->ino), con); size = 0; } rdata->fop->file_size = size; LOG_debug (FIO_LOG, INO_H"Remote file size: %"G_GUINT64_FORMAT, INO_T (rdata->ino), rdata->fop->file_size); local_size = cache_mng_get_file_length (application_get_cache_mng (rdata->fop->app), rdata->ino); if (local_size != rdata->fop->file_size) { LOG_debug (FIO_LOG, INO_H"Local and remote file sizes do not match, invalidating local cached file!", INO_T (rdata->ino)); cache_mng_remove_file (application_get_cache_mng (rdata->fop->app), rdata->ino); } } // 2. use one of the following ways to check that local and remote files are identical // if versioning is enabled: compare version IDs // if bucket has versioning disabled: compare MD5 sums if (conf_get_boolean (application_get_conf (rdata->fop->app), "s3.versioning")) { const char *versioning_header = http_find_header (headers, "x-amz-version-id"); if (versioning_header) { const gchar *local_version_id = cache_mng_get_version_id (application_get_cache_mng (rdata->fop->app), rdata->ino); if (local_version_id && !strcmp (local_version_id, versioning_header)) { LOG_debug (FIO_LOG, INO_H"Both version IDs match, using local cached file!", INO_T (rdata->ino)); } else { LOG_debug (FIO_LOG, INO_H"Version IDs do not match, invalidating local cached file!: %s %s", INO_T (rdata->ino), local_version_id, versioning_header); cache_mng_remove_file (application_get_cache_mng (rdata->fop->app), rdata->ino); } // header was not found } else { LOG_debug (FIO_LOG, INO_H"Versioning header was not found, invalidating local cached file!", INO_T (rdata->ino)); cache_mng_remove_file (application_get_cache_mng (rdata->fop->app), rdata->ino); } //check for MD5 } else { const char *md5_header = http_find_header (headers, "x-amz-meta-md5"); if (md5_header) { gchar *md5str = NULL; // at this point we have both remote and local MD5 sums if (cache_mng_get_md5 (application_get_cache_mng (rdata->fop->app), rdata->ino, &md5str)) { if (!strncmp (md5_header, md5str, 32)) { LOG_debug (FIO_LOG, INO_H"MD5 sums match, using local cached file!", INO_T (rdata->ino)); } else { LOG_debug (FIO_LOG, INO_H"MD5 sums do not match, invalidating local cached file!", INO_T (rdata->ino)); cache_mng_remove_file (application_get_cache_mng (rdata->fop->app), rdata->ino); } } else { LOG_debug (FIO_LOG, INO_H"Failed to get local MD5 sum, invalidating local cached file!", INO_T (rdata->ino)); cache_mng_remove_file (application_get_cache_mng (rdata->fop->app), rdata->ino); } if (md5str) g_free (md5str); // header was not found } else { LOG_debug (FIO_LOG, INO_H"MD5 sum header was not found, invalidating local cached file!", INO_T (rdata->ino)); cache_mng_remove_file (application_get_cache_mng (rdata->fop->app), rdata->ino); } } // resume downloading file fileio_read_get_buf (rdata); }