cloudabi_file.c revision 286359
1285307Sed/*- 2285307Sed * Copyright (c) 2015 Nuxi, https://nuxi.nl/ 3285307Sed * 4285307Sed * Redistribution and use in source and binary forms, with or without 5285307Sed * modification, are permitted provided that the following conditions 6285307Sed * are met: 7285307Sed * 1. Redistributions of source code must retain the above copyright 8285307Sed * notice, this list of conditions and the following disclaimer. 9285307Sed * 2. Redistributions in binary form must reproduce the above copyright 10285307Sed * notice, this list of conditions and the following disclaimer in the 11285307Sed * documentation and/or other materials provided with the distribution. 12285307Sed * 13285307Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14285307Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15285307Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16285307Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17285307Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18285307Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19285307Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20285307Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21285307Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22285307Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23285307Sed * SUCH DAMAGE. 24285307Sed */ 25285307Sed 26285307Sed#include <sys/cdefs.h> 27285307Sed__FBSDID("$FreeBSD: head/sys/compat/cloudabi/cloudabi_file.c 286359 2015-08-06 06:47:28Z ed $"); 28285307Sed 29285596Sed#include <sys/param.h> 30285930Sed#include <sys/capsicum.h> 31285998Sed#include <sys/dirent.h> 32285596Sed#include <sys/fcntl.h> 33285834Sed#include <sys/kernel.h> 34285834Sed#include <sys/malloc.h> 35285834Sed#include <sys/namei.h> 36285930Sed#include <sys/proc.h> 37285930Sed#include <sys/stat.h> 38285596Sed#include <sys/syscallsubr.h> 39285998Sed#include <sys/uio.h> 40285998Sed#include <sys/vnode.h> 41285596Sed 42285307Sed#include <compat/cloudabi/cloudabi_proto.h> 43285596Sed#include <compat/cloudabi/cloudabi_syscalldefs.h> 44285930Sed#include <compat/cloudabi/cloudabi_util.h> 45285307Sed 46285998Sed#include <security/mac/mac_framework.h> 47285998Sed 48285834Sedstatic MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); 49285834Sed 50285834Sed/* 51285834Sed * Copying pathnames from userspace to kernelspace. 52285834Sed * 53285834Sed * Unlike most operating systems, CloudABI doesn't use null-terminated 54285834Sed * pathname strings. Processes always pass pathnames to the kernel by 55285834Sed * providing a base pointer and a length. This has a couple of reasons: 56285834Sed * 57285834Sed * - It makes it easier to use CloudABI in combination with programming 58285834Sed * languages other than C, that may use non-null terminated strings. 59285834Sed * - It allows for calling system calls on individual components of the 60285834Sed * pathname without modifying the input string. 61285834Sed * 62285834Sed * The function below copies in pathname strings and null-terminates it. 63285834Sed * It also ensure that the string itself does not contain any null 64285834Sed * bytes. 65285834Sed * 66285834Sed * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass 67285834Sed * in unterminated pathname strings, so we can do away with 68285834Sed * the copying. 69285834Sed */ 70285834Sed 71285834Sedstatic int 72285834Sedcopyin_path(const char *uaddr, size_t len, char **result) 73285834Sed{ 74285834Sed char *buf; 75285834Sed int error; 76285834Sed 77285834Sed if (len >= PATH_MAX) 78285834Sed return (ENAMETOOLONG); 79285834Sed buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK); 80285834Sed error = copyin(uaddr, buf, len); 81285834Sed if (error != 0) { 82285834Sed free(buf, M_CLOUDABI_PATH); 83285834Sed return (error); 84285834Sed } 85285834Sed if (memchr(buf, '\0', len) != NULL) { 86285834Sed free(buf, M_CLOUDABI_PATH); 87285834Sed return (EINVAL); 88285834Sed } 89285834Sed buf[len] = '\0'; 90285834Sed *result = buf; 91285834Sed return (0); 92285834Sed} 93285834Sed 94285834Sedstatic void 95285834Sedcloudabi_freestr(char *buf) 96285834Sed{ 97285834Sed 98285834Sed free(buf, M_CLOUDABI_PATH); 99285834Sed} 100285834Sed 101285307Sedint 102285307Sedcloudabi_sys_file_advise(struct thread *td, 103285307Sed struct cloudabi_sys_file_advise_args *uap) 104285307Sed{ 105285596Sed int advice; 106285307Sed 107285596Sed switch (uap->advice) { 108285596Sed case CLOUDABI_ADVICE_DONTNEED: 109285596Sed advice = POSIX_FADV_DONTNEED; 110285596Sed break; 111285596Sed case CLOUDABI_ADVICE_NOREUSE: 112285596Sed advice = POSIX_FADV_NOREUSE; 113285596Sed break; 114285596Sed case CLOUDABI_ADVICE_NORMAL: 115285596Sed advice = POSIX_FADV_NORMAL; 116285596Sed break; 117285596Sed case CLOUDABI_ADVICE_RANDOM: 118285596Sed advice = POSIX_FADV_RANDOM; 119285596Sed break; 120285596Sed case CLOUDABI_ADVICE_SEQUENTIAL: 121285596Sed advice = POSIX_FADV_SEQUENTIAL; 122285596Sed break; 123285596Sed case CLOUDABI_ADVICE_WILLNEED: 124285596Sed advice = POSIX_FADV_WILLNEED; 125285596Sed break; 126285596Sed default: 127285596Sed return (EINVAL); 128285596Sed } 129285596Sed 130285596Sed return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice)); 131285307Sed} 132285307Sed 133285307Sedint 134285307Sedcloudabi_sys_file_allocate(struct thread *td, 135285307Sed struct cloudabi_sys_file_allocate_args *uap) 136285307Sed{ 137285307Sed 138285596Sed return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len)); 139285307Sed} 140285307Sed 141285307Sedint 142285307Sedcloudabi_sys_file_create(struct thread *td, 143285307Sed struct cloudabi_sys_file_create_args *uap) 144285307Sed{ 145285931Sed char *path; 146285931Sed int error; 147285307Sed 148285931Sed error = copyin_path(uap->path, uap->pathlen, &path); 149285931Sed if (error != 0) 150285931Sed return (error); 151285931Sed 152285931Sed /* 153285931Sed * CloudABI processes cannot interact with UNIX credentials and 154285931Sed * permissions. Depend on the umask that is set prior to 155285931Sed * execution to restrict the file permissions. 156285931Sed */ 157285931Sed switch (uap->type) { 158285931Sed case CLOUDABI_FILETYPE_DIRECTORY: 159285931Sed error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777); 160285931Sed break; 161285931Sed case CLOUDABI_FILETYPE_FIFO: 162285931Sed error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666); 163285931Sed break; 164285931Sed default: 165285931Sed error = EINVAL; 166285931Sed break; 167285931Sed } 168285931Sed cloudabi_freestr(path); 169285931Sed return (error); 170285307Sed} 171285307Sed 172285307Sedint 173285307Sedcloudabi_sys_file_link(struct thread *td, 174285307Sed struct cloudabi_sys_file_link_args *uap) 175285307Sed{ 176285834Sed char *path1, *path2; 177285834Sed int error; 178285307Sed 179285834Sed error = copyin_path(uap->path1, uap->path1len, &path1); 180285834Sed if (error != 0) 181285834Sed return (error); 182285834Sed error = copyin_path(uap->path2, uap->path2len, &path2); 183285834Sed if (error != 0) { 184285834Sed cloudabi_freestr(path1); 185285834Sed return (error); 186285834Sed } 187285834Sed 188285834Sed error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2, 189285834Sed UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 190285834Sed FOLLOW : NOFOLLOW); 191285834Sed cloudabi_freestr(path1); 192285834Sed cloudabi_freestr(path2); 193285834Sed return (error); 194285307Sed} 195285307Sed 196285307Sedint 197285307Sedcloudabi_sys_file_open(struct thread *td, 198285307Sed struct cloudabi_sys_file_open_args *uap) 199285307Sed{ 200286359Sed cloudabi_fdstat_t fds; 201286359Sed cap_rights_t rights; 202286359Sed struct filecaps fcaps = {}; 203286359Sed struct nameidata nd; 204286359Sed struct file *fp; 205286359Sed struct vnode *vp; 206286359Sed char *path; 207286359Sed int error, fd, fflags; 208286359Sed bool read, write; 209285307Sed 210286359Sed error = copyin(uap->fds, &fds, sizeof(fds)); 211286359Sed if (error != 0) 212286359Sed return (error); 213286359Sed 214286359Sed /* All the requested rights should be set on the descriptor. */ 215286359Sed error = cloudabi_convert_rights( 216286359Sed fds.fs_rights_base | fds.fs_rights_inheriting, &rights); 217286359Sed if (error != 0) 218286359Sed return (error); 219286359Sed cap_rights_set(&rights, CAP_LOOKUP); 220286359Sed 221286359Sed /* Convert rights to corresponding access mode. */ 222286359Sed read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ | 223286359Sed CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0; 224286359Sed write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC | 225286359Sed CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE | 226286359Sed CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0; 227286359Sed fflags = read ? write ? FREAD | FWRITE : FREAD : FWRITE; 228286359Sed 229286359Sed /* Convert open flags. */ 230286359Sed if ((uap->oflags & CLOUDABI_O_CREAT) != 0) { 231286359Sed fflags |= O_CREAT; 232286359Sed cap_rights_set(&rights, CAP_CREATE); 233286359Sed } 234286359Sed if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0) 235286359Sed fflags |= O_DIRECTORY; 236286359Sed if ((uap->oflags & CLOUDABI_O_EXCL) != 0) 237286359Sed fflags |= O_EXCL; 238286359Sed if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) { 239286359Sed fflags |= O_TRUNC; 240286359Sed cap_rights_set(&rights, CAP_FTRUNCATE); 241286359Sed } 242286359Sed if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0) 243286359Sed fflags |= O_APPEND; 244286359Sed if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0) 245286359Sed fflags |= O_NONBLOCK; 246286359Sed if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC | 247286359Sed CLOUDABI_FDFLAG_RSYNC)) != 0) { 248286359Sed fflags |= O_SYNC; 249286359Sed cap_rights_set(&rights, CAP_FSYNC); 250286359Sed } 251286359Sed if ((uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0) 252286359Sed fflags |= O_NOFOLLOW; 253286359Sed if (write && (fflags & (O_APPEND | O_TRUNC)) == 0) 254286359Sed cap_rights_set(&rights, CAP_SEEK); 255286359Sed 256286359Sed /* Allocate new file descriptor. */ 257286359Sed error = falloc_noinstall(td, &fp); 258286359Sed if (error != 0) 259286359Sed return (error); 260286359Sed fp->f_flag = fflags & FMASK; 261286359Sed 262286359Sed /* Open path. */ 263286359Sed error = copyin_path(uap->path, uap->pathlen, &path); 264286359Sed if (error != 0) { 265286359Sed fdrop(fp, td); 266286359Sed return (error); 267286359Sed } 268286359Sed NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->fd, 269286359Sed &rights, td); 270286359Sed error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp); 271286359Sed cloudabi_freestr(path); 272286359Sed if (error != 0) { 273286359Sed /* Custom operations provided. */ 274286359Sed if (error == ENXIO && fp->f_ops != &badfileops) 275286359Sed goto success; 276286359Sed 277286359Sed /* 278286359Sed * POSIX compliance: return ELOOP in case openat() is 279286359Sed * called on a symbolic link and O_NOFOLLOW is set. 280286359Sed */ 281286359Sed if (error == EMLINK) 282286359Sed error = ELOOP; 283286359Sed fdrop(fp, td); 284286359Sed return (error); 285286359Sed } 286286359Sed NDFREE(&nd, NDF_ONLY_PNBUF); 287286359Sed filecaps_free(&nd.ni_filecaps); 288286359Sed fp->f_vnode = vp = nd.ni_vp; 289286359Sed 290286359Sed /* Install vnode operations if no custom operations are provided. */ 291286359Sed if (fp->f_ops == &badfileops) { 292286359Sed fp->f_seqcount = 1; 293286359Sed finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK), 294286359Sed DTYPE_VNODE, vp, &vnops); 295286359Sed } 296286359Sed VOP_UNLOCK(vp, 0); 297286359Sed 298286359Sed /* Truncate file. */ 299286359Sed if (fflags & O_TRUNC) { 300286359Sed error = fo_truncate(fp, 0, td->td_ucred, td); 301286359Sed if (error != 0) { 302286359Sed fdrop(fp, td); 303286359Sed return (error); 304286359Sed } 305286359Sed } 306286359Sed 307286359Sedsuccess: 308286359Sed /* Determine which Capsicum rights to set on the file descriptor. */ 309286359Sed cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp), 310286359Sed &fds.fs_rights_base, &fds.fs_rights_inheriting); 311286359Sed cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting, 312286359Sed &fcaps.fc_rights); 313286359Sed if (cap_rights_is_set(&fcaps.fc_rights)) 314286359Sed fcaps.fc_fcntls = CAP_FCNTL_SETFL; 315286359Sed 316286359Sed error = finstall(td, fp, &fd, fflags, &fcaps); 317286359Sed fdrop(fp, td); 318286359Sed if (error != 0) 319286359Sed return (error); 320286359Sed td->td_retval[0] = fd; 321286359Sed return (0); 322285307Sed} 323285307Sed 324285998Sed/* Converts a FreeBSD directory entry structure and writes it to userspace. */ 325285998Sedstatic int 326285998Sedwrite_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio) 327285998Sed{ 328285998Sed cloudabi_dirent_t cde = { 329285998Sed .d_next = cookie, 330285998Sed .d_ino = bde->d_fileno, 331285998Sed .d_namlen = bde->d_namlen, 332285998Sed }; 333285998Sed size_t len; 334285998Sed int error; 335285998Sed 336285998Sed /* Convert file type. */ 337285998Sed switch (bde->d_type) { 338285998Sed case DT_BLK: 339285998Sed cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE; 340285998Sed break; 341285998Sed case DT_CHR: 342285998Sed cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 343285998Sed break; 344285998Sed case DT_DIR: 345285998Sed cde.d_type = CLOUDABI_FILETYPE_DIRECTORY; 346285998Sed break; 347285998Sed case DT_FIFO: 348285998Sed cde.d_type = CLOUDABI_FILETYPE_FIFO; 349285998Sed break; 350285998Sed case DT_LNK: 351285998Sed cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 352285998Sed break; 353285998Sed case DT_REG: 354285998Sed cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE; 355285998Sed break; 356285998Sed case DT_SOCK: 357285998Sed /* The exact socket type cannot be derived. */ 358285998Sed cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM; 359285998Sed break; 360285998Sed default: 361285998Sed cde.d_type = CLOUDABI_FILETYPE_UNKNOWN; 362285998Sed break; 363285998Sed } 364285998Sed 365285998Sed /* Write directory entry structure. */ 366285998Sed len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid; 367285998Sed error = uiomove(&cde, len, uio); 368285998Sed if (error != 0) 369285998Sed return (error); 370285998Sed 371285998Sed /* Write filename. */ 372285998Sed len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid; 373285998Sed return (uiomove(bde->d_name, len, uio)); 374285998Sed} 375285998Sed 376285307Sedint 377285307Sedcloudabi_sys_file_readdir(struct thread *td, 378285307Sed struct cloudabi_sys_file_readdir_args *uap) 379285307Sed{ 380285998Sed struct iovec iov = { 381285998Sed .iov_base = uap->buf, 382285998Sed .iov_len = uap->nbyte 383285998Sed }; 384285998Sed struct uio uio = { 385285998Sed .uio_iov = &iov, 386285998Sed .uio_iovcnt = 1, 387285998Sed .uio_resid = iov.iov_len, 388285998Sed .uio_segflg = UIO_USERSPACE, 389285998Sed .uio_rw = UIO_READ, 390285998Sed .uio_td = td 391285998Sed }; 392285998Sed struct file *fp; 393285998Sed struct vnode *vp; 394285998Sed void *readbuf; 395285998Sed cap_rights_t rights; 396285998Sed cloudabi_dircookie_t offset; 397285998Sed int error; 398285307Sed 399285998Sed /* Obtain directory vnode. */ 400285998Sed error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp); 401285998Sed if (error != 0) { 402285998Sed if (error == EINVAL) 403285998Sed return (ENOTDIR); 404285998Sed return (error); 405285998Sed } 406285998Sed if ((fp->f_flag & FREAD) == 0) { 407285998Sed fdrop(fp, td); 408285998Sed return (EBADF); 409285998Sed } 410285998Sed 411285998Sed /* 412285998Sed * Call VOP_READDIR() and convert resulting data until the user 413285998Sed * provided buffer is filled. 414285998Sed */ 415285998Sed readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 416285998Sed offset = uap->cookie; 417285998Sed vp = fp->f_vnode; 418285998Sed while (uio.uio_resid > 0) { 419285998Sed struct iovec readiov = { 420285998Sed .iov_base = readbuf, 421285998Sed .iov_len = MAXBSIZE 422285998Sed }; 423285998Sed struct uio readuio = { 424285998Sed .uio_iov = &readiov, 425285998Sed .uio_iovcnt = 1, 426285998Sed .uio_rw = UIO_READ, 427285998Sed .uio_segflg = UIO_SYSSPACE, 428285998Sed .uio_td = td, 429285998Sed .uio_resid = MAXBSIZE, 430285998Sed .uio_offset = offset 431285998Sed }; 432285998Sed struct dirent *bde; 433285998Sed unsigned long *cookies, *cookie; 434285998Sed size_t readbuflen; 435285998Sed int eof, ncookies; 436285998Sed 437285998Sed /* Validate file type. */ 438285998Sed vn_lock(vp, LK_SHARED | LK_RETRY); 439285998Sed if (vp->v_type != VDIR) { 440285998Sed VOP_UNLOCK(vp, 0); 441285998Sed error = ENOTDIR; 442285998Sed goto done; 443285998Sed } 444285998Sed#ifdef MAC 445285998Sed error = mac_vnode_check_readdir(td->td_ucred, vp); 446285998Sed if (error != 0) { 447285998Sed VOP_UNLOCK(vp, 0); 448285998Sed goto done; 449285998Sed } 450285998Sed#endif /* MAC */ 451285998Sed 452285998Sed /* Read new directory entries. */ 453285998Sed cookies = NULL; 454285998Sed ncookies = 0; 455285998Sed error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof, 456285998Sed &ncookies, &cookies); 457285998Sed VOP_UNLOCK(vp, 0); 458285998Sed if (error != 0) 459285998Sed goto done; 460285998Sed 461285998Sed /* Convert entries to CloudABI's format. */ 462285998Sed readbuflen = MAXBSIZE - readuio.uio_resid; 463285998Sed bde = readbuf; 464285998Sed cookie = cookies; 465285998Sed while (readbuflen >= offsetof(struct dirent, d_name) && 466285998Sed uio.uio_resid > 0 && ncookies > 0) { 467285998Sed /* Ensure that the returned offset always increases. */ 468285998Sed if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 && 469285998Sed *cookie > offset) { 470285998Sed error = write_dirent(bde, *cookie, &uio); 471285998Sed if (error != 0) { 472285998Sed free(cookies, M_TEMP); 473285998Sed goto done; 474285998Sed } 475285998Sed } 476285998Sed 477285998Sed if (offset < *cookie) 478285998Sed offset = *cookie; 479285998Sed ++cookie; 480285998Sed --ncookies; 481285998Sed readbuflen -= bde->d_reclen; 482285998Sed bde = (struct dirent *)((char *)bde + bde->d_reclen); 483285998Sed } 484285998Sed free(cookies, M_TEMP); 485285998Sed if (eof) 486285998Sed break; 487285998Sed } 488285998Sed 489285998Seddone: 490285998Sed fdrop(fp, td); 491285998Sed free(readbuf, M_TEMP); 492285998Sed if (error != 0) 493285998Sed return (error); 494285998Sed 495285998Sed /* Return number of bytes copied to userspace. */ 496285998Sed td->td_retval[0] = uap->nbyte - uio.uio_resid; 497285998Sed return (0); 498285307Sed} 499285307Sed 500285307Sedint 501285307Sedcloudabi_sys_file_readlink(struct thread *td, 502285307Sed struct cloudabi_sys_file_readlink_args *uap) 503285307Sed{ 504285834Sed char *path; 505285834Sed int error; 506285307Sed 507285834Sed error = copyin_path(uap->path, uap->pathlen, &path); 508285834Sed if (error != 0) 509285834Sed return (error); 510285834Sed 511285834Sed error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 512285834Sed uap->buf, UIO_USERSPACE, uap->bufsize); 513285834Sed cloudabi_freestr(path); 514285834Sed return (error); 515285307Sed} 516285307Sed 517285307Sedint 518285307Sedcloudabi_sys_file_rename(struct thread *td, 519285307Sed struct cloudabi_sys_file_rename_args *uap) 520285307Sed{ 521285834Sed char *old, *new; 522285834Sed int error; 523285307Sed 524285834Sed error = copyin_path(uap->old, uap->oldlen, &old); 525285834Sed if (error != 0) 526285834Sed return (error); 527285834Sed error = copyin_path(uap->new, uap->newlen, &new); 528285834Sed if (error != 0) { 529285834Sed cloudabi_freestr(old); 530285834Sed return (error); 531285834Sed } 532285834Sed 533285834Sed error = kern_renameat(td, uap->oldfd, old, uap->newfd, new, 534285834Sed UIO_SYSSPACE); 535285834Sed cloudabi_freestr(old); 536285834Sed cloudabi_freestr(new); 537285834Sed return (error); 538285307Sed} 539285307Sed 540285930Sed/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 541285930Sedstatic void 542285930Sedconvert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 543285930Sed{ 544285930Sed cloudabi_filestat_t res = { 545285930Sed .st_dev = sb->st_dev, 546285930Sed .st_ino = sb->st_ino, 547285930Sed .st_nlink = sb->st_nlink, 548285930Sed .st_size = sb->st_size, 549285930Sed }; 550285930Sed 551285930Sed cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 552285930Sed cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 553285930Sed cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 554285930Sed *csb = res; 555285930Sed} 556285930Sed 557285307Sedint 558285307Sedcloudabi_sys_file_stat_fget(struct thread *td, 559285307Sed struct cloudabi_sys_file_stat_fget_args *uap) 560285307Sed{ 561285930Sed struct stat sb; 562285930Sed cloudabi_filestat_t csb; 563285930Sed struct file *fp; 564285930Sed cap_rights_t rights; 565285930Sed cloudabi_filetype_t filetype; 566285930Sed int error; 567285307Sed 568285930Sed /* Fetch file descriptor attributes. */ 569285930Sed error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); 570285930Sed if (error != 0) 571285930Sed return (error); 572285930Sed error = fo_stat(fp, &sb, td->td_ucred, td); 573285930Sed if (error != 0) { 574285930Sed fdrop(fp, td); 575285930Sed return (error); 576285930Sed } 577285930Sed filetype = cloudabi_convert_filetype(fp); 578285930Sed fdrop(fp, td); 579285930Sed 580285930Sed /* Convert attributes to CloudABI's format. */ 581285930Sed convert_stat(&sb, &csb); 582285930Sed csb.st_filetype = filetype; 583285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 584285307Sed} 585285307Sed 586285954Sed/* Converts timestamps to arguments to futimens() and utimensat(). */ 587285954Sedstatic void 588285954Sedconvert_utimens_arguments(const cloudabi_filestat_t *fs, 589285954Sed cloudabi_fsflags_t flags, struct timespec *ts) 590285954Sed{ 591285954Sed 592285954Sed if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) { 593285954Sed ts[0].tv_nsec = UTIME_NOW; 594285954Sed } else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) { 595285954Sed ts[0].tv_sec = fs->st_atim / 1000000000; 596285954Sed ts[0].tv_nsec = fs->st_atim % 1000000000; 597285954Sed } else { 598285954Sed ts[0].tv_nsec = UTIME_OMIT; 599285954Sed } 600285954Sed 601285954Sed if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) { 602285954Sed ts[1].tv_nsec = UTIME_NOW; 603285954Sed } else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) { 604285954Sed ts[1].tv_sec = fs->st_mtim / 1000000000; 605285954Sed ts[1].tv_nsec = fs->st_mtim % 1000000000; 606285954Sed } else { 607285954Sed ts[1].tv_nsec = UTIME_OMIT; 608285954Sed } 609285954Sed} 610285954Sed 611285307Sedint 612285307Sedcloudabi_sys_file_stat_fput(struct thread *td, 613285307Sed struct cloudabi_sys_file_stat_fput_args *uap) 614285307Sed{ 615285954Sed cloudabi_filestat_t fs; 616285954Sed struct timespec ts[2]; 617285954Sed int error; 618285307Sed 619285954Sed error = copyin(uap->buf, &fs, sizeof(fs)); 620285954Sed if (error != 0) 621285954Sed return (error); 622285954Sed 623285954Sed /* 624285954Sed * Only support truncation and timestamp modification separately 625285954Sed * for now, to prevent unnecessary code duplication. 626285954Sed */ 627285954Sed if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) { 628285954Sed /* Call into kern_ftruncate() for file truncation. */ 629285954Sed if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0) 630285954Sed return (EINVAL); 631285954Sed return (kern_ftruncate(td, uap->fd, fs.st_size)); 632285954Sed } else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM | 633285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 634285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) { 635285954Sed /* Call into kern_futimens() for timestamp modification. */ 636285954Sed if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 637285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 638285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 639285954Sed return (EINVAL); 640285954Sed convert_utimens_arguments(&fs, uap->flags, ts); 641285954Sed return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE)); 642285954Sed } 643285954Sed return (EINVAL); 644285307Sed} 645285307Sed 646285307Sedint 647285307Sedcloudabi_sys_file_stat_get(struct thread *td, 648285307Sed struct cloudabi_sys_file_stat_get_args *uap) 649285307Sed{ 650285930Sed struct stat sb; 651285930Sed cloudabi_filestat_t csb; 652285930Sed char *path; 653285930Sed int error; 654285307Sed 655285930Sed error = copyin_path(uap->path, uap->pathlen, &path); 656285930Sed if (error != 0) 657285930Sed return (error); 658285930Sed 659285930Sed error = kern_statat(td, 660285930Sed (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 661285930Sed AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL); 662285930Sed cloudabi_freestr(path); 663285930Sed if (error != 0) 664285930Sed return (error); 665285930Sed 666285930Sed /* Convert results and return them. */ 667285930Sed convert_stat(&sb, &csb); 668285930Sed if (S_ISBLK(sb.st_mode)) 669285930Sed csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 670285930Sed else if (S_ISCHR(sb.st_mode)) 671285930Sed csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 672285930Sed else if (S_ISDIR(sb.st_mode)) 673285930Sed csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 674285930Sed else if (S_ISFIFO(sb.st_mode)) 675285930Sed csb.st_filetype = CLOUDABI_FILETYPE_FIFO; 676285930Sed else if (S_ISREG(sb.st_mode)) 677285930Sed csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 678285930Sed else if (S_ISSOCK(sb.st_mode)) { 679285930Sed /* Inaccurate, but the best that we can do. */ 680285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 681285930Sed } else if (S_ISLNK(sb.st_mode)) 682285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 683285930Sed else 684285930Sed csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 685285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 686285307Sed} 687285307Sed 688285307Sedint 689285307Sedcloudabi_sys_file_stat_put(struct thread *td, 690285307Sed struct cloudabi_sys_file_stat_put_args *uap) 691285307Sed{ 692285954Sed cloudabi_filestat_t fs; 693285954Sed struct timespec ts[2]; 694285954Sed char *path; 695285954Sed int error; 696285307Sed 697285954Sed /* 698285954Sed * Only support timestamp modification for now, as there is no 699285954Sed * truncateat(). 700285954Sed */ 701285954Sed if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 702285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 703285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 704285954Sed return (EINVAL); 705285954Sed 706285954Sed error = copyin(uap->buf, &fs, sizeof(fs)); 707285954Sed if (error != 0) 708285954Sed return (error); 709285954Sed error = copyin_path(uap->path, uap->pathlen, &path); 710285954Sed if (error != 0) 711285954Sed return (error); 712285954Sed 713285954Sed convert_utimens_arguments(&fs, uap->flags, ts); 714285954Sed error = kern_utimensat(td, uap->fd, path, UIO_SYSSPACE, ts, 715285954Sed UIO_SYSSPACE, (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 716285954Sed 0 : AT_SYMLINK_NOFOLLOW); 717285954Sed cloudabi_freestr(path); 718285954Sed return (error); 719285307Sed} 720285307Sed 721285307Sedint 722285307Sedcloudabi_sys_file_symlink(struct thread *td, 723285307Sed struct cloudabi_sys_file_symlink_args *uap) 724285307Sed{ 725285834Sed char *path1, *path2; 726285834Sed int error; 727285307Sed 728285834Sed error = copyin_path(uap->path1, uap->path1len, &path1); 729285834Sed if (error != 0) 730285834Sed return (error); 731285834Sed error = copyin_path(uap->path2, uap->path2len, &path2); 732285834Sed if (error != 0) { 733285834Sed cloudabi_freestr(path1); 734285834Sed return (error); 735285834Sed } 736285834Sed 737285834Sed error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 738285834Sed cloudabi_freestr(path1); 739285834Sed cloudabi_freestr(path2); 740285834Sed return (error); 741285307Sed} 742285307Sed 743285307Sedint 744285307Sedcloudabi_sys_file_unlink(struct thread *td, 745285307Sed struct cloudabi_sys_file_unlink_args *uap) 746285307Sed{ 747285834Sed char *path; 748285834Sed int error; 749285307Sed 750285834Sed error = copyin_path(uap->path, uap->pathlen, &path); 751285834Sed if (error != 0) 752285834Sed return (error); 753285834Sed 754285834Sed if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR) 755285834Sed error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 756285834Sed else 757285834Sed error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 758285834Sed cloudabi_freestr(path); 759285834Sed return (error); 760285307Sed} 761