cloudabi_file.c revision 285931
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 285931 2015-07-28 06:50:47Z ed $"); 28285307Sed 29285596Sed#include <sys/param.h> 30285930Sed#include <sys/capsicum.h> 31285596Sed#include <sys/fcntl.h> 32285834Sed#include <sys/kernel.h> 33285834Sed#include <sys/malloc.h> 34285834Sed#include <sys/namei.h> 35285930Sed#include <sys/proc.h> 36285930Sed#include <sys/stat.h> 37285596Sed#include <sys/syscallsubr.h> 38285596Sed 39285307Sed#include <compat/cloudabi/cloudabi_proto.h> 40285596Sed#include <compat/cloudabi/cloudabi_syscalldefs.h> 41285930Sed#include <compat/cloudabi/cloudabi_util.h> 42285307Sed 43285834Sedstatic MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); 44285834Sed 45285834Sed/* 46285834Sed * Copying pathnames from userspace to kernelspace. 47285834Sed * 48285834Sed * Unlike most operating systems, CloudABI doesn't use null-terminated 49285834Sed * pathname strings. Processes always pass pathnames to the kernel by 50285834Sed * providing a base pointer and a length. This has a couple of reasons: 51285834Sed * 52285834Sed * - It makes it easier to use CloudABI in combination with programming 53285834Sed * languages other than C, that may use non-null terminated strings. 54285834Sed * - It allows for calling system calls on individual components of the 55285834Sed * pathname without modifying the input string. 56285834Sed * 57285834Sed * The function below copies in pathname strings and null-terminates it. 58285834Sed * It also ensure that the string itself does not contain any null 59285834Sed * bytes. 60285834Sed * 61285834Sed * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass 62285834Sed * in unterminated pathname strings, so we can do away with 63285834Sed * the copying. 64285834Sed */ 65285834Sed 66285834Sedstatic int 67285834Sedcopyin_path(const char *uaddr, size_t len, char **result) 68285834Sed{ 69285834Sed char *buf; 70285834Sed int error; 71285834Sed 72285834Sed if (len >= PATH_MAX) 73285834Sed return (ENAMETOOLONG); 74285834Sed buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK); 75285834Sed error = copyin(uaddr, buf, len); 76285834Sed if (error != 0) { 77285834Sed free(buf, M_CLOUDABI_PATH); 78285834Sed return (error); 79285834Sed } 80285834Sed if (memchr(buf, '\0', len) != NULL) { 81285834Sed free(buf, M_CLOUDABI_PATH); 82285834Sed return (EINVAL); 83285834Sed } 84285834Sed buf[len] = '\0'; 85285834Sed *result = buf; 86285834Sed return (0); 87285834Sed} 88285834Sed 89285834Sedstatic void 90285834Sedcloudabi_freestr(char *buf) 91285834Sed{ 92285834Sed 93285834Sed free(buf, M_CLOUDABI_PATH); 94285834Sed} 95285834Sed 96285307Sedint 97285307Sedcloudabi_sys_file_advise(struct thread *td, 98285307Sed struct cloudabi_sys_file_advise_args *uap) 99285307Sed{ 100285596Sed int advice; 101285307Sed 102285596Sed switch (uap->advice) { 103285596Sed case CLOUDABI_ADVICE_DONTNEED: 104285596Sed advice = POSIX_FADV_DONTNEED; 105285596Sed break; 106285596Sed case CLOUDABI_ADVICE_NOREUSE: 107285596Sed advice = POSIX_FADV_NOREUSE; 108285596Sed break; 109285596Sed case CLOUDABI_ADVICE_NORMAL: 110285596Sed advice = POSIX_FADV_NORMAL; 111285596Sed break; 112285596Sed case CLOUDABI_ADVICE_RANDOM: 113285596Sed advice = POSIX_FADV_RANDOM; 114285596Sed break; 115285596Sed case CLOUDABI_ADVICE_SEQUENTIAL: 116285596Sed advice = POSIX_FADV_SEQUENTIAL; 117285596Sed break; 118285596Sed case CLOUDABI_ADVICE_WILLNEED: 119285596Sed advice = POSIX_FADV_WILLNEED; 120285596Sed break; 121285596Sed default: 122285596Sed return (EINVAL); 123285596Sed } 124285596Sed 125285596Sed return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice)); 126285307Sed} 127285307Sed 128285307Sedint 129285307Sedcloudabi_sys_file_allocate(struct thread *td, 130285307Sed struct cloudabi_sys_file_allocate_args *uap) 131285307Sed{ 132285307Sed 133285596Sed return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len)); 134285307Sed} 135285307Sed 136285307Sedint 137285307Sedcloudabi_sys_file_create(struct thread *td, 138285307Sed struct cloudabi_sys_file_create_args *uap) 139285307Sed{ 140285931Sed char *path; 141285931Sed int error; 142285307Sed 143285931Sed error = copyin_path(uap->path, uap->pathlen, &path); 144285931Sed if (error != 0) 145285931Sed return (error); 146285931Sed 147285931Sed /* 148285931Sed * CloudABI processes cannot interact with UNIX credentials and 149285931Sed * permissions. Depend on the umask that is set prior to 150285931Sed * execution to restrict the file permissions. 151285931Sed */ 152285931Sed switch (uap->type) { 153285931Sed case CLOUDABI_FILETYPE_DIRECTORY: 154285931Sed error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777); 155285931Sed break; 156285931Sed case CLOUDABI_FILETYPE_FIFO: 157285931Sed error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666); 158285931Sed break; 159285931Sed default: 160285931Sed error = EINVAL; 161285931Sed break; 162285931Sed } 163285931Sed cloudabi_freestr(path); 164285931Sed return (error); 165285307Sed} 166285307Sed 167285307Sedint 168285307Sedcloudabi_sys_file_link(struct thread *td, 169285307Sed struct cloudabi_sys_file_link_args *uap) 170285307Sed{ 171285834Sed char *path1, *path2; 172285834Sed int error; 173285307Sed 174285834Sed error = copyin_path(uap->path1, uap->path1len, &path1); 175285834Sed if (error != 0) 176285834Sed return (error); 177285834Sed error = copyin_path(uap->path2, uap->path2len, &path2); 178285834Sed if (error != 0) { 179285834Sed cloudabi_freestr(path1); 180285834Sed return (error); 181285834Sed } 182285834Sed 183285834Sed error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2, 184285834Sed UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 185285834Sed FOLLOW : NOFOLLOW); 186285834Sed cloudabi_freestr(path1); 187285834Sed cloudabi_freestr(path2); 188285834Sed return (error); 189285307Sed} 190285307Sed 191285307Sedint 192285307Sedcloudabi_sys_file_open(struct thread *td, 193285307Sed struct cloudabi_sys_file_open_args *uap) 194285307Sed{ 195285307Sed 196285307Sed /* Not implemented. */ 197285307Sed return (ENOSYS); 198285307Sed} 199285307Sed 200285307Sedint 201285307Sedcloudabi_sys_file_readdir(struct thread *td, 202285307Sed struct cloudabi_sys_file_readdir_args *uap) 203285307Sed{ 204285307Sed 205285307Sed /* Not implemented. */ 206285307Sed return (ENOSYS); 207285307Sed} 208285307Sed 209285307Sedint 210285307Sedcloudabi_sys_file_readlink(struct thread *td, 211285307Sed struct cloudabi_sys_file_readlink_args *uap) 212285307Sed{ 213285834Sed char *path; 214285834Sed int error; 215285307Sed 216285834Sed error = copyin_path(uap->path, uap->pathlen, &path); 217285834Sed if (error != 0) 218285834Sed return (error); 219285834Sed 220285834Sed error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 221285834Sed uap->buf, UIO_USERSPACE, uap->bufsize); 222285834Sed cloudabi_freestr(path); 223285834Sed return (error); 224285307Sed} 225285307Sed 226285307Sedint 227285307Sedcloudabi_sys_file_rename(struct thread *td, 228285307Sed struct cloudabi_sys_file_rename_args *uap) 229285307Sed{ 230285834Sed char *old, *new; 231285834Sed int error; 232285307Sed 233285834Sed error = copyin_path(uap->old, uap->oldlen, &old); 234285834Sed if (error != 0) 235285834Sed return (error); 236285834Sed error = copyin_path(uap->new, uap->newlen, &new); 237285834Sed if (error != 0) { 238285834Sed cloudabi_freestr(old); 239285834Sed return (error); 240285834Sed } 241285834Sed 242285834Sed error = kern_renameat(td, uap->oldfd, old, uap->newfd, new, 243285834Sed UIO_SYSSPACE); 244285834Sed cloudabi_freestr(old); 245285834Sed cloudabi_freestr(new); 246285834Sed return (error); 247285307Sed} 248285307Sed 249285930Sed/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 250285930Sedstatic void 251285930Sedconvert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 252285930Sed{ 253285930Sed cloudabi_filestat_t res = { 254285930Sed .st_dev = sb->st_dev, 255285930Sed .st_ino = sb->st_ino, 256285930Sed .st_nlink = sb->st_nlink, 257285930Sed .st_size = sb->st_size, 258285930Sed }; 259285930Sed 260285930Sed cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 261285930Sed cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 262285930Sed cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 263285930Sed *csb = res; 264285930Sed} 265285930Sed 266285307Sedint 267285307Sedcloudabi_sys_file_stat_fget(struct thread *td, 268285307Sed struct cloudabi_sys_file_stat_fget_args *uap) 269285307Sed{ 270285930Sed struct stat sb; 271285930Sed cloudabi_filestat_t csb; 272285930Sed struct file *fp; 273285930Sed cap_rights_t rights; 274285930Sed cloudabi_filetype_t filetype; 275285930Sed int error; 276285307Sed 277285930Sed /* Fetch file descriptor attributes. */ 278285930Sed error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); 279285930Sed if (error != 0) 280285930Sed return (error); 281285930Sed error = fo_stat(fp, &sb, td->td_ucred, td); 282285930Sed if (error != 0) { 283285930Sed fdrop(fp, td); 284285930Sed return (error); 285285930Sed } 286285930Sed filetype = cloudabi_convert_filetype(fp); 287285930Sed fdrop(fp, td); 288285930Sed 289285930Sed /* Convert attributes to CloudABI's format. */ 290285930Sed convert_stat(&sb, &csb); 291285930Sed csb.st_filetype = filetype; 292285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 293285307Sed} 294285307Sed 295285307Sedint 296285307Sedcloudabi_sys_file_stat_fput(struct thread *td, 297285307Sed struct cloudabi_sys_file_stat_fput_args *uap) 298285307Sed{ 299285307Sed 300285307Sed /* Not implemented. */ 301285307Sed return (ENOSYS); 302285307Sed} 303285307Sed 304285307Sedint 305285307Sedcloudabi_sys_file_stat_get(struct thread *td, 306285307Sed struct cloudabi_sys_file_stat_get_args *uap) 307285307Sed{ 308285930Sed struct stat sb; 309285930Sed cloudabi_filestat_t csb; 310285930Sed char *path; 311285930Sed int error; 312285307Sed 313285930Sed error = copyin_path(uap->path, uap->pathlen, &path); 314285930Sed if (error != 0) 315285930Sed return (error); 316285930Sed 317285930Sed error = kern_statat(td, 318285930Sed (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 319285930Sed AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL); 320285930Sed cloudabi_freestr(path); 321285930Sed if (error != 0) 322285930Sed return (error); 323285930Sed 324285930Sed /* Convert results and return them. */ 325285930Sed convert_stat(&sb, &csb); 326285930Sed if (S_ISBLK(sb.st_mode)) 327285930Sed csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 328285930Sed else if (S_ISCHR(sb.st_mode)) 329285930Sed csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 330285930Sed else if (S_ISDIR(sb.st_mode)) 331285930Sed csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 332285930Sed else if (S_ISFIFO(sb.st_mode)) 333285930Sed csb.st_filetype = CLOUDABI_FILETYPE_FIFO; 334285930Sed else if (S_ISREG(sb.st_mode)) 335285930Sed csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 336285930Sed else if (S_ISSOCK(sb.st_mode)) { 337285930Sed /* Inaccurate, but the best that we can do. */ 338285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 339285930Sed } else if (S_ISLNK(sb.st_mode)) 340285930Sed csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 341285930Sed else 342285930Sed csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 343285930Sed return (copyout(&csb, uap->buf, sizeof(csb))); 344285307Sed} 345285307Sed 346285307Sedint 347285307Sedcloudabi_sys_file_stat_put(struct thread *td, 348285307Sed struct cloudabi_sys_file_stat_put_args *uap) 349285307Sed{ 350285307Sed 351285307Sed /* Not implemented. */ 352285307Sed return (ENOSYS); 353285307Sed} 354285307Sed 355285307Sedint 356285307Sedcloudabi_sys_file_symlink(struct thread *td, 357285307Sed struct cloudabi_sys_file_symlink_args *uap) 358285307Sed{ 359285834Sed char *path1, *path2; 360285834Sed int error; 361285307Sed 362285834Sed error = copyin_path(uap->path1, uap->path1len, &path1); 363285834Sed if (error != 0) 364285834Sed return (error); 365285834Sed error = copyin_path(uap->path2, uap->path2len, &path2); 366285834Sed if (error != 0) { 367285834Sed cloudabi_freestr(path1); 368285834Sed return (error); 369285834Sed } 370285834Sed 371285834Sed error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 372285834Sed cloudabi_freestr(path1); 373285834Sed cloudabi_freestr(path2); 374285834Sed return (error); 375285307Sed} 376285307Sed 377285307Sedint 378285307Sedcloudabi_sys_file_unlink(struct thread *td, 379285307Sed struct cloudabi_sys_file_unlink_args *uap) 380285307Sed{ 381285834Sed char *path; 382285834Sed int error; 383285307Sed 384285834Sed error = copyin_path(uap->path, uap->pathlen, &path); 385285834Sed if (error != 0) 386285834Sed return (error); 387285834Sed 388285834Sed if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR) 389285834Sed error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 390285834Sed else 391285834Sed error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 392285834Sed cloudabi_freestr(path); 393285834Sed return (error); 394285307Sed} 395