.dev = NO_DEV,
};
+/* Buffer size for read requests */
+#define DATA_SIZE 26
+
int
add_gpio_inode(char *name, int nr, int mode)
{
}
}
-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);
|| 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);
-
- /* If the offset is beyond the end of the string, return EOF. */
- if (offset > strlen(data)) {
- *len = 0;
+ snprintf(ptr, DATA_SIZE, "%d\n", value);
+ len = strlen(ptr);
- return OK;
- }
+ /* If the offset is at or beyond the end of the string, return EOF. */
+ if (offset >= len)
+ return 0;
- /* 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;
+ /* Otherwise, we may have to move the data to the start of ptr. */
+ if (offset > 0) {
+ len -= offset;
- if (*len > strlen(data) - offset)
- *len = strlen(data) - offset;
+ memmove(ptr, &ptr[offset], len);
+ }
- 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
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;
}
#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;
}
/* 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) {
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;
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;
}
#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 */
/* 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,
};
/*===========================================================================*
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;
}
#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);
/*===========================================================================*
* 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) {
((void (*) (void)) cbdata)();
}
- *len = buf_get(ptr);
-
- return OK;
+ return buf_result();
}
/*===========================================================================*
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,
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 */
LIB= vtreefs
SRCS= \
+ file.c \
inode.c \
link.c \
mount.c \
path.c \
- read.c \
sdbm.c \
stadir.c \
table.c \
--- /dev/null
+/* 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);
+}
/*
* Initialize the inode-related state.
*/
-void
+int
init_inodes(unsigned int inodes, struct inode_stat * stat,
index_t nr_indexed_entries)
{
nr_inodes = inodes;
/* Allocate the inode and hash tables. */
- inode = malloc(nr_inodes * sizeof(inode[0]));
+ if ((inode = malloc(nr_inodes * sizeof(inode[0]))) == NULL)
+ return ENOMEM;
+
parent_name_head = malloc(nr_inodes * sizeof(parent_name_head[0]));
- parent_index_head = malloc(nr_inodes * sizeof(parent_index_head[0]));
+ if (parent_name_head == NULL) {
+ free(inode);
+ return ENOMEM;
+ }
- assert(inode != NULL);
- assert(parent_name_head != NULL);
- assert(parent_index_head != NULL);
+ 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",
set_inode_stat(node, stat);
node->i_indexed = nr_indexed_entries;
node->i_cbdata = NULL;
+
+ return OK;
}
/*
-/* 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)
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),
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));
+}
#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);
/* 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,
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 */
+++ /dev/null
-/* 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);
-}
-/* VTreeFS - stadir.c - file and file system status retrieval */
+/* VTreeFS - stadir.c - file and file system status management */
#include "inc.h"
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.
*/
.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
};
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;
}
* 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;
*/
msg = *m_ptr;
- vtreefs_hooks->message_hook(&msg);
+ vtreefs_hooks->message_hook(&msg, 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)
{
/*
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();
}
/* 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;
}
/* 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) {
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;
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;
}
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);
/*===========================================================================*
* 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;
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();
}
/*===========================================================================*
#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
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;
}
-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;
/* 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;
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;
}
#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);