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 \