1/*-
2 * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: stable/11/sys/compat/cloudabi/cloudabi_fd.c 351142 2019-08-16 21:01:35Z kevans $");
28
29#include <sys/param.h>
30#include <sys/capsicum.h>
31#include <sys/filedesc.h>
32#include <sys/proc.h>
33#include <sys/mman.h>
34#include <sys/socketvar.h>
35#include <sys/syscallsubr.h>
36#include <sys/sysproto.h>
37#include <sys/systm.h>
38#include <sys/unistd.h>
39#include <sys/vnode.h>
40
41#include <contrib/cloudabi/cloudabi_types_common.h>
42
43#include <compat/cloudabi/cloudabi_proto.h>
44#include <compat/cloudabi/cloudabi_util.h>
45
46/* Translation between CloudABI and Capsicum rights. */
47#define RIGHTS_MAPPINGS \
48	MAPPING(CLOUDABI_RIGHT_FD_DATASYNC, CAP_FSYNC)			\
49	MAPPING(CLOUDABI_RIGHT_FD_READ, CAP_READ)			\
50	MAPPING(CLOUDABI_RIGHT_FD_SEEK, CAP_SEEK)			\
51	MAPPING(CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS, CAP_FCNTL)		\
52	MAPPING(CLOUDABI_RIGHT_FD_SYNC, CAP_FSYNC)			\
53	MAPPING(CLOUDABI_RIGHT_FD_TELL, CAP_SEEK_TELL)			\
54	MAPPING(CLOUDABI_RIGHT_FD_WRITE, CAP_WRITE)			\
55	MAPPING(CLOUDABI_RIGHT_FILE_ADVISE)				\
56	MAPPING(CLOUDABI_RIGHT_FILE_ALLOCATE, CAP_WRITE)		\
57	MAPPING(CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY, CAP_MKDIRAT)	\
58	MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FILE, CAP_CREATE)		\
59	MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LINKAT_SOURCE)	\
60	MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT_TARGET)	\
61	MAPPING(CLOUDABI_RIGHT_FILE_OPEN, CAP_LOOKUP)			\
62	MAPPING(CLOUDABI_RIGHT_FILE_READDIR, CAP_READ)			\
63	MAPPING(CLOUDABI_RIGHT_FILE_READLINK, CAP_LOOKUP)		\
64	MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT_SOURCE)	\
65	MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_RENAMEAT_TARGET)	\
66	MAPPING(CLOUDABI_RIGHT_FILE_STAT_FGET, CAP_FSTAT)		\
67	MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE, CAP_FTRUNCATE)	\
68	MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES, CAP_FUTIMES)	\
69	MAPPING(CLOUDABI_RIGHT_FILE_STAT_GET, CAP_FSTATAT)		\
70	MAPPING(CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES, CAP_FUTIMESAT)	\
71	MAPPING(CLOUDABI_RIGHT_FILE_SYMLINK, CAP_SYMLINKAT)		\
72	MAPPING(CLOUDABI_RIGHT_FILE_UNLINK, CAP_UNLINKAT)		\
73	MAPPING(CLOUDABI_RIGHT_MEM_MAP, CAP_MMAP)			\
74	MAPPING(CLOUDABI_RIGHT_MEM_MAP_EXEC, CAP_MMAP_X)		\
75	MAPPING(CLOUDABI_RIGHT_POLL_FD_READWRITE, CAP_EVENT)		\
76	MAPPING(CLOUDABI_RIGHT_POLL_PROC_TERMINATE, CAP_EVENT)		\
77	MAPPING(CLOUDABI_RIGHT_PROC_EXEC, CAP_FEXECVE)			\
78	MAPPING(CLOUDABI_RIGHT_SOCK_SHUTDOWN, CAP_SHUTDOWN)		\
79
80int
81cloudabi_sys_fd_close(struct thread *td, struct cloudabi_sys_fd_close_args *uap)
82{
83
84	return (kern_close(td, uap->fd));
85}
86
87int
88cloudabi_sys_fd_create1(struct thread *td,
89    struct cloudabi_sys_fd_create1_args *uap)
90{
91	struct filecaps fcaps = {};
92
93	switch (uap->type) {
94	case CLOUDABI_FILETYPE_SHARED_MEMORY:
95		cap_rights_init(&fcaps.fc_rights, CAP_FSTAT, CAP_FTRUNCATE,
96		    CAP_MMAP_RWX);
97		return (kern_shm_open(td, SHM_ANON, O_RDWR | O_CLOEXEC, 0,
98		    &fcaps));
99	default:
100		return (EINVAL);
101	}
102}
103
104int
105cloudabi_sys_fd_create2(struct thread *td,
106    struct cloudabi_sys_fd_create2_args *uap)
107{
108	int fds[2];
109	int error, type;
110
111	switch (uap->type) {
112	case CLOUDABI_FILETYPE_SOCKET_DGRAM:
113		type = SOCK_DGRAM;
114		break;
115	case CLOUDABI_FILETYPE_SOCKET_STREAM:
116		type = SOCK_STREAM;
117		break;
118	default:
119		return (EINVAL);
120	}
121
122	error = kern_socketpair(td, AF_UNIX, type, 0, fds);
123	if (error == 0) {
124		td->td_retval[0] = fds[0];
125		td->td_retval[1] = fds[1];
126	}
127	return (0);
128}
129
130int
131cloudabi_sys_fd_datasync(struct thread *td,
132    struct cloudabi_sys_fd_datasync_args *uap)
133{
134
135	return (kern_fsync(td, uap->fd, false));
136}
137
138int
139cloudabi_sys_fd_dup(struct thread *td, struct cloudabi_sys_fd_dup_args *uap)
140{
141
142	return (kern_dup(td, FDDUP_NORMAL, 0, uap->from, 0));
143}
144
145int
146cloudabi_sys_fd_replace(struct thread *td,
147    struct cloudabi_sys_fd_replace_args *uap)
148{
149	int error;
150
151	/*
152	 * CloudABI's equivalent to dup2(). CloudABI processes should
153	 * not depend on hardcoded file descriptor layouts, but simply
154	 * use the file descriptor numbers that are allocated by the
155	 * kernel. Duplicating file descriptors to arbitrary numbers
156	 * should not be done.
157	 *
158	 * Invoke kern_dup() with FDDUP_MUSTREPLACE, so that we return
159	 * EBADF when duplicating to a nonexistent file descriptor. Also
160	 * clear the return value, as this system call yields no return
161	 * value.
162	 */
163	error = kern_dup(td, FDDUP_MUSTREPLACE, 0, uap->from, uap->to);
164	td->td_retval[0] = 0;
165	return (error);
166}
167
168int
169cloudabi_sys_fd_seek(struct thread *td, struct cloudabi_sys_fd_seek_args *uap)
170{
171	int whence;
172
173	switch (uap->whence) {
174	case CLOUDABI_WHENCE_CUR:
175		whence = SEEK_CUR;
176		break;
177	case CLOUDABI_WHENCE_END:
178		whence = SEEK_END;
179		break;
180	case CLOUDABI_WHENCE_SET:
181		whence = SEEK_SET;
182		break;
183	default:
184		return (EINVAL);
185	}
186
187	return (kern_lseek(td, uap->fd, uap->offset, whence));
188}
189
190/* Converts a file descriptor to a CloudABI file descriptor type. */
191cloudabi_filetype_t
192cloudabi_convert_filetype(const struct file *fp)
193{
194	struct socket *so;
195	struct vnode *vp;
196
197	switch (fp->f_type) {
198	case DTYPE_FIFO:
199		return (CLOUDABI_FILETYPE_SOCKET_STREAM);
200	case DTYPE_PIPE:
201		return (CLOUDABI_FILETYPE_SOCKET_STREAM);
202	case DTYPE_PROCDESC:
203		return (CLOUDABI_FILETYPE_PROCESS);
204	case DTYPE_SHM:
205		return (CLOUDABI_FILETYPE_SHARED_MEMORY);
206	case DTYPE_SOCKET:
207		so = fp->f_data;
208		switch (so->so_type) {
209		case SOCK_DGRAM:
210			return (CLOUDABI_FILETYPE_SOCKET_DGRAM);
211		case SOCK_STREAM:
212			return (CLOUDABI_FILETYPE_SOCKET_STREAM);
213		default:
214			return (CLOUDABI_FILETYPE_UNKNOWN);
215		}
216	case DTYPE_VNODE:
217		vp = fp->f_vnode;
218		switch (vp->v_type) {
219		case VBLK:
220			return (CLOUDABI_FILETYPE_BLOCK_DEVICE);
221		case VCHR:
222			return (CLOUDABI_FILETYPE_CHARACTER_DEVICE);
223		case VDIR:
224			return (CLOUDABI_FILETYPE_DIRECTORY);
225		case VFIFO:
226			return (CLOUDABI_FILETYPE_SOCKET_STREAM);
227		case VLNK:
228			return (CLOUDABI_FILETYPE_SYMBOLIC_LINK);
229		case VREG:
230			return (CLOUDABI_FILETYPE_REGULAR_FILE);
231		case VSOCK:
232			return (CLOUDABI_FILETYPE_SOCKET_STREAM);
233		default:
234			return (CLOUDABI_FILETYPE_UNKNOWN);
235		}
236	default:
237		return (CLOUDABI_FILETYPE_UNKNOWN);
238	}
239}
240
241/* Removes rights that conflict with the file descriptor type. */
242void
243cloudabi_remove_conflicting_rights(cloudabi_filetype_t filetype,
244    cloudabi_rights_t *base, cloudabi_rights_t *inheriting)
245{
246
247	/*
248	 * CloudABI has a small number of additional rights bits to
249	 * disambiguate between multiple purposes. Remove the bits that
250	 * don't apply to the type of the file descriptor.
251	 *
252	 * As file descriptor access modes (O_ACCMODE) has been fully
253	 * replaced by rights bits, CloudABI distinguishes between
254	 * rights that apply to the file descriptor itself (base) versus
255	 * rights of new file descriptors derived from them
256	 * (inheriting). The code below approximates the pair by
257	 * decomposing depending on the file descriptor type.
258	 *
259	 * We need to be somewhat accurate about which actions can
260	 * actually be performed on the file descriptor, as functions
261	 * like fcntl(fd, F_GETFL) are emulated on top of this.
262	 */
263	switch (filetype) {
264	case CLOUDABI_FILETYPE_DIRECTORY:
265		*base &= CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS |
266		    CLOUDABI_RIGHT_FD_SYNC | CLOUDABI_RIGHT_FILE_ADVISE |
267		    CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY |
268		    CLOUDABI_RIGHT_FILE_CREATE_FILE |
269		    CLOUDABI_RIGHT_FILE_LINK_SOURCE |
270		    CLOUDABI_RIGHT_FILE_LINK_TARGET |
271		    CLOUDABI_RIGHT_FILE_OPEN |
272		    CLOUDABI_RIGHT_FILE_READDIR |
273		    CLOUDABI_RIGHT_FILE_READLINK |
274		    CLOUDABI_RIGHT_FILE_RENAME_SOURCE |
275		    CLOUDABI_RIGHT_FILE_RENAME_TARGET |
276		    CLOUDABI_RIGHT_FILE_STAT_FGET |
277		    CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES |
278		    CLOUDABI_RIGHT_FILE_STAT_GET |
279		    CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES |
280		    CLOUDABI_RIGHT_FILE_SYMLINK |
281		    CLOUDABI_RIGHT_FILE_UNLINK |
282		    CLOUDABI_RIGHT_POLL_FD_READWRITE;
283		*inheriting &= CLOUDABI_RIGHT_FD_DATASYNC |
284		    CLOUDABI_RIGHT_FD_READ |
285		    CLOUDABI_RIGHT_FD_SEEK |
286		    CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS |
287		    CLOUDABI_RIGHT_FD_SYNC |
288		    CLOUDABI_RIGHT_FD_TELL |
289		    CLOUDABI_RIGHT_FD_WRITE |
290		    CLOUDABI_RIGHT_FILE_ADVISE |
291		    CLOUDABI_RIGHT_FILE_ALLOCATE |
292		    CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY |
293		    CLOUDABI_RIGHT_FILE_CREATE_FILE |
294		    CLOUDABI_RIGHT_FILE_LINK_SOURCE |
295		    CLOUDABI_RIGHT_FILE_LINK_TARGET |
296		    CLOUDABI_RIGHT_FILE_OPEN |
297		    CLOUDABI_RIGHT_FILE_READDIR |
298		    CLOUDABI_RIGHT_FILE_READLINK |
299		    CLOUDABI_RIGHT_FILE_RENAME_SOURCE |
300		    CLOUDABI_RIGHT_FILE_RENAME_TARGET |
301		    CLOUDABI_RIGHT_FILE_STAT_FGET |
302		    CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE |
303		    CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES |
304		    CLOUDABI_RIGHT_FILE_STAT_GET |
305		    CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES |
306		    CLOUDABI_RIGHT_FILE_SYMLINK |
307		    CLOUDABI_RIGHT_FILE_UNLINK |
308		    CLOUDABI_RIGHT_MEM_MAP |
309		    CLOUDABI_RIGHT_MEM_MAP_EXEC |
310		    CLOUDABI_RIGHT_POLL_FD_READWRITE |
311		    CLOUDABI_RIGHT_PROC_EXEC;
312		break;
313	case CLOUDABI_FILETYPE_PROCESS:
314		*base &= ~(CLOUDABI_RIGHT_FILE_ADVISE |
315		    CLOUDABI_RIGHT_POLL_FD_READWRITE);
316		*inheriting = 0;
317		break;
318	case CLOUDABI_FILETYPE_REGULAR_FILE:
319		*base &= CLOUDABI_RIGHT_FD_DATASYNC |
320		    CLOUDABI_RIGHT_FD_READ |
321		    CLOUDABI_RIGHT_FD_SEEK |
322		    CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS |
323		    CLOUDABI_RIGHT_FD_SYNC |
324		    CLOUDABI_RIGHT_FD_TELL |
325		    CLOUDABI_RIGHT_FD_WRITE |
326		    CLOUDABI_RIGHT_FILE_ADVISE |
327		    CLOUDABI_RIGHT_FILE_ALLOCATE |
328		    CLOUDABI_RIGHT_FILE_STAT_FGET |
329		    CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE |
330		    CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES |
331		    CLOUDABI_RIGHT_MEM_MAP |
332		    CLOUDABI_RIGHT_MEM_MAP_EXEC |
333		    CLOUDABI_RIGHT_POLL_FD_READWRITE |
334		    CLOUDABI_RIGHT_PROC_EXEC;
335		*inheriting = 0;
336		break;
337	case CLOUDABI_FILETYPE_SHARED_MEMORY:
338		*base &= ~(CLOUDABI_RIGHT_FD_SEEK |
339		    CLOUDABI_RIGHT_FD_TELL |
340		    CLOUDABI_RIGHT_FILE_ADVISE |
341		    CLOUDABI_RIGHT_FILE_ALLOCATE |
342		    CLOUDABI_RIGHT_FILE_READDIR);
343		*inheriting = 0;
344		break;
345	case CLOUDABI_FILETYPE_SOCKET_DGRAM:
346	case CLOUDABI_FILETYPE_SOCKET_STREAM:
347		*base &= CLOUDABI_RIGHT_FD_READ |
348		    CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS |
349		    CLOUDABI_RIGHT_FD_WRITE |
350		    CLOUDABI_RIGHT_FILE_STAT_FGET |
351		    CLOUDABI_RIGHT_POLL_FD_READWRITE |
352		    CLOUDABI_RIGHT_SOCK_SHUTDOWN;
353		break;
354	default:
355		*inheriting = 0;
356		break;
357	}
358}
359
360/* Converts FreeBSD's Capsicum rights to CloudABI's set of rights. */
361static void
362convert_capabilities(const cap_rights_t *capabilities,
363    cloudabi_filetype_t filetype, cloudabi_rights_t *base,
364    cloudabi_rights_t *inheriting)
365{
366	cloudabi_rights_t rights;
367
368	/* Convert FreeBSD bits to CloudABI bits. */
369	rights = 0;
370#define MAPPING(cloudabi, ...) do {				\
371	if (cap_rights_is_set(capabilities, ##__VA_ARGS__))	\
372		rights |= (cloudabi);				\
373} while (0);
374	RIGHTS_MAPPINGS
375#undef MAPPING
376
377	*base = rights;
378	*inheriting = rights;
379	cloudabi_remove_conflicting_rights(filetype, base, inheriting);
380}
381
382int
383cloudabi_sys_fd_stat_get(struct thread *td,
384    struct cloudabi_sys_fd_stat_get_args *uap)
385{
386	cloudabi_fdstat_t fsb = {};
387	struct filedesc *fdp;
388	struct file *fp;
389	seq_t seq;
390	cap_rights_t rights;
391	int error, oflags;
392	bool modified;
393
394	/* Obtain file descriptor properties. */
395	fdp = td->td_proc->p_fd;
396	do {
397		error = fget_unlocked(fdp, uap->fd, cap_rights_init(&rights),
398		    &fp, &seq);
399		if (error != 0)
400			return (error);
401		if (fp->f_ops == &badfileops) {
402			fdrop(fp, td);
403			return (EBADF);
404		}
405
406		rights = *cap_rights(fdp, uap->fd);
407		oflags = OFLAGS(fp->f_flag);
408		fsb.fs_filetype = cloudabi_convert_filetype(fp);
409
410		modified = fd_modified(fdp, uap->fd, seq);
411		fdrop(fp, td);
412	} while (modified);
413
414	/* Convert file descriptor flags. */
415	if (oflags & O_APPEND)
416		fsb.fs_flags |= CLOUDABI_FDFLAG_APPEND;
417	if (oflags & O_NONBLOCK)
418		fsb.fs_flags |= CLOUDABI_FDFLAG_NONBLOCK;
419	if (oflags & O_SYNC)
420		fsb.fs_flags |= CLOUDABI_FDFLAG_SYNC;
421
422	/* Convert capabilities to CloudABI rights. */
423	convert_capabilities(&rights, fsb.fs_filetype,
424	    &fsb.fs_rights_base, &fsb.fs_rights_inheriting);
425	return (copyout(&fsb, (void *)uap->buf, sizeof(fsb)));
426}
427
428/* Converts CloudABI rights to a set of Capsicum capabilities. */
429int
430cloudabi_convert_rights(cloudabi_rights_t in, cap_rights_t *out)
431{
432
433	cap_rights_init(out);
434#define MAPPING(cloudabi, ...) do {			\
435	if (in & (cloudabi)) {				\
436		cap_rights_set(out, ##__VA_ARGS__);	\
437		in &= ~(cloudabi);			\
438	}						\
439} while (0);
440	RIGHTS_MAPPINGS
441#undef MAPPING
442	if (in != 0)
443		return (ENOTCAPABLE);
444	return (0);
445}
446
447int
448cloudabi_sys_fd_stat_put(struct thread *td,
449    struct cloudabi_sys_fd_stat_put_args *uap)
450{
451	cloudabi_fdstat_t fsb;
452	cap_rights_t rights;
453	int error, oflags;
454
455	error = copyin(uap->buf, &fsb, sizeof(fsb));
456	if (error != 0)
457		return (error);
458
459	if (uap->flags == CLOUDABI_FDSTAT_FLAGS) {
460		/* Convert flags. */
461		oflags = 0;
462		if (fsb.fs_flags & CLOUDABI_FDFLAG_APPEND)
463			oflags |= O_APPEND;
464		if (fsb.fs_flags & CLOUDABI_FDFLAG_NONBLOCK)
465			oflags |= O_NONBLOCK;
466		if (fsb.fs_flags & (CLOUDABI_FDFLAG_SYNC |
467		    CLOUDABI_FDFLAG_DSYNC | CLOUDABI_FDFLAG_RSYNC))
468			oflags |= O_SYNC;
469		return (kern_fcntl(td, uap->fd, F_SETFL, oflags));
470	} else if (uap->flags == CLOUDABI_FDSTAT_RIGHTS) {
471		/* Convert rights. */
472		error = cloudabi_convert_rights(
473		    fsb.fs_rights_base | fsb.fs_rights_inheriting, &rights);
474		if (error != 0)
475			return (error);
476		return (kern_cap_rights_limit(td, uap->fd, &rights));
477	}
478	return (EINVAL);
479}
480
481int
482cloudabi_sys_fd_sync(struct thread *td, struct cloudabi_sys_fd_sync_args *uap)
483{
484
485	return (kern_fsync(td, uap->fd, true));
486}
487