libvtreefs: API changes/extensions, part 1

- move primary I/O buffer into vtreefs; change read hook API;
- add hooks for write, truncate, symlink, mknod, unlink, chmod/chown;
- modernize message_hook;
- change procfs, devman, gpio accordingly;

Change-Id: I9f0669e41195efa3253032e95d93f0a78e9d68d6
This commit is contained in:
David van Moolenbroek 2014-11-06 15:04:45 +00:00
parent 693ad767e8
commit 5eefd0fec2
21 changed files with 655 additions and 358 deletions

View file

@ -71,6 +71,9 @@ static struct inode_stat default_file_stat = {
.dev = NO_DEV,
};
/* Buffer size for read requests */
#define DATA_SIZE 26
int
add_gpio_inode(char *name, int nr, int mode)
{
@ -206,14 +209,12 @@ init_hook(void)
}
}
static int
static ssize_t
read_hook
(struct inode *inode, off_t offset, char **ptr, size_t * len,
cbdata_t cbdata)
(struct inode *inode, char *ptr, size_t len, off_t offset, cbdata_t cbdata)
{
/* This hook will be called every time a regular file is read. We use
* it to dyanmically generate the contents of our file. */
static char data[26];
int value;
struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
assert(gpio_cbdata->gpio != NULL);
@ -222,53 +223,42 @@ static int
|| gpio_cbdata->type == GPIO_CB_OFF) {
/* turn on or off */
if (gpio_set(gpio_cbdata->gpio,
(gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0)) {
*len = 0;
(gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0))
return EIO;
}
*len = 0;
return OK;
return 0;
}
if (gpio_cbdata->type == GPIO_CB_INTR_READ) {
/* reading interrupt */
if (gpio_intr_read(gpio_cbdata->gpio, &value)) {
*len = 0;
if (gpio_intr_read(gpio_cbdata->gpio, &value))
return EIO;
}
} else {
/* reading */
if (gpio_read(gpio_cbdata->gpio, &value)) {
*len = 0;
if (gpio_read(gpio_cbdata->gpio, &value))
return EIO;
}
}
snprintf(data, 26, "%d\n", value);
snprintf(ptr, DATA_SIZE, "%d\n", value);
len = strlen(ptr);
/* If the offset is beyond the end of the string, return EOF. */
if (offset > strlen(data)) {
*len = 0;
/* If the offset is at or beyond the end of the string, return EOF. */
if (offset >= len)
return 0;
return OK;
/* Otherwise, we may have to move the data to the start of ptr. */
if (offset > 0) {
len -= offset;
memmove(ptr, &ptr[offset], len);
}
/* Otherwise, return a pointer into 'data'. If necessary, bound the
* returned length to the length of the rest of the string. Note that
* 'data' has to be static, because it will be used after this
* function returns. */
*ptr = data + offset;
if (*len > strlen(data) - offset)
*len = strlen(data) - offset;
return OK;
/* Return the resulting length. */
return len;
}
static int
message_hook(message * m)
static void
message_hook(message * m, int __unused ipc_status)
{
gpio_intr_message(m);
return OK;
}
int
@ -294,7 +284,7 @@ main(int argc, char **argv)
root_stat.dev = NO_DEV;
/* limit the number of indexed entries */
start_vtreefs(&hooks, 30, &root_stat, 0);
start_vtreefs(&hooks, 30, &root_stat, 0, DATA_SIZE);
return EXIT_SUCCESS;
}

View file

@ -3,25 +3,25 @@
#include "inc.h"
#include <stdarg.h>
#define BUF_SIZE 4096
static char buf[BUF_SIZE + 1];
static size_t off, left, used;
static char *buf;
static size_t left, used;
static off_t skip;
/*===========================================================================*
* buf_init *
*===========================================================================*/
void buf_init(off_t start, size_t len)
void buf_init(char *ptr, size_t len, off_t start)
{
/* Initialize the buffer for fresh use. The first 'start' bytes of the
* produced output are to be skipped. After that, up to a total of
* 'len' bytes are requested.
/* Initialize the buffer for fresh use. The output is to be stored into
* 'ptr' which is BUF_SIZE bytes in size, since that is the size we
* requested. Due to the way vsnprintf works, we cannot use the last
* byte of this buffer. The first 'start' bytes of the produced output
* are to be skipped. After that, a total of 'len' bytes are requested.
*/
buf = ptr;
skip = start;
left = MIN(len, BUF_SIZE);
off = 0;
left = MIN(len, BUF_SIZE - 1);
used = 0;
}
@ -40,21 +40,23 @@ void buf_printf(char *fmt, ...)
/* There is no way to estimate how much space the result will take, so
* we need to produce the string even when skipping part of the start.
* If part of the result is to be skipped, do not memcpy; instead, save
* the offset of where the result starts within the buffer.
*
* The null terminating character is not part of the result, so room
* must be given for it to be stored after completely filling up the
* requested part of the buffer.
*/
max = MIN(skip + left, BUF_SIZE);
max = MIN(skip + left + 1, BUF_SIZE);
va_start(args, fmt);
len = vsnprintf(&buf[off + used], max + 1, fmt, args);
len = vsnprintf(&buf[used], max, fmt, args);
va_end(args);
/* The snprintf family returns the number of bytes that would be stored
* if the buffer were large enough, excluding the null terminator.
*/
if (len >= BUF_SIZE)
len = BUF_SIZE - 1;
if (skip > 0) {
assert(off == 0);
assert(used == 0);
if (skip >= len) {
@ -63,16 +65,15 @@ void buf_printf(char *fmt, ...)
return;
}
off = skip;
if (left > BUF_SIZE - off)
left = BUF_SIZE - off;
len -= off;
memmove(buf, &buf[skip], len - skip);
len -= skip;
skip = 0;
}
assert(skip == 0);
assert(len >= 0);
assert((long) left >= 0);
assert((ssize_t) left >= 0);
if (len > (ssize_t) left)
len = left;
@ -107,22 +108,20 @@ void buf_append(char *data, size_t len)
if (len > left)
len = left;
memcpy(&buf[off + used], data, len);
memcpy(&buf[used], data, len);
used += len;
left -= len;
}
/*===========================================================================*
* buf_get *
* buf_result *
*===========================================================================*/
size_t buf_get(char **ptr)
ssize_t buf_result(void)
{
/* Return the buffer's starting address and the length of the used
* part, not counting the trailing null character for the latter.
/* Return the resulting number of bytes produced, not counting the
* trailing null character in the buffer.
*/
*ptr = &buf[off];
return used;
}

