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