./usr/include/minix/ds.h minix-sys
./usr/include/minix/endpoint.h minix-sys
./usr/include/minix/fb.h minix-sys
+./usr/include/minix/fsdriver.h minix-sys
./usr/include/minix/fslib.h minix-sys
./usr/include/minix/gcov.h minix-sys
./usr/include/minix/gpio.h minix-sys
./usr/lib/libform.so minix-sys
./usr/lib/libform.so.6.0 minix-sys
./usr/lib/libform.so.6 minix-sys
+./usr/lib/libfsdriver.a minix-sys
+./usr/lib/libfsdriver_pic.a minix-sys
./usr/lib/libgcc.a minix-sys gcc=45
./usr/lib/libgcc_eh.a minix-sys gcc=45
./usr/lib/libgcc_s.a minix-sys
../minix/lib/libdevman \
../minix/lib/libexec \
../minix/lib/libfetch \
+ ../minix/lib/libfsdriver \
../minix/lib/libinputdriver \
../minix/lib/libminc \
../minix/lib/libminixfs \
config.h const.h cpufeature.h \
debug.h devio.h devman.h dmap.h \
driver.h drivers.h drvlib.h ds.h \
- endpoint.h fb.h fslib.h gpio.h gcov.h hash.h \
+ endpoint.h fb.h fsdriver.h fslib.h gpio.h gcov.h hash.h \
hgfs.h i2c.h i2cdriver.h ioctl.h input.h \
inputdriver.h ipc.h ipcconst.h \
keymap.h log.h mmio.h mthread.h minlib.h \
--- /dev/null
+#ifndef _MINIX_FSDRIVER_H
+#define _MINIX_FSDRIVER_H
+
+struct stat;
+struct statvfs;
+struct timespec;
+
+/* Resulting node properties. */
+struct fsdriver_node {
+ ino_t fn_ino_nr; /* inode number */
+ mode_t fn_mode; /* file mode */
+ off_t fn_size; /* file size */
+ uid_t fn_uid; /* owning user ID */
+ gid_t fn_gid; /* owning group ID */
+ dev_t fn_dev; /* device number, for block/char dev */
+};
+
+/* Opaque data structure for the fsdriver_copyin, _copyout, _zero functions. */
+struct fsdriver_data {
+ endpoint_t endpt; /* source/destination endpoint */
+ union {
+ cp_grant_id_t grant; /* grant, if endpt != SELF */
+ char *ptr; /* local pointer, if endpt == SELF */
+ };
+ size_t size; /* total buffer size (check only) */
+};
+
+/* Opaque data structure for the fsdriver_dentry_ functions. */
+struct fsdriver_dentry {
+ const struct fsdriver_data *data;
+ size_t data_size;
+ size_t data_off;
+ char *buf;
+ size_t buf_size;
+ size_t buf_off;
+};
+
+/*
+ * For a few groups of calls, the functions have the same signature, so that
+ * the file system can use a single implementation for multiple functions
+ * without requiring extra stubs. Thus, we pass in an extra parameter that
+ * identifies the call; one of the values below. For the same reason, the peek
+ * and bpeek calls have a "data" parameter which is always set to NULL.
+ */
+#define FSC_READ 0 /* read or bread call */
+#define FSC_WRITE 1 /* write or bwrite call */
+#define FSC_PEEK 2 /* peek or bpeek call */
+
+#define FSC_UNLINK 0 /* unlink call */
+#define FSC_RMDIR 1 /* rmdir call */
+
+/* Function call table for file system services. */
+struct fsdriver {
+ int (*fdr_mount)(dev_t dev, unsigned int flags,
+ struct fsdriver_node *root_node, unsigned int *res_flags);
+ void (*fdr_unmount)(void);
+ int (*fdr_lookup)(ino_t dir_nr, char *name, struct fsdriver_node *node,
+ int *is_mountpt);
+ int (*fdr_newnode)(mode_t mode, uid_t uid, gid_t gid, dev_t dev,
+ struct fsdriver_node *node);
+ int (*fdr_putnode)(ino_t ino_nr, unsigned int count);
+ ssize_t (*fdr_read)(ino_t ino_nr, struct fsdriver_data *data,
+ size_t bytes, off_t pos, int call);
+ ssize_t (*fdr_write)(ino_t ino_nr, struct fsdriver_data *data,
+ size_t bytes, off_t pos, int call);
+ ssize_t (*fdr_peek)(ino_t ino_nr, struct fsdriver_data *data,
+ size_t bytes, off_t pos, int call);
+ ssize_t (*fdr_getdents)(ino_t ino_nr, struct fsdriver_data *data,
+ size_t bytes, off_t *pos);
+ int (*fdr_trunc)(ino_t ino_nr, off_t start_pos, off_t end_pos);
+ void (*fdr_seek)(ino_t ino);
+ int (*fdr_create)(ino_t dir_nr, char *name, mode_t mode, uid_t uid,
+ gid_t gid, struct fsdriver_node *node);
+ int (*fdr_mkdir)(ino_t dir_nr, char *name, mode_t mode, uid_t uid,
+ gid_t gid);
+ int (*fdr_mknod)(ino_t dir_nr, char *name, mode_t mode, uid_t uid,
+ gid_t gid, dev_t rdev);
+ int (*fdr_link)(ino_t dir_nr, char *name, ino_t ino_nr);
+ int (*fdr_unlink)(ino_t dir_nr, char *name, int call);
+ int (*fdr_rmdir)(ino_t dir_nr, char *name, int call);
+ int (*fdr_rename)(ino_t old_dir_nr, char *old_name, ino_t new_dir_nr,
+ char *new_name);
+ int (*fdr_slink)(ino_t dir_nr, char *name, uid_t uid, gid_t gid,
+ struct fsdriver_data *data, size_t bytes);
+ ssize_t (*fdr_rdlink)(ino_t ino_nr, struct fsdriver_data *data,
+ size_t bytes);
+ int (*fdr_stat)(ino_t ino_nr, struct stat *buf);
+ int (*fdr_chown)(ino_t ino_nr, uid_t uid, gid_t gid, mode_t *mode);
+ int (*fdr_chmod)(ino_t ino_nr, mode_t *mode);
+ int (*fdr_utime)(ino_t ino_nr, struct timespec *atime,
+ struct timespec *mtime);
+ int (*fdr_mountpt)(ino_t ino_nr);
+ int (*fdr_statvfs)(struct statvfs *buf);
+ void (*fdr_sync)(void);
+ void (*fdr_driver)(dev_t dev, char *label);
+ ssize_t (*fdr_bread)(dev_t dev, struct fsdriver_data *data,
+ size_t bytes, off_t pos, int call);
+ ssize_t (*fdr_bwrite)(dev_t dev, struct fsdriver_data *data,
+ size_t bytes, off_t pos, int call);
+ ssize_t (*fdr_bpeek)(dev_t dev, struct fsdriver_data *data,
+ size_t bytes, off_t pos, int call);
+ void (*fdr_bflush)(dev_t dev);
+ void (*fdr_postcall)(void);
+ void (*fdr_other)(const message *m_ptr, int ipc_status);
+};
+
+/* Functions defined by libfsdriver. */
+void fsdriver_process(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_ptr, int ipc_status, int asyn_reply);
+void fsdriver_terminate(void);
+void fsdriver_task(struct fsdriver *fdp);
+
+int fsdriver_copyin(const struct fsdriver_data *data, size_t off, void *ptr,
+ size_t len);
+int fsdriver_copyout(const struct fsdriver_data *data, size_t off,
+ const void *ptr, size_t len);
+int fsdriver_zero(const struct fsdriver_data *data, size_t off, size_t len);
+
+void fsdriver_dentry_init(struct fsdriver_dentry * __restrict dentry,
+ const struct fsdriver_data * __restrict data, size_t bytes,
+ char * __restrict buf, size_t bufsize);
+ssize_t fsdriver_dentry_add(struct fsdriver_dentry * __restrict dentry,
+ ino_t ino_nr, const char * __restrict name, size_t namelen,
+ unsigned int type);
+ssize_t fsdriver_dentry_finish(struct fsdriver_dentry *dentry);
+
+#endif /* !_MINIX_FSDRIVER_H */
SUBDIR+= libdevman
SUBDIR+= libexec
SUBDIR+= libfetch
+SUBDIR+= libfsdriver
SUBDIR+= libinputdriver
SUBDIR+= libminc
SUBDIR+= libminixfs
--- /dev/null
+# Makefile for libfsdriver
+
+CPPFLAGS+= -D_MINIX_SYSTEM
+
+LIB= fsdriver
+
+SRCS= call.c dentry.c fsdriver.c lookup.c table.c utility.c
+
+.include <bsd.lib.mk>
--- /dev/null
+
+#include "fsdriver.h"
+#include <minix/ds.h>
+
+/*
+ * Process a READSUPER request from VFS.
+ */
+int
+fsdriver_readsuper(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ struct fsdriver_node root_node;
+ char label[DS_MAX_KEYLEN];
+ cp_grant_id_t label_grant;
+ size_t label_len;
+ unsigned int flags, res_flags;
+ dev_t dev;
+ int r;
+
+ dev = m_in->m_vfs_fs_readsuper.device;
+ label_grant = m_in->m_vfs_fs_readsuper.grant;
+ label_len = m_in->m_vfs_fs_readsuper.path_len;
+ flags = m_in->m_vfs_fs_readsuper.flags;
+
+ if (fdp->fdr_mount == NULL)
+ return ENOSYS;
+
+ if (fsdriver_mounted) {
+ printf("fsdriver: attempt to mount multiple times\n");
+ return EBUSY;
+ }
+
+ if ((r = fsdriver_getname(m_in->m_source, label_grant, label_len,
+ label, sizeof(label), FALSE /*not_empty*/)) != OK)
+ return r;
+
+ if (fdp->fdr_driver != NULL)
+ fdp->fdr_driver(dev, label);
+
+ res_flags = RES_NOFLAGS;
+
+ r = fdp->fdr_mount(dev, flags, &root_node, &res_flags);
+
+ if (r == OK) {
+ /* This one we can set on the file system's behalf. */
+ if (fdp->fdr_peek != NULL && fdp->fdr_bpeek != NULL)
+ res_flags |= RES_HASPEEK;
+
+ m_out->m_fs_vfs_readsuper.inode = root_node.fn_ino_nr;
+ m_out->m_fs_vfs_readsuper.mode = root_node.fn_mode;
+ m_out->m_fs_vfs_readsuper.file_size = root_node.fn_size;
+ m_out->m_fs_vfs_readsuper.uid = root_node.fn_uid;
+ m_out->m_fs_vfs_readsuper.gid = root_node.fn_gid;
+ m_out->m_fs_vfs_readsuper.flags = res_flags;
+
+ /* Update library-local state. */
+ fsdriver_mounted = TRUE;
+ fsdriver_root = root_node.fn_ino_nr;
+ }
+
+ return r;
+}
+
+/*
+ * Process an UNMOUNT request from VFS.
+ */
+int
+fsdriver_unmount(const struct fsdriver * __restrict fdp,
+ const message * __restrict __unused m_in,
+ message * __restrict __unused m_out)
+{
+
+ if (fdp->fdr_unmount != NULL)
+ fdp->fdr_unmount();
+
+ /* Update library-local state. */
+ fsdriver_mounted = FALSE;
+
+ return OK;
+}
+
+/*
+ * Process a PUTNODE request from VFS.
+ */
+int
+fsdriver_putnode(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ ino_t ino_nr;
+ unsigned int count;
+
+ ino_nr = m_in->m_vfs_fs_putnode.inode;
+ count = m_in->m_vfs_fs_putnode.count;
+
+ if (fdp->fdr_putnode == NULL)
+ return ENOSYS;
+
+ if (count == 0 || count > INT_MAX) {
+ printf("fsdriver: invalid reference count\n");
+ return EINVAL;
+ }
+
+ return fdp->fdr_putnode(ino_nr, count);
+}
+
+/*
+ * Process a NEWNODE request from VFS.
+ */
+int
+fsdriver_newnode(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ struct fsdriver_node node;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t dev;
+ int r;
+
+ mode = m_in->m_vfs_fs_newnode.mode;
+ uid = m_in->m_vfs_fs_newnode.uid;
+ gid = m_in->m_vfs_fs_newnode.gid;
+ dev = m_in->m_vfs_fs_newnode.device;
+
+ if (fdp->fdr_newnode == NULL)
+ return ENOSYS;
+
+ if ((r = fdp->fdr_newnode(mode, uid, gid, dev, &node)) == OK) {
+ m_out->m_fs_vfs_newnode.inode = node.fn_ino_nr;
+ m_out->m_fs_vfs_newnode.mode = node.fn_mode;
+ m_out->m_fs_vfs_newnode.file_size = node.fn_size;
+ m_out->m_fs_vfs_newnode.uid = node.fn_uid;
+ m_out->m_fs_vfs_newnode.gid = node.fn_gid;
+ m_out->m_fs_vfs_newnode.device = node.fn_dev;
+ }
+
+ return r;
+}
+
+/*
+ * Process a read or write request from VFS.
+ */
+static int
+read_write(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out, int call)
+{
+ struct fsdriver_data data;
+ ino_t ino_nr;
+ off_t pos;
+ size_t nbytes;
+ ssize_t r;
+
+ ino_nr = m_in->m_vfs_fs_readwrite.inode;
+ pos = m_in->m_vfs_fs_readwrite.seek_pos;
+ nbytes = m_in->m_vfs_fs_readwrite.nbytes;
+
+ if (pos < 0 || nbytes > SSIZE_MAX)
+ return EINVAL;
+
+ data.endpt = m_in->m_source;
+ data.grant = m_in->m_vfs_fs_readwrite.grant;
+ data.size = nbytes;
+
+ if (call == FSC_WRITE)
+ r = fdp->fdr_write(ino_nr, &data, nbytes, pos, call);
+ else
+ r = fdp->fdr_read(ino_nr, &data, nbytes, pos, call);
+
+ if (r >= 0) {
+ pos += r;
+
+ m_out->m_fs_vfs_readwrite.seek_pos = pos;
+ m_out->m_fs_vfs_readwrite.nbytes = r;
+ r = OK;
+ }
+
+ return r;
+}
+
+/*
+ * Process a READ request from VFS.
+ */
+int
+fsdriver_read(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+
+ if (fdp->fdr_read == NULL)
+ return ENOSYS;
+
+ return read_write(fdp, m_in, m_out, FSC_READ);
+}
+
+/*
+ * Process a WRITE request from VFS.
+ */
+int
+fsdriver_write(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+
+ if (fdp->fdr_write == NULL)
+ return ENOSYS;
+
+ return read_write(fdp, m_in, m_out, FSC_WRITE);
+}
+
+/*
+ * Process a PEEK request from VFS.
+ */
+int
+fsdriver_peek(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ ino_t ino_nr;
+ off_t pos;
+ size_t nbytes;
+ ssize_t r;
+
+ ino_nr = m_in->m_vfs_fs_readwrite.inode;
+ pos = m_in->m_vfs_fs_readwrite.seek_pos;
+ nbytes = m_in->m_vfs_fs_readwrite.nbytes;
+
+ if (fdp->fdr_peek == NULL)
+ return ENOSYS;
+
+ if (pos < 0 || nbytes > SSIZE_MAX)
+ return EINVAL;
+
+ r = fdp->fdr_peek(ino_nr, NULL /*data*/, nbytes, pos, FSC_PEEK);
+
+ /* Do not return a new position. */
+ if (r >= 0) {
+ m_out->m_fs_vfs_readwrite.nbytes = r;
+ r = OK;
+ }
+
+ return r;
+}
+
+/*
+ * Process a GETDENTS request from VFS.
+ */
+int
+fsdriver_getdents(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ struct fsdriver_data data;
+ ino_t ino_nr;
+ off_t pos;
+ size_t nbytes;
+ ssize_t r;
+
+ ino_nr = m_in->m_vfs_fs_getdents.inode;
+ pos = m_in->m_vfs_fs_getdents.seek_pos;
+ nbytes = m_in->m_vfs_fs_getdents.mem_size;
+
+ if (fdp->fdr_getdents == NULL)
+ return ENOSYS;
+
+ if (pos < 0 || nbytes > SSIZE_MAX)
+ return EINVAL;
+
+ data.endpt = m_in->m_source;
+ data.grant = m_in->m_vfs_fs_getdents.grant;
+ data.size = nbytes;
+
+ r = fdp->fdr_getdents(ino_nr, &data, nbytes, &pos);
+
+ if (r >= 0) {
+ m_out->m_fs_vfs_getdents.seek_pos = pos;
+ m_out->m_fs_vfs_getdents.nbytes = r;
+ r = OK;
+ }
+
+ return r;
+}
+
+/*
+ * Process a FTRUNC request from VFS.
+ */
+int
+fsdriver_trunc(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ ino_t ino_nr;
+ off_t start_pos, end_pos;
+
+ ino_nr = m_in->m_vfs_fs_ftrunc.inode;
+ start_pos = m_in->m_vfs_fs_ftrunc.trc_start;
+ end_pos = m_in->m_vfs_fs_ftrunc.trc_end;
+
+ if (start_pos < 0 || end_pos < 0)
+ return EINVAL;
+
+ if (fdp->fdr_trunc == NULL)
+ return ENOSYS;
+
+ return fdp->fdr_trunc(ino_nr, start_pos, end_pos);
+}
+
+/*
+ * Process a INHIBREAD request from VFS.
+ */
+int
+fsdriver_inhibread(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ ino_t ino_nr;
+
+ ino_nr = m_in->m_vfs_fs_inhibread.inode;
+
+ if (fdp->fdr_seek != NULL)
+ fdp->fdr_seek(ino_nr);
+
+ return OK;
+}
+
+/*
+ * Process a CREATE request from VFS.
+ */
+int
+fsdriver_create(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ struct fsdriver_node node;
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t len;
+ ino_t dir_nr;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int r;
+
+ grant = m_in->m_vfs_fs_create.grant;
+ len = m_in->m_vfs_fs_create.path_len;
+ dir_nr = m_in->m_vfs_fs_create.inode;
+ mode = m_in->m_vfs_fs_create.mode;
+ uid = m_in->m_vfs_fs_create.uid;
+ gid = m_in->m_vfs_fs_create.gid;
+
+ if (fdp->fdr_create == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return EEXIST;
+
+ if ((r = fdp->fdr_create(dir_nr, name, mode, uid, gid, &node)) == OK) {
+ m_out->m_fs_vfs_create.inode = node.fn_ino_nr;
+ m_out->m_fs_vfs_create.mode = node.fn_mode;
+ m_out->m_fs_vfs_create.file_size = node.fn_size;
+ m_out->m_fs_vfs_create.uid = node.fn_uid;
+ m_out->m_fs_vfs_create.gid = node.fn_gid;
+ }
+
+ return r;
+}
+
+/*
+ * Process a MKDIR request from VFS.
+ */
+int
+fsdriver_mkdir(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t path_len;
+ ino_t dir_nr;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int r;
+
+ grant = m_in->m_vfs_fs_mkdir.grant;
+ path_len = m_in->m_vfs_fs_mkdir.path_len;
+ dir_nr = m_in->m_vfs_fs_mkdir.inode;
+ mode = m_in->m_vfs_fs_mkdir.mode;
+ uid = m_in->m_vfs_fs_mkdir.uid;
+ gid = m_in->m_vfs_fs_mkdir.gid;
+
+ if (fdp->fdr_mkdir == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return EEXIST;
+
+ return fdp->fdr_mkdir(dir_nr, name, mode, uid, gid);
+}
+
+/*
+ * Process a MKNOD request from VFS.
+ */
+int
+fsdriver_mknod(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t path_len;
+ ino_t dir_nr;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t dev;
+ int r;
+
+ grant = m_in->m_vfs_fs_mknod.grant;
+ path_len = m_in->m_vfs_fs_mknod.path_len;
+ dir_nr = m_in->m_vfs_fs_mknod.inode;
+ mode = m_in->m_vfs_fs_mknod.mode;
+ uid = m_in->m_vfs_fs_mknod.uid;
+ gid = m_in->m_vfs_fs_mknod.gid;
+ dev = m_in->m_vfs_fs_mknod.device;
+
+ if (fdp->fdr_mknod == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return EEXIST;
+
+ return fdp->fdr_mknod(dir_nr, name, mode, uid, gid, dev);
+}
+
+/*
+ * Process a LINK request from VFS.
+ */
+int
+fsdriver_link(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t path_len;
+ ino_t dir_nr, ino_nr;
+ int r;
+
+ grant = m_in->m_vfs_fs_link.grant;
+ path_len = m_in->m_vfs_fs_link.path_len;
+ dir_nr = m_in->m_vfs_fs_link.dir_ino;
+ ino_nr = m_in->m_vfs_fs_link.inode;
+
+ if (fdp->fdr_link == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return EEXIST;
+
+ return fdp->fdr_link(dir_nr, name, ino_nr);
+}
+
+/*
+ * Process an UNLINK request from VFS.
+ */
+int
+fsdriver_unlink(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t path_len;
+ ino_t dir_nr;
+ int r;
+
+ grant = m_in->m_vfs_fs_unlink.grant;
+ path_len = m_in->m_vfs_fs_unlink.path_len;
+ dir_nr = m_in->m_vfs_fs_unlink.inode;
+
+ if (fdp->fdr_unlink == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return EPERM;
+
+ return fdp->fdr_unlink(dir_nr, name, FSC_UNLINK);
+}
+
+/*
+ * Process a RMDIR request from VFS.
+ */
+int
+fsdriver_rmdir(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t path_len;
+ ino_t dir_nr;
+ int r;
+
+ grant = m_in->m_vfs_fs_unlink.grant;
+ path_len = m_in->m_vfs_fs_unlink.path_len;
+ dir_nr = m_in->m_vfs_fs_unlink.inode;
+
+ if (fdp->fdr_rmdir == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, "."))
+ return EINVAL;
+
+ if (!strcmp(name, ".."))
+ return ENOTEMPTY;
+
+ return fdp->fdr_rmdir(dir_nr, name, FSC_RMDIR);
+}
+
+/*
+ * Process a RENAME request from VFS.
+ */
+int
+fsdriver_rename(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
+ cp_grant_id_t old_grant, new_grant;
+ size_t old_len, new_len;
+ ino_t old_dir_nr, new_dir_nr;
+ int r;
+
+ old_grant = m_in->m_vfs_fs_rename.grant_old;
+ old_len = m_in->m_vfs_fs_rename.len_old;
+ old_dir_nr = m_in->m_vfs_fs_rename.dir_old;
+ new_grant = m_in->m_vfs_fs_rename.grant_new;
+ new_len = m_in->m_vfs_fs_rename.len_new;
+ new_dir_nr = m_in->m_vfs_fs_rename.dir_new;
+
+ if (fdp->fdr_rename == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, old_grant, old_len, old_name,
+ sizeof(old_name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(old_name, ".") || !strcmp(old_name, ".."))
+ return EINVAL;
+
+ if ((r = fsdriver_getname(m_in->m_source, new_grant, new_len, new_name,
+ sizeof(new_name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(new_name, ".") || !strcmp(new_name, ".."))
+ return EINVAL;
+
+ return fdp->fdr_rename(old_dir_nr, old_name, new_dir_nr, new_name);
+}
+
+/*
+ * Process a SLINK request from VFS.
+ */
+int
+fsdriver_slink(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ struct fsdriver_data data;
+ char name[NAME_MAX+1];
+ cp_grant_id_t grant;
+ size_t path_len;
+ ino_t dir_nr;
+ uid_t uid;
+ gid_t gid;
+ int r;
+
+ grant = m_in->m_vfs_fs_slink.grant_path;
+ path_len = m_in->m_vfs_fs_slink.path_len;
+ dir_nr = m_in->m_vfs_fs_slink.inode;
+ uid = m_in->m_vfs_fs_slink.uid;
+ gid = m_in->m_vfs_fs_slink.gid;
+
+ if (fdp->fdr_slink == NULL)
+ return ENOSYS;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, name,
+ sizeof(name), TRUE /*not_empty*/)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return EEXIST;
+
+ data.endpt = m_in->m_source;
+ data.grant = m_in->m_vfs_fs_slink.grant_target;
+ data.size = m_in->m_vfs_fs_slink.mem_size;
+
+ return fdp->fdr_slink(dir_nr, name, uid, gid, &data, data.size);
+}
+
+/*
+ * Process a RDLINK request from VFS.
+ */
+int
+fsdriver_rdlink(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ struct fsdriver_data data;
+ ssize_t r;
+
+ if (fdp->fdr_rdlink == NULL)
+ return ENOSYS;
+
+ data.endpt = m_in->m_source;
+ data.grant = m_in->m_vfs_fs_rdlink.grant;
+ data.size = m_in->m_vfs_fs_rdlink.mem_size;
+
+ r = fdp->fdr_rdlink(m_in->m_vfs_fs_rdlink.inode, &data, data.size);
+
+ if (r >= 0) {
+ m_out->m_fs_vfs_rdlink.nbytes = r;
+ r = OK;
+ }
+
+ return r;
+}
+
+/*
+ * Process a STAT request from VFS.
+ */
+int
+fsdriver_stat(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ struct stat buf;
+ cp_grant_id_t grant;
+ ino_t ino_nr;
+ int r;
+
+ ino_nr = m_in->m_vfs_fs_stat.inode;
+ grant = m_in->m_vfs_fs_stat.grant;
+
+ if (fdp->fdr_stat == NULL)
+ return ENOSYS;
+
+ memset(&buf, 0, sizeof(buf));
+
+ if ((r = fdp->fdr_stat(ino_nr, &buf)) == OK)
+ r = sys_safecopyto(m_in->m_source, grant, 0, (vir_bytes)&buf,
+ (phys_bytes)sizeof(buf));
+
+ return r;
+}
+
+/*
+ * Process a CHOWN request from VFS.
+ */
+int
+fsdriver_chown(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ ino_t ino_nr;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ int r;
+
+ ino_nr = m_in->m_vfs_fs_chown.inode;
+ uid = m_in->m_vfs_fs_chown.uid;
+ gid = m_in->m_vfs_fs_chown.gid;
+
+ if (fdp->fdr_chown == NULL)
+ return ENOSYS;
+
+ if ((r = fdp->fdr_chown(ino_nr, uid, gid, &mode)) == OK)
+ m_out->m_fs_vfs_chown.mode = mode;
+
+ return r;
+}
+
+/*
+ * Process a CHMOD request from VFS.
+ */
+int
+fsdriver_chmod(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ ino_t ino_nr;
+ mode_t mode;
+ int r;
+
+ ino_nr = m_in->m_vfs_fs_chmod.inode;
+ mode = m_in->m_vfs_fs_chmod.mode;
+
+ if (fdp->fdr_chmod == NULL)
+ return ENOSYS;
+
+ if ((r = fdp->fdr_chmod(ino_nr, &mode)) == OK)
+ m_out->m_fs_vfs_chmod.mode = mode;
+
+ return r;
+}
+
+/*
+ * Process a UTIME request from VFS.
+ */
+int
+fsdriver_utime(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ ino_t ino_nr;
+ struct timespec atime, mtime;
+
+ ino_nr = m_in->m_vfs_fs_utime.inode;
+ atime.tv_sec = m_in->m_vfs_fs_utime.actime;
+ atime.tv_nsec = m_in->m_vfs_fs_utime.acnsec;
+ mtime.tv_sec = m_in->m_vfs_fs_utime.modtime;
+ mtime.tv_nsec = m_in->m_vfs_fs_utime.modnsec;
+
+ if (fdp->fdr_utime == NULL)
+ return ENOSYS;
+
+ return fdp->fdr_utime(ino_nr, &atime, &mtime);
+}
+
+/*
+ * Process a MOUNTPOINT request from VFS.
+ */
+int
+fsdriver_mountpoint(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ ino_t ino_nr;
+
+ ino_nr = m_in->m_vfs_fs_mountpoint.inode;
+
+ if (fdp->fdr_mountpt == NULL)
+ return ENOSYS;
+
+ return fdp->fdr_mountpt(ino_nr);
+}
+
+/*
+ * Process a STATVFS request from VFS.
+ */
+int
+fsdriver_statvfs(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ struct statvfs buf;
+ int r;
+
+ if (fdp->fdr_statvfs == NULL)
+ return ENOSYS;
+
+ memset(&buf, 0, sizeof(buf));
+
+ if ((r = fdp->fdr_statvfs(&buf)) != OK)
+ return r;
+
+ return sys_safecopyto(m_in->m_source, m_in->m_vfs_fs_statvfs.grant, 0,
+ (vir_bytes)&buf, (phys_bytes)sizeof(buf));
+}
+
+/*
+ * Process a SYNC request from VFS.
+ */
+int
+fsdriver_sync(const struct fsdriver * __restrict fdp,
+ const message * __restrict __unused m_in,
+ message * __restrict __unused m_out)
+{
+
+ if (fdp->fdr_sync != NULL)
+ fdp->fdr_sync();
+
+ return OK;
+}
+
+/*
+ * Process a NEW_DRIVER request from VFS.
+ */
+int
+fsdriver_newdriver(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ char label[DS_MAX_KEYLEN];
+ cp_grant_id_t grant;
+ size_t path_len;
+ dev_t dev;
+ int r;
+
+ dev = m_in->m_vfs_fs_new_driver.device;
+ grant = m_in->m_vfs_fs_new_driver.grant;
+ path_len = m_in->m_vfs_fs_new_driver.path_len;
+
+ if (fdp->fdr_driver == NULL)
+ return OK;
+
+ if ((r = fsdriver_getname(m_in->m_source, grant, path_len, label,
+ sizeof(label), FALSE /*not_empty*/)) != OK)
+ return r;
+
+ fdp->fdr_driver(dev, label);
+
+ return OK;
+}
+
+/*
+ * Process a block read or write request from VFS.
+ */
+static ssize_t
+bread_bwrite(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out, int call)
+{
+ struct fsdriver_data data;
+ dev_t dev;
+ off_t pos;
+ size_t nbytes;
+ ssize_t r;
+
+ dev = m_in->m_vfs_fs_breadwrite.device;
+ pos = m_in->m_vfs_fs_breadwrite.seek_pos;
+ nbytes = m_in->m_vfs_fs_breadwrite.nbytes;
+
+ if (pos < 0 || nbytes > SSIZE_MAX)
+ return EINVAL;
+
+ data.endpt = m_in->m_source;
+ data.grant = m_in->m_vfs_fs_breadwrite.grant;
+ data.size = nbytes;
+
+ if (call == FSC_WRITE)
+ r = fdp->fdr_bwrite(dev, &data, nbytes, pos, call);
+ else
+ r = fdp->fdr_bread(dev, &data, nbytes, pos, call);
+
+ if (r >= 0) {
+ pos += r;
+
+ m_out->m_fs_vfs_breadwrite.seek_pos = pos;
+ m_out->m_fs_vfs_breadwrite.nbytes = r;
+ r = OK;
+ }
+
+ return r;
+}
+
+/*
+ * Process a BREAD request from VFS.
+ */
+ssize_t
+fsdriver_bread(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+
+ if (fdp->fdr_bread == NULL)
+ return ENOSYS;
+
+ return bread_bwrite(fdp, m_in, m_out, FSC_READ);
+}
+
+/*
+ * Process a BWRITE request from VFS.
+ */
+ssize_t
+fsdriver_bwrite(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+
+ if (fdp->fdr_bwrite == NULL)
+ return ENOSYS;
+
+ return bread_bwrite(fdp, m_in, m_out, FSC_WRITE);
+}
+
+/*
+ * Process a BPEEK request from VFS.
+ */
+int
+fsdriver_bpeek(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ dev_t dev;
+ off_t pos;
+ size_t nbytes;
+ ssize_t r;
+
+ dev = m_in->m_vfs_fs_breadwrite.device;
+ pos = m_in->m_vfs_fs_breadwrite.seek_pos;
+ nbytes = m_in->m_vfs_fs_breadwrite.nbytes;
+
+ if (fdp->fdr_bpeek == NULL)
+ return ENOSYS;
+
+ if (pos < 0 || nbytes > SSIZE_MAX)
+ return EINVAL;
+
+ r = fdp->fdr_bpeek(dev, NULL /*data*/, nbytes, pos, FSC_PEEK);
+
+ /* Do not return a new position. */
+ if (r >= 0) {
+ m_out->m_fs_vfs_breadwrite.nbytes = r;
+ r = OK;
+ }
+
+ return r;
+}
+
+/*
+ * Process a FLUSH request from VFS.
+ */
+int
+fsdriver_flush(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict __unused m_out)
+{
+ dev_t dev;
+
+ dev = m_in->m_vfs_fs_flush.device;
+
+ if (fdp->fdr_bflush != NULL)
+ fdp->fdr_bflush(dev);
+
+ return OK;
+}
--- /dev/null
+
+#include "fsdriver.h"
+#include <sys/dirent.h>
+
+/*
+ * Initialize a directory entry listing.
+ */
+void
+fsdriver_dentry_init(struct fsdriver_dentry * __restrict dentry,
+ const struct fsdriver_data * __restrict data, size_t bytes,
+ char * __restrict buf, size_t bufsize)
+{
+
+ dentry->data = data;
+ dentry->data_size = bytes;
+ dentry->data_off = 0;
+ dentry->buf = buf;
+ dentry->buf_size = bufsize;
+ dentry->buf_off = 0;
+}
+
+/*
+ * Add an entry to a directory entry listing. Return the entry size if it was
+ * added, zero if no more entries could be added and the listing should stop,
+ * or an error code in case of an error.
+ */
+ssize_t
+fsdriver_dentry_add(struct fsdriver_dentry * __restrict dentry, ino_t ino_nr,
+ const char * __restrict name, size_t namelen, unsigned int type)
+{
+ struct dirent *dirent;
+ size_t len, used;
+ int r;
+
+ /* We could do several things here, but it should never happen.. */
+ if (namelen > MAXNAMLEN)
+ panic("fsdriver: directory entry name excessively long");
+
+ len = _DIRENT_RECLEN(dirent, namelen);
+
+ if (dentry->data_off + dentry->buf_off + len > dentry->data_size) {
+ if (dentry->data_off == 0 && dentry->buf_off == 0)
+ return EINVAL;
+
+ return 0;
+ }
+
+ if (dentry->buf_off + len > dentry->buf_size) {
+ if (dentry->buf_off == 0)
+ panic("fsdriver: getdents buffer too small");
+
+ if ((r = fsdriver_copyout(dentry->data, dentry->data_off,
+ dentry->buf, dentry->buf_off)) != OK)
+ return r;
+
+ dentry->data_off += dentry->buf_off;
+ dentry->buf_off = 0;
+ }
+
+ dirent = (struct dirent *)&dentry->buf[dentry->buf_off];
+ dirent->d_fileno = ino_nr;
+ dirent->d_reclen = len;
+ dirent->d_namlen = namelen;
+ dirent->d_type = type;
+ memcpy(dirent->d_name, name, namelen);
+
+ /*
+ * Null-terminate the name, and zero out any alignment bytes after it,
+ * so as not to leak any data.
+ */
+ used = _DIRENT_NAMEOFF(dirent) + namelen;
+ if (used >= len)
+ panic("fsdriver: inconsistency in dirent record");
+ memset(&dirent->d_name[namelen], 0, len - used);
+
+ dentry->buf_off += len;
+
+ return len;
+}
+
+/*
+ * Finish a directory entry listing operation. Return the total number of
+ * bytes copied to the caller, or an error code in case of an error.
+ */
+ssize_t
+fsdriver_dentry_finish(struct fsdriver_dentry *dentry)
+{
+ int r;
+
+ if (dentry->buf_off > 0) {
+ if ((r = fsdriver_copyout(dentry->data, dentry->data_off,
+ dentry->buf, dentry->buf_off)) != OK)
+ return r;
+
+ dentry->data_off += dentry->buf_off;
+ }
+
+ return dentry->data_off;
+}
--- /dev/null
+
+#include "fsdriver.h"
+
+/* Library-local variables. */
+ino_t fsdriver_root;
+int fsdriver_mounted = FALSE;
+
+static int fsdriver_running;
+
+/*
+ * Process an incoming VFS request, and send a reply. If the message is not
+ * a file system request from VFS, pass it on to the generic message handler.
+ * Multithreaded file systems should indicate that the reply is to be sent to
+ * VFS asynchronously.
+ */
+void
+fsdriver_process(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_ptr, int ipc_status, int asyn_reply)
+{
+ message m_out;
+ unsigned int call_nr;
+ int r, transid;
+
+ /* Is this a file system request at all? */
+ if (is_ipc_notify(ipc_status) || m_ptr->m_source != VFS_PROC_NR) {
+ if (fdp->fdr_other != NULL)
+ fdp->fdr_other(m_ptr, ipc_status);
+
+ return; /* do not send a reply */
+ }
+
+ /* Call the appropriate function. */
+ transid = TRNS_GET_ID(m_ptr->m_type);
+ call_nr = TRNS_DEL_ID(m_ptr->m_type);
+
+ memset(&m_out, 0, sizeof(m_out));
+
+ if (fsdriver_mounted || call_nr == REQ_READSUPER) {
+ call_nr -= FS_BASE; /* unsigned; wrapping is intended */
+
+ if (call_nr < NREQS && fsdriver_callvec[call_nr] != NULL)
+ r = (fsdriver_callvec[call_nr])(fdp, m_ptr, &m_out);
+ else
+ r = ENOSYS;
+ } else
+ r = EINVAL;
+
+ /* Send a reply. */
+ m_out.m_type = TRNS_ADD_ID(r, transid);
+
+ if (asyn_reply)
+ r = asynsend(m_ptr->m_source, &m_out);
+ else
+ r = ipc_send(m_ptr->m_source, &m_out);
+
+ if (r != OK)
+ printf("fsdriver: sending reply failed (%d)\n", r);
+
+ if (fdp->fdr_postcall != NULL)
+ fdp->fdr_postcall();
+}
+
+/*
+ * Terminate the file server as soon as the file system has been unmounted.
+ */
+void
+fsdriver_terminate(void)
+{
+
+ fsdriver_running = FALSE;
+
+ sef_cancel();
+}
+
+/*
+ * Main program of any file server task.
+ */
+void
+fsdriver_task(struct fsdriver * fdp)
+{
+ message mess;
+ int r, ipc_status;
+
+ fsdriver_running = TRUE;
+
+ while (fsdriver_running || fsdriver_mounted) {
+ if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
+ if (r == EINTR)
+ continue; /* sef_cancel() was called */
+
+ panic("fsdriver: sef_receive_status failed: %d", r);
+ }
+
+ fsdriver_process(fdp, &mess, ipc_status, FALSE /*asyn_reply*/);
+ }
+}
--- /dev/null
+#ifndef _LIBFSDRIVER_FSDRIVER_H
+#define _LIBFSDRIVER_FSDRIVER_H
+
+#include <minix/drivers.h>
+#include <minix/fsdriver.h>
+#include <minix/vfsif.h>
+
+#define ROOT_UID 0 /* user ID of superuser */
+
+extern int fsdriver_putnode(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_slink(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_trunc(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_chown(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_chmod(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_inhibread(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_stat(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_utime(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_statvfs(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_bread(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_bwrite(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_unlink(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_rmdir(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_unmount(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_sync(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_newdriver(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_flush(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_read(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_write(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_mknod(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_mkdir(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_create(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_link(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_rename(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_lookup(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_mountpoint(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_readsuper(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_newnode(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_rdlink(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_getdents(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_peek(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+extern int fsdriver_bpeek(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+
+extern int fsdriver_getname(endpoint_t endpt, cp_grant_id_t grant, size_t len,
+ char *name, size_t size, int not_empty);
+
+extern ino_t fsdriver_root;
+extern int fsdriver_mounted;
+extern int (*fsdriver_callvec[])(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict);
+
+#endif /* !_LIBFSDRIVER_FSDRIVER_H */
--- /dev/null
+
+#include "fsdriver.h"
+
+/*
+ * Check whether the given node may be accessed as directory.
+ * Return OK or an appropriate error code.
+ */
+static int
+access_as_dir(struct fsdriver_node * __restrict node,
+ vfs_ucred_t * __restrict ucred)
+{
+ mode_t mask;
+ int i;
+
+ /* The file must be a directory to begin with. */
+ if (!S_ISDIR(node->fn_mode)) return ENOTDIR;
+
+ /* The root user may access anything at all. */
+ if (ucred->vu_uid == ROOT_UID) return OK;
+
+ /* Otherwise, the caller must have search access to the directory. */
+ if (ucred->vu_uid == node->fn_uid) mask = S_IXUSR;
+ else if (ucred->vu_gid == node->fn_gid) mask = S_IXGRP;
+ else {
+ mask = S_IXOTH;
+
+ for (i = 0; i < ucred->vu_ngroups; i++) {
+ if (ucred->vu_sgroups[i] == node->fn_gid) {
+ mask = S_IXGRP;
+
+ break;
+ }
+ }
+ }
+
+ return (node->fn_mode & mask) ? OK : EACCES;
+}
+
+/*
+ * Get the next path component from a path. Return the start and end of the
+ * component into the path, and store its name in a null-terminated buffer.
+ */
+static int
+next_name(char ** ptr, char ** start, char * __restrict name, size_t namesize)
+{
+ char *p;
+ unsigned int i;
+
+ /* Skip one or more path separator characters; they have no effect. */
+ for (p = *ptr; *p == '/'; p++);
+
+ *start = p;
+
+ if (*p) {
+ /*
+ * Copy as much of the name as possible, up to the next path
+ * separator. Return an error if the name does not fit.
+ */
+ for (i = 0; *p && *p != '/' && i < namesize; p++, i++)
+ name[i] = *p;
+
+ if (i >= namesize)
+ return ENAMETOOLONG;
+
+ name[i] = 0;
+ } else
+ /* An empty path component implies the current directory. */
+ strlcpy(name, ".", namesize);
+
+ /*
+ * Return a pointer to the first character not part of this component.
+ * This would typically be either the path separator or a null.
+ */
+ *ptr = p;
+ return OK;
+}
+
+/*
+ * Given a symbolic link, resolve and return the contents of the link, followed
+ * by the remaining part of the path that has not yet been resolved (the tail).
+ * Note that the tail points into the given destination buffer.
+ */
+static int
+resolve_link(const struct fsdriver * __restrict fdp, ino_t ino_nr, char * pptr,
+ size_t size, char * tail)
+{
+ struct fsdriver_data data;
+ char path[PATH_MAX];
+ ssize_t r;
+
+ data.endpt = SELF;
+ data.ptr = path;
+ data.size = sizeof(path) - 1;
+
+ /*
+ * Let the file system the symbolic link. Note that the resulting path
+ * is not null-terminated.
+ */
+ if ((r = fdp->fdr_rdlink(ino_nr, &data, data.size)) < 0)
+ return r;
+
+ /* Append the remaining part of the original path to be resolved. */
+ if (r + strlen(tail) >= sizeof(path))
+ return ENAMETOOLONG;
+
+ strlcpy(&path[r], tail, sizeof(path) - r);
+
+ /* Copy back the result to the original buffer. */
+ strlcpy(pptr, path, size);
+
+ return OK;
+}
+
+/*
+ * Process a LOOKUP request from VFS.
+ */
+int
+fsdriver_lookup(const struct fsdriver * __restrict fdp,
+ const message * __restrict m_in, message * __restrict m_out)
+{
+ ino_t dir_ino_nr, root_ino_nr;
+ struct fsdriver_node cur_node, next_node;
+ char path[PATH_MAX], name[NAME_MAX+1];
+ char *ptr, *last;
+ cp_grant_id_t path_grant;
+ vfs_ucred_t ucred;
+ unsigned int flags;
+ size_t path_len, path_size;
+ int r, r2, going_up, is_mountpt, symloop;
+
+ if (fdp->fdr_lookup == NULL)
+ return ENOSYS;
+
+ dir_ino_nr = m_in->m_vfs_fs_lookup.dir_ino;
+ root_ino_nr = m_in->m_vfs_fs_lookup.root_ino;
+ path_grant = m_in->m_vfs_fs_lookup.grant_path;
+ path_size = m_in->m_vfs_fs_lookup.path_size;
+ path_len = m_in->m_vfs_fs_lookup.path_len;
+ flags = m_in->m_vfs_fs_lookup.flags;
+
+ /* Fetch the path name. */
+ if ((r = fsdriver_getname(m_in->m_source, path_grant, path_len, path,
+ sizeof(path), FALSE /*not_empty*/)) != OK)
+ return r;
+
+ /* Fetch the caller's credentials. */
+ if (flags & PATH_GET_UCRED) {
+ if (m_in->m_vfs_fs_lookup.ucred_size != sizeof(ucred)) {
+ printf("fsdriver: bad credential structure\n");
+
+ return EINVAL;
+ }
+
+ if ((r = sys_safecopyfrom(m_in->m_source,
+ m_in->m_vfs_fs_lookup.grant_ucred, 0, (vir_bytes)&ucred,
+ (phys_bytes)m_in->m_vfs_fs_lookup.ucred_size)) != OK)
+ return r;
+ } else {
+ ucred.vu_uid = m_in->m_vfs_fs_lookup.uid;
+ ucred.vu_gid = m_in->m_vfs_fs_lookup.gid;
+ ucred.vu_ngroups = 0;
+ }
+
+ /* Start the actual lookup by referencing the starting inode. */
+ strlcpy(name, ".", sizeof(name)); /* allow a non-const argument */
+
+ r = fdp->fdr_lookup(dir_ino_nr, name, &cur_node, &is_mountpt);
+ if (r != OK)
+ return r;
+
+ symloop = 0;
+
+ /* Whenever we leave this loop, 'cur_node' holds a referenced inode. */
+ for (ptr = last = path; *ptr != 0; ) {
+ /*
+ * Get the next path component. The result is a non-empty
+ * string.
+ */
+ if ((r = next_name(&ptr, &last, name, sizeof(name))) != OK)
+ break;
+
+ if (is_mountpt) {
+ /*
+ * If we start off from a mount point, the next path
+ * component *must* cause us to go up. Anything else
+ * is a protocol violation.
+ */
+ if (strcmp(name, "..")) {
+ r = EINVAL;
+ break;
+ }
+ } else {
+ /*
+ * There is more path to process. That means that the
+ * current file is now being accessed as a directory.
+ * Check type and permissions.
+ */
+ if ((r = access_as_dir(&cur_node, &ucred)) != OK)
+ break;
+ }
+
+ /* A single-dot component resolves to the current directory. */
+ if (!strcmp(name, "."))
+ continue;
+
+ /* A dot-dot component resolves to the parent directory. */
+ going_up = !strcmp(name, "..");
+
+ if (going_up) {
+ /*
+ * The parent of the process's root directory is the
+ * same root directory. All processes have a root
+ * directory, so this check also covers the case of
+ * going up from the global system root directory.
+ */
+ if (cur_node.fn_ino_nr == root_ino_nr)
+ continue;
+
+ /*
+ * Going up from the file system's root directory means
+ * crossing mount points. As indicated, the root file
+ * system is already covered by the check above.
+ */
+ if (cur_node.fn_ino_nr == fsdriver_root) {
+ ptr = last;
+
+ r = ELEAVEMOUNT;
+ break;
+ }
+ }
+
+ /*
+ * Descend into a child node or go up to a parent node, by
+ * asking the actual file system to perform a one-step
+ * resolution. The result, if successful, is an open
+ * (referenced) inode.
+ */
+ if ((r = fdp->fdr_lookup(cur_node.fn_ino_nr, name, &next_node,
+ &is_mountpt)) != OK)
+ break;
+
+ /* Sanity check: a parent node must always be a directory. */
+ if (going_up && !S_ISDIR(next_node.fn_mode))
+ panic("fsdriver: ascending into nondirectory");
+
+ /*
+ * Perform symlink resolution, unless the symlink is the last
+ * path component and VFS is asking us not to resolve it.
+ */
+ if (S_ISLNK(next_node.fn_mode) &&
+ (*ptr || !(flags & PATH_RET_SYMLINK))) {
+ /*
+ * Resolve the symlink, and append the remaining
+ * unresolved part of the path.
+ */
+ if (++symloop < _POSIX_SYMLOOP_MAX)
+ r = resolve_link(fdp, next_node.fn_ino_nr,
+ path, sizeof(path), ptr);
+ else
+ r = ELOOP;
+
+ fdp->fdr_putnode(next_node.fn_ino_nr, 1);
+
+ if (r != OK)
+ break;
+
+ ptr = path;
+
+ /* If the symlink is absolute, return it to VFS. */
+ if (path[0] == '/') {
+ r = ESYMLINK;
+ break;
+ }
+
+ continue;
+ }
+
+ /* We have found a new node. Continue from this node. */
+ fdp->fdr_putnode(cur_node.fn_ino_nr, 1);
+
+ cur_node = next_node;
+
+ /*
+ * If the new node is a mount point, yield to another file
+ * system.
+ */
+ if (is_mountpt) {
+ r = EENTERMOUNT;
+ break;
+ }
+ }
+
+ /* For special redirection errors, we need to return extra details. */
+ if (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
+ /* Copy back the path if we resolved at least one symlink. */
+ if (symloop > 0) {
+ if ((path_len = strlen(path) + 1) > path_size)
+ return ENAMETOOLONG;
+
+ r2 = sys_safecopyto(m_in->m_source, path_grant, 0,
+ (vir_bytes)path, (phys_bytes)path_len);
+ } else
+ r2 = OK;
+
+ if (r2 == OK) {
+ m_out->m_fs_vfs_lookup.offset = (int)(ptr - path);
+ m_out->m_fs_vfs_lookup.symloop = symloop;
+
+ if (r == EENTERMOUNT)
+ m_out->m_fs_vfs_lookup.inode =
+ cur_node.fn_ino_nr;
+ } else
+ r = r2;
+ }
+
+ /*
+ * On success, leave the resulting file open and return its details.
+ * If an error occurred, close the file and return error information.
+ */
+ if (r == OK) {
+ m_out->m_fs_vfs_lookup.inode = cur_node.fn_ino_nr;
+ m_out->m_fs_vfs_lookup.mode = cur_node.fn_mode;
+ m_out->m_fs_vfs_lookup.file_size = cur_node.fn_size;
+ m_out->m_fs_vfs_lookup.uid = cur_node.fn_uid;
+ m_out->m_fs_vfs_lookup.gid = cur_node.fn_gid;
+ m_out->m_fs_vfs_lookup.device = cur_node.fn_dev;
+ } else
+ fdp->fdr_putnode(cur_node.fn_ino_nr, 1);
+
+ return r;
+}
--- /dev/null
+
+#include "fsdriver.h"
+
+#define CALL(n) [((n) - FS_BASE)]
+
+int (*fsdriver_callvec[NREQS])(const struct fsdriver * __restrict,
+ const message * __restrict, message * __restrict) = {
+ CALL(REQ_PUTNODE) = fsdriver_putnode,
+ CALL(REQ_SLINK) = fsdriver_slink,
+ CALL(REQ_FTRUNC) = fsdriver_trunc,
+ CALL(REQ_CHOWN) = fsdriver_chown,
+ CALL(REQ_CHMOD) = fsdriver_chmod,
+ CALL(REQ_INHIBREAD) = fsdriver_inhibread,
+ CALL(REQ_STAT) = fsdriver_stat,
+ CALL(REQ_UTIME) = fsdriver_utime,
+ CALL(REQ_STATVFS) = fsdriver_statvfs,
+ CALL(REQ_BREAD) = fsdriver_bread,
+ CALL(REQ_BWRITE) = fsdriver_bwrite,
+ CALL(REQ_UNLINK) = fsdriver_unlink,
+ CALL(REQ_RMDIR) = fsdriver_rmdir,
+ CALL(REQ_UNMOUNT) = fsdriver_unmount,
+ CALL(REQ_SYNC) = fsdriver_sync,
+ CALL(REQ_NEW_DRIVER) = fsdriver_newdriver,
+ CALL(REQ_FLUSH) = fsdriver_flush,
+ CALL(REQ_READ) = fsdriver_read,
+ CALL(REQ_WRITE) = fsdriver_write,
+ CALL(REQ_MKNOD) = fsdriver_mknod,
+ CALL(REQ_MKDIR) = fsdriver_mkdir,
+ CALL(REQ_CREATE) = fsdriver_create,
+ CALL(REQ_LINK) = fsdriver_link,
+ CALL(REQ_RENAME) = fsdriver_rename,
+ CALL(REQ_LOOKUP) = fsdriver_lookup,
+ CALL(REQ_MOUNTPOINT) = fsdriver_mountpoint,
+ CALL(REQ_READSUPER) = fsdriver_readsuper,
+ CALL(REQ_NEWNODE) = fsdriver_newnode,
+ CALL(REQ_RDLINK) = fsdriver_rdlink,
+ CALL(REQ_GETDENTS) = fsdriver_getdents,
+ CALL(REQ_PEEK) = fsdriver_peek,
+ CALL(REQ_BPEEK) = fsdriver_bpeek
+};
--- /dev/null
+
+#include "fsdriver.h"
+
+/*
+ * Copy data from the caller into the local address space.
+ */
+int
+fsdriver_copyin(const struct fsdriver_data * data, size_t off, void * ptr,
+ size_t len)
+{
+
+ /* Do nothing for peek requests. */
+ if (data == NULL)
+ return OK;
+
+ /* The data size field is used only for this integrity check. */
+ if (off + len > data->size)
+ panic("fsdriver: copy-in buffer overflow");
+
+ if (data->endpt == SELF) {
+ memcpy(ptr, &data->ptr[off], len);
+
+ return OK;
+ }
+
+ return sys_safecopyfrom(data->endpt, data->grant, off, (vir_bytes)ptr,
+ (phys_bytes)len);
+}
+
+/*
+ * Copy data from the local address space to the caller.
+ */
+int
+fsdriver_copyout(const struct fsdriver_data * data, size_t off,
+ const void * ptr, size_t len)
+{
+
+ /* Do nothing for peek requests. */
+ if (data == NULL)
+ return OK;
+
+ /* The data size field is used only for this integrity check. */
+ if (off + len > data->size)
+ panic("fsdriver: copy-out buffer overflow");
+
+ if (data->endpt == SELF) {
+ memcpy(&data->ptr[off], ptr, len);
+
+ return OK;
+ }
+
+ return sys_safecopyto(data->endpt, data->grant, off, (vir_bytes)ptr,
+ (phys_bytes)len);
+}
+
+/*
+ * Zero out a data region in the caller.
+ */
+int
+fsdriver_zero(const struct fsdriver_data * data, size_t off, size_t len)
+{
+
+ /* Do nothing for peek requests. */
+ if (data == NULL)
+ return OK;
+
+ /* The data size field is used only for this integrity check. */
+ if (off + len > data->size)
+ panic("fsdriver: copy-out buffer overflow");
+
+ if (data->endpt == SELF) {
+ memset(&data->ptr[off], 0, len);
+
+ return OK;
+ }
+
+ return sys_safememset(data->endpt, data->grant, off, 0, len);
+}
+
+/*
+ * Copy in a null-terminated name, and perform sanity checks.
+ */
+int
+fsdriver_getname(endpoint_t endpt, cp_grant_id_t grant, size_t len,
+ char * name, size_t size, int not_empty)
+{
+ int r;
+
+ /* The given length includes the null terminator. */
+ if (len == 0 || (not_empty && len == 1))
+ return EINVAL;
+ if (len > size)
+ return ENAMETOOLONG;
+
+ if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes)name,
+ (phys_bytes)len)) != OK)
+ return r;
+
+ if (name[len - 1] != 0) {
+ printf("fsdriver: name not null-terminated\n");
+ return EINVAL;
+ }
+
+ return OK;
+}
devman \
elf \
exec \
+ fsdriver \
gpio \
hgfs \
i2cdriver \