cloudabi_file.c revision 285930
1/*- 2 * Copyright (c) 2015 Nuxi, https://nuxi.nl/ 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: head/sys/compat/cloudabi/cloudabi_file.c 285930 2015-07-28 06:36:49Z ed $"); 28 29#include <sys/param.h> 30#include <sys/capsicum.h> 31#include <sys/fcntl.h> 32#include <sys/kernel.h> 33#include <sys/malloc.h> 34#include <sys/namei.h> 35#include <sys/proc.h> 36#include <sys/stat.h> 37#include <sys/syscallsubr.h> 38 39#include <compat/cloudabi/cloudabi_proto.h> 40#include <compat/cloudabi/cloudabi_syscalldefs.h> 41#include <compat/cloudabi/cloudabi_util.h> 42 43static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames"); 44 45/* 46 * Copying pathnames from userspace to kernelspace. 47 * 48 * Unlike most operating systems, CloudABI doesn't use null-terminated 49 * pathname strings. Processes always pass pathnames to the kernel by 50 * providing a base pointer and a length. This has a couple of reasons: 51 * 52 * - It makes it easier to use CloudABI in combination with programming 53 * languages other than C, that may use non-null terminated strings. 54 * - It allows for calling system calls on individual components of the 55 * pathname without modifying the input string. 56 * 57 * The function below copies in pathname strings and null-terminates it. 58 * It also ensure that the string itself does not contain any null 59 * bytes. 60 * 61 * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass 62 * in unterminated pathname strings, so we can do away with 63 * the copying. 64 */ 65 66static int 67copyin_path(const char *uaddr, size_t len, char **result) 68{ 69 char *buf; 70 int error; 71 72 if (len >= PATH_MAX) 73 return (ENAMETOOLONG); 74 buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK); 75 error = copyin(uaddr, buf, len); 76 if (error != 0) { 77 free(buf, M_CLOUDABI_PATH); 78 return (error); 79 } 80 if (memchr(buf, '\0', len) != NULL) { 81 free(buf, M_CLOUDABI_PATH); 82 return (EINVAL); 83 } 84 buf[len] = '\0'; 85 *result = buf; 86 return (0); 87} 88 89static void 90cloudabi_freestr(char *buf) 91{ 92 93 free(buf, M_CLOUDABI_PATH); 94} 95 96int 97cloudabi_sys_file_advise(struct thread *td, 98 struct cloudabi_sys_file_advise_args *uap) 99{ 100 int advice; 101 102 switch (uap->advice) { 103 case CLOUDABI_ADVICE_DONTNEED: 104 advice = POSIX_FADV_DONTNEED; 105 break; 106 case CLOUDABI_ADVICE_NOREUSE: 107 advice = POSIX_FADV_NOREUSE; 108 break; 109 case CLOUDABI_ADVICE_NORMAL: 110 advice = POSIX_FADV_NORMAL; 111 break; 112 case CLOUDABI_ADVICE_RANDOM: 113 advice = POSIX_FADV_RANDOM; 114 break; 115 case CLOUDABI_ADVICE_SEQUENTIAL: 116 advice = POSIX_FADV_SEQUENTIAL; 117 break; 118 case CLOUDABI_ADVICE_WILLNEED: 119 advice = POSIX_FADV_WILLNEED; 120 break; 121 default: 122 return (EINVAL); 123 } 124 125 return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice)); 126} 127 128int 129cloudabi_sys_file_allocate(struct thread *td, 130 struct cloudabi_sys_file_allocate_args *uap) 131{ 132 133 return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len)); 134} 135 136int 137cloudabi_sys_file_create(struct thread *td, 138 struct cloudabi_sys_file_create_args *uap) 139{ 140 141 /* Not implemented. */ 142 return (ENOSYS); 143} 144 145int 146cloudabi_sys_file_link(struct thread *td, 147 struct cloudabi_sys_file_link_args *uap) 148{ 149 char *path1, *path2; 150 int error; 151 152 error = copyin_path(uap->path1, uap->path1len, &path1); 153 if (error != 0) 154 return (error); 155 error = copyin_path(uap->path2, uap->path2len, &path2); 156 if (error != 0) { 157 cloudabi_freestr(path1); 158 return (error); 159 } 160 161 error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2, 162 UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 163 FOLLOW : NOFOLLOW); 164 cloudabi_freestr(path1); 165 cloudabi_freestr(path2); 166 return (error); 167} 168 169int 170cloudabi_sys_file_open(struct thread *td, 171 struct cloudabi_sys_file_open_args *uap) 172{ 173 174 /* Not implemented. */ 175 return (ENOSYS); 176} 177 178int 179cloudabi_sys_file_readdir(struct thread *td, 180 struct cloudabi_sys_file_readdir_args *uap) 181{ 182 183 /* Not implemented. */ 184 return (ENOSYS); 185} 186 187int 188cloudabi_sys_file_readlink(struct thread *td, 189 struct cloudabi_sys_file_readlink_args *uap) 190{ 191 char *path; 192 int error; 193 194 error = copyin_path(uap->path, uap->pathlen, &path); 195 if (error != 0) 196 return (error); 197 198 error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE, 199 uap->buf, UIO_USERSPACE, uap->bufsize); 200 cloudabi_freestr(path); 201 return (error); 202} 203 204int 205cloudabi_sys_file_rename(struct thread *td, 206 struct cloudabi_sys_file_rename_args *uap) 207{ 208 char *old, *new; 209 int error; 210 211 error = copyin_path(uap->old, uap->oldlen, &old); 212 if (error != 0) 213 return (error); 214 error = copyin_path(uap->new, uap->newlen, &new); 215 if (error != 0) { 216 cloudabi_freestr(old); 217 return (error); 218 } 219 220 error = kern_renameat(td, uap->oldfd, old, uap->newfd, new, 221 UIO_SYSSPACE); 222 cloudabi_freestr(old); 223 cloudabi_freestr(new); 224 return (error); 225} 226 227/* Converts a FreeBSD stat structure to a CloudABI stat structure. */ 228static void 229convert_stat(const struct stat *sb, cloudabi_filestat_t *csb) 230{ 231 cloudabi_filestat_t res = { 232 .st_dev = sb->st_dev, 233 .st_ino = sb->st_ino, 234 .st_nlink = sb->st_nlink, 235 .st_size = sb->st_size, 236 }; 237 238 cloudabi_convert_timespec(&sb->st_atim, &res.st_atim); 239 cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim); 240 cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim); 241 *csb = res; 242} 243 244int 245cloudabi_sys_file_stat_fget(struct thread *td, 246 struct cloudabi_sys_file_stat_fget_args *uap) 247{ 248 struct stat sb; 249 cloudabi_filestat_t csb; 250 struct file *fp; 251 cap_rights_t rights; 252 cloudabi_filetype_t filetype; 253 int error; 254 255 /* Fetch file descriptor attributes. */ 256 error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp); 257 if (error != 0) 258 return (error); 259 error = fo_stat(fp, &sb, td->td_ucred, td); 260 if (error != 0) { 261 fdrop(fp, td); 262 return (error); 263 } 264 filetype = cloudabi_convert_filetype(fp); 265 fdrop(fp, td); 266 267 /* Convert attributes to CloudABI's format. */ 268 convert_stat(&sb, &csb); 269 csb.st_filetype = filetype; 270 return (copyout(&csb, uap->buf, sizeof(csb))); 271} 272 273int 274cloudabi_sys_file_stat_fput(struct thread *td, 275 struct cloudabi_sys_file_stat_fput_args *uap) 276{ 277 278 /* Not implemented. */ 279 return (ENOSYS); 280} 281 282int 283cloudabi_sys_file_stat_get(struct thread *td, 284 struct cloudabi_sys_file_stat_get_args *uap) 285{ 286 struct stat sb; 287 cloudabi_filestat_t csb; 288 char *path; 289 int error; 290 291 error = copyin_path(uap->path, uap->pathlen, &path); 292 if (error != 0) 293 return (error); 294 295 error = kern_statat(td, 296 (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : 297 AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL); 298 cloudabi_freestr(path); 299 if (error != 0) 300 return (error); 301 302 /* Convert results and return them. */ 303 convert_stat(&sb, &csb); 304 if (S_ISBLK(sb.st_mode)) 305 csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE; 306 else if (S_ISCHR(sb.st_mode)) 307 csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE; 308 else if (S_ISDIR(sb.st_mode)) 309 csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY; 310 else if (S_ISFIFO(sb.st_mode)) 311 csb.st_filetype = CLOUDABI_FILETYPE_FIFO; 312 else if (S_ISREG(sb.st_mode)) 313 csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE; 314 else if (S_ISSOCK(sb.st_mode)) { 315 /* Inaccurate, but the best that we can do. */ 316 csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM; 317 } else if (S_ISLNK(sb.st_mode)) 318 csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK; 319 else 320 csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN; 321 return (copyout(&csb, uap->buf, sizeof(csb))); 322} 323 324int 325cloudabi_sys_file_stat_put(struct thread *td, 326 struct cloudabi_sys_file_stat_put_args *uap) 327{ 328 329 /* Not implemented. */ 330 return (ENOSYS); 331} 332 333int 334cloudabi_sys_file_symlink(struct thread *td, 335 struct cloudabi_sys_file_symlink_args *uap) 336{ 337 char *path1, *path2; 338 int error; 339 340 error = copyin_path(uap->path1, uap->path1len, &path1); 341 if (error != 0) 342 return (error); 343 error = copyin_path(uap->path2, uap->path2len, &path2); 344 if (error != 0) { 345 cloudabi_freestr(path1); 346 return (error); 347 } 348 349 error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE); 350 cloudabi_freestr(path1); 351 cloudabi_freestr(path2); 352 return (error); 353} 354 355int 356cloudabi_sys_file_unlink(struct thread *td, 357 struct cloudabi_sys_file_unlink_args *uap) 358{ 359 char *path; 360 int error; 361 362 error = copyin_path(uap->path, uap->pathlen, &path); 363 if (error != 0) 364 return (error); 365 366 if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR) 367 error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); 368 else 369 error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); 370 cloudabi_freestr(path); 371 return (error); 372} 373