1 /* $NetBSD: rumpuser.c,v 1.1 2010/02/26 18:54:20 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2007-2010 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #if !defined(lint) 30 __RCSID("$NetBSD: rumpuser.c,v 1.1 2010/02/26 18:54:20 pooka Exp $"); 31 #endif /* !lint */ 32 33 /* thank the maker for this */ 34 #ifdef __linux__ 35 #define _XOPEN_SOURCE 500 36 #define _BSD_SOURCE 37 #define _FILE_OFFSET_BITS 64 38 #include <features.h> 39 #endif 40 41 #include <sys/param.h> 42 #include <sys/event.h> 43 #include <sys/ioctl.h> 44 #include <sys/mman.h> 45 #include <sys/uio.h> 46 47 #ifdef __NetBSD__ 48 #include <sys/disklabel.h> 49 #endif 50 51 #include <assert.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <poll.h> 56 #include <stdarg.h> 57 #include <stdint.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <time.h> 62 #include <unistd.h> 63 64 #include <rump/rumpuser.h> 65 66 #include "rumpuser_int.h" 67 68 int 69 rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp, int *error) 70 { 71 struct stat sb; 72 uint64_t size; 73 int needsdev = 0, rv = 0, ft; 74 75 if (stat(path, &sb) == -1) { 76 *error = errno; 77 return -1; 78 } 79 80 switch (sb.st_mode & S_IFMT) { 81 case S_IFDIR: 82 ft = RUMPUSER_FT_DIR; 83 break; 84 case S_IFREG: 85 ft = RUMPUSER_FT_REG; 86 break; 87 case S_IFBLK: 88 ft = RUMPUSER_FT_BLK; 89 needsdev = 1; 90 break; 91 case S_IFCHR: 92 ft = RUMPUSER_FT_CHR; 93 needsdev = 1; 94 break; 95 default: 96 ft = RUMPUSER_FT_OTHER; 97 break; 98 } 99 100 if (!needsdev) { 101 size = sb.st_size; 102 } else if (sizep) { 103 /* 104 * Welcome to the jungle. Of course querying the kernel 105 * for a device partition size is supposed to be far from 106 * trivial. On NetBSD we use ioctl. On $other platform 107 * we have a problem. We try "the lseek trick" and just 108 * fail if that fails. Platform specific code can later 109 * be written here if appropriate. 110 * 111 * On NetBSD we hope and pray that for block devices nobody 112 * else is holding them open, because otherwise the kernel 113 * will not permit us to open it. Thankfully, this is 114 * usually called only in bootstrap and then we can 115 * forget about it. 116 */ 117 #ifndef __NetBSD__ 118 off_t off; 119 int fd; 120 121 fd = open(path, O_RDONLY); 122 if (fd == -1) { 123 *error = errno; 124 rv = -1; 125 goto out; 126 } 127 128 off = lseek(fd, 0, SEEK_END); 129 close(fd); 130 if (off != 0) { 131 size = off; 132 goto out; 133 } 134 fprintf(stderr, "error: device size query not implemented on " 135 "this platform\n"); 136 *error = EOPNOTSUPP; 137 rv = -1; 138 goto out; 139 #else 140 struct disklabel lab; 141 struct partition *parta; 142 int fd; 143 144 fd = open(path, O_RDONLY); 145 if (fd == -1) { 146 *error = errno; 147 rv = -1; 148 goto out; 149 } 150 151 if (ioctl(fd, DIOCGDINFO, &lab) == -1) { 152 *error = errno; 153 rv = -1; 154 goto out; 155 } 156 close(fd); 157 158 parta = &lab.d_partitions[DISKPART(sb.st_rdev)]; 159 size = (uint64_t)lab.d_secsize * parta->p_size; 160 #endif /* __NetBSD__ */ 161 } 162 163 out: 164 if (rv == 0 && sizep) 165 *sizep = size; 166 if (rv == 0 && ftp) 167 *ftp = ft; 168 169 return rv; 170 } 171 172 int 173 rumpuser_nanosleep(uint64_t *sec, uint64_t *nsec, int *error) 174 { 175 struct timespec rqt, rmt; 176 int rv; 177 178 /*LINTED*/ 179 rqt.tv_sec = *sec; 180 /*LINTED*/ 181 rqt.tv_nsec = *nsec; 182 183 KLOCK_WRAP(rv = nanosleep(&rqt, &rmt)); 184 if (rv == -1) 185 *error = errno; 186 187 *sec = rmt.tv_sec; 188 *nsec = rmt.tv_nsec; 189 190 return rv; 191 } 192 193 void * 194 rumpuser__malloc(size_t howmuch, int canfail, const char *func, int line) 195 { 196 void *rv; 197 198 rv = malloc(howmuch); 199 if (rv == NULL && canfail == 0) { 200 warn("malloc failed %s (%d)", func, line); 201 abort(); 202 } 203 204 return rv; 205 } 206 207 void * 208 rumpuser__realloc(void *ptr, size_t howmuch, int canfail, 209 const char *func, int line) 210 { 211 void *rv; 212 213 rv = realloc(ptr, howmuch); 214 if (rv == NULL && canfail == 0) { 215 warn("realloc failed %s (%d)", func, line); 216 abort(); 217 } 218 219 return rv; 220 } 221 222 void 223 rumpuser_free(void *ptr) 224 { 225 226 free(ptr); 227 } 228 229 void * 230 rumpuser_anonmmap(size_t size, int alignbit, int exec, int *error) 231 { 232 void *rv; 233 int prot; 234 235 prot = PROT_READ|PROT_WRITE; 236 if (exec) 237 prot |= PROT_EXEC; 238 /* XXX: MAP_ALIGNED() is not portable */ 239 rv = mmap(NULL, size, prot, MAP_ANON | MAP_ALIGNED(alignbit), -1, 0); 240 if (rv == MAP_FAILED) { 241 *error = errno; 242 return NULL; 243 } 244 return rv; 245 } 246 247 void 248 rumpuser_unmap(void *addr, size_t len) 249 { 250 int rv; 251 252 rv = munmap(addr, len); 253 assert(rv == 0); 254 } 255 256 void * 257 rumpuser_filemmap(int fd, off_t offset, size_t len, int flags, int *error) 258 { 259 void *rv; 260 int mmflags, prot; 261 262 if (flags & RUMPUSER_FILEMMAP_TRUNCATE) 263 ftruncate(fd, offset + len); 264 265 mmflags = MAP_FILE; 266 if (flags & RUMPUSER_FILEMMAP_SHARED) 267 mmflags |= MAP_SHARED; 268 else 269 mmflags |= MAP_PRIVATE; 270 271 prot = 0; 272 if (flags & RUMPUSER_FILEMMAP_READ) 273 prot |= PROT_READ; 274 if (flags & RUMPUSER_FILEMMAP_WRITE) 275 prot |= PROT_WRITE; 276 277 rv = mmap(NULL, len, PROT_READ|PROT_WRITE, mmflags, fd, offset); 278 if (rv == MAP_FAILED) { 279 *error = errno; 280 return NULL; 281 } 282 283 *error = 0; 284 return rv; 285 } 286 287 int 288 rumpuser_memsync(void *addr, size_t len, int *error) 289 { 290 291 DOCALL_KLOCK(int, (msync(addr, len, MS_SYNC))); 292 } 293 294 int 295 rumpuser_open(const char *path, int flags, int *error) 296 { 297 298 DOCALL(int, (open(path, flags, 0644))); 299 } 300 301 int 302 rumpuser_ioctl(int fd, u_long cmd, void *data, int *error) 303 { 304 305 DOCALL_KLOCK(int, (ioctl(fd, cmd, data))); 306 } 307 308 int 309 rumpuser_close(int fd, int *error) 310 { 311 312 DOCALL(int, close(fd)); 313 } 314 315 int 316 rumpuser_fsync(int fd, int *error) 317 { 318 319 DOCALL_KLOCK(int, fsync(fd)); 320 } 321 322 ssize_t 323 rumpuser_read(int fd, void *data, size_t size, int *error) 324 { 325 ssize_t rv; 326 327 KLOCK_WRAP(rv = read(fd, data, size)); 328 if (rv == -1) 329 *error = errno; 330 331 return rv; 332 } 333 334 ssize_t 335 rumpuser_pread(int fd, void *data, size_t size, off_t offset, int *error) 336 { 337 ssize_t rv; 338 339 KLOCK_WRAP(rv = pread(fd, data, size, offset)); 340 if (rv == -1) 341 *error = errno; 342 343 return rv; 344 } 345 346 void 347 rumpuser_read_bio(int fd, void *data, size_t size, off_t offset, 348 rump_biodone_fn biodone, void *biodonecookie) 349 { 350 ssize_t rv; 351 int error = 0; 352 353 rv = rumpuser_pread(fd, data, size, offset, &error); 354 /* check against <0 instead of ==-1 to get typing below right */ 355 if (rv < 0) 356 rv = 0; 357 358 /* LINTED: see above */ 359 biodone(biodonecookie, rv, error); 360 } 361 362 ssize_t 363 rumpuser_write(int fd, const void *data, size_t size, int *error) 364 { 365 ssize_t rv; 366 367 KLOCK_WRAP(rv = write(fd, data, size)); 368 if (rv == -1) 369 *error = errno; 370 371 return rv; 372 } 373 374 ssize_t 375 rumpuser_pwrite(int fd, const void *data, size_t size, off_t offset, int *error) 376 { 377 ssize_t rv; 378 379 KLOCK_WRAP(rv = pwrite(fd, data, size, offset)); 380 if (rv == -1) 381 *error = errno; 382 383 return rv; 384 } 385 386 void 387 rumpuser_write_bio(int fd, const void *data, size_t size, off_t offset, 388 rump_biodone_fn biodone, void *biodonecookie) 389 { 390 ssize_t rv; 391 int error = 0; 392 393 rv = rumpuser_pwrite(fd, data, size, offset, &error); 394 /* check against <0 instead of ==-1 to get typing below right */ 395 if (rv < 0) 396 rv = 0; 397 398 /* LINTED: see above */ 399 biodone(biodonecookie, rv, error); 400 } 401 402 ssize_t 403 rumpuser_readv(int fd, const struct rumpuser_iovec *riov, int iovcnt, 404 int *error) 405 { 406 struct iovec *iovp; 407 ssize_t rv; 408 int i; 409 410 iovp = malloc(iovcnt * sizeof(struct iovec)); 411 if (iovp == NULL) { 412 *error = ENOMEM; 413 return -1; 414 } 415 for (i = 0; i < iovcnt; i++) { 416 iovp[i].iov_base = riov[i].iov_base; 417 /*LINTED*/ 418 iovp[i].iov_len = riov[i].iov_len; 419 } 420 421 KLOCK_WRAP(rv = readv(fd, iovp, iovcnt)); 422 if (rv == -1) 423 *error = errno; 424 free(iovp); 425 426 return rv; 427 } 428 429 ssize_t 430 rumpuser_writev(int fd, const struct rumpuser_iovec *riov, int iovcnt, 431 int *error) 432 { 433 struct iovec *iovp; 434 ssize_t rv; 435 int i; 436 437 iovp = malloc(iovcnt * sizeof(struct iovec)); 438 if (iovp == NULL) { 439 *error = ENOMEM; 440 return -1; 441 } 442 for (i = 0; i < iovcnt; i++) { 443 iovp[i].iov_base = riov[i].iov_base; 444 /*LINTED*/ 445 iovp[i].iov_len = riov[i].iov_len; 446 } 447 448 KLOCK_WRAP(rv = writev(fd, iovp, iovcnt)); 449 if (rv == -1) 450 *error = errno; 451 free(iovp); 452 453 return rv; 454 } 455 456 int 457 rumpuser_gettime(uint64_t *sec, uint64_t *nsec, int *error) 458 { 459 struct timeval tv; 460 int rv; 461 462 rv = gettimeofday(&tv, NULL); 463 if (rv == -1) { 464 *error = errno; 465 return rv; 466 } 467 468 *sec = tv.tv_sec; 469 *nsec = tv.tv_usec * 1000; 470 471 return 0; 472 } 473 474 int 475 rumpuser_getenv(const char *name, char *buf, size_t blen, int *error) 476 { 477 478 DOCALL(int, getenv_r(name, buf, blen)); 479 } 480 481 int 482 rumpuser_gethostname(char *name, size_t namelen, int *error) 483 { 484 485 DOCALL(int, (gethostname(name, namelen))); 486 } 487 488 int 489 rumpuser_poll(struct pollfd *fds, int nfds, int timeout, int *error) 490 { 491 492 DOCALL_KLOCK(int, (poll(fds, (nfds_t)nfds, timeout))); 493 } 494 495 int 496 rumpuser_putchar(int c, int *error) 497 { 498 499 DOCALL(int, (putchar(c))); 500 } 501 502 void 503 rumpuser_exit(int rv) 504 { 505 506 if (rv == RUMPUSER_PANIC) 507 abort(); 508 else 509 exit(rv); 510 } 511 512 void 513 rumpuser_seterrno(int error) 514 { 515 516 errno = error; 517 } 518 519 int 520 rumpuser_writewatchfile_setup(int kq, int fd, intptr_t opaque, int *error) 521 { 522 struct kevent kev; 523 524 if (kq == -1) { 525 kq = kqueue(); 526 if (kq == -1) { 527 *error = errno; 528 return -1; 529 } 530 } 531 532 EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR, 533 NOTE_WRITE, 0, opaque); 534 if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) { 535 *error = errno; 536 return -1; 537 } 538 539 return kq; 540 } 541 542 int 543 rumpuser_writewatchfile_wait(int kq, intptr_t *opaque, int *error) 544 { 545 struct kevent kev; 546 int rv; 547 548 KLOCK_WRAP(rv = kevent(kq, NULL, 0, &kev, 1, NULL)); 549 if (rv == -1) { 550 *error = errno; 551 return -1; 552 } 553 554 if (opaque) 555 *opaque = kev.udata; 556 return rv; 557 } 558 559 /* 560 * This is meant for safe debugging prints from the kernel. 561 */ 562 int 563 rumpuser_dprintf(const char *format, ...) 564 { 565 va_list ap; 566 int rv; 567 568 va_start(ap, format); 569 rv = vprintf(format, ap); 570 va_end(ap); 571 572 return rv; 573 } 574