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 324250 2017-10-04 07:35:01Z 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 default: 163285931Sed error = EINVAL; 164285931Sed break; 165285931Sed } 166285931Sed cloudabi_freestr(path); 167285931Sed return (error); 168285307Sed} 169285307Sed 170285307Sedint 171285307Sedcloudabi_sys_file_link(struct thread *td, 172285307Sed struct cloudabi_sys_file_link_args *uap) 173285307Sed{ 174285834Sed char *path1, *path2; 175285834Sed int error; 176285307Sed 177316574Sed error = copyin_path(uap->path1, uap->path1_len, &path1); 178285834Sed if (error != 0) 179285834Sed return (error); 180316574Sed error = copyin_path(uap->path2, uap->path2_len, &path2); 181285834Sed if (error != 0) { 182285834Sed cloudabi_freestr(path1); 183285834Sed return (error); 184285834Sed } 185285834Sed 186297247Sed error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2, 187297247Sed UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 188285834Sed FOLLOW : NOFOLLOW); 189285834Sed cloudabi_freestr(path1); 190285834Sed cloudabi_freestr(path2); 191285834Sed return (error); 192285307Sed} 193285307Sed 194285307Sedint 195285307Sedcloudabi_sys_file_open(struct thread *td, 196285307Sed struct cloudabi_sys_file_open_args *uap) 197285307Sed{ 198286359Sed cloudabi_fdstat_t fds; 199286359Sed cap_rights_t rights; 200286359Sed struct filecaps fcaps = {}; 201286359Sed struct nameidata nd; 202286359Sed struct file *fp; 203286359Sed struct vnode *vp; 204286359Sed char *path; 205286359Sed int error, fd, fflags; 206286359Sed bool read, write; 207285307Sed 208286359Sed error = copyin(uap->fds, &fds, sizeof(fds)); 209286359Sed if (error != 0) 210286359Sed return (error); 211286359Sed 212286359Sed /* All the requested rights should be set on the descriptor. */ 213286359Sed error = cloudabi_convert_rights( 214286359Sed fds.fs_rights_base | fds.fs_rights_inheriting, &rights); 215286359Sed if (error != 0) 216286359Sed return (error); 217286359Sed cap_rights_set(&rights, CAP_LOOKUP); 218286359Sed 219286359Sed /* Convert rights to corresponding access mode. */ 220286359Sed read = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_READ | 221286359Sed CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_MEM_MAP_EXEC)) != 0; 222286359Sed write = (fds.fs_rights_base & (CLOUDABI_RIGHT_FD_DATASYNC | 223286359Sed CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ALLOCATE | 224286359Sed CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE)) != 0; 225286633Sed fflags = write ? read ? FREAD | FWRITE : FWRITE : FREAD; 226286359Sed 227286359Sed /* Convert open flags. */ 228286359Sed if ((uap->oflags & CLOUDABI_O_CREAT) != 0) { 229286359Sed fflags |= O_CREAT; 230286359Sed cap_rights_set(&rights, CAP_CREATE); 231286359Sed } 232286359Sed if ((uap->oflags & CLOUDABI_O_DIRECTORY) != 0) 233286359Sed fflags |= O_DIRECTORY; 234286359Sed if ((uap->oflags & CLOUDABI_O_EXCL) != 0) 235286359Sed fflags |= O_EXCL; 236286359Sed if ((uap->oflags & CLOUDABI_O_TRUNC) != 0) { 237286359Sed fflags |= O_TRUNC; 238286359Sed cap_rights_set(&rights, CAP_FTRUNCATE); 239286359Sed } 240286359Sed if ((fds.fs_flags & CLOUDABI_FDFLAG_APPEND) != 0) 241286359Sed fflags |= O_APPEND; 242286359Sed if ((fds.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) != 0) 243286359Sed fflags |= O_NONBLOCK; 244286359Sed if ((fds.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC | 245286359Sed CLOUDABI_FDFLAG_RSYNC)) != 0) { 246286359Sed fflags |= O_SYNC; 247286359Sed cap_rights_set(&rights, CAP_FSYNC); 248286359Sed } 249297247Sed if ((uap->dirfd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0) 250286359Sed fflags |= O_NOFOLLOW; 251286359Sed if (write && (fflags & (O_APPEND | O_TRUNC)) == 0) 252286359Sed cap_rights_set(&rights, CAP_SEEK); 253286359Sed 254286359Sed /* Allocate new file descriptor. */ 255286359Sed error = falloc_noinstall(td, &fp); 256286359Sed if (error != 0) 257286359Sed return (error); 258286359Sed fp->f_flag = fflags & FMASK; 259286359Sed 260286359Sed /* Open path. */ 261316574Sed error = copyin_path(uap->path, uap->path_len, &path); 262286359Sed if (error != 0) { 263286359Sed fdrop(fp, td); 264286359Sed return (error); 265286359Sed } 266297247Sed NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->dirfd.fd, 267286359Sed &rights, td); 268286359Sed error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp); 269286359Sed cloudabi_freestr(path); 270286359Sed if (error != 0) { 271286359Sed /* Custom operations provided. */ 272286359Sed if (error == ENXIO && fp->f_ops != &badfileops) 273286359Sed goto success; 274286359Sed 275286359Sed /* 276286359Sed * POSIX compliance: return ELOOP in case openat() is 277286359Sed * called on a symbolic link and O_NOFOLLOW is set. 278286359Sed */ 279286359Sed if (error == EMLINK) 280286359Sed error = ELOOP; 281286359Sed fdrop(fp, td); 282286359Sed return (error); 283286359Sed } 284286359Sed NDFREE(&nd, NDF_ONLY_PNBUF); 285286359Sed filecaps_free(&nd.ni_filecaps); 286286359Sed fp->f_vnode = vp = nd.ni_vp; 287286359Sed 288286359Sed /* Install vnode operations if no custom operations are provided. */ 289286359Sed if (fp->f_ops == &badfileops) { 290286359Sed fp->f_seqcount = 1; 291286359Sed finit(fp, (fflags & FMASK) | (fp->f_flag & FHASLOCK), 292286359Sed DTYPE_VNODE, vp, &vnops); 293286359Sed } 294286359Sed VOP_UNLOCK(vp, 0); 295286359Sed 296286359Sed /* Truncate file. */ 297286359Sed if (fflags & O_TRUNC) { 298286359Sed error = fo_truncate(fp, 0, td->td_ucred, td); 299286359Sed if (error != 0) { 300286359Sed fdrop(fp, td); 301286359Sed return (error); 302286359Sed } 303286359Sed } 304286359Sed 305286359Sedsuccess: 306286359Sed /* Determine which Capsicum rights to set on the file descriptor. */ 307286359Sed cloudabi_remove_conflicting_rights(cloudabi_convert_filetype(fp), 308286359Sed &fds.fs_rights_base, &fds.fs_rights_inheriting); 309286359Sed cloudabi_convert_rights(fds.fs_rights_base | fds.fs_rights_inheriting, 310286359Sed &fcaps.fc_rights); 311286359Sed if (cap_rights_is_set(&fcaps.fc_rights)) 312286359Sed fcaps.fc_fcntls = CAP_FCNTL_SETFL; 313286359Sed 314286359Sed error = finstall(td, fp, &fd, fflags, &fcaps); 315286359Sed fdrop(fp, td); 316286359Sed if (error != 0) 317286359Sed return (error); 318286359Sed td->td_retval[0] = fd; 319286359Sed return (0); 320285307Sed} 321285307Sed 322285998Sed/* Converts a FreeBSD directory entry structure and writes it to userspace. */ 323285998Sedstatic int 324285998Sedwrite_dirent(struct dirent *bde, cloudabi_dircookie_t cookie, struct uio *uio) 325285998Sed{ 326285998Sed cloudabi_dirent_t cde = { 327285998Sed .d_next = cookie, 328285998Sed .d_ino = bde->d_fileno, 329285998Sed .d_namlen = bde->d_namlen, 330285998Sed }; 331285998Sed size_t len; 332285998Sed int error; 333285998Sed 334285998Sed /* Convert file type. */ 335285998Sed switch (bde->d_type) { 336285998Sed case DT_BLK: 337285998Sed cde.d_type = CLOUDABI_FILETYPE_BLOCK_DEVICE; 338285998Sed break; 339285998Sed case DT_CHR: 340285998Sed cde.d_type = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 341285998Sed break; 342285998Sed case DT_DIR: 343285998Sed cde.d_type = CLOUDABI_FILETYPE_DIRECTORY; 344285998Sed break; 345285998Sed case DT_FIFO: 346324250Sed cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM; 347285998Sed break; 348285998Sed case DT_LNK: 349285998Sed cde.d_type = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 350285998Sed break; 351285998Sed case DT_REG: 352285998Sed cde.d_type = CLOUDABI_FILETYPE_REGULAR_FILE; 353285998Sed break; 354285998Sed case DT_SOCK: 355285998Sed /* The exact socket type cannot be derived. */ 356285998Sed cde.d_type = CLOUDABI_FILETYPE_SOCKET_STREAM; 357285998Sed break; 358285998Sed default: 359285998Sed cde.d_type = CLOUDABI_FILETYPE_UNKNOWN; 360285998Sed break; 361285998Sed } 362285998Sed 363285998Sed /* Write directory entry structure. */ 364285998Sed len = sizeof(cde) < uio->uio_resid ? sizeof(cde) : uio->uio_resid; 365285998Sed error = uiomove(&cde, len, uio); 366285998Sed if (error != 0) 367285998Sed return (error); 368285998Sed 369285998Sed /* Write filename. */ 370285998Sed len = bde->d_namlen < uio->uio_resid ? bde->d_namlen : uio->uio_resid; 371285998Sed return (uiomove(bde->d_name, len, uio)); 372285998Sed} 373285998Sed 374285307Sedint 375285307Sedcloudabi_sys_file_readdir(struct thread *td, 376285307Sed struct cloudabi_sys_file_readdir_args *uap) 377285307Sed{ 378285998Sed struct iovec iov = { 379285998Sed .iov_base = uap->buf, 380316574Sed .iov_len = uap->buf_len 381285998Sed }; 382285998Sed struct uio uio = { 383285998Sed .uio_iov = &iov, 384285998Sed .uio_iovcnt = 1, 385285998Sed .uio_resid = iov.iov_len, 386285998Sed .uio_segflg = UIO_USERSPACE, 387285998Sed .uio_rw = UIO_READ, 388285998Sed .uio_td = td 389285998Sed }; 390285998Sed struct file *fp; 391285998Sed struct vnode *vp; 392285998Sed void *readbuf; 393285998Sed cap_rights_t rights; 394285998Sed cloudabi_dircookie_t offset; 395285998Sed int error; 396285307Sed 397285998Sed /* Obtain directory vnode. */ 398285998Sed error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_READ), &fp); 399285998Sed if (error != 0) { 400285998Sed if (error == EINVAL) 401285998Sed return (ENOTDIR); 402285998Sed return (error); 403285998Sed } 404285998Sed if ((fp->f_flag & FREAD) == 0) { 405285998Sed fdrop(fp, td); 406285998Sed return (EBADF); 407285998Sed } 408285998Sed 409285998Sed /* 410285998Sed * Call VOP_READDIR() and convert resulting data until the user 411285998Sed * provided buffer is filled. 412285998Sed */ 413285998Sed readbuf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 414285998Sed offset = uap->cookie; 415285998Sed vp = fp->f_vnode; 416285998Sed while (uio.uio_resid > 0) { 417285998Sed struct iovec readiov = { 418285998Sed .iov_base = readbuf, 419285998Sed .iov_len = MAXBSIZE 420285998Sed }; 421285998Sed struct uio readuio = { 422285998Sed .uio_iov = &readiov, 423285998Sed .uio_iovcnt = 1, 424285998Sed .uio_rw = UIO_READ, 425285998Sed .uio_segflg = UIO_SYSSPACE, 426285998Sed .uio_td = td, 427285998Sed .uio_resid = MAXBSIZE, 428285998Sed .uio_offset = offset 429285998Sed }; 430285998Sed struct dirent *bde; 431285998Sed unsigned long *cookies, *cookie; 432285998Sed size_t readbuflen; 433285998Sed int eof, ncookies; 434285998Sed 435285998Sed /* Validate file type. */ 436285998Sed vn_lock(vp, LK_SHARED | LK_RETRY); 437285998Sed if (vp->v_type != VDIR) { 438285998Sed VOP_UNLOCK(vp, 0); 439285998Sed error = ENOTDIR; 440285998Sed goto done; 441285998Sed } 442285998Sed#ifdef MAC 443285998Sed error = mac_vnode_check_readdir(td->td_ucred, vp); 444285998Sed if (error != 0) { 445285998Sed VOP_UNLOCK(vp, 0); 446285998Sed goto done; 447285998Sed } 448285998Sed#endif /* MAC */ 449285998Sed 450285998Sed /* Read new directory entries. */ 451285998Sed cookies = NULL; 452285998Sed ncookies = 0; 453285998Sed error = VOP_READDIR(vp, &readuio, fp->f_cred, &eof, 454285998Sed &ncookies, &cookies); 455285998Sed VOP_UNLOCK(vp, 0); 456285998Sed if (error != 0) 457285998Sed goto done; 458285998Sed 459285998Sed /* Convert entries to CloudABI's format. */ 460285998Sed readbuflen = MAXBSIZE - readuio.uio_resid; 461285998Sed bde = readbuf; 462285998Sed cookie = cookies; 463285998Sed while (readbuflen >= offsetof(struct dirent, d_name) && 464285998Sed uio.uio_resid > 0 && ncookies > 0) { 465285998Sed /* Ensure that the returned offset always increases. */ 466285998Sed if (readbuflen >= bde->d_reclen && bde->d_fileno != 0 && 467285998Sed *cookie > offset) { 468285998Sed error = write_dirent(bde, *cookie, &uio); 469285998Sed if (error != 0) { 470285998Sed free(cookies, M_TEMP); 471285998Sed goto done; 472285998Sed } 473285998Sed } 474285998Sed 475285998Sed if (offset < *cookie) 476285998Sed offset = *cookie; 477285998Sed ++cookie; 478285998Sed --ncookies; 479285998Sed readbuflen -= bde->d_reclen; 480285998Sed bde = (struct dirent *)((char *)bde + bde->d_reclen); 481285998Sed } 482285998Sed free(cookies, M_TEMP); 483285998Sed if (eof) 484285998Sed break; 485285998Sed } 486285998Sed 487285998Seddone: 488285998Sed fdrop(fp, td); 489285998Sed free(readbuf, M_TEMP); 490285998Sed if (error != 0) 491285998Sed return (error); 492285998Sed 493285998Sed /* Return number of bytes copied to userspace. */ 494316574Sed td->td_retval[0] = uap->buf_len - uio.uio_resid; 495285998Sed return (0); 496285307Sed} 497285307Sed 498285307Sedint 499285307Sedcloudabi_sys_file_readlink(struct thread *td, 500285307Sed struct cloudabi_sys_file_readlink_args *uap) 501285307Sed{ 502285834Sed char *path; 503285834Sed int error; 504285307Sed 505316574Sed error = copyin_path(uap->path, uap->path_len, &path); 506285834Sed if (error != 0) 507285834Sed return (error); 508285834Sed 509285834Sed error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 510316574Sed uap->buf, UIO_USERSPACE, uap->buf_len); 511285834Sed cloudabi_freestr(path); 512285834Sed return (error); 513285307Sed} 514285307Sed 515285307Sedint 516285307Sedcloudabi_sys_file_rename(struct thread *td, 517285307Sed struct cloudabi_sys_file_rename_args *uap) 518285307Sed{ 519285834Sed char *old, *new; 520285834Sed int error; 521285307Sed 522316574Sed error = copyin_path(uap->path1, uap->path1_len, &old); 523285834Sed if (error != 0) 524285834Sed return (error); 525316574Sed error = copyin_path(uap->path2, uap->path2_len, &new); 526285834Sed if (error != 0) { 527285834Sed cloudabi_freestr(old); 528285834Sed return (error); 529285834Sed } 530285834Sed 531316574Sed error = kern_renameat(td, uap->fd1, old, uap->fd2, new, 532285834Sed UIO_SYSSPACE); 533285834Sed cloudabi_freestr(old); 534285834Sed cloudabi_freestr(new); 535285834Sed return (error); 536285307Sed} 537285307Sed 538285930Sed/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 539285930Sedstatic void 540285930Sedconvert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 541285930Sed{ 542285930Sed cloudabi_filestat_t res = { 543285930Sed .st_dev = sb->st_dev, 544285930Sed .st_ino = sb->st_ino, 545285930Sed .st_nlink = sb->st_nlink, 546285930Sed .st_size = sb->st_size, 547285930Sed }; 548285930Sed 549285930Sed cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 550285930Sed cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 551285930Sed cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 552285930Sed *csb = res; 553285930Sed} 554285930Sed 555285307Sedint 556285307Sedcloudabi_sys_file_stat_fget(struct thread *td, 557285307Sed struct cloudabi_sys_file_stat_fget_args *uap) 558285307Sed{ 559285930Sed struct stat sb; 560285930Sed cloudabi_filestat_t csb; 561285930Sed struct file *fp; 562285930Sed cap_rights_t rights; 563285930Sed cloudabi_filetype_t filetype; 564285930Sed int error; 565285307Sed 566285930Sed /* Fetch file descriptor attributes. */ 567285930Sed error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); 568285930Sed if (error != 0) 569285930Sed return (error); 570285930Sed error = fo_stat(fp, &sb, td->td_ucred, td); 571285930Sed if (error != 0) { 572285930Sed fdrop(fp, td); 573285930Sed return (error); 574285930Sed } 575285930Sed filetype = cloudabi_convert_filetype(fp); 576285930Sed fdrop(fp, td); 577285930Sed 578285930Sed /* Convert attributes to CloudABI's format. */ 579285930Sed convert_stat(&sb, &csb); 580285930Sed csb.st_filetype = filetype; 581285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 582285307Sed} 583285307Sed 584285954Sed/* Converts timestamps to arguments to futimens() and utimensat(). */ 585285954Sedstatic void 586285954Sedconvert_utimens_arguments(const cloudabi_filestat_t *fs, 587285954Sed cloudabi_fsflags_t flags, struct timespec *ts) 588285954Sed{ 589285954Sed 590285954Sed if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) { 591285954Sed ts[0].tv_nsec = UTIME_NOW; 592285954Sed } else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) { 593285954Sed ts[0].tv_sec = fs->st_atim / 1000000000; 594285954Sed ts[0].tv_nsec = fs->st_atim % 1000000000; 595285954Sed } else { 596285954Sed ts[0].tv_nsec = UTIME_OMIT; 597285954Sed } 598285954Sed 599285954Sed if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) { 600285954Sed ts[1].tv_nsec = UTIME_NOW; 601285954Sed } else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) { 602285954Sed ts[1].tv_sec = fs->st_mtim / 1000000000; 603285954Sed ts[1].tv_nsec = fs->st_mtim % 1000000000; 604285954Sed } else { 605285954Sed ts[1].tv_nsec = UTIME_OMIT; 606285954Sed } 607285954Sed} 608285954Sed 609285307Sedint 610285307Sedcloudabi_sys_file_stat_fput(struct thread *td, 611285307Sed struct cloudabi_sys_file_stat_fput_args *uap) 612285307Sed{ 613285954Sed cloudabi_filestat_t fs; 614285954Sed struct timespec ts[2]; 615285954Sed int error; 616285307Sed 617285954Sed error = copyin(uap->buf, &fs, sizeof(fs)); 618285954Sed if (error != 0) 619285954Sed return (error); 620285954Sed 621285954Sed /* 622285954Sed * Only support truncation and timestamp modification separately 623285954Sed * for now, to prevent unnecessary code duplication. 624285954Sed */ 625285954Sed if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) { 626285954Sed /* Call into kern_ftruncate() for file truncation. */ 627285954Sed if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0) 628285954Sed return (EINVAL); 629285954Sed return (kern_ftruncate(td, uap->fd, fs.st_size)); 630285954Sed } else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM | 631285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 632285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) { 633285954Sed /* Call into kern_futimens() for timestamp modification. */ 634285954Sed if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 635285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 636285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 637285954Sed return (EINVAL); 638285954Sed convert_utimens_arguments(&fs, uap->flags, ts); 639285954Sed return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE)); 640285954Sed } 641285954Sed return (EINVAL); 642285307Sed} 643285307Sed 644285307Sedint 645285307Sedcloudabi_sys_file_stat_get(struct thread *td, 646285307Sed struct cloudabi_sys_file_stat_get_args *uap) 647285307Sed{ 648285930Sed struct stat sb; 649285930Sed cloudabi_filestat_t csb; 650285930Sed char *path; 651285930Sed int error; 652285307Sed 653316574Sed error = copyin_path(uap->path, uap->path_len, &path); 654285930Sed if (error != 0) 655285930Sed return (error); 656285930Sed 657285930Sed error = kern_statat(td, 658297247Sed (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 659297247Sed AT_SYMLINK_NOFOLLOW, uap->fd.fd, path, UIO_SYSSPACE, &sb, NULL); 660285930Sed cloudabi_freestr(path); 661285930Sed if (error != 0) 662285930Sed return (error); 663285930Sed 664285930Sed /* Convert results and return them. */ 665285930Sed convert_stat(&sb, &csb); 666285930Sed if (S_ISBLK(sb.st_mode)) 667285930Sed csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 668285930Sed else if (S_ISCHR(sb.st_mode)) 669285930Sed csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 670285930Sed else if (S_ISDIR(sb.st_mode)) 671285930Sed csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 672285930Sed else if (S_ISFIFO(sb.st_mode)) 673324250Sed csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 674285930Sed else if (S_ISREG(sb.st_mode)) 675285930Sed csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 676285930Sed else if (S_ISSOCK(sb.st_mode)) { 677285930Sed /* Inaccurate, but the best that we can do. */ 678285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 679285930Sed } else if (S_ISLNK(sb.st_mode)) 680285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 681285930Sed else 682285930Sed csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 683285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 684285307Sed} 685285307Sed 686285307Sedint 687285307Sedcloudabi_sys_file_stat_put(struct thread *td, 688285307Sed struct cloudabi_sys_file_stat_put_args *uap) 689285307Sed{ 690285954Sed cloudabi_filestat_t fs; 691285954Sed struct timespec ts[2]; 692285954Sed char *path; 693285954Sed int error; 694285307Sed 695285954Sed /* 696285954Sed * Only support timestamp modification for now, as there is no 697285954Sed * truncateat(). 698285954Sed */ 699285954Sed if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | 700285954Sed CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | 701285954Sed CLOUDABI_FILESTAT_MTIM_NOW)) != 0) 702285954Sed return (EINVAL); 703285954Sed 704285954Sed error = copyin(uap->buf, &fs, sizeof(fs)); 705285954Sed if (error != 0) 706285954Sed return (error); 707316574Sed error = copyin_path(uap->path, uap->path_len, &path); 708285954Sed if (error != 0) 709285954Sed return (error); 710285954Sed 711285954Sed convert_utimens_arguments(&fs, uap->flags, ts); 712297247Sed error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts, 713297247Sed UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 714285954Sed 0 : AT_SYMLINK_NOFOLLOW); 715285954Sed cloudabi_freestr(path); 716285954Sed return (error); 717285307Sed} 718285307Sed 719285307Sedint 720285307Sedcloudabi_sys_file_symlink(struct thread *td, 721285307Sed struct cloudabi_sys_file_symlink_args *uap) 722285307Sed{ 723285834Sed char *path1, *path2; 724285834Sed int error; 725285307Sed 726316574Sed error = copyin_path(uap->path1, uap->path1_len, &path1); 727285834Sed if (error != 0) 728285834Sed return (error); 729316574Sed error = copyin_path(uap->path2, uap->path2_len, &path2); 730285834Sed if (error != 0) { 731285834Sed cloudabi_freestr(path1); 732285834Sed return (error); 733285834Sed } 734285834Sed 735285834Sed error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 736285834Sed cloudabi_freestr(path1); 737285834Sed cloudabi_freestr(path2); 738285834Sed return (error); 739285307Sed} 740285307Sed 741285307Sedint 742285307Sedcloudabi_sys_file_unlink(struct thread *td, 743285307Sed struct cloudabi_sys_file_unlink_args *uap) 744285307Sed{ 745285834Sed char *path; 746285834Sed int error; 747285307Sed 748316574Sed error = copyin_path(uap->path, uap->path_len, &path); 749285834Sed if (error != 0) 750285834Sed return (error); 751285834Sed 752297247Sed if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) 753285834Sed error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 754285834Sed else 755285834Sed error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 756285834Sed cloudabi_freestr(path); 757285834Sed return (error); 758285307Sed} 759