Right now we only change the file size, but don't actually store the data.
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
clean :
rm -f $(OBJS) bluesky
+ for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
dep :
touch Makefile.dep
/* File-specific fields */
uint64_t size;
+ GArray *blocks;
/* Directory-specific fields */
GSequence *dirents;
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();
uint64_t inum);
void bluesky_directory_dump(BlueSkyInode *dir);
+void bluesky_file_truncate(BlueSkyInode *inode, uint64_t size);
+
#endif
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:
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++;
+}
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;
}
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;
}