Begin adding write support.
authorMichael Vrable <mvrable@cs.ucsd.edu>
Tue, 25 Aug 2009 23:10:11 +0000 (16:10 -0700)
committerMichael Vrable <mvrable@turin.ucsd.edu>
Tue, 25 Aug 2009 23:10:11 +0000 (16:10 -0700)
Right now we only change the file size, but don't actually store the data.

Makefile
bluesky.h
inode.c
main.c
nfs3/nfs3.c

index 7f5b123..8a1ea81 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 PACKAGES=glib-2.0 gthread-2.0
 DEBUG=-g
-CFLAGS=-O -Wall -D_FILE_OFFSET_BITS=64 $(DEBUG) \
+CFLAGS=-O -std=gnu99 -Wall -D_FILE_OFFSET_BITS=64 $(DEBUG) \
        $(shell pkg-config --cflags $(PACKAGES))
 LDFLAGS=$(DEBUG) $(shell pkg-config --libs $(PACKAGES))
 SUBDIRS=nfs3
@@ -20,6 +20,7 @@ bluesky : main.o bluesky.a
 
 clean :
        rm -f $(OBJS) bluesky
+       for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
 
 dep :
        touch Makefile.dep
index 75ba2de..c85fd70 100644 (file)
--- a/bluesky.h
+++ b/bluesky.h
@@ -65,6 +65,7 @@ typedef struct {
 
     /* File-specific fields */
     uint64_t size;
+    GArray *blocks;
 
     /* Directory-specific fields */
     GSequence *dirents;
@@ -79,7 +80,26 @@ typedef struct {
     gchar *name;
     uint64_t hash;
     uint64_t inum;
-} BlueSkyDirent ;
+} BlueSkyDirent;
+
+/* File data is divided into fixed-size blocks (except the last block which may
+ * be short?).  These blocks are backed by storage in a key/value store, but
+ * may also be dirty if modifications have been made in-memory that have not
+ * been committed. */
+#define BLUESKY_BLOCK_SIZE 32768ULL
+#define BLUESKY_MAX_FILE_SIZE (BLUESKY_BLOCK_SIZE << 24)
+typedef enum {
+    BLUESKY_BLOCK_ZERO = 0,     /* Data is all zeroes, not explicitly stored */
+    BLUESKY_BLOCK_REF = 1,      /* Reference to key/value store, not cached */
+    BLUESKY_BLOCK_CACHED = 2,   /* Data is cached in memory, clean */
+    BLUESKY_BLOCK_DIRTY = 3,    /* Data needs to be committed to store */
+} BlueSkyBlockType;
+
+typedef struct {
+    BlueSkyBlockType type;
+    gchar *ref;                 /* Name of data block in the backing store */
+    gchar *data;                /* Pointer to data in memory */
+} BlueSkyBlock;
 
 BlueSkyFS *bluesky_new_fs(gchar *name);
 int64_t bluesky_get_current_time();
@@ -96,4 +116,6 @@ gboolean bluesky_directory_insert(BlueSkyInode *dir, gchar *name,
                                   uint64_t inum);
 void bluesky_directory_dump(BlueSkyInode *dir);
 
+void bluesky_file_truncate(BlueSkyInode *inode, uint64_t size);
+
 #endif
diff --git a/inode.c b/inode.c
index 4437c04..95afc0a 100644 (file)
--- a/inode.c
+++ b/inode.c
@@ -81,9 +81,11 @@ BlueSkyInode *bluesky_new_inode(uint64_t inum, BlueSkyFileType type)
 
     switch (type) {
     case BLUESKY_REGULAR:
+        i->blocks = g_array_new(FALSE, TRUE, sizeof(BlueSkyBlock));
         break;
     case BLUESKY_DIRECTORY:
         i->dirents = g_sequence_new(bluesky_dirent_destroy);
+        break;
     case BLUESKY_BLOCK:
     case BLUESKY_CHARACTER:
     case BLUESKY_SYMLINK:
@@ -116,3 +118,35 @@ void bluesky_insert_inode(BlueSkyFS *fs, BlueSkyInode *inode)
     g_hash_table_insert(fs->inodes, &inode->inum, inode);
     g_mutex_unlock(fs->lock);
 }
