From: David van Moolenbroek Date: Tue, 10 Aug 2010 20:05:51 +0000 (+0000) Subject: VTreeFS library X-Git-Tag: v3.1.8~83 X-Git-Url: http://zhaoyanbai.com/repos/man.host.html?a=commitdiff_plain;h=bee1f38e0110272013d5bbcf42eacfa1a8d3a22b;p=minix.git VTreeFS library --- diff --git a/include/Makefile b/include/Makefile index 16442f64c..b68984311 100644 --- a/include/Makefile +++ b/include/Makefile @@ -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 index 000000000..afcee8c52 --- /dev/null +++ b/include/minix/vtreefs.h @@ -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 */ diff --git a/lib/Makefile b/lib/Makefile index 640a0b934..f3afacc12 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,7 +1,8 @@ .include 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 index 000000000..a4906cff6 --- /dev/null +++ b/lib/libvtreefs/Makefile @@ -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 diff --git a/lib/libvtreefs/glo.h b/lib/libvtreefs/glo.h new file mode 100644 index 000000000..e0298358e --- /dev/null +++ b/lib/libvtreefs/glo.h @@ -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 index 000000000..41e6883bd --- /dev/null +++ b/lib/libvtreefs/inc.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..792ebc727 --- /dev/null +++ b/lib/libvtreefs/inode.c @@ -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 and 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 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 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 hash table. */ + slot = parent_name_hash(parent, name); + LIST_INSERT_HEAD(&parent_name_head[slot], newnode, i_hname); + + /* Add the inode to the 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 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 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 table. */ + LIST_REMOVE(node, i_hname); + + /* Unhash the inode from the 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 index 000000000..a20445bf7 --- /dev/null +++ b/lib/libvtreefs/inode.h @@ -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 + * -> inode hashtable, and if it has an index into the parent, + * is part of a -> 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 index 000000000..01237e0d7 --- /dev/null +++ b/lib/libvtreefs/link.c @@ -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 index 000000000..652b13018 --- /dev/null +++ b/lib/libvtreefs/mount.c @@ -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 index 000000000..b07aaa7ec --- /dev/null +++ b/lib/libvtreefs/path.c @@ -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 index 000000000..02c13e460 --- /dev/null +++ b/lib/libvtreefs/proto.h @@ -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 index 000000000..98d74e668 --- /dev/null +++ b/lib/libvtreefs/read.c @@ -0,0 +1,215 @@ +/* VTreeFS - read.c - by Alen Stojanov and David van Moolenbroek */ + +#include "inc.h" +#include + +#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 index 000000000..6efb46a1f --- /dev/null +++ b/lib/libvtreefs/sdbm.c @@ -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 index 000000000..58859f6c4 --- /dev/null +++ b/lib/libvtreefs/stadir.c @@ -0,0 +1,88 @@ +/* VTreeFS - stadir.c - by Alen Stojanov and David van Moolenbroek */ + +#include "inc.h" + +#include +#include +#include + +/*===========================================================================* + * 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 index 000000000..5ab965e75 --- /dev/null +++ b/lib/libvtreefs/table.c @@ -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 index 000000000..cf8cecfb3 --- /dev/null +++ b/lib/libvtreefs/utility.c @@ -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 index 000000000..689c3c71a --- /dev/null +++ b/lib/libvtreefs/vtreefs.c @@ -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); +}