From f48181d57295355b68dffdd8fad5729bf952ba7a Mon Sep 17 00:00:00 2001 From: Michael Vrable Date: Tue, 25 Aug 2009 16:10:11 -0700 Subject: [PATCH] Begin adding write support. Right now we only change the file size, but don't actually store the data. --- Makefile | 3 ++- bluesky.h | 24 +++++++++++++++++++++++- inode.c | 34 ++++++++++++++++++++++++++++++++++ main.c | 22 ++++++++++++++-------- nfs3/nfs3.c | 27 ++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 7f5b123..8a1ea81 100644 --- 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 diff --git a/bluesky.h b/bluesky.h index 75ba2de..c85fd70 100644 --- 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 --- 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 --- 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; } diff --git a/nfs3/nfs3.c b/nfs3/nfs3.c index aaf23b4..90f62d7 100644 --- a/nfs3/nfs3.c +++ b/nfs3/nfs3.c @@ -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; } -- 2.20.1