View file

@ -30,4 +30,7 @@
#define DIR_ALL_MODE (S_IFDIR | 0555) /* world-accessible directory */
#define LNK_ALL_MODE (S_IFLNK | 0777) /* symbolic link */
/* Size of the I/O buffer. */
#define BUF_SIZE 4097 /* 4KB+1 (see buf.c) */
#endif /* _PROCFS_CONST_H */

View file

@ -7,13 +7,11 @@ static void init_hook(void);
/* The hook functions that will be called by VTreeFS. */
static struct fs_hooks hooks = {
init_hook,
NULL, /* cleanup_hook */
lookup_hook,
getdents_hook,
read_hook,
rdlink_hook,
NULL /* message_hook */
.init_hook = init_hook,
.lookup_hook = lookup_hook,
.getdents_hook = getdents_hook,
.read_hook = read_hook,
.rdlink_hook = rdlink_hook,
};
/*===========================================================================*
@ -90,7 +88,7 @@ int main(void)
stat.dev = NO_DEV;
/* Start VTreeFS. */
start_vtreefs(&hooks, NR_INODES, &stat, NR_PROCS + NR_TASKS);
start_vtreefs(&hooks, NR_INODES, &stat, NR_PROCS + NR_TASKS, BUF_SIZE);
return 0;
}

View file

@ -2,19 +2,18 @@
#define _PROCFS_PROTO_H
/* buf.c */
void buf_init(off_t start, size_t len);
void buf_init(char *ptr, size_t len, off_t start);
void buf_printf(char *fmt, ...);
void buf_append(char *data, size_t len);
size_t buf_get(char **ptr);
ssize_t buf_result(void);
/* tree.c */
int init_tree(void);
int lookup_hook(struct inode *parent, char *name, cbdata_t cbdata);
int getdents_hook(struct inode *inode, cbdata_t cbdata);
int read_hook(struct inode *inode, off_t offset, char **ptr, size_t
*len, cbdata_t cbdata);
int rdlink_hook(struct inode *inode, char *ptr, size_t max, cbdata_t
cbdata);
ssize_t read_hook(struct inode *inode, char *ptr, size_t len, off_t off,
cbdata_t cbdata);
int rdlink_hook(struct inode *inode, char *ptr, size_t max, cbdata_t cbdata);
/* util.c */
int procfs_getloadavg(struct load *loadavg, int nelem);

View file

