cloudabi_file.c revision 286633
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: head/sys/compat/cloudabi/cloudabi_file.c 286633 2015-08-11 14:08:46Z ed $");
28
29#include <sys/param.h>
30#include <sys/capsicum.h>
31#include <sys/dirent.h>
32#include <sys/fcntl.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/namei.h>
36#include <sys/proc.h>
37#include <sys/stat.h>
38#include <sys/syscallsubr.h>
39#include <sys/uio.h>
40#include <sys/vnode.h>
41
42#include <compat/cloudabi/cloudabi_proto.h>
43#include <compat/cloudabi/cloudabi_syscalldefs.h>
44#include <compat/cloudabi/cloudabi_util.h>
45
46#include <security/mac/mac_framework.h>
47
48static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames");
49
50/*
51 * Copying pathnames from userspace to kernelspace.
52 *
53 * Unlike most operating systems, CloudABI doesn't use null-terminated
54 * pathname strings. Processes always pass pathnames to the kernel by
55 * providing a base pointer and a length. This has a couple of reasons:
56 *
57 * - It makes it easier to use CloudABI in combination with programming
58 *   languages other than C, that may use non-null terminated strings.
59 * - It allows for calling system calls on individual components of the
60 *   pathname without modifying the input string.
61 *
62 * The function below copies in pathname strings and null-terminates it.
63 * It also ensure that the string itself does not contain any null
64 * bytes.
65 *
66 * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass
67 *           in unterminated pathname strings, so we can do away with
68 *           the copying.
69 */
70
71static int
72copyin_path(const char *uaddr, size_t len, char **result)
73{
74	char *buf;
75	int error;
76
77	if (len >= PATH_MAX)
78		return (ENAMETOOLONG);
79	buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK);
80	error = copyin(uaddr, buf, len);
81	if (error != 0) {
82		free(buf, M_CLOUDABI_PATH);
83		return (error);
84	}
85	if (memchr(buf, '\0', len) != NULL) {
86		free(buf, M_CLOUDABI_PATH);
87		return (EINVAL);
88	}
89	buf[len] = '\0';
90	*result = buf;
91	return (0);
92}
93
94static void
95cloudabi_freestr(char *buf)
96{
97
98	free(buf, M_CLOUDABI_PATH);
99}
100
101int
102cloudabi_sys_file_advise(struct thread *td,
103    struct cloudabi_sys_file_advise_args *uap)
104{
105	int advice;
106
107	switch (uap->advice) {
108	case CLOUDABI_ADVICE_DONTNEED:
109		advice = POSIX_FADV_DONTNEED;
110		break;
111	case CLOUDABI_ADVICE_NOREUSE:
112		advice = POSIX_FADV_NOREUSE;
113		break;
114	case CLOUDABI_ADVICE_NORMAL:
115		advice = POSIX_FADV_NORMAL;
116		break;
117	case CLOUDABI_ADVICE_RANDOM:
118		advice = POSIX_FADV_RANDOM;
119		break;
120	case CLOUDABI_ADVICE_SEQUENTIAL:
121		advice = POSIX_FADV_SEQUENTIAL;
122		break;
123	case CLOUDABI_ADVICE_WILLNEED:
124		advice = POSIX_FADV_WILLNEED;
125		break;
126	default:
127		return (EINVAL);
128	}
129
130	return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice));
131}
132
133int
134cloudabi_sys_file_allocate(struct thread *td,
135    struct cloudabi_sys_file_allocate_args *uap)
136{
137
138	return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len));
139}
140
141int
142cloudabi_sys_file_create(struct thread *td,
143    struct cloudabi_sys_file_create_args *uap)
144{
145	char *path;
146	int error;
147
148	error = copyin_path(uap->path, uap->pathlen, &path);
149	if (error != 0)
150		return (error);
151
152	/*
153	 * CloudABI processes cannot interact with UNIX credentials and
154	 * permissions. Depend on the umask that is set prior to
155	 * execution to restrict the file permissions.
156	 */
157	switch (uap->type) {
158	case CLOUDABI_FILETYPE_DIRECTORY:
159		error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777);
160		break;
161	case CLOUDABI_FILETYPE_FIFO:
162		error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666);
163		break;
164	default:
165		error = EINVAL;
166		break;
167	}
168	cloudabi_freestr(path);
169	return (error);
170}
171
172int
173cloudabi_sys_file_link(struct thread *td,
174    struct cloudabi_sys_file_link_args *uap)
175{
176	char *path1, *path2;
177	int error;
178
179	error = copyin_path(uap->path1, uap->path1len, &path1);
180	if (error != 0)
181		return (error);
182	error = copyin_path(uap->path2, uap->path2len, &path2);
183	if (error != 0) {
184		cloudabi_freestr(path1);
185		return (error);
186	}
187
188	error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2,
189	    UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ?
190	    FOLLOW : NOFOLLOW);
191	cloudabi_freestr(path1);
192	cloudabi_freestr(path2);
193	return (error);
194}
195
196int
197cloudabi_sys_file_open(struct thread *td,
198    struct cloudabi_sys_file_open_args *uap)
199{
200	cloudabi_fdstat_t fds;
201	cap_rights_t rights;
202	struct filecaps fcaps = {};
203	struct nameidata nd;
204	struct file *fp;
205	struct vnode *vp;
206	char *path;
207	int error, fd, fflags;
208	bool read, write;
209
210	error = copyin(uap->fds, &fds, sizeof(fds));
211	if (error != 0)
212		return (error);
213
214	/* All the requested rights should be set on the descriptor. */
215	error = cloudabi_convert_rights(
216	    fds.fs_rights_base | fds.fs_rights_inheriting, &rights);
217	if (error != 0)
218		return (error);
219	cap_rights_set(&rights, CAP_LOOKUP);
220
221	/* Convert rights to corresponding access mode. */
222	read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ |
223	    CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0;
224	write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC |
225	    CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE |
226	    CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0;
227	fflags = write ? read ? FREAD | FWRITE : FWRITE : FREAD;
228
229	/* Convert open flags. */
230	if ((uap->oflags & CLOUDABI_O_CREAT) != 0) {
231		fflags |= O_CREAT;
232		cap_rights_set(&rights, CAP_CREATE);
233	}
234	if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0)
235		fflags |= O_DIRECTORY;
236	if ((uap->oflags & CLOUDABI_O_EXCL) != 0)
237		fflags |= O_EXCL;
238	if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) {
239		fflags |= O_TRUNC;
240		cap_rights_set(&rights, CAP_FTRUNCATE);
241	}
242	if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0)
243		fflags |= O_APPEND;
244	if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0)
245		fflags |= O_NONBLOCK;
246	if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC |
247	    CLOUDABI_FDFLAG_RSYNC)) != 0) {
248		fflags |= O_SYNC;
249		cap_rights_set(&rights, CAP_FSYNC);
250	}
251	if ((uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0)
252		fflags |= O_NOFOLLOW;
253	if (write && (fflags & (O_APPEND | O_TRUNC)) == 0)
254		cap_rights_set(&rights, CAP_SEEK);
255
256	/* Allocate new file descriptor. */
257	error = falloc_noinstall(td, &fp);
258	if (error != 0)
259		return (error);
260	fp->f_flag = fflags & FMASK;
261
262	/* Open path. */
263	error = copyin_path(uap->path, uap->pathlen, &path);
264	if (error != 0) {
265		fdrop(fp, td);
266		return (error);
267	}
268	NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->fd,
269	    &rights, td);
270	error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp);
271	cloudabi_freestr(path);
272	if (error != 0) {
273		/* Custom operations provided. */
274		if (error == ENXIO && fp->f_ops != &badfileops)
275			goto success;
276
277		/*
278		 * POSIX compliance: return ELOOP in case openat() is
279		 * called on a symbolic link and O_NOFOLLOW is set.
280		 */
281		if (error == EMLINK)
282			error = ELOOP;
283		fdrop(fp, td);
284		return (error);
285	}
286	NDFREE(&nd, NDF_ONLY_PNBUF);
287	filecaps_free(&nd.ni_filecaps);
288	fp->f_vnode = vp = nd.ni_vp;
289
290	/* Install vnode operations if no custom operations are provided. */
291	if (fp->f_ops == &badfileops) {
292		fp->f_seqcount = 1;
293		finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK),
294		    DTYPE_VNODE, vp, &vnops);
295	}
296	VOP_UNLOCK(vp, 0);
297
298	/* Truncate file. */
299	if (fflags & O_TRUNC) {
300		error = fo_truncate(fp, 0, td->td_ucred, td);
301		if (error != 0) {
302			fdrop(fp, td);
303			return (error);
304		}
305	}
306
307success:
308	/* Determine which Capsicum rights to set on the file descriptor. */
309	cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp),
310	    &fds.fs_rights_base, &fds.fs_rights_inheriting);
311	cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting,
312	    &fcaps.fc_rights);
313	if (cap_rights_is_set(&fcaps.fc_rights))
314		fcaps.fc_fcntls = CAP_FCNTL_SETFL;
315
316	error = finstall(td, fp, &fd, fflags, &fcaps);
317	fdrop(fp, td);
318	if (error != 0)
319		return (error);
320	td->td_retval[0] = fd;
321	return (0);
322}
323
324/* Converts a FreeBSD directory entry structure and writes it to userspace. */
325static int
326write_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio)
327{
328	cloudabi_dirent_t cde = {
329		.d_next = cookie,
330		.d_ino = bde->d_fileno,
331		.d_namlen = bde->d_namlen,
332	};
333	size_t len;
334	int error;
335
336	/* Convert file type. */
337	switch (bde->d_type) {
338	case DT_BLK:
339		cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE;
340		break;
341	case DT_CHR:
342		cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
343		break;
344	case DT_DIR:
345		cde.d_type = CLOUDABI_FILETYPE_DIRECTORY;
346		break;
347	case DT_FIFO:
348		cde.d_type = CLOUDABI_FILETYPE_FIFO;
349		break;
350	case DT_LNK:
351		cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
352		break;
353	case DT_REG:
354		cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE;
355		break;
356	case DT_SOCK:
357		/* The exact socket type cannot be derived. */
358		cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM;
359		break;
360	default:
361		cde.d_type = CLOUDABI_FILETYPE_UNKNOWN;
362		break;
363	}
364
365	/* Write directory entry structure. */
366	len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid;
367	error = uiomove(&cde, len, uio);
368	if (error != 0)
369		return (error);
370
371	/* Write filename. */
372	len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid;
373	return (uiomove(bde->d_name, len, uio));
374}
375
376int
377cloudabi_sys_file_readdir(struct thread *td,
378    struct cloudabi_sys_file_readdir_args *uap)
379{
380	struct iovec iov = {
381		.iov_base = uap->buf,
382		.iov_len = uap->nbyte
383	};
384	struct uio uio = {
385		.uio_iov = &iov,
386		.uio_iovcnt = 1,
387		.uio_resid = iov.iov_len,
388		.uio_segflg = UIO_USERSPACE,
389		.uio_rw = UIO_READ,
390		.uio_td = td
391	};
392	struct file *fp;
393	struct vnode *vp;
394	void *readbuf;
395	cap_rights_t rights;
396	cloudabi_dircookie_t offset;
397	int error;
398
399	/* Obtain directory vnode. */
400	error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp);
401	if (error != 0) {
402		if (error == EINVAL)
403			return (ENOTDIR);
404		return (error);
405	}
406	if ((fp->f_flag & FREAD) == 0) {
407		fdrop(fp, td);
408		return (EBADF);
409	}
410
411	/*
412	 * Call VOP_READDIR() and convert resulting data until the user
413	 * provided buffer is filled.
414	 */
415	readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
416	offset = uap->cookie;
417	vp = fp->f_vnode;
418	while (uio.uio_resid > 0) {
419		struct iovec readiov = {
420			.iov_base = readbuf,
421			.iov_len = MAXBSIZE
422		};
423		struct uio readuio = {
424			.uio_iov = &readiov,
425			.uio_iovcnt = 1,
426			.uio_rw = UIO_READ,
427			.uio_segflg = UIO_SYSSPACE,
428			.uio_td = td,
429			.uio_resid = MAXBSIZE,
430			.uio_offset = offset
431		};
432		struct dirent *bde;
433		unsigned long *cookies, *cookie;
434		size_t readbuflen;
435		int eof, ncookies;
436
437		/* Validate file type. */
438		vn_lock(vp, LK_SHARED | LK_RETRY);
439		if (vp->v_type != VDIR) {
440			VOP_UNLOCK(vp, 0);
441			error = ENOTDIR;
442			goto done;
443		}
444#ifdef MAC
445		error = mac_vnode_check_readdir(td->td_ucred, vp);
446		if (error != 0) {
447			VOP_UNLOCK(vp, 0);
448			goto done;
449		}
450#endif /* MAC */
451
452		/* Read new directory entries. */
453		cookies = NULL;
454		ncookies = 0;
455		error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof,
456		    &ncookies, &cookies);
457		VOP_UNLOCK(vp, 0);
458		if (error != 0)
459			goto done;
460
461		/* Convert entries to CloudABI's format. */
462		readbuflen = MAXBSIZE - readuio.uio_resid;
463		bde = readbuf;
464		cookie = cookies;
465		while (readbuflen >= offsetof(struct dirent, d_name) &&
466		    uio.uio_resid > 0 && ncookies > 0) {
467			/* Ensure that the returned offset always increases. */
468			if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 &&
469			    *cookie > offset) {
470				error = write_dirent(bde, *cookie, &uio);
471				if (error != 0) {
472					free(cookies, M_TEMP);
473					goto done;
474				}
475			}
476
477			if (offset < *cookie)
478				offset = *cookie;
479			++cookie;
480			--ncookies;
481			readbuflen -= bde->d_reclen;
482			bde = (struct dirent *)((char *)bde + bde->d_reclen);
483		}
484		free(cookies, M_TEMP);
485		if (eof)
486			break;
487	}
488
489done:
490	fdrop(fp, td);
491	free(readbuf, M_TEMP);
492	if (error != 0)
493		return (error);
494
495	/* Return number of bytes copied to userspace. */
496	td->td_retval[0] = uap->nbyte - uio.uio_resid;
497	return (0);
498}
499
500int
501cloudabi_sys_file_readlink(struct thread *td,
502    struct cloudabi_sys_file_readlink_args *uap)
503{
504	char *path;
505	int error;
506
507	error = copyin_path(uap->path, uap->pathlen, &path);
508	if (error != 0)
509		return (error);
510
511	error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE,
512	    uap->buf, UIO_USERSPACE, uap->bufsize);
513	cloudabi_freestr(path);
514	return (error);
515}
516
517int
518cloudabi_sys_file_rename(struct thread *td,
519    struct cloudabi_sys_file_rename_args *uap)
520{
521	char *old, *new;
522	int error;
523
524	error = copyin_path(uap->old, uap->oldlen, &old);
525	if (error != 0)
526		return (error);
527	error = copyin_path(uap->new, uap->newlen, &new);
528	if (error != 0) {
529		cloudabi_freestr(old);
530		return (error);
531	}
532
533	error = kern_renameat(td, uap->oldfd, old, uap->newfd, new,
534	    UIO_SYSSPACE);
535	cloudabi_freestr(old);
536	cloudabi_freestr(new);
537	return (error);
538}
539
540/* Converts a FreeBSD stat structure to a CloudABI stat structure. */
541static void
542convert_stat(const struct stat *sb, cloudabi_filestat_t *csb)
543{
544	cloudabi_filestat_t res = {
545		.st_dev		= sb->st_dev,
546		.st_ino		= sb->st_ino,
547		.st_nlink	= sb->st_nlink,
548		.st_size	= sb->st_size,
549	};
550
551	cloudabi_convert_timespec(&sb->st_atim, &res.st_atim);
552	cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim);
553	cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim);
554	*csb = res;
555}
556
557int
558cloudabi_sys_file_stat_fget(struct thread *td,
559    struct cloudabi_sys_file_stat_fget_args *uap)
560{
561	struct stat sb;
562	cloudabi_filestat_t csb;
563	struct file *fp;
564	cap_rights_t rights;
565	cloudabi_filetype_t filetype;
566	int error;
567
568	/* Fetch file descriptor attributes. */
569	error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp);
570	if (error != 0)
571		return (error);
572	error = fo_stat(fp, &sb, td->td_ucred, td);
573	if (error != 0) {
574		fdrop(fp, td);
575		return (error);
576	}
577	filetype = cloudabi_convert_filetype(fp);
578	fdrop(fp, td);
579
580	/* Convert attributes to CloudABI's format. */
581	convert_stat(&sb, &csb);
582	csb.st_filetype = filetype;
583	return (copyout(&csb, uap->buf, sizeof(csb)));
584}
585
586/* Converts timestamps to arguments to futimens() and utimensat(). */
587static void
588convert_utimens_arguments(const cloudabi_filestat_t *fs,
589    cloudabi_fsflags_t flags, struct timespec *ts)
590{
591
592	if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) {
593		ts[0].tv_nsec = UTIME_NOW;
594	} else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) {
595		ts[0].tv_sec = fs->st_atim / 1000000000;
596		ts[0].tv_nsec = fs->st_atim % 1000000000;
597	} else {
598		ts[0].tv_nsec = UTIME_OMIT;
599	}
600
601	if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) {
602		ts[1].tv_nsec = UTIME_NOW;
603	} else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) {
604		ts[1].tv_sec = fs->st_mtim / 1000000000;
605		ts[1].tv_nsec = fs->st_mtim % 1000000000;
606	} else {
607		ts[1].tv_nsec = UTIME_OMIT;
608	}
609}
610
611int
612cloudabi_sys_file_stat_fput(struct thread *td,
613    struct cloudabi_sys_file_stat_fput_args *uap)
614{
615	cloudabi_filestat_t fs;
616	struct timespec ts[2];
617	int error;
618
619	error = copyin(uap->buf, &fs, sizeof(fs));
620	if (error != 0)
621		return (error);
622
623	/*
624	 * Only support truncation and timestamp modification separately
625	 * for now, to prevent unnecessary code duplication.
626	 */
627	if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) {
628		/* Call into kern_ftruncate() for file truncation. */
629		if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0)
630			return (EINVAL);
631		return (kern_ftruncate(td, uap->fd, fs.st_size));
632	} else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM |
633	    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
634	    CLOUDABI_FILESTAT_MTIM_NOW)) != 0) {
635		/* Call into kern_futimens() for timestamp modification. */
636		if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM |
637		    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
638		    CLOUDABI_FILESTAT_MTIM_NOW)) != 0)
639			return (EINVAL);
640		convert_utimens_arguments(&fs, uap->flags, ts);
641		return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE));
642	}
643	return (EINVAL);
644}
645
646int
647cloudabi_sys_file_stat_get(struct thread *td,
648    struct cloudabi_sys_file_stat_get_args *uap)
649{
650	struct stat sb;
651	cloudabi_filestat_t csb;
652	char *path;
653	int error;
654
655	error = copyin_path(uap->path, uap->pathlen, &path);
656	if (error != 0)
657		return (error);
658
659	error = kern_statat(td,
660	    (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 :
661	    AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL);
662	cloudabi_freestr(path);
663	if (error != 0)
664		return (error);
665
666	/* Convert results and return them. */
667	convert_stat(&sb, &csb);
668	if (S_ISBLK(sb.st_mode))
669		csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE;
670	else if (S_ISCHR(sb.st_mode))
671		csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
672	else if (S_ISDIR(sb.st_mode))
673		csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY;
674	else if (S_ISFIFO(sb.st_mode))
675		csb.st_filetype = CLOUDABI_FILETYPE_FIFO;
676	else if (S_ISREG(sb.st_mode))
677		csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE;
678	else if (S_ISSOCK(sb.st_mode)) {
679		/* Inaccurate, but the best that we can do. */
680		csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM;
681	} else if (S_ISLNK(sb.st_mode))
682		csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
683	else
684		csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN;
685	return (copyout(&csb, uap->buf, sizeof(csb)));
686}
687
688int
689cloudabi_sys_file_stat_put(struct thread *td,
690    struct cloudabi_sys_file_stat_put_args *uap)
691{
692	cloudabi_filestat_t fs;
693	struct timespec ts[2];
694	char *path;
695	int error;
696
697	/*
698	 * Only support timestamp modification for now, as there is no
699	 * truncateat().
700	 */
701	if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM |
702	    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
703	    CLOUDABI_FILESTAT_MTIM_NOW)) != 0)
704		return (EINVAL);
705
706	error = copyin(uap->buf, &fs, sizeof(fs));
707	if (error != 0)
708		return (error);
709	error = copyin_path(uap->path, uap->pathlen, &path);
710	if (error != 0)
711		return (error);
712
713	convert_utimens_arguments(&fs, uap->flags, ts);
714	error = kern_utimensat(td, uap->fd, path, UIO_SYSSPACE, ts,
715	    UIO_SYSSPACE, (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ?
716	    0 : AT_SYMLINK_NOFOLLOW);
717	cloudabi_freestr(path);
718	return (error);
719}
720
721int
722cloudabi_sys_file_symlink(struct thread *td,
723    struct cloudabi_sys_file_symlink_args *uap)
724{
725	char *path1, *path2;
726	int error;
727
728	error = copyin_path(uap->path1, uap->path1len, &path1);
729	if (error != 0)
730		return (error);
731	error = copyin_path(uap->path2, uap->path2len, &path2);
732	if (error != 0) {
733		cloudabi_freestr(path1);
734		return (error);
735	}
736
737	error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE);
738	cloudabi_freestr(path1);
739	cloudabi_freestr(path2);
740	return (error);
741}
742
743int
744cloudabi_sys_file_unlink(struct thread *td,
745    struct cloudabi_sys_file_unlink_args *uap)
746{
747	char *path;
748	int error;
749
750	error = copyin_path(uap->path, uap->pathlen, &path);
751	if (error != 0)
752		return (error);
753
754	if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR)
755		error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE);
756	else
757		error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0);
758	cloudabi_freestr(path);
759	return (error);
760}
761