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