@ -494,14 +494,14 @@ int getdents_hook(struct inode *node, cbdata_t UNUSED(cbdata))
/*===========================================================================*
* read_hook *
*===========================================================================*/
int read_hook(struct inode *node, off_t off, char **ptr,
size_t *len, cbdata_t cbdata)
ssize_t read_hook(struct inode *node, char *ptr, size_t len, off_t off,
cbdata_t cbdata)
{
/* Regular file read hook. Call the appropriate callback function to
* generate and return the data.
*/
buf_init(off, *len);
buf_init(ptr, len, off);
/* Populate the buffer with the proper content. */
if (get_inode_index(node) != NO_INDEX) {
@ -510,9 +510,7 @@ int read_hook(struct inode *node, off_t off, char **ptr,
((void (*) (void)) cbdata)();
}
*len = buf_get(ptr);
return OK;
return buf_result();
}
/*===========================================================================*

View file

@ -25,11 +25,21 @@ struct fs_hooks {
void (*cleanup_hook)(void);
int (*lookup_hook)(struct inode *inode, char *name, cbdata_t cbdata);
int (*getdents_hook)(struct inode *inode, cbdata_t cbdata);
int (*read_hook)(struct inode *inode, off_t offset, char **ptr,
size_t *len, cbdata_t cbdata);
ssize_t (*read_hook)(struct inode *inode, char *ptr, size_t len,
off_t off, cbdata_t cbdata);
ssize_t (*write_hook)(struct inode *inode, char *ptr, size_t max,
off_t off, cbdata_t cbdata);
int (*trunc_hook)(struct inode *inode, off_t offset, cbdata_t cbdata);
int (*mknod_hook)(struct inode *inode, char *name,
struct inode_stat *stat, cbdata_t cbdata);
int (*unlink_hook)(struct inode *inode, cbdata_t cbdata);
int (*slink_hook)(struct inode *inode, char *name,
struct inode_stat *stat, char *path, cbdata_t cbdata);
int (*rdlink_hook)(struct inode *inode, char *ptr, size_t max,
cbdata_t cbdata);
int (*message_hook)(message *m);
cbdata_t cbdata);
int (*chstat_hook)(struct inode *inode, struct inode_stat *stat,
cbdata_t cbdata);
void (*message_hook)(message *m, int ipc_status);
};
extern struct inode *add_inode(struct inode *parent, const char *name,
@ -55,6 +65,6 @@ extern void get_inode_stat(const struct inode *inode, struct inode_stat *stat);
extern void set_inode_stat(struct inode *inode, struct inode_stat *stat);
extern void start_vtreefs(struct fs_hooks *hooks, unsigned int nr_inodes,
struct inode_stat *stat, index_t nr_indexed_entries);
struct inode_stat *stat, index_t nr_indexed_entries, size_t buf_size);
#endif /* _MINIX_VTREEFS_H */

View file

@ -7,11 +7,11 @@ CPPFLAGS+= -D_MINIX_SYSTEM
LIB= vtreefs
SRCS= \
file.c \
inode.c \
link.c \
mount.c \
path.c \
read.c \
sdbm.c \
stadir.c \
table.c \

293
minix/lib/libvtreefs/file.c Normal file
View file

@ -0,0 +1,293 @@
/* VTreeFS - file.c - file and directory I/O */
#include "inc.h"
#include <dirent.h>
#define GETDENTS_BUFSIZ 4096
static char *buf = NULL;
static size_t bufsize = 0;
/*
* Initialize the main buffer used for I/O. Return OK or an error code.
*/
int
init_buf(size_t size)
{
/* A default buffer size, for at least getdents. */
if (size < GETDENTS_BUFSIZ)
size = GETDENTS_BUFSIZ;
if ((buf = malloc(size)) == NULL)
return ENOMEM;
bufsize = size;
return OK;
}
/*
* Free up the I/O buffer.
*/
void
cleanup_buf(void)
{
free(buf);
buf = NULL;
bufsize = 0;
}
/*
* Read from a file.
*/
ssize_t
fs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
off_t pos, int __unused call)
{
struct inode *node;
size_t off, chunk;
ssize_t r, len;
/* Try to get inode by its inode number. */
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
/* Check whether the node is a regular file. */
if (!S_ISREG(node->i_stat.mode))
return EINVAL;
/* For deleted files or with no read hook, feign an empty file. */
if (is_inode_deleted(node) || vtreefs_hooks->read_hook == NULL)
return 0; /* EOF */
assert(buf != NULL);
assert(bufsize > 0);
/*
* Call the read hook to fill the result buffer, repeatedly for as long
* as 1) the request is not yet fully completed, and 2) the read hook
* fills the entire buffer.
*/
for (off = 0; off < bytes; ) {
/* Get the next result chunk by calling the read hook. */
chunk = bytes - off;
if (chunk > bufsize)
chunk = bufsize;
len = vtreefs_hooks->read_hook(node, buf, chunk, pos,
get_inode_cbdata(node));
/* Copy any resulting data to user space. */
if (len > 0)
r = fsdriver_copyout(data, off, buf, len);
else
r = len; /* EOF or error */
/*
* If an error occurred, but we already produced some output,
* return a partial result. Otherwise return the error.
*/
if (r < 0)
return (off > 0) ? off : r;
off += len;
pos += len;
if ((size_t)len < bufsize)
break;
}
return off;
}
/*
* Write to a file.
*/
ssize_t
fs_write(ino_t ino_nr, struct fsdriver_data * data, size_t bytes, off_t pos,
int __unused call)
{
struct inode *node;
size_t off, chunk;
ssize_t r;
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
if (!S_ISREG(node->i_stat.mode))
return EINVAL;
if (is_inode_deleted(node) || vtreefs_hooks->write_hook == NULL)
return EACCES;
if (bytes == 0)
return 0;
assert(buf != NULL);
assert(bufsize > 0);
/*
* Call the write hook to process the incoming data, repeatedly for as
* long as 1) the request is not yet fully completed, and 2) the write
* hook processes at least some of the given data.
*/
for (off = 0; off < bytes; ) {
chunk = bytes - off;
if (chunk > bufsize)
chunk = bufsize;
/* Copy the data from user space. */
r = fsdriver_copyin(data, off, buf, chunk);
/* Call the write hook for the chunk. */
if (r == OK)
r = vtreefs_hooks->write_hook(node, buf, chunk, pos,
get_inode_cbdata(node));
/*
* If an error occurred, but we already processed some input,
* return a partial result. Otherwise return the error.
*/
if (r < 0)
return (off > 0) ? off : r;
off += r;
pos += r;
if ((size_t)r == 0)
break;
}
return off;
}
/*
* Truncate a file.
*/
int
fs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos)
{
struct inode *node;
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
if (!S_ISREG(node->i_stat.mode))
return EINVAL;
if (is_inode_deleted(node) || vtreefs_hooks->trunc_hook == NULL)
return EACCES;
/* TODO: translate this case into all-zeroes write callbacks. */
if (end_pos != 0)
return EINVAL;
return vtreefs_hooks->trunc_hook(node, start_pos,
get_inode_cbdata(node));
}
/*
* Retrieve directory entries.
*/
ssize_t
fs_getdents(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
off_t * posp)
{
struct fsdriver_dentry fsdentry;
struct inode *node, *child;
const char *name;
off_t pos;
int r, skip, get_next, indexed;
if (*posp >= ULONG_MAX)
return EIO;
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
indexed = node->i_indexed;
get_next = FALSE;
child = NULL;
/* Call the getdents hook, if any, to "refresh" the directory. */
if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) {
r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node));
if (r != OK)
return r;
}
assert(buf != NULL);
assert(bufsize > 0);
fsdriver_dentry_init(&fsdentry, data, bytes, buf, bufsize);
do {
/* Determine which inode and name to use for this entry. */
pos = (*posp)++;
if (pos == 0) {
/* The "." entry. */
child = node;
name = ".";
} else if (pos == 1) {
/* The ".." entry. */
child = get_parent_inode(node);
if (child == NULL)
child = node;
name = "..";
} else if (pos - 2 < indexed) {
/* All indexed entries. */
child = get_inode_by_index(node, pos - 2);
/*
* If there is no inode with this particular index,
* continue with the next index number.
*/
if (child == NULL) continue;
name = child->i_name;
} else {
/* All non-indexed entries. */
/*
* If this is the first loop iteration, first get to
* the non-indexed child identified by the current
* position.
*/
if (get_next == FALSE) {
skip = pos - indexed - 2;
child = get_first_inode(node);
/* Skip indexed children. */
while (child != NULL &&
child->i_index != NO_INDEX)
child = get_next_inode(child);
/* Skip to the right position. */
while (child != NULL && skip-- > 0)
child = get_next_inode(child);
get_next = TRUE;
} else
child = get_next_inode(child);
/* No more children? Then stop. */
if (child == NULL)
break;
assert(!is_inode_deleted(child));
name = child->i_name;
}
/* Add the directory entry to the output. */
r = fsdriver_dentry_add(&fsdentry,
(ino_t)get_inode_number(child), name, strlen(name),
IFTODT(child->i_stat.mode));
if (r < 0)
return r;
} while (r > 0);
return fsdriver_dentry_finish(&fsdentry);
}

