Create SFFS library out of HGFS

This Shared Folders File System library (libsffs) now contains all the
file system logic originally in HGFS. The actual HGFS server code is
now a stub that passes on all the work to libsffs. The libhgfs library
is changed accordingly.
This commit is contained in:
David van Moolenbroek
2012-04-09 18:08:26 +02:00
parent 09b327b042
commit ef7b484e5c
41 changed files with 664 additions and 534 deletions

View File

@@ -2,11 +2,11 @@
SUBDIR= csu libcompat_minix libc libblockdriver libchardriver \
libnetdriver libedit libm libsys libtimers libutil \
libl libhgfs libz libfetch libvtreefs libaudiodriver libmthread \
libl libz libfetch libvtreefs libaudiodriver libmthread \
libexec libdevman libusb libminlib libasyn \
libddekit libminixfs libbdev libelf libminc libcrypt libterminfo \
libcurses libvassert libutil libpuffs librefuse libbz2 libarchive libprop \
libnetsock
libcurses libvassert libutil libpuffs librefuse libbz2 libarchive \
libprop libnetsock libsffs libhgfs
SUBDIR+= ../external/public-domain/xz/lib

View File

@@ -2,6 +2,6 @@
LIB= hgfs
SRCS= backdoor.S attr.c channel.c dir.c error.c file.c \
info.c link.c misc.c path.c rpc.c time.c
hgfs.c info.c link.c path.c rpc.c time.c
.include <bsd.lib.mk>

View File

