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: stable/12/sys/compat/cloudabi/cloudabi_file.c 366937 2020-10-22 15:23:41Z kib $");
28
29 #include <sys/param.h>
30 #include <sys/capsicum.h>
31 #include <sys/dirent.h>
32 #include <sys/fcntl.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/namei.h>
36 #include <sys/proc.h>
37 #include <sys/stat.h>
38 #include <sys/syscallsubr.h>
39 #include <sys/uio.h>
40 #include <sys/vnode.h>
41
42 #include <contrib/cloudabi/cloudabi_types_common.h>
43
44 #include <compat/cloudabi/cloudabi_proto.h>
45 #include <compat/cloudabi/cloudabi_util.h>
46
47 #include <security/mac/mac_framework.h>
48
49 static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames");
50
51 /*
52 * Copying pathnames from userspace to kernelspace.
53 *
54 * Unlike most operating systems, CloudABI doesn't use null-terminated
55 * pathname strings. Processes always pass pathnames to the kernel by
56 * providing a base pointer and a length. This has a couple of reasons:
57 *
58 * - It makes it easier to use CloudABI in combination with programming
59 * languages other than C, that may use non-null terminated strings.
60 * - It allows for calling system calls on individual components of the
61 * pathname without modifying the input string.
62 *
63 * The function below copies in pathname strings and null-terminates it.
64 * It also ensure that the string itself does not contain any null
65 * bytes.
66 *
67 * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass
68 * in unterminated pathname strings, so we can do away with
69 * the copying.
70 */
71
72 static int
copyin_path(const char * uaddr,size_t len,char ** result)73 copyin_path(const char *uaddr, size_t len, char **result)
74 {
75 char *buf;
76 int error;
77
78 if (len >= PATH_MAX)
79 return (ENAMETOOLONG);
80 buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK);
81 error = copyin(uaddr, buf, len);
82 if (error != 0) {
83 free(buf, M_CLOUDABI_PATH);
84 return (error);
85 }
86 if (memchr(buf, '\0', len) != NULL) {
87 free(buf, M_CLOUDABI_PATH);
88 return (EINVAL);
89 }
90 buf[len] = '\0';
91 *result = buf;
92 return (0);
93 }
94
95 static void
cloudabi_freestr(char * buf)96 cloudabi_freestr(char *buf)
97 {
98
99 free(buf, M_CLOUDABI_PATH);
100 }
101
102 int
cloudabi_sys_file_advise(struct thread * td,struct cloudabi_sys_file_advise_args * uap)103 cloudabi_sys_file_advise(struct thread *td,
104 struct cloudabi_sys_file_advise_args *uap)
105 {
106 int advice;
107
108 switch (uap->advice) {
109 case CLOUDABI_ADVICE_DONTNEED:
110 advice = POSIX_FADV_DONTNEED;
111 break;
112 case CLOUDABI_ADVICE_NOREUSE:
113 advice = POSIX_FADV_NOREUSE;
114 break;
115 case CLOUDABI_ADVICE_NORMAL:
116 advice = POSIX_FADV_NORMAL;
117 break;
118 case CLOUDABI_ADVICE_RANDOM:
119 advice = POSIX_FADV_RANDOM;
120 break;
121 case CLOUDABI_ADVICE_SEQUENTIAL:
122 advice = POSIX_FADV_SEQUENTIAL;
123 break;
124 case CLOUDABI_ADVICE_WILLNEED:
125 advice = POSIX_FADV_WILLNEED;
126 break;
127 default:
128 return (EINVAL);
129 }
130
131 return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice));
132 }
133
134 int
cloudabi_sys_file_allocate(struct thread * td,struct cloudabi_sys_file_allocate_args * uap)135 cloudabi_sys_file_allocate(struct thread *td,
136 struct cloudabi_sys_file_allocate_args *uap)
137 {
138
139 return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len));
140 }
141
142 int
cloudabi_sys_file_create(struct thread * td,struct cloudabi_sys_file_create_args * uap)143 cloudabi_sys_file_create(struct thread *td,
144 struct cloudabi_sys_file_create_args *uap)
145 {
146 char *path;
147 int error;
148
149 error = copyin_path(uap->path, uap->path_len, &path);
150 if (error != 0)
151 return (error);
152
153 /*
154 * CloudABI processes cannot interact with UNIX credentials and
155 * permissions. Depend on the umask that is set prior to
156 * execution to restrict the file permissions.
157 */
158 switch (uap->type) {
159 case CLOUDABI_FILETYPE_DIRECTORY:
160 error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777);
161 break;
162 default:
163 error = EINVAL;
164 break;
165 }
166 cloudabi_freestr(path);
167 return (error);
168 }
169
170 int
cloudabi_sys_file_link(struct thread * td,struct cloudabi_sys_file_link_args * uap)171 cloudabi_sys_file_link(struct thread *td,
172 struct cloudabi_sys_file_link_args *uap)
173 {
174 char *path1, *path2;
175 int error;
176
177 error = copyin_path(uap->path1, uap->path1_len, &path1);
178 if (error != 0)
179 return (error);
180 error = copyin_path(uap->path2, uap->path2_len, &path2);
181 if (error != 0) {
182 cloudabi_freestr(path1);
183 return (error);
184 }
185
186 error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2,
187 UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ?
188 FOLLOW : NOFOLLOW);
189 cloudabi_freestr(path1);
190 cloudabi_freestr(path2);
191 return (error);
192 }
193
194 int
cloudabi_sys_file_open(struct thread * td,struct cloudabi_sys_file_open_args * uap)195 cloudabi_sys_file_open(struct thread *td,
196 struct cloudabi_sys_file_open_args *uap)
197 {
198 cloudabi_fdstat_t fds;
199 cap_rights_t rights;
200 struct filecaps fcaps = {};
201 struct nameidata nd;
202 struct file *fp;
203 struct vnode *vp;
204 char *path;
205 int error, fd, fflags;
206 bool read, write;
207
208 error = copyin(uap->fds, &fds, sizeof(fds));
209 if (error != 0)
210 return (error);
211
212 /* All the requested rights should be set on the descriptor. */
213 error = cloudabi_convert_rights(
214 fds.fs_rights_base | fds.fs_rights_inheriting, &rights);
215 if (error != 0)
216 return (error);
217 cap_rights_set(&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
write_dirent(struct dirent * bde,cloudabi_dircookie_t cookie,struct uio * uio)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
cloudabi_sys_file_readdir(struct thread * td,struct cloudabi_sys_file_readdir_args * uap)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
cloudabi_sys_file_readlink(struct thread * td,struct cloudabi_sys_file_readlink_args * uap)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
cloudabi_sys_file_rename(struct thread * td,struct cloudabi_sys_file_rename_args * uap)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
convert_stat(const struct stat * sb,cloudabi_filestat_t * csb)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
cloudabi_sys_file_stat_fget(struct thread * td,struct cloudabi_sys_file_stat_fget_args * uap)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
convert_utimens_arguments(const cloudabi_filestat_t * fs,cloudabi_fsflags_t flags,struct timespec * ts)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
cloudabi_sys_file_stat_fput(struct thread * td,struct cloudabi_sys_file_stat_fput_args * uap)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
cloudabi_sys_file_stat_get(struct thread * td,struct cloudabi_sys_file_stat_get_args * uap)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
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->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
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->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
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->path_len, &path);
751 if (error != 0)
752 return (error);
753
754 if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR)
755 error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE, 0);
756 else
757 error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0, 0);
758 cloudabi_freestr(path);
759 return (error);
760 }
761