+
+/* Set the size of a file.  This will truncate or extend the file as needed.
+ * Newly-allocated bytes are zeroed. */
+void bluesky_file_truncate(BlueSkyInode *inode, uint64_t size)
+{
+    g_return_if_fail(size <= BLUESKY_MAX_FILE_SIZE);
+
+    if (size == inode->size)
+        return;
+
+    uint64_t blocks = (size + BLUESKY_BLOCK_SIZE - 1) / BLUESKY_MAX_FILE_SIZE;
+
+    if (blocks > inode->blocks->len) {
+        /* Need to add new blocks to the end of a file.  New block structures
+         * are automatically zeroed, which initializes them to be pointers to
+         * zero blocks so we don't need to do any more work. */
+        g_array_set_size(inode->blocks, blocks);
+    } else if (blocks < inode->blocks->len) {
+        /* Delete blocks from a file.  Must reclaim memory. */
+        for (guint i = inode->blocks->len; i < blocks; i++) {
+            BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock, i);
+            g_free(b->ref);
+            g_free(b->data);
+        }
+        g_array_set_size(inode->blocks, blocks);
+    }
+
+    /* TODO: Zero out partial blocks if needed? */
+
+    inode->size = size;
+    inode->change_count++;
+}
diff --git a/main.c b/main.c
index 596f7cb..62fcd0c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -23,18 +23,24 @@ int main(int argc, char *argv[])
 
     printf("  time = %lld\n", bluesky_get_current_time());
 
-    BlueSkyInode *root = bluesky_new_inode(1, BLUESKY_DIRECTORY);
+    BlueSkyFS *fs = bluesky_new_fs("export");
 
-    bluesky_directory_insert(root, "foo", 2);
-    bluesky_directory_insert(root, "bar", 3);
-    bluesky_directory_insert(root, "baz", 4);
-    bluesky_directory_insert(root, "baz", 5);
+    BlueSkyInode *root;
+    root = bluesky_new_inode(BLUESKY_ROOT_INUM, BLUESKY_DIRECTORY);
+    root->nlink = 1;
+    root->mode = 0755;
+    bluesky_insert_inode(fs, root);
+
+    BlueSkyInode *file;
+    file = bluesky_new_inode(bluesky_fs_alloc_inode(fs), BLUESKY_REGULAR);
+    file->nlink = 1;
+    file->mode = 0755;
+    bluesky_insert_inode(fs, file);
+    bluesky_directory_insert(root, "demo", file->inum);
 
     bluesky_directory_dump(root);
     bluesky_directory_lookup(root, "foo");
-    bluesky_directory_lookup(root, "bar");
-    bluesky_directory_lookup(root, "baz");
-    bluesky_directory_lookup(root, "boo");
+    bluesky_directory_lookup(root, "demo");
 
     return 0;
 }
index aaf23b4..90f62d7 100644 (file)
@@ -198,8 +198,33 @@ write3res *
 nfsproc3_write_3_svc(write3args *argp, struct svc_req *rqstp)
 {
     static write3res result;
+    struct wcc_data wcc;
+    memset(&wcc, 0, sizeof(wcc));
 
-    result.status = NFS3ERR_NOTSUPP;
+    BlueSkyInode *inode = lookup_fh(&argp->file);
+    if (inode == NULL) {
+        result.status = NFS3ERR_STALE;
+        result.write3res_u.resfail = wcc;
+        return &result;
+    }
+
+    encode_pre_wcc(&wcc, inode);
+    if (inode->type != BLUESKY_REGULAR) {
+        result.status = NFS3ERR_INVAL;
+        result.write3res_u.resfail = wcc;
+        return &result;
+    }
+
+    uint64_t lastbyte = argp->offset + argp->count;
+    if (lastbyte > inode->size) {
+        bluesky_file_truncate(inode, lastbyte);
+    }
+    inode->change_count++;
+
+    encode_fattr3(&wcc.after.post_op_attr_u.attributes, inode);
+    result.write3res_u.resok.file_wcc = wcc;
+    result.write3res_u.resok.count = argp->count;
+    result.write3res_u.resok.committed = FILE_SYNC;
 
     return &result;
 }