Add pluggable support for multiple storage backends.
authorMichael Vrable <mvrable@cs.ucsd.edu>
Fri, 4 Sep 2009 22:10:22 +0000 (15:10 -0700)
committerMichael Vrable <mvrable@turin.ucsd.edu>
Fri, 4 Sep 2009 22:10:22 +0000 (15:10 -0700)
bluesky/CMakeLists.txt
bluesky/bluesky.h
bluesky/file.c
bluesky/init.c [new file with mode: 0644]
bluesky/inode.c
bluesky/s3store.c
bluesky/store.c
nfs3/nfsd.c

index c4fe011..b21f468 100644 (file)
@@ -1,6 +1,7 @@
 link_directories(/home/mvrable/scratch/libs3-1.4/build/lib)
 
-add_library(bluesky SHARED crypto.c dir.c file.c inode.c serialize.c store.c s3store.c)
+add_library(bluesky SHARED
+            crypto.c dir.c file.c init.c inode.c serialize.c store.c s3store.c)
 add_executable(bluesky-test main.c)
 
 set(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
index b250206..8980c98 100644 (file)
@@ -16,7 +16,7 @@
 extern "C" {
 #endif
 
-struct S3Store;
+void bluesky_init(void);
 
 /* Reference-counted blocks of memory, used for passing data in and out of
  * storage backends and in other places. */
@@ -40,6 +40,35 @@ void bluesky_crypt_random_bytes(guchar *buf, gint len);
 BlueSkyRCStr *bluesky_crypt_encrypt(BlueSkyRCStr *in, const uint8_t *key);
 BlueSkyRCStr *bluesky_crypt_decrypt(BlueSkyRCStr *in, const uint8_t *key);
 
+/* Storage interface.  This presents a key-value store abstraction, and can
+ * have multiple implementations: in-memory, on-disk, in-cloud. */
+typedef struct {
+    /* Create a new store instance and return a handle to it. */
+    gpointer (*create)();
+
+    /* Clean up any resources used by this store. */
+    void (*destroy)(gpointer store);
+
+    /* Fetch an item with the given name, or return NULL if not found. */
+    BlueSkyRCStr * (*get)(gpointer store, const gchar *key);
+
+    /* Store an item to the given key name. */
+    void (*put)(gpointer store, const gchar *key, BlueSkyRCStr *val);
+} BlueSkyStoreImplementation;
+
+void bluesky_store_register(const BlueSkyStoreImplementation *impl,
+                            const gchar *name);
+
+struct _BlueSkyStore;
+typedef struct _BlueSkyStore BlueSkyStore;
+
+void bluesky_store_init();
+BlueSkyStore *bluesky_store_new(const gchar *type);
+void bluesky_store_free(BlueSkyStore *store);
+BlueSkyRCStr *bluesky_store_get(BlueSkyStore *store, const gchar *key);
+void bluesky_store_put(BlueSkyStore *store,
+                       const gchar *key, BlueSkyRCStr *val);
+
 /* File types.  The numeric values are chosen to match with those used in
  * NFSv3. */
 typedef enum {
@@ -61,7 +90,7 @@ typedef struct {
     GHashTable *inodes;         /* Cached inodes */
     uint64_t next_inum;         /* Next available inode for allocation */
 
-    struct S3Store *store;
+    BlueSkyStore *store;
 
     uint8_t *encryption_key;
 } BlueSkyFS;
@@ -170,10 +199,6 @@ void bluesky_file_write(BlueSkyInode *inode, uint64_t offset,
 void bluesky_file_read(BlueSkyInode *inode, uint64_t offset,
                        char *buf, gint len);
 
-struct S3Store *s3store_new();
-BlueSkyRCStr *s3store_get(struct S3Store *store, const gchar *key);
-void s3store_put(struct S3Store *store, const gchar *key, BlueSkyRCStr *val);
-
 #ifdef __cplusplus
 }
 #endif
index 5a4fe9b..4badc75 100644 (file)
@@ -154,7 +154,7 @@ void bluesky_block_fetch(BlueSkyFS *fs, BlueSkyBlock *block)
         return;
 
     g_print("Fetching block from %s\n", block->ref);
-    BlueSkyRCStr *string = s3store_get(fs->store, block->ref);
+    BlueSkyRCStr *string = bluesky_store_get(fs->store, block->ref);
 
     bluesky_string_unref(block->data);
     block->data = bluesky_crypt_decrypt(string, fs->encryption_key);
@@ -176,7 +176,7 @@ void bluesky_block_flush(BlueSkyFS *fs, BlueSkyBlock *block)
     gchar *name = g_strdup(g_checksum_get_string(csum));
 
     g_print("Flushing block as %s\n", name);
-    s3store_put(fs->store, name, data);
+    bluesky_store_put(fs->store, name, data);
     g_free(block->ref);
     block->ref = name;
 
diff --git a/bluesky/init.c b/bluesky/init.c
new file mode 100644 (file)
index 0000000..f12aae4
--- /dev/null
@@ -0,0 +1,28 @@
+/* Blue Sky: File Systems in the Cloud
+ *
+ * Copyright (C) 2009  The Regents of the University of California
+ * Written by Michael Vrable <mvrable@cs.ucsd.edu>
+ *
+ * TODO: Licensing
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <string.h>
+
+#include "bluesky.h"
+
+/* BlueSky library initialization. */
+
+void bluesky_store_init_s3(void);
+
+/* Initialize the BlueSky library and dependent libraries. */
+void bluesky_init(void)
+{
+    g_thread_init(NULL);
+    bluesky_crypt_init();
+
+    bluesky_store_init();
+    bluesky_store_init_s3();
+}
index d7d6c92..d8b80cf 100644 (file)
@@ -114,7 +114,7 @@ BlueSkyFS *bluesky_new_fs(gchar *name)
     fs->inodes = g_hash_table_new(bluesky_fs_key_hash_func,
                                   bluesky_fs_key_equal_func);
     fs->next_inum = BLUESKY_ROOT_INUM + 1;
-    fs->store = s3store_new();
+    fs->store = bluesky_store_new("file");
 
     return fs;
 }
index 47a8f60..4602136 100644 (file)
 /* Interface to Amazon S3 storage. */
 
 /* Simple in-memory data store for test purposes. */
-struct S3Store {
+typedef struct {
     S3BucketContext bucket;
-};
+} S3Store;
 
-struct S3Store *s3store_new()
+static gpointer s3store_new()
 {
-    struct S3Store *store = g_new(struct S3Store, 1);
+    S3Store *store = g_new(S3Store, 1);
     store->bucket.bucketName = "mvrable-bluesky";
     store->bucket.protocol = S3ProtocolHTTP;
     store->bucket.uriStyle = S3UriStylePath;
@@ -36,6 +36,11 @@ struct S3Store *s3store_new()
     return store;
 }
 
+static void s3store_destroy(gpointer store)
+{
+    g_free(store);
+}
+
 struct get_info {
     gchar *buf;
     gint offset;
@@ -73,7 +78,7 @@ static S3Status s3store_properties_callback(const S3ResponseProperties *properti
     return S3StatusOK;
 }
 
-void s3store_response_callback(S3Status status,
+static void s3store_response_callback(S3Status status,
                                const S3ErrorDetails *errorDetails,
                                void *callbackData)
 {
@@ -84,8 +89,10 @@ void s3store_response_callback(S3Status status,
     }
 }
 
-BlueSkyRCStr *s3store_get(struct S3Store *store, const gchar *key)
+static BlueSkyRCStr *s3store_get(gpointer s, const gchar *key)
 {
+    S3Store *store = (S3Store *)s;
+
     struct get_info info;
     info.buf = (char *)g_malloc0(BLUESKY_BLOCK_SIZE);
     info.offset = 0;
@@ -102,8 +109,10 @@ BlueSkyRCStr *s3store_get(struct S3Store *store, const gchar *key)
     return bluesky_string_new(info.buf, BLUESKY_BLOCK_SIZE);
 }
 
-void s3store_put(struct S3Store *store, const gchar *key, BlueSkyRCStr *val)
+static void s3store_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
 {
+    S3Store *store = (S3Store *)s;
+
     struct put_info info;
     info.val = val;
     info.offset = 0;
@@ -117,3 +126,16 @@ void s3store_put(struct S3Store *store, const gchar *key, BlueSkyRCStr *val)
     S3_put_object(&store->bucket, key, val->len, NULL, NULL,
                   &handler, &info);
 }
+
+static BlueSkyStoreImplementation store_impl = {
+    .create = s3store_new,
+    .destroy = s3store_destroy,
+    .get = s3store_get,
+    .put = s3store_put,
+};
+
+void bluesky_store_init_s3(void)
+{
+    S3_initialize(NULL, S3_INIT_ALL);
+    bluesky_store_register(&store_impl, "s3");
+}
index 9b509e6..abe7020 100644 (file)
  * interface, which different backends can implement.  Available backends
  * (will) include Amazon S3 and a simple local store for testing purposes. */
 
+struct _BlueSkyStore {
+    const BlueSkyStoreImplementation *impl;
+    gpointer handle;
+};
+
+GHashTable *store_implementations;
+
+void bluesky_store_register(const BlueSkyStoreImplementation *impl,
+                            const gchar *name)
+{
+    g_hash_table_insert(store_implementations, g_strdup(name), (gpointer)impl);
+}
+
+BlueSkyStore *bluesky_store_new(const gchar *type)
+{
+    const BlueSkyStoreImplementation *impl;
+
+    impl = g_hash_table_lookup(store_implementations, type);
+    if (impl == NULL)
+        return NULL;
+
+    gpointer handle = impl->create();
+    if (handle == NULL)
+        return NULL;
+
+    BlueSkyStore *store = g_new(BlueSkyStore, 1);
+    store->impl = impl;
+    store->handle = handle;
+    return store;
+}
+
+void bluesky_store_free(BlueSkyStore *store)
+{
+    store->impl->destroy(store->handle);
+    g_free(store);
+}
+
+BlueSkyRCStr *bluesky_store_get(BlueSkyStore *store, const gchar *key)
+{
+    return store->impl->get(store->handle, key);
+}
+
+void bluesky_store_put(BlueSkyStore *store,
+                       const gchar *key, BlueSkyRCStr *val)
+{
+    store->impl->put(store->handle, key, val);
+}
+
 /* Create and return a new reference-counted string.  The reference count is
  * initially one.  The newly-returned string takes ownership of the memory
  * pointed at by data, and will call g_free on it when the reference count
@@ -77,7 +125,7 @@ typedef struct {
     GHashTable *store;
 } MemStore;
 
-MemStore *memstore_new()
+static gpointer memstore_create()
 {
     MemStore *store = g_new(MemStore, 1);
     store->lock = g_mutex_new();
@@ -85,19 +133,75 @@ MemStore *memstore_new()
                                          g_free,
                                          (GDestroyNotify)bluesky_string_unref);
 
-    return store;
+    return (gpointer)store;
 }
 
-BlueSkyRCStr *memstore_get(MemStore *store, const gchar *key)
+static void memstore_destroy(gpointer store)
 {
+    /* TODO */
+}
+
+static BlueSkyRCStr *memstore_get(gpointer st, const gchar *key)
+{
+    MemStore *store = (MemStore *)st;
     BlueSkyRCStr *s = g_hash_table_lookup(store->store, key);
     if (s != NULL)
         bluesky_string_ref(s);
     return s;
 }
 
-void memstore_put(MemStore *store, const gchar *key, BlueSkyRCStr *val)
+static void memstore_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
 {
+    MemStore *store = (MemStore *)s;
     bluesky_string_ref(val);
     g_hash_table_insert(store->store, g_strdup(key), val);
 }
+
+static BlueSkyStoreImplementation memstore_impl = {
+    .create = memstore_create,
+    .destroy = memstore_destroy,
+    .get = memstore_get,
+    .put = memstore_put,
+};
+
+/* Store implementation which writes data as files to disk. */
+static gpointer filestore_create()
+{
+    return GINT_TO_POINTER(1);
+}
+
+static void filestore_destroy()
+{
+}
+
+static BlueSkyRCStr *filestore_get(gpointer s, const gchar *key)
+{
+    gchar *contents = NULL;
+    gsize length;
+    GError *error;
+
+    g_file_get_contents(key, &contents, &length, &error);
+    if (contents == NULL)
+        return NULL;
+
+    return bluesky_string_new(contents, length);
+}
+
+static void filestore_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
+{
+    g_file_set_contents(key, val->data, val->len, NULL);
+}
+
+static BlueSkyStoreImplementation filestore_impl = {
+    .create = filestore_create,
+    .destroy = filestore_destroy,
+    .get = filestore_get,
+    .put = filestore_put,
+};
+
+void bluesky_store_init()
+{
+    store_implementations = g_hash_table_new(g_str_hash, g_str_equal);
+    bluesky_store_register(&memstore_impl, "mem");
+    bluesky_store_register(&filestore_impl, "file");
+}
index f80b01a..8ba3ef0 100644 (file)
@@ -30,8 +30,7 @@ static uint8_t filesystem_key[16];
 int main(int argc, char *argv[])
 {
     int i;
-    g_thread_init(NULL);
-    bluesky_crypt_init();
+    bluesky_init();
     register_rpc();
 
     bluesky_crypt_random_bytes(filesystem_key, sizeof(filesystem_key));
@@ -41,8 +40,6 @@ int main(int argc, char *argv[])
     }
     printf("\n");
 
-    S3_initialize(NULL, S3_INIT_ALL);
-
     fs = bluesky_new_fs("export");
     fs->encryption_key = filesystem_key;