@@ -8,7 +8,7 @@
* attr_get *
*===========================================================================*/
void attr_get(attr)
struct hgfs_attr *attr;
struct sffs_attr *attr;
{
/* Get attribute information from the RPC buffer, storing the requested parts
* in the given attr structure.
@@ -20,16 +20,16 @@ struct hgfs_attr *attr;
size_lo = RPC_NEXT32;
size_hi = RPC_NEXT32;
if (attr->a_mask & HGFS_ATTR_SIZE)
if (attr->a_mask & SFFS_ATTR_SIZE)
attr->a_size = make64(size_lo, size_hi);
time_get((attr->a_mask & HGFS_ATTR_CRTIME) ? &attr->a_crtime : NULL);
time_get((attr->a_mask & HGFS_ATTR_ATIME) ? &attr->a_atime : NULL);
time_get((attr->a_mask & HGFS_ATTR_MTIME) ? &attr->a_mtime : NULL);
time_get((attr->a_mask & HGFS_ATTR_CTIME) ? &attr->a_ctime : NULL);
time_get((attr->a_mask & SFFS_ATTR_CRTIME) ? &attr->a_crtime : NULL);
time_get((attr->a_mask & SFFS_ATTR_ATIME) ? &attr->a_atime : NULL);
time_get((attr->a_mask & SFFS_ATTR_MTIME) ? &attr->a_mtime : NULL);
time_get((attr->a_mask & SFFS_ATTR_CTIME) ? &attr->a_ctime : NULL);
mode |= HGFS_PERM_TO_MODE(RPC_NEXT8);
if (attr->a_mask & HGFS_ATTR_MODE) attr->a_mode = mode;
if (attr->a_mask & SFFS_ATTR_MODE) attr->a_mode = mode;
}
/*===========================================================================*
@@ -37,7 +37,7 @@ struct hgfs_attr *attr;
*===========================================================================*/
int hgfs_getattr(path, attr)
char *path;
struct hgfs_attr *attr;
struct sffs_attr *attr;
{
/* Get selected attributes of a file by path name.
*/
@@ -60,7 +60,7 @@ struct hgfs_attr *attr;
*===========================================================================*/
int hgfs_setattr(path, attr)
char *path;
struct hgfs_attr *attr;
struct sffs_attr *attr;
{
/* Set selected attributes of a file by path name.
*/
@@ -74,14 +74,14 @@ struct hgfs_attr *attr;
* HGFS protocol version (v2/v3).
*/
mask = 0;
if (attr->a_mask & HGFS_ATTR_MODE) mask |= HGFS_ATTR_MODE;
if (attr->a_mask & HGFS_ATTR_SIZE) mask |= HGFS_ATTR_SIZE;
if (attr->a_mask & HGFS_ATTR_CRTIME) mask |= HGFS_ATTR_CRTIME;
if (attr->a_mask & HGFS_ATTR_ATIME)
if (attr->a_mask & SFFS_ATTR_MODE) mask |= HGFS_ATTR_MODE;
if (attr->a_mask & SFFS_ATTR_SIZE) mask |= HGFS_ATTR_SIZE;
if (attr->a_mask & SFFS_ATTR_CRTIME) mask |= HGFS_ATTR_CRTIME;
if (attr->a_mask & SFFS_ATTR_ATIME)
mask |= HGFS_ATTR_ATIME | HGFS_ATTR_ATIME_SET;
if (attr->a_mask & HGFS_ATTR_MTIME)
if (attr->a_mask & SFFS_ATTR_MTIME)
mask |= HGFS_ATTR_MTIME | HGFS_ATTR_MTIME_SET;
if (attr->a_mask & HGFS_ATTR_CTIME) mask |= HGFS_ATTR_CTIME;
if (attr->a_mask & SFFS_ATTR_CTIME) mask |= HGFS_ATTR_CTIME;
RPC_NEXT8 = mask;

View File

@@ -7,7 +7,7 @@
*===========================================================================*/
int hgfs_opendir(path, handle)
char *path;
hgfs_dir_t *handle;
sffs_dir_t *handle;
{
/* Open a directory. Store a directory handle upon success.
*/
@@ -20,7 +20,7 @@ hgfs_dir_t *handle;
if ((r = rpc_query()) != OK)
return r;
*handle = (hgfs_dir_t)RPC_NEXT32;
*handle = (sffs_dir_t)RPC_NEXT32;
return OK;
}
@@ -29,11 +29,11 @@ hgfs_dir_t *handle;
* hgfs_readdir *
*===========================================================================*/
int hgfs_readdir(handle, index, buf, size, attr)
hgfs_dir_t handle;
sffs_dir_t handle;
unsigned int index;
char *buf;
size_t size;
struct hgfs_attr *attr;
struct sffs_attr *attr;
{
/* Read a directory entry from an open directory, using a zero-based index
* number. Upon success, the resulting path name is stored in the given buffer
@@ -67,7 +67,7 @@ struct hgfs_attr *attr;
* hgfs_closedir *
*===========================================================================*/
int hgfs_closedir(handle)
hgfs_dir_t handle;
sffs_dir_t handle;
{
/* Close an open directory.
*/

View File

@@ -12,7 +12,7 @@ int hgfs_open(path, flags, mode, handle)
char *path; /* path name to open */
int flags; /* open flags to use */
int mode; /* mode to create (user bits only) */
hgfs_file_t *handle; /* place to store resulting handle */
sffs_file_t *handle; /* place to store resulting handle */
{
/* Open a file. Store a file handle upon success.
*/
@@ -42,7 +42,7 @@ hgfs_file_t *handle; /* place to store resulting handle */
if ((r = rpc_query()) != OK)
return r;
*handle = (hgfs_file_t)RPC_NEXT32;
*handle = (sffs_file_t)RPC_NEXT32;
return OK;
}
@@ -50,8 +50,8 @@ hgfs_file_t *handle; /* place to store resulting handle */
/*===========================================================================*
* hgfs_read *
*===========================================================================*/
int hgfs_read(handle, buf, size, off)
hgfs_file_t handle; /* handle to open file */
ssize_t hgfs_read(handle, buf, size, off)
sffs_file_t handle; /* handle to open file */
char *buf; /* data buffer or NULL */
size_t size; /* maximum number of bytes to read */
u64_t off; /* file offset */
@@ -84,8 +84,8 @@ u64_t off; /* file offset */
/*===========================================================================*
* hgfs_write *
*===========================================================================*/
int hgfs_write(handle, buf, len, off)
hgfs_file_t handle; /* handle to open file */
ssize_t hgfs_write(handle, buf, len, off)
sffs_file_t handle; /* handle to open file */
char *buf; /* data buffer or NULL */
size_t len; /* number of bytes to write */
u64_t off; /* file offset */
@@ -116,7 +116,7 @@ u64_t off; /* file offset */
* hgfs_close *
*===========================================================================*/
int hgfs_close(handle)
hgfs_file_t handle; /* handle to open file */
sffs_file_t handle; /* handle to open file */
{
/* Close an open file.
*/

View File

@@ -2,18 +2,48 @@
#include "inc.h"
struct sffs_table hgfs_table = {
.t_open = hgfs_open,
.t_read = hgfs_read,
.t_write = hgfs_write,
.t_close = hgfs_close,
.t_readbuf = hgfs_readbuf,
.t_writebuf = hgfs_writebuf,
.t_opendir = hgfs_opendir,
.t_readdir = hgfs_readdir,
.t_closedir = hgfs_closedir,
.t_getattr = hgfs_getattr,
.t_setattr = hgfs_setattr,
.t_mkdir = hgfs_mkdir,
.t_unlink = hgfs_unlink,
.t_rmdir = hgfs_rmdir,
.t_rename = hgfs_rename,
.t_queryvol = hgfs_queryvol,
};
/*===========================================================================*
* hgfs_init *
*===========================================================================*/
int hgfs_init()
int hgfs_init(const struct sffs_table **tablep)
{
/* Initialize the library. Return OK on success, or a negative error code
* otherwise. If EAGAIN is returned, shared folders are disabled.
*/
int r;
time_init();
return rpc_open();
r = rpc_open();
if (r == OK)
*tablep = &hgfs_table;
return r;
}
/*===========================================================================*
@@ -26,3 +56,4 @@ void hgfs_cleanup()
rpc_close();
}

View File

@@ -1,16 +1,7 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#ifndef __NBSD_LIBC
#define _POSIX_SOURCE 1 /* need PATH_MAX */
#endif
#define _SYSTEM 1 /* need negative error codes */
#include <minix/config.h>
#include <minix/const.h>
#include <string.h>
#include <errno.h>
#include <minix/drivers.h>
#include <minix/sffs.h>
#include <minix/hgfs.h>
#define PREFIX(x) __libhgfs_##x

View File

@@ -2,7 +2,9 @@
/* attr.c */
#define attr_get PREFIX(attr_get)
void attr_get(struct hgfs_attr *attr);
void attr_get(struct sffs_attr *attr);
int hgfs_getattr(char *path, struct sffs_attr *attr);
int hgfs_setattr(char *path, struct sffs_attr *attr);
/* backdoor.s */
#define backdoor PREFIX(backdoor)
@@ -22,10 +24,33 @@ void channel_close(struct channel *ch);
int channel_send(struct channel *ch, char *buf, int len);
int channel_recv(struct channel *ch, char *buf, int max);
/* dir.c */
int hgfs_opendir(char *path, sffs_dir_t *handle);
int hgfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, size_t size,
struct sffs_attr *attr);
int hgfs_closedir(sffs_dir_t handle);
/* error.c */
#define error_convert PREFIX(error_convert)
int error_convert(int err);
/* file.c */
int hgfs_open(char *path, int flags, int mode, sffs_file_t *handle);
ssize_t hgfs_read(sffs_file_t handle, char *buf, size_t size, u64_t offset);
ssize_t hgfs_write(sffs_file_t handle, char *buf, size_t len, u64_t offset);
int hgfs_close(sffs_file_t handle);
size_t hgfs_readbuf(char **ptr);
size_t hgfs_writebuf(char **ptr);
/* info.c */
int hgfs_queryvol(char *path, u64_t *free, u64_t *total);
/* link.c */
int hgfs_mkdir(char *path, int mode);
int hgfs_unlink(char *path);
int hgfs_rmdir(char *path);
int hgfs_rename(char *opath, char *npath);
/* path.c */
#define path_put PREFIX(path_put)
#define path_get PREFIX(path_get)

9
lib/libsffs/Makefile Normal file
View File

@@ -0,0 +1,9 @@
# Makefile for libsffs
.include <bsd.own.mk>
LIB= sffs
SRCS= dentry.c handle.c inode.c link.c lookup.c main.c misc.c mount.c \
name.c path.c read.c stat.c table.c util.c verify.c write.c
.include <bsd.lib.mk>

16
lib/libsffs/const.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _SFFS_CONST_H
#define _SFFS_CONST_H
/* Number of inodes. */
/* The following number must not exceed 16. The i_num field is only a short. */
#define NUM_INODE_BITS 8
/* Number of entries in the name hashtable. */
#define NUM_HASH_SLOTS 1023
/* Arbitrary block size constant returned by fstatfs and statvfs.
* Also used by getdents. This is not the underlying data transfer unit size.
*/
#define BLOCK_SIZE 4096
#endif /* _SFFS_CONST_H */

193
lib/libsffs/dentry.c Normal file
View File

@@ -0,0 +1,193 @@
/* This file contains directory entry management and the name lookup hashtable.
*
* The entry points into this file are:
* init_dentry initialize the directory entry name lookup hashtable
* lookup_dentry find an inode based on parent directory and name
* add_dentry add an inode as directory entry to a parent directory
* del_dentry delete an inode from its parent directory
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
static LIST_HEAD(hash_head, inode) hash_table[NUM_HASH_SLOTS];
static void del_one_dentry(struct inode *ino);
static unsigned int hash_dentry(struct inode *parent, char *name);
/*===========================================================================*
* init_dentry *
*===========================================================================*/
void init_dentry()
{
/* Initialize the names hashtable.
*/
int i;
for (i = 0; i < NUM_HASH_SLOTS; i++)
LIST_INIT(&hash_table[i]);
}
/*===========================================================================*
* lookup_dentry *
*===========================================================================*/
struct inode *lookup_dentry(parent, name)
struct inode *parent;
char *name;
{
/* Given a directory inode and a component name, look up the inode associated
* with that directory entry. Return the inode (with increased reference
* count) if found, or NULL otherwise.
*/
struct inode *ino;
unsigned int slot;
assert(IS_DIR(parent));
slot = hash_dentry(parent, name);
LIST_FOREACH(ino, &hash_table[slot], i_hash) {
if (compare_name(ino->i_name, name) == TRUE)
break;
}
if (ino == NULL)
return NULL;
get_inode(ino);
return ino;
}
/*===========================================================================*
* add_dentry *
*===========================================================================*/
void add_dentry(parent, name, ino)
struct inode *parent;
char *name;
struct inode *ino;
{
/* Add an entry to a parent inode, in the form of a new inode, with the given
* name. An entry with this name must not already exist.
*/
unsigned int slot;
assert(IS_DIR(parent));
assert(parent->i_ref > 0);
assert(ino->i_ref > 0);
assert(name[0]);
assert(strlen(name) <= NAME_MAX);
link_inode(parent, ino);
strcpy(ino->i_name, name);
/* hash_add(ino); */
slot = hash_dentry(parent, ino->i_name);
LIST_INSERT_HEAD(&hash_table[slot], ino, i_hash);
}
/*===========================================================================*
* del_one_dentry *
*===========================================================================*/
static void del_one_dentry(ino)
struct inode *ino;
{
/* This inode has become inaccessible by name. Disassociate it from its parent
* and remove it from the names hash table.
*/
/* There can and must be exactly one root inode, so don't delete it! */
if (IS_ROOT(ino))
return;
/* INUSE -> DELETED, CACHED -> FREE */
/* Remove the entry from the hashtable.
* Decrease parent's refcount, possibly adding it to the free list.
* Do not touch open handles. Do not add to the free list.
*/
assert(ino->i_parent != NULL);
/* hash_del(ino); */
LIST_REMOVE(ino, i_hash);
ino->i_name[0] = 0;
unlink_inode(ino);
}
/*===========================================================================*
* del_dentry *
*===========================================================================*/
void del_dentry(ino)
struct inode *ino;
{
/* Disassociate an inode from its parent, effectively deleting it. Recursively
* delete all its children as well, fragmenting the deleted branch into single
* inodes.
*/
LIST_HEAD(work_list, inode) work_list;
struct inode *child;
del_one_dentry(ino);
/* Quick way out: one directory entry that itself has no children. */
if (!HAS_CHILDREN(ino))
return;
/* Recursively delete all children of the inode as well.
* Iterative version: this is potentially 128 levels deep.
*/
LIST_INIT(&work_list);
LIST_INSERT_HEAD(&work_list, ino, i_next);
do {
ino = LIST_FIRST(&work_list);
LIST_REMOVE(ino, i_next);
assert(IS_DIR(ino));
while (!LIST_EMPTY(&ino->i_child)) {
child = LIST_FIRST(&ino->i_child);
LIST_REMOVE(child, i_next);
del_one_dentry(child);
if (HAS_CHILDREN(child))
LIST_INSERT_HEAD(&work_list, child, i_next);
}
} while (!LIST_EMPTY(&work_list));
}
/*===========================================================================*
* hash_dentry *
*===========================================================================*/
static unsigned int hash_dentry(parent, name)
struct inode *parent;
char *name;
{
/* Generate a hash value for a given name. Normalize the name first, so that
* different variations of the name will result in the same hash value.
*/
unsigned int val;
char buf[NAME_MAX+1], *p;
dprintf(("%s: hash_dentry for '%s'\n", sffs_name, name));
normalize_name(buf, name);
/* djb2 string hash algorithm, XOR variant */
val = 5381;
for (p = buf; *p; p++)
val = ((val << 5) + val) ^ *p;
/* Mix with inode number: typically, many file names occur in several
* different directories.
*/
return (val ^ parent->i_num) % NUM_HASH_SLOTS;
}

19
lib/libsffs/glo.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef _SFFS_GLO_H
#define _SFFS_GLO_H
#ifdef _TABLE
#undef EXTERN
#define EXTERN
#endif
EXTERN char *sffs_name; /* file server name */
EXTERN const struct sffs_table *sffs_table; /* call table */
EXTERN struct sffs_params *sffs_params; /* parameters */
EXTERN message m_in; /* request message */
EXTERN message m_out; /* reply message */
EXTERN struct state state; /* global state */
extern int(*call_vec[]) (void);
#endif /* _SFFS_GLO_H */

79
lib/libsffs/handle.c Normal file
View File

@@ -0,0 +1,79 @@
/* This file contains open file and directory handle management functions.
*
* The entry points into this file are:
* get_handle open a handle for an inode and store the handle
* put_handle close any handles associated with an inode
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <fcntl.h>
/*===========================================================================*
* get_handle *
*===========================================================================*/
int get_handle(ino)
struct inode *ino;
{
/* Get an open file or directory handle for an inode.
*/
char path[PATH_MAX];
int r;
/* If we don't have a file handle yet, try to open the file now. */
if (ino->i_flags & I_HANDLE)
return OK;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
if (IS_DIR(ino)) {
r = sffs_table->t_opendir(path, &ino->i_dir);
}
else {
if (!state.s_read_only)
r = sffs_table->t_open(path, O_RDWR, 0, &ino->i_file);
/* Protection or mount status might prevent us from writing. With the
* information that we have available, this is the best we can do..
*/
if (state.s_read_only || r != OK)
r = sffs_table->t_open(path, O_RDONLY, 0, &ino->i_file);
}
if (r != OK)
return r;
ino->i_flags |= I_HANDLE;
return OK;
}
/*===========================================================================*
* put_handle *
*===========================================================================*/
void put_handle(ino)
struct inode *ino;
{
/* Close an open file or directory handle associated with an inode.
*/
int r;
if (!(ino->i_flags & I_HANDLE))
return;
/* We ignore any errors here, because we can't deal with them anyway. */
if (IS_DIR(ino))
r = sffs_table->t_closedir(ino->i_dir);
else
r = sffs_table->t_close(ino->i_file);
if (r != OK)
printf("%s: put_handle: handle close operation returned %d\n",
sffs_name, r);
ino->i_flags &= ~I_HANDLE;
}

24
lib/libsffs/inc.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef _SFFS_INC_H
#define _SFFS_INC_H
#include <minix/drivers.h>
#include <minix/vfsif.h>
#include <minix/optset.h>
#include <minix/sffs.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <assert.h>
#if DEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif
#include "type.h"
#include "const.h"
#include "proto.h"
#include "glo.h"
#include "inode.h"
#endif /* _SFFS_INC_H */

301
lib/libsffs/inode.c Normal file
View File

@@ -0,0 +1,301 @@
/* This file deals with inode management.
*
* The entry points into this file are:
* init_inode initialize the inode table, return the root inode
* find_inode find an inode based on its inode number
* get_inode increase the reference count of an inode
* put_inode decrease the reference count of an inode
* link_inode link an inode as a directory entry to another inode
* unlink_inode unlink an inode from its parent directory
* get_free_inode return a free inode object
* have_free_inode check whether there is a free inode available
* have_used_inode check whether any inode is still in use
* do_putnode perform the PUTNODE file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
static struct inode inodes[NUM_INODES];
static TAILQ_HEAD(free_head, inode) free_list;
/*===========================================================================*
* init_inode *
*===========================================================================*/
struct inode *init_inode()
{
/* Initialize inode table. Return the root inode.
*/
struct inode *ino;
unsigned int index;
TAILQ_INIT(&free_list);
dprintf(("%s: %d inodes, %u bytes each, equals %u bytes\n",
sffs_name, NUM_INODES, sizeof(struct inode), sizeof(inodes)));
/* Mark all inodes except the root inode as free. */
for (index = 1; index < NUM_INODES; index++) {
ino = &inodes[index];
ino->i_parent = NULL;
LIST_INIT(&ino->i_child);
ino->i_num = index + 1;
ino->i_gen = (unsigned short)-1; /* aesthetics */
ino->i_ref = 0;
ino->i_flags = 0;
TAILQ_INSERT_TAIL(&free_list, ino, i_free);
}
/* Initialize and return the root inode. */
ino = &inodes[0];
ino->i_parent = ino; /* root inode is its own parent */
LIST_INIT(&ino->i_child);
ino->i_num = ROOT_INODE_NR;
ino->i_gen = 0; /* unused by root node */
ino->i_ref = 1; /* root inode is hereby in use */
ino->i_flags = I_DIR; /* root inode is a directory */
ino->i_name[0] = 0; /* root inode has empty name */
return ino;
}
/*===========================================================================*
* find_inode *
*===========================================================================*/
struct inode *find_inode(ino_nr)
ino_t ino_nr;
{
/* Get an inode based on its inode number. Do not increase its reference count.
*/
struct inode *ino;
int index;
/* Inode 0 (= index -1) is not a valid inode number. */
index = INODE_INDEX(ino_nr);
if (index < 0) {
printf("%s: VFS passed invalid inode number!\n", sffs_name);
return NULL;
}
assert(index < NUM_INODES);
ino = &inodes[index];
/* Make sure the generation number matches. */
if (INODE_GEN(ino_nr) != ino->i_gen) {
printf("%s: VFS passed outdated inode number!\n", sffs_name);
return NULL;
}
/* The VFS/FS protocol only uses referenced inodes. */
if (ino->i_ref == 0)
printf("%s: VFS passed unused inode!\n", sffs_name);
return ino;
}
/*===========================================================================*
* get_inode *
*===========================================================================*/
void get_inode(ino)
struct inode *ino;
{
/* Increase the given inode's reference count. If both reference and link
* count were zero before, remove the inode from the free list.
*/
dprintf(("%s: get_inode(%p) ['%s']\n", sffs_name, ino, ino->i_name));
/* (INUSE, CACHED) -> INUSE */
/* If this is the first reference, remove the node from the free list. */
if (ino->i_ref == 0 && !HAS_CHILDREN(ino))
TAILQ_REMOVE(&free_list, ino, i_free);
ino->i_ref++;
if (ino->i_ref == 0)
panic("inode reference count wrapped");
}
/*===========================================================================*
* put_inode *
*===========================================================================*/
void put_inode(ino)
struct inode *ino;
{
/* Decrease an inode's reference count. If this count has reached zero, close
* the inode's file handle, if any. If both reference and link count have
* reached zero, mark the inode as cached or free.
*/
dprintf(("%s: put_inode(%p) ['%s']\n", sffs_name, ino, ino->i_name));
assert(ino != NULL);
assert(ino->i_ref > 0);
ino->i_ref--;
/* If there are still references to this inode, we're done here. */
if (ino->i_ref > 0)
return;
/* Close any file handle associated with this inode. */
put_handle(ino);
/* Only add the inode to the free list if there are also no links to it. */
if (HAS_CHILDREN(ino))
return;
/* INUSE -> CACHED, DELETED -> FREE */
/* Add the inode to the head or tail of the free list, depending on whether
* it is also deleted (and therefore can never be reused as is).
*/
if (ino->i_parent == NULL)
TAILQ_INSERT_HEAD(&free_list, ino, i_free);
else
TAILQ_INSERT_TAIL(&free_list, ino, i_free);
}
/*===========================================================================*
* link_inode *
*===========================================================================*/
void link_inode(parent, ino)
struct inode *parent;
struct inode *ino;
{
/* Link an inode to a parent. If both reference and link count were zero
* before, remove the inode from the free list. This function should only be
* called from add_dentry().
*/
/* This can never happen, right? */
if (parent->i_ref == 0 && !HAS_CHILDREN(parent))
TAILQ_REMOVE(&free_list, parent, i_free);
LIST_INSERT_HEAD(&parent->i_child, ino, i_next);
ino->i_parent = parent;
}
/*===========================================================================*
* unlink_inode *
*===========================================================================*/
void unlink_inode(ino)
struct inode *ino;
{
/* Unlink an inode from its parent. If both reference and link count have
* reached zero, mark the inode as cached or free. This function should only
* be used from del_dentry().
*/
struct inode *parent;
parent = ino->i_parent;
LIST_REMOVE(ino, i_next);
if (parent->i_ref == 0 && !HAS_CHILDREN(parent)) {
if (parent->i_parent == NULL)
TAILQ_INSERT_HEAD(&free_list, parent, i_free);
else
TAILQ_INSERT_TAIL(&free_list, parent, i_free);
}
ino->i_parent = NULL;
}
/*===========================================================================*
* get_free_inode *
*===========================================================================*/
struct inode *get_free_inode()
{
/* Return a free inode object (with reference count 1), if available.
*/
struct inode *ino;
/* [CACHED -> FREE,] FREE -> DELETED */
/* If there are no inodes on the free list, we cannot satisfy the request. */
if (TAILQ_EMPTY(&free_list)) {
printf("%s: out of inodes!\n", sffs_name);
return NULL;
}
ino = TAILQ_FIRST(&free_list);
TAILQ_REMOVE(&free_list, ino, i_free);
assert(ino->i_ref == 0);
assert(!HAS_CHILDREN(ino));
/* If this was a cached inode, free it first. */
if (ino->i_parent != NULL)
del_dentry(ino);
assert(ino->i_parent == NULL);
/* Initialize a subset of its fields */
ino->i_gen++;
ino->i_ref = 1;
return ino;
}
/*===========================================================================*
* have_free_inode *
*===========================================================================*/
int have_free_inode()
{
/* Check whether there are any free inodes at the moment. Kind of lame, but
* this allows for easier error recovery in some places.
*/
return !TAILQ_EMPTY(&free_list);
}
/*===========================================================================*
* have_used_inode *
*===========================================================================*/
int have_used_inode()
{
/* Check whether any inodes are still in use, that is, any of the inodes have
* a reference count larger than zero.
*/
unsigned int index;
for (index = 0; index < NUM_INODES; index++)
if (inodes[index].i_ref > 0)
return TRUE;
return FALSE;
}
/*===========================================================================*
* do_putnode *
*===========================================================================*/
int do_putnode()
{
/* Decrease an inode's reference count.
*/
struct inode *ino;
int count;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
count = m_in.REQ_COUNT;
if (count <= 0 || count > ino->i_ref) return EINVAL;
ino->i_ref -= count - 1;
put_inode(ino);
return OK;
}

88
lib/libsffs/inode.h Normal file
View File

@@ -0,0 +1,88 @@
#ifndef _SFFS_INODE_H
#define _SFFS_INODE_H
/* We cannot use inode number 0, so to be able to use bitmasks to combine
* inode and generation numbers, we have to use one fewer than the maximum of
* inodes possible by using NUM_INODE_BITS bits.
*/
#define NUM_INODES ((1 << NUM_INODE_BITS) - 1)
/* The main portion of the inode array forms a fully linked tree, providing a
* cached partial view of what the server believes is on the host system. Each
* inode contains only a pointer to its parent and its path component name, so
* a path for an inode is constructed by walking up to the root. Inodes that
* are in use as directory for a child node must not be recycled; in this case,
* the i_child list is not empty. Naturally, inodes for which VFS holds a
* reference must also not be recycled; the i_ref count takes care of that.
*
* Multiple hard links to a single file do not exist; that is why an inode is
* also a directory entry (when in IN USE or CACHED state). Notifications about
* modifications on the host system are not part of the protocol, so sometimes
* the server may discover that some files do not exist anymore. In that case,
* they are marked as DELETED in the inode table. Such files may still be used
* because of open file handles, but cannot be referenced by path anymore. The
* underlying protocol may not support truncation of open files anyway. Since
* we currently cannot guarantee that a file is actually opened before it is
* deleted (as this would consistute opening every file being looked up), we
* effectively do not properly support open deleted files at all anyway.
*
* An inode is REFERENCED iff it has a reference count > 0 *or* has children.
* An inode is LINKED IN iff it has a parent.
*
* An inode is IN USE iff it is REFERENCED and LINKED IN.
* An inode is CACHED iff it is NOT REFERENCED and LINKED IN.
* An inode is DELETED iff it is REFERENCED and NOT LINKED IN.
* An inode is FREE iff it is NOT REFERENCED and NOT LINKED IN.
*
* An inode may have an open file handle if it is IN USE or DELETED.
* An inode may have children if it is IN USE (and is a directory).
* An inode is in the names hashtable iff it is IN USE or CACHED.
* An inode is on the free list iff it is CACHED or FREE.
*
* - An IN USE inode becomes DELETED when it is either deleted explicitly, or
* when it has been determined to have become unreachable by path name on the
* host system (the verify_* functions take care of this).
* - An IN USE inode may become CACHED when there are no VFS references to it
* anymore (i_ref == 0), and it is not a directory with children.
* - A DELETED inode cannot have children, but may become FREE when there are
* also no VFS references to it anymore.
* - A CACHED inode may become IN USE when either i_ref or i_link is increased
* from zero. Practically, it will always be i_ref that gets increased, since
* i_link cannot be increased by VFS without having a reference to the inode.
* - A CACHED or FREE inode may be reused for other purposes at any time.
*/
struct inode {
struct inode *i_parent; /* parent inode pointer */
LIST_HEAD(child_head, inode) i_child; /* child inode anchor */
LIST_ENTRY(inode) i_next; /* sibling inode chain entry */
LIST_ENTRY(inode) i_hash; /* hashtable chain entry */
unsigned short i_num; /* inode number for quick reference */
unsigned short i_gen; /* inode generation number */
unsigned short i_ref; /* VFS reference count */
unsigned short i_flags; /* any combination of I_* flags */
union {
TAILQ_ENTRY(inode) i_free; /* free list chain entry */
sffs_file_t i_file; /* handle to open file */
sffs_dir_t i_dir; /* handle to open directory */
};
char i_name[NAME_MAX+1]; /* entry name in parent directory */
};
#define I_DIR 0x01 /* this inode represents a directory */
#define I_HANDLE 0x02 /* this inode has an open handle */
/* warning: the following line is not a proper macro */
#define INODE_NR(i) (((i)->i_gen << NUM_INODE_BITS) | (i)->i_num)
#define INODE_INDEX(n) (((n) & ((1 << NUM_INODE_BITS) - 1)) - 1)
#define INODE_GEN(n) (((n) >> NUM_INODE_BITS) & 0xffff)
#define ROOT_INODE_NR 1
#define IS_DIR(i) ((i)->i_flags & I_DIR)
#define IS_ROOT(i) ((i)->i_num == ROOT_INODE_NR)
#define HAS_CHILDREN(i) (!LIST_EMPTY(&(i)->i_child))
#define MODE_TO_DIRFLAG(m) (S_ISDIR(m) ? I_DIR : 0)
#endif /* _SFFS_INODE_H */

404
lib/libsffs/link.c Normal file
View File

@@ -0,0 +1,404 @@
/* This file contains directory entry related file system call handlers.
*
* The entry points into this file are:
* do_create perform the CREATE file system call
* do_mkdir perform the MKDIR file system call
* do_unlink perform the UNLINK file system call
* do_rmdir perform the RMDIR file system call
* do_rename perform the RENAME file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <fcntl.h>
static int force_remove(char *path, int dir);
/*===========================================================================*
* do_create *
*===========================================================================*/
int do_create()
{
/* Create a new file.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
struct sffs_attr attr;
sffs_file_t handle;
int r;
/* We cannot create files on a read-only file system. */
if (state.s_read_only)
return EROFS;
/* Get path, name, parent inode and possibly inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Are we going to need a new inode upon success?
* Then make sure there is one available before trying anything.
*/
if (ino == NULL || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
if (!have_free_inode()) {
if (ino != NULL)
put_inode(ino);
return ENFILE;
}
}
/* Perform the actual create call. */
r = sffs_table->t_open(path, O_CREAT | O_EXCL | O_RDWR, m_in.REQ_MODE,
&handle);
if (r != OK) {
/* Let's not try to be too clever with error codes here. If something
* is wrong with the directory, we'll find out later anyway.
*/
if (ino != NULL)
put_inode(ino);
return r;
}
/* Get the created file's attributes. */
attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
r = sffs_table->t_getattr(path, &attr);
/* If this fails, or returns a directory, we have a problem. This
* scenario is in fact possible with race conditions.
* Simulate a close and return a somewhat appropriate error.
*/
if (r != OK || S_ISDIR(attr.a_mode)) {
printf("%s: lost file after creation!\n", sffs_name);
sffs_table->t_close(handle);
if (ino != NULL) {
del_dentry(ino);
put_inode(ino);
}
return (r == OK) ? EEXIST : r;
}
/* We do assume that the underlying open(O_CREAT|O_EXCL) call did its job.
* If we previousy found an inode, get rid of it now. It's old.
*/
if (ino != NULL) {
del_dentry(ino);
put_inode(ino);
}
/* Associate the open file handle with an inode, and reply with its details.
*/
ino = get_free_inode();
assert(ino != NULL); /* we checked before whether we had a free one */
ino->i_file = handle;
ino->i_flags = I_HANDLE;
add_dentry(parent, name, ino);
m_out.RES_INODE_NR = INODE_NR(ino);
m_out.RES_MODE = get_mode(ino, attr.a_mode);
m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
m_out.RES_UID = sffs_params->p_uid;
m_out.RES_GID = sffs_params->p_gid;
m_out.RES_DEV = NO_DEV;
return OK;
}
/*===========================================================================*
* do_mkdir *
*===========================================================================*/
int do_mkdir()
{
/* Make a new directory.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
int r;
/* We cannot create directories on a read-only file system. */
if (state.s_read_only)
return EROFS;
/* Get the path string and possibly an inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Perform the actual mkdir call. */
r = sffs_table->t_mkdir(path, m_in.REQ_MODE);
if (r != OK) {
if (ino != NULL)
put_inode(ino);
return r;
}
/* If we thought the new dentry already existed, it was apparently gone
* already. Delete it.
*/
if (ino != NULL) {
del_dentry(ino);
put_inode(ino);
}
return OK;
}
/*===========================================================================*
* force_remove *
*===========================================================================*/
static int force_remove(path, dir)
char *path; /* path to file or directory */
int dir; /* TRUE iff directory */
{
/* Remove a file or directory. Wrapper around unlink and rmdir that makes the
* target temporarily writable if the operation fails with an access denied
* error. On Windows hosts, read-only files or directories cannot be removed
* (even though they can be renamed). In general, the SFFS library follows the
* behavior of the host file system, but this case just confuses the hell out
* of the MINIX userland..
*/
struct sffs_attr attr;
int r, r2;
/* First try to remove the target. */
if (dir)
r = sffs_table->t_rmdir(path);
else
r = sffs_table->t_unlink(path);
if (r != EACCES) return r;
/* If this fails with an access error, retrieve the target's mode. */
attr.a_mask = SFFS_ATTR_MODE;
r2 = sffs_table->t_getattr(path, &attr);
if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
/* If the target is not writable, temporarily set it to writable. */
attr.a_mode |= S_IWUSR;
r2 = sffs_table->t_setattr(path, &attr);
if (r2 != OK) return r;
/* Then try the original operation again. */
if (dir)
r = sffs_table->t_rmdir(path);
else
r = sffs_table->t_unlink(path);
if (r == OK) return r;
/* If the operation still fails, unset the writable bit again. */
attr.a_mode &= ~S_IWUSR;
sffs_table->t_setattr(path, &attr);
return r;
}
/*===========================================================================*
* do_unlink *
*===========================================================================*/
int do_unlink()
{
/* Delete a file.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
int r;
/* We cannot delete files on a read-only file system. */
if (state.s_read_only)
return EROFS;
/* Get the path string and possibly preexisting inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".") || !strcmp(name, "..")) return EPERM;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Perform the unlink call. */
r = force_remove(path, FALSE /*dir*/);
if (r != OK) {
if (ino != NULL)
put_inode(ino);
return r;
}
/* If a dentry existed for this name, it is gone now. */
if (ino != NULL) {
del_dentry(ino);
put_inode(ino);
}
return OK;
}
/*===========================================================================*
* do_rmdir *
*===========================================================================*/
int do_rmdir()
{
/* Remove an empty directory.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
int r;
/* We cannot remove directories on a read-only file system. */
if (state.s_read_only)
return EROFS;
/* Get the path string and possibly preexisting inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".")) return EINVAL;
if (!strcmp(name, "..")) return ENOTEMPTY;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Perform the rmdir call. */
r = force_remove(path, TRUE /*dir*/);
if (r != OK) {
if (ino != NULL)
put_inode(ino);
return r;
}
/* If a dentry existed for this name, it is gone now. */
if (ino != NULL) {
del_dentry(ino);
put_inode(ino);
}
return OK;
}
/*===========================================================================*
* do_rename *
*===========================================================================*/
int do_rename()
{
/* Rename a file or directory.
*/
char old_path[PATH_MAX], new_path[PATH_MAX];
char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
struct inode *old_parent, *new_parent;
struct inode *old_ino, *new_ino;
int r;
/* We cannot do rename on a read-only file system. */
if (state.s_read_only)
return EROFS;
/* Get path strings, names, directory inodes and possibly preexisting inodes
* for the old and new paths.
*/
if ((r = get_name(m_in.REQ_REN_GRANT_OLD, m_in.REQ_REN_LEN_OLD,
old_name)) != OK) return r;
if ((r = get_name(m_in.REQ_REN_GRANT_NEW, m_in.REQ_REN_LEN_NEW,
new_name)) != OK) return r;
if (!strcmp(old_name, ".") || !strcmp(old_name, "..") ||
!strcmp(new_name, ".") || !strcmp(new_name, "..")) return EINVAL;
if ((old_parent = find_inode(m_in.REQ_REN_OLD_DIR)) == NULL ||
(new_parent = find_inode(m_in.REQ_REN_NEW_DIR)) == NULL)
return EINVAL;
if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
return r;
if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
if (old_ino != NULL)
put_inode(old_ino);
return r;
}
/* Perform the actual rename call. */
r = sffs_table->t_rename(old_path, new_path);
/* If we failed, or if we have nothing further to do: both inodes are
* NULL, or they both refer to the same file.
*/
if (r != OK || old_ino == new_ino) {
if (old_ino != NULL) put_inode(old_ino);
if (new_ino != NULL) put_inode(new_ino);
return r;
}
/* If the new dentry already existed, it has now been overwritten.
* Delete the associated inode if we had found one.
*/
if (new_ino != NULL) {
del_dentry(new_ino);
put_inode(new_ino);
}
/* If the old dentry existed, rename it accordingly. */
if (old_ino != NULL) {
del_dentry(old_ino);
add_dentry(new_parent, new_name, old_ino);
put_inode(old_ino);
}
return OK;
}

332
lib/libsffs/lookup.c Normal file
View File

@@ -0,0 +1,332 @@
/* This file provides path-to-inode lookup functionality.
*
* The entry points into this file are:
* do_lookup perform the LOOKUP file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
static int get_mask(vfs_ucred_t *ucred);
static int access_as_dir(struct inode *ino, struct sffs_attr *attr, int
uid, int mask);
static int next_name(char **ptr, char **start, char name[NAME_MAX+1]);
static int go_up(char path[PATH_MAX], struct inode *ino, struct inode
**res_ino, struct sffs_attr *attr);
static int go_down(char path[PATH_MAX], struct inode *ino, char *name,
struct inode **res_ino, struct sffs_attr *attr);
/*===========================================================================*
* get_mask *
*===========================================================================*/
static int get_mask(ucred)
vfs_ucred_t *ucred; /* credentials of the caller */
{
/* Given the caller's credentials, precompute a search access mask to test
* against directory modes.
*/
int i;
if (ucred->vu_uid == sffs_params->p_uid) return S_IXUSR;
if (ucred->vu_gid == sffs_params->p_gid) return S_IXGRP;
for (i = 0; i < ucred->vu_ngroups; i++)
if (ucred->vu_sgroups[i] == sffs_params->p_gid) return S_IXGRP;
return S_IXOTH;
}
/*===========================================================================*
* access_as_dir *
*===========================================================================*/
static int access_as_dir(ino, attr, uid, mask)
struct inode *ino; /* the inode to test */
struct sffs_attr *attr; /* attributes of the inode */
int uid; /* UID of the caller */
int mask; /* search access mask of the caller */
{
/* Check whether the given inode may be accessed as directory.
* Return OK or an appropriate error code.
*/
mode_t mode;
assert(attr->a_mask & SFFS_ATTR_MODE);
/* The inode must be a directory to begin with. */
if (!IS_DIR(ino)) return ENOTDIR;
/* The caller must have search access to the directory. Root always does. */
if (uid == 0) return OK;
mode = get_mode(ino, attr->a_mode);
return (mode & mask) ? OK : EACCES;
}
/*===========================================================================*
* next_name *
*===========================================================================*/
static int next_name(ptr, start, name)
char **ptr; /* cursor pointer into path (in, out) */
char **start; /* place to store start of name */
char name[NAME_MAX+1]; /* place to store name */
{
/* 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 <= NAME_MAX; p++, i++)
name[i] = *p;
if (i > NAME_MAX)
return ENAMETOOLONG;
name[i] = 0;
} else {
strcpy(name, ".");
}
*ptr = p;
return OK;
}
/*===========================================================================*
* go_up *
*===========================================================================*/
static int go_up(path, ino, res_ino, attr)
char path[PATH_MAX]; /* path to take the last part from */
struct inode *ino; /* inode of the current directory */
struct inode **res_ino; /* place to store resulting inode */
struct sffs_attr *attr; /* place to store inode attributes */
{
/* Given an inode, progress into the parent directory.
*/
struct inode *parent;
int r;
pop_path(path);
parent = ino->i_parent;
assert(parent != NULL);
if ((r = verify_path(path, parent, attr, NULL)) != OK)
return r;
get_inode(parent);
*res_ino = parent;
return r;
}
/*===========================================================================*
* go_down *
*===========================================================================*/
static int go_down(path, parent, name, res_ino, attr)
char path[PATH_MAX]; /* path to add the name to */
struct inode *parent; /* inode of the current directory */
char *name; /* name of the directory entry */
struct inode **res_ino; /* place to store resulting inode */
struct sffs_attr *attr; /* place to store inode attributes */
{
/* Given a directory inode and a name, progress into a directory entry.
*/
struct inode *ino;
int r, stale = 0;
if ((r = push_path(path, name)) != OK)
return r;
dprintf(("%s: go_down: name '%s', path now '%s'\n", sffs_name, name, path));
ino = lookup_dentry(parent, name);
dprintf(("%s: lookup_dentry('%s') returned %p\n", sffs_name, name, ino));
if (ino != NULL)
r = verify_path(path, ino, attr, &stale);
else
r = sffs_table->t_getattr(path, attr);
dprintf(("%s: path query returned %d\n", sffs_name, r));
if (r != OK) {
if (ino != NULL) {
put_inode(ino);
ino = NULL;
}
if (!stale)
return r;
}
dprintf(("%s: name '%s'\n", sffs_name, name));
if (ino == NULL) {
if ((ino = get_free_inode()) == NULL)
return ENFILE;
dprintf(("%s: inode %p ref %d\n", sffs_name, ino, ino->i_ref));
ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode);
add_dentry(parent, name, ino);
}
*res_ino = ino;
return OK;
}
/*===========================================================================*
* do_lookup *
*===========================================================================*/
int do_lookup()
{
/* Resolve a path string to an inode.
*/
ino_t dir_ino_nr, root_ino_nr;
struct inode *cur_ino, *root_ino;
struct inode *next_ino = NULL;
struct sffs_attr attr;
char buf[PATH_MAX], path[PATH_MAX];
char name[NAME_MAX+1];
char *ptr, *last;
vfs_ucred_t ucred;
mode_t mask;
size_t len;
int r;
dir_ino_nr = m_in.REQ_DIR_INO;
root_ino_nr = m_in.REQ_ROOT_INO;
len = m_in.REQ_PATH_LEN;
/* Fetch the path name. */
if (len < 1 || len > PATH_MAX)
return EINVAL;
r = sys_safecopyfrom(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) buf, len, D);
if (r != OK)
return r;
if (buf[len-1] != 0) {
printf("%s: VFS did not zero-terminate path!\n", sffs_name);
return EINVAL;
}
/* Fetch the credentials, and generate a search access mask to test against
* directory modes.
*/
if (m_in.REQ_FLAGS & PATH_GET_UCRED) {
if (m_in.REQ_UCRED_SIZE != sizeof(ucred)) {
printf("%s: bad credential structure size\n", sffs_name);
return EINVAL;
}
r = sys_safecopyfrom(m_in.m_source, m_in.REQ_GRANT2, 0,
(vir_bytes) &ucred, m_in.REQ_UCRED_SIZE, D);
if (r != OK)
return r;
}
else {
ucred.vu_uid = m_in.REQ_UID;
ucred.vu_gid = m_in.REQ_GID;
ucred.vu_ngroups = 0;
}
mask = get_mask(&ucred);
/* Start the actual lookup. */
dprintf(("%s: lookup: got query '%s'\n", sffs_name, buf));
if ((cur_ino = find_inode(dir_ino_nr)) == NULL)
return EINVAL;
attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
if ((r = verify_inode(cur_ino, path, &attr)) != OK)
return r;
get_inode(cur_ino);
if (root_ino_nr > 0)
root_ino = find_inode(root_ino_nr);
else
root_ino = NULL;
/* One possible optimization would be to check a path only right before the
* first ".." in a row, and at the very end (if still necessary). This would
* have consequences for inode validation, though.
*/
for (ptr = last = buf; *ptr != 0; ) {
if ((r = access_as_dir(cur_ino, &attr, ucred.vu_uid, mask)) != OK)
break;
if ((r = next_name(&ptr, &last, name)) != OK)
break;
dprintf(("%s: lookup: next name '%s'\n", sffs_name, name));
if (!strcmp(name, ".") ||
(cur_ino == root_ino && !strcmp(name, "..")))
continue;
if (!strcmp(name, "..")) {
if (IS_ROOT(cur_ino))
r = ELEAVEMOUNT;
else
r = go_up(path, cur_ino, &next_ino, &attr);
} else {
r = go_down(path, cur_ino, name, &next_ino, &attr);
}
if (r != OK)
break;
assert(next_ino != NULL);
put_inode(cur_ino);
cur_ino = next_ino;
}
dprintf(("%s: lookup: result %d\n", sffs_name, r));
if (r != OK) {
put_inode(cur_ino);
/* We'd need support for these here. We don't have such support. */
assert(r != EENTERMOUNT && r != ESYMLINK);
if (r == ELEAVEMOUNT) {
m_out.RES_OFFSET = (int) (last - buf);
m_out.RES_SYMLOOP = 0;
}
return r;
}
m_out.RES_INODE_NR = INODE_NR(cur_ino);
m_out.RES_MODE = get_mode(cur_ino, attr.a_mode);
m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
m_out.RES_UID = sffs_params->p_uid;
m_out.RES_GID = sffs_params->p_gid;
m_out.RES_DEV = NO_DEV;
return OK;
}

154
lib/libsffs/main.c Normal file
View File

@@ -0,0 +1,154 @@
/* This file contains the SFFS initialization code and message loop.
*
* The entry points into this file are:
* sffs_init initialization
* sffs_signal signal handler
* sffs_loop main message loop
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* sffs_init *
*===========================================================================*/
int sffs_init(char *name, const struct sffs_table *table,
struct sffs_params *params)
{
/* Initialize this file server. Called at startup time.
*/
int i;
/* Make sure that the given path prefix doesn't end with a slash. */
i = strlen(params->p_prefix);
while (i > 0 && params->p_prefix[i - 1] == '/') i--;
params->p_prefix[i] = 0;
state.s_mounted = FALSE;
state.s_signaled = FALSE;
sffs_name = name;
sffs_table = table;
sffs_params = params;
return OK;
}
/*===========================================================================*
* sffs_signal *
*===========================================================================*/
void sffs_signal(int signo)
{
/* Only check for termination signal, ignore anything else. */
if (signo != SIGTERM) return;
/* We can now terminate if we have also been unmounted. */
state.s_signaled = TRUE;
if (state.s_mounted) {
dprintf(("%s: got SIGTERM, still mounted\n", sffs_name));
} else {
dprintf(("%s: got SIGTERM, shutting down\n", sffs_name));
/* Break out of the main loop, giving the main program the chance to
* perform further cleanup. This causes sef_receive() to return with
* an EINTR error code.
*/
sef_cancel();
}
}
/*===========================================================================*
* get_work *
*===========================================================================*/
static int get_work(who_e)
endpoint_t *who_e;
{
/* Receive a request message from VFS. Return TRUE if a new message is ready
* to be processed, or FALSE if sef_stop() was called from the signal handler.
*/
int r;
if ((r = sef_receive(ANY, &m_in)) != OK) {
if (r != EINTR)
panic("receive failed: %d", r);
return FALSE;
}
*who_e = m_in.m_source;
return TRUE;
}
/*===========================================================================*
* send_reply *
*===========================================================================*/
static void send_reply(err, transid)
int err; /* resulting error code */
int transid;
{
/* Send a reply message to the requesting party, with the given error code.
*/
int r;
m_out.m_type = err;
if (IS_VFS_FS_TRANSID(transid)) {
/* If a transaction ID was set, reset it */
m_out.m_type = TRNS_ADD_ID(m_out.m_type, transid);
}
if ((r = send(m_in.m_source, &m_out)) != OK)
printf("%s: send failed (%d)\n", sffs_name, r);
}
/*===========================================================================*
* sffs_loop *
*===========================================================================*/
void sffs_loop(void)
{
/* The main function of this file server. After initializing, loop, receiving
* one request from VFS at a time, processing it, and sending a reply back to
* VFS. Termination occurs when we both have been unmounted and have received
* a termination signal.
*/
endpoint_t who_e;
int call_nr, err, transid;
while (state.s_mounted || !state.s_signaled) {
if (!get_work(&who_e))
continue; /* Recheck running conditions */
transid = TRNS_GET_ID(m_in.m_type);
m_in.m_type = TRNS_DEL_ID(m_in.m_type);
if (m_in.m_type == 0) {
assert(!IS_VFS_FS_TRANSID(transid));
m_in.m_type = transid; /* Backwards compat. */
transid = 0;
} else
assert(IS_VFS_FS_TRANSID(transid));
call_nr = m_in.m_type;
if (who_e != VFS_PROC_NR) {
continue;
}
if (state.s_mounted || call_nr == REQ_READSUPER) {
call_nr -= VFS_BASE;
dprintf(("%s: call %d\n", sffs_name, call_nr));
if (call_nr >= 0 && call_nr < NREQS) {
err = (*call_vec[call_nr])();
} else {
err = ENOSYS;
}
dprintf(("%s: call %d result %d\n", sffs_name, call_nr, err));
}
else err = EINVAL;
send_reply(err, transid);
}
}

78
lib/libsffs/misc.c Normal file
View File

@@ -0,0 +1,78 @@
/* This file contains miscellaneous file system call handlers.
*
* The entry points into this file are:
* do_fstatfs perform the FSTATFS file system call
* do_statvfs perform the STATVFS file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <sys/statfs.h>
#include <sys/statvfs.h>
/*===========================================================================*
* do_fstatfs *
*===========================================================================*/
int do_fstatfs()
{
/* Retrieve file system statistics.
*/
struct statfs statfs;
statfs.f_bsize = BLOCK_SIZE; /* arbitrary block size constant */
return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) &statfs, sizeof(statfs), D);
}
/*===========================================================================*
* do_statvfs *
*===========================================================================*/
int do_statvfs()
{
/* Retrieve file system statistics.
*/
struct statvfs statvfs;
struct inode *ino;
char path[PATH_MAX];
u64_t free, total;
int r;
/* Unfortunately, we cannot be any more specific than this, because we are
* not given an inode number. Statistics of individual shared folders can
* only be obtained by making sure that the root of the file system is an
* actual share, and not a list of available shares.
*/
if ((ino = find_inode(ROOT_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
if ((r = sffs_table->t_queryvol(path, &free, &total)) != OK)
return r;
memset(&statvfs, 0, sizeof(statvfs));
/* Returning zero for unknown values seems to be the convention. However, we
* do have to use a nonzero block size, even though it is entirely arbitrary.
*/
statvfs.f_bsize = BLOCK_SIZE;
statvfs.f_frsize = BLOCK_SIZE;
statvfs.f_blocks = div64u(total, BLOCK_SIZE);
statvfs.f_bfree = div64u(free, BLOCK_SIZE);
statvfs.f_bavail = statvfs.f_bfree;
statvfs.f_files = 0;
statvfs.f_ffree = 0;
statvfs.f_favail = 0;
statvfs.f_fsid = state.s_dev;
statvfs.f_flag = state.s_read_only ? ST_RDONLY : 0;
statvfs.f_flag |= ST_NOTRUNC;
statvfs.f_namemax = NAME_MAX;
return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) &statvfs, sizeof(statvfs), D);
}

96
lib/libsffs/mount.c Normal file
View File

@@ -0,0 +1,96 @@
/* This file contains mount and unmount functionality.
*
* The entry points into this file are:
* do_readsuper perform the READSUPER file system call
* do_unmount perform the UNMOUNT file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* do_readsuper *
*===========================================================================*/
int do_readsuper()
{
/* Mount the file system.
*/
char path[PATH_MAX];
struct inode *ino;
struct sffs_attr attr;
int r;
dprintf(("%s: readsuper (dev %x, flags %x)\n",
sffs_name, (dev_t) m_in.REQ_DEV, m_in.REQ_FLAGS));
if (m_in.REQ_FLAGS & REQ_ISROOT) {
printf("%s: attempt to mount as root device\n", sffs_name);
return EINVAL;
}
state.s_read_only = !!(m_in.REQ_FLAGS & REQ_RDONLY);
state.s_dev = m_in.REQ_DEV;
init_dentry();
ino = init_inode();
attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
/* We cannot continue if we fail to get the properties of the root inode at
* all, because we cannot guess the details of the root node to return to
* VFS. Print a (hopefully) helpful error message, and abort the mount.
*/
if ((r = verify_inode(ino, path, &attr)) != OK) {
if (r == EAGAIN)
printf("%s: shared folders disabled\n", sffs_name);
else if (sffs_params->p_prefix[0] && (r == ENOENT || r == EACCES))
printf("%s: unable to access the given prefix directory\n",
sffs_name);
else
printf("%s: unable to access shared folders\n", sffs_name);
return r;
}
m_out.RES_INODE_NR = INODE_NR(ino);
m_out.RES_MODE = get_mode(ino, attr.a_mode);
m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
m_out.RES_UID = sffs_params->p_uid;
m_out.RES_GID = sffs_params->p_gid;
m_out.RES_DEV = NO_DEV;
m_out.RES_CONREQS = 1; /* We can handle only 1 request at a time */
state.s_mounted = TRUE;
return OK;
}
/*===========================================================================*
* do_unmount *
*===========================================================================*/
int do_unmount()
{
/* Unmount the file system.
*/
struct inode *ino;
dprintf(("%s: do_unmount\n", sffs_name));
/* Decrease the reference count of the root inode. */
if ((ino = find_inode(ROOT_INODE_NR)) == NULL)
return EINVAL;
put_inode(ino);
/* There should not be any referenced inodes anymore now. */
if (have_used_inode())
printf("%s: in-use inodes left at unmount time!\n", sffs_name);
state.s_mounted = FALSE;
return OK;
}

56
lib/libsffs/name.c Normal file
View File

@@ -0,0 +1,56 @@
/* This file contains path component name utility functions.
*
* The entry points into this file are:
* normalize_name normalize a path component name for hashing purposes
* compare_name check whether two path component names are equivalent
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <ctype.h>
/*===========================================================================*
* normalize_name *
*===========================================================================*/
void normalize_name(dst, src)
char dst[NAME_MAX+1];
char *src;
{
/* Normalize the given path component name, storing the result in the given
* buffer.
*/
size_t i, size;
size = strlen(src) + 1;
assert(size <= NAME_MAX+1);
if (sffs_params->p_case_insens) {
for (i = 0; i < size; i++)
*dst++ = tolower(*src++);
}
else memcpy(dst, src, size);
}
/*===========================================================================*
* compare_name *
*===========================================================================*/
int compare_name(name1, name2)
char *name1;
char *name2;
{
/* Return TRUE if the given path component names are equivalent, FALSE
* otherwise.
*/
int r;
if (sffs_params->p_case_insens)
r = strcasecmp(name1, name2);
else
r = strcmp(name1, name2);
return r ? FALSE : TRUE;
}

113
lib/libsffs/path.c Normal file
View File

@@ -0,0 +1,113 @@
/* This file contains routines for creating and manipulating path strings.
*
* The entry points into this file are:
* make_path construct a path string for an inode
* push_path add a path component to the end of a path string
* pop_path remove the last path component from a path string
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* make_path *
*===========================================================================*/
int make_path(path, ino)
char path[PATH_MAX];
struct inode *ino;
{
/* Given an inode, construct the path identifying that inode.
*/
char buf[PATH_MAX], *p, *prefix;
size_t len, plen, total;
p = &buf[sizeof(buf) - 1];
p[0] = 0;
dprintf(("%s: make_path: constructing path for inode %d\n",
sffs_name, ino->i_num));
/* Get the length of the prefix, skipping any leading slashes. */
for (prefix = sffs_params->p_prefix; prefix[0] == '/'; prefix++);
plen = strlen(prefix);
/* Construct the path right-to-left in a temporary buffer first. */
for (total = plen; ino != NULL && !IS_ROOT(ino); ino = ino->i_parent) {
len = strlen(ino->i_name);
total += len + 1;
p -= len + 1;
if (total >= sizeof(buf))
return ENAMETOOLONG;
p[0] = '/';
memcpy(p + 1, ino->i_name, len);
}
/* If any of the intermediate inodes has no parent, the final inode is no
* longer addressable by name.
*/
if (ino == NULL)
return ENOENT;
/* Put the result in the actual buffer. We need the leading slash in the
* temporary buffer only when the prefix is not empty.
*/
if (!prefix[0] && p[0] == '/') p++;
strcpy(path, prefix);
strcpy(&path[plen], p);
dprintf(("%s: make_path: resulting path is '%s'\n", sffs_name, path));
return OK;
}
/*===========================================================================*
* push_path *
*===========================================================================*/
int push_path(path, name)
char path[PATH_MAX];
char *name;
{
/* Add a component to the end of a path.
*/
size_t len, add;
len = strlen(path);
add = strlen(name);
if (len > 0) add++;
if (len + add >= PATH_MAX)
return ENAMETOOLONG;
if (len > 0) path[len++] = '/';
strcpy(&path[len], name);
return OK;
}
/*===========================================================================*
* pop_path *
*===========================================================================*/
void pop_path(path)
char path[PATH_MAX];
{
/* Remove the last component from a path.
*/
char *p;
p = strrchr(path, '/');
if (p == NULL) {
p = path;
/* Can't pop the root component */
assert(p[0] != 0);
}
p[0] = 0;
}

83
lib/libsffs/proto.h Normal file
View File

@@ -0,0 +1,83 @@
#ifndef _SFFS_PROTO_H
#define _SFFS_PROTO_H
/* dentry.c */
void init_dentry(void);
struct inode *lookup_dentry(struct inode *parent, char *name);
void add_dentry(struct inode *parent, char *name, struct inode *ino);
void del_dentry(struct inode *ino);
/* handle.c */
int get_handle(struct inode *ino);
void put_handle(struct inode *ino);
/* inode.c */
struct inode *init_inode(void);
struct inode *find_inode(ino_t ino_nr);
void get_inode(struct inode *ino);
void put_inode(struct inode *ino);
void link_inode(struct inode *parent, struct inode *ino);
void unlink_inode(struct inode *ino);
struct inode *get_free_inode(void);
int have_free_inode(void);
int have_used_inode(void);
int do_putnode(void);
/* link.c */
int do_create(void);
int do_mkdir(void);
int do_unlink(void);
int do_rmdir(void);
int do_rename(void);
/* lookup.c */
int do_lookup(void);
/* main.c */
int main(int argc, char *argv[]);
/* misc.c */
int do_fstatfs(void);
int do_statvfs(void);
/* mount.c */
int do_readsuper(void);
int do_unmount(void);
/* name.c */
void normalize_name(char dst[NAME_MAX+1], char *src);
int compare_name(char *name1, char *name2);
/* path.c */
int make_path(char path[PATH_MAX], struct inode *ino);
int push_path(char path[PATH_MAX], char *name);
void pop_path(char path[PATH_MAX]);
/* read.c */
int do_read(void);
int do_getdents(void);
/* stat.c */
mode_t get_mode(struct inode *ino, int mode);
int do_stat(void);
int do_chmod(void);
int do_utime(void);
/* util.c */
int get_name(cp_grant_id_t grant, size_t len, char name[NAME_MAX+1]);
int do_noop(void);
int no_sys(void);
/* verify.c */
int verify_path(char *path, struct inode *ino, struct sffs_attr *attr,
int *stale);
int verify_inode(struct inode *ino, char path[PATH_MAX],
struct sffs_attr *attr);
int verify_dentry(struct inode *parent, char name[NAME_MAX+1],
char path[PATH_MAX], struct inode **res_ino);
/* write.c */
int do_write(void);
int do_ftrunc(void);
#endif /* _SFFS_PROTO_H */

234
lib/libsffs/read.c Normal file
View File

@@ -0,0 +1,234 @@
/* This file contains file and directory reading file system call handlers.
*
* The entry points into this file are:
* do_read perform the READ file system call
* do_getdents perform the GETDENTS file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <dirent.h>
#define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
/*===========================================================================*
* do_read *
*===========================================================================*/
int do_read()
{
/* Read data from a file.
*/
struct inode *ino;
u64_t pos;
size_t count, size;
vir_bytes off;
char *ptr;
int r, chunk;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
if ((r = get_handle(ino)) != OK)
return r;
pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
count = m_in.REQ_NBYTES;
assert(count > 0);
/* Use the buffer from below to eliminate extra copying. */
size = sffs_table->t_readbuf(&ptr);
off = 0;
while (count > 0) {
chunk = MIN(count, size);
if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
break;
chunk = r;
r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, off,
(vir_bytes) ptr, chunk, D);
if (r != OK)
break;
count -= chunk;
off += chunk;
pos = add64u(pos, chunk);
}
if (r < 0)
return r;
m_out.RES_SEEK_POS_HI = ex64hi(pos);
m_out.RES_SEEK_POS_LO = ex64lo(pos);
m_out.RES_NBYTES = off;
return OK;
}
/*===========================================================================*
* do_getdents *
*===========================================================================*/
int do_getdents()
{
/* Retrieve directory entries.
*/
char name[NAME_MAX+1];
struct inode *ino, *child;
struct dirent *dent;
struct sffs_attr attr;
size_t len, off, user_off, user_left;
off_t pos;
int r;
/* must be at least sizeof(struct dirent) + NAME_MAX */
static char buf[BLOCK_SIZE];
attr.a_mask = SFFS_ATTR_MODE;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if (m_in.REQ_SEEK_POS_HI != 0) return EINVAL;
if (!IS_DIR(ino)) return ENOTDIR;
/* We are going to need at least one free inode to store children in. */
if (!have_free_inode()) return ENFILE;
/* If we don't have a directory handle yet, get one now. */
if ((r = get_handle(ino)) != OK)
return r;
off = 0;
user_off = 0;
user_left = m_in.REQ_MEM_SIZE;
/* We use the seek position as file index number. The first position is for
* the "." entry, the second position is for the ".." entry, and the next
* position numbers each represent a file in the directory.
*/
for (pos = m_in.REQ_SEEK_POS_LO; ; pos++) {
/* Determine which inode and name to use for this entry.
* We have no idea whether the host will give us "." and/or "..",
* so generate our own and skip those from the host.
*/
if (pos == 0) {
/* Entry for ".". */
child = ino;
strcpy(name, ".");
get_inode(child);
}
else if (pos == 1) {
/* Entry for "..", but only when there is a parent. */
if (ino->i_parent == NULL)
continue;
child = ino->i_parent;
strcpy(name, "..");
get_inode(child);
}
else {
/* Any other entry, not being "." or "..". */
r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
sizeof(name), &attr);
if (r != OK) {
/* No more entries? Then close the handle and stop. */
if (r == ENOENT) {
put_handle(ino);
break;
}
/* FIXME: what if the error is ENAMETOOLONG? */
return r;
}
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if ((child = lookup_dentry(ino, name)) == NULL) {
child = get_free_inode();
/* We were promised a free inode! */
assert(child != NULL);
child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
add_dentry(ino, name, child);
}
}
len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name));
/* Is the user buffer too small to store another record?
* Note that we will be rerequesting the same dentry upon a subsequent
* getdents call this way, but we really need the name length for this.
*/
if (user_off + off + len > user_left) {
put_inode(child);
/* 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(m_in.m_source, m_in.REQ_GRANT,
user_off, (vir_bytes) buf, off, D);
if (r != OK) {
put_inode(child);
return r;
}
user_off += off;
user_left -= off;
off = 0;
}
/* Fill in the actual directory entry. */
dent = (struct dirent *) &buf[off];
dent->d_ino = INODE_NR(child);
dent->d_off = pos;
dent->d_reclen = len;
strcpy(dent->d_name, name);
off += len;
put_inode(child);
}
/* If there is anything left in our own buffer, copy that out now. */
if (off > 0) {
r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, user_off,
(vir_bytes) buf, off, D);
if (r != OK)
return r;
user_off += off;
}
m_out.RES_SEEK_POS_HI = 0;
m_out.RES_SEEK_POS_LO = pos;
m_out.RES_NBYTES = user_off;
return OK;
}

