cloudabi_file.c revision 316574
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/11/sys/compat/cloudabi/cloudabi_file.c 316574 2017-04-06 15:10:36Z ed $"); 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 49static 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 72static int 73copyin_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 95static void 96cloudabi_freestr(char *buf) 97{ 98 99 free(buf, M_CLOUDABI_PATH); 100} 101 102int 103cloudabi_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 134int 135cloudabi_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 142int 143cloudabi_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 case CLOUDABI_FILETYPE_FIFO: 163 error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666); 164 break; 165 default: 166 error = EINVAL; 167 break; 168 } 169 cloudabi_freestr(path); 170 return (error); 171} 172 173int 174cloudabi_sys_file_link(struct thread *td, 175 struct cloudabi_sys_file_link_args *uap) 176{ 177 char *path1, *path2; 178 int error; 179 180 error = copyin_path(uap->path1, uap->path1_len, &path1); 181 if (error != 0) 182 return (error); 183 error = copyin_path(uap->path2, uap->path2_len, &path2); 184 if (error != 0) { 185 cloudabi_freestr(path1); 186 return (error); 187 } 188 189 error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2, 190 UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 191 FOLLOW : NOFOLLOW); 192 cloudabi_freestr(path1); 193 cloudabi_freestr(path2); 194 return (error); 195} 196 197int 198cloudabi_sys_file_open(struct thread *td, 199 struct cloudabi_sys_file_open_args *uap) 200{ 201 cloudabi_fdstat_t fds; 202 cap_rights_t rights; 203 struct filecaps fcaps = {}; 204 struct nameidata nd; 205 struct file *fp; 206 struct vnode *vp; 207 char *path; 208 int error, fd, fflags; 209 bool read, write; 210 211 error = copyin(uap->fds, &fds, sizeof(fds)); 212 if (error != 0) 213 return (error); 214 215 /* All the requested rights should be set on the descriptor. */ 216 error = cloudabi_convert_rights( 217 fds.fs_rights_base | fds.fs_rights_inheriting, &rights); 218 if (error != 0) 219 return (error); 220 cap_rights_set(&rights, CAP_LOOKUP); 221 222 /* Convert rights to corresponding access mode. */ 223 read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ | 224 CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0; 225 write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC | 226 CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE | 227 CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0; 228 fflags = write ? read ? FREAD | FWRITE : FWRITE : FREAD; 229 230 /* Convert open flags. */ 231 if ((uap->oflags & CLOUDABI_O_CREAT) != 0) { 232 fflags |= O_CREAT; 233 cap_rights_set(&rights, CAP_CREATE); 234 } 235 if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0) 236 fflags |= O_DIRECTORY; 237 if ((uap->oflags & CLOUDABI_O_EXCL) != 0) 238 fflags |= O_EXCL; 239 if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) { 240 fflags |= O_TRUNC; 241 cap_rights_set(&rights, CAP_FTRUNCATE); 242 } 243 if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0) 244 fflags |= O_APPEND; 245 if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0) 246 fflags |= O_NONBLOCK; 247 if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC | 248 CLOUDABI_FDFLAG_RSYNC)) != 0) { 249 fflags |= O_SYNC; 250 cap_rights_set(&rights, CAP_FSYNC); 251 } 252 if ((uap->dirfd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0) 253 fflags |= O_NOFOLLOW; 254 if (write && (fflags & (O_APPEND | O_TRUNC)) == 0) 255 cap_rights_set(&rights, CAP_SEEK); 256 257 /* Allocate new file descriptor. */ 258 error = falloc_noinstall(td, &fp); 259 if (error != 0) 260 return (error); 261 fp->f_flag = fflags & FMASK; 262 263 /* Open path. */ 264 error = copyin_path(uap->path, uap->path_len, &path); 265 if (error != 0) { 266 fdrop(fp, td); 267 return (error); 268 } 269 NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->dirfd.fd, 270 &rights, td); 271 error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp); 272 cloudabi_freestr(path); 273 if (error != 0) { 274 /* Custom operations provided. */ 275 if (error == ENXIO && fp->f_ops != &badfileops) 276 goto success; 277 278 /* 279 * POSIX compliance: return ELOOP in case openat() is 280 * called on a symbolic link and O_NOFOLLOW is set. 281 */ 282 if (error == EMLINK) 283 error = ELOOP; 284 fdrop(fp, td); 285 return (error); 286 } 287 NDFREE(&nd, NDF_ONLY_PNBUF); 288 filecaps_free(&nd.ni_filecaps); 289 fp->f_vnode = vp = nd.ni_vp; 290 291 /* Install vnode operations if no custom operations are provided. */ 292 if (fp->f_ops == &badfileops) { 293 fp->f_seqcount = 1; 294 finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK), 295 DTYPE_VNODE, vp, &vnops); 296 } 297 VOP_UNLOCK(vp, 0); 298 299 /* Truncate file. */ 300 if (fflags & O_TRUNC) { 301 error = fo_truncate(fp, 0, td->td_ucred, td); 302 if (error != 0) { 303 fdrop(fp, td); 304 return (error); 305 } 306 } 307 308success: 309 /* Determine which Capsicum rights to set on the file descriptor. */ 310 cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp), 311 &fds.fs_rights_base, &fds.fs_rights_inheriting); 312 cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting, 313 &fcaps.fc_rights); 314 if (cap_rights_is_set(&fcaps.fc_rights)) 315 fcaps.fc_fcntls = CAP_FCNTL_SETFL; 316 317 error = finstall(td, fp, &fd, fflags, &fcaps); 318 fdrop(fp, td); 319 if (error != 0) 320 return (error); 321 td->td_retval[0] = fd; 322 return (0); 323} 324 325/* Converts a FreeBSD directory entry structure and writes it to userspace. */ 326static int 327write_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio) 328{ 329 cloudabi_dirent_t cde = { 330 .d_next = cookie, 331 .d_ino = bde->d_fileno, 332 .d_namlen = bde->d_namlen, 333 }; 334 size_t len; 335 int error; 336 337 /* Convert file type. */ 338 switch (bde->d_type) { 339 case DT_BLK: 340 cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE; 341 break; 342 case DT_CHR: 343 cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 344 break; 345 case DT_DIR: 346 cde.d_type = CLOUDABI_FILETYPE_DIRECTORY; 347 break; 348 case DT_FIFO: 349 cde.d_type = CLOUDABI_FILETYPE_FIFO; 350 break; 351 case DT_LNK: 352 cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 353 break; 354 case DT_REG: 355 cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE; 356 break; 357 case DT_SOCK: 358 /* The exact socket type cannot be derived. */ 359 cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM; 360 break; 361 default: 362 cde.d_type = CLOUDABI_FILETYPE_UNKNOWN; 363 break; 364 } 365 366 /* Write directory entry structure. */ 367 len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid; 368 error = uiomove(&cde, len, uio); 369 if (error != 0) 370 return (error); 371 372 /* Write filename. */ 373 len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid; 374 return (uiomove(bde->d_name, len, uio)); 375} 376 377int 378cloudabi_sys_file_readdir(struct thread *td, 379 struct cloudabi_sys_file_readdir_args *uap) 380{ 381 struct iovec iov = { 382 .iov_base = uap->buf, 383 .iov_len = uap->buf_len 384 }; 385 struct uio uio = { 386 .uio_iov = &iov, 387 .uio_iovcnt = 1, 388 .uio_resid = iov.iov_len, 389 .uio_segflg = UIO_USERSPACE, 390 .uio_rw = UIO_READ, 391 .uio_td = td 392 }; 393 struct file *fp; 394 struct vnode *vp; 395 void *readbuf; 396 cap_rights_t rights; 397 cloudabi_dircookie_t offset; 398 int error; 399 400 /* Obtain directory vnode. */ 401 error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp); 402 if (error != 0) { 403 if (error == EINVAL) 404 return (ENOTDIR); 405 return (error); 406 } 407 if ((fp->f_flag & FREAD) == 0) { 408 fdrop(fp, td); 409 return (EBADF); 410 } 411 412 /* 413 * Call VOP_READDIR() and convert resulting data until the user 414 * provided buffer is filled. 415 */ 416 readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 417 offset = uap->cookie; 418 vp = fp->f_vnode; 419 while (uio.uio_resid > 0) { 420 struct iovec readiov = { 421 .iov_base = readbuf, 422 .iov_len = MAXBSIZE 423 }; 424 struct uio readuio = { 425 .uio_iov = &readiov, 426 .uio_iovcnt = 1, 427 .uio_rw = UIO_READ, 428 .uio_segflg = UIO_SYSSPACE, 429 .uio_td = td, 430 .uio_resid = MAXBSIZE, 431 .uio_offset = offset 432 }; 433 struct dirent *bde; 434 unsigned long *cookies, *cookie; 435 size_t readbuflen; 436 int eof, ncookies; 437 438 /* Validate file type. */ 439 vn_lock(vp, LK_SHARED | LK_RETRY); 440 if (vp->v_type != VDIR) { 441 VOP_UNLOCK(vp, 0); 442 error = ENOTDIR; 443 goto done; 444 } 445#ifdef MAC 446 error = mac_vnode_check_readdir(td->td_ucred, vp); 447 if (error != 0) { 448 VOP_UNLOCK(vp, 0); 449 goto done; 450 } 451#endif /* MAC */ 452 453 /* Read new directory entries. */ 454 cookies = NULL; 455 ncookies = 0; 456 error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof, 457 &ncookies, &cookies); 458 VOP_UNLOCK(vp, 0); 459 if (error != 0) 460 goto done; 461 462 /* Convert entries to CloudABI's format. */ 463 readbuflen = MAXBSIZE - readuio.uio_resid; 464 bde = readbuf; 465 cookie = cookies; 466 while (readbuflen >= offsetof(struct dirent, d_name) && 467 uio.uio_resid > 0 && ncookies > 0) { 468 /* Ensure that the returned offset always increases. */ 469 if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 && 470 *cookie > offset) { 471 error = write_dirent(bde, *cookie, &uio); 472 if (error != 0) { 473 free(cookies, M_TEMP); 474 goto done; 475 } 476 } 477 478 if (offset < *cookie) 479 offset = *cookie; 480 ++cookie; 481 --ncookies; 482 readbuflen -= bde->d_reclen; 483 bde = (struct dirent *)((char *)bde + bde->d_reclen); 484 } 485 free(cookies, M_TEMP); 486 if (eof) 487 break; 488 } 489 490done: 491 fdrop(fp, td); 492 free(readbuf, M_TEMP); 493 if (error != 0) 494 return (error); 495 496 /* Return number of bytes copied to userspace. */ 497 td->td_retval[0] = uap->buf_len - uio.uio_resid; 498 return (0); 499} 500 501int 502cloudabi_sys_file_readlink(struct thread *td, 503 struct cloudabi_sys_file_readlink_args *uap) 504{ 505 char *path; 506 int error; 507 508 error = copyin_path(uap->path, uap->path_len, &path); 509 if (error != 0) 510 return (error); 511 512 error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 513 uap->buf, UIO_USERSPACE, uap->buf_len); 514 cloudabi_freestr(path); 515 return (error); 516} 517 518int 519cloudabi_sys_file_rename(struct thread *td, 520 struct cloudabi_sys_file_rename_args *uap) 521{ 522 char *old, *new; 523 int error; 524 525 error = copyin_path(uap->path1, uap->path1_len, &old); 526 if (error != 0) 527 return (error); 528 error = copyin_path(uap->path2, uap->path2_len, &new); 529 if (error != 0) { 530 cloudabi_freestr(old); 531 return (error); 532 } 533 534 error = kern_renameat(td, uap->fd1, old, uap->fd2, new, 535 UIO_SYSSPACE); 536 cloudabi_freestr(old); 537 cloudabi_freestr(new); 538 return (error); 539} 540 541/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 542static void 543convert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 544{ 545 cloudabi_filestat_t res = { 546 .st_dev = sb->st_dev, 547 .st_ino = sb->st_ino, 548 .st_nlink = sb->st_nlink, 549 .st_size = sb->st_size, 550 }; 551 552 cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 553 cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 554 cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 555 *csb = res; 556} 557 558int 559cloudabi_sys_file_stat_fget(struct thread *td, 560 struct cloudabi_sys_file_stat_fget_args *uap) 561{ 562 struct stat sb; 563 cloudabi_filestat_t csb; 564 struct file *fp; 565 cap_rights_t rights; 566 cloudabi_filetype_t filetype; 567 int error; 568 569 /* Fetch file descriptor attributes. */ 570 error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); 571 if (error != 0) 572 return (error); 573 error = fo_stat(fp, &sb, td->td_ucred, td); 574 if (error != 0) { 575 fdrop(fp, td); 576 return (error); 577 } 578 filetype = cloudabi_convert_filetype(fp); 579 fdrop(fp, td); 580 581 /* Convert attributes to CloudABI's format. */ 582 convert_stat(&sb, &csb); 583 csb.st_filetype = filetype; 584 return (copyout(&csb, uap->buf, sizeof(csb))); 585} 586 587/* Converts timestamps to arguments to futimens() and utimensat(). */ 588static void 589convert_utimens_arguments(const cloudabi_filestat_t *fs, 590 cloudabi_fsflags_t flags, struct timespec *ts) 591{ 592 593 if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) { 594 ts[0].tv_nsec = UTIME_NOW; 595 } else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) { 596 ts[0].tv_sec = fs->st_atim / 1000000000; 597 ts[0].tv_nsec = fs->st_atim % 1000000000; 598 } else { 599 ts[0].tv_nsec = UTIME_OMIT; 600 } 601 602 if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) { 603 ts[1].tv_nsec = UTIME_NOW; 604 } else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) { 605 ts[1].tv_sec = fs->st_mtim / 1000000000; 606 ts[1].tv_nsec = fs->st_mtim % 1000000000; 607 } else { 608 ts[1].tv_nsec = UTIME_OMIT; 609 } 610} 611 612int 613cloudabi_sys_file_stat_fput(struct thread *td, 614 struct cloudabi_sys_file_stat_fput_args *uap) 615{ 616 cloudabi_filestat_t fs; 617 struct timespec ts[2]; 618 int error; 619 620 error = copyin(uap->buf, &fs, sizeof(fs)); 621 if (error != 0) 622 return (error); 623 624 /* 625 * Only support truncation and timestamp modification separately 626 * for now, to prevent unnecessary code duplication. 627 */ 628 if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) { 629 /* Call into kern_ftruncate() for file truncation. */ 630 if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0) 631 return (EINVAL); 632 return (kern_ftruncate(td, uap->fd, fs.st_size)); 633 } else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM | 634 CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 635 CLOUDABI_FILESTAT_MTIM_NOW)) != 0) { 636 /* Call into kern_futimens() for timestamp modification. */ 637 if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 638 CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 639 CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 640 return (EINVAL); 641 convert_utimens_arguments(&fs, uap->flags, ts); 642 return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE)); 643 } 644 return (EINVAL); 645} 646 647int 648cloudabi_sys_file_stat_get(struct thread *td, 649 struct cloudabi_sys_file_stat_get_args *uap) 650{ 651 struct stat sb; 652 cloudabi_filestat_t csb; 653 char *path; 654 int error; 655 656 error = copyin_path(uap->path, uap->path_len, &path); 657 if (error != 0) 658 return (error); 659 660 error = kern_statat(td, 661 (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 662 AT_SYMLINK_NOFOLLOW, uap->fd.fd, path, UIO_SYSSPACE, &sb, NULL); 663 cloudabi_freestr(path); 664 if (error != 0) 665 return (error); 666 667 /* Convert results and return them. */ 668 convert_stat(&sb, &csb); 669 if (S_ISBLK(sb.st_mode)) 670 csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 671 else if (S_ISCHR(sb.st_mode)) 672 csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 673 else if (S_ISDIR(sb.st_mode)) 674 csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 675 else if (S_ISFIFO(sb.st_mode)) 676 csb.st_filetype = CLOUDABI_FILETYPE_FIFO; 677 else if (S_ISREG(sb.st_mode)) 678 csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 679 else if (S_ISSOCK(sb.st_mode)) { 680 /* Inaccurate, but the best that we can do. */ 681 csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 682 } else if (S_ISLNK(sb.st_mode)) 683 csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 684 else 685 csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 686 return (copyout(&csb, uap->buf, sizeof(csb))); 687} 688 689int 690cloudabi_sys_file_stat_put(struct thread *td, 691 struct cloudabi_sys_file_stat_put_args *uap) 692{ 693 cloudabi_filestat_t fs; 694 struct timespec ts[2]; 695 char *path; 696 int error; 697 698 /* 699 * Only support timestamp modification for now, as there is no 700 * truncateat(). 701 */ 702 if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 703 CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 704 CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 705 return (EINVAL); 706 707 error = copyin(uap->buf, &fs, sizeof(fs)); 708 if (error != 0) 709 return (error); 710 error = copyin_path(uap->path, uap->path_len, &path); 711 if (error != 0) 712 return (error); 713 714 convert_utimens_arguments(&fs, uap->flags, ts); 715 error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts, 716 UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 717 0 : AT_SYMLINK_NOFOLLOW); 718 cloudabi_freestr(path); 719 return (error); 720} 721 722int 723cloudabi_sys_file_symlink(struct thread *td, 724 struct cloudabi_sys_file_symlink_args *uap) 725{ 726 char *path1, *path2; 727 int error; 728 729 error = copyin_path(uap->path1, uap->path1_len, &path1); 730 if (error != 0) 731 return (error); 732 error = copyin_path(uap->path2, uap->path2_len, &path2); 733 if (error != 0) { 734 cloudabi_freestr(path1); 735 return (error); 736 } 737 738 error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 739 cloudabi_freestr(path1); 740 cloudabi_freestr(path2); 741 return (error); 742} 743 744int 745cloudabi_sys_file_unlink(struct thread *td, 746 struct cloudabi_sys_file_unlink_args *uap) 747{ 748 char *path; 749 int error; 750 751 error = copyin_path(uap->path, uap->path_len, &path); 752 if (error != 0) 753 return (error); 754 755 if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) 756 error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 757 else 758 error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 759 cloudabi_freestr(path); 760 return (error); 761} 762