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$");
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 
49 static 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 
72 static int
copyin_path(const char * uaddr,size_t len,char ** result)73 copyin_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 
95 static void
cloudabi_freestr(char * buf)96 cloudabi_freestr(char *buf)
97 {
98 
99 	free(buf, M_CLOUDABI_PATH);
100 }
101 
102 int
cloudabi_sys_file_advise(struct thread * td,struct cloudabi_sys_file_advise_args * uap)103 cloudabi_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 
134 int
cloudabi_sys_file_allocate(struct thread * td,struct cloudabi_sys_file_allocate_args * uap)135 cloudabi_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 
142 int
cloudabi_sys_file_create(struct thread * td,struct cloudabi_sys_file_create_args * uap)143 cloudabi_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 	default:
163 		error = EINVAL;
164 		break;
165 	}
166 	cloudabi_freestr(path);
167 	return (error);
168 }
169 
170 int
cloudabi_sys_file_link(struct thread * td,struct cloudabi_sys_file_link_args * uap)171 cloudabi_sys_file_link(struct thread *td,
172     struct cloudabi_sys_file_link_args *uap)
173 {
174 	char *path1, *path2;
175 	int error;
176 
177 	error = copyin_path(uap->path1, uap->path1_len, &path1);
178 	if (error != 0)
179 		return (error);
180 	error = copyin_path(uap->path2, uap->path2_len, &path2);
181 	if (error != 0) {
182 		cloudabi_freestr(path1);
183 		return (error);
184 	}
185 
186 	error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2,
187 	    UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ?
188 	    FOLLOW : NOFOLLOW);
189 	cloudabi_freestr(path1);
190 	cloudabi_freestr(path2);
191 	return (error);
192 }
193 
194 int
cloudabi_sys_file_open(struct thread * td,struct cloudabi_sys_file_open_args * uap)195 cloudabi_sys_file_open(struct thread *td,
196     struct cloudabi_sys_file_open_args *uap)
197 {
198 	cloudabi_fdstat_t fds;
199 	cap_rights_t rights;
200 	struct filecaps fcaps = {};
201 	struct nameidata nd;
202 	struct file *fp;
203 	struct vnode *vp;
204 	char *path;
205 	int error, fd, fflags;
206 	bool read, write;
207 
208 	error = copyin(uap->fds, &fds, sizeof(fds));
209 	if (error != 0)
210 		return (error);
211 
212 	/* All the requested rights should be set on the descriptor. */
213 	error = cloudabi_convert_rights(
214 	    fds.fs_rights_base | fds.fs_rights_inheriting, &rights);
215 	if (error != 0)
216 		return (error);
217 	cap_rights_set_one(&rights, CAP_LOOKUP);
218 
219 	/* Convert rights to corresponding access mode. */
220 	read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ |
221 	    CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0;
222 	write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC |
223 	    CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE |
224 	    CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0;
225 	fflags = write ? read ? FREAD | FWRITE : FWRITE : FREAD;
226 
227 	/* Convert open flags. */
228 	if ((uap->oflags & CLOUDABI_O_CREAT) != 0) {
229 		fflags |= O_CREAT;
230 		cap_rights_set_one(&rights, CAP_CREATE);
231 	}
232 	if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0)
233 		fflags |= O_DIRECTORY;
234 	if ((uap->oflags & CLOUDABI_O_EXCL) != 0)
235 		fflags |= O_EXCL;
236 	if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) {
237 		fflags |= O_TRUNC;
238 		cap_rights_set_one(&rights, CAP_FTRUNCATE);
239 	}
240 	if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0)
241 		fflags |= O_APPEND;
242 	if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0)
243 		fflags |= O_NONBLOCK;
244 	if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC |
245 	    CLOUDABI_FDFLAG_RSYNC)) != 0) {
246 		fflags |= O_SYNC;
247 		cap_rights_set_one(&rights, CAP_FSYNC);
248 	}
249 	if ((uap->dirfd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0)
250 		fflags |= O_NOFOLLOW;
251 	if (write && (fflags & (O_APPEND | O_TRUNC)) == 0)
252 		cap_rights_set_one(&rights, CAP_SEEK);
253 
254 	/* Allocate new file descriptor. */
255 	error = falloc_noinstall(td, &fp);
256 	if (error != 0)
257 		return (error);
258 	fp->f_flag = fflags & FMASK;
259 
260 	/* Open path. */
261 	error = copyin_path(uap->path, uap->path_len, &path);
262 	if (error != 0) {
263 		fdrop(fp, td);
264 		return (error);
265 	}
266 	NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->dirfd.fd,
267 	    &rights, td);
268 	error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_pd->pd_cmask, fp);
269 	cloudabi_freestr(path);
270 	if (error != 0) {
271 		/* Custom operations provided. */
272 		if (error == ENXIO && fp->f_ops != &badfileops)
273 			goto success;
274 
275 		/*
276 		 * POSIX compliance: return ELOOP in case openat() is
277 		 * called on a symbolic link and O_NOFOLLOW is set.
278 		 */
279 		if (error == EMLINK)
280 			error = ELOOP;
281 		fdrop(fp, td);
282 		return (error);
283 	}
284 	NDFREE(&nd, NDF_ONLY_PNBUF);
285 	filecaps_free(&nd.ni_filecaps);
286 	fp->f_vnode = vp = nd.ni_vp;
287 
288 	/* Install vnode operations if no custom operations are provided. */
289 	if (fp->f_ops == &badfileops) {
290 		fp->f_seqcount[UIO_READ] = 1;
291 		fp->f_seqcount[UIO_WRITE] = 1;
292 		finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK),
293 		    DTYPE_VNODE, vp, &vnops);
294 	}
295 	VOP_UNLOCK(vp);
296 
297 	/* Truncate file. */
298 	if (fflags & O_TRUNC) {
299 		error = fo_truncate(fp, 0, td->td_ucred, td);
300 		if (error != 0) {
301 			fdrop(fp, td);
302 			return (error);
303 		}
304 	}
305 
306 success:
307 	/* Determine which Capsicum rights to set on the file descriptor. */
308 	cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp),
309 	    &fds.fs_rights_base, &fds.fs_rights_inheriting);
310 	cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting,
311 	    &fcaps.fc_rights);
312 	if (cap_rights_is_set(&fcaps.fc_rights))
313 		fcaps.fc_fcntls = CAP_FCNTL_SETFL;
314 
315 	error = finstall(td, fp, &fd, fflags, &fcaps);
316 	fdrop(fp, td);
317 	if (error != 0)
318 		return (error);
319 	td->td_retval[0] = fd;
320 	return (0);
321 }
322 
323 /* Converts a FreeBSD directory entry structure and writes it to userspace. */
324 static int
write_dirent(struct dirent * bde,cloudabi_dircookie_t cookie,struct uio * uio)325 write_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio)
326 {
327 	cloudabi_dirent_t cde = {
328 		.d_next = cookie,
329 		.d_ino = bde->d_fileno,
330 		.d_namlen = bde->d_namlen,
331 	};
332 	size_t len;
333 	int error;
334 
335 	/* Convert file type. */
336 	switch (bde->d_type) {
337 	case DT_BLK:
338 		cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE;
339 		break;
340 	case DT_CHR:
341 		cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
342 		break;
343 	case DT_DIR:
344 		cde.d_type = CLOUDABI_FILETYPE_DIRECTORY;
345 		break;
346 	case DT_FIFO:
347 		cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM;
348 		break;
349 	case DT_LNK:
350 		cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
351 		break;
352 	case DT_REG:
353 		cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE;
354 		break;
355 	case DT_SOCK:
356 		/* The exact socket type cannot be derived. */
357 		cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM;
358 		break;
359 	default:
360 		cde.d_type = CLOUDABI_FILETYPE_UNKNOWN;
361 		break;
362 	}
363 
364 	/* Write directory entry structure. */
365 	len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid;
366 	error = uiomove(&cde, len, uio);
367 	if (error != 0)
368 		return (error);
369 
370 	/* Write filename. */
371 	len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid;
372 	return (uiomove(bde->d_name, len, uio));
373 }
374 
375 int
cloudabi_sys_file_readdir(struct thread * td,struct cloudabi_sys_file_readdir_args * uap)376 cloudabi_sys_file_readdir(struct thread *td,
377     struct cloudabi_sys_file_readdir_args *uap)
378 {
379 	struct iovec iov = {
380 		.iov_base = uap->buf,
381 		.iov_len = uap->buf_len
382 	};
383 	struct uio uio = {
384 		.uio_iov = &iov,
385 		.uio_iovcnt = 1,
386 		.uio_resid = iov.iov_len,
387 		.uio_segflg = UIO_USERSPACE,
388 		.uio_rw = UIO_READ,
389 		.uio_td = td
390 	};
391 	struct file *fp;
392 	struct vnode *vp;
393 	void *readbuf;
394 	cloudabi_dircookie_t offset;
395 	int error;
396 
397 	/* Obtain directory vnode. */
398 	error = getvnode(td, uap->fd, &cap_read_rights, &fp);
399 	if (error != 0) {
400 		if (error == EINVAL)
401 			return (ENOTDIR);
402 		return (error);
403 	}
404 	if ((fp->f_flag & FREAD) == 0) {
405 		fdrop(fp, td);
406 		return (EBADF);
407 	}
408 
409 	/*
410 	 * Call VOP_READDIR() and convert resulting data until the user
411 	 * provided buffer is filled.
412 	 */
413 	readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
414 	offset = uap->cookie;
415 	vp = fp->f_vnode;
416 	while (uio.uio_resid > 0) {
417 		struct iovec readiov = {
418 			.iov_base = readbuf,
419 			.iov_len = MAXBSIZE
420 		};
421 		struct uio readuio = {
422 			.uio_iov = &readiov,
423 			.uio_iovcnt = 1,
424 			.uio_rw = UIO_READ,
425 			.uio_segflg = UIO_SYSSPACE,
426 			.uio_td = td,
427 			.uio_resid = MAXBSIZE,
428 			.uio_offset = offset
429 		};
430 		struct dirent *bde;
431 		unsigned long *cookies, *cookie;
432 		size_t readbuflen;
433 		int eof, ncookies;
434 
435 		/* Validate file type. */
436 		vn_lock(vp, LK_SHARED | LK_RETRY);
437 		if (vp->v_type != VDIR) {
438 			VOP_UNLOCK(vp);
439 			error = ENOTDIR;
440 			goto done;
441 		}
442 #ifdef MAC
443 		error = mac_vnode_check_readdir(td->td_ucred, vp);
444 		if (error != 0) {
445 			VOP_UNLOCK(vp);
446 			goto done;
447 		}
448 #endif /* MAC */
449 
450 		/* Read new directory entries. */
451 		cookies = NULL;
452 		ncookies = 0;
453 		error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof,
454 		    &ncookies, &cookies);
455 		VOP_UNLOCK(vp);
456 		if (error != 0)
457 			goto done;
458 
459 		/* Convert entries to CloudABI's format. */
460 		readbuflen = MAXBSIZE - readuio.uio_resid;
461 		bde = readbuf;
462 		cookie = cookies;
463 		while (readbuflen >= offsetof(struct dirent, d_name) &&
464 		    uio.uio_resid > 0 && ncookies > 0) {
465 			/* Ensure that the returned offset always increases. */
466 			if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 &&
467 			    *cookie > offset) {
468 				error = write_dirent(bde, *cookie, &uio);
469 				if (error != 0) {
470 					free(cookies, M_TEMP);
471 					goto done;
472 				}
473 			}
474 
475 			if (offset < *cookie)
476 				offset = *cookie;
477 			++cookie;
478 			--ncookies;
479 			readbuflen -= bde->d_reclen;
480 			bde = (struct dirent *)((char *)bde + bde->d_reclen);
481 		}
482 		free(cookies, M_TEMP);
483 		if (eof)
484 			break;
485 	}
486 
487 done:
488 	fdrop(fp, td);
489 	free(readbuf, M_TEMP);
490 	if (error != 0)
491 		return (error);
492 
493 	/* Return number of bytes copied to userspace. */
494 	td->td_retval[0] = uap->buf_len - uio.uio_resid;
495 	return (0);
496 }
497 
498 int
cloudabi_sys_file_readlink(struct thread * td,struct cloudabi_sys_file_readlink_args * uap)499 cloudabi_sys_file_readlink(struct thread *td,
500     struct cloudabi_sys_file_readlink_args *uap)
501 {
502 	char *path;
503 	int error;
504 
505 	error = copyin_path(uap->path, uap->path_len, &path);
506 	if (error != 0)
507 		return (error);
508 
509 	error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE,
510 	    uap->buf, UIO_USERSPACE, uap->buf_len);
511 	cloudabi_freestr(path);
512 	return (error);
513 }
514 
515 int
cloudabi_sys_file_rename(struct thread * td,struct cloudabi_sys_file_rename_args * uap)516 cloudabi_sys_file_rename(struct thread *td,
517     struct cloudabi_sys_file_rename_args *uap)
518 {
519 	char *old, *new;
520 	int error;
521 
522 	error = copyin_path(uap->path1, uap->path1_len, &old);
523 	if (error != 0)
524 		return (error);
525 	error = copyin_path(uap->path2, uap->path2_len, &new);
526 	if (error != 0) {
527 		cloudabi_freestr(old);
528 		return (error);
529 	}
530 
531 	error = kern_renameat(td, uap->fd1, old, uap->fd2, new,
532 	    UIO_SYSSPACE);
533 	cloudabi_freestr(old);
534 	cloudabi_freestr(new);
535 	return (error);
536 }
537 
538 /* Converts a FreeBSD stat structure to a CloudABI stat structure. */
539 static void
convert_stat(const struct stat * sb,cloudabi_filestat_t * csb)540 convert_stat(const struct stat *sb, cloudabi_filestat_t *csb)
541 {
542 	cloudabi_filestat_t res = {
543 		.st_dev		= sb->st_dev,
544 		.st_ino		= sb->st_ino,
545 		.st_nlink	= sb->st_nlink,
546 		.st_size	= sb->st_size,
547 	};
548 
549 	cloudabi_convert_timespec(&sb->st_atim, &res.st_atim);
550 	cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim);
551 	cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim);
552 	*csb = res;
553 }
554 
555 int
cloudabi_sys_file_stat_fget(struct thread * td,struct cloudabi_sys_file_stat_fget_args * uap)556 cloudabi_sys_file_stat_fget(struct thread *td,
557     struct cloudabi_sys_file_stat_fget_args *uap)
558 {
559 	struct stat sb;
560 	cloudabi_filestat_t csb;
561 	struct file *fp;
562 	cloudabi_filetype_t filetype;
563 	int error;
564 
565 	memset(&csb, 0, sizeof(csb));
566 
567 	/* Fetch file descriptor attributes. */
568 	error = fget(td, uap->fd, &cap_fstat_rights, &fp);
569 	if (error != 0)
570 		return (error);
571 	error = fo_stat(fp, &sb, td->td_ucred, td);
572 	if (error != 0) {
573 		fdrop(fp, td);
574 		return (error);
575 	}
576 	filetype = cloudabi_convert_filetype(fp);
577 	fdrop(fp, td);
578 
579 	/* Convert attributes to CloudABI's format. */
580 	convert_stat(&sb, &csb);
581 	csb.st_filetype = filetype;
582 	return (copyout(&csb, uap->buf, sizeof(csb)));
583 }
584 
585 /* Converts timestamps to arguments to futimens() and utimensat(). */
586 static void
convert_utimens_arguments(const cloudabi_filestat_t * fs,cloudabi_fsflags_t flags,struct timespec * ts)587 convert_utimens_arguments(const cloudabi_filestat_t *fs,
588     cloudabi_fsflags_t flags, struct timespec *ts)
589 {
590 
591 	if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) {
592 		ts[0].tv_nsec = UTIME_NOW;
593 	} else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) {
594 		ts[0].tv_sec = fs->st_atim / 1000000000;
595 		ts[0].tv_nsec = fs->st_atim % 1000000000;
596 	} else {
597 		ts[0].tv_nsec = UTIME_OMIT;
598 	}
599 
600 	if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) {
601 		ts[1].tv_nsec = UTIME_NOW;
602 	} else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) {
603 		ts[1].tv_sec = fs->st_mtim / 1000000000;
604 		ts[1].tv_nsec = fs->st_mtim % 1000000000;
605 	} else {
606 		ts[1].tv_nsec = UTIME_OMIT;
607 	}
608 }
609 
610 int
cloudabi_sys_file_stat_fput(struct thread * td,struct cloudabi_sys_file_stat_fput_args * uap)611 cloudabi_sys_file_stat_fput(struct thread *td,
612     struct cloudabi_sys_file_stat_fput_args *uap)
613 {
614 	cloudabi_filestat_t fs;
615 	struct timespec ts[2];
616 	int error;
617 
618 	error = copyin(uap->buf, &fs, sizeof(fs));
619 	if (error != 0)
620 		return (error);
621 
622 	/*
623 	 * Only support truncation and timestamp modification separately
624 	 * for now, to prevent unnecessary code duplication.
625 	 */
626 	if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) {
627 		/* Call into kern_ftruncate() for file truncation. */
628 		if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0)
629 			return (EINVAL);
630 		return (kern_ftruncate(td, uap->fd, fs.st_size));
631 	} else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM |
632 	    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
633 	    CLOUDABI_FILESTAT_MTIM_NOW)) != 0) {
634 		/* Call into kern_futimens() for timestamp modification. */
635 		if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM |
636 		    CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
637 		    CLOUDABI_FILESTAT_MTIM_NOW)) != 0)
638 			return (EINVAL);
639 		convert_utimens_arguments(&fs, uap->flags, ts);
640 		return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE));
641 	}
642 	return (EINVAL);
643 }
644 
645 int
cloudabi_sys_file_stat_get(struct thread * td,struct cloudabi_sys_file_stat_get_args * uap)646 cloudabi_sys_file_stat_get(struct thread *td,
647     struct cloudabi_sys_file_stat_get_args *uap)
648 {
649 	struct stat sb;
650 	cloudabi_filestat_t csb;
651 	char *path;
652 	int error;
653 
654 	memset(&csb, 0, sizeof(csb));
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_SOCKET_STREAM;
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 
689 int
cloudabi_sys_file_stat_put(struct thread * td,struct cloudabi_sys_file_stat_put_args * uap)690 cloudabi_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 
722 int
cloudabi_sys_file_symlink(struct thread * td,struct cloudabi_sys_file_symlink_args * uap)723 cloudabi_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 
744 int
cloudabi_sys_file_unlink(struct thread * td,struct cloudabi_sys_file_unlink_args * uap)745 cloudabi_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_frmdirat(td, uap->fd, path, FD_NONE,
757 		    UIO_SYSSPACE, 0);
758 	else
759 		error = kern_funlinkat(td, uap->fd, path, FD_NONE,
760 		    UIO_SYSSPACE, 0, 0);
761 	cloudabi_freestr(path);
762 	return (error);
763 }
764