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