f27d6532f5
arc4random() hacks in rump with stubs that call the host arc4random() to get numbers that are hopefully actually random (arc4random() keyed with stack junk is not). This should fix some of the currently failing anita tests -- we should no longer generate duplicate "random" MAC addresses in the test environment.
649 lines
12 KiB
C
649 lines
12 KiB
C
/* $NetBSD: rumpuser.c,v 1.16 2011/11/28 08:05:05 tls Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2007-2010 Antti Kantee. All Rights Reserved.
|
|
*
|
|
* 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 ``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 <sys/cdefs.h>
|
|
#if !defined(lint)
|
|
__RCSID("$NetBSD: rumpuser.c,v 1.16 2011/11/28 08:05:05 tls Exp $");
|
|
#endif /* !lint */
|
|
|
|
/* thank the maker for this */
|
|
#ifdef __linux__
|
|
#define _XOPEN_SOURCE 500
|
|
#define _BSD_SOURCE
|
|
#define _FILE_OFFSET_BITS 64
|
|
#include <features.h>
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/event.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/uio.h>
|
|
|
|
#ifdef __NetBSD__
|
|
#include <sys/disk.h>
|
|
#include <sys/disklabel.h>
|
|
#include <sys/dkio.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <rump/rumpuser.h>
|
|
|
|
#include "rumpuser_int.h"
|
|
|
|
int
|
|
rumpuser_getversion()
|
|
{
|
|
|
|
return RUMPUSER_VERSION;
|
|
}
|
|
|
|
int
|
|
rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp, int *error)
|
|
{
|
|
struct stat sb;
|
|
uint64_t size;
|
|
int needsdev = 0, rv = 0, ft;
|
|
int fd = -1;
|
|
|
|
if (stat(path, &sb) == -1) {
|
|
seterror(errno);
|
|
return -1;
|
|
}
|
|
|
|
switch (sb.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
ft = RUMPUSER_FT_DIR;
|
|
break;
|
|
case S_IFREG:
|
|
ft = RUMPUSER_FT_REG;
|
|
break;
|
|
case S_IFBLK:
|
|
ft = RUMPUSER_FT_BLK;
|
|
needsdev = 1;
|
|
break;
|
|
case S_IFCHR:
|
|
ft = RUMPUSER_FT_CHR;
|
|
needsdev = 1;
|
|
break;
|
|
default:
|
|
ft = RUMPUSER_FT_OTHER;
|
|
break;
|
|
}
|
|
|
|
if (!needsdev) {
|
|
size = sb.st_size;
|
|
} else if (sizep) {
|
|
/*
|
|
* Welcome to the jungle. Of course querying the kernel
|
|
* for a device partition size is supposed to be far from
|
|
* trivial. On NetBSD we use ioctl. On $other platform
|
|
* we have a problem. We try "the lseek trick" and just
|
|
* fail if that fails. Platform specific code can later
|
|
* be written here if appropriate.
|
|
*
|
|
* On NetBSD we hope and pray that for block devices nobody
|
|
* else is holding them open, because otherwise the kernel
|
|
* will not permit us to open it. Thankfully, this is
|
|
* usually called only in bootstrap and then we can
|
|
* forget about it.
|
|
*/
|
|
#ifndef __NetBSD__
|
|
off_t off;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
if (fd == -1) {
|
|
seterror(errno);
|
|
rv = -1;
|
|
goto out;
|
|
}
|
|
|
|
off = lseek(fd, 0, SEEK_END);
|
|
if (off != 0) {
|
|
size = off;
|
|
goto out;
|
|
}
|
|
fprintf(stderr, "error: device size query not implemented on "
|
|
"this platform\n");
|
|
seterror(EOPNOTSUPP);
|
|
rv = -1;
|
|
goto out;
|
|
#else
|
|
struct disklabel lab;
|
|
struct partition *parta;
|
|
struct dkwedge_info dkw;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
if (fd == -1) {
|
|
seterror(errno);
|
|
rv = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl(fd, DIOCGDINFO, &lab) == 0) {
|
|
parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
|
|
size = (uint64_t)lab.d_secsize * parta->p_size;
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
|
|
/*
|
|
* XXX: should use DIOCGDISKINFO to query
|
|
* sector size, but that requires proplib,
|
|
* so just don't bother for now. it's nice
|
|
* that something as difficult as figuring out
|
|
* a partition's size has been made so easy.
|
|
*/
|
|
size = dkw.dkw_size << DEV_BSHIFT;
|
|
goto out;
|
|
}
|
|
|
|
seterror(errno);
|
|
rv = -1;
|
|
#endif /* __NetBSD__ */
|
|
}
|
|
|
|
out:
|
|
if (rv == 0 && sizep)
|
|
*sizep = size;
|
|
if (rv == 0 && ftp)
|
|
*ftp = ft;
|
|
if (fd != -1)
|
|
close(fd);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
rumpuser_nanosleep(uint64_t *sec, uint64_t *nsec, int *error)
|
|
{
|
|
struct timespec rqt, rmt;
|
|
int rv;
|
|
|
|
/*LINTED*/
|
|
rqt.tv_sec = *sec;
|
|
/*LINTED*/
|
|
rqt.tv_nsec = *nsec;
|
|
|
|
KLOCK_WRAP(rv = nanosleep(&rqt, &rmt));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
|
|
*sec = rmt.tv_sec;
|
|
*nsec = rmt.tv_nsec;
|
|
|
|
return rv;
|
|
}
|
|
|
|
void *
|
|
rumpuser_malloc(size_t howmuch, int alignment)
|
|
{
|
|
void *mem;
|
|
int rv;
|
|
|
|
if (alignment == 0)
|
|
alignment = sizeof(void *);
|
|
|
|
rv = posix_memalign(&mem, (size_t)alignment, howmuch);
|
|
if (__predict_false(rv != 0)) {
|
|
if (rv == EINVAL) {
|
|
printf("rumpuser_malloc: invalid alignment %d\n",
|
|
alignment);
|
|
abort();
|
|
}
|
|
mem = NULL;
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
void *
|
|
rumpuser_realloc(void *ptr, size_t howmuch)
|
|
{
|
|
|
|
return realloc(ptr, howmuch);
|
|
}
|
|
|
|
void
|
|
rumpuser_free(void *ptr)
|
|
{
|
|
|
|
free(ptr);
|
|
}
|
|
|
|
void *
|
|
rumpuser_anonmmap(void *prefaddr, size_t size, int alignbit,
|
|
int exec, int *error)
|
|
{
|
|
void *rv;
|
|
int prot;
|
|
|
|
prot = PROT_READ|PROT_WRITE;
|
|
if (exec)
|
|
prot |= PROT_EXEC;
|
|
/* XXX: MAP_ALIGNED() is not portable */
|
|
rv = mmap(prefaddr, size, prot,
|
|
MAP_ANON | MAP_ALIGNED(alignbit), -1, 0);
|
|
if (rv == MAP_FAILED) {
|
|
seterror(errno);
|
|
return NULL;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
rumpuser_unmap(void *addr, size_t len)
|
|
{
|
|
int rv;
|
|
|
|
rv = munmap(addr, len);
|
|
assert(rv == 0);
|
|
}
|
|
|
|
void *
|
|
rumpuser_filemmap(int fd, off_t offset, size_t len, int flags, int *error)
|
|
{
|
|
void *rv;
|
|
int mmflags, prot;
|
|
|
|
if (flags & RUMPUSER_FILEMMAP_TRUNCATE)
|
|
ftruncate(fd, offset + len);
|
|
|
|
mmflags = MAP_FILE;
|
|
if (flags & RUMPUSER_FILEMMAP_SHARED)
|
|
mmflags |= MAP_SHARED;
|
|
else
|
|
mmflags |= MAP_PRIVATE;
|
|
|
|
prot = 0;
|
|
if (flags & RUMPUSER_FILEMMAP_READ)
|
|
prot |= PROT_READ;
|
|
if (flags & RUMPUSER_FILEMMAP_WRITE)
|
|
prot |= PROT_WRITE;
|
|
|
|
rv = mmap(NULL, len, PROT_READ|PROT_WRITE, mmflags, fd, offset);
|
|
if (rv == MAP_FAILED) {
|
|
seterror(errno);
|
|
return NULL;
|
|
}
|
|
|
|
seterror(0);
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
rumpuser_memsync(void *addr, size_t len, int *error)
|
|
{
|
|
|
|
DOCALL_KLOCK(int, (msync(addr, len, MS_SYNC)));
|
|
}
|
|
|
|
int
|
|
rumpuser_open(const char *path, int flags, int *error)
|
|
{
|
|
|
|
DOCALL(int, (open(path, flags, 0644)));
|
|
}
|
|
|
|
int
|
|
rumpuser_ioctl(int fd, u_long cmd, void *data, int *error)
|
|
{
|
|
|
|
DOCALL_KLOCK(int, (ioctl(fd, cmd, data)));
|
|
}
|
|
|
|
int
|
|
rumpuser_close(int fd, int *error)
|
|
{
|
|
|
|
DOCALL(int, close(fd));
|
|
}
|
|
|
|
int
|
|
rumpuser_fsync(int fd, int *error)
|
|
{
|
|
|
|
DOCALL_KLOCK(int, fsync(fd));
|
|
}
|
|
|
|
ssize_t
|
|
rumpuser_read(int fd, void *data, size_t size, int *error)
|
|
{
|
|
ssize_t rv;
|
|
|
|
KLOCK_WRAP(rv = read(fd, data, size));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
|
|
return rv;
|
|
}
|
|
|
|
ssize_t
|
|
rumpuser_pread(int fd, void *data, size_t size, off_t offset, int *error)
|
|
{
|
|
ssize_t rv;
|
|
|
|
KLOCK_WRAP(rv = pread(fd, data, size, offset));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
rumpuser_read_bio(int fd, void *data, size_t size, off_t offset,
|
|
rump_biodone_fn biodone, void *biodonecookie)
|
|
{
|
|
ssize_t rv;
|
|
int error = 0;
|
|
|
|
rv = rumpuser_pread(fd, data, size, offset, &error);
|
|
/* check against <0 instead of ==-1 to get typing below right */
|
|
if (rv < 0)
|
|
rv = 0;
|
|
|
|
/* LINTED: see above */
|
|
biodone(biodonecookie, rv, error);
|
|
}
|
|
|
|
ssize_t
|
|
rumpuser_write(int fd, const void *data, size_t size, int *error)
|
|
{
|
|
ssize_t rv;
|
|
|
|
KLOCK_WRAP(rv = write(fd, data, size));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
|
|
return rv;
|
|
}
|
|
|
|
ssize_t
|
|
rumpuser_pwrite(int fd, const void *data, size_t size, off_t offset, int *error)
|
|
{
|
|
ssize_t rv;
|
|
|
|
KLOCK_WRAP(rv = pwrite(fd, data, size, offset));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
rumpuser_write_bio(int fd, const void *data, size_t size, off_t offset,
|
|
rump_biodone_fn biodone, void *biodonecookie)
|
|
{
|
|
ssize_t rv;
|
|
int error = 0;
|
|
|
|
rv = rumpuser_pwrite(fd, data, size, offset, &error);
|
|
/* check against <0 instead of ==-1 to get typing below right */
|
|
if (rv < 0)
|
|
rv = 0;
|
|
|
|
/* LINTED: see above */
|
|
biodone(biodonecookie, rv, error);
|
|
}
|
|
|
|
ssize_t
|
|
rumpuser_readv(int fd, const struct rumpuser_iovec *riov, int iovcnt,
|
|
int *error)
|
|
{
|
|
struct iovec *iovp;
|
|
ssize_t rv;
|
|
int i;
|
|
|
|
iovp = malloc(iovcnt * sizeof(struct iovec));
|
|
if (iovp == NULL) {
|
|
seterror(ENOMEM);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < iovcnt; i++) {
|
|
iovp[i].iov_base = riov[i].iov_base;
|
|
/*LINTED*/
|
|
iovp[i].iov_len = riov[i].iov_len;
|
|
}
|
|
|
|
KLOCK_WRAP(rv = readv(fd, iovp, iovcnt));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
free(iovp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
ssize_t
|
|
rumpuser_writev(int fd, const struct rumpuser_iovec *riov, int iovcnt,
|
|
int *error)
|
|
{
|
|
struct iovec *iovp;
|
|
ssize_t rv;
|
|
int i;
|
|
|
|
iovp = malloc(iovcnt * sizeof(struct iovec));
|
|
if (iovp == NULL) {
|
|
seterror(ENOMEM);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < iovcnt; i++) {
|
|
iovp[i].iov_base = riov[i].iov_base;
|
|
/*LINTED*/
|
|
iovp[i].iov_len = riov[i].iov_len;
|
|
}
|
|
|
|
KLOCK_WRAP(rv = writev(fd, iovp, iovcnt));
|
|
if (rv == -1)
|
|
seterror(errno);
|
|
free(iovp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
rumpuser_gettime(uint64_t *sec, uint64_t *nsec, int *error)
|
|
{
|
|
struct timeval tv;
|
|
int rv;
|
|
|
|
rv = gettimeofday(&tv, NULL);
|
|
if (rv == -1) {
|
|
seterror(errno);
|
|
return rv;
|
|
}
|
|
|
|
*sec = tv.tv_sec;
|
|
*nsec = tv.tv_usec * 1000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rumpuser_getenv(const char *name, char *buf, size_t blen, int *error)
|
|
{
|
|
|
|
DOCALL(int, getenv_r(name, buf, blen));
|
|
}
|
|
|
|
int
|
|
rumpuser_gethostname(char *name, size_t namelen, int *error)
|
|
{
|
|
char tmp[MAXHOSTNAMELEN];
|
|
|
|
if (gethostname(tmp, sizeof(tmp)) == -1) {
|
|
snprintf(name, namelen, "rump-%05d.rumpdomain", getpid());
|
|
} else {
|
|
snprintf(name, namelen, "rump-%05d.%s.rumpdomain",
|
|
getpid(), tmp);
|
|
}
|
|
|
|
*error = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rumpuser_poll(struct pollfd *fds, int nfds, int timeout, int *error)
|
|
{
|
|
|
|
DOCALL_KLOCK(int, (poll(fds, (nfds_t)nfds, timeout)));
|
|
}
|
|
|
|
int
|
|
rumpuser_putchar(int c, int *error)
|
|
{
|
|
|
|
DOCALL(int, (putchar(c)));
|
|
}
|
|
|
|
void
|
|
rumpuser_exit(int rv)
|
|
{
|
|
|
|
if (rv == RUMPUSER_PANIC)
|
|
abort();
|
|
else
|
|
exit(rv);
|
|
}
|
|
|
|
void
|
|
rumpuser_seterrno(int error)
|
|
{
|
|
|
|
errno = error;
|
|
}
|
|
|
|
int
|
|
rumpuser_writewatchfile_setup(int kq, int fd, intptr_t opaque, int *error)
|
|
{
|
|
struct kevent kev;
|
|
|
|
if (kq == -1) {
|
|
kq = kqueue();
|
|
if (kq == -1) {
|
|
seterror(errno);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR,
|
|
NOTE_WRITE, 0, opaque);
|
|
if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
|
|
seterror(errno);
|
|
return -1;
|
|
}
|
|
|
|
return kq;
|
|
}
|
|
|
|
int
|
|
rumpuser_writewatchfile_wait(int kq, intptr_t *opaque, int *error)
|
|
{
|
|
struct kevent kev;
|
|
int rv;
|
|
|
|
again:
|
|
KLOCK_WRAP(rv = kevent(kq, NULL, 0, &kev, 1, NULL));
|
|
if (rv == -1) {
|
|
if (errno == EINTR)
|
|
goto again;
|
|
seterror(errno);
|
|
return -1;
|
|
}
|
|
|
|
if (opaque)
|
|
*opaque = kev.udata;
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* This is meant for safe debugging prints from the kernel.
|
|
*/
|
|
int
|
|
rumpuser_dprintf(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
int rv;
|
|
|
|
va_start(ap, format);
|
|
rv = vfprintf(stderr, format, ap);
|
|
va_end(ap);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
rumpuser_kill(int64_t pid, int sig, int *error)
|
|
{
|
|
|
|
#ifdef __NetBSD__
|
|
if (pid == RUMPUSER_PID_SELF) {
|
|
DOCALL(int, raise(sig));
|
|
} else {
|
|
DOCALL(int, kill((pid_t)pid, sig));
|
|
}
|
|
#else
|
|
/* XXXfixme: signal numbers may not match on non-NetBSD */
|
|
seterror(EOPNOTSUPP);
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
rumpuser_getnhostcpu(void)
|
|
{
|
|
int ncpu;
|
|
size_t sz = sizeof(ncpu);
|
|
|
|
#ifdef __NetBSD__
|
|
if (sysctlbyname("hw.ncpu", &ncpu, &sz, NULL, 0) == -1)
|
|
return 1;
|
|
return ncpu;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
uint32_t
|
|
rumpuser_arc4random(void)
|
|
{
|
|
return arc4random();
|
|
}
|