cloudabi_file.c revision 297247
1285307Sed/*-
2285307Sed * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3285307Sed *
4285307Sed * Redistribution and use in source and binary forms, with or without
5285307Sed * modification, are permitted provided that the following conditions
6285307Sed * are met:
7285307Sed * 1. Redistributions of source code must retain the above copyright
8285307Sed *    notice, this list of conditions and the following disclaimer.
9285307Sed * 2. Redistributions in binary form must reproduce the above copyright
10285307Sed *    notice, this list of conditions and the following disclaimer in the
11285307Sed *    documentation and/or other materials provided with the distribution.
12285307Sed *
13285307Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14285307Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15285307Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16285307Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17285307Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18285307Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19285307Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20285307Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21285307Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22285307Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23285307Sed * SUCH DAMAGE.
24285307Sed */
25285307Sed
26285307Sed#include <sys/cdefs.h>
27285307Sed__FBSDID("$FreeBSD: head/sys/compat/cloudabi/cloudabi_file.c 297247 2016-03-24 21:47:15Z ed $");
28285307Sed
29285596Sed#include <sys/param.h>
30285930Sed#include <sys/capsicum.h>
31285998Sed#include <sys/dirent.h>
32285596Sed#include <sys/fcntl.h>
33285834Sed#include <sys/kernel.h>
34285834Sed#include <sys/malloc.h>
35285834Sed#include <sys/namei.h>
36285930Sed#include <sys/proc.h>
37285930Sed#include <sys/stat.h>
38285596Sed#include <sys/syscallsubr.h>
39285998Sed#include <sys/uio.h>
40285998Sed#include <sys/vnode.h>
41285596Sed
42297247Sed#include <contrib/cloudabi/cloudabi_types_common.h>
43297247Sed
44285307Sed#include <compat/cloudabi/cloudabi_proto.h>
45285930Sed#include <compat/cloudabi/cloudabi_util.h>
46285307Sed
47285998Sed#include <security/mac/mac_framework.h>
48285998Sed
49285834Sedstatic MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames");
50285834Sed
51285834Sed/*
52285834Sed * Copying pathnames from userspace to kernelspace.
53285834Sed *
54285834Sed * Unlike most operating systems, CloudABI doesn't use null-terminated
55285834Sed * pathname strings. Processes always pass pathnames to the kernel by
56285834Sed * providing a base pointer and a length. This has a couple of reasons:
57285834Sed *
58285834Sed * - It makes it easier to use CloudABI in combination with programming
59285834Sed *   languages other than C, that may use non-null terminated strings.
60285834Sed * - It allows for calling system calls on individual components of the
61285834Sed *   pathname without modifying the input string.
62285834Sed *
63285834Sed * The function below copies in pathname strings and null-terminates it.
64285834Sed * It also ensure that the string itself does not contain any null
65285834Sed * bytes.
66285834Sed *
67285834Sed * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass
68285834Sed *           in unterminated pathname strings, so we can do away with
69285834Sed *           the copying.
70285834Sed */
71285834Sed
72285834Sedstatic int
73285834Sedcopyin_path(const char *uaddr, size_t len, char **result)
74285834Sed{
75285834Sed	char *buf;
76285834Sed	int error;
77285834Sed
78285834Sed	if (len >= PATH_MAX)
79285834Sed		return (ENAMETOOLONG);
80285834Sed	buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK);
81285834Sed	error = copyin(uaddr, buf, len);
82285834Sed	if (error != 0) {
83285834Sed		free(buf, M_CLOUDABI_PATH);
84285834Sed		return (error);
85285834Sed	}
86285834Sed	if (memchr(buf, '\0', len) != NULL) {
87285834Sed		free(buf, M_CLOUDABI_PATH);
88285834Sed		return (EINVAL);
89285834Sed	}
90285834Sed	buf[len] = '\0';
91285834Sed	*result = buf;
92285834Sed	return (0);
93285834Sed}
94285834Sed
95285834Sedstatic void
96285834Sedcloudabi_freestr(char *buf)
97285834Sed{
98285834Sed
99285834Sed	free(buf, M_CLOUDABI_PATH);
100285834Sed}
101285834Sed
102285307Sedint
103285307Sedcloudabi_sys_file_advise(struct thread *td,
104285307Sed    struct cloudabi_sys_file_advise_args *uap)
105285307Sed{
106285596Sed	int advice;
107285307Sed
108285596Sed	switch (uap->advice) {
109285596Sed	case CLOUDABI_ADVICE_DONTNEED:
110285596Sed		advice = POSIX_FADV_DONTNEED;
111285596Sed		break;
112285596Sed	case CLOUDABI_ADVICE_NOREUSE:
113285596Sed		advice = POSIX_FADV_NOREUSE;
114285596Sed		break;
115285596Sed	case CLOUDABI_ADVICE_NORMAL:
116285596Sed		advice = POSIX_FADV_NORMAL;
117285596Sed		break;
118285596Sed	case CLOUDABI_ADVICE_RANDOM:
119285596Sed		advice = POSIX_FADV_RANDOM;
120285596Sed		break;
121285596Sed	case CLOUDABI_ADVICE_SEQUENTIAL:
122285596Sed		advice = POSIX_FADV_SEQUENTIAL;
123285596Sed		break;
124285596Sed	case CLOUDABI_ADVICE_WILLNEED:
125285596Sed		advice = POSIX_FADV_WILLNEED;
126285596Sed		break;
127285596Sed	default:
128285596Sed		return (EINVAL);
129285596Sed	}
130285596Sed
131285596Sed	return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice));
132285307Sed}
133285307Sed
134285307Sedint
135285307Sedcloudabi_sys_file_allocate(struct thread *td,
136285307Sed    struct cloudabi_sys_file_allocate_args *uap)
137285307Sed{
138285307Sed
139285596Sed	return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len));
140285307Sed}
141285307Sed
142285307Sedint
143285307Sedcloudabi_sys_file_create(struct thread *td,
144285307Sed    struct cloudabi_sys_file_create_args *uap)
145285307Sed{
146285931Sed	char *path;
147285931Sed	int error;
148285307Sed
149285931Sed	error = copyin_path(uap->path, uap->pathlen, &path);
150285931Sed	if (error != 0)
151285931Sed		return (error);
152285931Sed
153285931Sed	/*
154285931Sed	 * CloudABI processes cannot interact with UNIX credentials and
155285931Sed	 * permissions. Depend on the umask that is set prior to
156285931Sed	 * execution to restrict the file permissions.
157285931Sed	 */
158285931Sed	switch (uap->type) {
159285931Sed	case CLOUDABI_FILETYPE_DIRECTORY:
160285931Sed		error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777);
161285931Sed		break;
162285931Sed	case CLOUDABI_FILETYPE_FIFO:
163285931Sed		error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666);
164285931Sed		break;
165285931Sed	default:
166285931Sed		error = EINVAL;
167285931Sed		break;
168285931Sed	}
169285931Sed	cloudabi_freestr(path);
170285931Sed	return (error);
171285307Sed}
172285307Sed
173285307Sedint
174285307Sedcloudabi_sys_file_link(struct thread *td,
175285307Sed    struct cloudabi_sys_file_link_args *uap)
176285307Sed{
177285834Sed	char *path1, *path2;
178285834Sed	int error;
179285307Sed
180285834Sed	error = copyin_path(uap->path1, uap->path1len, &path1);
181285834Sed	if (error != 0)
182285834Sed		return (error);
183285834Sed	error = copyin_path(uap->path2, uap->path2len, &path2);
184285834Sed	if (error != 0) {
185285834Sed		cloudabi_freestr(path1);
186285834Sed		return (error);
187285834Sed	}
188285834Sed
189297247Sed	error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2,
190297247Sed	    UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ?
191285834Sed	    FOLLOW : NOFOLLOW);
192285834Sed	cloudabi_freestr(path1);
193285834Sed	cloudabi_freestr(path2);
194285834Sed	return (error);
195285307Sed}
196285307Sed
197285307Sedint
198285307Sedcloudabi_sys_file_open(struct thread *td,
199285307Sed    struct cloudabi_sys_file_open_args *uap)
200285307Sed{
201286359Sed	cloudabi_fdstat_t fds;
202286359Sed	cap_rights_t rights;
203286359Sed	struct filecaps fcaps = {};
204286359Sed	struct nameidata nd;
205286359Sed	struct file *fp;
206286359Sed	struct vnode *vp;
207286359Sed	char *path;
208286359Sed	int error, fd, fflags;
209286359Sed	bool read, write;
210285307Sed
211286359Sed	error = copyin(uap->fds, &fds, sizeof(fds));
212286359Sed	if (error != 0)
213286359Sed		return (error);
214286359Sed
215286359Sed	/* All the requested rights should be set on the descriptor. */
216286359Sed	error = cloudabi_convert_rights(
217286359Sed	    fds.fs_rights_base | fds.fs_rights_inheriting, &rights);
218286359Sed	if (error != 0)
219286359Sed		return (error);
220286359Sed	cap_rights_set(&rights, CAP_LOOKUP);
221286359Sed
222286359Sed	/* Convert rights to corresponding access mode. */
223286359Sed	read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ |
224286359Sed	    CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0;
225286359Sed	write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC |
226286359Sed	    CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE |
227286359Sed	    CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0;
228286633Sed	fflags = write ? read ? FREAD | FWRITE : FWRITE : FREAD;
229286359Sed
230286359Sed	/* Convert open flags. */
231286359Sed	if ((uap->oflags & CLOUDABI_O_CREAT) != 0) {
232286359Sed		fflags |= O_CREAT;
233286359Sed		cap_rights_set(&rights, CAP_CREATE);
234286359Sed	}
235286359Sed	if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0)
236286359Sed		fflags |= O_DIRECTORY;
237286359Sed	if ((uap->oflags & CLOUDABI_O_EXCL) != 0)
238286359Sed		fflags |= O_EXCL;
239286359Sed	if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) {
240286359Sed		fflags |= O_TRUNC;
241286359Sed		cap_rights_set(&rights, CAP_FTRUNCATE);
242286359Sed	}
243286359Sed	if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0)
244286359Sed		fflags |= O_APPEND;
245286359Sed	if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0)
246286359Sed		fflags |= O_NONBLOCK;
247286359Sed	if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC |
248286359Sed	    CLOUDABI_FDFLAG_RSYNC)) != 0) {
249286359Sed		fflags |= O_SYNC;
250286359Sed		cap_rights_set(&rights, CAP_FSYNC);
251286359Sed	}
252297247Sed	if ((uap->dirfd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0)
253286359Sed		fflags |= O_NOFOLLOW;
254286359Sed	if (write && (fflags & (O_APPEND | O_TRUNC)) == 0)
255286359Sed		cap_rights_set(&rights, CAP_SEEK);
256286359Sed
257286359Sed	/* Allocate new file descriptor. */
258286359Sed	error = falloc_noinstall(td, &fp);
259286359Sed	if (error != 0)
260286359Sed		return (error);
261286359Sed	fp->f_flag = fflags & FMASK;
262286359Sed
263286359Sed	/* Open path. */
264286359Sed	error = copyin_path(uap->path, uap->pathlen, &path);
265286359Sed	if (error != 0) {
266286359Sed		fdrop(fp, td);
267286359Sed		return (error);
268286359Sed	}
269297247Sed	NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->dirfd.fd,
270286359Sed	    &rights, td);
271286359Sed	error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp);
272286359Sed	cloudabi_freestr(path);
273286359Sed	if (error != 0) {
274286359Sed		/* Custom operations provided. */
275286359Sed		if (error == ENXIO && fp->f_ops != &badfileops)
276286359Sed			goto success;
277286359Sed
278286359Sed		/*
279286359Sed		 * POSIX compliance: return ELOOP in case openat() is
280286359Sed		 * called on a symbolic link and O_NOFOLLOW is set.
281286359Sed		 */
282286359Sed		if (error == EMLINK)
283286359Sed			error = ELOOP;
284286359Sed		fdrop(fp, td);
285286359Sed		return (error);
286286359Sed	}
287286359Sed	NDFREE(&nd, NDF_ONLY_PNBUF);
288286359Sed	filecaps_free(&nd.ni_filecaps);
289286359Sed	fp->f_vnode = vp = nd.ni_vp;
290286359Sed
291286359Sed	/* Install vnode operations if no custom operations are provided. */
292286359Sed	if (fp->f_ops == &badfileops) {
293286359Sed		fp->f_seqcount = 1;
294286359Sed		finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK),
295286359Sed		    DTYPE_VNODE, vp, &vnops);
296286359Sed	}
297286359Sed	VOP_UNLOCK(vp, 0);
298286359Sed
299286359Sed	/* Truncate file. */
300286359Sed	if (fflags & O_TRUNC) {
301286359Sed		error = fo_truncate(fp, 0, td->td_ucred, td);
302286359Sed		if (error != 0) {
303286359Sed			fdrop(fp, td);
304286359Sed			return (error);
305286359Sed		}
306286359Sed	}
307286359Sed
308286359Sedsuccess:
309286359Sed	/* Determine which Capsicum rights to set on the file descriptor. */
310286359Sed	cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp),
311286359Sed	    &fds.fs_rights_base, &fds.fs_rights_inheriting);
312286359Sed	cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting,
313286359Sed	    &fcaps.fc_rights);
314286359Sed	if (cap_rights_is_set(&fcaps.fc_rights))
315286359Sed		fcaps.fc_fcntls = CAP_FCNTL_SETFL;
316286359Sed
317286359Sed	error = finstall(td, fp, &fd, fflags, &fcaps);
318286359Sed	fdrop(fp, td);
319286359Sed	if (error != 0)
320286359Sed		return (error);
321286359Sed	td->td_retval[0] = fd;
322286359Sed	return (0);
323285307Sed}
324285307Sed
325285998Sed/* Converts a FreeBSD directory entry structure and writes it to userspace. */
326285998Sedstatic int
327285998Sedwrite_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio)
328285998Sed{
329285998Sed	cloudabi_dirent_t cde = {
330285998Sed		.d_next = cookie,
331285998Sed		.d_ino = bde->d_fileno,
332285998Sed		.d_namlen = bde->d_namlen,
333285998Sed	};
334285998Sed	size_t len;
335285998Sed	int error;
336285998Sed
337285998Sed	/* Convert file type. */
338285998Sed	switch (bde->d_type) {
339285998Sed	case DT_BLK:
340285998Sed		cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE;
341285998Sed		break;
342285998Sed	case DT_CHR:
343285998Sed		cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
344285998Sed		break;
345285998Sed	case DT_DIR:
346285998Sed		cde.d_type = CLOUDABI_FILETYPE_DIRECTORY;
347285998Sed		break;
348285998Sed	case DT_FIFO:
349285998Sed		cde.d_type = CLOUDABI_FILETYPE_FIFO;
350285998Sed		break;
351285998Sed	case DT_LNK:
352285998Sed		cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
353285998Sed		break;
354285998Sed	case DT_REG:
355285998Sed		cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE;
356285998Sed		break;
357285998Sed	case DT_SOCK:
358285998Sed		/* The exact socket type cannot be derived. */
359285998Sed		cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM;
360285998Sed		break;
361285998Sed	default:
362285998Sed		cde.d_type = CLOUDABI_FILETYPE_UNKNOWN;
363285998Sed		break;
364285998Sed	}
365285998Sed
366285998Sed	/* Write directory entry structure. */
367285998Sed	len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid;
368285998Sed	error = uiomove(&cde, len, uio);
369285998Sed	if (error != 0)
370285998Sed		return (error);
371285998Sed
372285998Sed	/* Write filename. */
373285998Sed	len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid;
374285998Sed	return (uiomove(bde->d_name, len, uio));
375285998Sed}
376285998Sed
377285307Sedint
378285307Sedcloudabi_sys_file_readdir(struct thread *td,
379285307Sed    struct cloudabi_sys_file_readdir_args *uap)
380285307Sed{
381285998Sed	struct iovec iov = {
382285998Sed		.iov_base = uap->buf,
383285998Sed		.iov_len = uap->nbyte
384285998Sed	};
385285998Sed	struct uio uio = {
386285998Sed		.uio_iov = &iov,
387285998Sed		.uio_iovcnt = 1,
388285998Sed		.uio_resid = iov.iov_len,
389285998Sed		.uio_segflg = UIO_USERSPACE,
390285998Sed		.uio_rw = UIO_READ,
391285998Sed		.uio_td = td
392285998Sed	};
393285998Sed	struct file *fp;
394285998Sed	struct vnode *vp;
395285998Sed	void *readbuf;
396285998Sed	cap_rights_t rights;
397285998Sed	cloudabi_dircookie_t offset;
398285998Sed	int error;
399285307Sed
400285998Sed	/* Obtain directory vnode. */
401285998Sed	error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp);
402285998Sed	if (error != 0) {
403285998Sed		if (error == EINVAL)
404285998Sed			return (ENOTDIR);
405285998Sed		return (error);
406285998Sed	}
407285998Sed	if ((fp->f_flag & FREAD) == 0) {
408285998Sed		fdrop(fp, td);
409285998Sed		return (EBADF);
410285998Sed	}
411285998Sed
412285998Sed	/*
413285998Sed	 * Call VOP_READDIR() and convert resulting data until the user
414285998Sed	 * provided buffer is filled.
415285998Sed	 */
416285998Sed	readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
417285998Sed	offset = uap->cookie;
418285998Sed	vp = fp->f_vnode;
419285998Sed	while (uio.uio_resid > 0) {
420285998Sed		struct iovec readiov = {
421285998Sed			.iov_base = readbuf,
422285998Sed			.iov_len = MAXBSIZE
423285998Sed		};
424285998Sed		struct uio readuio = {
425285998Sed			.uio_iov = &readiov,
426285998Sed			.uio_iovcnt = 1,
427285998Sed			.uio_rw = UIO_READ,
428285998Sed			.uio_segflg = UIO_SYSSPACE,
429285998Sed			.uio_td = td,
430285998Sed			.uio_resid = MAXBSIZE,
431285998Sed			.uio_offset = offset
432285998Sed		};
433285998Sed		struct dirent *bde;
434285998Sed		unsigned long *cookies, *cookie;
435285998Sed		size_t readbuflen;
436285998Sed		int eof, ncookies;
437285998Sed
438285998Sed		/* Validate file type. */
439285998Sed		vn_lock(vp, LK_SHARED | LK_RETRY);
440285998Sed		if (vp->v_type != VDIR) {
441285998Sed			VOP_UNLOCK(vp, 0);
442285998Sed			error = ENOTDIR;
443285998Sed			goto done;
444285998Sed		}
445285998Sed#ifdef MAC
446285998Sed		error = mac_vnode_check_readdir(td->td_ucred, vp);
447285998Sed		if (error != 0) {
448285998Sed			VOP_UNLOCK(vp, 0);
449285998Sed			goto done;
450285998Sed		}
451285998Sed#endif /* MAC */
452285998Sed
453285998Sed		/* Read new directory entries. */
454285998Sed		cookies = NULL;
455285998Sed		ncookies = 0;
456285998Sed		error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof,
457285998Sed		    &ncookies, &cookies);
458285998Sed		VOP_UNLOCK(vp, 0);
459285998Sed		if (error != 0)
460285998Sed			goto done;
461285998Sed
462285998Sed		/* Convert entries to CloudABI's format. */
463285998Sed		readbuflen = MAXBSIZE - readuio.uio_resid;
464285998Sed		bde = readbuf;
465285998Sed		cookie = cookies;
466285998Sed		while (readbuflen >= offsetof(struct dirent, d_name) &&
467285998Sed		    uio.uio_resid > 0 && ncookies > 0) {
468285998Sed			/* Ensure that the returned offset always increases. */
469285998Sed			if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 &&
470285998Sed			    *cookie > offset) {
471285998Sed				error = write_dirent(bde, *cookie, &uio);
472285998Sed				if (error != 0) {
473285998Sed					free(cookies, M_TEMP);
474285998Sed					goto done;
475285998Sed				}
476285998Sed			}
477285998Sed
478285998Sed			if (offset < *cookie)
479285998Sed				offset = *cookie;
480285998Sed			++cookie;
481285998Sed			--ncookies;
482285998Sed			readbuflen -= bde->d_reclen;
483285998Sed			bde = (struct dirent *)((char *)bde + bde->d_reclen);
484285998Sed		}
485285998Sed		free(cookies, M_TEMP);
486285998Sed		if (eof)
487285998Sed			break;
488285998Sed	}
489285998Sed
490285998Seddone:
491285998Sed	fdrop(fp, td);
492285998Sed	free(readbuf, M_TEMP);
493285998Sed	if (error != 0)
494285998Sed		return (error);
495285998Sed
496285998Sed	/* Return number of bytes copied to userspace. */
497285998Sed	td->td_retval[0] = uap->nbyte - uio.uio_resid;
498285998Sed	return (0);
499285307Sed}
500285307Sed
501285307Sedint
502285307Sedcloudabi_sys_file_readlink(struct thread *td,
503285307Sed    struct cloudabi_sys_file_readlink_args *uap)
504285307Sed{
505285834Sed	char *path;
506285834Sed	int error;
507285307Sed
508285834Sed	error = copyin_path(uap->path, uap->pathlen, &path);
509285834Sed	if (error != 0)
510285834Sed		return (error);
511285834Sed
512285834Sed	error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE,
513285834Sed	    uap->buf, UIO_USERSPACE, uap->bufsize);
514285834Sed	cloudabi_freestr(path);
515285834Sed	return (error);
516285307Sed}
517285307Sed
518285307Sedint
519285307Sedcloudabi_sys_file_rename(struct thread *td,
520285307Sed    struct cloudabi_sys_file_rename_args *uap)
521285307Sed{
522285834Sed	char *old, *new;
523285834Sed	int error;
524285307Sed
525285834Sed	error = copyin_path(uap->old, uap->oldlen, &old);
526285834Sed	if (error != 0)
527285834Sed		return (error);
528285834Sed	error = copyin_path(uap->new, uap->newlen, &new);
529285834Sed	if (error != 0) {
530285834Sed		cloudabi_freestr(old);
531285834Sed		return (error);
532285834Sed	}
533285834Sed
534285834Sed	error = kern_renameat(td, uap->oldfd, old, uap->newfd, new,
535285834Sed	    UIO_SYSSPACE);
536285834Sed	cloudabi_freestr(old);
537285834Sed	cloudabi_freestr(new);
538285834Sed	return (error);
539285307Sed}
540285307Sed
541285930Sed/* Converts a FreeBSD stat structure to a CloudABI stat structure. */
542285930Sedstatic void
543285930Sedconvert_stat(const struct stat *sb, cloudabi_filestat_t *csb)
544285930Sed{
545285930Sed	cloudabi_filestat_t res = {
546285930Sed		.st_dev		= sb->st_dev,
547285930Sed		.st_ino		= sb->st_ino,
548285930Sed		.st_nlink	= sb->st_nlink,
549285930Sed		.st_size	= sb->st_size,
550285930Sed	};
551285930Sed
552285930Sed	cloudabi_convert_timespec(&sb->st_atim, &res.st_atim);
553285930Sed	cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim);
554285930Sed	cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim);
555285930Sed	*csb = res;
556285930Sed}
557285930Sed
558285307Sedint
559285307Sedcloudabi_sys_file_stat_fget(struct thread *td,
560285307Sed    struct cloudabi_sys_file_stat_fget_args *uap)
561285307Sed{
562285930Sed	struct stat sb;
563285930Sed	cloudabi_filestat_t csb;
564285930Sed	struct file *fp;
565285930Sed	cap_rights_t rights;
566285930Sed	cloudabi_filetype_t filetype;
567285930Sed	int error;
568285307Sed
569285930Sed	/* Fetch file descriptor attributes. */
570285930Sed	error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp);
571285930Sed	if (error != 0)
572285930Sed		return (error);
573285930Sed	error = fo_stat(fp, &sb, td->td_ucred, td);
574285930Sed	if (error != 0) {
575285930Sed		fdrop(fp, td);
576285930Sed		return (error);
577285930Sed	}
578285930Sed	filetype = cloudabi_convert_filetype(fp);
579285930Sed	fdrop(fp, td);
580285930Sed
581285930Sed	/* Convert attributes to CloudABI's format. */
582285930Sed	convert_stat(&sb, &csb);
583285930Sed	csb.st_filetype = filetype;
584285930Sed	return (copyout(&csb, uap->buf, sizeof(csb)));
585285307Sed}
586285307Sed
587285954Sed/* Converts timestamps to arguments to futimens() and utimensat(). */
588285954Sedstatic void
589285954Sedconvert_utimens_arguments(const cloudabi_filestat_t *fs,
590285954Sed    cloudabi_fsflags_t flags, struct timespec *ts)
591285954Sed{
592285954Sed
593285954Sed	if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) {
594285954Sed		ts[0].tv_nsec = UTIME_NOW;
595285954Sed	} else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) {
596285954Sed		ts[0].tv_sec = fs->st_atim / 1000000000;
597285954Sed		ts[0].tv_nsec = fs->st_atim % 1000000000;
598285954Sed	} else {
599285954Sed		ts[0].tv_nsec = UTIME_OMIT;
600285954Sed	}
601285954Sed
602285954Sed	if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) {
603285954Sed		ts[1].tv_nsec = UTIME_NOW;
604285954Sed	} else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) {
605285954Sed		ts[1].tv_sec = fs->st_mtim / 1000000000;
606285954Sed		ts[1].tv_nsec = fs->st_mtim % 1000000000;
607285954Sed	} else {
608285954Sed		ts[1].tv_nsec = UTIME_OMIT;
609285954Sed	}
610285954Sed}
611285954Sed
612285307Sedint
613285307Sedcloudabi_sys_file_stat_fput(struct thread *td,
614285307Sed    struct cloudabi_sys_file_stat_fput_args *uap)
615285307Sed{
616285954Sed	cloudabi_filestat_t fs;
617285954Sed	struct timespec ts[2];
618285954Sed	int error;
619285307Sed
620285954Sed	error = copyin(uap->buf, &fs, sizeof(fs));
621285954Sed	if (error != 0)
622285954Sed		return (error);
623285954Sed
624285954Sed	/*
625285954Sed	 * Only support truncation and timestamp modification separately
626285954Sed	 * for now, to prevent unnecessary code duplication.
627285954Sed	 */
628285954Sed	if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) {
629285954Sed		/* Call into kern_ftruncate() for file truncation. */
630285954Sed		if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0)
631285954Sed			return (EINVAL);
632285954Sed		return (kern_ftruncate(td, uap->fd, fs.st_size));
633285954Sed	} else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM |
634285954Sed	    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
635285954Sed	    CLOUDABI_FILESTAT_MTIM_NOW)) != 0) {
636285954Sed		/* Call into kern_futimens() for timestamp modification. */
637285954Sed		if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM |
638285954Sed		    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
639285954Sed		    CLOUDABI_FILESTAT_MTIM_NOW)) != 0)
640285954Sed			return (EINVAL);
641285954Sed		convert_utimens_arguments(&fs, uap->flags, ts);
642285954Sed		return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE));
643285954Sed	}
644285954Sed	return (EINVAL);
645285307Sed}
646285307Sed
647285307Sedint
648285307Sedcloudabi_sys_file_stat_get(struct thread *td,
649285307Sed    struct cloudabi_sys_file_stat_get_args *uap)
650285307Sed{
651285930Sed	struct stat sb;
652285930Sed	cloudabi_filestat_t csb;
653285930Sed	char *path;
654285930Sed	int error;
655285307Sed
656285930Sed	error = copyin_path(uap->path, uap->pathlen, &path);
657285930Sed	if (error != 0)
658285930Sed		return (error);
659285930Sed
660285930Sed	error = kern_statat(td,
661297247Sed	    (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 :
662297247Sed	    AT_SYMLINK_NOFOLLOW, uap->fd.fd, path, UIO_SYSSPACE, &sb, NULL);
663285930Sed	cloudabi_freestr(path);
664285930Sed	if (error != 0)
665285930Sed		return (error);
666285930Sed
667285930Sed	/* Convert results and return them. */
668285930Sed	convert_stat(&sb, &csb);
669285930Sed	if (S_ISBLK(sb.st_mode))
670285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE;
671285930Sed	else if (S_ISCHR(sb.st_mode))
672285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
673285930Sed	else if (S_ISDIR(sb.st_mode))
674285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY;
675285930Sed	else if (S_ISFIFO(sb.st_mode))
676285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_FIFO;
677285930Sed	else if (S_ISREG(sb.st_mode))
678285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE;
679285930Sed	else if (S_ISSOCK(sb.st_mode)) {
680285930Sed		/* Inaccurate, but the best that we can do. */
681285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM;
682285930Sed	} else if (S_ISLNK(sb.st_mode))
683285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
684285930Sed	else
685285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN;
686285930Sed	return (copyout(&csb, uap->buf, sizeof(csb)));
687285307Sed}
688285307Sed
689285307Sedint
690285307Sedcloudabi_sys_file_stat_put(struct thread *td,
691285307Sed    struct cloudabi_sys_file_stat_put_args *uap)
692285307Sed{
693285954Sed	cloudabi_filestat_t fs;
694285954Sed	struct timespec ts[2];
695285954Sed	char *path;
696285954Sed	int error;
697285307Sed
698285954Sed	/*
699285954Sed	 * Only support timestamp modification for now, as there is no
700285954Sed	 * truncateat().
701285954Sed	 */
702285954Sed	if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM |
703285954Sed	    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
704285954Sed	    CLOUDABI_FILESTAT_MTIM_NOW)) != 0)
705285954Sed		return (EINVAL);
706285954Sed
707285954Sed	error = copyin(uap->buf, &fs, sizeof(fs));
708285954Sed	if (error != 0)
709285954Sed		return (error);
710285954Sed	error = copyin_path(uap->path, uap->pathlen, &path);
711285954Sed	if (error != 0)
712285954Sed		return (error);
713285954Sed
714285954Sed	convert_utimens_arguments(&fs, uap->flags, ts);
715297247Sed	error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts,
716297247Sed	    UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ?
717285954Sed	    0 : AT_SYMLINK_NOFOLLOW);
718285954Sed	cloudabi_freestr(path);
719285954Sed	return (error);
720285307Sed}
721285307Sed
722285307Sedint
723285307Sedcloudabi_sys_file_symlink(struct thread *td,
724285307Sed    struct cloudabi_sys_file_symlink_args *uap)
725285307Sed{
726285834Sed	char *path1, *path2;
727285834Sed	int error;
728285307Sed
729285834Sed	error = copyin_path(uap->path1, uap->path1len, &path1);
730285834Sed	if (error != 0)
731285834Sed		return (error);
732285834Sed	error = copyin_path(uap->path2, uap->path2len, &path2);
733285834Sed	if (error != 0) {
734285834Sed		cloudabi_freestr(path1);
735285834Sed		return (error);
736285834Sed	}
737285834Sed
738285834Sed	error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE);
739285834Sed	cloudabi_freestr(path1);
740285834Sed	cloudabi_freestr(path2);
741285834Sed	return (error);
742285307Sed}
743285307Sed
744285307Sedint
745285307Sedcloudabi_sys_file_unlink(struct thread *td,
746285307Sed    struct cloudabi_sys_file_unlink_args *uap)
747285307Sed{
748285834Sed	char *path;
749285834Sed	int error;
750285307Sed
751285834Sed	error = copyin_path(uap->path, uap->pathlen, &path);
752285834Sed	if (error != 0)
753285834Sed		return (error);
754285834Sed
755297247Sed	if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR)
756285834Sed		error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE);
757285834Sed	else
758285834Sed		error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0);
759285834Sed	cloudabi_freestr(path);
760285834Sed	return (error);
761285307Sed}
762