Home | History | Annotate | Line # | Download | only in librumpuser
      1 /*	$NetBSD: rumpuser_file.c,v 1.5 2024/10/16 09:09:39 ozaki-r 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.5 2024/10/16 09:09:39 ozaki-r 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 #ifdef HAVE_PREADV
    254 		KLOCK_WRAP(nn = preadv(fd, iov, iovlen, off));
    255 #else
    256 		int nlocks;
    257 
    258 		rumpkern_unsched(&nlocks, NULL);
    259 		if (lseek(fd, off, SEEK_SET) == off) {
    260 			nn = readv(fd, iov, iovlen);
    261 		} else {
    262 			nn = -1;
    263 		}
    264 		rumpkern_sched(nlocks, NULL);
    265 #endif
    266 	}
    267 
    268 	if (nn == -1) {
    269 		rv = errno;
    270 	} else {
    271 		*retp = (size_t)nn;
    272 		rv = 0;
    273 	}
    274 
    275 	ET(rv);
    276 }
    277 
    278 int
    279 rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen,
    280 	int64_t roff, size_t *retp)
    281 {
    282 	const struct iovec *iov = (const struct iovec *)ruiov;
    283 	off_t off = (off_t)roff;
    284 	ssize_t nn;
    285 	int rv;
    286 
    287 	if (off == RUMPUSER_IOV_NOSEEK) {
    288 		KLOCK_WRAP(nn = writev(fd, iov, iovlen));
    289 	} else {
    290 #ifdef HAVE_PWRITEV
    291 		KLOCK_WRAP(nn = pwritev(fd, iov, iovlen, off));
    292 #else
    293 		int nlocks;
    294 
    295 		rumpkern_unsched(&nlocks, NULL);
    296 		if (lseek(fd, off, SEEK_SET) == off) {
    297 			nn = writev(fd, iov, iovlen);
    298 		} else {
    299 			nn = -1;
    300 		}
    301 		rumpkern_sched(nlocks, NULL);
    302 #endif
    303 	}
    304 
    305 	if (nn == -1) {
    306 		rv = errno;
    307 	} else {
    308 		*retp = (size_t)nn;
    309 		rv = 0;
    310 	}
    311 
    312 	ET(rv);
    313 }
    314 
    315 int
    316 rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len)
    317 {
    318 	int rv = 0;
    319 
    320 	/*
    321 	 * For now, assume fd is regular file and does not care
    322 	 * about read syncing
    323 	 */
    324 	if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) {
    325 		rv = EINVAL;
    326 		goto out;
    327 	}
    328 	if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) {
    329 		rv = 0;
    330 		goto out;
    331 	}
    332 
    333 #if defined(HAVE_FSYNC_RANGE)
    334 	{
    335 	int fsflags = FDATASYNC;
    336 
    337 	if (fsflags & RUMPUSER_SYNCFD_SYNC)
    338 		fsflags |= FDISKSYNC;
    339 	if (fsync_range(fd, fsflags, start, len) == -1)
    340 		rv = errno;
    341 	}
    342 #else
    343 	/* el-simplo */
    344 	if (fsync(fd) == -1)
    345 		rv = errno;
    346 #endif
    347 
    348  out:
    349 	ET(rv);
    350 }
    351