diff options
author | ed <ed@FreeBSD.org> | 2015-07-28 06:36:49 +0000 |
---|---|---|
committer | ed <ed@FreeBSD.org> | 2015-07-28 06:36:49 +0000 |
commit | 734349761a341cf74cad5d38aa4afa0b3e4166e0 (patch) | |
tree | 68425b29eb6d3e6926ae7e7cc9b083ebd55cdabd /sys/compat | |
parent | db2fee092eabf3a1fc094f15b96c5a5d5fafe29a (diff) | |
download | FreeBSD-src-734349761a341cf74cad5d38aa4afa0b3e4166e0.zip FreeBSD-src-734349761a341cf74cad5d38aa4afa0b3e4166e0.tar.gz |
Make fstat() and friends work.
Summary:
CloudABI provides access to two different stat structures:
- fdstat, containing file descriptor level status: oflags, file
descriptor type and Capsicum rights, used by cap_rights_get(),
fcntl(F_GETFL), getsockopt(SO_TYPE).
- filestat, containing your regular file status: timestamps, inode
number, used by fstat().
Unlike FreeBSD's stat::st_mode, CloudABI file descriptor types don't
have overloaded meanings (e.g., returning S_ISCHR() for kqueues). Add a
utility function to extract the type of a file descriptor accurately.
CloudABI does not work with O_ACCMODEs. File descriptors have two sets
of Capsicum-style rights: rights that apply to the file descriptor
itself ('base') and rights that apply to any new file descriptors
yielded through openat() ('inheriting'). Though not perfect, we can
pretty safely decompose Capsicum rights to such a pair. This is done in
convert_capabilities().
Test Plan: Tests for these system calls are fairly extensive in cloudlibc.
Reviewers: jonathan, mjg, #manpages
Reviewed By: mjg
Subscribers: imp
Differential Revision: https://reviews.freebsd.org/D3171
Diffstat (limited to 'sys/compat')
-rw-r--r-- | sys/compat/cloudabi/cloudabi_fd.c | 256 | ||||
-rw-r--r-- | sys/compat/cloudabi/cloudabi_file.c | 82 | ||||
-rw-r--r-- | sys/compat/cloudabi/cloudabi_util.h | 4 |
3 files changed, 336 insertions, 6 deletions
diff --git a/sys/compat/cloudabi/cloudabi_fd.c b/sys/compat/cloudabi/cloudabi_fd.c index 8b72c19..23eb78b 100644 --- a/sys/compat/cloudabi/cloudabi_fd.c +++ b/sys/compat/cloudabi/cloudabi_fd.c @@ -27,13 +27,64 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/capsicum.h> #include <sys/filedesc.h> #include <sys/proc.h> +#include <sys/socketvar.h> #include <sys/syscallsubr.h> #include <sys/sysproto.h> +#include <sys/systm.h> #include <sys/unistd.h> +#include <sys/vnode.h> #include <compat/cloudabi/cloudabi_proto.h> +#include <compat/cloudabi/cloudabi_syscalldefs.h> +#include <compat/cloudabi/cloudabi_util.h> + +/* Translation between CloudABI and Capsicum rights. */ +#define RIGHTS_MAPPINGS \ + MAPPING(CLOUDABI_RIGHT_FD_DATASYNC, CAP_FSYNC) \ + MAPPING(CLOUDABI_RIGHT_FD_READ, CAP_READ) \ + MAPPING(CLOUDABI_RIGHT_FD_SEEK, CAP_SEEK) \ + MAPPING(CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS, CAP_FCNTL) \ + MAPPING(CLOUDABI_RIGHT_FD_SYNC, CAP_FSYNC) \ + MAPPING(CLOUDABI_RIGHT_FD_TELL, CAP_SEEK_TELL) \ + MAPPING(CLOUDABI_RIGHT_FD_WRITE, CAP_WRITE) \ + MAPPING(CLOUDABI_RIGHT_FILE_ADVISE) \ + MAPPING(CLOUDABI_RIGHT_FILE_ALLOCATE, CAP_WRITE) \ + MAPPING(CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY, CAP_MKDIRAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FILE, CAP_CREATE) \ + MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FIFO, CAP_MKFIFOAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LOOKUP) \ + MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_OPEN, CAP_LOOKUP) \ + MAPPING(CLOUDABI_RIGHT_FILE_READDIR, CAP_READ) \ + MAPPING(CLOUDABI_RIGHT_FILE_READLINK, CAP_LOOKUP) \ + MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_LINKAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_STAT_FGET, CAP_FSTAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE, CAP_FTRUNCATE) \ + MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES, CAP_FUTIMES) \ + MAPPING(CLOUDABI_RIGHT_FILE_STAT_GET, CAP_FSTATAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES, CAP_FUTIMESAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_SYMLINK, CAP_SYMLINKAT) \ + MAPPING(CLOUDABI_RIGHT_FILE_UNLINK, CAP_UNLINKAT) \ + MAPPING(CLOUDABI_RIGHT_MEM_MAP, CAP_MMAP) \ + MAPPING(CLOUDABI_RIGHT_MEM_MAP_EXEC, CAP_MMAP_X) \ + MAPPING(CLOUDABI_RIGHT_POLL_FD_READWRITE, CAP_EVENT) \ + MAPPING(CLOUDABI_RIGHT_POLL_MODIFY, CAP_KQUEUE_CHANGE) \ + MAPPING(CLOUDABI_RIGHT_POLL_PROC_TERMINATE, CAP_PDWAIT) \ + MAPPING(CLOUDABI_RIGHT_POLL_WAIT, CAP_KQUEUE_EVENT) \ + MAPPING(CLOUDABI_RIGHT_PROC_EXEC, CAP_FEXECVE) \ + MAPPING(CLOUDABI_RIGHT_SOCK_ACCEPT, CAP_ACCEPT) \ + MAPPING(CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY, CAP_BINDAT) \ + MAPPING(CLOUDABI_RIGHT_SOCK_BIND_SOCKET, CAP_BIND) \ + MAPPING(CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY, CAP_CONNECTAT) \ + MAPPING(CLOUDABI_RIGHT_SOCK_CONNECT_SOCKET, CAP_CONNECT) \ + MAPPING(CLOUDABI_RIGHT_SOCK_LISTEN, CAP_LISTEN) \ + MAPPING(CLOUDABI_RIGHT_SOCK_SHUTDOWN, CAP_SHUTDOWN) \ + MAPPING(CLOUDABI_RIGHT_SOCK_STAT_GET, CAP_GETPEERNAME, \ + CAP_GETSOCKNAME, CAP_GETSOCKOPT) int cloudabi_sys_fd_close(struct thread *td, struct cloudabi_sys_fd_close_args *uap) @@ -160,13 +211,214 @@ cloudabi_sys_fd_seek(struct thread *td, struct cloudabi_sys_fd_seek_args *uap) return (sys_lseek(td, &lseek_args)); } +/* Converts a file descriptor to a CloudABI file descriptor type. */ +cloudabi_filetype_t +cloudabi_convert_filetype(const struct file *fp) +{ + struct socket *so; + struct vnode *vp; + + switch (fp->f_type) { + case DTYPE_FIFO: + return (CLOUDABI_FILETYPE_FIFO); + case DTYPE_KQUEUE: + return (CLOUDABI_FILETYPE_POLL); + case DTYPE_PIPE: + return (CLOUDABI_FILETYPE_FIFO); + case DTYPE_PROCDESC: + return (CLOUDABI_FILETYPE_PROCESS); + case DTYPE_SHM: + return (CLOUDABI_FILETYPE_SHARED_MEMORY); + case DTYPE_SOCKET: + so = fp->f_data; + switch (so->so_type) { + case SOCK_DGRAM: + return (CLOUDABI_FILETYPE_SOCKET_DGRAM); + case SOCK_SEQPACKET: + return (CLOUDABI_FILETYPE_SOCKET_SEQPACKET); + case SOCK_STREAM: + return (CLOUDABI_FILETYPE_SOCKET_STREAM); + default: + return (CLOUDABI_FILETYPE_UNKNOWN); + } + case DTYPE_VNODE: + vp = fp->f_vnode; + switch (vp->v_type) { + case VBLK: + return (CLOUDABI_FILETYPE_BLOCK_DEVICE); + case VCHR: + return (CLOUDABI_FILETYPE_CHARACTER_DEVICE); + case VDIR: + return (CLOUDABI_FILETYPE_DIRECTORY); + case VFIFO: + return (CLOUDABI_FILETYPE_FIFO); + case VLNK: + return (CLOUDABI_FILETYPE_SYMBOLIC_LINK); + case VREG: + return (CLOUDABI_FILETYPE_REGULAR_FILE); + case VSOCK: + return (CLOUDABI_FILETYPE_SOCKET_STREAM); + default: + return (CLOUDABI_FILETYPE_UNKNOWN); + } + default: + return (CLOUDABI_FILETYPE_UNKNOWN); + } +} + +/* + * Converts FreeBSD's Capsicum rights to CloudABI's set of rights. + */ +static void +convert_capabilities(const cap_rights_t *capabilities, + cloudabi_filetype_t filetype, cloudabi_rights_t *base, + cloudabi_rights_t *inheriting) +{ + cloudabi_rights_t rights; + + /* Convert FreeBSD bits to CloudABI bits. */ + rights = 0; +#define MAPPING(cloudabi, ...) do { \ + if (cap_rights_is_set(capabilities, ##__VA_ARGS__)) \ + rights |= (cloudabi); \ +} while (0); + RIGHTS_MAPPINGS +#undef MAPPING + + /* + * CloudABI has a small number of additional rights bits to + * disambiguate between multiple purposes. Remove the bits that + * don't apply to the type of the file descriptor. + * + * As file descriptor access modes (O_ACCMODE) has been fully + * replaced by rights bits, CloudABI distinguishes between + * rights that apply to the file descriptor itself (base) versus + * rights of new file descriptors derived from them + * (inheriting). The code below approximates the pair by + * decomposing depending on the file descriptor type. + * + * We need to be somewhat accurate about which actions can + * actually be performed on the file descriptor, as functions + * like fcntl(fd, F_GETFL) are emulated on top of this. + */ + switch (filetype) { + case CLOUDABI_FILETYPE_DIRECTORY: + *base = rights & (CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | + CLOUDABI_RIGHT_FD_SYNC | CLOUDABI_RIGHT_FILE_ADVISE | + CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY | + CLOUDABI_RIGHT_FILE_CREATE_FILE | + CLOUDABI_RIGHT_FILE_CREATE_FIFO | + CLOUDABI_RIGHT_FILE_LINK_SOURCE | + CLOUDABI_RIGHT_FILE_LINK_TARGET | + CLOUDABI_RIGHT_FILE_OPEN | + CLOUDABI_RIGHT_FILE_READDIR | + CLOUDABI_RIGHT_FILE_READLINK | + CLOUDABI_RIGHT_FILE_RENAME_SOURCE | + CLOUDABI_RIGHT_FILE_RENAME_TARGET | + CLOUDABI_RIGHT_FILE_STAT_FGET | + CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES | + CLOUDABI_RIGHT_FILE_STAT_GET | + CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES | + CLOUDABI_RIGHT_FILE_SYMLINK | + CLOUDABI_RIGHT_FILE_UNLINK | + CLOUDABI_RIGHT_POLL_FD_READWRITE | + CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY | + CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY); + *inheriting = rights; + break; + case CLOUDABI_FILETYPE_FIFO: + *base = rights & ~(CLOUDABI_RIGHT_FILE_ADVISE | + CLOUDABI_RIGHT_FILE_ALLOCATE | + CLOUDABI_RIGHT_FILE_READDIR); + *inheriting = 0; + break; + case CLOUDABI_FILETYPE_POLL: + *base = rights & ~CLOUDABI_RIGHT_FILE_ADVISE; + *inheriting = 0; + break; + case CLOUDABI_FILETYPE_PROCESS: + *base = rights & ~CLOUDABI_RIGHT_FILE_ADVISE; + *inheriting = 0; + break; + case CLOUDABI_FILETYPE_REGULAR_FILE: + *base = rights & ~CLOUDABI_RIGHT_FILE_READDIR; + *inheriting = 0; + break; + case CLOUDABI_FILETYPE_SHARED_MEMORY: + *base = rights & ~(CLOUDABI_RIGHT_FD_SEEK | + CLOUDABI_RIGHT_FD_TELL | + CLOUDABI_RIGHT_FILE_ADVISE | + CLOUDABI_RIGHT_FILE_ALLOCATE | + CLOUDABI_RIGHT_FILE_READDIR); + *inheriting = 0; + break; + case CLOUDABI_FILETYPE_SOCKET_DGRAM: + case CLOUDABI_FILETYPE_SOCKET_SEQPACKET: + case CLOUDABI_FILETYPE_SOCKET_STREAM: + *base = rights & (CLOUDABI_RIGHT_FD_READ | + CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | + CLOUDABI_RIGHT_FD_WRITE | + CLOUDABI_RIGHT_FILE_STAT_FGET | + CLOUDABI_RIGHT_POLL_FD_READWRITE | + CLOUDABI_RIGHT_SOCK_ACCEPT | + CLOUDABI_RIGHT_SOCK_BIND_SOCKET | + CLOUDABI_RIGHT_SOCK_CONNECT_SOCKET | + CLOUDABI_RIGHT_SOCK_LISTEN | + CLOUDABI_RIGHT_SOCK_SHUTDOWN | + CLOUDABI_RIGHT_SOCK_STAT_GET); + *inheriting = rights; + break; + default: + *base = rights; + *inheriting = 0; + break; + } +} + int cloudabi_sys_fd_stat_get(struct thread *td, struct cloudabi_sys_fd_stat_get_args *uap) { + cloudabi_fdstat_t fsb = {}; + struct filedesc *fdp; + struct file *fp; + seq_t seq; + cap_rights_t rights; + int error, oflags; + bool modified; - /* Not implemented. */ - return (ENOSYS); + /* Obtain file descriptor properties. */ + fdp = td->td_proc->p_fd; + do { + error = fget_unlocked(fdp, uap->fd, cap_rights_init(&rights), + &fp, &seq); + if (error != 0) + return (error); + if (fp->f_ops == &badfileops) { + fdrop(fp, td); + return (EBADF); + } + + rights = *cap_rights(fdp, uap->fd); + oflags = OFLAGS(fp->f_flag); + fsb.fs_filetype = cloudabi_convert_filetype(fp); + + modified = fd_modified(fdp, uap->fd, seq); + fdrop(fp, td); + } while (modified); + + /* Convert file descriptor flags. */ + if (oflags & O_APPEND) + fsb.fs_flags |= CLOUDABI_FDFLAG_APPEND; + if (oflags & O_NONBLOCK) + fsb.fs_flags |= CLOUDABI_FDFLAG_NONBLOCK; + if (oflags & O_SYNC) + fsb.fs_flags |= CLOUDABI_FDFLAG_SYNC; + + /* Convert capabilities to CloudABI rights. */ + convert_capabilities(&rights, fsb.fs_filetype, + &fsb.fs_rights_base, &fsb.fs_rights_inheriting); + return (copyout(&fsb, (void *)uap->buf, sizeof(fsb))); } int diff --git a/sys/compat/cloudabi/cloudabi_file.c b/sys/compat/cloudabi/cloudabi_file.c index b508517..3f917a0 100644 --- a/sys/compat/cloudabi/cloudabi_file.c +++ b/sys/compat/cloudabi/cloudabi_file.c @@ -27,14 +27,18 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/capsicum.h> #include <sys/fcntl.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/namei.h> +#include <sys/proc.h> +#include <sys/stat.h> #include <sys/syscallsubr.h> #include <compat/cloudabi/cloudabi_proto.h> #include <compat/cloudabi/cloudabi_syscalldefs.h> +#include <compat/cloudabi/cloudabi_util.h> static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); @@ -220,13 +224,50 @@ cloudabi_sys_file_rename(struct thread *td, return (error); } +/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ +static void +convert_stat(const struct stat *sb, cloudabi_filestat_t *csb) +{ + cloudabi_filestat_t res = { + .st_dev = sb->st_dev, + .st_ino = sb->st_ino, + .st_nlink = sb->st_nlink, + .st_size = sb->st_size, + }; + + cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); + cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); + cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); + *csb = res; +} + int cloudabi_sys_file_stat_fget(struct thread *td, struct cloudabi_sys_file_stat_fget_args *uap) { + struct stat sb; + cloudabi_filestat_t csb; + struct file *fp; + cap_rights_t rights; + cloudabi_filetype_t filetype; + int error; - /* Not implemented. */ - return (ENOSYS); + /* Fetch file descriptor attributes. */ + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); + if (error != 0) + return (error); + error = fo_stat(fp, &sb, td->td_ucred, td); + if (error != 0) { + fdrop(fp, td); + return (error); + } + filetype = cloudabi_convert_filetype(fp); + fdrop(fp, td); + + /* Convert attributes to CloudABI's format. */ + convert_stat(&sb, &csb); + csb.st_filetype = filetype; + return (copyout(&csb, uap->buf, sizeof(csb))); } int @@ -242,9 +283,42 @@ int cloudabi_sys_file_stat_get(struct thread *td, struct cloudabi_sys_file_stat_get_args *uap) { + struct stat sb; + cloudabi_filestat_t csb; + char *path; + int error; - /* Not implemented. */ - return (ENOSYS); + error = copyin_path(uap->path, uap->pathlen, &path); + if (error != 0) + return (error); + + error = kern_statat(td, + (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : + AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL); + cloudabi_freestr(path); + if (error != 0) + return (error); + + /* Convert results and return them. */ + convert_stat(&sb, &csb); + if (S_ISBLK(sb.st_mode)) + csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; + else if (S_ISCHR(sb.st_mode)) + csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; + else if (S_ISDIR(sb.st_mode)) + csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; + else if (S_ISFIFO(sb.st_mode)) + csb.st_filetype = CLOUDABI_FILETYPE_FIFO; + else if (S_ISREG(sb.st_mode)) + csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; + else if (S_ISSOCK(sb.st_mode)) { + /* Inaccurate, but the best that we can do. */ + csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; + } else if (S_ISLNK(sb.st_mode)) + csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; + else + csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; + return (copyout(&csb, uap->buf, sizeof(csb))); } int diff --git a/sys/compat/cloudabi/cloudabi_util.h b/sys/compat/cloudabi/cloudabi_util.h index 5350d24..557cf17 100644 --- a/sys/compat/cloudabi/cloudabi_util.h +++ b/sys/compat/cloudabi/cloudabi_util.h @@ -30,6 +30,7 @@ #include <compat/cloudabi/cloudabi_syscalldefs.h> +struct file; struct thread; struct timespec; @@ -40,6 +41,9 @@ int cloudabi_clock_time_get(struct thread *, cloudabi_clockid_t, /* Converts a FreeBSD errno to a CloudABI errno. */ cloudabi_errno_t cloudabi_convert_errno(int); +/* Converts a file descriptor to a CloudABI file descriptor type. */ +cloudabi_filetype_t cloudabi_convert_filetype(const struct file *); + /* Converts a struct timespec to a CloudABI timestamp. */ int cloudabi_convert_timespec(const struct timespec *, cloudabi_timestamp_t *); |