cloudabi_file.c revision 316574
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: stable/11/sys/compat/cloudabi/cloudabi_file.c 316574 2017-04-06 15:10:36Z 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 42297247Sed#include <contrib/cloudabi/cloudabi_types_common.h> 43297247Sed 44285307Sed#include <compat/cloudabi/cloudabi_proto.h> 45285930Sed#include <compat/cloudabi/cloudabi_util.h> 46285307Sed 47285998Sed#include <security/mac/mac_framework.h> 48285998Sed 49285834Sedstatic MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); 50285834Sed 51285834Sed/* 52285834Sed * Copying pathnames from userspace to kernelspace. 53285834Sed * 54285834Sed * Unlike most operating systems, CloudABI doesn't use null-terminated 55285834Sed * pathname strings. Processes always pass pathnames to the kernel by 56285834Sed * providing a base pointer and a length. This has a couple of reasons: 57285834Sed * 58285834Sed * - It makes it easier to use CloudABI in combination with programming 59285834Sed * languages other than C, that may use non-null terminated strings. 60285834Sed * - It allows for calling system calls on individual components of the 61285834Sed * pathname without modifying the input string. 62285834Sed * 63285834Sed * The function below copies in pathname strings and null-terminates it. 64285834Sed * It also ensure that the string itself does not contain any null 65285834Sed * bytes. 66285834Sed * 67285834Sed * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass 68285834Sed * in unterminated pathname strings, so we can do away with 69285834Sed * the copying. 70285834Sed */ 71285834Sed 72285834Sedstatic int 73285834Sedcopyin_path(const char *uaddr, size_t len, char **result) 74285834Sed{ 75285834Sed char *buf; 76285834Sed int error; 77285834Sed 78285834Sed if (len >= PATH_MAX) 79285834Sed return (ENAMETOOLONG); 80285834Sed buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK); 81285834Sed error = copyin(uaddr, buf, len); 82285834Sed if (error != 0) { 83285834Sed free(buf, M_CLOUDABI_PATH); 84285834Sed return (error); 85285834Sed } 86285834Sed if (memchr(buf, '\0', len) != NULL) { 87285834Sed free(buf, M_CLOUDABI_PATH); 88285834Sed return (EINVAL); 89285834Sed } 90285834Sed buf[len] = '\0'; 91285834Sed *result = buf; 92285834Sed return (0); 93285834Sed} 94285834Sed 95285834Sedstatic void 96285834Sedcloudabi_freestr(char *buf) 97285834Sed{ 98285834Sed 99285834Sed free(buf, M_CLOUDABI_PATH); 100285834Sed} 101285834Sed 102285307Sedint 103285307Sedcloudabi_sys_file_advise(struct thread *td, 104285307Sed struct cloudabi_sys_file_advise_args *uap) 105285307Sed{ 106285596Sed int advice; 107285307Sed 108285596Sed switch (uap->advice) { 109285596Sed case CLOUDABI_ADVICE_DONTNEED: 110285596Sed advice = POSIX_FADV_DONTNEED; 111285596Sed break; 112285596Sed case CLOUDABI_ADVICE_NOREUSE: 113285596Sed advice = POSIX_FADV_NOREUSE; 114285596Sed break; 115285596Sed case CLOUDABI_ADVICE_NORMAL: 116285596Sed advice = POSIX_FADV_NORMAL; 117285596Sed break; 118285596Sed case CLOUDABI_ADVICE_RANDOM: 119285596Sed advice = POSIX_FADV_RANDOM; 120285596Sed break; 121285596Sed case CLOUDABI_ADVICE_SEQUENTIAL: 122285596Sed advice = POSIX_FADV_SEQUENTIAL; 123285596Sed break; 124285596Sed case CLOUDABI_ADVICE_WILLNEED: 125285596Sed advice = POSIX_FADV_WILLNEED; 126285596Sed break; 127285596Sed default: 128285596Sed return (EINVAL); 129285596Sed } 130285596Sed 131285596Sed return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice)); 132285307Sed} 133285307Sed 134285307Sedint 135285307Sedcloudabi_sys_file_allocate(struct thread *td, 136285307Sed struct cloudabi_sys_file_allocate_args *uap) 137285307Sed{ 138285307Sed 139285596Sed return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len)); 140285307Sed} 141285307Sed 142285307Sedint 143285307Sedcloudabi_sys_file_create(struct thread *td, 144285307Sed struct cloudabi_sys_file_create_args *uap) 145285307Sed{ 146285931Sed char *path; 147285931Sed int error; 148285307Sed 149316574Sed error = copyin_path(uap->path, uap->path_len, &path); 150285931Sed if (error != 0) 151285931Sed return (error); 152285931Sed 153285931Sed /* 154285931Sed * CloudABI processes cannot interact with UNIX credentials and 155285931Sed * permissions. Depend on the umask that is set prior to 156285931Sed * execution to restrict the file permissions. 157285931Sed */ 158285931Sed switch (uap->type) { 159285931Sed case CLOUDABI_FILETYPE_DIRECTORY: 160285931Sed error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777); 161285931Sed break; 162285931Sed case CLOUDABI_FILETYPE_FIFO: 163285931Sed error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666); 164285931Sed break; 165285931Sed default: 166285931Sed error = EINVAL; 167285931Sed break; 168285931Sed } 169285931Sed cloudabi_freestr(path); 170285931Sed return (error); 171285307Sed} 172285307Sed 173285307Sedint 174285307Sedcloudabi_sys_file_link(struct thread *td, 175285307Sed struct cloudabi_sys_file_link_args *uap) 176285307Sed{ 177285834Sed char *path1, *path2; 178285834Sed int error; 179285307Sed 180316574Sed error = copyin_path(uap->path1, uap->path1_len, &path1); 181285834Sed if (error != 0) 182285834Sed return (error); 183316574Sed error = copyin_path(uap->path2, uap->path2_len, &path2); 184285834Sed if (error != 0) { 185285834Sed cloudabi_freestr(path1); 186285834Sed return (error); 187285834Sed } 188285834Sed 189297247Sed error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2, 190297247Sed UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 191285834Sed FOLLOW : NOFOLLOW); 192285834Sed cloudabi_freestr(path1); 193285834Sed cloudabi_freestr(path2); 194285834Sed return (error); 195285307Sed} 196285307Sed 197285307Sedint 198285307Sedcloudabi_sys_file_open(struct thread *td, 199285307Sed struct cloudabi_sys_file_open_args *uap) 200285307Sed{ 201286359Sed cloudabi_fdstat_t fds; 202286359Sed cap_rights_t rights; 203286359Sed struct filecaps fcaps = {}; 204286359Sed struct nameidata nd; 205286359Sed struct file *fp; 206286359Sed struct vnode *vp; 207286359Sed char *path; 208286359Sed int error, fd, fflags; 209286359Sed bool read, write; 210285307Sed 211286359Sed error = copyin(uap->fds, &fds, sizeof(fds)); 212286359Sed if (error != 0) 213286359Sed return (error); 214286359Sed 215286359Sed /* All the requested rights should be set on the descriptor. */ 216286359Sed error = cloudabi_convert_rights( 217286359Sed fds.fs_rights_base | fds.fs_rights_inheriting, &rights); 218286359Sed if (error != 0) 219286359Sed return (error); 220286359Sed cap_rights_set(&rights, CAP_LOOKUP); 221286359Sed 222286359Sed /* Convert rights to corresponding access mode. */ 223286359Sed read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ | 224286359Sed CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0; 225286359Sed write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC | 226286359Sed CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE | 227286359Sed CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0; 228286633Sed fflags = write ? read ? FREAD | FWRITE : FWRITE : FREAD; 229286359Sed 230286359Sed /* Convert open flags. */ 231286359Sed if ((uap->oflags & CLOUDABI_O_CREAT) != 0) { 232286359Sed fflags |= O_CREAT; 233286359Sed cap_rights_set(&rights, CAP_CREATE); 234286359Sed } 235286359Sed if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0) 236286359Sed fflags |= O_DIRECTORY; 237286359Sed if ((uap->oflags & CLOUDABI_O_EXCL) != 0) 238286359Sed fflags |= O_EXCL; 239286359Sed if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) { 240286359Sed fflags |= O_TRUNC; 241286359Sed cap_rights_set(&rights, CAP_FTRUNCATE); 242286359Sed } 243286359Sed if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0) 244286359Sed fflags |= O_APPEND; 245286359Sed if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0) 246286359Sed fflags |= O_NONBLOCK; 247286359Sed if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC | 248286359Sed CLOUDABI_FDFLAG_RSYNC)) != 0) { 249286359Sed fflags |= O_SYNC; 250286359Sed cap_rights_set(&rights, CAP_FSYNC); 251286359Sed } 252297247Sed if ((uap->dirfd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0) 253286359Sed fflags |= O_NOFOLLOW; 254286359Sed if (write && (fflags & (O_APPEND | O_TRUNC)) == 0) 255286359Sed cap_rights_set(&rights, CAP_SEEK); 256286359Sed 257286359Sed /* Allocate new file descriptor. */ 258286359Sed error = falloc_noinstall(td, &fp); 259286359Sed if (error != 0) 260286359Sed return (error); 261286359Sed fp->f_flag = fflags & FMASK; 262286359Sed 263286359Sed /* Open path. */ 264316574Sed error = copyin_path(uap->path, uap->path_len, &path); 265286359Sed if (error != 0) { 266286359Sed fdrop(fp, td); 267286359Sed return (error); 268286359Sed } 269297247Sed NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->dirfd.fd, 270286359Sed &rights, td); 271286359Sed error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp); 272286359Sed cloudabi_freestr(path); 273286359Sed if (error != 0) { 274286359Sed /* Custom operations provided. */ 275286359Sed if (error == ENXIO && fp->f_ops != &badfileops) 276286359Sed goto success; 277286359Sed 278286359Sed /* 279286359Sed * POSIX compliance: return ELOOP in case openat() is 280286359Sed * called on a symbolic link and O_NOFOLLOW is set. 281286359Sed */ 282286359Sed if (error == EMLINK) 283286359Sed error = ELOOP; 284286359Sed fdrop(fp, td); 285286359Sed return (error); 286286359Sed } 287286359Sed NDFREE(&nd, NDF_ONLY_PNBUF); 288286359Sed filecaps_free(&nd.ni_filecaps); 289286359Sed fp->f_vnode = vp = nd.ni_vp; 290286359Sed 291286359Sed /* Install vnode operations if no custom operations are provided. */ 292286359Sed if (fp->f_ops == &badfileops) { 293286359Sed fp->f_seqcount = 1; 294286359Sed finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK), 295286359Sed DTYPE_VNODE, vp, &vnops); 296286359Sed } 297286359Sed VOP_UNLOCK(vp, 0); 298286359Sed 299286359Sed /* Truncate file. */ 300286359Sed if (fflags & O_TRUNC) { 301286359Sed error = fo_truncate(fp, 0, td->td_ucred, td); 302286359Sed if (error != 0) { 303286359Sed fdrop(fp, td); 304286359Sed return (error); 305286359Sed } 306286359Sed } 307286359Sed 308286359Sedsuccess: 309286359Sed /* Determine which Capsicum rights to set on the file descriptor. */ 310286359Sed cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp), 311286359Sed &fds.fs_rights_base, &fds.fs_rights_inheriting); 312286359Sed cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting, 313286359Sed &fcaps.fc_rights); 314286359Sed if (cap_rights_is_set(&fcaps.fc_rights)) 315286359Sed fcaps.fc_fcntls = CAP_FCNTL_SETFL; 316286359Sed 317286359Sed error = finstall(td, fp, &fd, fflags, &fcaps); 318286359Sed fdrop(fp, td); 319286359Sed if (error != 0) 320286359Sed return (error); 321286359Sed td->td_retval[0] = fd; 322286359Sed return (0); 323285307Sed} 324285307Sed 325285998Sed/* Converts a FreeBSD directory entry structure and writes it to userspace. */ 326285998Sedstatic int 327285998Sedwrite_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio) 328285998Sed{ 329285998Sed cloudabi_dirent_t cde = { 330285998Sed .d_next = cookie, 331285998Sed .d_ino = bde->d_fileno, 332285998Sed .d_namlen = bde->d_namlen, 333285998Sed }; 334285998Sed size_t len; 335285998Sed int error; 336285998Sed 337285998Sed /* Convert file type. */ 338285998Sed switch (bde->d_type) { 339285998Sed case DT_BLK: 340285998Sed cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE; 341285998Sed break; 342285998Sed case DT_CHR: 343285998Sed cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 344285998Sed break; 345285998Sed case DT_DIR: 346285998Sed cde.d_type = CLOUDABI_FILETYPE_DIRECTORY; 347285998Sed break; 348285998Sed case DT_FIFO: 349285998Sed cde.d_type = CLOUDABI_FILETYPE_FIFO; 350285998Sed break; 351285998Sed case DT_LNK: 352285998Sed cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 353285998Sed break; 354285998Sed case DT_REG: 355285998Sed cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE; 356285998Sed break; 357285998Sed case DT_SOCK: 358285998Sed /* The exact socket type cannot be derived. */ 359285998Sed cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM; 360285998Sed break; 361285998Sed default: 362285998Sed cde.d_type = CLOUDABI_FILETYPE_UNKNOWN; 363285998Sed break; 364285998Sed } 365285998Sed 366285998Sed /* Write directory entry structure. */ 367285998Sed len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid; 368285998Sed error = uiomove(&cde, len, uio); 369285998Sed if (error != 0) 370285998Sed return (error); 371285998Sed 372285998Sed /* Write filename. */ 373285998Sed len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid; 374285998Sed return (uiomove(bde->d_name, len, uio)); 375285998Sed} 376285998Sed 377285307Sedint 378285307Sedcloudabi_sys_file_readdir(struct thread *td, 379285307Sed struct cloudabi_sys_file_readdir_args *uap) 380285307Sed{ 381285998Sed struct iovec iov = { 382285998Sed .iov_base = uap->buf, 383316574Sed .iov_len = uap->buf_len 384285998Sed }; 385285998Sed struct uio uio = { 386285998Sed .uio_iov = &iov, 387285998Sed .uio_iovcnt = 1, 388285998Sed .uio_resid = iov.iov_len, 389285998Sed .uio_segflg = UIO_USERSPACE, 390285998Sed .uio_rw = UIO_READ, 391285998Sed .uio_td = td 392285998Sed }; 393285998Sed struct file *fp; 394285998Sed struct vnode *vp; 395285998Sed void *readbuf; 396285998Sed cap_rights_t rights; 397285998Sed cloudabi_dircookie_t offset; 398285998Sed int error; 399285307Sed 400285998Sed /* Obtain directory vnode. */ 401285998Sed error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp); 402285998Sed if (error != 0) { 403285998Sed if (error == EINVAL) 404285998Sed return (ENOTDIR); 405285998Sed return (error); 406285998Sed } 407285998Sed if ((fp->f_flag & FREAD) == 0) { 408285998Sed fdrop(fp, td); 409285998Sed return (EBADF); 410285998Sed } 411285998Sed 412285998Sed /* 413285998Sed * Call VOP_READDIR() and convert resulting data until the user 414285998Sed * provided buffer is filled. 415285998Sed */ 416285998Sed readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 417285998Sed offset = uap->cookie; 418285998Sed vp = fp->f_vnode; 419285998Sed while (uio.uio_resid > 0) { 420285998Sed struct iovec readiov = { 421285998Sed .iov_base = readbuf, 422285998Sed .iov_len = MAXBSIZE 423285998Sed }; 424285998Sed struct uio readuio = { 425285998Sed .uio_iov = &readiov, 426285998Sed .uio_iovcnt = 1, 427285998Sed .uio_rw = UIO_READ, 428285998Sed .uio_segflg = UIO_SYSSPACE, 429285998Sed .uio_td = td, 430285998Sed .uio_resid = MAXBSIZE, 431285998Sed .uio_offset = offset 432285998Sed }; 433285998Sed struct dirent *bde; 434285998Sed unsigned long *cookies, *cookie; 435285998Sed size_t readbuflen; 436285998Sed int eof, ncookies; 437285998Sed 438285998Sed /* Validate file type. */ 439285998Sed vn_lock(vp, LK_SHARED | LK_RETRY); 440285998Sed if (vp->v_type != VDIR) { 441285998Sed VOP_UNLOCK(vp, 0); 442285998Sed error = ENOTDIR; 443285998Sed goto done; 444285998Sed } 445285998Sed#ifdef MAC 446285998Sed error = mac_vnode_check_readdir(td->td_ucred, vp); 447285998Sed if (error != 0) { 448285998Sed VOP_UNLOCK(vp, 0); 449285998Sed goto done; 450285998Sed } 451285998Sed#endif /* MAC */ 452285998Sed 453285998Sed /* Read new directory entries. */ 454285998Sed cookies = NULL; 455285998Sed ncookies = 0; 456285998Sed error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof, 457285998Sed &ncookies, &cookies); 458285998Sed VOP_UNLOCK(vp, 0); 459285998Sed if (error != 0) 460285998Sed goto done; 461285998Sed 462285998Sed /* Convert entries to CloudABI's format. */ 463285998Sed readbuflen = MAXBSIZE - readuio.uio_resid; 464285998Sed bde = readbuf; 465285998Sed cookie = cookies; 466285998Sed while (readbuflen >= offsetof(struct dirent, d_name) && 467285998Sed uio.uio_resid > 0 && ncookies > 0) { 468285998Sed /* Ensure that the returned offset always increases. */ 469285998Sed if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 && 470285998Sed *cookie > offset) { 471285998Sed error = write_dirent(bde, *cookie, &uio); 472285998Sed if (error != 0) { 473285998Sed free(cookies, M_TEMP); 474285998Sed goto done; 475285998Sed } 476285998Sed } 477285998Sed 478285998Sed if (offset < *cookie) 479285998Sed offset = *cookie; 480285998Sed ++cookie; 481285998Sed --ncookies; 482285998Sed readbuflen -= bde->d_reclen; 483285998Sed bde = (struct dirent *)((char *)bde + bde->d_reclen); 484285998Sed } 485285998Sed free(cookies, M_TEMP); 486285998Sed if (eof) 487285998Sed break; 488285998Sed } 489285998Sed 490285998Seddone: 491285998Sed fdrop(fp, td); 492285998Sed free(readbuf, M_TEMP); 493285998Sed if (error != 0) 494285998Sed return (error); 495285998Sed 496285998Sed /* Return number of bytes copied to userspace. */ 497316574Sed td->td_retval[0] = uap->buf_len - uio.uio_resid; 498285998Sed return (0); 499285307Sed} 500285307Sed 501285307Sedint 502285307Sedcloudabi_sys_file_readlink(struct thread *td, 503285307Sed struct cloudabi_sys_file_readlink_args *uap) 504285307Sed{ 505285834Sed char *path; 506285834Sed int error; 507285307Sed 508316574Sed error = copyin_path(uap->path, uap->path_len, &path); 509285834Sed if (error != 0) 510285834Sed return (error); 511285834Sed 512285834Sed error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 513316574Sed uap->buf, UIO_USERSPACE, uap->buf_len); 514285834Sed cloudabi_freestr(path); 515285834Sed return (error); 516285307Sed} 517285307Sed 518285307Sedint 519285307Sedcloudabi_sys_file_rename(struct thread *td, 520285307Sed struct cloudabi_sys_file_rename_args *uap) 521285307Sed{ 522285834Sed char *old, *new; 523285834Sed int error; 524285307Sed 525316574Sed error = copyin_path(uap->path1, uap->path1_len, &old); 526285834Sed if (error != 0) 527285834Sed return (error); 528316574Sed error = copyin_path(uap->path2, uap->path2_len, &new); 529285834Sed if (error != 0) { 530285834Sed cloudabi_freestr(old); 531285834Sed return (error); 532285834Sed } 533285834Sed 534316574Sed error = kern_renameat(td, uap->fd1, old, uap->fd2, new, 535285834Sed UIO_SYSSPACE); 536285834Sed cloudabi_freestr(old); 537285834Sed cloudabi_freestr(new); 538285834Sed return (error); 539285307Sed} 540285307Sed 541285930Sed/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 542285930Sedstatic void 543285930Sedconvert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 544285930Sed{ 545285930Sed cloudabi_filestat_t res = { 546285930Sed .st_dev = sb->st_dev, 547285930Sed .st_ino = sb->st_ino, 548285930Sed .st_nlink = sb->st_nlink, 549285930Sed .st_size = sb->st_size, 550285930Sed }; 551285930Sed 552285930Sed cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 553285930Sed cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 554285930Sed cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 555285930Sed *csb = res; 556285930Sed} 557285930Sed 558285307Sedint 559285307Sedcloudabi_sys_file_stat_fget(struct thread *td, 560285307Sed struct cloudabi_sys_file_stat_fget_args *uap) 561285307Sed{ 562285930Sed struct stat sb; 563285930Sed cloudabi_filestat_t csb; 564285930Sed struct file *fp; 565285930Sed cap_rights_t rights; 566285930Sed cloudabi_filetype_t filetype; 567285930Sed int error; 568285307Sed 569285930Sed /* Fetch file descriptor attributes. */ 570285930Sed error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); 571285930Sed if (error != 0) 572285930Sed return (error); 573285930Sed error = fo_stat(fp, &sb, td->td_ucred, td); 574285930Sed if (error != 0) { 575285930Sed fdrop(fp, td); 576285930Sed return (error); 577285930Sed } 578285930Sed filetype = cloudabi_convert_filetype(fp); 579285930Sed fdrop(fp, td); 580285930Sed 581285930Sed /* Convert attributes to CloudABI's format. */ 582285930Sed convert_stat(&sb, &csb); 583285930Sed csb.st_filetype = filetype; 584285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 585285307Sed} 586285307Sed 587285954Sed/* Converts timestamps to arguments to futimens() and utimensat(). */ 588285954Sedstatic void 589285954Sedconvert_utimens_arguments(const cloudabi_filestat_t *fs, 590285954Sed cloudabi_fsflags_t flags, struct timespec *ts) 591285954Sed{ 592285954Sed 593285954Sed if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) { 594285954Sed ts[0].tv_nsec = UTIME_NOW; 595285954Sed } else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) { 596285954Sed ts[0].tv_sec = fs->st_atim / 1000000000; 597285954Sed ts[0].tv_nsec = fs->st_atim % 1000000000; 598285954Sed } else { 599285954Sed ts[0].tv_nsec = UTIME_OMIT; 600285954Sed } 601285954Sed 602285954Sed if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) { 603285954Sed ts[1].tv_nsec = UTIME_NOW; 604285954Sed } else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) { 605285954Sed ts[1].tv_sec = fs->st_mtim / 1000000000; 606285954Sed ts[1].tv_nsec = fs->st_mtim % 1000000000; 607285954Sed } else { 608285954Sed ts[1].tv_nsec = UTIME_OMIT; 609285954Sed } 610285954Sed} 611285954Sed 612285307Sedint 613285307Sedcloudabi_sys_file_stat_fput(struct thread *td, 614285307Sed struct cloudabi_sys_file_stat_fput_args *uap) 615285307Sed{ 616285954Sed cloudabi_filestat_t fs; 617285954Sed struct timespec ts[2]; 618285954Sed int error; 619285307Sed 620285954Sed error = copyin(uap->buf, &fs, sizeof(fs)); 621285954Sed if (error != 0) 622285954Sed return (error); 623285954Sed 624285954Sed /* 625285954Sed * Only support truncation and timestamp modification separately 626285954Sed * for now, to prevent unnecessary code duplication. 627285954Sed */ 628285954Sed if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) { 629285954Sed /* Call into kern_ftruncate() for file truncation. */ 630285954Sed if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0) 631285954Sed return (EINVAL); 632285954Sed return (kern_ftruncate(td, uap->fd, fs.st_size)); 633285954Sed } else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM | 634285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 635285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) { 636285954Sed /* Call into kern_futimens() for timestamp modification. */ 637285954Sed if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 638285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 639285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 640285954Sed return (EINVAL); 641285954Sed convert_utimens_arguments(&fs, uap->flags, ts); 642285954Sed return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE)); 643285954Sed } 644285954Sed return (EINVAL); 645285307Sed} 646285307Sed 647285307Sedint 648285307Sedcloudabi_sys_file_stat_get(struct thread *td, 649285307Sed struct cloudabi_sys_file_stat_get_args *uap) 650285307Sed{ 651285930Sed struct stat sb; 652285930Sed cloudabi_filestat_t csb; 653285930Sed char *path; 654285930Sed int error; 655285307Sed 656316574Sed error = copyin_path(uap->path, uap->path_len, &path); 657285930Sed if (error != 0) 658285930Sed return (error); 659285930Sed 660285930Sed error = kern_statat(td, 661297247Sed (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 662297247Sed AT_SYMLINK_NOFOLLOW, uap->fd.fd, path, UIO_SYSSPACE, &sb, NULL); 663285930Sed cloudabi_freestr(path); 664285930Sed if (error != 0) 665285930Sed return (error); 666285930Sed 667285930Sed /* Convert results and return them. */ 668285930Sed convert_stat(&sb, &csb); 669285930Sed if (S_ISBLK(sb.st_mode)) 670285930Sed csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 671285930Sed else if (S_ISCHR(sb.st_mode)) 672285930Sed csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 673285930Sed else if (S_ISDIR(sb.st_mode)) 674285930Sed csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 675285930Sed else if (S_ISFIFO(sb.st_mode)) 676285930Sed csb.st_filetype = CLOUDABI_FILETYPE_FIFO; 677285930Sed else if (S_ISREG(sb.st_mode)) 678285930Sed csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 679285930Sed else if (S_ISSOCK(sb.st_mode)) { 680285930Sed /* Inaccurate, but the best that we can do. */ 681285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 682285930Sed } else if (S_ISLNK(sb.st_mode)) 683285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 684285930Sed else 685285930Sed csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 686285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 687285307Sed} 688285307Sed 689285307Sedint 690285307Sedcloudabi_sys_file_stat_put(struct thread *td, 691285307Sed struct cloudabi_sys_file_stat_put_args *uap) 692285307Sed{ 693285954Sed cloudabi_filestat_t fs; 694285954Sed struct timespec ts[2]; 695285954Sed char *path; 696285954Sed int error; 697285307Sed 698285954Sed /* 699285954Sed * Only support timestamp modification for now, as there is no 700285954Sed * truncateat(). 701285954Sed */ 702285954Sed if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 703285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 704285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 705285954Sed return (EINVAL); 706285954Sed 707285954Sed error = copyin(uap->buf, &fs, sizeof(fs)); 708285954Sed if (error != 0) 709285954Sed return (error); 710316574Sed error = copyin_path(uap->path, uap->path_len, &path); 711285954Sed if (error != 0) 712285954Sed return (error); 713285954Sed 714285954Sed convert_utimens_arguments(&fs, uap->flags, ts); 715297247Sed error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts, 716297247Sed UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 717285954Sed 0 : AT_SYMLINK_NOFOLLOW); 718285954Sed cloudabi_freestr(path); 719285954Sed return (error); 720285307Sed} 721285307Sed 722285307Sedint 723285307Sedcloudabi_sys_file_symlink(struct thread *td, 724285307Sed struct cloudabi_sys_file_symlink_args *uap) 725285307Sed{ 726285834Sed char *path1, *path2; 727285834Sed int error; 728285307Sed 729316574Sed error = copyin_path(uap->path1, uap->path1_len, &path1); 730285834Sed if (error != 0) 731285834Sed return (error); 732316574Sed error = copyin_path(uap->path2, uap->path2_len, &path2); 733285834Sed if (error != 0) { 734285834Sed cloudabi_freestr(path1); 735285834Sed return (error); 736285834Sed } 737285834Sed 738285834Sed error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 739285834Sed cloudabi_freestr(path1); 740285834Sed cloudabi_freestr(path2); 741285834Sed return (error); 742285307Sed} 743285307Sed 744285307Sedint 745285307Sedcloudabi_sys_file_unlink(struct thread *td, 746285307Sed struct cloudabi_sys_file_unlink_args *uap) 747285307Sed{ 748285834Sed char *path; 749285834Sed int error; 750285307Sed 751316574Sed error = copyin_path(uap->path, uap->path_len, &path); 752285834Sed if (error != 0) 753285834Sed return (error); 754285834Sed 755297247Sed if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) 756285834Sed error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 757285834Sed else 758285834Sed error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 759285834Sed cloudabi_freestr(path); 760285834Sed return (error); 761285307Sed} 762