From 5eefd0fec2bd5bc6ad818ba164bcc653f954426c Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Thu, 6 Nov 2014 15:04:45 +0000 Subject: [PATCH] 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 --- minix/drivers/system/gpio/gpio.c | 58 +++--- minix/fs/procfs/buf.c | 55 +++--- minix/fs/procfs/const.h | 3 + minix/fs/procfs/main.c | 14 +- minix/fs/procfs/proto.h | 11 +- minix/fs/procfs/tree.c | 10 +- minix/include/minix/vtreefs.h | 20 ++- minix/lib/libvtreefs/Makefile | 2 +- minix/lib/libvtreefs/file.c | 293 +++++++++++++++++++++++++++++++ minix/lib/libvtreefs/inode.c | 22 ++- minix/lib/libvtreefs/link.c | 102 ++++++++++- minix/lib/libvtreefs/proto.h | 28 ++- minix/lib/libvtreefs/read.c | 153 ---------------- minix/lib/libvtreefs/stadir.c | 68 ++++++- minix/lib/libvtreefs/table.c | 7 + minix/lib/libvtreefs/vtreefs.c | 20 ++- minix/servers/devman/buf.c | 92 +++------- minix/servers/devman/device.c | 24 +-- minix/servers/devman/devman.h | 6 +- minix/servers/devman/main.c | 19 +- minix/servers/devman/proto.h | 4 +- 21 files changed, 654 insertions(+), 357 deletions(-) create mode 100644 minix/lib/libvtreefs/file.c delete mode 100644 minix/lib/libvtreefs/read.c diff --git a/minix/drivers/system/gpio/gpio.c b/minix/drivers/system/gpio/gpio.c index b9da4e522..9ddc19292 100644 --- a/minix/drivers/system/gpio/gpio.c +++ b/minix/drivers/system/gpio/gpio.c @@ -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); - - /* 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 @@ -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; } diff --git a/minix/fs/procfs/buf.c b/minix/fs/procfs/buf.c index ee2ef7990..463c1b822 100644 --- a/minix/fs/procfs/buf.c +++ b/minix/fs/procfs/buf.c @@ -3,25 +3,25 @@ #include "inc.h" #include -#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; } diff --git a/minix/fs/procfs/const.h b/minix/fs/procfs/const.h index 18ad75dc3..ed52f99c7 100644 --- a/minix/fs/procfs/const.h +++ b/minix/fs/procfs/const.h @@ -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 */ diff --git a/minix/fs/procfs/main.c b/minix/fs/procfs/main.c index 180543aa2..9e31fb588 100644 --- a/minix/fs/procfs/main.c +++ b/minix/fs/procfs/main.c @@ -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; } diff --git a/minix/fs/procfs/proto.h b/minix/fs/procfs/proto.h index 64f257a67..4fa224e40 100644 --- a/minix/fs/procfs/proto.h +++ b/minix/fs/procfs/proto.h @@ -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); diff --git a/minix/fs/procfs/tree.c b/minix/fs/procfs/tree.c index 239bd888d..4e5fabde8 100644 --- a/minix/fs/procfs/tree.c +++ b/minix/fs/procfs/tree.c @@ -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(); } /*===========================================================================* diff --git a/minix/include/minix/vtreefs.h b/minix/include/minix/vtreefs.h index 5ab27b863..91c340f84 100644 --- a/minix/include/minix/vtreefs.h +++ b/minix/include/minix/vtreefs.h @@ -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 */ diff --git a/minix/lib/libvtreefs/Makefile b/minix/lib/libvtreefs/Makefile index f885652fb..181b053c0 100644 --- a/minix/lib/libvtreefs/Makefile +++ b/minix/lib/libvtreefs/Makefile @@ -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 \ diff --git a/minix/lib/libvtreefs/file.c b/minix/lib/libvtreefs/file.c new file mode 100644 index 000000000..3d3b2c9fe --- /dev/null +++ b/minix/lib/libvtreefs/file.c @@ -0,0 +1,293 @@ +/* VTreeFS - file.c - file and directory I/O */ + +#include "inc.h" +#include + +#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); +} diff --git a/minix/lib/libvtreefs/inode.c b/minix/lib/libvtreefs/inode.c index 98eac18e4..0363dc595 100644 --- a/minix/lib/libvtreefs/inode.c +++ b/minix/lib/libvtreefs/inode.c @@ -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])); + 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", @@ -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; } /* diff --git a/minix/lib/libvtreefs/link.c b/minix/lib/libvtreefs/link.c index 5637ba658..5296dbab8 100644 --- a/minix/lib/libvtreefs/link.c +++ b/minix/lib/libvtreefs/link.c @@ -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)); +} diff --git a/minix/lib/libvtreefs/proto.h b/minix/lib/libvtreefs/proto.h index 683daea7a..acead73cb 100644 --- a/minix/lib/libvtreefs/proto.h +++ b/minix/lib/libvtreefs/proto.h @@ -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 */ diff --git a/minix/lib/libvtreefs/read.c b/minix/lib/libvtreefs/read.c deleted file mode 100644 index 7d510c3db..000000000 --- a/minix/lib/libvtreefs/read.c +++ /dev/null @@ -1,153 +0,0 @@ -/* VTreeFS - read.c - reading from files and directories */ - -#include "inc.h" -#include - -#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); -} diff --git a/minix/lib/libvtreefs/stadir.c b/minix/lib/libvtreefs/stadir.c index 6eba24ead..329c6e065 100644 --- a/minix/lib/libvtreefs/stadir.c +++ b/minix/lib/libvtreefs/stadir.c @@ -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. */ diff --git a/minix/lib/libvtreefs/table.c b/minix/lib/libvtreefs/table.c index 17370bcfd..df2085cbe 100644 --- a/minix/lib/libvtreefs/table.c +++ b/minix/lib/libvtreefs/table.c @@ -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 }; diff --git a/minix/lib/libvtreefs/vtreefs.c b/minix/lib/libvtreefs/vtreefs.c index e433a736a..f63d1064a 100644 --- a/minix/lib/libvtreefs/vtreefs.c +++ b/minix/lib/libvtreefs/vtreefs.c @@ -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(); } diff --git a/minix/servers/devman/buf.c b/minix/servers/devman/buf.c index 961e7f769..694af690f 100755 --- a/minix/servers/devman/buf.c +++ b/minix/servers/devman/buf.c @@ -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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - +#include "devman.h" +#include "proto.h" #include #include -#include -#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; } diff --git a/minix/servers/devman/device.c b/minix/servers/devman/device.c index 9d5ed54c4..0babbfe1b 100644 --- a/minix/servers/devman/device.c +++ b/minix/servers/devman/device.c @@ -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(); } /*===========================================================================* diff --git a/minix/servers/devman/devman.h b/minix/servers/devman/devman.h index dff8d7c69..a9d77d5f0 100644 --- a/minix/servers/devman/devman.h +++ b/minix/servers/devman/devman.h @@ -36,6 +36,8 @@ #include #include +#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; diff --git a/minix/servers/devman/main.c b/minix/servers/devman/main.c index 91477ff9f..924d6fb70 100644 --- a/minix/servers/devman/main.c +++ b/minix/servers/devman/main.c @@ -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; } diff --git a/minix/servers/devman/proto.h b/minix/servers/devman/proto.h index 540fb9137..bcc2ebbdf 100644 --- a/minix/servers/devman/proto.h +++ b/minix/servers/devman/proto.h @@ -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); -- 2.44.0