From 89c9de7d091f384bd4337bd6775fb15c93b8e8c6 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Sun, 24 Aug 2014 09:27:54 +0000 Subject: [PATCH] Add libfsdriver: a library to drive file systems This library provides new abstractions for the upper (VFS) side of file system services, and should be used for all file system service implementations from now on. It provides the following functionality: - a function call table abstraction, hiding the details of the VFS-FS protocol with simple parameters; - a (currently limited) number of per-function steps required for all file system implementations, such as copying in and out path names and result buffers; - a default implementation for multicomponent path lookups, such that the file system merely has to implement resolution of single components at a time; - an abstraction for copying data from and to the file system, which allows transparent intraprocess copying as required for the lookup implementation; - a set of functions to simplify getdents implementations. The message loop provided by the library is currently for use by single-threaded file system implementations only. Multithreaded file system services may use the more low-level message processing functionality. Protocol-level optimizations such as including names in protocol messages may be hidden entirely in this library. In addition, in the future, the lookup implementation may be replaced by a single- component lookup VFS/FS protocol request as part of a VFS name cache implementation; this, too, can be hidden entirely in this library. Change-Id: Ib34f0d0e021dfa3426ce8826efcf3eaa94d3ef3e --- distrib/sets/lists/minix/mi | 3 + lib/Makefile | 1 + minix/include/minix/Makefile | 2 +- minix/include/minix/fsdriver.h | 127 +++++ minix/lib/Makefile | 1 + minix/lib/libfsdriver/Makefile | 9 + minix/lib/libfsdriver/call.c | 935 +++++++++++++++++++++++++++++++ minix/lib/libfsdriver/dentry.c | 99 ++++ minix/lib/libfsdriver/fsdriver.c | 96 ++++ minix/lib/libfsdriver/fsdriver.h | 83 +++ minix/lib/libfsdriver/lookup.c | 331 +++++++++++ minix/lib/libfsdriver/table.c | 40 ++ minix/lib/libfsdriver/utility.c | 105 ++++ share/mk/bsd.prog.mk | 1 + 14 files changed, 1832 insertions(+), 1 deletion(-) create mode 100644 minix/include/minix/fsdriver.h create mode 100644 minix/lib/libfsdriver/Makefile create mode 100644 minix/lib/libfsdriver/call.c create mode 100644 minix/lib/libfsdriver/dentry.c create mode 100644 minix/lib/libfsdriver/fsdriver.c create mode 100644 minix/lib/libfsdriver/fsdriver.h create mode 100644 minix/lib/libfsdriver/lookup.c create mode 100644 minix/lib/libfsdriver/table.c create mode 100644 minix/lib/libfsdriver/utility.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index a41727100..b20cf1976 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -1345,6 +1345,7 @@ ./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 @@ -2051,6 +2052,8 @@ ./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 diff --git a/lib/Makefile b/lib/Makefile index 65e3ae4ca..17a5e17ba 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -41,6 +41,7 @@ SUBDIR+= ../minix/lib/libasyn \ ../minix/lib/libdevman \ ../minix/lib/libexec \ ../minix/lib/libfetch \ + ../minix/lib/libfsdriver \ ../minix/lib/libinputdriver \ ../minix/lib/libminc \ ../minix/lib/libminixfs \ diff --git a/minix/include/minix/Makefile b/minix/include/minix/Makefile index f9d886cf5..bb255599f 100644 --- a/minix/include/minix/Makefile +++ b/minix/include/minix/Makefile @@ -10,7 +10,7 @@ INCS+= acpi.h audio_fw.h bitmap.h \ 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 \ diff --git a/minix/include/minix/fsdriver.h b/minix/include/minix/fsdriver.h new file mode 100644 index 000000000..4a55e35ba --- /dev/null +++ b/minix/include/minix/fsdriver.h @@ -0,0 +1,127 @@ +#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 */ diff --git a/minix/lib/Makefile b/minix/lib/Makefile index 40b0fc7da..752269a51 100644 --- a/minix/lib/Makefile +++ b/minix/lib/Makefile @@ -13,6 +13,7 @@ SUBDIR+= libddekit SUBDIR+= libdevman SUBDIR+= libexec SUBDIR+= libfetch +SUBDIR+= libfsdriver SUBDIR+= libinputdriver SUBDIR+= libminc SUBDIR+= libminixfs diff --git a/minix/lib/libfsdriver/Makefile b/minix/lib/libfsdriver/Makefile new file mode 100644 index 000000000..96624e1ea --- /dev/null +++ b/minix/lib/libfsdriver/Makefile @@ -0,0 +1,9 @@ +# Makefile for libfsdriver + +CPPFLAGS+= -D_MINIX_SYSTEM + +LIB= fsdriver + +SRCS= call.c dentry.c fsdriver.c lookup.c table.c utility.c + +.include diff --git a/minix/lib/libfsdriver/call.c b/minix/lib/libfsdriver/call.c new file mode 100644 index 000000000..8ebda1c29 --- /dev/null +++ b/minix/lib/libfsdriver/call.c @@ -0,0 +1,935 @@ + +#include "fsdriver.h" +#include + +/* + * 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; +} diff --git a/minix/lib/libfsdriver/dentry.c b/minix/lib/libfsdriver/dentry.c new file mode 100644 index 000000000..76d068bec --- /dev/null +++ b/minix/lib/libfsdriver/dentry.c @@ -0,0 +1,99 @@ + +#include "fsdriver.h" +#include + +/* + * 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; +} diff --git a/minix/lib/libfsdriver/fsdriver.c b/minix/lib/libfsdriver/fsdriver.c new file mode 100644 index 000000000..145ac5466 --- /dev/null +++ b/minix/lib/libfsdriver/fsdriver.c @@ -0,0 +1,96 @@ + +#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*/); + } +} diff --git a/minix/lib/libfsdriver/fsdriver.h b/minix/lib/libfsdriver/fsdriver.h new file mode 100644 index 000000000..7c83f7b75 --- /dev/null +++ b/minix/lib/libfsdriver/fsdriver.h @@ -0,0 +1,83 @@ +#ifndef _LIBFSDRIVER_FSDRIVER_H +#define _LIBFSDRIVER_FSDRIVER_H + +#include +#include +#include + +#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 */ diff --git a/minix/lib/libfsdriver/lookup.c b/minix/lib/libfsdriver/lookup.c new file mode 100644 index 000000000..b3bee1a90 --- /dev/null +++ b/minix/lib/libfsdriver/lookup.c @@ -0,0 +1,331 @@ + +#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; +} diff --git a/minix/lib/libfsdriver/table.c b/minix/lib/libfsdriver/table.c new file mode 100644 index 000000000..7009c4ba8 --- /dev/null +++ b/minix/lib/libfsdriver/table.c @@ -0,0 +1,40 @@ + +#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 +}; diff --git a/minix/lib/libfsdriver/utility.c b/minix/lib/libfsdriver/utility.c new file mode 100644 index 000000000..d0553df92 --- /dev/null +++ b/minix/lib/libfsdriver/utility.c @@ -0,0 +1,105 @@ + +#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; +} diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 64c807b77..633a2e9f6 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -222,6 +222,7 @@ LIB${_lib:tu}= ${DESTDIR}/usr/lib/lib${_lib:S/xx/++/:S/atf_c/atf-c/}.a devman \ elf \ exec \ + fsdriver \ gpio \ hgfs \ i2cdriver \ -- 2.44.0