cloudabi_file.c revision 286633
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: head/sys/compat/cloudabi/cloudabi_file.c 286633 2015-08-11 14:08:46Z 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 <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 48static 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 71static int 72copyin_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 94static void 95cloudabi_freestr(char *buf) 96{ 97 98 free(buf, M_CLOUDABI_PATH); 99} 100 101int 102cloudabi_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 133int 134cloudabi_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 141int 142cloudabi_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 172int 173cloudabi_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 196int 197cloudabi_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 307success: 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. */ 325static int 326write_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 376int 377cloudabi_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 489done: 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 500int 501cloudabi_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 517int 518cloudabi_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. */ 541static void 542convert_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 557int 558cloudabi_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(). */ 587static void 588convert_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 611int 612cloudabi_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 646int 647cloudabi_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 688int 689cloudabi_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 721int 722cloudabi_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 743int 744cloudabi_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