View file

@ -26,7 +26,7 @@ static LIST_HEAD(index_head, inode) *parent_index_head;
/*
* Initialize the inode-related state.
*/
void
int
init_inodes(unsigned int inodes, struct inode_stat * stat,
index_t nr_indexed_entries)
{
@ -39,13 +39,21 @@ init_inodes(unsigned int inodes, struct inode_stat * stat,
nr_inodes = inodes;
/* Allocate the inode and hash tables. */
inode = malloc(nr_inodes * sizeof(inode[0]));
parent_name_head = malloc(nr_inodes * sizeof(parent_name_head[0]));
parent_index_head = malloc(nr_inodes * sizeof(parent_index_head[0]));
if ((inode = malloc(nr_inodes * sizeof(inode[0]))) == NULL)
return ENOMEM;
assert(inode != NULL);
assert(parent_name_head != NULL);
assert(parent_index_head != NULL);
parent_name_head = malloc(nr_inodes * sizeof(parent_name_head[0]));
if (parent_name_head == NULL) {
free(inode);
return ENOMEM;
}
parent_index_head = malloc(nr_inodes * sizeof(parent_index_head[0]));
if (parent_index_head == NULL) {
free(parent_name_head);
free(inode);
return ENOMEM;
}
#if DEBUG
printf("VTREEFS: allocated %d+%d+%d bytes\n",
@ -83,6 +91,8 @@ init_inodes(unsigned int inodes, struct inode_stat * stat,
set_inode_stat(node, stat);
node->i_indexed = nr_indexed_entries;
node->i_cbdata = NULL;
return OK;
}
/*

View file

@ -1,9 +1,9 @@
/* VTreeFS - link.c - support for symbolic links */
/* VTreeFS - link.c - support for symbolic links and device nodes */
#include "inc.h"
/*
* Retrieve symbolic link target.
* Retrieve a symbolic link target.
*/
ssize_t
fs_rdlink(ino_t ino_nr, struct fsdriver_data * data, size_t bytes)
@ -16,11 +16,10 @@ fs_rdlink(ino_t ino_nr, struct fsdriver_data * data, size_t bytes)
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
/*
* Call the rdlink hook. The hook must be non-NULL if the file system
* adds symlink nodes. If it doesn't, we will never get here.
*/
assert(vtreefs_hooks->rdlink_hook != NULL);
/* The hook should be provided for any FS that adds symlink inodes.. */
if (vtreefs_hooks->rdlink_hook == NULL)
return ENOSYS;
assert(!is_inode_deleted(node)); /* symlinks cannot be opened */
r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path),
@ -39,3 +38,92 @@ fs_rdlink(ino_t ino_nr, struct fsdriver_data * data, size_t bytes)
return len;
}
/*
* Create a symbolic link.
*/
int
fs_slink(ino_t dir_nr, char * name, uid_t uid, gid_t gid,
struct fsdriver_data * data, size_t bytes)
{
char path[PATH_MAX];
struct inode *node;
struct inode_stat stat;
int r;
if ((node = find_inode(dir_nr)) == NULL)
return EINVAL;
if (vtreefs_hooks->slink_hook == NULL)
return ENOSYS;
if (get_inode_by_name(node, name) != NULL)
return EEXIST;
if (bytes >= sizeof(path))
return ENAMETOOLONG;
if ((r = fsdriver_copyin(data, 0, path, bytes)) != OK)
return r;
path[bytes] = 0;
memset(&stat, 0, sizeof(stat));
stat.mode = S_IFLNK | RWX_MODES;
stat.uid = uid;
stat.gid = gid;
stat.size = strlen(path);
stat.dev = 0;
return vtreefs_hooks->slink_hook(node, name, &stat, path,
get_inode_cbdata(node));
}
/*
* Create a device node.
*/
int
fs_mknod(ino_t dir_nr, char * name, mode_t mode, uid_t uid, gid_t gid,
dev_t rdev)
{
struct inode *node;
struct inode_stat stat;
if ((node = find_inode(dir_nr)) == NULL)
return EINVAL;
if (get_inode_by_name(node, name) != NULL)
return EEXIST;
if (vtreefs_hooks->mknod_hook == NULL)
return ENOSYS;
memset(&stat, 0, sizeof(stat));
stat.mode = mode;
stat.uid = uid;
stat.gid = gid;
stat.size = 0;
stat.dev = rdev;
return vtreefs_hooks->mknod_hook(node, name, &stat,
get_inode_cbdata(node));
}
/*
* Unlink a node.
*/
int
fs_unlink(ino_t dir_nr, char * name, int __unused call)
{
struct inode *dir_node, *node;
if ((dir_node = find_inode(dir_nr)) == NULL)
return EINVAL;
if ((node = get_inode_by_name(dir_node, name)) == NULL)
return ENOENT;
if (vtreefs_hooks->unlink_hook == NULL)
return ENOSYS;
return vtreefs_hooks->unlink_hook(node, get_inode_cbdata(node));
}

View file

@ -1,9 +1,20 @@
#ifndef _VTREEFS_PROTO_H
#define _VTREEFS_PROTO_H
/* file.c */
int init_buf(size_t size);
void cleanup_buf(void);
ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
off_t pos, int call);
ssize_t fs_write(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
off_t pos, int call);
int fs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos);
ssize_t fs_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
off_t *pos);
/* inode.c */
void init_inodes(unsigned int inodes, struct inode_stat *stat, index_t
nr_indexed_entries);
int init_inodes(unsigned int inodes, struct inode_stat *stat,
index_t nr_indexed_entries);
void cleanup_inodes(void);
struct inode *find_inode(ino_t num);
struct inode *get_inode(ino_t num);
@ -15,6 +26,11 @@ int fs_putnode(ino_t ino_nr, unsigned int count);
/* link.c */
ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes);
int fs_slink(ino_t dir_nr, char *name, uid_t uid, gid_t gid,
struct fsdriver_data *data, size_t bytes);
int fs_mknod(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid,
dev_t rdev);
int fs_unlink(ino_t dir_nr, char *name, int call);
/* mount.c */
int fs_mount(dev_t dev, unsigned int flags, struct fsdriver_node *root_node,
@ -28,17 +44,13 @@ void fs_other(const message *m_ptr, int ipc_status);
int fs_lookup(ino_t dir_nr, char *name, struct fsdriver_node *node,
int *is_mountpt);
/* read.c */
ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
off_t pos, int call);
ssize_t fs_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
off_t *pos);
/* sdbm.c */
long sdbm_hash(const char *str, int len);
/* stadir.c */
int fs_stat(ino_t ino_nr, struct stat *buf);
int fs_chmod(ino_t ino_nr, mode_t *mode);
int fs_chown(ino_t ino_nr, uid_t uid, gid_t gid, mode_t *mode);
int fs_statvfs(struct statvfs *buf);
#endif /* _VTREEFS_PROTO_H */

