]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/cloudabi/cloudabi_file.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / sys / compat / cloudabi / cloudabi_file.c
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
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
96 cloudabi_freestr(char *buf)
97 {
98
99         free(buf, M_CLOUDABI_PATH);
100 }
101
102 int
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
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
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
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
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(&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(&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(&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(&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(&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_fd->fd_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 = 1;
291                 finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK),
292                     DTYPE_VNODE, vp, &vnops);
293         }
294         VOP_UNLOCK(vp, 0);
295
296         /* Truncate file. */
297         if (fflags & O_TRUNC) {
298                 error = fo_truncate(fp, 0, td->td_ucred, td);
299                 if (error != 0) {
300                         fdrop(fp, td);
301                         return (error);
302                 }
303         }
304
305 success:
306         /* Determine which Capsicum rights to set on the file descriptor. */
307         cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp),
308             &fds.fs_rights_base, &fds.fs_rights_inheriting);
309         cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting,
310             &fcaps.fc_rights);
311         if (cap_rights_is_set(&fcaps.fc_rights))
312                 fcaps.fc_fcntls = CAP_FCNTL_SETFL;
313
314         error = finstall(td, fp, &fd, fflags, &fcaps);
315         fdrop(fp, td);
316         if (error != 0)
317                 return (error);
318         td->td_retval[0] = fd;
319         return (0);
320 }
321
322 /* Converts a FreeBSD directory entry structure and writes it to userspace. */
323 static int
324 write_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio)
325 {
326         cloudabi_dirent_t cde = {
327                 .d_next = cookie,
328                 .d_ino = bde->d_fileno,
329                 .d_namlen = bde->d_namlen,
330         };
331         size_t len;
332         int error;
333
334         /* Convert file type. */
335         switch (bde->d_type) {
336         case DT_BLK:
337                 cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE;
338                 break;
339         case DT_CHR:
340                 cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
341                 break;
342         case DT_DIR:
343                 cde.d_type = CLOUDABI_FILETYPE_DIRECTORY;
344                 break;
345         case DT_FIFO:
346                 cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM;
347                 break;
348         case DT_LNK:
349                 cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
350                 break;
351         case DT_REG:
352                 cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE;
353                 break;
354         case DT_SOCK:
355                 /* The exact socket type cannot be derived. */
356                 cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM;
357                 break;
358         default:
359                 cde.d_type = CLOUDABI_FILETYPE_UNKNOWN;
360                 break;
361         }
362
363         /* Write directory entry structure. */
364         len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid;
365         error = uiomove(&cde, len, uio);
366         if (error != 0)
367                 return (error);
368
369         /* Write filename. */
370         len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid;
371         return (uiomove(bde->d_name, len, uio));
372 }
373
374 int
375 cloudabi_sys_file_readdir(struct thread *td,
376     struct cloudabi_sys_file_readdir_args *uap)
377 {
378         struct iovec iov = {
379                 .iov_base = uap->buf,
380                 .iov_len = uap->buf_len
381         };
382         struct uio uio = {
383                 .uio_iov = &iov,
384                 .uio_iovcnt = 1,
385                 .uio_resid = iov.iov_len,
386                 .uio_segflg = UIO_USERSPACE,
387                 .uio_rw = UIO_READ,
388                 .uio_td = td
389         };
390         struct file *fp;
391         struct vnode *vp;
392         void *readbuf;
393         cloudabi_dircookie_t offset;
394         int error;
395
396         /* Obtain directory vnode. */
397         error = getvnode(td, uap->fd, &cap_read_rights, &fp);
398         if (error != 0) {
399                 if (error == EINVAL)
400                         return (ENOTDIR);
401                 return (error);
402         }
403         if ((fp->f_flag & FREAD) == 0) {
404                 fdrop(fp, td);
405                 return (EBADF);
406         }
407
408         /*
409          * Call VOP_READDIR() and convert resulting data until the user
410          * provided buffer is filled.
411          */
412         readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
413         offset = uap->cookie;
414         vp = fp->f_vnode;
415         while (uio.uio_resid > 0) {
416                 struct iovec readiov = {
417                         .iov_base = readbuf,
418                         .iov_len = MAXBSIZE
419                 };
420                 struct uio readuio = {
421                         .uio_iov = &readiov,
422                         .uio_iovcnt = 1,
423                         .uio_rw = UIO_READ,
424                         .uio_segflg = UIO_SYSSPACE,
425                         .uio_td = td,
426                         .uio_resid = MAXBSIZE,
427                         .uio_offset = offset
428                 };
429                 struct dirent *bde;
430                 unsigned long *cookies, *cookie;
431                 size_t readbuflen;
432                 int eof, ncookies;
433
434                 /* Validate file type. */
435                 vn_lock(vp, LK_SHARED | LK_RETRY);
436                 if (vp->v_type != VDIR) {
437                         VOP_UNLOCK(vp, 0);
438                         error = ENOTDIR;
439                         goto done;
440                 }
441 #ifdef MAC
442                 error = mac_vnode_check_readdir(td->td_ucred, vp);
443                 if (error != 0) {
444                         VOP_UNLOCK(vp, 0);
445                         goto done;
446                 }
447 #endif /* MAC */
448
449                 /* Read new directory entries. */
450                 cookies = NULL;
451                 ncookies = 0;
452                 error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof,
453                     &ncookies, &cookies);
454                 VOP_UNLOCK(vp, 0);
455                 if (error != 0)
456                         goto done;
457
458                 /* Convert entries to CloudABI's format. */
459                 readbuflen = MAXBSIZE - readuio.uio_resid;
460                 bde = readbuf;
461                 cookie = cookies;
462                 while (readbuflen >= offsetof(struct dirent, d_name) &&
463                     uio.uio_resid > 0 && ncookies > 0) {
464                         /* Ensure that the returned offset always increases. */
465                         if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 &&
466                             *cookie > offset) {
467                                 error = write_dirent(bde, *cookie, &uio);
468                                 if (error != 0) {
469                                         free(cookies, M_TEMP);
470                                         goto done;
471                                 }
472                         }
473
474                         if (offset < *cookie)
475                                 offset = *cookie;
476                         ++cookie;
477                         --ncookies;
478                         readbuflen -= bde->d_reclen;
479                         bde = (struct dirent *)((char *)bde + bde->d_reclen);
480                 }
481                 free(cookies, M_TEMP);
482                 if (eof)
483                         break;
484         }
485
486 done:
487         fdrop(fp, td);
488         free(readbuf, M_TEMP);
489         if (error != 0)
490                 return (error);
491
492         /* Return number of bytes copied to userspace. */
493         td->td_retval[0] = uap->buf_len - uio.uio_resid;
494         return (0);
495 }
496
497 int
498 cloudabi_sys_file_readlink(struct thread *td,
499     struct cloudabi_sys_file_readlink_args *uap)
500 {
501         char *path;
502         int error;
503
504         error = copyin_path(uap->path, uap->path_len, &path);
505         if (error != 0)
506                 return (error);
507
508         error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE,
509             uap->buf, UIO_USERSPACE, uap->buf_len);
510         cloudabi_freestr(path);
511         return (error);
512 }
513
514 int
515 cloudabi_sys_file_rename(struct thread *td,
516     struct cloudabi_sys_file_rename_args *uap)
517 {
518         char *old, *new;
519         int error;
520
521         error = copyin_path(uap->path1, uap->path1_len, &old);
522         if (error != 0)
523                 return (error);
524         error = copyin_path(uap->path2, uap->path2_len, &new);
525         if (error != 0) {
526                 cloudabi_freestr(old);
527                 return (error);
528         }
529
530         error = kern_renameat(td, uap->fd1, old, uap->fd2, new,
531             UIO_SYSSPACE);
532         cloudabi_freestr(old);
533         cloudabi_freestr(new);
534         return (error);
535 }
536
537 /* Converts a FreeBSD stat structure to a CloudABI stat structure. */
538 static void
539 convert_stat(const struct stat *sb, cloudabi_filestat_t *csb)
540 {
541         cloudabi_filestat_t res = {
542                 .st_dev         = sb->st_dev,
543                 .st_ino         = sb->st_ino,
544                 .st_nlink       = sb->st_nlink,
545                 .st_size        = sb->st_size,
546         };
547
548         cloudabi_convert_timespec(&sb->st_atim, &res.st_atim);
549         cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim);
550         cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim);
551         *csb = res;
552 }
553
554 int
555 cloudabi_sys_file_stat_fget(struct thread *td,
556     struct cloudabi_sys_file_stat_fget_args *uap)
557 {
558         struct stat sb;
559         cloudabi_filestat_t csb;
560         struct file *fp;
561         cloudabi_filetype_t filetype;
562         int error;
563
564         memset(&csb, 0, sizeof(csb));
565
566         /* Fetch file descriptor attributes. */
567         error = fget(td, uap->fd, &cap_fstat_rights, &fp);
568         if (error != 0)
569                 return (error);
570         error = fo_stat(fp, &sb, td->td_ucred, td);
571         if (error != 0) {
572                 fdrop(fp, td);
573                 return (error);
574         }
575         filetype = cloudabi_convert_filetype(fp);
576         fdrop(fp, td);
577
578         /* Convert attributes to CloudABI's format. */
579         convert_stat(&sb, &csb);
580         csb.st_filetype = filetype;
581         return (copyout(&csb, uap->buf, sizeof(csb)));
582 }
583
584 /* Converts timestamps to arguments to futimens() and utimensat(). */
585 static void
586 convert_utimens_arguments(const cloudabi_filestat_t *fs,
587     cloudabi_fsflags_t flags, struct timespec *ts)
588 {
589
590         if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) {
591                 ts[0].tv_nsec = UTIME_NOW;
592         } else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) {
593                 ts[0].tv_sec = fs->st_atim / 1000000000;
594                 ts[0].tv_nsec = fs->st_atim % 1000000000;
595         } else {
596                 ts[0].tv_nsec = UTIME_OMIT;
597         }
598
599         if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) {
600                 ts[1].tv_nsec = UTIME_NOW;
601         } else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) {
602                 ts[1].tv_sec = fs->st_mtim / 1000000000;
603                 ts[1].tv_nsec = fs->st_mtim % 1000000000;
604         } else {
605                 ts[1].tv_nsec = UTIME_OMIT;
606         }
607 }
608
609 int
610 cloudabi_sys_file_stat_fput(struct thread *td,
611     struct cloudabi_sys_file_stat_fput_args *uap)
612 {
613         cloudabi_filestat_t fs;
614         struct timespec ts[2];
615         int error;
616
617         error = copyin(uap->buf, &fs, sizeof(fs));
618         if (error != 0)
619                 return (error);
620
621         /*
622          * Only support truncation and timestamp modification separately
623          * for now, to prevent unnecessary code duplication.
624          */
625         if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) {
626                 /* Call into kern_ftruncate() for file truncation. */
627                 if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0)
628                         return (EINVAL);
629                 return (kern_ftruncate(td, uap->fd, fs.st_size));
630         } else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM |
631             CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
632             CLOUDABI_FILESTAT_MTIM_NOW)) != 0) {
633                 /* Call into kern_futimens() for timestamp modification. */
634                 if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM |
635                     CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM |
636                     CLOUDABI_FILESTAT_MTIM_NOW)) != 0)
637                         return (EINVAL);
638                 convert_utimens_arguments(&fs, uap->flags, ts);
639                 return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE));
640         }
641         return (EINVAL);
642 }
643
644 int
645 cloudabi_sys_file_stat_get(struct thread *td,
646     struct cloudabi_sys_file_stat_get_args *uap)
647 {
648         struct stat sb;
649         cloudabi_filestat_t csb;
650         char *path;
651         int error;
652
653         memset(&csb, 0, sizeof(csb));
654
655         error = copyin_path(uap->path, uap->path_len, &path);
656         if (error != 0)
657                 return (error);
658
659         error = kern_statat(td,
660             (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 :
661             AT_SYMLINK_NOFOLLOW, uap->fd.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_SOCKET_STREAM;
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
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->path_len, &path);
710         if (error != 0)
711                 return (error);
712
713         convert_utimens_arguments(&fs, uap->flags, ts);
714         error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts,
715             UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ?
716             0 : AT_SYMLINK_NOFOLLOW);
717         cloudabi_freestr(path);
718         return (error);
719 }
720
721 int
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->path1_len, &path1);
729         if (error != 0)
730                 return (error);
731         error = copyin_path(uap->path2, uap->path2_len, &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
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->path_len, &path);
751         if (error != 0)
752                 return (error);
753
754         if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR)
755                 error = kern_frmdirat(td, uap->fd, path, FD_NONE,
756                     UIO_SYSSPACE, 0);
757         else
758                 error = kern_funlinkat(td, uap->fd, path, FD_NONE,
759                     UIO_SYSSPACE, 0, 0);
760         cloudabi_freestr(path);
761         return (error);
762 }