Index: head/sys/compat/cloudabi/cloudabi_fd.c =================================================================== --- head/sys/compat/cloudabi/cloudabi_fd.c +++ head/sys/compat/cloudabi/cloudabi_fd.c @@ -27,13 +27,64 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include +#include #include #include +#include #include +#include #include +#include +#include + +/* 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 @@ 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) { - - /* Not implemented. */ - return (ENOSYS); + cloudabi_fdstat_t fsb = {}; + struct filedesc *fdp; + struct file *fp; + seq_t seq; + cap_rights_t rights; + int error, oflags; + bool modified; + + /* 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 Index: head/sys/compat/cloudabi/cloudabi_file.c =================================================================== --- head/sys/compat/cloudabi/cloudabi_file.c +++ head/sys/compat/cloudabi/cloudabi_file.c @@ -27,14 +27,18 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include #include +#include +#include #include #include #include +#include static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); @@ -220,13 +224,50 @@ 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 @@ 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 Index: head/sys/compat/cloudabi/cloudabi_util.h =================================================================== --- head/sys/compat/cloudabi/cloudabi_util.h +++ head/sys/compat/cloudabi/cloudabi_util.h @@ -30,6 +30,7 @@ #include +struct file; struct thread; struct timespec; @@ -40,6 +41,9 @@ /* 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 *);