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:
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
9
lib/libsffs/Makefile
Normal 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
16
lib/libsffs/const.h
Normal 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
193
lib/libsffs/dentry.c
Normal 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
19
lib/libsffs/glo.h
Normal 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
79
lib/libsffs/handle.c
Normal 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
24
lib/libsffs/inc.h
Normal 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
301
lib/libsffs/inode.c
Normal 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
88
lib/libsffs/inode.h
Normal 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
404
lib/libsffs/link.c
Normal 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
332
lib/libsffs/lookup.c
Normal 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
154
lib/libsffs/main.c
Normal 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
78
lib/libsffs/misc.c
Normal 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
96
lib/libsffs/mount.c
Normal 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
56
lib/libsffs/name.c
Normal 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
113
lib/libsffs/path.c
Normal 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
83
lib/libsffs/proto.h
Normal 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
234
lib/libsffs/read.c
Normal 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
168
lib/libsffs/stat.c
Normal 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
47
lib/libsffs/table.c
Normal 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
13
lib/libsffs/type.h
Normal 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
63
lib/libsffs/util.c
Normal 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
119
lib/libsffs/verify.c
Normal 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
164
lib/libsffs/write.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user