View file

@ -1,153 +0,0 @@
/* VTreeFS - read.c - reading from files and directories */
#include "inc.h"
#include <dirent.h>
#define GETDENTS_BUFSIZ 4096
/*
* Read from a file.
*/
ssize_t
fs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
off_t pos, int __unused call)
{
struct inode *node;
size_t len;
char *ptr;
int r;
/* Try to get inode by its inode number. */
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
/* Check whether the node is a regular file. */
if (!S_ISREG(node->i_stat.mode))
return EINVAL;
/* Call the read hook, if any. */
if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) {
len = bytes;
/*
* On success, the read hook provides us with a pointer to the
* resulting data. This avoids copying overhead.
*/
r = vtreefs_hooks->read_hook(node, pos, &ptr, &len,
get_inode_cbdata(node));
assert(len <= bytes);
/* Copy the resulting data to user space. */
if (r == OK && len > 0)
r = fsdriver_copyout(data, 0, ptr, len);
} else {
/* Feign an empty file. */
r = OK;
len = 0;
}
return (r != OK) ? r : len;
}
/*
* Retrieve directory entries.
*/
ssize_t
fs_getdents(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
off_t * posp)
{
struct fsdriver_dentry fsdentry;
struct inode *node, *child;
const char *name;
off_t pos;
int r, skip, get_next, indexed;
static char buf[GETDENTS_BUFSIZ];
if (*posp >= ULONG_MAX)
return EIO;
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
indexed = node->i_indexed;
get_next = FALSE;
child = NULL;
/* Call the getdents hook, if any, to "refresh" the directory. */
if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) {
r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node));
if (r != OK)
return r;
}
fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf));
do {
/* Determine which inode and name to use for this entry. */
pos = (*posp)++;
if (pos == 0) {
/* The "." entry. */
child = node;
name = ".";
} else if (pos == 1) {
/* The ".." entry. */
child = get_parent_inode(node);
if (child == NULL)
child = node;
name = "..";
} else if (pos - 2 < indexed) {
/* All indexed entries. */
child = get_inode_by_index(node, pos - 2);
/*
* If there is no inode with this particular index,
* continue with the next index number.
*/
if (child == NULL) continue;
name = child->i_name;
} else {
/* All non-indexed entries. */
/*
* If this is the first loop iteration, first get to
* the non-indexed child identified by the current
* position.
*/
if (get_next == FALSE) {
skip = pos - indexed - 2;
child = get_first_inode(node);
/* Skip indexed children. */
while (child != NULL &&
child->i_index != NO_INDEX)
child = get_next_inode(child);
/* Skip to the right position. */
while (child != NULL && skip-- > 0)
child = get_next_inode(child);
get_next = TRUE;
} else
child = get_next_inode(child);
/* No more children? Then stop. */
if (child == NULL)
break;
assert(!is_inode_deleted(child));
name = child->i_name;
}
/* Add the directory entry to the output. */
r = fsdriver_dentry_add(&fsdentry,
(ino_t)get_inode_number(child), name, strlen(name),
IFTODT(child->i_stat.mode));
if (r < 0)
return r;
} while (r > 0);
return fsdriver_dentry_finish(&fsdentry);
}

