cmake_minimum_required(VERSION 2.6)
project(bluesky)
+set(CMAKE_BUILD_TYPE Debug)
include(FindPkgConfig)
pkg_check_modules(GLIB REQUIRED glib-2.0 gthread-2.0)
link_directories(/home/mvrable/scratch/libs3-1.4/build/lib)
-add_library(bluesky SHARED dir.c inode.c store.c s3store.c)
+add_library(bluesky SHARED crypto.c dir.c inode.c store.c s3store.c)
add_executable(bluesky-test main.c)
set(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
set(INSTALL_RPATH_USE_LINK_PATH 1)
include_directories(${GLIB_INCLUDE_DIRS})
-target_link_libraries(bluesky ${GLIB_LIBRARIES} s3)
+target_link_libraries(bluesky ${GLIB_LIBRARIES} gcrypt s3)
target_link_libraries(bluesky-test bluesky ${GLIB_LIBRARIES})
#set_target_properties(bluesky PROPERTIES LINK_INTERFACE_LIBRARIES "")
void bluesky_string_unref(BlueSkyRCStr *string);
BlueSkyRCStr *bluesky_string_dup(BlueSkyRCStr *string);
+/* Cryptographic operations. */
+void bluesky_crypt_init();
+void bluesky_crypt_random_bytes(guchar *buf, gint len);
+BlueSkyRCStr *bluesky_crypt_encrypt(BlueSkyRCStr *in, const uint8_t *key);
+
/* File types. The numeric values are chosen to match with those used in
* NFSv3. */
typedef enum {
uint64_t next_inum; /* Next available inode for allocation */
struct S3Store *store;
+
+ uint8_t *encryption_key;
} BlueSkyFS;
/* Inode number of the root directory. */
/* In-memory representation of an inode within a Blue Sky server. This
* corresponds roughly with information that is committed to persistent
- * storage. */
+ * storage. Locking/refcounting rules:
+ * - To access or modify any data fields, the lock must be held. This
+ * includes file blocks.
+ * - One reference is held by the BlueSkyFS inode hash table. If that is the
+ * only reference (and the inode is unlocked), the inode is subject to
+ * dropping from the cache.
+ * - Any pending operations should hold extra references to the inode as
+ * appropriate to keep it available until the operation completes.
+ * */
typedef struct {
- gint refcnt; /* May be accessed atomically without lock */
GMutex *lock;
+ gint refcount;
BlueSkyFS *fs;
--- /dev/null
+/* 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 <errno.h>
+#include <pthread.h>
+#include <glib.h>
+#include <string.h>
+#include <gcrypt.h>
+
+#include "bluesky.h"
+
+/* Cryptographic operations. The rest of the BlueSky code merely calls into
+ * the functions in this file, so this is the only point where we interface
+ * with an external cryptographic library. */
+
+#define CRYPTO_BLOCK_SIZE 16 /* 128-bit AES */
+#define CRYPTO_KEY_SIZE 16
+
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+void bluesky_crypt_init()
+{
+ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+
+ if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return;
+
+ g_print("libgcrypt not yet initialized, initializing...\n");
+
+ if (!gcry_check_version(GCRYPT_VERSION))
+ g_error("libgcrypt version mismatch\n");
+
+ gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+/* Return cryptographically-strong random data. */
+void bluesky_crypt_random_bytes(guchar *buf, gint len)
+{
+ gcry_randomize(buf, len, GCRY_STRONG_RANDOM);
+}
+
+/* Encrypt a data block. */
+BlueSkyRCStr *bluesky_crypt_encrypt(BlueSkyRCStr *in, const uint8_t *key)
+{
+ gcry_error_t status;
+ gcry_cipher_hd_t handle;
+
+ status = gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC,
+ GCRY_CIPHER_CBC_CTS);
+ if (status) {
+ g_error("gcrypt error setting up encryption: %s\n",
+ gcry_strerror(status));
+ }
+
+ uint8_t *out = g_malloc0(in->len + CRYPTO_BLOCK_SIZE);
+
+ gcry_cipher_setkey(handle, key, CRYPTO_KEY_SIZE);
+ if (status) {
+ g_error("gcrypt error setting key: %s\n",
+ gcry_strerror(status));
+ }
+
+ bluesky_crypt_random_bytes(out, CRYPTO_BLOCK_SIZE);
+ status = gcry_cipher_setiv(handle, out, CRYPTO_BLOCK_SIZE);
+ if (status) {
+ g_error("gcrypt error setting IV: %s\n",
+ gcry_strerror(status));
+ }
+
+ gcry_cipher_encrypt(handle, out + CRYPTO_BLOCK_SIZE, in->len,
+ in->data, in->len);
+ if (status) {
+ g_error("gcrypt error encrypting: %s\n",
+ gcry_strerror(status));
+ }
+
+ gcry_cipher_close(handle);
+
+ return bluesky_string_new(out, in->len + CRYPTO_BLOCK_SIZE);
+}
return fs;
}
+/* Inode reference counting. */
+void bluesky_inode_ref(BlueSkyInode *inode)
+{
+ g_atomic_int_inc(&inode->refcount);
+}
+
+void bluesky_inode_unref(BlueSkyInode *inode)
+{
+ if (g_atomic_int_dec_and_test(&inode->refcount)) {
+ g_error("Reference count for inode %lld dropped to zero!\n",
+ inode->inum);
+ }
+}
+
/* Allocate a fresh inode number which has not been used before within a
* filesystem. */
uint64_t bluesky_fs_alloc_inode(BlueSkyFS *fs)
BlueSkyInode *i = g_new0(BlueSkyInode, 1);
i->lock = g_mutex_new();
+ i->refcount = 1;
i->type = type;
i->fs = fs;
i->inum = inum;
if (block->type != BLUESKY_BLOCK_DIRTY)
return;
+ BlueSkyRCStr *data = block->data;
+ data = bluesky_crypt_encrypt(data, fs->encryption_key);
+
GChecksum *csum = g_checksum_new(G_CHECKSUM_SHA256);
- g_checksum_update(csum, block->data->data, block->data->len);
+ g_checksum_update(csum, data->data, data->len);
gchar *name = g_strdup(g_checksum_get_string(csum));
g_print("Flushing block as %s\n", name);
- s3store_put(fs->store, name, block->data);
+ s3store_put(fs->store, name, data);
g_free(block->ref);
block->ref = name;
block->type = BLUESKY_BLOCK_REF;
g_checksum_free(csum);
+ bluesky_string_unref(data);
}
void register_rpc();
BlueSkyFS *fs;
+static uint8_t filesystem_key[16];
int main(int argc, char *argv[])
{
+ int i;
g_thread_init(NULL);
+ bluesky_crypt_init();
register_rpc();
+ bluesky_crypt_random_bytes(filesystem_key, sizeof(filesystem_key));
+ printf("Filesystem key: ");
+ for (i = 0; i < sizeof(filesystem_key); i++) {
+ printf("%02x", filesystem_key[i]);
+ }
+ printf("\n");
+
S3_initialize(NULL, S3_INIT_ALL);
fs = bluesky_new_fs("export");
+ fs->encryption_key = filesystem_key;
BlueSkyInode *root;
root = bluesky_new_inode(BLUESKY_ROOT_INUM, fs, BLUESKY_DIRECTORY);