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