NetBSD/lib/librumpuser/rumpuser_file.c
justin 4ce85d0b6f Split out file and memory access in librumpuser
This allows alternate implementations to reuse these parts, and the file
parts will at the next hypercall revision be moved to their own driver.

Discussed with pooka@
2014-07-09 23:41:40 +00:00

340 lines
7.1 KiB
C

/*
* 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.
*/
/* NOTE this code will move to a new driver in the next hypercall revision */
#include "rumpuser_port.h"
#if !defined(lint)
__RCSID("$NetBSD: rumpuser_file.c,v 1.1 2014/07/09 23:41:40 justin Exp $");
#endif /* !lint */
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef __NetBSD__
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#endif
#if defined(__NetBSD__) || defined(__FreeBSD__) || \
defined(__DragonFly__) || defined(__APPLE__)
#define __BSD__
#endif
#if defined(__BSD__)
#include <sys/sysctl.h>
#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rump/rumpuser.h>
#include "rumpuser_int.h"
int
rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp)
{
struct stat sb;
uint64_t size = 0;
int needsdev = 0, rv = 0, ft = 0;
int fd = -1;
if (stat(path, &sb) == -1) {
rv = errno;
goto out;
}
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) {
rv = errno;
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");
rv = EOPNOTSUPP;
goto out;
#else
struct disklabel lab;
struct partition *parta;
struct dkwedge_info dkw;
fd = open(path, O_RDONLY);
if (fd == -1) {
rv = errno;
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;
}
rv = errno;
#endif /* __NetBSD__ */
}
out:
if (rv == 0 && sizep)
*sizep = size;
if (rv == 0 && ftp)
*ftp = ft;
if (fd != -1)
close(fd);
ET(rv);
}
int
rumpuser_open(const char *path, int ruflags, int *fdp)
{
int fd, flags, rv;
switch (ruflags & RUMPUSER_OPEN_ACCMODE) {
case RUMPUSER_OPEN_RDONLY:
flags = O_RDONLY;
break;
case RUMPUSER_OPEN_WRONLY:
flags = O_WRONLY;
break;
case RUMPUSER_OPEN_RDWR:
flags = O_RDWR;
break;
default:
rv = EINVAL;
goto out;
}
#define TESTSET(_ru_, _h_) if (ruflags & _ru_) flags |= _h_;
TESTSET(RUMPUSER_OPEN_CREATE, O_CREAT);
TESTSET(RUMPUSER_OPEN_EXCL, O_EXCL);
#undef TESTSET
KLOCK_WRAP(fd = open(path, flags, 0644));
if (fd == -1) {
rv = errno;
} else {
*fdp = fd;
rv = 0;
}
out:
ET(rv);
}
int
rumpuser_close(int fd)
{
int nlocks;
rumpkern_unsched(&nlocks, NULL);
fsync(fd);
close(fd);
rumpkern_sched(nlocks, NULL);
ET(0);
}
/*
* Assume "struct rumpuser_iovec" and "struct iovec" are the same.
* If you encounter POSIX platforms where they aren't, add some
* translation for iovlen > 1.
*/
int
rumpuser_iovread(int fd, struct rumpuser_iovec *ruiov, size_t iovlen,
int64_t roff, size_t *retp)
{
struct iovec *iov = (struct iovec *)ruiov;
off_t off = (off_t)roff;
ssize_t nn;
int rv;
if (off == RUMPUSER_IOV_NOSEEK) {
KLOCK_WRAP(nn = readv(fd, iov, iovlen));
} else {
int nlocks;
rumpkern_unsched(&nlocks, NULL);
if (lseek(fd, off, SEEK_SET) == off) {
nn = readv(fd, iov, iovlen);
} else {
nn = -1;
}
rumpkern_sched(nlocks, NULL);
}
if (nn == -1) {
rv = errno;
} else {
*retp = (size_t)nn;
rv = 0;
}
ET(rv);
}
int
rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen,
int64_t roff, size_t *retp)
{
const struct iovec *iov = (const struct iovec *)ruiov;
off_t off = (off_t)roff;
ssize_t nn;
int rv;
if (off == RUMPUSER_IOV_NOSEEK) {
KLOCK_WRAP(nn = writev(fd, iov, iovlen));
} else {
int nlocks;
rumpkern_unsched(&nlocks, NULL);
if (lseek(fd, off, SEEK_SET) == off) {
nn = writev(fd, iov, iovlen);
} else {
nn = -1;
}
rumpkern_sched(nlocks, NULL);
}
if (nn == -1) {
rv = errno;
} else {
*retp = (size_t)nn;
rv = 0;
}
ET(rv);
}
int
rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len)
{
int rv = 0;
/*
* For now, assume fd is regular file and does not care
* about read syncing
*/
if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) {
rv = EINVAL;
goto out;
}
if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) {
rv = 0;
goto out;
}
#ifdef __NetBSD__
{
int fsflags = FDATASYNC;
if (fsflags & RUMPUSER_SYNCFD_SYNC)
fsflags |= FDISKSYNC;
if (fsync_range(fd, fsflags, start, len) == -1)
rv = errno;
}
#else
/* el-simplo */
if (fsync(fd) == -1)
rv = errno;
#endif
out:
ET(rv);
}