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:
parent
693ad767e8
commit
5eefd0fec2
21 changed files with 655 additions and 358 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
293
minix/lib/libvtreefs/file.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue