]> Zhao Yanbai Git Server - minix.git/commitdiff
VTreeFS library
authorDavid van Moolenbroek <david@minix3.org>
Tue, 10 Aug 2010 20:05:51 +0000 (20:05 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Tue, 10 Aug 2010 20:05:51 +0000 (20:05 +0000)
18 files changed:
include/Makefile
include/minix/vtreefs.h [new file with mode: 0644]
lib/Makefile
lib/libvtreefs/Makefile [new file with mode: 0644]
lib/libvtreefs/glo.h [new file with mode: 0644]
lib/libvtreefs/inc.h [new file with mode: 0644]
lib/libvtreefs/inode.c [new file with mode: 0644]
lib/libvtreefs/inode.h [new file with mode: 0644]
lib/libvtreefs/link.c [new file with mode: 0644]
lib/libvtreefs/mount.c [new file with mode: 0644]
lib/libvtreefs/path.c [new file with mode: 0644]
lib/libvtreefs/proto.h [new file with mode: 0644]
lib/libvtreefs/read.c [new file with mode: 0644]
lib/libvtreefs/sdbm.c [new file with mode: 0644]
lib/libvtreefs/stadir.c [new file with mode: 0644]
lib/libvtreefs/table.c [new file with mode: 0644]
lib/libvtreefs/utility.c [new file with mode: 0644]
lib/libvtreefs/vtreefs.c [new file with mode: 0644]

index 16442f64c28353b98f9953210d01f48fde166525..b68984311c77f611887a0862f77db8684d9dc30f 100644 (file)
@@ -24,7 +24,7 @@ INCS+=        minix/a.out.h minix/bitmap.h minix/callnr.h minix/cdrom.h \
        minix/rs.h minix/safecopies.h minix/sched.h minix/sef.h minix/sound.h \
        minix/spin.h minix/sys_config.h minix/sysinfo.h minix/syslib.h \
        minix/sysutil.h minix/timers.h minix/tty.h minix/type.h minix/types.h \
-       minix/u64.h minix/vfsif.h minix/vm.h \
+       minix/u64.h minix/vfsif.h minix/vm.h minix/vtreefs.h \
        minix/compiler.h minix/compiler-ack.h minix/sha2.h minix/sha1.h minix/md5.h
 INCS+= net/hton.h net/if.h net/ioctl.h net/netlib.h
 INCS+= net/gen/arp_io.h net/gen/dhcp.h net/gen/ether.h \
diff --git a/include/minix/vtreefs.h b/include/minix/vtreefs.h
new file mode 100644 (file)
index 0000000..afcee8c
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _MINIX_VTREEFS_H
+#define _MINIX_VTREEFS_H
+
+struct inode;
+typedef int index_t;
+typedef void *cbdata_t;
+
+#define NO_INDEX       ((index_t) -1)
+
+/* Maximum file name length, excluding terminating null character. It is set
+ * to a low value to limit memory usage, but can be changed to any value.
+ */
+#define PNAME_MAX      16
+
+struct inode_stat {
+       mode_t mode;            /* file mode (type and permissions) */
+       uid_t uid;              /* user ID */
+       gid_t gid;              /* group ID */
+       off_t size;             /* file size */
+       dev_t dev;              /* device number (for char/block type files) */
+};
+
+struct fs_hooks {
+       void (*init_hook)(void);
+       void (*cleanup_hook)(void);
+       int (*lookup_hook)(struct inode *inode, char *name, cbdata_t cbdata);
+       int (*getdents_hook)(struct inode *inode, cbdata_t cbdata);
+       int (*read_hook)(struct inode *inode, off_t offset, char **ptr,
+               size_t *len, cbdata_t cbdata);
+       int (*rdlink_hook)(struct inode *inode, char *ptr, size_t max,
+               cbdata_t cbdata);
+       int (*message_hook)(message *m);
+};
+
+extern struct inode *add_inode(struct inode *parent, char *name, index_t index,
+       struct inode_stat *stat, index_t nr_indexed_entries, cbdata_t cbdata);
+extern void delete_inode(struct inode *inode);
+
+extern struct inode *get_inode_by_name(struct inode *parent, char *name);
+extern struct inode *get_inode_by_index(struct inode *parent, index_t index);
+
+extern char const *get_inode_name(struct inode *inode);
+extern index_t get_inode_index(struct inode *inode);
+extern cbdata_t get_inode_cbdata(struct inode *inode);
+
+extern struct inode *get_root_inode(void);
+extern struct inode *get_parent_inode(struct inode *inode);
+extern struct inode *get_first_inode(struct inode *parent);
+extern struct inode *get_next_inode(struct inode *previous);
+
+extern void get_inode_stat(struct inode *inode, struct inode_stat *stat);
+extern void set_inode_stat(struct inode *inode, struct inode_stat *stat);
+
+extern void start_vtreefs(struct fs_hooks *hooks, unsigned int nr_inodes,
+       struct inode_stat *stat, index_t nr_indexed_entries);
+
+#endif /* _MINIX_VTREEFS_H */
index 640a0b934ce44f6ae2149c31d8e5b6f7ea221b7b..f3afacc1244ca0aff16cde19d668faf73d9b04a8 100644 (file)
@@ -1,7 +1,8 @@
 .include <bsd.own.mk>
 
 SUBDIR= csu libc libcurses libdriver libnetdriver libend libedit libm libsys \
-       libtimers libutil libbz2 libl libhgfs libz libfetch libarchive
+       libtimers libutil libbz2 libl libhgfs libz libfetch libarchive \
+       libvtreefs
 
 .if ${COMPILER_TYPE} == "ack"
 SUBDIR+= ack/libd ack/libe ack/libfp ack/liby
diff --git a/lib/libvtreefs/Makefile b/lib/libvtreefs/Makefile
new file mode 100644 (file)
index 0000000..a4906cf
--- /dev/null
@@ -0,0 +1,17 @@
+# Makefile for libvtreefs
+
+LIB=   vtreefs
+
+SRCS=  \
+       inode.c \
+       link.c \
+       mount.c \
+       path.c \
+       read.c \
+       sdbm.c \
+       stadir.c \
+       table.c \
+       utility.c \
+       vtreefs.c
+
+.include <bsd.lib.mk>
diff --git a/lib/libvtreefs/glo.h b/lib/libvtreefs/glo.h
new file mode 100644 (file)
index 0000000..e029835
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _VTREEFS_GLO_H
+#define _VTREEFS_GLO_H
+
+#ifdef _TABLE
+#undef EXTERN
+#define EXTERN
+#endif
+
+EXTERN struct fs_hooks *vtreefs_hooks;
+
+EXTERN message fs_m_in;
+EXTERN message fs_m_out;
+
+EXTERN dev_t fs_dev;
+
+EXTERN int fs_mounted;
+
+extern _PROTOTYPE( int (*fs_call_vec[]), (void) );
+
+#endif /* _VTREEFS_GLO_H */
diff --git a/lib/libvtreefs/inc.h b/lib/libvtreefs/inc.h
new file mode 100644 (file)
index 0000000..41e6883
--- /dev/null
@@ -0,0 +1,34 @@
+
+#define _POSIX_SOURCE      1   /* tell headers to include POSIX stuff */
+#define _MINIX             1   /* tell headers to include MINIX stuff */
+#define _SYSTEM            1   /* tell headers that this is the kernel */
+
+#include <ansi.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <limits.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <minix/config.h>
+#include <minix/callnr.h>
+#include <minix/type.h>
+#include <minix/const.h>
+#include <minix/com.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+#include <minix/keymap.h>
+#include <minix/bitmap.h>
+#include <minix/vfsif.h>
+#include <minix/endpoint.h>
+#include <minix/vtreefs.h>
+
+#include "glo.h"
+#include "proto.h"
+#include "inode.h"
diff --git a/lib/libvtreefs/inode.c b/lib/libvtreefs/inode.c
new file mode 100644 (file)
index 0000000..792ebc7
--- /dev/null
@@ -0,0 +1,603 @@
+/* VTreeFS - inode.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+/* The number of inodes and hash table slots. */
+PRIVATE unsigned int nr_inodes;
+
+/* The table of all the inodes. */
+PRIVATE struct inode *inode;
+
+/* The list of unused inodes. */
+PRIVATE TAILQ_HEAD(unused_head, inode) unused_inodes;
+
+/* The hash tables for lookup of <parent,name> and <parent,index> to inode. */
+PRIVATE LIST_HEAD(name_head, inode) *parent_name_head;
+PRIVATE LIST_HEAD(index_head, inode) *parent_index_head;
+
+/* Internal integrity check. */
+#define CHECK_INODE(node)                                              \
+       do {                                                            \
+               assert(node >= &inode[0] && node < &inode[nr_inodes]);  \
+               assert(node == &inode[0] ||                             \
+                       node->i_parent != NULL ||                       \
+                       (node->i_flags & I_DELETED));                   \
+       } while (0);
+
+/*===========================================================================*
+ *                             init_inodes                                  *
+ *===========================================================================*/
+PUBLIC void init_inodes(unsigned int inodes, struct inode_stat *stat,
+       index_t nr_indexed_entries)
+{
+       /* Initialize the inode-related state.
+        */
+       struct inode *node;
+       int i;
+
+       assert(inodes > 0);
+       assert(nr_indexed_entries >= 0);
+
+       nr_inodes = inodes;
+
+       /* Allocate the inode and hash tables. */
+       inode = malloc(nr_inodes * sizeof(inode[0]));
+       parent_name_head = malloc(nr_inodes * sizeof(parent_name_head[0]));
+       parent_index_head = malloc(nr_inodes * sizeof(parent_index_head[0]));
+
+       assert(inode != NULL);
+       assert(parent_name_head != NULL);
+       assert(parent_index_head != NULL);
+
+#if DEBUG
+       printf("VTREEFS: allocated %d+%d+%d bytes\n",
+               nr_inodes * sizeof(inode[0]),
+               nr_inodes * sizeof(parent_name_head[0]),
+               nr_inodes * sizeof(parent_index_head[0]));
+#endif
+
+       /* Initialize the free/unused list. */
+       TAILQ_INIT(&unused_inodes);
+
+       /* Add free inodes to the unused/free list. Skip the root inode. */
+       for (i = 1; i < nr_inodes; i++) {
+               node = &inode[i];
+
+               node->i_parent = NULL;
+               node->i_count = 0;
+               TAILQ_INIT(&node->i_children);
+               TAILQ_INSERT_HEAD(&unused_inodes, node, i_unused);
+       }
+
+       /* Initialize the hash lists. */
+       for (i = 0; i < nr_inodes; i++) {
+               LIST_INIT(&parent_name_head[i]);
+               LIST_INIT(&parent_index_head[i]);
+       }
+
+       /* Initialize the root inode. */
+       node = &inode[0];
+       node->i_parent = NULL;
+       node->i_count = 0;
+       TAILQ_INIT(&node->i_children);
+       node->i_flags = 0;
+       node->i_index = NO_INDEX;
+       set_inode_stat(node, stat);
+       node->i_indexed = nr_indexed_entries;
+       node->i_cbdata = NULL;
+}
+
+/*===========================================================================*
+ *                             cleanup_inodes                               *
+ *===========================================================================*/
+PUBLIC void cleanup_inodes(void)
+{
+       /* Clean up the inode-related state.
+        */
+
+       /* Free the inode and hash tables. */
+       free(parent_index_head);
+       free(parent_name_head);
+       free(inode);
+}
+
+/*===========================================================================*
+ *                             parent_name_hash                             *
+ *===========================================================================*/
+PRIVATE int parent_name_hash(struct inode *parent, char *name)
+{
+       /* Return the hash value of <parent,name> tuple.
+        */
+       unsigned int name_hash;
+       unsigned long parent_hash;
+
+       /* The parent hash is a simple array entry; find its index. */
+       parent_hash = parent - &inode[0];
+
+       /* Use the sdbm algorithm to hash the name. */
+       name_hash = sdbm_hash(name, strlen(name));
+
+       return (parent_hash ^ name_hash) % nr_inodes;
+}
+
+/*===========================================================================*
+ *                             parent_index_hash                            *
+ *===========================================================================*/
+PRIVATE int parent_index_hash(struct inode *parent, index_t index)
+{
+       /* Return the hash value of a <parent,index> tuple.
+        */
+
+       return ((parent - &inode[0]) ^ index) % nr_inodes;
+}
+
+/*===========================================================================*
+ *                             purge_inode                                  *
+ *===========================================================================*/
+PUBLIC void purge_inode(struct inode *parent)
+{
+       /* Delete a deletable inode to make room for a new inode.
+        */
+       /* An inode is deletable if:
+        * - it is in use;
+        * - it is indexed;
+        * - it is not the given parent inode;
+        * - it has a zero reference count;
+        * - it does not have any children.
+        * The first point is true for all inodes, or we would not be here.
+        * The latter two points also imply that I_DELETED is not set.
+        */
+       static int last_checked = 0;
+       struct inode *node;
+       int count;
+
+       assert(TAILQ_EMPTY(&unused_inodes));
+
+       /* This should not happen often enough to warrant an extra linked list,
+        * especially as maintenance of that list would be rather error-prone..
+        */
+       for (count = 0; count < nr_inodes; count++) {
+               node = &inode[last_checked];
+               last_checked = (last_checked + 1) % nr_inodes;
+
+               if (node != parent && node->i_index != NO_INDEX &&
+                       node->i_count == 0 && TAILQ_EMPTY(&node->i_children)) {
+
+                       assert(!(node->i_flags & I_DELETED));
+
+                       delete_inode(node);
+
+                       break;
+               }
+       }
+}
+
+/*===========================================================================*
+ *                             add_inode                                    *
+ *===========================================================================*/
+PUBLIC struct inode *add_inode(struct inode *parent, char *name,
+       index_t index, struct inode_stat *stat, index_t nr_indexed_entries,
+       cbdata_t cbdata)
+{
+       /* Add an inode.
+        */
+       struct inode *newnode;
+       int slot;
+
+       CHECK_INODE(parent);
+       assert(S_ISDIR(parent->i_stat.mode));
+       assert(!(parent->i_flags & I_DELETED));
+       assert(strlen(name) <= PNAME_MAX);
+       assert(index >= 0 || index == NO_INDEX);
+       assert(stat != NULL);
+       assert(nr_indexed_entries >= 0);
+       assert(get_inode_by_name(parent, name) == NULL);
+
+       /* Get a free inode. Free one up if necessary. */
+       if (TAILQ_EMPTY(&unused_inodes))
+               purge_inode(parent);
+
+       assert(!TAILQ_EMPTY(&unused_inodes));
+
+       newnode = TAILQ_FIRST(&unused_inodes);
+       TAILQ_REMOVE(&unused_inodes, newnode, i_unused);
+
+       assert(newnode->i_count == 0);
+
+       /* Copy the relevant data to the inode. */
+       newnode->i_parent = parent;
+       newnode->i_flags = 0;
+       newnode->i_index = index;
+       newnode->i_stat = *stat;
+       newnode->i_indexed = nr_indexed_entries;
+       newnode->i_cbdata = cbdata;
+       strcpy(newnode->i_name, name);
+
+       /* Add the inode to the list of children inodes of the parent. */
+       TAILQ_INSERT_HEAD(&parent->i_children, newnode, i_siblings);
+
+       /* Add the inode to the <parent,name> hash table. */
+       slot = parent_name_hash(parent, name);
+       LIST_INSERT_HEAD(&parent_name_head[slot], newnode, i_hname);
+
+       /* Add the inode to the <parent,index> hash table. */
+       if (index != NO_INDEX) {
+               slot = parent_index_hash(parent, index);
+               LIST_INSERT_HEAD(&parent_index_head[slot], newnode, i_hindex);
+       }
+
+       return newnode;
+}
+
+/*===========================================================================*
+ *                             get_root_inode                               *
+ *===========================================================================*/
+PUBLIC struct inode *get_root_inode(void)
+{
+       /* Return the file system's root inode.
+        */
+
+       /* The root node is always the first node in the inode table */
+       return &inode[0];
+}
+
+/*===========================================================================*
+ *                             get_inode_name                               *
+ *===========================================================================*/
+PUBLIC char const *get_inode_name(struct inode *node)
+{
+       /* Return the name that an inode has in its parent directory.
+        */
+
+       CHECK_INODE(node);
+
+       return node->i_name;
+}
+
+/*===========================================================================*
+ *                             get_inode_index                              *
+ *===========================================================================*/
+PUBLIC index_t get_inode_index(struct inode *node)
+{
+       /* Return the index that an inode has in its parent directory.
+        */
+
+       CHECK_INODE(node);
+
+       return node->i_index;
+}
+
+/*===========================================================================*
+ *                             get_inode_cbdata                             *
+ *===========================================================================*/
+PUBLIC cbdata_t get_inode_cbdata(struct inode *node)
+{
+       /* Return the callback data associated with the given inode.
+        */
+
+       CHECK_INODE(node);
+
+       return node->i_cbdata;
+}
+
+/*===========================================================================*
+ *                             get_parent_inode                             *
+ *===========================================================================*/
+PUBLIC struct inode *get_parent_inode(struct inode *node)
+{
+       /* Return an inode's parent inode.
+        */
+
+       CHECK_INODE(node);
+
+       /* The root inode does not have parent. */
+       if (node == &inode[0])
+               return NULL;
+
+       return node->i_parent;
+}
+
+/*===========================================================================*
+ *                             get_first_inode                              *
+ *===========================================================================*/
+PUBLIC struct inode *get_first_inode(struct inode *parent)
+{
+       /* Return a directory's first (non-deleted) child inode.
+        */
+       struct inode *node;
+
+       CHECK_INODE(parent);
+       assert(S_ISDIR(parent->i_stat.mode));
+
+       node = TAILQ_FIRST(&parent->i_children);
+
+       while (node != NULL && (node->i_flags & I_DELETED))
+               node = TAILQ_NEXT(node, i_siblings);
+
+       return node;
+}
+
+/*===========================================================================*
+ *                             get_next_inode                               *
+ *===========================================================================*/
+PUBLIC struct inode *get_next_inode(struct inode *previous)
+{
+       /* Return a directory's next (non-deleted) child inode.
+        */
+       struct inode *node;
+
+       CHECK_INODE(previous);
+
+       node = TAILQ_NEXT(previous, i_siblings);
+
+       while (node != NULL && (node->i_flags & I_DELETED))
+               node = TAILQ_NEXT(node, i_siblings);
+
+       return node;
+}
+
+/*===========================================================================*
+ *                             get_inode_number                             *
+ *===========================================================================*/
+PUBLIC int get_inode_number(struct inode *node)
+{
+       /* Return the inode number of the given inode.
+        */
+
+       CHECK_INODE(node);
+
+       return (int) (node - &inode[0]) + 1;
+}
+
+/*===========================================================================*
+ *                             get_inode_stat                               *
+ *===========================================================================*/
+PUBLIC void get_inode_stat(struct inode *node, struct inode_stat *stat)
+{
+       /* Retrieve an inode's status.
+        */
+
+       CHECK_INODE(node);
+
+       *stat = node->i_stat;
+}
+
+/*===========================================================================*
+ *                             set_inode_stat                               *
+ *===========================================================================*/
+PUBLIC void set_inode_stat(struct inode *node, struct inode_stat *stat)
+{
+       /* Set an inode's status.
+        */
+
+       CHECK_INODE(node);
+
+       node->i_stat = *stat;
+}
+
+/*===========================================================================*
+ *                             get_inode_by_name                            *
+ *===========================================================================*/
+PUBLIC struct inode *get_inode_by_name(struct inode *parent, char *name)
+{
+       /* Look up an inode using a <parent,name> tuple.
+        */
+       struct inode *node;
+       int slot;
+
+       CHECK_INODE(parent);
+       assert(strlen(name) <= PNAME_MAX);
+       assert(S_ISDIR(parent->i_stat.mode));
+
+       /* Get the hash value, and search for the inode. */
+       slot = parent_name_hash(parent, name);
+       LIST_FOREACH(node, &parent_name_head[slot], i_hname) {
+               if (parent == node->i_parent && !strcmp(name, node->i_name))
+                       return node;    /* found */
+       }
+
+       return NULL;
+}
+
+/*===========================================================================*
+ *                             get_inode_by_index                           *
+ *===========================================================================*/
+PUBLIC struct inode *get_inode_by_index(struct inode *parent, index_t index)
+{
+       /* Look up an inode using a <parent,index> tuple.
+        */
+       struct inode *node;
+       int slot;
+
+       CHECK_INODE(parent);
+       assert(S_ISDIR(parent->i_stat.mode));
+       assert(index >= 0 && index < parent->i_indexed);
+
+       /* Get the hash value, and search for the inode. */
+       slot = parent_index_hash(parent, index);
+       LIST_FOREACH(node, &parent_index_head[slot], i_hindex) {
+               if (parent == node->i_parent && index == node->i_index)
+                       return node;    /* found */
+       }
+
+       return NULL;
+}
+
+/*===========================================================================*
+ *                             find_inode                                   *
+ *===========================================================================*/
+PUBLIC struct inode *find_inode(ino_t num)
+{
+       /* Retrieve an inode by inode number.
+        */
+       struct inode *node;
+
+       node = &inode[num - 1];
+
+       CHECK_INODE(node);
+
+       return node;
+}
+
+/*===========================================================================*
+ *                             get_inode                                    *
+ *===========================================================================*/
+PUBLIC struct inode *get_inode(ino_t num)
+{
+       /* Retrieve an inode by inode number, and increase its reference count.
+        */
+       struct inode *node;
+
+       if ((node = find_inode(num)) == NULL)
+               return NULL;
+
+       node->i_count++;
+       return node;
+}
+
+/*===========================================================================*
+ *                             put_inode                                    *
+ *===========================================================================*/
+PUBLIC void put_inode(struct inode *node)
+{
+       /* Decrease an inode's reference count.
+        */
+
+       CHECK_INODE(node);
+       assert(node->i_count > 0);
+
+       node->i_count--;
+
+       /* If the inode is scheduled for deletion, and has no more references,
+        * actually delete it now.
+        */
+       if ((node->i_flags & I_DELETED) && node->i_count == 0)
+               delete_inode(node);
+}
+
+/*===========================================================================*
+ *                             ref_inode                                    *
+ *===========================================================================*/
+PUBLIC void ref_inode(struct inode *node)
+{
+       /* Increase an inode's reference count.
+        */
+
+       CHECK_INODE(node);
+       assert(node->i_count >= 0);
+
+       node->i_count++;
+}
+
+/*===========================================================================*
+ *                             unlink_inode                                 *
+ *===========================================================================*/
+PRIVATE void unlink_inode(struct inode *node)
+{
+       /* Unlink the given node from its parent, if it is still linked in.
+        */
+       struct inode *parent;
+
+       assert(node->i_flags & I_DELETED);
+
+       parent = node->i_parent;
+       if (parent == NULL)
+               return;
+
+       /* Delete the node from the parent list. */
+       node->i_parent = NULL;
+
+       TAILQ_REMOVE(&parent->i_children, node, i_siblings);
+
+       /* Optionally recheck if the parent can now be deleted. */
+       if (parent->i_flags & I_DELETED)
+               delete_inode(parent);
+}
+
+/*===========================================================================*
+ *                             delete_inode                                 *
+ *===========================================================================*/
+PUBLIC void delete_inode(struct inode *node)
+{
+       /* Delete the given inode. If its reference count is nonzero, or it
+        * still has children that cannot be deleted for the same reason, keep
+        * the inode around for the time being. If the node is a directory,
+        * keep around its parent so that we can still do a "cd .." out of it.
+        * For these reasons, this function may be called on an inode more than
+        * once before it is actually deleted.
+        */
+       struct inode *cnode, *ctmp;
+
+       CHECK_INODE(node);
+       assert(node != &inode[0]);
+
+       /* If the inode was not already scheduled for deletion,
+        * partially remove the node.
+        */
+       if (!(node->i_flags & I_DELETED)) {
+               /* Remove any children first (before I_DELETED is set!). */
+               TAILQ_FOREACH_SAFE(cnode, &node->i_children, i_siblings, ctmp)
+                       delete_inode(cnode);
+
+               /* Unhash the inode from the <parent,name> table. */
+               LIST_REMOVE(node, i_hname);
+
+               /* Unhash the inode from the <parent,index> table if needed. */
+               if (node->i_index != NO_INDEX)
+                       LIST_REMOVE(node, i_hindex);
+
+               node->i_flags |= I_DELETED;
+
+               /* If this inode is not a directory, we don't care about being
+                * able to find its parent. Unlink it from the parent now.
+                */
+               if (!S_ISDIR(node->i_stat.mode))
+                       unlink_inode(node);
+       }
+
+       if (node->i_count == 0 && TAILQ_EMPTY(&node->i_children)) {
+               /* If this inode still has a parent at this point, unlink it
+                * now; noone can possibly refer to it anymore.
+                */
+               if (node->i_parent != NULL)
+                       unlink_inode(node);
+
+               /* Delete the actual node. */
+               TAILQ_INSERT_HEAD(&unused_inodes, node, i_unused);
+       }
+}
+
+/*===========================================================================*
+ *                             is_inode_deleted                             *
+ *===========================================================================*/
+PUBLIC int is_inode_deleted(struct inode *node)
+{
+       /* Return whether the given inode has been deleted.
+        */
+
+       return (node->i_flags & I_DELETED);
+}
+
+/*===========================================================================*
+ *                             fs_putnode                                   *
+ *===========================================================================*/
+PUBLIC int fs_putnode(void)
+{
+       /* Find the inode specified by the request message, and decrease its
+        * reference count.
+        */
+       struct inode *node;
+
+       /* Get the inode specified by its number. */
+       if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
+               return EINVAL;
+
+       /* Decrease the reference count. */
+       node->i_count -= fs_m_in.REQ_COUNT - 1;
+
+       assert(node->i_count > 0);
+
+       put_inode(node);
+
+       return OK;
+}
diff --git a/lib/libvtreefs/inode.h b/lib/libvtreefs/inode.h
new file mode 100644 (file)
index 0000000..a20445b
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VTREEFS_INODE_H
+#define _VTREEFS_INODE_H
+
+/* The inodes that are active, form a fully connected tree. Each node except
+ * the root node has a parent and a tail queue of children, where each child
+ * inode points to the "next" and "previous" inode with a common parent.
+ *
+ * Each inode that has a parent (i.e. active and not the root), is part of a
+ * <parent,name> -> inode hashtable, and if it has an index into the parent,
+ * is part of a <parent,index> -> inode hashtable.
+ *
+ * Inodes that are not active, are either deleted or free. A deleted inode is
+ * in use as long as it still has a nonzero reference count, even though it is
+ * no longer part of the tree. Inodes that are free, are part of the list of
+ * unused inodes.
+ */
+struct inode {
+       /* Inode metadata */
+       struct inode_stat i_stat;       /* POSIX attributes */
+       char i_name[PNAME_MAX + 1];     /* name of the inode in the parent */
+       int i_count;                    /* reference count */
+       index_t i_index;                /* index number in parent / NO_INDEX */
+       int i_indexed;                  /* number of indexed entries */
+       cbdata_t i_cbdata;              /* callback data */
+       unsigned short i_flags;         /* I_DELETED or 0 */
+
+       /* Tree structure */
+       struct inode *i_parent;         /* parent of the node */
+       TAILQ_ENTRY(inode) i_siblings;  /* hash list for parent's children */
+       TAILQ_HEAD(i_child, inode) i_children;  /* parent's children */
+
+       /* Hash/free structure */
+       LIST_ENTRY(inode) i_hname;      /* hash list for name hash table */
+       LIST_ENTRY(inode) i_hindex;     /* hash list for index hash table */
+       TAILQ_ENTRY(inode) i_unused;    /* list of unused nodes */
+};
+
+#define I_DELETED      0x1     /* the inode is scheduled for deletion */
+
+#endif /* _VTREEFS_INODE_H */
diff --git a/lib/libvtreefs/link.c b/lib/libvtreefs/link.c
new file mode 100644 (file)
index 0000000..01237e0
--- /dev/null
@@ -0,0 +1,41 @@
+/* VTreeFS - link.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+/*===========================================================================*
+ *                             fs_rdlink                                    *
+ *===========================================================================*/
+PUBLIC int fs_rdlink(void)
+{
+       /* Retrieve symbolic link target.
+        */
+       char path[PATH_MAX];
+       struct inode *node;
+       size_t len;
+       int r;
+
+       if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
+               return EINVAL;
+
+       /* Call the rdlink hook. */
+       assert(vtreefs_hooks->rdlink_hook != NULL);
+       assert(!is_inode_deleted(node));        /* symlinks cannot be opened */
+
+       r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path),
+               get_inode_cbdata(node));
+       if (r != OK) return r;
+
+       len = strlen(path);
+       assert(len > 0 && len < sizeof(path));
+
+       if (len > fs_m_in.REQ_MEM_SIZE)
+               len = fs_m_in.REQ_MEM_SIZE;
+
+       /* Copy out the result. */
+       r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
+               (vir_bytes) path, len, D);
+       if (r != OK) return r;
+
+       fs_m_out.RES_NBYTES = len;
+       return OK;
+}
diff --git a/lib/libvtreefs/mount.c b/lib/libvtreefs/mount.c
new file mode 100644 (file)
index 0000000..652b130
--- /dev/null
@@ -0,0 +1,65 @@
+/* VTreeFS - mount.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+/*===========================================================================*
+ *                             fs_readsuper                                 *
+ *===========================================================================*/
+PUBLIC int fs_readsuper(void)
+{
+       /* This function gets the root inode and sends back its details.
+        */
+       struct inode *root;
+
+       /* Get the device number, for stat requests. */
+       fs_dev = fs_m_in.REQ_DEV;
+
+       /* The VTreeFS must not be mounted as a root file system. */
+       if (fs_m_in.REQ_FLAGS & REQ_ISROOT)
+               return EINVAL;
+
+       /* Get the root inode and increase its reference count. */
+       root = get_root_inode();
+       ref_inode(root);
+
+       /* The system is now mounted. Call the initialization hook. */
+       if (vtreefs_hooks->init_hook != NULL)
+               vtreefs_hooks->init_hook();
+
+       /* Return the root inode's properties. */
+       fs_m_out.RES_INODE_NR = get_inode_number(root);
+       fs_m_out.RES_MODE = root->i_stat.mode;
+       fs_m_out.RES_FILE_SIZE_HI = 0;
+       fs_m_out.RES_FILE_SIZE_LO = root->i_stat.size;
+       fs_m_out.RES_UID = root->i_stat.uid;
+       fs_m_out.RES_GID = root->i_stat.gid;
+       fs_m_out.RES_DEV = NO_DEV;
+
+       fs_mounted = TRUE;
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             fs_unmount                                   *
+ *===========================================================================*/
+PUBLIC int fs_unmount(void)
+{
+       /* Unmount the file system.
+        */
+       struct inode *root;
+
+       /* Decrease the count of the root inode. */
+       root = get_root_inode();
+
+       put_inode(root);
+
+       /* The system is unmounted. Call the cleanup hook. */
+       if (vtreefs_hooks->cleanup_hook != NULL)
+               vtreefs_hooks->cleanup_hook();
+
+       /* We can now be shut down safely. */
+       fs_mounted = FALSE;
+
+       return OK;
+}
diff --git a/lib/libvtreefs/path.c b/lib/libvtreefs/path.c
new file mode 100644 (file)
index 0000000..b07aaa7
--- /dev/null
@@ -0,0 +1,313 @@
+/* VTreeFS - path.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+/*===========================================================================*
+ *                             access_as_dir                                *
+ *===========================================================================*/
+PRIVATE int access_as_dir(struct inode *node, vfs_ucred_t *ucred)
+{
+       /* Check whether the given inode may be accessed as directory.
+        * Return OK or an appropriate error code.
+        */
+       mode_t mask;
+       int i;
+
+       /* The inode must be a directory to begin with. */
+       if (!S_ISDIR(node->i_stat.mode)) return ENOTDIR;
+
+       /* The caller must have search access to the directory.
+        * Root always does.
+        */
+       if (ucred->vu_uid == SUPER_USER) return OK;
+
+       if (ucred->vu_uid == node->i_stat.uid) mask = S_IXUSR;
+       else if (ucred->vu_gid == node->i_stat.gid) mask = S_IXGRP;
+       else {
+               mask = S_IXOTH;
+
+               for (i = 0; i < ucred->vu_ngroups; i++) {
+                       if (ucred->vu_sgroups[i] == node->i_stat.gid) {
+                               mask = S_IXGRP;
+
+                               break;
+                       }
+               }
+       }
+
+       return (node->i_stat.mode & mask) ? OK : EACCES;
+}
+
+/*===========================================================================*
+ *                             next_name                                    *
+ *===========================================================================*/
+PRIVATE int next_name(char **ptr, char **start, char name[PNAME_MAX+1])
+{
+       /* Get the next path component from a path.
+        */
+       char *p;
+       int i;
+
+       for (p = *ptr; *p == '/'; p++);
+
+       *start = p;
+
+       if (*p) {
+               for (i = 0; *p && *p != '/' && i <= PNAME_MAX; p++, i++)
+                       name[i] = *p;
+
+               if (i > PNAME_MAX)
+                       return ENAMETOOLONG;
+
+               name[i] = 0;
+       } else {
+               strcpy(name, ".");
+       }
+
+       *ptr = p;
+       return OK;
+}
+
+/*===========================================================================*
+ *                             go_up                                        *
+ *===========================================================================*/
+PRIVATE int go_up(struct inode *node, struct inode **parent)
+{
+       /* Given a directory inode, progress into the parent directory.
+        */
+
+       *parent = get_parent_inode(node);
+
+       /* Trapped in a deleted directory? Should not be possible. */
+       if (*parent == NULL)
+               return ENOENT;
+
+       ref_inode(*parent);
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             go_down                                      *
+ *===========================================================================*/
+PRIVATE int go_down(struct inode *parent, char *name, struct inode **child)
+{
+       /* Given a directory inode and a name, progress into a directory entry.
+        */
+       int r;
+
+       /* Call the lookup hook, if present, before doing the actual lookup. */
+       if (!is_inode_deleted(parent) && vtreefs_hooks->lookup_hook != NULL) {
+               r = vtreefs_hooks->lookup_hook(parent, name,
+                       get_inode_cbdata(parent));
+               if (r != OK) return r;
+       }
+
+       if ((*child = get_inode_by_name(parent, name)) == NULL)
+               return ENOENT;
+
+       ref_inode(*child);
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             resolve_link                                 *
+ *===========================================================================*/
+PRIVATE int resolve_link(struct inode *node, char *pptr, char *tail)
+{
+       /* Given a symbolic link, resolve and return the contents of the link.
+        */
+       char path[PATH_MAX];
+       size_t len;
+       int r;
+
+       assert(vtreefs_hooks->rdlink_hook != NULL);
+       assert(!is_inode_deleted(node));
+
+       r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path),
+               get_inode_cbdata(node));
+       if (r != OK) return r;
+
+       len = strlen(path);
+       assert(len > 0 && len < sizeof(path));
+
+       if (len + strlen(tail) >= sizeof(path))
+               return ENAMETOOLONG;
+
+       strcat(path, tail);
+
+       strcpy(pptr, path);
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             fs_lookup                                    *
+ *===========================================================================*/
+PUBLIC int fs_lookup(void)
+{
+       /* Resolve a path string to an inode.
+        */
+       ino_t dir_ino_nr, root_ino_nr;
+       struct inode *cur_ino, *next_ino, *root_ino;
+       char path[PATH_MAX], name[PNAME_MAX+1];
+       char *ptr, *last;
+       vfs_ucred_t ucred;
+       size_t len;
+       int r, r2, symloop;
+
+       dir_ino_nr = fs_m_in.REQ_DIR_INO;
+       root_ino_nr = fs_m_in.REQ_ROOT_INO;
+       len = fs_m_in.REQ_PATH_LEN;
+
+       /* Fetch the path name. */
+       if (len < 1 || len > PATH_MAX)
+               return EINVAL;
+
+       r = sys_safecopyfrom(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
+               (vir_bytes) path, (phys_bytes) len, D);
+       if (r != OK) return r;
+
+       if (path[len-1] != 0) return EINVAL;
+
+       /* Fetch the caller's credentials. */
+       if (fs_m_in.REQ_FLAGS & PATH_GET_UCRED) {
+               assert(fs_m_in.REQ_UCRED_SIZE == sizeof(ucred));
+
+               r = sys_safecopyfrom(fs_m_in.m_source, fs_m_in.REQ_GRANT2, 0,
+                       (vir_bytes) &ucred, fs_m_in.REQ_UCRED_SIZE, D);
+
+               if (r != OK)
+                       return r;
+       }
+       else {
+               ucred.vu_uid = fs_m_in.REQ_UID;
+               ucred.vu_gid = fs_m_in.REQ_GID;
+               ucred.vu_ngroups = 0;
+       }
+
+       /* Start the actual lookup. */
+       if ((cur_ino = get_inode(dir_ino_nr)) == NULL)
+               return EINVAL;
+
+       /* Chroot'ed environment? */
+       if (root_ino_nr > 0)
+               root_ino = find_inode(root_ino_nr);
+       else
+               root_ino = NULL;
+
+       symloop = 0;
+
+       for (ptr = last = path; ptr[0] != 0; ) {
+               /* There is more path to process. That means that the current
+                * file is now being accessed as a directory. Check type and
+                * permissions.
+                */
+               if ((r = access_as_dir(cur_ino, &ucred)) != OK)
+                       break;
+
+               /* Get the next path component. The result is a non-empty
+                * string.
+                */
+               if ((r = next_name(&ptr, &last, name)) != OK)
+                       break;
+
+               if (!strcmp(name, ".") ||
+                               (cur_ino == root_ino && !strcmp(name, "..")))
+                       continue;
+
+               if (!strcmp(name, "..")) {
+                       if (cur_ino == get_root_inode())
+                               r = ELEAVEMOUNT;
+                       else
+                               r = go_up(cur_ino, &next_ino);
+               } else {
+                       r = go_down(cur_ino, name, &next_ino);
+
+                       /* Perform symlink resolution if we have to. */
+                       if (r == OK && S_ISLNK(next_ino->i_stat.mode) &&
+                               (ptr[0] != '\0' ||
+                               !(fs_m_in.REQ_FLAGS & PATH_RET_SYMLINK))) {
+
+                               if (++symloop == SYMLOOP_MAX) {
+                                       put_inode(next_ino);
+
+                                       r = ELOOP;
+
+                                       break;
+                               }
+
+                               /* Resolve the symlink, and append the
+                                * remaining unresolved part of the path.
+                                */
+                               r = resolve_link(next_ino, path, ptr);
+
+                               put_inode(next_ino);
+
+                               if (r != OK)
+                                       break;
+
+                               /* If the symlink is absolute, return it to
+                                * VFS.
+                                */
+                               if (path[0] == '/') {
+                                       r = ESYMLINK;
+                                       last = path;
+
+                                       break;
+                               }
+
+                               ptr = path;
+                               continue;
+                       }
+               }
+
+               if (r != OK)
+                       break;
+
+               /* We have found a new file. Continue from this file. */
+               assert(next_ino != NULL);
+
+               put_inode(cur_ino);
+
+               cur_ino = next_ino;
+       }
+
+       /* If an error occurred, close the file and return error information.
+        */
+       if (r != OK) {
+               put_inode(cur_ino);
+
+               /* We'd need support for this here. */
+               assert(r != EENTERMOUNT);
+
+               /* Copy back the path if we resolved at least one symlink. */
+               if (symloop > 0 && (r == ELEAVEMOUNT || r == ESYMLINK)) {
+                       r2 = sys_safecopyto(fs_m_in.m_source,
+                               fs_m_in.REQ_GRANT, 0, (vir_bytes) path,
+                               strlen(path) + 1, D);
+
+                       if (r2 != OK)
+                               r = r2;
+               }
+
+               if (r == ELEAVEMOUNT || r == ESYMLINK) {
+                       fs_m_out.RES_OFFSET = (int) (last - path);
+                       fs_m_out.RES_SYMLOOP = symloop;
+               }
+
+               return r;
+       }
+
+       /* On success, leave the resulting file open and return its details. */
+       fs_m_out.RES_INODE_NR = get_inode_number(cur_ino);
+       fs_m_out.RES_MODE = cur_ino->i_stat.mode;
+       fs_m_out.RES_FILE_SIZE_HI = 0;
+       fs_m_out.RES_FILE_SIZE_LO = cur_ino->i_stat.size;
+       fs_m_out.RES_UID = cur_ino->i_stat.uid;
+       fs_m_out.RES_GID = cur_ino->i_stat.gid;
+       fs_m_out.RES_DEV = cur_ino->i_stat.dev;
+
+       return OK;
+}
diff --git a/lib/libvtreefs/proto.h b/lib/libvtreefs/proto.h
new file mode 100644 (file)
index 0000000..02c13e4
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _VTREEFS_PROTO_H
+#define _VTREEFS_PROTO_H
+
+/* inode.c */
+_PROTOTYPE( void init_inodes, (unsigned int inodes,
+       struct inode_stat *stat, index_t nr_indexed_entries)            );
+_PROTOTYPE( void cleanup_inodes, (void)                                        );
+_PROTOTYPE( struct inode *find_inode, (ino_t num)                      );
+_PROTOTYPE( struct inode *get_inode, (ino_t num)                       );
+_PROTOTYPE( void put_inode, (struct inode *node)                       );
+_PROTOTYPE( void ref_inode, (struct inode *node)                       );
+_PROTOTYPE( int get_inode_number, (struct inode *node)                 );
+_PROTOTYPE( int is_inode_deleted, (struct inode *node)                 );
+_PROTOTYPE( int fs_putnode, (void)                                     );
+
+/* link.c */
+_PROTOTYPE( int fs_rdlink, (void)                                      );
+
+/* mount.c */
+_PROTOTYPE( int fs_readsuper, (void)                                   );
+_PROTOTYPE( int fs_unmount, (void)                                     );
+
+/* path.c */
+_PROTOTYPE( int fs_lookup, (void)                                      );
+
+/* read.c */
+_PROTOTYPE( int fs_read, (void)                                                );
+_PROTOTYPE( int fs_getdents, (void)                                    );
+
+/* sdbm.c */
+_PROTOTYPE( long sdbm_hash, (char *str, int len)                       );
+
+/* stadir.c */
+_PROTOTYPE( int fs_stat, (void)                                                );
+_PROTOTYPE( int fs_fstatfs, (void)                                     );
+_PROTOTYPE( int fs_statvfs, (void)                                     );
+
+/* utility.c */
+_PROTOTYPE( int no_sys, (void)                                         );
+_PROTOTYPE( int do_noop, (void)                                                );
+
+#endif /* _VTREEFS_PROTO_H */
diff --git a/lib/libvtreefs/read.c b/lib/libvtreefs/read.c
new file mode 100644 (file)
index 0000000..98d74e6
--- /dev/null
@@ -0,0 +1,215 @@
+/* VTreeFS - read.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+#include <dirent.h>
+
+#define GETDENTS_BUFSIZ 4096
+#define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+
+/*===========================================================================*
+ *                             fs_read                                      *
+ *===========================================================================*/
+PUBLIC int fs_read(void)
+{
+       /* Read from a file.
+        */
+       cp_grant_id_t gid;
+       struct inode *node;
+       off_t pos;
+       size_t len;
+       char *ptr;
+       int r;
+
+       if (fs_m_in.REQ_SEEK_POS_HI != 0)
+               return EIO;
+
+       /* Try to get inode by to its inode number. */
+       if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
+               return EINVAL;
+
+       /* Check whether the node is a regular file. */
+       if (!S_ISREG(node->i_stat.mode))
+               return EINVAL;
+
+       /* Get the values from the request message. */
+       gid = fs_m_in.REQ_GRANT;
+       pos = fs_m_in.REQ_SEEK_POS_LO;
+
+       /* Call the read hook, if any. */
+       if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) {
+               len = fs_m_in.REQ_NBYTES;
+
+               /* On success, the read hook provides us with a pointer to the
+                * resulting data. This avoids copying overhead.
+                */
+               r = vtreefs_hooks->read_hook(node, pos, &ptr, &len,
+                       get_inode_cbdata(node));
+
+               assert(len >= 0 && len <= fs_m_in.REQ_NBYTES);
+
+               /* Copy the resulting data to user space. */
+               if (r == OK && len > 0) {
+                       r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
+                               0, (vir_bytes) ptr, len, D);
+               }
+       } else {
+               /* Feign an empty file. */
+               r = OK;
+               len = 0;
+       }
+
+       if (r == OK) {
+               fs_m_out.RES_SEEK_POS_HI = 0;
+               fs_m_out.RES_SEEK_POS_LO = pos + len;
+               fs_m_out.RES_NBYTES = len;
+       }
+
+       return r;
+}
+
+/*===========================================================================*
+ *                             fs_getdents                                  *
+ *===========================================================================*/
+PUBLIC int fs_getdents(void)
+{
+       /* Retrieve directory entries.
+        */
+       struct inode *node, *child = NULL;
+       struct dirent *dent;
+       char *name;
+       size_t len, off, user_off, user_left;
+       off_t pos;
+       int r, skip, get_next, indexed;
+       static char buf[GETDENTS_BUFSIZ];
+
+       if (fs_m_in.REQ_SEEK_POS_HI != 0)
+               return EIO;
+
+       if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
+               return EINVAL;
+
+       off = 0;
+       user_off = 0;
+       user_left = fs_m_in.REQ_MEM_SIZE;
+       indexed = node->i_indexed;
+       get_next = FALSE;
+       child = NULL;
+
+       /* Call the getdents hook, if any, to "refresh" the directory. */
+       if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) {
+               r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node));
+               if (r != OK) return r;
+       }
+
+       for (pos = fs_m_in.REQ_SEEK_POS_LO; ; pos++) {
+               /* Determine which inode and name to use for this entry. */
+               if (pos == 0) {
+                       /* The "." entry. */
+                       child = node;
+                       name = ".";
+               }
+               else if (pos == 1) {
+                       /* The ".." entry. */
+                       child = get_parent_inode(node);
+                       if (child == NULL)
+                               child = node;
+                       name = "..";
+               }
+               else if (pos - 2 < indexed) {
+                       /* All indexed entries. */
+                       child = get_inode_by_index(node, pos - 2);
+
+                       /* If there is no inode with this particular index,
+                        * continue with the next index number.
+                        */
+                       if (child == NULL) continue;
+
+                       name = child->i_name;
+               }
+               else {
+                       /* All non-indexed entries. */
+
+                       /* If this is the first loop iteration, first get to
+                        * the non-indexed child identified by the current
+                        * position.
+                        */
+                       if (get_next == FALSE) {
+                               skip = pos - indexed - 2;
+                               child = get_first_inode(node);
+
+                               /* Skip indexed children. */
+                               while (child != NULL &&
+                                               child->i_index != NO_INDEX)
+                                       child = get_next_inode(child);
+
+                               /* Skip to the right position. */
+                               while (child != NULL && skip-- > 0)
+                                       child = get_next_inode(child);
+
+                               get_next = TRUE;
+                       }
+                       else {
+                               child = get_next_inode(child);
+                       }
+
+                       /* No more children? Then stop. */
+                       if (child == NULL)
+                               break;
+
+                       assert(!is_inode_deleted(child));
+
+                       name = child->i_name;
+               }
+
+               len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name));
+
+               /* Is the user buffer too small to store another record? */
+               if (user_off + off + len > user_left) {
+                       /* Is the user buffer too small for even a single
+                        * record?
+                        */
+                       if (user_off == 0 && off == 0)
+                               return EINVAL;
+
+                       break;
+               }
+
+               /* If our own buffer cannot contain the new record, copy out
+                * first.
+                */
+               if (off + len > sizeof(buf)) {
+                       r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
+                               user_off, (vir_bytes) buf, off, D);
+                       if (r != OK) return r;
+
+                       user_off += off;
+                       user_left -= off;
+                       off = 0;
+               }
+
+               /* Fill in the actual directory entry. */
+               dent = (struct dirent *) &buf[off];
+               dent->d_ino = get_inode_number(child);
+               dent->d_off = pos;
+               dent->d_reclen = len;
+               strcpy(dent->d_name, name);
+
+               off += len;
+       }
+
+       /* If there is anything left in our own buffer, copy that out now. */
+       if (off > 0) {
+               r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
+                       user_off, (vir_bytes) buf, off, D);
+               if (r != OK)
+                       return r;
+
+               user_off += off;
+       }
+
+       fs_m_out.RES_SEEK_POS_HI = 0;
+       fs_m_out.RES_SEEK_POS_LO = pos;
+       fs_m_out.RES_NBYTES = user_off;
+
+       return OK;
+}
diff --git a/lib/libvtreefs/sdbm.c b/lib/libvtreefs/sdbm.c
new file mode 100644 (file)
index 0000000..6efb46a
--- /dev/null
@@ -0,0 +1,28 @@
+/* VTreeFS - sdbm.c - by Alen Stojanov and David van Moolenbroek */
+
+/*
+ * sdbm - ndbm work-alike hashed database library
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
+ * author: oz@nexus.yorku.ca
+ * status: public domain. keep it that way.
+ *
+ * hashing routine
+ */
+
+#include "inc.h"
+/*
+ * polynomial conversion ignoring overflows
+ * [this seems to work remarkably well, in fact better
+ * than the ndbm hash function. Replace at your own risk]
+ * use: 65599  nice.
+ *      65587   even better.
+ */
+PUBLIC long sdbm_hash(char *str, int len)
+{
+       unsigned long n = 0;
+
+       while (len--)
+               n = *str++ + (n << 6) + (n << 16) - n;
+
+       return n;
+}
diff --git a/lib/libvtreefs/stadir.c b/lib/libvtreefs/stadir.c
new file mode 100644 (file)
index 0000000..58859f6
--- /dev/null
@@ -0,0 +1,88 @@
+/* VTreeFS - stadir.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+#include <time.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+
+/*===========================================================================*
+ *                             fs_stat                                      *
+ *===========================================================================*/
+PUBLIC int fs_stat(void)
+{
+       /* Retrieve file status.
+        */
+       char path[PATH_MAX];
+       struct stat statbuf;
+       time_t cur_time;
+       struct inode *node;
+       int r;
+
+       if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
+               return EINVAL;
+
+       /* Fill in the basic info. */
+       statbuf.st_dev = fs_dev;
+       statbuf.st_ino = get_inode_number(node);
+       statbuf.st_mode = node->i_stat.mode;
+       statbuf.st_nlink = !is_inode_deleted(node);
+       statbuf.st_uid = node->i_stat.uid;
+       statbuf.st_gid = node->i_stat.gid;
+       statbuf.st_rdev = (dev_t) node->i_stat.dev;
+       statbuf.st_size = node->i_stat.size;
+
+       /* If it is a symbolic link, return the size of the link target. */
+       if (S_ISLNK(node->i_stat.mode) && vtreefs_hooks->rdlink_hook != NULL) {
+               r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path),
+                       get_inode_cbdata(node));
+
+               if (r == OK)
+                       statbuf.st_size = strlen(path);
+       }
+
+       /* Take the current time as file time for all files. */
+       cur_time = time(NULL);
+       statbuf.st_atime = cur_time;
+       statbuf.st_mtime = cur_time;
+       statbuf.st_ctime = cur_time;
+
+       /* Copy the struct to user space. */
+       return sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
+               (vir_bytes) &statbuf, (phys_bytes) sizeof(statbuf), D);
+}
+
+/*===========================================================================*
+ *                             fs_fstatfs                                   *
+ *===========================================================================*/
+PUBLIC int fs_fstatfs(void)
+{
+       /* Retrieve file system statistics.
+        */
+       struct statfs statfs;
+
+       memset(&statfs, 0, sizeof(statfs));
+
+       /* Copy the struct to user space. */
+       return sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
+               (vir_bytes) &statfs, (phys_bytes) sizeof(statfs), D);
+}
+
+/*===========================================================================*
+ *                             fs_fstatfs                                   *
+ *===========================================================================*/
+PUBLIC int fs_statvfs(void)
+{
+       /* Retrieve file system statistics.
+        */
+       struct statvfs statvfs;
+
+       memset(&statvfs, 0, sizeof(statvfs));
+
+       statvfs.f_fsid = fs_dev;
+       statvfs.f_flag = ST_RDONLY | ST_NOTRUNC;
+       statvfs.f_namemax = PNAME_MAX;
+
+       return sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
+               (vir_bytes) &statvfs, sizeof(statvfs), D);
+}
diff --git a/lib/libvtreefs/table.c b/lib/libvtreefs/table.c
new file mode 100644 (file)
index 0000000..5ab965e
--- /dev/null
@@ -0,0 +1,44 @@
+/* VTreeFS - table.c - by Alen Stojanov and David van Moolenbroek */
+
+#define _TABLE
+#include "inc.h"
+
+PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = {
+       no_sys,         /*  0                   */
+       no_sys,         /*  1   getnode         */
+       fs_putnode,     /*  2   putnode         */
+       no_sys,         /*  3   slink           */
+       no_sys,         /*  4   ftrunc          */
+       no_sys,         /*  5   chown           */
+       no_sys,         /*  6   chmod           */
+       no_sys,         /*  7   inhibread       */
+       fs_stat,        /*  8   stat            */
+       no_sys,         /*  9   utime           */
+       fs_fstatfs,     /* 10   fstatfs         */
+       no_sys,         /* 11   bread           */
+       no_sys,         /* 12   bwrite          */
+       no_sys,         /* 13   unlink          */
+       no_sys,         /* 14   rmdir           */
+       fs_unmount,     /* 15   unmount         */
+       do_noop,        /* 16   sync            */
+       do_noop,        /* 17   new_driver      */
+       no_sys,         /* 18   flush           */
+       fs_read,        /* 19   read            */
+       no_sys,         /* 20   write           */
+       no_sys,         /* 21   mknod           */
+       no_sys,         /* 22   mkdir           */
+       no_sys,         /* 23   create          */
+       no_sys,         /* 24   link            */
+       no_sys,         /* 25   rename          */
+       fs_lookup,      /* 26   lookup          */
+       no_sys,         /* 27   mountpoint      */
+       fs_readsuper,   /* 28   readsuper       */
+       no_sys,         /* 29   newnode         */
+       fs_rdlink,      /* 30   rdlink          */
+       fs_getdents,    /* 31   getdents        */
+       fs_statvfs,     /* 32   statvfs         */
+};
+
+/* This should not fail with "array size is negative": */
+extern int
+       dummy[sizeof(fs_call_vec) == NREQS * sizeof(fs_call_vec[0]) ? 1 : -1];
diff --git a/lib/libvtreefs/utility.c b/lib/libvtreefs/utility.c
new file mode 100644 (file)
index 0000000..cf8cecf
--- /dev/null
@@ -0,0 +1,29 @@
+/* VTreeFS - utility.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+/*===========================================================================*
+ *                             no_sys                                       *
+ *===========================================================================*/
+PUBLIC int no_sys(void)
+{
+       /* This call is not recognized by VTreeFS. If a message hook is
+        * defined, let it handle the call; otherwise return ENOSYS.
+        */
+
+       if (vtreefs_hooks->message_hook != NULL)
+               return vtreefs_hooks->message_hook(&fs_m_in);
+
+       return ENOSYS;
+}
+
+/*===========================================================================*
+ *                             do_noop                                      *
+ *===========================================================================*/
+PUBLIC int do_noop(void)
+{
+       /* This call has no effect.
+        */
+
+       return OK;
+}
diff --git a/lib/libvtreefs/vtreefs.c b/lib/libvtreefs/vtreefs.c
new file mode 100644 (file)
index 0000000..689c3c7
--- /dev/null
@@ -0,0 +1,152 @@
+/* VTreeFS - vtreefs.c - by Alen Stojanov and David van Moolenbroek */
+
+#include "inc.h"
+
+FORWARD _PROTOTYPE( int get_work, (void)                               );
+FORWARD _PROTOTYPE( void send_reply, (int err)                         );
+FORWARD _PROTOTYPE( void got_signal, (int signal)                      );
+
+PRIVATE unsigned int inodes;
+PRIVATE struct inode_stat *root_stat;
+PRIVATE index_t root_entries;
+
+/*===========================================================================*
+ *                             init_server                                  *
+ *===========================================================================*/
+PRIVATE int init_server(int UNUSED(type), sef_init_info_t *UNUSED(info))
+{
+       /* Initialize internal state, and register with VFS.
+        */
+       int r;
+
+       /* Initialize the virtual tree. */
+       init_inodes(inodes, root_stat, root_entries);
+
+       /* Tell VFS that we are here. */
+       fs_m_out.m_type = FS_READY;
+
+       if ((r = send(VFS_PROC_NR, &fs_m_out)) != OK)
+               panic(__FILE__, "error sending login to VFS", r);
+
+       /* Do not yet allow any requests except REQ_READSUPER. */
+       fs_mounted = FALSE;
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             sef_local_startup                            *
+ *===========================================================================*/
+PRIVATE void sef_local_startup(void)
+{
+       sef_setcb_init_fresh(init_server);
+       sef_setcb_init_restart(init_server);
+
+       sef_setcb_signal_handler(got_signal);
+
+       /* No support for live update yet. */
+
+       sef_startup();
+}
+
+/*===========================================================================*
+ *                             start_vtreefs                                *
+ *===========================================================================*/
+PUBLIC void start_vtreefs(struct fs_hooks *hooks, unsigned int nr_inodes,
+               struct inode_stat *stat, index_t nr_indexed_entries)
+{
+       /* This is the main routine of this service. The main loop consists of
+        * three major activities: getting new work, processing the work, and
+        * sending the reply. The loop exits when the process is signaled to
+        * exit; due to limitations of SEF, it can not return to the caller.
+        */
+       int call_nr, err;
+
+       /* Use global variables to work around the inability to pass parameters
+        * through SEF to the initialization function..
+        */
+       vtreefs_hooks = hooks;
+       inodes = nr_inodes;
+       root_stat = stat;
+       root_entries = nr_indexed_entries;
+
+       sef_local_startup();
+
+       for (;;) {
+               call_nr = get_work();
+
+               if (fs_m_in.m_source != VFS_PROC_NR) {
+                       if (vtreefs_hooks->message_hook != NULL) {
+                               /* If the request is not among the recognized
+                                * requests, call the message hook.
+                                */
+                               vtreefs_hooks->message_hook(&fs_m_in);
+                       }
+
+                       continue;
+               }
+
+               if (fs_mounted || call_nr == REQ_READSUPER) {
+                       call_nr -= VFS_BASE;
+
+                       if (call_nr >= 0 && call_nr < NREQS) {
+                               err = (*fs_call_vec[call_nr])();
+                       } else {
+                               err = ENOSYS;
+                       }
+               }
+               else err = EINVAL;
+
+               send_reply(err);
+       }
+}
+
+/*===========================================================================*
+ *                             get_work                                     *
+ *===========================================================================*/
+PRIVATE int get_work(void)
+{
+       /* Retrieve work. Return the call number.
+        */
+       int r;
+
+       if ((r = sef_receive(ANY, &fs_m_in)) != OK)
+               panic(__FILE__, "receive failed", r);
+
+       return fs_m_in.m_type;
+}
+
+/*===========================================================================*
+ *                             send_reply                                   *
+ *===========================================================================*/
+PRIVATE void send_reply(int err)
+{
+       /* Send a reply to the caller.
+        */
+       int r;
+
+       fs_m_out.m_type = err;
+
+       if ((r = send(fs_m_in.m_source, &fs_m_out)) != OK)
+               panic(__FILE__, "unable to send reply", r);
+}
+
+/*===========================================================================*
+ *                             got_signal                                   *
+ *===========================================================================*/
+PRIVATE void got_signal(int signal)
+{
+       /* We received a signal. If it is a termination signal, and the file
+        * system has already been unmounted, clean up and exit.
+        */
+
+       if (signal != SIGTERM)
+               return;
+
+       if (fs_mounted)
+               return;
+
+       cleanup_inodes();
+
+       exit(0);
+}