View file

@ -1,4 +1,4 @@
/* VTreeFS - stadir.c - file and file system status retrieval */
/* VTreeFS - stadir.c - file and file system status management */
#include "inc.h"
@ -44,6 +44,72 @@ fs_stat(ino_t ino_nr, struct stat * buf)
return OK;
}
/*
* Change file mode.
*/
int
fs_chmod(ino_t ino_nr, mode_t * mode)
{
struct inode *node;
struct inode_stat stat;
int r;
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
if (vtreefs_hooks->chstat_hook == NULL)
return ENOSYS;
get_inode_stat(node, &stat);
stat.mode = (stat.mode & ~ALL_MODES) | (*mode & ALL_MODES);
r = vtreefs_hooks->chstat_hook(node, &stat, get_inode_cbdata(node));
if (r != OK)
return r;
get_inode_stat(node, &stat);
*mode = stat.mode;
return OK;
}
/*
* Change file ownership.
*/
int
fs_chown(ino_t ino_nr, uid_t uid, gid_t gid, mode_t * mode)
{
struct inode *node;
struct inode_stat stat;
int r;
if ((node = find_inode(ino_nr)) == NULL)
return EINVAL;
if (vtreefs_hooks->chstat_hook == NULL)
return ENOSYS;
get_inode_stat(node, &stat);
stat.uid = uid;
stat.gid = gid;
stat.mode &= ~(S_ISUID | S_ISGID);
r = vtreefs_hooks->chstat_hook(node, &stat, get_inode_cbdata(node));
if (r != OK)
return r;
get_inode_stat(node, &stat);
*mode = stat.mode;
return OK;
}
/*
* Retrieve file system statistics.
*/

View file

@ -9,9 +9,16 @@ struct fsdriver vtreefs_table = {
.fdr_lookup = fs_lookup,
.fdr_putnode = fs_putnode,
.fdr_read = fs_read,
.fdr_write = fs_write,
.fdr_getdents = fs_getdents,
.fdr_trunc = fs_trunc,
.fdr_mknod = fs_mknod,
.fdr_unlink = fs_unlink,
.fdr_slink = fs_slink,
.fdr_rdlink = fs_rdlink,
.fdr_stat = fs_stat,
.fdr_chmod = fs_chmod,
.fdr_chown = fs_chown,
.fdr_statvfs = fs_statvfs,
.fdr_other = fs_other
};

View file

