-PACKAGES=glib-2.0
+PACKAGES=glib-2.0 gthread-2.0
DEBUG=-g
CFLAGS=-O -Wall -D_FILE_OFFSET_BITS=64 $(DEBUG) \
$(shell pkg-config --cflags $(PACKAGES))
LDFLAGS=$(DEBUG) $(shell pkg-config --libs $(PACKAGES))
-SRCS=nfsd.c rpc.c mount.c mount_prot_xdr.c
+SRCS=dir.c inode.c main.c
OBJS=$(SRCS:.c=.o)
-nfsproxy : $(OBJS)
+bluesky : $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
clean :
- rm -f $(OBJS) nfsproxy
+ rm -f $(OBJS) bluesky
dep :
touch Makefile.dep
--- /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
+ */
+
+#ifndef _BLUESKY_H
+#define _BLUESKY_H
+
+#include <stdint.h>
+#include <glib.h>
+
+/* File types. The numeric values are chosen to match with those used in
+ * NFSv3. */
+enum BlueSkyFileType {
+ BLUESKY_INVALID = 0,
+ BLUESKY_REGULAR = 1,
+ BLUESKY_DIRECTORY = 2,
+ BLUESKY_BLOCK = 3,
+ BLUESKY_CHARACTER = 4,
+ BLUESKY_SYMLINK = 5,
+ BLUESKY_SOCKET = 6,
+ BLUESKY_FIFO = 7,
+};
+
+/* Filesystem state. Each filesystem which is exported is represented by a
+ * single bluesky_fs structure in memory. */
+typedef struct {
+ GMutex *lock;
+
+ gchar *name; /* Descriptive name for the filesystem */
+ GHashTable *inodes; /* Cached inodes */
+ uint64_t next_inum; /* Next available inode for allocation */
+} BlueSkyFS;
+
+/* Timestamp, measured in microseconds since the Unix epoch. */
+typedef int64_t bluesky_time;
+
+/* In-memory representation of an inode within a Blue Sky server. This
+ * corresponds roughly with information that is committed to persistent
+ * storage. */
+typedef struct {
+ gint refcnt; /* May be accessed atomically without lock */
+ GMutex *lock;
+
+ int type;
+ uint32_t mode;
+ uint32_t uid, gid;
+ uint32_t nlink;
+
+ /* Rather than track an inode number and generation number, we will simply
+ * never re-use a fileid after a file is deleted. 64 bits should be enough
+ * that we don't exhaust the identifier space. */
+ uint64_t inum;
+
+ uint64_t change_count; /* Incremented each with each change made */
+ int64_t atime; /* Microseconds since the Unix epoch */
+ int64_t ctime;
+ int64_t mtime;
+ int64_t ntime; /* "new time": time object was created */
+
+ /* File-specific fields */
+ uint64_t size;
+
+ /* Directory-specific fields */
+ GSequence *dirents;
+} BlueSkyInode;
+
+/* A directory entry. The name is UTF-8 and is a freshly-allocated string.
+ * The name is hashed to a 64-bit value, and the directory entries are sorted
+ * by hash value (the hash value can then be used as a cookie for resuming a
+ * READDIR call). */
+typedef struct {
+ gchar *name;
+ uint64_t hash;
+ uint64_t inum;
+} BlueSkyDirent ;
+
+int64_t bluesky_get_current_time();
+uint64_t bluesky_fs_alloc_inode(BlueSkyFS *fs);
+BlueSkyInode *bluesky_new_inode(uint64_t inum);
+
+void bluesky_dirent_destroy(gpointer dirent);
+uint64_t bluesky_directory_hash(gchar *name);
+uint64_t bluesky_directory_lookup(BlueSkyInode *inode, gchar *name);
+gboolean bluesky_directory_insert(BlueSkyInode *dir, gchar *name,
+ uint64_t inum);
+void bluesky_directory_dump(BlueSkyInode *dir);
+
+#endif
--- /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 <glib.h>
+
+#include "bluesky.h"
+
+/* Core filesystem: handling of directories. */
+
+/* Hash a filename for a directory lookup. The string is hashed to a 64-bit
+ * value. It is guaranteed that the hash values 0, 1, and 2 are never returned
+ * (to allow the hash value to be used as an NFS cookie for a READDIR
+ * operation). */
+uint64_t bluesky_directory_hash(gchar *name)
+{
+ GChecksum *csum = g_checksum_new(G_CHECKSUM_MD5);
+ g_checksum_update(csum, (guchar *)name, -1);
+
+ guint64 hashbytes[2];
+ gsize hashsize = sizeof(hashbytes);
+ g_checksum_get_digest(csum, (guint8 *)hashbytes, &hashsize);
+
+ uint64_t hash = GUINT64_FROM_BE(hashbytes[0]);
+ if (hash < 3)
+ hash = 3;
+
+ g_checksum_free(csum);
+
+ return hash;
+}
+
+void bluesky_dirent_destroy(gpointer data)
+{
+ BlueSkyDirent *dirent = (BlueSkyDirent *)data;
+ g_free(dirent->name);
+ g_free(dirent);
+}
+
+static gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
+ gpointer unused)
+{
+ /* We can't simply subtract the hash values, since they are 64-bit and the
+ * result could overflow when converted to a gint. */
+ uint64_t hash1 = ((const BlueSkyDirent *)a)->hash;
+ uint64_t hash2 = ((const BlueSkyDirent *)b)->hash;
+
+ if (hash1 < hash2)
+ return -1;
+ else if (hash1 > hash2)
+ return 1;
+ else
+ return 0;
+}
+
+/* Perform a lookup for a file name within a directory. Returns the inode
+ * number if found, or 0 if not (0 is never a valid inode number). Should be
+ * called with the inode lock already held. */
+uint64_t bluesky_directory_lookup(BlueSkyInode *inode, gchar *name)
+{
+ g_return_val_if_fail(inode->type == BLUESKY_DIRECTORY, 0);
+ g_return_val_if_fail(inode->dirents != NULL, 0);
+
+ /* First, construct a hash of the file name. Search the directory for a
+ * match, then check to see if it does really match. */
+ uint64_t hash = bluesky_directory_hash(name);
+
+ BlueSkyDirent d = {name, hash, 0};
+ GSequenceIter *i = g_sequence_search(inode->dirents, &d,
+ bluesky_dirent_compare, NULL);
+ i = g_sequence_iter_prev(i);
+
+ if (g_sequence_iter_is_end(i))
+ return 0;
+ BlueSkyDirent *dirent = g_sequence_get(i);
+ g_print("Lookup(%s) -> 0x%016llx\n", name, dirent->hash);
+ if (dirent->hash != hash)
+ return 0;
+ if (g_strcmp0(name, dirent->name) != 0)
+ return 0;
+
+ return dirent->inum;
+}
+
+/* Insert a new entry into a directory. Should be called with the inode lock
+ * already held. */
+gboolean bluesky_directory_insert(BlueSkyInode *dir, gchar *name, uint64_t inum)
+{
+ /* First, construct a hash of the file name. Search the directory for a
+ * match, then check to see if it does really match. */
+ uint64_t hash = bluesky_directory_hash(name);
+
+ BlueSkyDirent *d = g_new(BlueSkyDirent, 1);
+ d->name = name;
+ d->hash = hash;
+ d->inum = inum;
+
+ GSequenceIter *i = g_sequence_search(dir->dirents, d,
+ bluesky_dirent_compare, NULL);
+ g_sequence_insert_before(i, d);
+
+ return TRUE;
+}
+
+/* Dump the contents of a directory to stdout. Debugging only. */
+void bluesky_directory_dump(BlueSkyInode *dir)
+{
+ g_print("Directory dump:\n");
+
+ GSequenceIter *i = g_sequence_get_begin_iter(dir->dirents);
+
+ while (!g_sequence_iter_is_end(i)) {
+ BlueSkyDirent *d = g_sequence_get(i);
+ g_print(" 0x%016llx [inum=%lld] %s\n", d->hash, d->inum, d->name);
+ i = g_sequence_iter_next(i);
+ }
+}
#include <stdint.h>
#include <glib.h>
-#include "inode.h"
+#include "bluesky.h"
/* Core filesystem. Different proxies, such as the NFSv3 one, interface to
* this, but the core actually tracks the data which is stored. So far we just
* implement an in-memory filesystem, but eventually this will be state which
* is persisted to the cloud. */
-
-/* Hash a filename for a directory lookup. The string is hashed to a 64-bit
- * value. */
-uint64_t bluesky_directory_hash(gchar *name)
-{
- GChecksum *csum = g_checksum_new(G_CHECKSUM_MD5);
- g_checksum_update(csum, (guchar *)name, -1);
-
- guint64 hashbytes[2];
- gsize hashsize = sizeof(hashbytes);
- g_checksum_get_digest(csum, (guint8 *)hashbytes, &hashsize);
- return GUINT64_FROM_LE(hashbytes[0]);
-}
-
/* Return the current time, in microseconds since the epoch. */
int64_t bluesky_get_current_time()
{
return i;
}
-
-void bluesky_dirent_destroy(BlueSkyDirent *dirent)
-{
- g_free(dirent->name);
- g_free(dirent);
-}
-
-gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
- gpointer unused)
-{
- /* We can't simply subtract the hash values, since they are 64-bit and the
- * result could overflow when converted to a gint. */
- uint64_t hash1 = ((const BlueSkyDirent *)a)->hash;
- uint64_t hash2 = ((const BlueSkyDirent *)b)->hash;
-
- if (hash1 < hash2)
- return -1;
- else if (hash1 > hash2)
- return 1;
- else
- return 0;
-}
-
-/* Perform a lookup for a file name within a directory. Returns the inode
- * number if found, or 0 if not (0 is never a valid inode number). Should be
- * called with the inode lock already held. */
-uint64_t bluesky_directory_lookup(BlueSkyInode *inode, gchar *name)
-{
- g_return_val_if_fail(inode->type != BLUESKY_DIRECTORY, 0);
- g_return_val_if_fail(inode->dirents != NULL, 0);
-
- /* First, construct a hash of the file name. Search the directory for a
- * match, then check to see if it does really match. */
- uint64_t hash = bluesky_directory_hash(name);
-
- BlueSkyDirent d = {name, hash, 0};
- GSequenceIter *i = g_sequence_search(inode->dirents, &d,
- bluesky_dirent_compare, NULL);
-
- if (g_sequence_iter_is_end(i))
- return 0;
- BlueSkyDirent *dirent = g_sequence_get(i);
- if (dirent->hash != hash)
- return 0;
- if (g_strcmp0(name, dirent->name) != 0)
- return 0;
-
- return dirent->inum;
-}
+++ /dev/null
-#ifndef _BLUESKY_INODE_H
-#define _BLUESKY_INODE_H
-
-#include <stdint.h>
-#include <glib.h>
-
-/* File types. The numeric values are chosen to match with those used in
- * NFSv3. */
-enum BlueSkyFileType {
- BLUESKY_INVALID = 0,
- BLUESKY_REGULAR = 1,
- BLUESKY_DIRECTORY = 2,
- BLUESKY_BLOCK = 3,
- BLUESKY_CHARACTER = 4,
- BLUESKY_SYMLINK = 5,
- BLUESKY_SOCKET = 6,
- BLUESKY_FIFO = 7,
-};
-
-/* Filesystem state. Each filesystem which is exported is represented by a
- * single bluesky_fs structure in memory. */
-typedef struct {
- GMutex *lock;
-
- gchar *name; /* Descriptive name for the filesystem */
- GHashTable *inodes; /* Cached inodes */
- uint64_t next_inum; /* Next available inode for allocation */
-} BlueSkyFS;
-
-/* Timestamp, measured in microseconds since the Unix epoch. */
-typedef int64_t bluesky_time;
-
-/* In-memory representation of an inode within a Blue Sky server. This
- * corresponds roughly with information that is committed to persistent
- * storage. */
-typedef struct {
- gint refcnt; /* May be accessed atomically without lock */
- GMutex *lock;
-
- int type;
- uint32_t mode;
- uint32_t uid, gid;
- uint32_t nlink;
-
- /* Rather than track an inode number and generation number, we will simply
- * never re-use a fileid after a file is deleted. 64 bits should be enough
- * that we don't exhaust the identifier space. */
- uint64_t inum;
-
- uint64_t change_count; /* Incremented each with each change made */
- int64_t atime; /* Microseconds since the Unix epoch */
- int64_t ctime;
- int64_t mtime;
- int64_t ntime; /* "new time": time object was created */
-
- /* File-specific fields */
- uint64_t size;
-
- /* Directory-specific fields */
- GSequence *dirents;
-} BlueSkyInode;
-
-/* A directory entry. The name is UTF-8 and is a freshly-allocated string.
- * The name is hashed to a 64-bit value, and the directory entries are sorted
- * by hash value (the hash value can then be used as a cookie for resuming a
- * READDIR call). */
-typedef struct {
- gchar *name;
- uint64_t hash;
- uint64_t inum;
-} BlueSkyDirent ;
-
-uint64_t bluesky_directory_hash(gchar *name);
-
-#endif
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "bluesky.h"
+
+/* Small test program for BlueSkyFS. Doesn't do much useful. */
+
+int main(int argc, char *argv[])
+{
+ g_thread_init(NULL);
+
+ printf("BlueSkyFS starting...\n");
+
+ BlueSkyInode *root = bluesky_new_inode(1);
+ root->type = BLUESKY_DIRECTORY;
+ root->dirents = g_sequence_new(bluesky_dirent_destroy);
+
+ bluesky_directory_insert(root, "foo", 2);
+ bluesky_directory_insert(root, "bar", 3);
+ bluesky_directory_insert(root, "baz", 4);
+ bluesky_directory_insert(root, "baz", 5);
+
+ bluesky_directory_dump(root);
+ bluesky_directory_lookup(root, "foo");
+ bluesky_directory_lookup(root, "bar");
+ bluesky_directory_lookup(root, "baz");
+
+ return 0;
+}