Home | History | Annotate | Line # | Download | only in librumpuser
rumpuser_file.c revision 1.4
      1 /*	$NetBSD: rumpuser_file.c,v 1.4 2014/11/04 21:08:12 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 /* NOTE this code will move to a new driver in the next hypercall revision */
     29 
     30 #include "rumpuser_port.h"
     31 
     32 #if !defined(lint)
     33 __RCSID("$NetBSD: rumpuser_file.c,v 1.4 2014/11/04 21:08:12 pooka Exp $");
     34 #endif /* !lint */
     35 
     36 #include <sys/ioctl.h>
     37 #include <sys/mman.h>
     38 #include <sys/uio.h>
     39 #include <sys/stat.h>
     40 #include <sys/time.h>
     41 #include <sys/types.h>
     42 
     43 #if defined(HAVE_SYS_DISK_H)
     44 #include <sys/disk.h>
     45 #endif
     46 #if defined(HAVE_SYS_DISKLABEL_H)
     47 #include <sys/disklabel.h>
     48 #endif
     49 #if defined(HAVE_SYS_DKIO_H)
     50 #include <sys/dkio.h>
     51 #endif
     52 
     53 #if defined(HAVE_SYS_SYSCTL_H)
     54 #include <sys/sysctl.h>
     55 #endif
     56 
     57 #include <assert.h>
     58 #include <errno.h>
     59 #include <fcntl.h>
     60 #include <netdb.h>
     61 #include <stdarg.h>
     62 #include <stdint.h>
     63 #include <stdio.h>
     64 #include <stdlib.h>
     65 #include <string.h>
     66 #include <unistd.h>
     67 
     68 #include <rump/rumpuser.h>
     69 
     70 #include "rumpuser_int.h"
     71 
     72 int
     73 rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp)
     74 {
     75 	struct stat sb;
     76 	uint64_t size = 0;
     77 	int needsdev = 0, rv = 0, ft = 0;
     78 	int fd = -1;
     79 
     80 	if (stat(path, &sb) == -1) {
     81 		rv = errno;
     82 		goto out;
     83 	}
     84 
     85 	switch (sb.st_mode & S_IFMT) {
     86 	case S_IFDIR:
     87 		ft = RUMPUSER_FT_DIR;
     88 		break;
     89 	case S_IFREG:
     90 		ft = RUMPUSER_FT_REG;
     91 		break;
     92 	case S_IFBLK:
     93 		ft = RUMPUSER_FT_BLK;
     94 		needsdev = 1;
     95 		break;
     96 	case S_IFCHR:
     97 		ft = RUMPUSER_FT_CHR;
     98 		needsdev = 1;
     99 		break;
    100 	default:
    101 		ft = RUMPUSER_FT_OTHER;
    102 		break;
    103 	}
    104 
    105 	if (!needsdev) {
    106 		size = sb.st_size;
    107 	} else if (sizep) {
    108 		/*
    109 		 * Welcome to the jungle.  Of course querying the kernel
    110 		 * for a device partition size is supposed to be far from
    111 		 * trivial.  On NetBSD we use ioctl.  On $other platform
    112 		 * we have a problem.  We try "the lseek trick" and just
    113 		 * fail if that fails.  Platform specific code can later
    114 		 * be written here if appropriate.
    115 		 *
    116 		 * On NetBSD we hope and pray that for block devices nobody
    117 		 * else is holding them open, because otherwise the kernel
    118 		 * will not permit us to open it.  Thankfully, this is
    119 		 * usually called only in bootstrap and then we can
    120 		 * forget about it.
    121 		 */
    122 
    123 		fd = open(path, O_RDONLY);
    124 		if (fd == -1) {
    125 			rv = errno;
    126 			goto out;
    127 		}
    128 
    129 #if (!defined(DIOCGDINFO) || !defined(DISKPART)) && !defined(DIOCGWEDGEINFO)
    130 		{
    131 		off_t off = lseek(fd, 0, SEEK_END);
    132 		if (off != 0) {
    133 			size = off;
    134 			goto out;
    135 		}
    136 		fprintf(stderr, "error: device size query not implemented on "
    137 		    "this platform\n");
    138 		rv = EOPNOTSUPP;
    139 		goto out;
    140 		}
    141 #else
    142 
    143 #if defined(DIOCGDINFO) && defined(DISKPART)
    144 		{
    145 		struct disklabel lab;
    146 		struct partition *parta;
    147 		if (ioctl(fd, DIOCGDINFO, &lab) == 0) {
    148 			parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
    149 			size = (uint64_t)lab.d_secsize * parta->p_size;
    150 			goto out;
    151 		}
    152 		}
    153 #endif
    154 
    155 #if defined(DIOCGWEDGEINFO)
    156 		{
    157 		struct dkwedge_info dkw;
    158 		if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
    159 			/*
    160 			 * XXX: should use DIOCGDISKINFO to query
    161 			 * sector size, but that requires proplib,
    162 			 * so just don't bother for now.  it's nice
    163 			 * that something as difficult as figuring out
    164 			 * a partition's size has been made so easy.
    165 			 */
    166 			size = dkw.dkw_size << DEV_BSHIFT;
    167 			goto out;
    168 		}
    169 		}
    170 #endif
    171 		rv = errno;
    172 #endif
    173 	}
    174 
    175  out:
    176 	if (rv == 0 && sizep)
    177 		*sizep = size;
    178 	if (rv == 0 && ftp)
    179 		*ftp = ft;
    180 	if (fd != -1)
    181 		close(fd);
    182 
    183 	ET(rv);
    184 }
    185 
    186 int
    187 rumpuser_open(const char *path, int ruflags, int *fdp)
    188 {
    189 	int fd, flags, rv;
    190 
    191 	switch (ruflags & RUMPUSER_OPEN_ACCMODE) {
    192 	case RUMPUSER_OPEN_RDONLY:
    193 		flags = O_RDONLY;
    194 		break;
    195 	case RUMPUSER_OPEN_WRONLY:
    196 		flags = O_WRONLY;
    197 		break;
    198 	case RUMPUSER_OPEN_RDWR:
    199 		flags = O_RDWR;
    200 		break;
    201 	default:
    202 		rv = EINVAL;
    203 		goto out;
    204 	}
    205 
    206 #define TESTSET(_ru_, _h_) if (ruflags & _ru_) flags |= _h_;
    207 	TESTSET(RUMPUSER_OPEN_CREATE, O_CREAT);
    208 	TESTSET(RUMPUSER_OPEN_EXCL, O_EXCL);
    209 #undef TESTSET
    210 
    211 	KLOCK_WRAP(fd = open(path, flags, 0644));
    212 	if (fd == -1) {
    213 		rv = errno;
    214 	} else {
    215 		*fdp = fd;
    216 		rv = 0;
    217 	}
    218 
    219  out:
    220 	ET(rv);
    221 }
    222 
    223 int
    224 rumpuser_close(int fd)
    225 {
    226 	int nlocks;
    227 
    228 	rumpkern_unsched(&nlocks, NULL);
    229 	fsync(fd);
    230 	close(fd);
    231 	rumpkern_sched(nlocks, NULL);
    232 
    233 	ET(0);
    234 }
    235 
    236 /*
    237  * Assume "struct rumpuser_iovec" and "struct iovec" are the same.
    238  * If you encounter POSIX platforms where they aren't, add some
    239  * translation for iovlen > 1.
    240  */
    241 int
    242 rumpuser_iovread(int fd, struct rumpuser_iovec *ruiov, size_t iovlen,
    243 	int64_t roff, size_t *retp)
    244 {
    245 	struct iovec *iov = (struct iovec *)ruiov;
    246 	off_t off = (off_t)roff;
    247 	ssize_t nn;
    248 	int rv;
    249 
    250 	if (off == RUMPUSER_IOV_NOSEEK) {
    251 		KLOCK_WRAP(nn = readv(fd, iov, iovlen));
    252 	} else {
    253 		int nlocks;
    254 
    255 		rumpkern_unsched(&nlocks, NULL);
    256 		if (lseek(fd, off, SEEK_SET) == off) {
    257 			nn = readv(fd, iov, iovlen);
    258 		} else {
    259 			nn = -1;
    260 		}
    261 		rumpkern_sched(nlocks, NULL);
    262 	}
    263 
    264 	if (nn == -1) {
    265 		rv = errno;
    266 	} else {
    267 		*retp = (size_t)nn;
    268 		rv = 0;
    269 	}
    270 
    271 	ET(rv);
    272 }
    273 
    274 int
    275 rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen,
    276 	int64_t roff, size_t *retp)
    277 {
    278 	const struct iovec *iov = (const struct iovec *)ruiov;
    279 	off_t off = (off_t)roff;
    280 	ssize_t nn;
    281 	int rv;
    282 
    283 	if (off == RUMPUSER_IOV_NOSEEK) {
    284 		KLOCK_WRAP(nn = writev(fd, iov, iovlen));
    285 	} else {
    286 		int nlocks;
    287 
    288 		rumpkern_unsched(&nlocks, NULL);
    289 		if (lseek(fd, off, SEEK_SET) == off) {
    290 			nn = writev(fd, iov, iovlen);
    291 		} else {
    292 			nn = -1;
    293 		}
    294 		rumpkern_sched(nlocks, NULL);
    295 	}
    296 
    297 	if (nn == -1) {
    298 		rv = errno;
    299 	} else {
    300 		*retp = (size_t)nn;
    301 		rv = 0;
    302 	}
    303 
    304 	ET(rv);
    305 }
    306 
    307 int
    308 rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len)
    309 {
    310 	int rv = 0;
    311 
    312 	/*
    313 	 * For now, assume fd is regular file and does not care
    314 	 * about read syncing
    315 	 */
    316 	if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) {
    317 		rv = EINVAL;
    318 		goto out;
    319 	}
    320 	if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) {
    321 		rv = 0;
    322 		goto out;
    323 	}
    324 
    325 #if defined(HAVE_FSYNC_RANGE)
    326 	{
    327 	int fsflags = FDATASYNC;
    328 
    329 	if (fsflags & RUMPUSER_SYNCFD_SYNC)
    330 		fsflags |= FDISKSYNC;
    331 	if (fsync_range(fd, fsflags, start, len) == -1)
    332 		rv = errno;
    333 	}
    334 #else
    335 	/* el-simplo */
    336 	if (fsync(fd) == -1)
    337 		rv = errno;
    338 #endif
    339 
    340  out:
    341 	ET(rv);
    342 }
    343