From 8819789ef2264b26aebfae489932a447f6e0f65f Mon Sep 17 00:00:00 2001 From: Michael Vrable Date: Fri, 21 Aug 2009 09:58:44 -0700 Subject: [PATCH] Hook NFS proxy together with BlueSky core. --- .gitignore | 1 + Makefile | 14 +++++++--- bluesky.h | 16 ++++++++---- dir.c | 23 ++++++++++++++--- inode.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++- main.c | 5 ++-- nfs3/Makefile | 6 ++--- nfs3/mount.c | 4 +-- nfs3/nfs3.c | 60 ++++++++++++++++++++++++++++++------------- nfs3/nfsd.c | 12 +++++++++ 10 files changed, 174 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 5761abc..827a703 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.o +bluesky.a diff --git a/Makefile b/Makefile index 386cc23..7f5b123 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,19 @@ DEBUG=-g 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 : @@ -17,6 +25,6 @@ dep : touch Makefile.dep makedepend -fMakefile.dep $(SRCS) -.PHONY : clean dep +.PHONY : all clean dep -include *.dep diff --git a/bluesky.h b/bluesky.h index 068173f..40c989b 100644 --- a/bluesky.h +++ b/bluesky.h @@ -14,8 +14,7 @@ /* 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, @@ -23,7 +22,7 @@ enum BlueSkyFileType { 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. */ @@ -35,6 +34,9 @@ typedef struct { 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; @@ -45,7 +47,7 @@ typedef struct { gint refcnt; /* May be accessed atomically without lock */ GMutex *lock; - int type; + BlueSkyFileType type; uint32_t mode; uint32_t uid, gid; uint32_t nlink; @@ -66,6 +68,7 @@ typedef struct { /* 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. @@ -80,7 +83,10 @@ typedef struct { 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); diff --git a/dir.c b/dir.c index c4cbec2..eab71be 100644 --- a/dir.c +++ b/dir.c @@ -16,7 +16,8 @@ /* 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); @@ -73,17 +74,22 @@ uint64_t bluesky_directory_lookup(BlueSkyInode *inode, gchar *name) 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; } @@ -96,13 +102,24 @@ gboolean bluesky_directory_insert(BlueSkyInode *dir, gchar *name, uint64_t 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; } diff --git a/inode.c b/inode.c index ec90757..b9da798 100644 --- a/inode.c +++ b/inode.c @@ -24,6 +24,39 @@ int64_t bluesky_get_current_time() 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) @@ -38,12 +71,48 @@ 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); +} diff --git a/main.c b/main.c index 6ddc7b4..9c92356 100644 --- a/main.c +++ b/main.c @@ -21,9 +21,7 @@ int main(int argc, char *argv[]) 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); @@ -34,6 +32,7 @@ int main(int argc, char *argv[]) bluesky_directory_lookup(root, "foo"); bluesky_directory_lookup(root, "bar"); bluesky_directory_lookup(root, "baz"); + bluesky_directory_lookup(root, "boo"); return 0; } diff --git a/nfs3/Makefile b/nfs3/Makefile index 87b2c04..59ee50b 100644 --- a/nfs3/Makefile +++ b/nfs3/Makefile @@ -1,6 +1,6 @@ -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)) @@ -8,7 +8,7 @@ SRCS=nfsd.c rpc.c mount.c nfs3.c mount_prot_xdr.c nfs3_prot_xdr.c OBJS=$(SRCS:.c=.o) nfsproxy : $(OBJS) - $(CC) $(LDFLAGS) -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ ../bluesky.a clean : rm -f $(OBJS) nfsproxy diff --git a/nfs3/mount.c b/nfs3/mount.c index e4eb3a1..8b2ba7b 100644 --- a/nfs3/mount.c +++ b/nfs3/mount.c @@ -19,8 +19,8 @@ mountproc3_null_3_svc(void *argp, struct svc_req *rqstp) 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; diff --git a/nfs3/nfs3.c b/nfs3/nfs3.c index cbf5650..25552a7 100644 --- a/nfs3/nfs3.c +++ b/nfs3/nfs3.c @@ -5,29 +5,46 @@ */ #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 * @@ -41,8 +58,13 @@ nfsproc3_getattr_3_svc(nfs_fh3 *argp, struct svc_req *rqstp) { 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; } @@ -222,9 +244,10 @@ nfsproc3_fsinfo_3_svc(nfs_fh3 *argp, struct svc_req *rqstp) { 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; @@ -246,9 +269,10 @@ nfsproc3_pathconf_3_svc(nfs_fh3 *argp, struct svc_req *rqstp) { 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; diff --git a/nfs3/nfsd.c b/nfs3/nfsd.c index 7a0338d..03b39fb 100644 --- a/nfs3/nfsd.c +++ b/nfs3/nfsd.c @@ -17,13 +17,25 @@ #include #include #include +#include + +#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); -- 2.20.1