From: Michael Vrable Date: Sat, 30 Oct 2010 00:40:14 +0000 (-0700) Subject: Implement a rangeset data type and use it to track items in log segments. X-Git-Url: https://git.vrable.net/?a=commitdiff_plain;h=772058dfc33fbe4857864b4bdb8ba947eca19303;p=bluesky.git Implement a rangeset data type and use it to track items in log segments. The range set can be used to track which byte ranges in a file correspond to valid objects, and so can be used in the cloud log code to check that 1) objects are actually available if partial log fetching is implemented, and 2) that objects being fetched have actually been authenticated (if needed). This isn't fully working yet. The range sets should be changed from a hash table to a sequence, since some lookups in the cloud log code are into the middle of an object (to skip the header and remap the data), and that needs to be handled properly. --- diff --git a/bluesky/bluesky-private.h b/bluesky/bluesky-private.h index 1574a54..2f69166 100644 --- a/bluesky/bluesky-private.h +++ b/bluesky/bluesky-private.h @@ -81,7 +81,8 @@ gboolean bluesky_crypt_block_decrypt(gchar *cloud_block, size_t len, BlueSkyCryptKeys *keys); void bluesky_cloudlog_encrypt(GString *segment, BlueSkyCryptKeys *keys); void bluesky_cloudlog_decrypt(char *segment, size_t len, - BlueSkyCryptKeys *keys); + BlueSkyCryptKeys *keys, + BlueSkyRangeset *items); /* Storage layer. Requests can be performed asynchronously, so these objects * help keep track of operations in progress. */ @@ -385,6 +386,7 @@ struct BlueSkyCacheFile { BlueSkyLog *log; gboolean fetching, ready; // Cloud data: downloading or ready for use int64_t atime; // Access time, for cache management + BlueSkyRangeset *items; // Locations of valid items }; BlueSkyLog *bluesky_log_new(const char *log_directory); diff --git a/bluesky/bluesky.h b/bluesky/bluesky.h index ad7b942..87c58a2 100644 --- a/bluesky/bluesky.h +++ b/bluesky/bluesky.h @@ -85,6 +85,14 @@ void bluesky_string_unref(BlueSkyRCStr *string); BlueSkyRCStr *bluesky_string_dup(BlueSkyRCStr *string); void bluesky_string_resize(BlueSkyRCStr *string, gsize len); +struct BlueSkyRangeset; +typedef struct BlueSkyRangeset BlueSkyRangeset; +BlueSkyRangeset *bluesky_rangeset_new(); +void bluesky_rangeset_free(BlueSkyRangeset *rangeset); +gboolean bluesky_rangeset_insert(BlueSkyRangeset *rangeset, + int start, int length, gpointer data); +gpointer bluesky_rangeset_lookup(BlueSkyRangeset *rangeset, int start); + /* Storage interface. This presents a key-value store abstraction, and can * have multiple implementations: in-memory, on-disk, in-cloud. */ struct BlueSkyStore; diff --git a/bluesky/cache.c b/bluesky/cache.c index bfbfe3c..12b8be7 100644 --- a/bluesky/cache.c +++ b/bluesky/cache.c @@ -361,6 +361,7 @@ void bluesky_cachefile_gc(BlueSkyFS *fs) g_atomic_int_add(&fs->log->disk_used, -(cachefile->len / 1024)); g_hash_table_remove(fs->log->mmap_cache, cachefile->filename); + bluesky_rangeset_free(cachefile->items); g_mutex_unlock(cachefile->lock); g_mutex_free(cachefile->lock); g_cond_free(cachefile->cond); diff --git a/bluesky/cloudlog.c b/bluesky/cloudlog.c index e868d99..4d52d54 100644 --- a/bluesky/cloudlog.c +++ b/bluesky/cloudlog.c @@ -517,12 +517,15 @@ void bluesky_cloudlog_encrypt(GString *segment, BlueSkyCryptKeys *keys) } /* Make an decryption pass over a cloud log segment to decrypt items which were - * encrypted. TODO: Also computes a list of all offsets which at which valid - * cloud log items are found. */ -void bluesky_cloudlog_decrypt(char *segment, size_t len, BlueSkyCryptKeys *keys) + * encrypted. Also computes a list of all offsets which at which valid + * cloud log items are found and adds those offsets to items (if non-NULL). */ +void bluesky_cloudlog_decrypt(char *segment, size_t len, + BlueSkyCryptKeys *keys, + BlueSkyRangeset *items) { char *data = segment; size_t remaining_size = len; + size_t offset = 0; while (remaining_size >= sizeof(struct cloudlog_header)) { struct cloudlog_header *header = (struct cloudlog_header *)data; @@ -533,10 +536,21 @@ void bluesky_cloudlog_decrypt(char *segment, size_t len, BlueSkyCryptKeys *keys) if (item_size > remaining_size) break; if (bluesky_crypt_block_decrypt(data, item_size, keys)) { - /* TODO: Mark block as valid. */ + if (items != NULL) { + g_print(" data item at %zx\n", offset); + bluesky_rangeset_insert(items, offset, item_size, + GINT_TO_POINTER(TRUE)); + } + } else { + g_warning("Unauthenticated data at offset %zd", offset); + if (items != NULL) { + bluesky_rangeset_insert(items, offset, item_size, + GINT_TO_POINTER(TRUE)); + } } data += item_size; + offset += item_size; remaining_size -= item_size; } } diff --git a/bluesky/imap.c b/bluesky/imap.c index 1b9feb8..7181a1e 100644 --- a/bluesky/imap.c +++ b/bluesky/imap.c @@ -283,7 +283,7 @@ gboolean bluesky_checkpoint_load(BlueSkyFS *fs) } last = bluesky_string_dup(last); - bluesky_cloudlog_decrypt(last->data, last->len, fs->keys); + bluesky_cloudlog_decrypt(last->data, last->len, fs->keys, NULL); /* Scan through the contents of the last log segment to find a checkpoint * record. We need to do a linear scan since at this point we don't have a diff --git a/bluesky/log.c b/bluesky/log.c index 76d218a..8801b33 100644 --- a/bluesky/log.c +++ b/bluesky/log.c @@ -385,7 +385,7 @@ static void cloudlog_fetch_complete(BlueSkyStoreAsync *async, cachefile->filename); async->data = bluesky_string_dup(async->data); bluesky_cloudlog_decrypt(async->data->data, async->data->len, - cachefile->fs->keys); + cachefile->fs->keys, cachefile->items); if (!g_file_set_contents(pathname, async->data->data, async->data->len, NULL)) g_print("Error writing out fetched file to cache!\n"); @@ -451,6 +451,7 @@ BlueSkyCacheFile *bluesky_cachefile_lookup(BlueSkyFS *fs, map->log = log; g_atomic_int_set(&map->mapcount, 0); g_atomic_int_set(&map->refcount, 0); + map->items = bluesky_rangeset_new(); g_hash_table_insert(log->mmap_cache, map->filename, map); @@ -481,6 +482,14 @@ BlueSkyRCStr *bluesky_log_map_object(BlueSkyFS *fs, int log_dir, return NULL; } + /* Log segments fetched from the cloud might only be partially-fetched. + * Check whether the object we are interested in is available. */ + if (log_dir >= 0) { + if (!bluesky_rangeset_lookup(map->items, log_offset)) { + g_warning("log-%d: Item at offset %d does not seem to be available\n", log_seq, log_offset); + } + } + if (map->addr == NULL) { while (!map->ready && map->fetching) { g_print("Waiting for log segment to be fetched from cloud...\n"); diff --git a/bluesky/util.c b/bluesky/util.c index d292634..be378ef 100644 --- a/bluesky/util.c +++ b/bluesky/util.c @@ -219,3 +219,40 @@ BlueSkyInode *bluesky_list_tail(GList *head) else return (BlueSkyInode *)head->prev->data; } + +/**** Range sets. ****/ + +/* These are a data structure which can track a set of discontiguous integer + * ranges--such as the partitioning of the inode number space or the bytes in a + * log file into objects. This current prototype implementation just tracks + * the starting offset with a hash table and doesn't track the length, but + * should be extended later to track properly. */ + +struct BlueSkyRangeset { + GHashTable *hashtable; +}; + +BlueSkyRangeset *bluesky_rangeset_new() +{ + BlueSkyRangeset *rangeset = g_new(BlueSkyRangeset, 1); + rangeset->hashtable = g_hash_table_new(g_direct_hash, g_direct_equal); + return rangeset; +} + +void bluesky_rangeset_free(BlueSkyRangeset *rangeset) +{ + g_hash_table_unref(rangeset->hashtable); + g_free(rangeset); +} + +gboolean bluesky_rangeset_insert(BlueSkyRangeset *rangeset, + int start, int length, gpointer data) +{ + g_hash_table_insert(rangeset->hashtable, GINT_TO_POINTER(start), data); + return TRUE; +} + +gpointer bluesky_rangeset_lookup(BlueSkyRangeset *rangeset, int start) +{ + return g_hash_table_lookup(rangeset->hashtable, GINT_TO_POINTER(start)); +}