VTreeFS library
This commit is contained in:
parent
4e95b347a7
commit
bee1f38e01
18 changed files with 1791 additions and 2 deletions
|
@ -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/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/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/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
|
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/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 \
|
INCS+= net/gen/arp_io.h net/gen/dhcp.h net/gen/ether.h \
|
||||||
|
|
57
include/minix/vtreefs.h
Normal file
57
include/minix/vtreefs.h
Normal file
|
@ -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 */
|
|
@ -1,7 +1,8 @@
|
||||||
.include <bsd.own.mk>
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
SUBDIR= csu libc libcurses libdriver libnetdriver libend libedit libm libsys \
|
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"
|
.if ${COMPILER_TYPE} == "ack"
|
||||||
SUBDIR+= ack/libd ack/libe ack/libfp ack/liby
|
SUBDIR+= ack/libd ack/libe ack/libfp ack/liby
|
||||||
|
|
17
lib/libvtreefs/Makefile
Normal file
17
lib/libvtreefs/Makefile
Normal file
|
@ -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>
|
20
lib/libvtreefs/glo.h
Normal file
20
lib/libvtreefs/glo.h
Normal file
|
@ -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 */
|
34
lib/libvtreefs/inc.h
Normal file
34
lib/libvtreefs/inc.h
Normal file
|
@ -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"
|
603
lib/libvtreefs/inode.c
Normal file
603
lib/libvtreefs/inode.c
Normal file
|
@ -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;
|
||||||
|
}
|
40
lib/libvtreefs/inode.h
Normal file
40
lib/libvtreefs/inode.h
Normal file
|
@ -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 */
|
41
lib/libvtreefs/link.c
Normal file
41
lib/libvtreefs/link.c
Normal file
|
@ -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;
|
||||||
|
}
|
65
lib/libvtreefs/mount.c
Normal file
65
lib/libvtreefs/mount.c
Normal file
|
@ -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;
|
||||||
|
}
|
313
lib/libvtreefs/path.c
Normal file
313
lib/libvtreefs/path.c
Normal file
|
@ -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;
|
||||||
|
}
|
42
lib/libvtreefs/proto.h
Normal file
42
lib/libvtreefs/proto.h
Normal file
|
@ -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 */
|
215
lib/libvtreefs/read.c
Normal file
215
lib/libvtreefs/read.c
Normal file
|
@ -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;
|
||||||
|
}
|
28
lib/libvtreefs/sdbm.c
Normal file
28
lib/libvtreefs/sdbm.c
Normal file
|
@ -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;
|
||||||
|
}
|
88
lib/libvtreefs/stadir.c
Normal file
88
lib/libvtreefs/stadir.c
Normal file
|
@ -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);
|
||||||
|
}
|
44
lib/libvtreefs/table.c
Normal file
44
lib/libvtreefs/table.c
Normal file
|
@ -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];
|
29
lib/libvtreefs/utility.c
Normal file
29
lib/libvtreefs/utility.c
Normal file
|
@ -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;
|
||||||
|
}
|
152
lib/libvtreefs/vtreefs.c
Normal file
152
lib/libvtreefs/vtreefs.c
Normal file
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in a new issue