168
lib/libsffs/stat.c Normal file
View File

@@ -0,0 +1,168 @@
/* This file contains file metadata retrieval and manipulation routines.
*
* The entry points into this file are:
* get_mode return a file's mode
* do_stat perform the STAT file system call
* do_chmod perform the CHMOD file system call
* do_utime perform the UTIME file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* get_mode *
*===========================================================================*/
mode_t get_mode(ino, mode)
struct inode *ino;
int mode;
{
/* Return the mode for an inode, given the inode and the retrieved mode.
*/
mode &= S_IRWXU;
mode = mode | (mode >> 3) | (mode >> 6);
if (IS_DIR(ino))
mode = S_IFDIR | (mode & sffs_params->p_dir_mask);
else
mode = S_IFREG | (mode & sffs_params->p_file_mask);
if (state.s_read_only)
mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
return mode;
}
/*===========================================================================*
* do_stat *
*===========================================================================*/
int do_stat()
{
/* Retrieve inode status.
*/
struct inode *ino;
struct sffs_attr attr;
struct stat stat;
char path[PATH_MAX];
ino_t ino_nr;
int r;
ino_nr = m_in.REQ_INODE_NR;
/* Don't increase the inode refcount: it's already open anyway */
if ((ino = find_inode(ino_nr)) == NULL)
return EINVAL;
attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE | SFFS_ATTR_CRTIME |
SFFS_ATTR_ATIME | SFFS_ATTR_MTIME | SFFS_ATTR_CTIME;
if ((r = verify_inode(ino, path, &attr)) != OK)
return r;
memset(&stat, 0, sizeof(struct stat));
stat.st_dev = state.s_dev;
stat.st_ino = ino_nr;
stat.st_mode = get_mode(ino, attr.a_mode);
stat.st_uid = sffs_params->p_uid;
stat.st_gid = sffs_params->p_gid;
stat.st_rdev = NO_DEV;
if (cmp64u(attr.a_size, LONG_MAX) > 0)
stat.st_size = LONG_MAX;
else
stat.st_size = ex64lo(attr.a_size);
stat.st_atimespec = attr.a_atime;
stat.st_mtimespec = attr.a_mtime;
stat.st_ctimespec = attr.a_ctime;
stat.st_birthtimespec = attr.a_crtime;
stat.st_blocks = stat.st_size / S_BLKSIZE;
if (stat.st_size % S_BLKSIZE != 0)
stat.st_blocks += 1;
stat.st_blksize = BLOCK_SIZE;
/* We could make this more accurate by iterating over directory inodes'
* children, counting how many of those are directories as well.
* It's just not worth it.
*/
stat.st_nlink = 0;
if (ino->i_parent != NULL) stat.st_nlink++;
if (IS_DIR(ino)) {
stat.st_nlink++;
if (HAS_CHILDREN(ino)) stat.st_nlink++;
}
return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) &stat, sizeof(stat), D);
}
/*===========================================================================*
* do_chmod *
*===========================================================================*/
int do_chmod()
{
/* Change file mode.
*/
struct inode *ino;
char path[PATH_MAX];
struct sffs_attr attr;
int r;
if (state.s_read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
/* Set the new file mode. */
attr.a_mask = SFFS_ATTR_MODE;
attr.a_mode = m_in.REQ_MODE; /* no need to convert in this direction */
if ((r = sffs_table->t_setattr(path, &attr)) != OK)
return r;
/* We have no idea what really happened. Query for the mode again. */
if ((r = verify_path(path, ino, &attr, NULL)) != OK)
return r;
m_out.RES_MODE = get_mode(ino, attr.a_mode);
return OK;
}
/*===========================================================================*
* do_utime *
*===========================================================================*/
int do_utime()
{
/* Set file times.
*/
struct inode *ino;
char path[PATH_MAX];
struct sffs_attr attr;
int r;
if (state.s_read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
attr.a_mask = SFFS_ATTR_ATIME | SFFS_ATTR_MTIME;
attr.a_atime.tv_sec = m_in.REQ_ACTIME;
attr.a_atime.tv_nsec = 0;
attr.a_mtime.tv_sec = m_in.REQ_MODTIME;
attr.a_mtime.tv_nsec = 0;
return sffs_table->t_setattr(path, &attr);
}

47
lib/libsffs/table.c Normal file
View File

@@ -0,0 +1,47 @@
/* This file contains the file system call table.
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#define _TABLE
#include "inc.h"
int (*call_vec[])(void) = {
no_sys, /* 0 */
no_sys, /* 1 getnode */
do_putnode, /* 2 putnode */
no_sys, /* 3 slink */
do_ftrunc, /* 4 ftrunc */
no_sys, /* 5 chown */
do_chmod, /* 6 chmod */
do_noop, /* 7 inhibread */
do_stat, /* 8 stat */
do_utime, /* 9 utime */
do_fstatfs, /* 10 fstatfs */
no_sys, /* 11 bread */
no_sys, /* 12 bwrite */
do_unlink, /* 13 unlink */
do_rmdir, /* 14 rmdir */
do_unmount, /* 15 unmount */
do_noop, /* 16 sync */
do_noop, /* 17 new_driver */
do_noop, /* 18 flush */
do_read, /* 19 read */
do_write, /* 20 write */
no_sys, /* 21 mknod */
do_mkdir, /* 22 mkdir */
do_create, /* 23 create */
no_sys, /* 24 link */
do_rename, /* 25 rename */
do_lookup, /* 26 lookup */
no_sys, /* 27 mountpoint */
do_readsuper, /* 28 readsuper */
no_sys, /* 29 newnode */
no_sys, /* 30 rdlink */
do_getdents, /* 31 getdents */
do_statvfs, /* 32 statvfs */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NREQS * sizeof(call_vec[0]) ? 1 : -1];

13
lib/libsffs/type.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef _SFFS_TYPE_H
#define _SFFS_TYPE_H
/* Structure with global file system state. */
struct state {
int s_mounted; /* is the file system mounted? */
int s_signaled; /* have we received a SIGTERM? */
int s_read_only; /* is the file system mounted read-only? note,
* has no relation to the shared folder mode */
dev_t s_dev; /* device the file system is mounted on */
};
#endif /* _SFFS_TYPE_H */

63
lib/libsffs/util.c Normal file
View File

@@ -0,0 +1,63 @@
/* This file contains various utility functions.
*
* The entry points into this file are:
* get_name retrieve a path component string from VFS
* do_noop handle file system calls that do nothing and succeed
* no_sys handle file system calls that are not implemented
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* get_name *
*===========================================================================*/
int get_name(grant, len, name)
cp_grant_id_t grant; /* memory grant for the path component */
size_t len; /* length of the name, including '\0' */
char name[NAME_MAX+1]; /* buffer in which store the result */
{
/* Retrieve a path component from the caller, using a given grant.
*/
int r;
/* Copy in the name of the directory entry. */
if (len <= 1) return EINVAL;
if (len > NAME_MAX+1) return ENAMETOOLONG;
r = sys_safecopyfrom(m_in.m_source, grant, 0, (vir_bytes) name, len, D);
if (r != OK) return r;
if (name[len-1] != 0) {
printf("%s: VFS did not zero-terminate path component!\n", sffs_name);
return EINVAL;
}
return OK;
}
/*===========================================================================*
* do_noop *
*===========================================================================*/
int do_noop()
{
/* Generic handler for no-op system calls.
*/
return OK;
}
/*===========================================================================*
* no_sys *
*===========================================================================*/
int no_sys()
{
/* Generic handler for unimplemented system calls.
*/
return ENOSYS;
}

119
lib/libsffs/verify.c Normal file
View File

@@ -0,0 +1,119 @@
/* This file contains routines that verify inodes and paths against the host.
*
* The entry points into this file are:
* verify_path check whether a path,inode pair is still valid
* verify_inode construct a path for an inode and verify the inode
* verify_dentry check a directory inode and look for a directory entry
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* verify_path *
*===========================================================================*/
int verify_path(path, ino, attr, stale)
char path[PATH_MAX];
struct inode *ino;
struct sffs_attr *attr;
int *stale;
{
/* Given a path, and the inode associated with that path, verify if the inode
* still matches the real world. Obtain the attributes of the file identified
* by the given path, and see if they match. If not, possibly mark the inode
* as deleted and return an error. Only upon success is the inode guaranteed
* to be usable.
*
* The caller must set the a_mask field of the passed attr struct.
* If 'stale' is not NULL, the value it points to must be initialized to 0,
* and will be set to 1 if the path was valid but the inode wasn't.
*/
int r;
attr->a_mask |= SFFS_ATTR_MODE;
r = sffs_table->t_getattr(path, attr);
dprintf(("%s: verify_path: getattr('%s') returned %d\n",
sffs_name, path, r));
if (r != OK) {
/* If we are told that the path does not exist, delete the inode */
if (r == ENOENT || r == ENOTDIR)
del_dentry(ino);
return r; /* path isn't valid */
}
/* If the file type (reg, dir) isn't what we thought, delete the inode */
if ((ino->i_flags & I_DIR) != MODE_TO_DIRFLAG(attr->a_mode)) {
del_dentry(ino);
if (stale != NULL) *stale = 1;
return ENOENT; /* path is valid, inode wasn't */
}
return OK; /* path and inode are valid */
}
/*===========================================================================*
* verify_inode *
*===========================================================================*/
int verify_inode(ino, path, attr)
struct inode *ino; /* inode to verify */
char path[PATH_MAX]; /* buffer in which to store the path */
struct sffs_attr *attr; /* buffer for attributes, or NULL */
{
/* Given an inode, construct a path identifying the inode, and check whether
* that path is still valid for that inode (as far as we can tell). As a side
* effect, store attributes in the given attribute structure if not NULL (its
* a_mask member must then be set).
*/
struct sffs_attr attr2;
int r;
if ((r = make_path(path, ino)) != OK) return r;
if (attr == NULL) {
attr2.a_mask = 0;
attr = &attr2;
}
return verify_path(path, ino, attr, NULL);
}
/*===========================================================================*
* verify_dentry *
*===========================================================================*/
int verify_dentry(parent, name, path, res_ino)
struct inode *parent; /* parent inode: the inode to verify */
char name[NAME_MAX+1]; /* the given directory entry path component */
char path[PATH_MAX]; /* buffer to store the resulting path in */
struct inode **res_ino; /* pointer for addressed inode (or NULL) */
{
/* Given a directory inode and a name, construct a path identifying that
* directory entry, check whether the path to the parent is still valid, and
* check whether there is an inode pointed to by the full path. Upon success,
* res_ino will contain either the inode for the full path, with increased
* refcount, or NULL if no such inode exists.
*/
int r;
if ((r = verify_inode(parent, path, NULL)) != OK)
return r;
dprintf(("%s: verify_dentry: given path is '%s', name '%s'\n",
sffs_name, path, name));
if ((r = push_path(path, name)) != OK)
return r;
dprintf(("%s: verify_dentry: path now '%s'\n", sffs_name, path));
*res_ino = lookup_dentry(parent, name);
return OK;
}

164
lib/libsffs/write.c Normal file
View File

@@ -0,0 +1,164 @@
/* This file contains file writing system call handlers.
*
* The entry points into this file are:
* do_write perform the WRITE file system call
* do_ftrunc perform the FTRUNC file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
static int write_file(struct inode *ino, u64_t *posp, size_t *countp,
cp_grant_id_t *grantp);
/*===========================================================================*
* write_file *
*===========================================================================*/
static int write_file(ino, posp, countp, grantp)
struct inode *ino;
u64_t *posp;
size_t *countp;
cp_grant_id_t *grantp;
{
/* Write data or zeroes to a file, depending on whether a valid pointer to
* a data grant was provided.
*/
u64_t pos;
size_t count, size;
vir_bytes off;
char *ptr;
int r, chunk;
assert(!IS_DIR(ino));
if ((r = get_handle(ino)) != OK)
return r;
pos = *posp;
count = *countp;
assert(count > 0);
/* Use the buffer from below to eliminate extra copying. */
size = sffs_table->t_writebuf(&ptr);
off = 0;
while (count > 0) {
chunk = MIN(count, size);
if (grantp != NULL) {
r = sys_safecopyfrom(m_in.m_source, *grantp,
off, (vir_bytes) ptr, chunk, D);
if (r != OK)
break;
} else {
/* Do this every time. We don't know what happens below. */
memset(ptr, 0, chunk);
}
if ((r = sffs_table->t_write(ino->i_file, ptr, chunk, pos)) <= 0)
break;
count -= r;
off += r;
pos = add64u(pos, r);
}
if (r < 0)
return r;
*posp = pos;
*countp = off;
return OK;
}
/*===========================================================================*
* do_write *
*===========================================================================*/
int do_write()
{
/* Write data to a file.
*/
struct inode *ino;
u64_t pos;
size_t count;
cp_grant_id_t grant;
int r;
if (state.s_read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
count = m_in.REQ_NBYTES;
grant = m_in.REQ_GRANT;
if (count == 0) return EINVAL;
if ((r = write_file(ino, &pos, &count, &grant)) != OK)
return r;
m_out.RES_SEEK_POS_HI = ex64hi(pos);
m_out.RES_SEEK_POS_LO = ex64lo(pos);
m_out.RES_NBYTES = count;
return OK;
}
/*===========================================================================*
* do_ftrunc *
*===========================================================================*/
int do_ftrunc()
{
/* Change file size or create file holes.
*/
char path[PATH_MAX];
struct inode *ino;
struct sffs_attr attr;
u64_t start, end, delta;
size_t count;
int r;
if (state.s_read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
start = make64(m_in.REQ_TRC_START_LO, m_in.REQ_TRC_START_HI);
end = make64(m_in.REQ_TRC_END_LO, m_in.REQ_TRC_END_HI);
if (cmp64u(end, 0) == 0) {
/* Truncate or expand the file. */
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
attr.a_mask = SFFS_ATTR_SIZE;
attr.a_size = start;
r = sffs_table->t_setattr(path, &attr);
} else {
/* Write zeroes to the file. We can't create holes. */
if (cmp64(end, start) <= 0) return EINVAL;
delta = sub64(end, start);
if (ex64hi(delta) != 0) return EINVAL;
count = ex64lo(delta);
r = write_file(ino, &start, &count, NULL);
}
return r;
}