cloudabi_file.c revision 324250
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 324250 2017-10-04 07:35:01Z 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 default: 163 error = EINVAL; 164 break; 165 } 166 cloudabi_freestr(path); 167 return (error); 168} 169 170int 171cloudabi_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 194int 195cloudabi_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 305success: 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. */ 323static int 324write_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 374int 375cloudabi_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 cap_rights_t rights; 394 cloudabi_dircookie_t offset; 395 int error; 396 397 /* Obtain directory vnode. */ 398 error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp); 399 if (error != 0) { 400 if (error == EINVAL) 401 return (ENOTDIR); 402 return (error); 403 } 404 if ((fp->f_flag & FREAD) == 0) { 405 fdrop(fp, td); 406 return (EBADF); 407 } 408 409 /* 410 * Call VOP_READDIR() and convert resulting data until the user 411 * provided buffer is filled. 412 */ 413 readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 414 offset = uap->cookie; 415 vp = fp->f_vnode; 416 while (uio.uio_resid > 0) { 417 struct iovec readiov = { 418 .iov_base = readbuf, 419 .iov_len = MAXBSIZE 420 }; 421 struct uio readuio = { 422 .uio_iov = &readiov, 423 .uio_iovcnt = 1, 424 .uio_rw = UIO_READ, 425 .uio_segflg = UIO_SYSSPACE, 426 .uio_td = td, 427 .uio_resid = MAXBSIZE, 428 .uio_offset = offset 429 }; 430 struct dirent *bde; 431 unsigned long *cookies, *cookie; 432 size_t readbuflen; 433 int eof, ncookies; 434 435 /* Validate file type. */ 436 vn_lock(vp, LK_SHARED | LK_RETRY); 437 if (vp->v_type != VDIR) { 438 VOP_UNLOCK(vp, 0); 439 error = ENOTDIR; 440 goto done; 441 } 442#ifdef MAC 443 error = mac_vnode_check_readdir(td->td_ucred, vp); 444 if (error != 0) { 445 VOP_UNLOCK(vp, 0); 446 goto done; 447 } 448#endif /* MAC */ 449 450 /* Read new directory entries. */ 451 cookies = NULL; 452 ncookies = 0; 453 error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof, 454 &ncookies, &cookies); 455 VOP_UNLOCK(vp, 0); 456 if (error != 0) 457 goto done; 458 459 /* Convert entries to CloudABI's format. */ 460 readbuflen = MAXBSIZE - readuio.uio_resid; 461 bde = readbuf; 462 cookie = cookies; 463 while (readbuflen >= offsetof(struct dirent, d_name) && 464 uio.uio_resid > 0 && ncookies > 0) { 465 /* Ensure that the returned offset always increases. */ 466 if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 && 467 *cookie > offset) { 468 error = write_dirent(bde, *cookie, &uio); 469 if (error != 0) { 470 free(cookies, M_TEMP); 471 goto done; 472 } 473 } 474 475 if (offset < *cookie) 476 offset = *cookie; 477 ++cookie; 478 --ncookies; 479 readbuflen -= bde->d_reclen; 480 bde = (struct dirent *)((char *)bde + bde->d_reclen); 481 } 482 free(cookies, M_TEMP); 483 if (eof) 484 break; 485 } 486 487done: 488 fdrop(fp, td); 489 free(readbuf, M_TEMP); 490 if (error != 0) 491 return (error); 492 493 /* Return number of bytes copied to userspace. */ 494 td->td_retval[0] = uap->buf_len - uio.uio_resid; 495 return (0); 496} 497 498int 499cloudabi_sys_file_readlink(struct thread *td, 500 struct cloudabi_sys_file_readlink_args *uap) 501{ 502 char *path; 503 int error; 504 505 error = copyin_path(uap->path, uap->path_len, &path); 506 if (error != 0) 507 return (error); 508 509 error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 510 uap->buf, UIO_USERSPACE, uap->buf_len); 511 cloudabi_freestr(path); 512 return (error); 513} 514 515int 516cloudabi_sys_file_rename(struct thread *td, 517 struct cloudabi_sys_file_rename_args *uap) 518{ 519 char *old, *new; 520 int error; 521 522 error = copyin_path(uap->path1, uap->path1_len, &old); 523 if (error != 0) 524 return (error); 525 error = copyin_path(uap->path2, uap->path2_len, &new); 526 if (error != 0) { 527 cloudabi_freestr(old); 528 return (error); 529 } 530 531 error = kern_renameat(td, uap->fd1, old, uap->fd2, new, 532 UIO_SYSSPACE); 533 cloudabi_freestr(old); 534 cloudabi_freestr(new); 535 return (error); 536} 537 538/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 539static void 540convert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 541{ 542 cloudabi_filestat_t res = { 543 .st_dev = sb->st_dev, 544 .st_ino = sb->st_ino, 545 .st_nlink = sb->st_nlink, 546 .st_size = sb->st_size, 547 }; 548 549 cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 550 cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 551 cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 552 *csb = res; 553} 554 555int 556cloudabi_sys_file_stat_fget(struct thread *td, 557 struct cloudabi_sys_file_stat_fget_args *uap) 558{ 559 struct stat sb; 560 cloudabi_filestat_t csb; 561 struct file *fp; 562 cap_rights_t rights; 563 cloudabi_filetype_t filetype; 564 int error; 565 566 /* Fetch file descriptor attributes. */ 567 error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &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(). */ 585static void 586convert_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 609int 610cloudabi_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 644int 645cloudabi_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 error = copyin_path(uap->path, uap->path_len, &path); 654 if (error != 0) 655 return (error); 656 657 error = kern_statat(td, 658 (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 659 AT_SYMLINK_NOFOLLOW, uap->fd.fd, path, UIO_SYSSPACE, &sb, NULL); 660 cloudabi_freestr(path); 661 if (error != 0) 662 return (error); 663 664 /* Convert results and return them. */ 665 convert_stat(&sb, &csb); 666 if (S_ISBLK(sb.st_mode)) 667 csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 668 else if (S_ISCHR(sb.st_mode)) 669 csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 670 else if (S_ISDIR(sb.st_mode)) 671 csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 672 else if (S_ISFIFO(sb.st_mode)) 673 csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 674 else if (S_ISREG(sb.st_mode)) 675 csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 676 else if (S_ISSOCK(sb.st_mode)) { 677 /* Inaccurate, but the best that we can do. */ 678 csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 679 } else if (S_ISLNK(sb.st_mode)) 680 csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 681 else 682 csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 683 return (copyout(&csb, uap->buf, sizeof(csb))); 684} 685 686int 687cloudabi_sys_file_stat_put(struct thread *td, 688 struct cloudabi_sys_file_stat_put_args *uap) 689{ 690 cloudabi_filestat_t fs; 691 struct timespec ts[2]; 692 char *path; 693 int error; 694 695 /* 696 * Only support timestamp modification for now, as there is no 697 * truncateat(). 698 */ 699 if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 700 CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 701 CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 702 return (EINVAL); 703 704 error = copyin(uap->buf, &fs, sizeof(fs)); 705 if (error != 0) 706 return (error); 707 error = copyin_path(uap->path, uap->path_len, &path); 708 if (error != 0) 709 return (error); 710 711 convert_utimens_arguments(&fs, uap->flags, ts); 712 error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts, 713 UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 714 0 : AT_SYMLINK_NOFOLLOW); 715 cloudabi_freestr(path); 716 return (error); 717} 718 719int 720cloudabi_sys_file_symlink(struct thread *td, 721 struct cloudabi_sys_file_symlink_args *uap) 722{ 723 char *path1, *path2; 724 int error; 725 726 error = copyin_path(uap->path1, uap->path1_len, &path1); 727 if (error != 0) 728 return (error); 729 error = copyin_path(uap->path2, uap->path2_len, &path2); 730 if (error != 0) { 731 cloudabi_freestr(path1); 732 return (error); 733 } 734 735 error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 736 cloudabi_freestr(path1); 737 cloudabi_freestr(path2); 738 return (error); 739} 740 741int 742cloudabi_sys_file_unlink(struct thread *td, 743 struct cloudabi_sys_file_unlink_args *uap) 744{ 745 char *path; 746 int error; 747 748 error = copyin_path(uap->path, uap->path_len, &path); 749 if (error != 0) 750 return (error); 751 752 if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) 753 error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 754 else 755 error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 756 cloudabi_freestr(path); 757 return (error); 758} 759