CFLAGS=-O -Wall -D_FILE_OFFSET_BITS=64 $(DEBUG) \
$(shell pkg-config --cflags $(PACKAGES))
LDFLAGS=$(DEBUG) $(shell pkg-config --libs $(PACKAGES))
+SUBDIRS=nfs3
-SRCS=dir.c inode.c main.c
+SRCS=dir.c inode.c
OBJS=$(SRCS:.c=.o)
-bluesky : $(OBJS)
+all : bluesky
+ for d in $(SUBDIRS); do $(MAKE) -C $$d; done
+
+bluesky.a : $(OBJS)
+ $(AR) -r $@ $(OBJS)
+ ranlib $@
+
+bluesky : main.o bluesky.a
$(CC) $(LDFLAGS) -o $@ $^
clean :
touch Makefile.dep
makedepend -fMakefile.dep $(SRCS)
-.PHONY : clean dep
+.PHONY : all clean dep
-include *.dep
/* File types. The numeric values are chosen to match with those used in
* NFSv3. */
-enum BlueSkyFileType {
- BLUESKY_INVALID = 0,
+typedef enum {
BLUESKY_REGULAR = 1,
BLUESKY_DIRECTORY = 2,
BLUESKY_BLOCK = 3,
BLUESKY_SYMLINK = 5,
BLUESKY_SOCKET = 6,
BLUESKY_FIFO = 7,
-};
+} BlueSkyFileType;
/* Filesystem state. Each filesystem which is exported is represented by a
* single bluesky_fs structure in memory. */
uint64_t next_inum; /* Next available inode for allocation */
} BlueSkyFS;
+/* Inode number of the root directory. */
+#define BLUESKY_ROOT_INUM 1
+
/* Timestamp, measured in microseconds since the Unix epoch. */
typedef int64_t bluesky_time;
gint refcnt; /* May be accessed atomically without lock */
GMutex *lock;
- int type;
+ BlueSkyFileType type;
uint32_t mode;
uint32_t uid, gid;
uint32_t nlink;
/* Directory-specific fields */
GSequence *dirents;
+ uint64_t parent_inum; /* inode for ".."; 0 if the root directory */
} BlueSkyInode;
/* A directory entry. The name is UTF-8 and is a freshly-allocated string.
int64_t bluesky_get_current_time();
uint64_t bluesky_fs_alloc_inode(BlueSkyFS *fs);
-BlueSkyInode *bluesky_new_inode(uint64_t inum);
+BlueSkyInode *bluesky_new_inode(uint64_t inum, BlueSkyFileType type);
+
+BlueSkyInode *bluesky_get_inode(BlueSkyFS *fs, uint64_t inum);
+void bluesky_insert_inode(BlueSkyFS *fs, BlueSkyInode *inode);
void bluesky_dirent_destroy(gpointer dirent);
uint64_t bluesky_directory_hash(gchar *name);
/* 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). */
+ * operation). TODO: We perhaps should make this a keyed hash, and may want to
+ * use something cheaper than MD5 to compute. */
uint64_t bluesky_directory_hash(gchar *name)
{
GChecksum *csum = g_checksum_new(G_CHECKSUM_MD5);
BlueSkyDirent d = {name, hash, 0};
GSequenceIter *i = g_sequence_search(inode->dirents, &d,
bluesky_dirent_compare, NULL);
+
+ /* g_sequence_search will return an iterator pointing just beyond the
+ * desired node if there is a match, so that when inserting the new node
+ * will go after the match. But we are trying to do a lookup, so back up
+ * by one position. */
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;
+ g_print("Lookup(%s) -> 0x%016llx\n", name, dirent->hash);
return dirent->inum;
}
uint64_t hash = bluesky_directory_hash(name);
BlueSkyDirent *d = g_new(BlueSkyDirent, 1);
- d->name = name;
+ d->name = g_strdup(name);
d->hash = hash;
d->inum = inum;
GSequenceIter *i = g_sequence_search(dir->dirents, d,
bluesky_dirent_compare, NULL);
+
+ /* If a directory entry already exists, return failure. The caller must
+ * delete the old entry and try again. TODO: We'll fail on a hash
+ * collision; we should handle that case. */
+ if (!g_sequence_iter_is_end(g_sequence_iter_prev(i))) {
+ BlueSkyDirent *d2 = g_sequence_get(g_sequence_iter_prev(i));
+ if (d2->hash == hash)
+ return FALSE;
+ }
+
g_sequence_insert_before(i, d);
+ dir->change_count++;
return TRUE;
}
return t.tv_sec * 1000000 + t.tv_usec;
}
+/* Unfortunately a glib hash table is only guaranteed to be able to store
+ * 32-bit keys if we use the key directly. If we want 64-bit inode numbers,
+ * we'll have to allocate memory to store the 64-bit inumber, and use a pointer
+ * to it. Rather than allocate the memory for the key, we'll just include a
+ * pointer to the 64-bit inum stored in the inode itself, so that we don't need
+ * to do any more memory management. */
+static guint bluesky_fs_key_hash_func(gconstpointer key)
+{
+ uint64_t inum = *(const uint64_t *)key;
+ return (guint)inum;
+}
+
+static gboolean bluesky_fs_key_equal_func(gconstpointer a, gconstpointer b)
+{
+ uint64_t i1 = *(const uint64_t *)a;
+ uint64_t i2 = *(const uint64_t *)b;
+ return i1 == i2;
+}
+
+/* Filesystem-level operations. A filesystem is like a directory tree that we
+ * are willing to export. */
+BlueSkyFS *bluesky_new_fs(gchar *name)
+{
+ BlueSkyFS *fs = g_new0(BlueSkyFS, 1);
+ fs->lock = g_mutex_new();
+ fs->name = g_strdup(name);
+ fs->inodes = g_hash_table_new(bluesky_fs_key_hash_func,
+ bluesky_fs_key_equal_func);
+ fs->next_inum = BLUESKY_ROOT_INUM + 1;
+
+ return fs;
+}
+
/* Allocate a fresh inode number which has not been used before within a
* filesystem. */
uint64_t bluesky_fs_alloc_inode(BlueSkyFS *fs)
return inum;
}
-BlueSkyInode *bluesky_new_inode(uint64_t inum)
+BlueSkyInode *bluesky_new_inode(uint64_t inum, BlueSkyFileType type)
{
BlueSkyInode *i = g_new0(BlueSkyInode, 1);
i->lock = g_mutex_new();
+ i->type = type;
i->inum = inum;
+ switch (type) {
+ case BLUESKY_REGULAR:
+ break;
+ case BLUESKY_DIRECTORY:
+ i->dirents = g_sequence_new(bluesky_dirent_destroy);
+ case BLUESKY_BLOCK:
+ case BLUESKY_CHARACTER:
+ case BLUESKY_SYMLINK:
+ case BLUESKY_SOCKET:
+ case BLUESKY_FIFO:
+ break;
+ }
+
return i;
}
+
+/* Retrieve an inode from the filesystem. Eventually this will be a cache and
+ * so we might need to go fetch the inode from elsewhere; for now all
+ * filesystem state is stored here. */
+BlueSkyInode *bluesky_get_inode(BlueSkyFS *fs, uint64_t inum)
+{
+ BlueSkyInode *inode = NULL;
+
+ g_mutex_lock(fs->lock);
+ inode = (BlueSkyInode *)g_hash_table_lookup(fs->inodes, &inum);
+ g_mutex_unlock(fs->lock);
+
+ return inode;
+}
+
+/* Insert an inode into the filesystem inode cache. */
+void bluesky_insert_inode(BlueSkyFS *fs, BlueSkyInode *inode)
+{
+ g_mutex_lock(fs->lock);
+ g_hash_table_insert(fs->inodes, &inode->inum, inode);
+ g_mutex_unlock(fs->lock);
+}
printf("BlueSkyFS starting...\n");
- BlueSkyInode *root = bluesky_new_inode(1);
- root->type = BLUESKY_DIRECTORY;
- root->dirents = g_sequence_new(bluesky_dirent_destroy);
+ BlueSkyInode *root = bluesky_new_inode(1, BLUESKY_DIRECTORY);
bluesky_directory_insert(root, "foo", 2);
bluesky_directory_insert(root, "bar", 3);
bluesky_directory_lookup(root, "foo");
bluesky_directory_lookup(root, "bar");
bluesky_directory_lookup(root, "baz");
+ bluesky_directory_lookup(root, "boo");
return 0;
}
-PACKAGES=glib-2.0
+PACKAGES=glib-2.0 gthread-2.0
DEBUG=-g
-CFLAGS=-O -Wall -D_FILE_OFFSET_BITS=64 $(DEBUG) \
+CFLAGS=-O -Wall -D_FILE_OFFSET_BITS=64 $(DEBUG) -I.. \
$(shell pkg-config --cflags $(PACKAGES))
LDFLAGS=$(DEBUG) $(shell pkg-config --libs $(PACKAGES))
OBJS=$(SRCS:.c=.o)
nfsproxy : $(OBJS)
- $(CC) $(LDFLAGS) -o $@ $^
+ $(CC) $(LDFLAGS) -o $@ $^ ../bluesky.a
clean :
rm -f $(OBJS) nfsproxy
mountres3 *
mountproc3_mnt_3_svc(dirpath *argp, struct svc_req *rqstp)
{
- static char fhbytes[] = {0, 0, 0, 1};
- static fhandle3 fh = {4, fhbytes};
+ static char fhbytes[] = {0, 0, 0, 0, 0, 0, 0, 1};
+ static fhandle3 fh = {8, fhbytes};
static mountres3 result;
static int auth_flavors = AUTH_UNIX;
*/
#include "nfs3_prot.h"
+#include "bluesky.h"
+
+extern BlueSkyFS *fs;
static int null_int;
static void *null_result = (void *)&null_int;
-void encode_fattr3(struct fattr3 *result, uint64_t inum)
+/* Look up a BlueSkyInode given an NFS filehandle. Returns NULL if the
+ * filehandle is invalid. */
+BlueSkyInode *lookup_fh(nfs_fh3 *fh)
{
- result->type = NF3DIR;
- result->mode = 0755;
- result->nlink = 1;
- result->uid = 0;
- result->gid = 0;
- result->size = 0;
+ BlueSkyInode *inode = NULL;
+ if (fh->data.data_len == 8) {
+ uint64_t inum = GUINT64_FROM_BE(*(uint64_t *)(fh->data.data_val));
+ inode = bluesky_get_inode(fs, inum);
+ }
+ return inode;
+}
+
+/* Copy inode attributes into NFS response. The BlueSkyInode should be locked
+ * by the caller. */
+void encode_fattr3(struct fattr3 *result, BlueSkyInode *inode)
+{
+ result->type = inode->type;
+ result->mode = inode->mode;
+ result->nlink = inode->nlink;
+ result->uid = inode->uid;
+ result->gid = inode->gid;
+ result->size = inode->size;
result->used = 0;
result->rdev.major = 0;
result->rdev.minor = 0;
result->fsid = 0;
- result->fileid = inum;
- result->atime.seconds = 0;
- result->atime.nseconds = 0;
- result->mtime.seconds = 0;
- result->mtime.nseconds = 0;
- result->ctime.seconds = 0;
- result->ctime.nseconds = 0;
+ result->fileid = inode->inum;
+ result->atime.seconds = inode->atime / 1000000;
+ result->atime.nseconds = (inode->atime % 1000000) * 1000;
+ result->mtime.seconds = inode->mtime / 1000000;
+ result->mtime.nseconds = (inode->mtime % 1000000) * 1000;
+ result->ctime.seconds = inode->ctime / 1000000;
+ result->ctime.nseconds = (inode->ctime % 1000000) * 1000;
}
void *
{
static getattr3res result;
- result.status = NFS3_OK;
- encode_fattr3(&result.getattr3res_u.attributes, 1);
+ BlueSkyInode *inode = lookup_fh(argp);
+ if (inode != NULL) {
+ result.status = NFS3_OK;
+ encode_fattr3(&result.getattr3res_u.attributes, inode);
+ } else {
+ result.status = NFS3ERR_STALE;
+ }
return &result;
}
{
static fsinfo3res result;
+ BlueSkyInode *inode = bluesky_get_inode(fs, 1);
result.status = NFS3_OK;
result.fsinfo3res_u.resok.obj_attributes.present = TRUE;
- encode_fattr3(&result.fsinfo3res_u.resok.obj_attributes.post_op_attr_u.attributes, 1);
+ encode_fattr3(&result.fsinfo3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
result.fsinfo3res_u.resok.rtmax = 32768;
result.fsinfo3res_u.resok.rtpref = 32768;
result.fsinfo3res_u.resok.rtmult = 4096;
{
static pathconf3res result;
+ BlueSkyInode *inode = bluesky_get_inode(fs, 1);
result.status = NFS3_OK;
result.pathconf3res_u.resok.obj_attributes.present = TRUE;
- encode_fattr3(&result.pathconf3res_u.resok.obj_attributes.post_op_attr_u.attributes, 1);
+ encode_fattr3(&result.pathconf3res_u.resok.obj_attributes.post_op_attr_u.attributes, inode);
result.pathconf3res_u.resok.linkmax = 0xffffffff;
result.pathconf3res_u.resok.name_max = 255;
result.pathconf3res_u.resok.no_trunc = TRUE;
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <glib.h>
+
+#include "bluesky.h"
void register_rpc();
+BlueSkyFS *fs;
+
int main(int argc, char *argv[])
{
+ g_thread_init(NULL);
register_rpc();
+ BlueSkyInode *root;
+ root = bluesky_new_inode(BLUESKY_ROOT_INUM, BLUESKY_DIRECTORY);
+ root->nlink = 1;
+ root->mode = 0755;
+ bluesky_insert_inode(fs, root);
+
svc_run ();
fprintf (stderr, "%s", "svc_run returned");
exit (1);