xref: /NextBSD/sys/compat/cloudabi/cloudabi_file.c (revision c21ffb8d6aca32c9584cfa072f309a5890a21aea)
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 <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 
48 static 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 
71 static int
copyin_path(const char * uaddr,size_t len,char ** result)72 copyin_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 
94 static void
cloudabi_freestr(char * buf)95 cloudabi_freestr(char *buf)
96 {
97 
98 	free(buf, M_CLOUDABI_PATH);
99 }
100 
101 int
cloudabi_sys_file_advise(struct thread * td,struct cloudabi_sys_file_advise_args * uap)102 cloudabi_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 
133 int
cloudabi_sys_file_allocate(struct thread * td,struct cloudabi_sys_file_allocate_args * uap)134 cloudabi_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 
141 int
cloudabi_sys_file_create(struct thread * td,struct cloudabi_sys_file_create_args * uap)142 cloudabi_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 
172 int
cloudabi_sys_file_link(struct thread * td,struct cloudabi_sys_file_link_args * uap)173 cloudabi_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 
196 int
cloudabi_sys_file_open(struct thread * td,struct cloudabi_sys_file_open_args * uap)197 cloudabi_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 
307 success:
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. */
325 static int
write_dirent(struct dirent * bde,cloudabi_dircookie_t cookie,struct uio * uio)326 write_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 
376 int
cloudabi_sys_file_readdir(struct thread * td,struct cloudabi_sys_file_readdir_args * uap)377 cloudabi_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 
489 done:
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 
500 int
cloudabi_sys_file_readlink(struct thread * td,struct cloudabi_sys_file_readlink_args * uap)501 cloudabi_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 
517 int
cloudabi_sys_file_rename(struct thread * td,struct cloudabi_sys_file_rename_args * uap)518 cloudabi_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. */
541 static void
convert_stat(const struct stat * sb,cloudabi_filestat_t * csb)542 convert_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 
557 int
cloudabi_sys_file_stat_fget(struct thread * td,struct cloudabi_sys_file_stat_fget_args * uap)558 cloudabi_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(). */
587 static void
convert_utimens_arguments(const cloudabi_filestat_t * fs,cloudabi_fsflags_t flags,struct timespec * ts)588 convert_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 
611 int
cloudabi_sys_file_stat_fput(struct thread * td,struct cloudabi_sys_file_stat_fput_args * uap)612 cloudabi_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 
646 int
cloudabi_sys_file_stat_get(struct thread * td,struct cloudabi_sys_file_stat_get_args * uap)647 cloudabi_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 
688 int
cloudabi_sys_file_stat_put(struct thread * td,struct cloudabi_sys_file_stat_put_args * uap)689 cloudabi_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 
721 int
cloudabi_sys_file_symlink(struct thread * td,struct cloudabi_sys_file_symlink_args * uap)722 cloudabi_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 
743 int
cloudabi_sys_file_unlink(struct thread * td,struct cloudabi_sys_file_unlink_args * uap)744 cloudabi_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