@ -5,16 +5,24 @@
static unsigned int inodes;
static struct inode_stat *root_stat;
static index_t root_entries;
static size_t buf_size;
/*
* Initialize internal state, and register with VFS.
* Initialize internal state. This is the only place where dynamic memory
* allocation takes place.
*/
static int
init_server(int __unused type, sef_init_info_t * __unused info)
{
int r;
/* Initialize the virtual tree. */
init_inodes(inodes, root_stat, root_entries);
if ((r = init_inodes(inodes, root_stat, root_entries)) != OK)
panic("init_inodes failed: %d", r);
/* Initialize the I/O buffer. */
if ((r = init_buf(buf_size)) != OK)
panic("init_buf failed: %d", r);
return OK;
}
@ -54,7 +62,7 @@ sef_local_startup(void)
* Call the message hook, if there is one.
*/
void
fs_other(const message * m_ptr, int __unused ipc_status)
fs_other(const message * m_ptr, int ipc_status)
{
message msg;
@ -65,7 +73,7 @@ fs_other(const message * m_ptr, int __unused ipc_status)
*/
msg = *m_ptr;
vtreefs_hooks->message_hook(&msg);
vtreefs_hooks->message_hook(&msg, ipc_status);
}
}
@ -76,7 +84,7 @@ fs_other(const message * m_ptr, int __unused ipc_status)
*/
void
start_vtreefs(struct fs_hooks * hooks, unsigned int nr_inodes,
struct inode_stat * stat, index_t nr_indexed_entries)
struct inode_stat * stat, index_t nr_indexed_entries, size_t bufsize)
{
/*
@ -87,10 +95,12 @@ start_vtreefs(struct fs_hooks * hooks, unsigned int nr_inodes,
inodes = nr_inodes;
root_stat = stat;
root_entries = nr_indexed_entries;
buf_size = bufsize;
sef_local_startup();
fsdriver_task(&vtreefs_table);
cleanup_buf();
cleanup_inodes();
}

View file

@ -1,62 +1,29 @@
/* buf.c - by Alen Stojanov and David van Moolenbroek, taken from procfs */
#define _SYSTEM 1 /* tell headers that this is the kernel */
#define DEVMAN_SERVER 1
#include <minix/config.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lib.h>
#include <minix/timers.h>
#include <minix/callnr.h>
#include <minix/type.h>
#include <minix/const.h>
#include <minix/com.h>
#include <minix/syslib.h>
#include <minix/sysutil.h>
#include <minix/vfsif.h>
#include <minix/endpoint.h>
#include <minix/sysinfo.h>
#include <minix/u64.h>
#include <minix/sysinfo.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <minix/vtreefs.h>
#include <minix/devman.h>
#include "devman.h"
#include "proto.h"
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#define BUF_SIZE 4096
static char buf[BUF_SIZE + 1];
static size_t off, left, used;
static char *buf;
static size_t left, used;
static off_t skip;
/*===========================================================================*
* buf_init *
*===========================================================================*/
void buf_init(off_t start, size_t len)
void buf_init(char *ptr, size_t len, off_t start)
{
/* Initialize the buffer for fresh use. The first 'start' bytes of the
* produced output are to be skipped. After that, up to a total of
* 'len' bytes are requested.
/* Initialize the buffer for fresh use. The output is to be stored into
* 'ptr' which is BUF_SIZE bytes in size, since that is the size we
* requested. Due to the way vsnprintf works, we cannot use the last
* byte of this buffer. The first 'start' bytes of the produced output
* are to be skipped. After that, a total of 'len' bytes are requested.
*/
buf = ptr;
skip = start;
left = MIN(len, BUF_SIZE);
off = 0;
left = MIN(len, BUF_SIZE - 1);
used = 0;
}
@ -75,21 +42,23 @@ void buf_printf(char *fmt, ...)
/* There is no way to estimate how much space the result will take, so
* we need to produce the string even when skipping part of the start.
* If part of the result is to be skipped, do not memcpy; instead, save
* the offset of where the result starts within the buffer.
*
* The null terminating character is not part of the result, so room
* must be given for it to be stored after completely filling up the
* requested part of the buffer.
*/
max = MIN(skip + left, BUF_SIZE);
max = MIN(skip + left + 1, BUF_SIZE);
va_start(args, fmt);
len = vsnprintf(&buf[off + used], max + 1, fmt, args);
len = vsnprintf(&buf[used], max, fmt, args);
va_end(args);
/* The snprintf family returns the number of bytes that would be stored
* if the buffer were large enough, excluding the null terminator.
*/
if (len >= BUF_SIZE)
len = BUF_SIZE - 1;
if (skip > 0) {
assert(off == 0);
assert(used == 0);
if (skip >= len) {
@ -98,16 +67,15 @@ void buf_printf(char *fmt, ...)
return;
}
off = skip;
if (left > BUF_SIZE - off)
left = BUF_SIZE - off;
len -= off;
memmove(buf, &buf[skip], len - skip);
len -= skip;
skip = 0;
}
assert(skip == 0);
assert(len >= 0);
assert((long) left >= 0);
assert((ssize_t) left >= 0);
if (len > (ssize_t) left)
len = left;
@ -142,22 +110,20 @@ void buf_append(char *data, size_t len)
if (len > left)
len = left;
memcpy(&buf[off + used], data, len);
memcpy(&buf[used], data, len);
used += len;
left -= len;
}
/*===========================================================================*
* buf_get *
* buf_result *
*===========================================================================*/
size_t buf_get(char **ptr)
ssize_t buf_result(void)
{
/* Return the buffer's starting address and the length of the used
* part, not counting the trailing null character for the latter.
/* Return the resulting number of bytes produced, not counting the
* trailing null character in the buffer.
*/
*ptr = &buf[off];
return used;
}

