xref: /netbsd-src/lib/librumpuser/rumpuser_file.c (revision b5159575adf315bed658e6d48d1655857c2d6f1f)
1*b5159575Sozaki-r /*	$NetBSD: rumpuser_file.c,v 1.5 2024/10/16 09:09:39 ozaki-r Exp $	*/
282dada91Spooka 
34ce85d0bSjustin /*
44ce85d0bSjustin  * Copyright (c) 2007-2010 Antti Kantee.  All Rights Reserved.
54ce85d0bSjustin  *
64ce85d0bSjustin  * Redistribution and use in source and binary forms, with or without
74ce85d0bSjustin  * modification, are permitted provided that the following conditions
84ce85d0bSjustin  * are met:
94ce85d0bSjustin  * 1. Redistributions of source code must retain the above copyright
104ce85d0bSjustin  *    notice, this list of conditions and the following disclaimer.
114ce85d0bSjustin  * 2. Redistributions in binary form must reproduce the above copyright
124ce85d0bSjustin  *    notice, this list of conditions and the following disclaimer in the
134ce85d0bSjustin  *    documentation and/or other materials provided with the distribution.
144ce85d0bSjustin  *
154ce85d0bSjustin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
164ce85d0bSjustin  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
174ce85d0bSjustin  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
184ce85d0bSjustin  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194ce85d0bSjustin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204ce85d0bSjustin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
214ce85d0bSjustin  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224ce85d0bSjustin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234ce85d0bSjustin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244ce85d0bSjustin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254ce85d0bSjustin  * SUCH DAMAGE.
264ce85d0bSjustin  */
274ce85d0bSjustin 
284ce85d0bSjustin /* NOTE this code will move to a new driver in the next hypercall revision */
294ce85d0bSjustin 
304ce85d0bSjustin #include "rumpuser_port.h"
314ce85d0bSjustin 
324ce85d0bSjustin #if !defined(lint)
33*b5159575Sozaki-r __RCSID("$NetBSD: rumpuser_file.c,v 1.5 2024/10/16 09:09:39 ozaki-r Exp $");
344ce85d0bSjustin #endif /* !lint */
354ce85d0bSjustin 
364ce85d0bSjustin #include <sys/ioctl.h>
374ce85d0bSjustin #include <sys/mman.h>
384ce85d0bSjustin #include <sys/uio.h>
394ce85d0bSjustin #include <sys/stat.h>
404ce85d0bSjustin #include <sys/time.h>
414ce85d0bSjustin #include <sys/types.h>
424ce85d0bSjustin 
4323dfcd74Spooka #if defined(HAVE_SYS_DISK_H)
444ce85d0bSjustin #include <sys/disk.h>
4523dfcd74Spooka #endif
4623dfcd74Spooka #if defined(HAVE_SYS_DISKLABEL_H)
474ce85d0bSjustin #include <sys/disklabel.h>
4823dfcd74Spooka #endif
4923dfcd74Spooka #if defined(HAVE_SYS_DKIO_H)
504ce85d0bSjustin #include <sys/dkio.h>
514ce85d0bSjustin #endif
524ce85d0bSjustin 
5323dfcd74Spooka #if defined(HAVE_SYS_SYSCTL_H)
544ce85d0bSjustin #include <sys/sysctl.h>
554ce85d0bSjustin #endif
564ce85d0bSjustin 
574ce85d0bSjustin #include <assert.h>
584ce85d0bSjustin #include <errno.h>
594ce85d0bSjustin #include <fcntl.h>
604ce85d0bSjustin #include <netdb.h>
614ce85d0bSjustin #include <stdarg.h>
624ce85d0bSjustin #include <stdint.h>
634ce85d0bSjustin #include <stdio.h>
644ce85d0bSjustin #include <stdlib.h>
654ce85d0bSjustin #include <string.h>
664ce85d0bSjustin #include <unistd.h>
674ce85d0bSjustin 
684ce85d0bSjustin #include <rump/rumpuser.h>
694ce85d0bSjustin 
704ce85d0bSjustin #include "rumpuser_int.h"
714ce85d0bSjustin 
724ce85d0bSjustin int
734ce85d0bSjustin rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp)
744ce85d0bSjustin {
754ce85d0bSjustin 	struct stat sb;
764ce85d0bSjustin 	uint64_t size = 0;
774ce85d0bSjustin 	int needsdev = 0, rv = 0, ft = 0;
784ce85d0bSjustin 	int fd = -1;
794ce85d0bSjustin 
804ce85d0bSjustin 	if (stat(path, &sb) == -1) {
814ce85d0bSjustin 		rv = errno;
824ce85d0bSjustin 		goto out;
834ce85d0bSjustin 	}
844ce85d0bSjustin 
854ce85d0bSjustin 	switch (sb.st_mode & S_IFMT) {
864ce85d0bSjustin 	case S_IFDIR:
874ce85d0bSjustin 		ft = RUMPUSER_FT_DIR;
884ce85d0bSjustin 		break;
894ce85d0bSjustin 	case S_IFREG:
904ce85d0bSjustin 		ft = RUMPUSER_FT_REG;
914ce85d0bSjustin 		break;
924ce85d0bSjustin 	case S_IFBLK:
934ce85d0bSjustin 		ft = RUMPUSER_FT_BLK;
944ce85d0bSjustin 		needsdev = 1;
954ce85d0bSjustin 		break;
964ce85d0bSjustin 	case S_IFCHR:
974ce85d0bSjustin 		ft = RUMPUSER_FT_CHR;
984ce85d0bSjustin 		needsdev = 1;
994ce85d0bSjustin 		break;
1004ce85d0bSjustin 	default:
1014ce85d0bSjustin 		ft = RUMPUSER_FT_OTHER;
1024ce85d0bSjustin 		break;
1034ce85d0bSjustin 	}
1044ce85d0bSjustin 
1054ce85d0bSjustin 	if (!needsdev) {
1064ce85d0bSjustin 		size = sb.st_size;
1074ce85d0bSjustin 	} else if (sizep) {
1084ce85d0bSjustin 		/*
1094ce85d0bSjustin 		 * Welcome to the jungle.  Of course querying the kernel
1104ce85d0bSjustin 		 * for a device partition size is supposed to be far from
1114ce85d0bSjustin 		 * trivial.  On NetBSD we use ioctl.  On $other platform
1124ce85d0bSjustin 		 * we have a problem.  We try "the lseek trick" and just
1134ce85d0bSjustin 		 * fail if that fails.  Platform specific code can later
1144ce85d0bSjustin 		 * be written here if appropriate.
1154ce85d0bSjustin 		 *
1164ce85d0bSjustin 		 * On NetBSD we hope and pray that for block devices nobody
1174ce85d0bSjustin 		 * else is holding them open, because otherwise the kernel
1184ce85d0bSjustin 		 * will not permit us to open it.  Thankfully, this is
1194ce85d0bSjustin 		 * usually called only in bootstrap and then we can
1204ce85d0bSjustin 		 * forget about it.
1214ce85d0bSjustin 		 */
1224ce85d0bSjustin 
1234ce85d0bSjustin 		fd = open(path, O_RDONLY);
1244ce85d0bSjustin 		if (fd == -1) {
1254ce85d0bSjustin 			rv = errno;
1264ce85d0bSjustin 			goto out;
1274ce85d0bSjustin 		}
1284ce85d0bSjustin 
12912956bdeSpooka #if (!defined(DIOCGDINFO) || !defined(DISKPART)) && !defined(DIOCGWEDGEINFO)
13023dfcd74Spooka 		{
13123dfcd74Spooka 		off_t off = lseek(fd, 0, SEEK_END);
1324ce85d0bSjustin 		if (off != 0) {
1334ce85d0bSjustin 			size = off;
1344ce85d0bSjustin 			goto out;
1354ce85d0bSjustin 		}
1364ce85d0bSjustin 		fprintf(stderr, "error: device size query not implemented on "
1374ce85d0bSjustin 		    "this platform\n");
1384ce85d0bSjustin 		rv = EOPNOTSUPP;
1394ce85d0bSjustin 		goto out;
14023dfcd74Spooka 		}
1414ce85d0bSjustin #else
14223dfcd74Spooka 
14312956bdeSpooka #if defined(DIOCGDINFO) && defined(DISKPART)
14423dfcd74Spooka 		{
1454ce85d0bSjustin 		struct disklabel lab;
1464ce85d0bSjustin 		struct partition *parta;
1474ce85d0bSjustin 		if (ioctl(fd, DIOCGDINFO, &lab) == 0) {
1484ce85d0bSjustin 			parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
1494ce85d0bSjustin 			size = (uint64_t)lab.d_secsize * parta->p_size;
1504ce85d0bSjustin 			goto out;
1514ce85d0bSjustin 		}
15223dfcd74Spooka 		}
15323dfcd74Spooka #endif
1544ce85d0bSjustin 
15523dfcd74Spooka #if defined(DIOCGWEDGEINFO)
15623dfcd74Spooka 		{
15723dfcd74Spooka 		struct dkwedge_info dkw;
1584ce85d0bSjustin 		if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
1594ce85d0bSjustin 			/*
1604ce85d0bSjustin 			 * XXX: should use DIOCGDISKINFO to query
1614ce85d0bSjustin 			 * sector size, but that requires proplib,
1624ce85d0bSjustin 			 * so just don't bother for now.  it's nice
1634ce85d0bSjustin 			 * that something as difficult as figuring out
1644ce85d0bSjustin 			 * a partition's size has been made so easy.
1654ce85d0bSjustin 			 */
1664ce85d0bSjustin 			size = dkw.dkw_size << DEV_BSHIFT;
1674ce85d0bSjustin 			goto out;
1684ce85d0bSjustin 		}
16923dfcd74Spooka 		}
17023dfcd74Spooka #endif
1714ce85d0bSjustin 		rv = errno;
17223dfcd74Spooka #endif
1734ce85d0bSjustin 	}
1744ce85d0bSjustin 
1754ce85d0bSjustin  out:
1764ce85d0bSjustin 	if (rv == 0 && sizep)
1774ce85d0bSjustin 		*sizep = size;
1784ce85d0bSjustin 	if (rv == 0 && ftp)
1794ce85d0bSjustin 		*ftp = ft;
1804ce85d0bSjustin 	if (fd != -1)
1814ce85d0bSjustin 		close(fd);
1824ce85d0bSjustin 
1834ce85d0bSjustin 	ET(rv);
1844ce85d0bSjustin }
1854ce85d0bSjustin 
1864ce85d0bSjustin int
1874ce85d0bSjustin rumpuser_open(const char *path, int ruflags, int *fdp)
1884ce85d0bSjustin {
1894ce85d0bSjustin 	int fd, flags, rv;
1904ce85d0bSjustin 
1914ce85d0bSjustin 	switch (ruflags & RUMPUSER_OPEN_ACCMODE) {
1924ce85d0bSjustin 	case RUMPUSER_OPEN_RDONLY:
1934ce85d0bSjustin 		flags = O_RDONLY;
1944ce85d0bSjustin 		break;
1954ce85d0bSjustin 	case RUMPUSER_OPEN_WRONLY:
1964ce85d0bSjustin 		flags = O_WRONLY;
1974ce85d0bSjustin 		break;
1984ce85d0bSjustin 	case RUMPUSER_OPEN_RDWR:
1994ce85d0bSjustin 		flags = O_RDWR;
2004ce85d0bSjustin 		break;
2014ce85d0bSjustin 	default:
2024ce85d0bSjustin 		rv = EINVAL;
2034ce85d0bSjustin 		goto out;
2044ce85d0bSjustin 	}
2054ce85d0bSjustin 
2064ce85d0bSjustin #define TESTSET(_ru_, _h_) if (ruflags & _ru_) flags |= _h_;
2074ce85d0bSjustin 	TESTSET(RUMPUSER_OPEN_CREATE, O_CREAT);
2084ce85d0bSjustin 	TESTSET(RUMPUSER_OPEN_EXCL, O_EXCL);
2094ce85d0bSjustin #undef TESTSET
2104ce85d0bSjustin 
2114ce85d0bSjustin 	KLOCK_WRAP(fd = open(path, flags, 0644));
2124ce85d0bSjustin 	if (fd == -1) {
2134ce85d0bSjustin 		rv = errno;
2144ce85d0bSjustin 	} else {
2154ce85d0bSjustin 		*fdp = fd;
2164ce85d0bSjustin 		rv = 0;
2174ce85d0bSjustin 	}
2184ce85d0bSjustin 
2194ce85d0bSjustin  out:
2204ce85d0bSjustin 	ET(rv);
2214ce85d0bSjustin }
2224ce85d0bSjustin 
2234ce85d0bSjustin int
2244ce85d0bSjustin rumpuser_close(int fd)
2254ce85d0bSjustin {
2264ce85d0bSjustin 	int nlocks;
2274ce85d0bSjustin 
2284ce85d0bSjustin 	rumpkern_unsched(&nlocks, NULL);
2294ce85d0bSjustin 	fsync(fd);
2304ce85d0bSjustin 	close(fd);
2314ce85d0bSjustin 	rumpkern_sched(nlocks, NULL);
2324ce85d0bSjustin 
2334ce85d0bSjustin 	ET(0);
2344ce85d0bSjustin }
2354ce85d0bSjustin 
2364ce85d0bSjustin /*
2374ce85d0bSjustin  * Assume "struct rumpuser_iovec" and "struct iovec" are the same.
2384ce85d0bSjustin  * If you encounter POSIX platforms where they aren't, add some
2394ce85d0bSjustin  * translation for iovlen > 1.
2404ce85d0bSjustin  */
2414ce85d0bSjustin int
2424ce85d0bSjustin rumpuser_iovread(int fd, struct rumpuser_iovec *ruiov, size_t iovlen,
2434ce85d0bSjustin 	int64_t roff, size_t *retp)
2444ce85d0bSjustin {
2454ce85d0bSjustin 	struct iovec *iov = (struct iovec *)ruiov;
2464ce85d0bSjustin 	off_t off = (off_t)roff;
2474ce85d0bSjustin 	ssize_t nn;
2484ce85d0bSjustin 	int rv;
2494ce85d0bSjustin 
2504ce85d0bSjustin 	if (off == RUMPUSER_IOV_NOSEEK) {
2514ce85d0bSjustin 		KLOCK_WRAP(nn = readv(fd, iov, iovlen));
2524ce85d0bSjustin 	} else {
253*b5159575Sozaki-r #ifdef HAVE_PREADV
254*b5159575Sozaki-r 		KLOCK_WRAP(nn = preadv(fd, iov, iovlen, off));
255*b5159575Sozaki-r #else
2564ce85d0bSjustin 		int nlocks;
2574ce85d0bSjustin 
2584ce85d0bSjustin 		rumpkern_unsched(&nlocks, NULL);
2594ce85d0bSjustin 		if (lseek(fd, off, SEEK_SET) == off) {
2604ce85d0bSjustin 			nn = readv(fd, iov, iovlen);
2614ce85d0bSjustin 		} else {
2624ce85d0bSjustin 			nn = -1;
2634ce85d0bSjustin 		}
2644ce85d0bSjustin 		rumpkern_sched(nlocks, NULL);
265*b5159575Sozaki-r #endif
2664ce85d0bSjustin 	}
2674ce85d0bSjustin 
2684ce85d0bSjustin 	if (nn == -1) {
2694ce85d0bSjustin 		rv = errno;
2704ce85d0bSjustin 	} else {
2714ce85d0bSjustin 		*retp = (size_t)nn;
2724ce85d0bSjustin 		rv = 0;
2734ce85d0bSjustin 	}
2744ce85d0bSjustin 
2754ce85d0bSjustin 	ET(rv);
2764ce85d0bSjustin }
2774ce85d0bSjustin 
2784ce85d0bSjustin int
2794ce85d0bSjustin rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen,
2804ce85d0bSjustin 	int64_t roff, size_t *retp)
2814ce85d0bSjustin {
2824ce85d0bSjustin 	const struct iovec *iov = (const struct iovec *)ruiov;
2834ce85d0bSjustin 	off_t off = (off_t)roff;
2844ce85d0bSjustin 	ssize_t nn;
2854ce85d0bSjustin 	int rv;
2864ce85d0bSjustin 
2874ce85d0bSjustin 	if (off == RUMPUSER_IOV_NOSEEK) {
2884ce85d0bSjustin 		KLOCK_WRAP(nn = writev(fd, iov, iovlen));
2894ce85d0bSjustin 	} else {
290*b5159575Sozaki-r #ifdef HAVE_PWRITEV
291*b5159575Sozaki-r 		KLOCK_WRAP(nn = pwritev(fd, iov, iovlen, off));
292*b5159575Sozaki-r #else
2934ce85d0bSjustin 		int nlocks;
2944ce85d0bSjustin 
2954ce85d0bSjustin 		rumpkern_unsched(&nlocks, NULL);
2964ce85d0bSjustin 		if (lseek(fd, off, SEEK_SET) == off) {
2974ce85d0bSjustin 			nn = writev(fd, iov, iovlen);
2984ce85d0bSjustin 		} else {
2994ce85d0bSjustin 			nn = -1;
3004ce85d0bSjustin 		}
3014ce85d0bSjustin 		rumpkern_sched(nlocks, NULL);
302*b5159575Sozaki-r #endif
3034ce85d0bSjustin 	}
3044ce85d0bSjustin 
3054ce85d0bSjustin 	if (nn == -1) {
3064ce85d0bSjustin 		rv = errno;
3074ce85d0bSjustin 	} else {
3084ce85d0bSjustin 		*retp = (size_t)nn;
3094ce85d0bSjustin 		rv = 0;
3104ce85d0bSjustin 	}
3114ce85d0bSjustin 
3124ce85d0bSjustin 	ET(rv);
3134ce85d0bSjustin }
3144ce85d0bSjustin 
3154ce85d0bSjustin int
3164ce85d0bSjustin rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len)
3174ce85d0bSjustin {
3184ce85d0bSjustin 	int rv = 0;
3194ce85d0bSjustin 
3204ce85d0bSjustin 	/*
3214ce85d0bSjustin 	 * For now, assume fd is regular file and does not care
3224ce85d0bSjustin 	 * about read syncing
3234ce85d0bSjustin 	 */
3244ce85d0bSjustin 	if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) {
3254ce85d0bSjustin 		rv = EINVAL;
3264ce85d0bSjustin 		goto out;
3274ce85d0bSjustin 	}
3284ce85d0bSjustin 	if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) {
3294ce85d0bSjustin 		rv = 0;
3304ce85d0bSjustin 		goto out;
3314ce85d0bSjustin 	}
3324ce85d0bSjustin 
33323dfcd74Spooka #if defined(HAVE_FSYNC_RANGE)
3344ce85d0bSjustin 	{
3354ce85d0bSjustin 	int fsflags = FDATASYNC;
3364ce85d0bSjustin 
3374ce85d0bSjustin 	if (fsflags & RUMPUSER_SYNCFD_SYNC)
3384ce85d0bSjustin 		fsflags |= FDISKSYNC;
3394ce85d0bSjustin 	if (fsync_range(fd, fsflags, start, len) == -1)
3404ce85d0bSjustin 		rv = errno;
3414ce85d0bSjustin 	}
3424ce85d0bSjustin #else
3434ce85d0bSjustin 	/* el-simplo */
3444ce85d0bSjustin 	if (fsync(fd) == -1)
3454ce85d0bSjustin 		rv = errno;
3464ce85d0bSjustin #endif
3474ce85d0bSjustin 
3484ce85d0bSjustin  out:
3494ce85d0bSjustin 	ET(rv);
3504ce85d0bSjustin }
351