Index: head/sys/compat/cloudabi/cloudabi_file.c =================================================================== --- head/sys/compat/cloudabi/cloudabi_file.c (revision 285953) +++ head/sys/compat/cloudabi/cloudabi_file.c (revision 285954) @@ -1,394 +1,469 @@ /*- * Copyright (c) 2015 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); /* * Copying pathnames from userspace to kernelspace. * * Unlike most operating systems, CloudABI doesn't use null-terminated * pathname strings. Processes always pass pathnames to the kernel by * providing a base pointer and a length. This has a couple of reasons: * * - It makes it easier to use CloudABI in combination with programming * languages other than C, that may use non-null terminated strings. * - It allows for calling system calls on individual components of the * pathname without modifying the input string. * * The function below copies in pathname strings and null-terminates it. * It also ensure that the string itself does not contain any null * bytes. * * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass * in unterminated pathname strings, so we can do away with * the copying. */ static int copyin_path(const char *uaddr, size_t len, char **result) { char *buf; int error; if (len >= PATH_MAX) return (ENAMETOOLONG); buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK); error = copyin(uaddr, buf, len); if (error != 0) { free(buf, M_CLOUDABI_PATH); return (error); } if (memchr(buf, '\0', len) != NULL) { free(buf, M_CLOUDABI_PATH); return (EINVAL); } buf[len] = '\0'; *result = buf; return (0); } static void cloudabi_freestr(char *buf) { free(buf, M_CLOUDABI_PATH); } int cloudabi_sys_file_advise(struct thread *td, struct cloudabi_sys_file_advise_args *uap) { int advice; switch (uap->advice) { case CLOUDABI_ADVICE_DONTNEED: advice = POSIX_FADV_DONTNEED; break; case CLOUDABI_ADVICE_NOREUSE: advice = POSIX_FADV_NOREUSE; break; case CLOUDABI_ADVICE_NORMAL: advice = POSIX_FADV_NORMAL; break; case CLOUDABI_ADVICE_RANDOM: advice = POSIX_FADV_RANDOM; break; case CLOUDABI_ADVICE_SEQUENTIAL: advice = POSIX_FADV_SEQUENTIAL; break; case CLOUDABI_ADVICE_WILLNEED: advice = POSIX_FADV_WILLNEED; break; default: return (EINVAL); } return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice)); } int cloudabi_sys_file_allocate(struct thread *td, struct cloudabi_sys_file_allocate_args *uap) { return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len)); } int cloudabi_sys_file_create(struct thread *td, struct cloudabi_sys_file_create_args *uap) { char *path; int error; error = copyin_path(uap->path, uap->pathlen, &path); if (error != 0) return (error); /* * CloudABI processes cannot interact with UNIX credentials and * permissions. Depend on the umask that is set prior to * execution to restrict the file permissions. */ switch (uap->type) { case CLOUDABI_FILETYPE_DIRECTORY: error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777); break; case CLOUDABI_FILETYPE_FIFO: error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666); break; default: error = EINVAL; break; } cloudabi_freestr(path); return (error); } int cloudabi_sys_file_link(struct thread *td, struct cloudabi_sys_file_link_args *uap) { char *path1, *path2; int error; error = copyin_path(uap->path1, uap->path1len, &path1); if (error != 0) return (error); error = copyin_path(uap->path2, uap->path2len, &path2); if (error != 0) { cloudabi_freestr(path1); return (error); } error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2, UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? FOLLOW : NOFOLLOW); cloudabi_freestr(path1); cloudabi_freestr(path2); return (error); } int cloudabi_sys_file_open(struct thread *td, struct cloudabi_sys_file_open_args *uap) { /* Not implemented. */ return (ENOSYS); } int cloudabi_sys_file_readdir(struct thread *td, struct cloudabi_sys_file_readdir_args *uap) { /* Not implemented. */ return (ENOSYS); } int cloudabi_sys_file_readlink(struct thread *td, struct cloudabi_sys_file_readlink_args *uap) { char *path; int error; error = copyin_path(uap->path, uap->pathlen, &path); if (error != 0) return (error); error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, uap->buf, UIO_USERSPACE, uap->bufsize); cloudabi_freestr(path); return (error); } int cloudabi_sys_file_rename(struct thread *td, struct cloudabi_sys_file_rename_args *uap) { char *old, *new; int error; error = copyin_path(uap->old, uap->oldlen, &old); if (error != 0) return (error); error = copyin_path(uap->new, uap->newlen, &new); if (error != 0) { cloudabi_freestr(old); return (error); } error = kern_renameat(td, uap->oldfd, old, uap->newfd, new, UIO_SYSSPACE); cloudabi_freestr(old); cloudabi_freestr(new); return (error); } /* Converts a FreeBSD stat structure to a CloudABI stat structure. */ static void convert_stat(const struct stat *sb, cloudabi_filestat_t *csb) { cloudabi_filestat_t res = { .st_dev = sb->st_dev, .st_ino = sb->st_ino, .st_nlink = sb->st_nlink, .st_size = sb->st_size, }; cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); *csb = res; } int cloudabi_sys_file_stat_fget(struct thread *td, struct cloudabi_sys_file_stat_fget_args *uap) { struct stat sb; cloudabi_filestat_t csb; struct file *fp; cap_rights_t rights; cloudabi_filetype_t filetype; int error; /* Fetch file descriptor attributes. */ error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); if (error != 0) return (error); error = fo_stat(fp, &sb, td->td_ucred, td); if (error != 0) { fdrop(fp, td); return (error); } filetype = cloudabi_convert_filetype(fp); fdrop(fp, td); /* Convert attributes to CloudABI's format. */ convert_stat(&sb, &csb); csb.st_filetype = filetype; return (copyout(&csb, uap->buf, sizeof(csb))); } +/* Converts timestamps to arguments to futimens() and utimensat(). */ +static void +convert_utimens_arguments(const cloudabi_filestat_t *fs, + cloudabi_fsflags_t flags, struct timespec *ts) +{ + + if ((flags & CLOUDABI_FILESTAT_ATIM_NOW) != 0) { + ts[0].tv_nsec = UTIME_NOW; + } else if ((flags & CLOUDABI_FILESTAT_ATIM) != 0) { + ts[0].tv_sec = fs->st_atim / 1000000000; + ts[0].tv_nsec = fs->st_atim % 1000000000; + } else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if ((flags & CLOUDABI_FILESTAT_MTIM_NOW) != 0) { + ts[1].tv_nsec = UTIME_NOW; + } else if ((flags & CLOUDABI_FILESTAT_MTIM) != 0) { + ts[1].tv_sec = fs->st_mtim / 1000000000; + ts[1].tv_nsec = fs->st_mtim % 1000000000; + } else { + ts[1].tv_nsec = UTIME_OMIT; + } +} + int cloudabi_sys_file_stat_fput(struct thread *td, struct cloudabi_sys_file_stat_fput_args *uap) { + cloudabi_filestat_t fs; + struct timespec ts[2]; + int error; - /* Not implemented. */ - return (ENOSYS); + error = copyin(uap->buf, &fs, sizeof(fs)); + if (error != 0) + return (error); + + /* + * Only support truncation and timestamp modification separately + * for now, to prevent unnecessary code duplication. + */ + if ((uap->flags & CLOUDABI_FILESTAT_SIZE) != 0) { + /* Call into kern_ftruncate() for file truncation. */ + if ((uap->flags & ~CLOUDABI_FILESTAT_SIZE) != 0) + return (EINVAL); + return (kern_ftruncate(td, uap->fd, fs.st_size)); + } else if ((uap->flags & (CLOUDABI_FILESTAT_ATIM | + CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | + CLOUDABI_FILESTAT_MTIM_NOW)) != 0) { + /* Call into kern_futimens() for timestamp modification. */ + if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | + CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | + CLOUDABI_FILESTAT_MTIM_NOW)) != 0) + return (EINVAL); + convert_utimens_arguments(&fs, uap->flags, ts); + return (kern_futimens(td, uap->fd, ts, UIO_SYSSPACE)); + } + return (EINVAL); } int cloudabi_sys_file_stat_get(struct thread *td, struct cloudabi_sys_file_stat_get_args *uap) { struct stat sb; cloudabi_filestat_t csb; char *path; int error; error = copyin_path(uap->path, uap->pathlen, &path); if (error != 0) return (error); error = kern_statat(td, (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL); cloudabi_freestr(path); if (error != 0) return (error); /* Convert results and return them. */ convert_stat(&sb, &csb); if (S_ISBLK(sb.st_mode)) csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; else if (S_ISCHR(sb.st_mode)) csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; else if (S_ISDIR(sb.st_mode)) csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; else if (S_ISFIFO(sb.st_mode)) csb.st_filetype = CLOUDABI_FILETYPE_FIFO; else if (S_ISREG(sb.st_mode)) csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; else if (S_ISSOCK(sb.st_mode)) { /* Inaccurate, but the best that we can do. */ csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; } else if (S_ISLNK(sb.st_mode)) csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; else csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; return (copyout(&csb, uap->buf, sizeof(csb))); } int cloudabi_sys_file_stat_put(struct thread *td, struct cloudabi_sys_file_stat_put_args *uap) { + cloudabi_filestat_t fs; + struct timespec ts[2]; + char *path; + int error; - /* Not implemented. */ - return (ENOSYS); + /* + * Only support timestamp modification for now, as there is no + * truncateat(). + */ + if ((uap->flags & ~(CLOUDABI_FILESTAT_ATIM | + CLOUDABI_FILESTAT_ATIM_NOW | CLOUDABI_FILESTAT_MTIM | + CLOUDABI_FILESTAT_MTIM_NOW)) != 0) + return (EINVAL); + + error = copyin(uap->buf, &fs, sizeof(fs)); + if (error != 0) + return (error); + error = copyin_path(uap->path, uap->pathlen, &path); + if (error != 0) + return (error); + + convert_utimens_arguments(&fs, uap->flags, ts); + error = kern_utimensat(td, uap->fd, path, UIO_SYSSPACE, ts, + UIO_SYSSPACE, (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? + 0 : AT_SYMLINK_NOFOLLOW); + cloudabi_freestr(path); + return (error); } int cloudabi_sys_file_symlink(struct thread *td, struct cloudabi_sys_file_symlink_args *uap) { char *path1, *path2; int error; error = copyin_path(uap->path1, uap->path1len, &path1); if (error != 0) return (error); error = copyin_path(uap->path2, uap->path2len, &path2); if (error != 0) { cloudabi_freestr(path1); return (error); } error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); cloudabi_freestr(path1); cloudabi_freestr(path2); return (error); } int cloudabi_sys_file_unlink(struct thread *td, struct cloudabi_sys_file_unlink_args *uap) { char *path; int error; error = copyin_path(uap->path, uap->pathlen, &path); if (error != 0) return (error); if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR) error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); else error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); cloudabi_freestr(path); return (error); }