View file

@ -8,7 +8,7 @@ static struct devman_device *_find_dev(struct devman_device *dev, int
dev_id);
static int devman_dev_add_info(struct devman_device *dev, struct
devman_device_info_entry *entry, char *buf);
static int devman_event_read(char **ptr, size_t *len,off_t offset, void
static ssize_t devman_event_read(char *ptr, size_t len, off_t offset, void
*data);
static int devman_del_device(struct devman_device *dev);
@ -138,11 +138,12 @@ devman_device_remove_event(struct devman_device* dev)
/*===========================================================================*
* devman_event_read *
*===========================================================================*/
static int
devman_event_read(char **ptr, size_t *len,off_t offset, void *data)
static ssize_t
devman_event_read(char *ptr, size_t len, off_t offset, void *data)
{
struct devman_event *ev = NULL;
struct devman_event_inode *n;
ssize_t r;
n = (struct devman_event_inode *) data;
@ -150,35 +151,34 @@ devman_event_read(char **ptr, size_t *len,off_t offset, void *data)
ev = TAILQ_LAST(&n->event_queue, event_head);
}
buf_init(offset, *len);
buf_init(ptr, len, offset);
if (ev != NULL)
buf_printf("%s", ev->data);
*len = buf_get(ptr);
r = buf_result();
/* read all (EOF)? */
if (ev != NULL && *len == 0) {
if (ev != NULL && r == 0) {
TAILQ_REMOVE(&n->event_queue, ev, events);
free(ev);
}
return 0;
return r;
}
/*===========================================================================*
* devman_static_info_read *
*===========================================================================*/
static int
devman_static_info_read(char **ptr, size_t *len, off_t offset, void *data)
static ssize_t
devman_static_info_read(char *ptr, size_t len, off_t offset, void *data)
{
struct devman_static_info_inode *n;
n = (struct devman_static_info_inode *) data;
buf_init(offset, *len);
buf_init(ptr, len, offset);
buf_printf("%s\n", n->data);
*len = buf_get(ptr);
return 0;
return buf_result();
}
/*===========================================================================*

View file

@ -36,6 +36,8 @@
#include <minix/devman.h>
#include <sys/queue.h>
#define BUF_SIZE 4097
#define DEVMAN_DEFAULT_MODE (S_IRUSR | S_IRGRP | S_IROTH)
#define DEVMAN_STRING_LEN 128
@ -48,8 +50,8 @@ enum devman_inode_type {
DEVMAN_DEVICE
};
typedef int (*devman_read_fn)
(char **ptr, size_t *len, off_t offset, void *data);
typedef ssize_t (*devman_read_fn)
(char *ptr, size_t len, off_t offset, void *data);
struct devman_device_file {
int minor;

View file

@ -43,24 +43,23 @@ static void init_hook(void) {
}
static int message_hook (message *m)
static void message_hook(message *m, int __unused ipc_status)
{
switch (m->m_type) {
case DEVMAN_ADD_DEV:
return do_add_device(m);
do_add_device(m);
case DEVMAN_DEL_DEV:
return do_del_device(m);
do_del_device(m);
case DEVMAN_BIND:
return do_bind_device(m);
do_bind_device(m);
case DEVMAN_UNBIND:
return do_unbind_device(m);
default: return -1;
do_unbind_device(m);
}
}
static int
static ssize_t
read_hook
(struct inode *inode, off_t offset, char **ptr, size_t *len, cbdata_t cbdata)
(struct inode *inode, char *ptr, size_t len, off_t offset, cbdata_t cbdata)
{
struct devman_inode *d_inode = (struct devman_inode *) cbdata;
@ -77,7 +76,7 @@ int main (int argc, char* argv[])
/* fill in the hooks */
memset(&hooks, 0, sizeof(hooks));
hooks.init_hook = init_hook;
hooks.read_hook = read_hook; /* read will never be called */
hooks.read_hook = read_hook;
hooks.message_hook = message_hook; /* handle the ds_update call */
root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
@ -87,7 +86,7 @@ int main (int argc, char* argv[])
root_stat.dev = NO_DEV;
/* limit the number of indexed entries */
start_vtreefs(&hooks, 1024 , &root_stat, 0);
start_vtreefs(&hooks, 1024, &root_stat, 0, BUF_SIZE);
return 0;
}

View file

@ -2,10 +2,10 @@
#define _DEVMAN_PROTO_H
/* buf.c */
void buf_init(off_t start, size_t len);
void buf_init(char *ptr, size_t len, off_t off);
void buf_printf(char *fmt, ...);
void buf_append(char *data, size_t len);
size_t buf_get(char **ptr);
ssize_t buf_result(void);
/* message handlers */
int do_add_device(message *m);