Add file_open(): the underlying system call of openat().

CloudABI purely operates on file descriptor rights (CAP_*). File
descriptor access modes (O_ACCMODE) are emulated on top of rights.

Instead of accepting the traditional flags argument, file_open() copies
in an fdstat_t object that contains the initial rights the descriptor
should have, but also file descriptor flags that should persist after
opening (APPEND, NONBLOCK, *SYNC). Only flags that don't persist (EXCL,
TRUNC, CREAT, DIRECTORY) are passed in as an argument.

file_open() first converts the rights, the persistent flags and the
non-persistent flags to fflags. It then calls into vn_open(). If
successful, it installs the file descriptor with the requested
rights, trimming off rights that don't apply to the type of
the file that has been opened.

Unlike kern_openat(), this function does not support /dev/fd/*. I can't
think of a reason why we need to support this for CloudABI.

Obtained from:	https://github.com/NuxiNL/freebsd
Differential Revision:	https://reviews.freebsd.org/D3235
This commit is contained in:
Ed Schouten 2015-08-06 06:47:28 +00:00
parent d98d7ba0b4
commit 0f85ff377b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=286359
3 changed files with 148 additions and 3 deletions

View File

@ -290,7 +290,7 @@ cloudabi_convert_filetype(const struct file *fp)
}
/* Removes rights that conflict with the file descriptor type. */
static void
void
cloudabi_remove_conflicting_rights(cloudabi_filetype_t filetype,
cloudabi_rights_t *base, cloudabi_rights_t *inheriting)
{
@ -499,6 +499,25 @@ cloudabi_sys_fd_stat_get(struct thread *td,
return (copyout(&fsb, (void *)uap->buf, sizeof(fsb)));
}
/* Converts CloudABI rights to a set of Capsicum capabilities. */
int
cloudabi_convert_rights(cloudabi_rights_t in, cap_rights_t *out)
{
cap_rights_init(out);
#define MAPPING(cloudabi, ...) do { \
if (in & (cloudabi)) { \
cap_rights_set(out, ##__VA_ARGS__); \
in &= ~(cloudabi); \
} \
} while (0);
RIGHTS_MAPPINGS
#undef MAPPING
if (in != 0)
return (ENOTCAPABLE);
return (0);
}
int
cloudabi_sys_fd_stat_put(struct thread *td,
struct cloudabi_sys_fd_stat_put_args *uap)

View File

@ -197,9 +197,128 @@ int
cloudabi_sys_file_open(struct thread *td,
struct cloudabi_sys_file_open_args *uap)
{
cloudabi_fdstat_t fds;
cap_rights_t rights;
struct filecaps fcaps = {};
struct nameidata nd;
struct file *fp;
struct vnode *vp;
char *path;
int error, fd, fflags;
bool read, write;
/* Not implemented. */
return (ENOSYS);
error = copyin(uap->fds, &fds, sizeof(fds));
if (error != 0)
return (error);
/* All the requested rights should be set on the descriptor. */
error = cloudabi_convert_rights(
fds.fs_rights_base | fds.fs_rights_inheriting, &rights);
if (error != 0)
return (error);
cap_rights_set(&rights, CAP_LOOKUP);
/* Convert rights to corresponding access mode. */
read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ |
CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0;
write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC |
CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE |
CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0;
fflags = read ? write ? FREAD | FWRITE : FREAD : FWRITE;
/* Convert open flags. */
if ((uap->oflags & CLOUDABI_O_CREAT) != 0) {
fflags |= O_CREAT;
cap_rights_set(&rights, CAP_CREATE);
}
if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0)
fflags |= O_DIRECTORY;
if ((uap->oflags & CLOUDABI_O_EXCL) != 0)
fflags |= O_EXCL;
if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) {
fflags |= O_TRUNC;
cap_rights_set(&rights, CAP_FTRUNCATE);
}
if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0)
fflags |= O_APPEND;
if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0)
fflags |= O_NONBLOCK;
if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC |
CLOUDABI_FDFLAG_RSYNC)) != 0) {
fflags |= O_SYNC;
cap_rights_set(&rights, CAP_FSYNC);
}
if ((uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0)
fflags |= O_NOFOLLOW;
if (write && (fflags & (O_APPEND | O_TRUNC)) == 0)
cap_rights_set(&rights, CAP_SEEK);
/* Allocate new file descriptor. */
error = falloc_noinstall(td, &fp);
if (error != 0)
return (error);
fp->f_flag = fflags & FMASK;
/* Open path. */
error = copyin_path(uap->path, uap->pathlen, &path);
if (error != 0) {
fdrop(fp, td);
return (error);
}
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->fd,
&rights, td);
error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp);
cloudabi_freestr(path);
if (error != 0) {
/* Custom operations provided. */
if (error == ENXIO && fp->f_ops != &badfileops)
goto success;
/*
* POSIX compliance: return ELOOP in case openat() is
* called on a symbolic link and O_NOFOLLOW is set.
*/
if (error == EMLINK)
error = ELOOP;
fdrop(fp, td);
return (error);
}
NDFREE(&nd, NDF_ONLY_PNBUF);
filecaps_free(&nd.ni_filecaps);
fp->f_vnode = vp = nd.ni_vp;
/* Install vnode operations if no custom operations are provided. */
if (fp->f_ops == &badfileops) {
fp->f_seqcount = 1;
finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK),
DTYPE_VNODE, vp, &vnops);
}
VOP_UNLOCK(vp, 0);
/* Truncate file. */
if (fflags & O_TRUNC) {
error = fo_truncate(fp, 0, td->td_ucred, td);
if (error != 0) {
fdrop(fp, td);
return (error);
}
}
success:
/* Determine which Capsicum rights to set on the file descriptor. */
cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp),
&fds.fs_rights_base, &fds.fs_rights_inheriting);
cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting,
&fcaps.fc_rights);
if (cap_rights_is_set(&fcaps.fc_rights))
fcaps.fc_fcntls = CAP_FCNTL_SETFL;
error = finstall(td, fp, &fd, fflags, &fcaps);
fdrop(fp, td);
if (error != 0)
return (error);
td->td_retval[0] = fd;
return (0);
}
/* Converts a FreeBSD directory entry structure and writes it to userspace. */

View File

@ -50,6 +50,13 @@ void cloudabi_convert_sockaddr(const struct sockaddr *, socklen_t,
/* Converts a file descriptor to a CloudABI file descriptor type. */
cloudabi_filetype_t cloudabi_convert_filetype(const struct file *);
/* Converts CloudABI rights to a set of Capsicum capabilities. */
int cloudabi_convert_rights(cloudabi_rights_t, cap_rights_t *);
/* Removes rights that conflict with the file descriptor type. */
void cloudabi_remove_conflicting_rights(cloudabi_filetype_t,
cloudabi_rights_t *, cloudabi_rights_t *);
/* Converts a struct timespec to a CloudABI timestamp. */
int cloudabi_convert_timespec(const struct timespec *, cloudabi_timestamp_t *);