1 1.5 ozaki /* $NetBSD: rumpuser_file.c,v 1.5 2024/10/16 09:09:39 ozaki-r Exp $ */ 2 1.2 pooka 3 1.1 justin /* 4 1.1 justin * Copyright (c) 2007-2010 Antti Kantee. All Rights Reserved. 5 1.1 justin * 6 1.1 justin * Redistribution and use in source and binary forms, with or without 7 1.1 justin * modification, are permitted provided that the following conditions 8 1.1 justin * are met: 9 1.1 justin * 1. Redistributions of source code must retain the above copyright 10 1.1 justin * notice, this list of conditions and the following disclaimer. 11 1.1 justin * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 justin * notice, this list of conditions and the following disclaimer in the 13 1.1 justin * documentation and/or other materials provided with the distribution. 14 1.1 justin * 15 1.1 justin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 1.1 justin * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 1.1 justin * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 1.1 justin * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 1.1 justin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 1.1 justin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 1.1 justin * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 1.1 justin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 1.1 justin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 1.1 justin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 1.1 justin * SUCH DAMAGE. 26 1.1 justin */ 27 1.1 justin 28 1.1 justin /* NOTE this code will move to a new driver in the next hypercall revision */ 29 1.1 justin 30 1.1 justin #include "rumpuser_port.h" 31 1.1 justin 32 1.1 justin #if !defined(lint) 33 1.5 ozaki __RCSID("$NetBSD: rumpuser_file.c,v 1.5 2024/10/16 09:09:39 ozaki-r Exp $"); 34 1.1 justin #endif /* !lint */ 35 1.1 justin 36 1.1 justin #include <sys/ioctl.h> 37 1.1 justin #include <sys/mman.h> 38 1.1 justin #include <sys/uio.h> 39 1.1 justin #include <sys/stat.h> 40 1.1 justin #include <sys/time.h> 41 1.1 justin #include <sys/types.h> 42 1.1 justin 43 1.3 pooka #if defined(HAVE_SYS_DISK_H) 44 1.1 justin #include <sys/disk.h> 45 1.3 pooka #endif 46 1.3 pooka #if defined(HAVE_SYS_DISKLABEL_H) 47 1.1 justin #include <sys/disklabel.h> 48 1.3 pooka #endif 49 1.3 pooka #if defined(HAVE_SYS_DKIO_H) 50 1.1 justin #include <sys/dkio.h> 51 1.1 justin #endif 52 1.1 justin 53 1.3 pooka #if defined(HAVE_SYS_SYSCTL_H) 54 1.1 justin #include <sys/sysctl.h> 55 1.1 justin #endif 56 1.1 justin 57 1.1 justin #include <assert.h> 58 1.1 justin #include <errno.h> 59 1.1 justin #include <fcntl.h> 60 1.1 justin #include <netdb.h> 61 1.1 justin #include <stdarg.h> 62 1.1 justin #include <stdint.h> 63 1.1 justin #include <stdio.h> 64 1.1 justin #include <stdlib.h> 65 1.1 justin #include <string.h> 66 1.1 justin #include <unistd.h> 67 1.1 justin 68 1.1 justin #include <rump/rumpuser.h> 69 1.1 justin 70 1.1 justin #include "rumpuser_int.h" 71 1.1 justin 72 1.1 justin int 73 1.1 justin rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp) 74 1.1 justin { 75 1.1 justin struct stat sb; 76 1.1 justin uint64_t size = 0; 77 1.1 justin int needsdev = 0, rv = 0, ft = 0; 78 1.1 justin int fd = -1; 79 1.1 justin 80 1.1 justin if (stat(path, &sb) == -1) { 81 1.1 justin rv = errno; 82 1.1 justin goto out; 83 1.1 justin } 84 1.1 justin 85 1.1 justin switch (sb.st_mode & S_IFMT) { 86 1.1 justin case S_IFDIR: 87 1.1 justin ft = RUMPUSER_FT_DIR; 88 1.1 justin break; 89 1.1 justin case S_IFREG: 90 1.1 justin ft = RUMPUSER_FT_REG; 91 1.1 justin break; 92 1.1 justin case S_IFBLK: 93 1.1 justin ft = RUMPUSER_FT_BLK; 94 1.1 justin needsdev = 1; 95 1.1 justin break; 96 1.1 justin case S_IFCHR: 97 1.1 justin ft = RUMPUSER_FT_CHR; 98 1.1 justin needsdev = 1; 99 1.1 justin break; 100 1.1 justin default: 101 1.1 justin ft = RUMPUSER_FT_OTHER; 102 1.1 justin break; 103 1.1 justin } 104 1.1 justin 105 1.1 justin if (!needsdev) { 106 1.1 justin size = sb.st_size; 107 1.1 justin } else if (sizep) { 108 1.1 justin /* 109 1.1 justin * Welcome to the jungle. Of course querying the kernel 110 1.1 justin * for a device partition size is supposed to be far from 111 1.1 justin * trivial. On NetBSD we use ioctl. On $other platform 112 1.1 justin * we have a problem. We try "the lseek trick" and just 113 1.1 justin * fail if that fails. Platform specific code can later 114 1.1 justin * be written here if appropriate. 115 1.1 justin * 116 1.1 justin * On NetBSD we hope and pray that for block devices nobody 117 1.1 justin * else is holding them open, because otherwise the kernel 118 1.1 justin * will not permit us to open it. Thankfully, this is 119 1.1 justin * usually called only in bootstrap and then we can 120 1.1 justin * forget about it. 121 1.1 justin */ 122 1.1 justin 123 1.1 justin fd = open(path, O_RDONLY); 124 1.1 justin if (fd == -1) { 125 1.1 justin rv = errno; 126 1.1 justin goto out; 127 1.1 justin } 128 1.1 justin 129 1.4 pooka #if (!defined(DIOCGDINFO) || !defined(DISKPART)) && !defined(DIOCGWEDGEINFO) 130 1.3 pooka { 131 1.3 pooka off_t off = lseek(fd, 0, SEEK_END); 132 1.1 justin if (off != 0) { 133 1.1 justin size = off; 134 1.1 justin goto out; 135 1.1 justin } 136 1.1 justin fprintf(stderr, "error: device size query not implemented on " 137 1.1 justin "this platform\n"); 138 1.1 justin rv = EOPNOTSUPP; 139 1.1 justin goto out; 140 1.3 pooka } 141 1.1 justin #else 142 1.3 pooka 143 1.4 pooka #if defined(DIOCGDINFO) && defined(DISKPART) 144 1.3 pooka { 145 1.1 justin struct disklabel lab; 146 1.1 justin struct partition *parta; 147 1.1 justin if (ioctl(fd, DIOCGDINFO, &lab) == 0) { 148 1.1 justin parta = &lab.d_partitions[DISKPART(sb.st_rdev)]; 149 1.1 justin size = (uint64_t)lab.d_secsize * parta->p_size; 150 1.1 justin goto out; 151 1.1 justin } 152 1.3 pooka } 153 1.3 pooka #endif 154 1.1 justin 155 1.3 pooka #if defined(DIOCGWEDGEINFO) 156 1.3 pooka { 157 1.3 pooka struct dkwedge_info dkw; 158 1.1 justin if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) { 159 1.1 justin /* 160 1.1 justin * XXX: should use DIOCGDISKINFO to query 161 1.1 justin * sector size, but that requires proplib, 162 1.1 justin * so just don't bother for now. it's nice 163 1.1 justin * that something as difficult as figuring out 164 1.1 justin * a partition's size has been made so easy. 165 1.1 justin */ 166 1.1 justin size = dkw.dkw_size << DEV_BSHIFT; 167 1.1 justin goto out; 168 1.1 justin } 169 1.3 pooka } 170 1.3 pooka #endif 171 1.1 justin rv = errno; 172 1.3 pooka #endif 173 1.1 justin } 174 1.1 justin 175 1.1 justin out: 176 1.1 justin if (rv == 0 && sizep) 177 1.1 justin *sizep = size; 178 1.1 justin if (rv == 0 && ftp) 179 1.1 justin *ftp = ft; 180 1.1 justin if (fd != -1) 181 1.1 justin close(fd); 182 1.1 justin 183 1.1 justin ET(rv); 184 1.1 justin } 185 1.1 justin 186 1.1 justin int 187 1.1 justin rumpuser_open(const char *path, int ruflags, int *fdp) 188 1.1 justin { 189 1.1 justin int fd, flags, rv; 190 1.1 justin 191 1.1 justin switch (ruflags & RUMPUSER_OPEN_ACCMODE) { 192 1.1 justin case RUMPUSER_OPEN_RDONLY: 193 1.1 justin flags = O_RDONLY; 194 1.1 justin break; 195 1.1 justin case RUMPUSER_OPEN_WRONLY: 196 1.1 justin flags = O_WRONLY; 197 1.1 justin break; 198 1.1 justin case RUMPUSER_OPEN_RDWR: 199 1.1 justin flags = O_RDWR; 200 1.1 justin break; 201 1.1 justin default: 202 1.1 justin rv = EINVAL; 203 1.1 justin goto out; 204 1.1 justin } 205 1.1 justin 206 1.1 justin #define TESTSET(_ru_, _h_) if (ruflags & _ru_) flags |= _h_; 207 1.1 justin TESTSET(RUMPUSER_OPEN_CREATE, O_CREAT); 208 1.1 justin TESTSET(RUMPUSER_OPEN_EXCL, O_EXCL); 209 1.1 justin #undef TESTSET 210 1.1 justin 211 1.1 justin KLOCK_WRAP(fd = open(path, flags, 0644)); 212 1.1 justin if (fd == -1) { 213 1.1 justin rv = errno; 214 1.1 justin } else { 215 1.1 justin *fdp = fd; 216 1.1 justin rv = 0; 217 1.1 justin } 218 1.1 justin 219 1.1 justin out: 220 1.1 justin ET(rv); 221 1.1 justin } 222 1.1 justin 223 1.1 justin int 224 1.1 justin rumpuser_close(int fd) 225 1.1 justin { 226 1.1 justin int nlocks; 227 1.1 justin 228 1.1 justin rumpkern_unsched(&nlocks, NULL); 229 1.1 justin fsync(fd); 230 1.1 justin close(fd); 231 1.1 justin rumpkern_sched(nlocks, NULL); 232 1.1 justin 233 1.1 justin ET(0); 234 1.1 justin } 235 1.1 justin 236 1.1 justin /* 237 1.1 justin * Assume "struct rumpuser_iovec" and "struct iovec" are the same. 238 1.1 justin * If you encounter POSIX platforms where they aren't, add some 239 1.1 justin * translation for iovlen > 1. 240 1.1 justin */ 241 1.1 justin int 242 1.1 justin rumpuser_iovread(int fd, struct rumpuser_iovec *ruiov, size_t iovlen, 243 1.1 justin int64_t roff, size_t *retp) 244 1.1 justin { 245 1.1 justin struct iovec *iov = (struct iovec *)ruiov; 246 1.1 justin off_t off = (off_t)roff; 247 1.1 justin ssize_t nn; 248 1.1 justin int rv; 249 1.1 justin 250 1.1 justin if (off == RUMPUSER_IOV_NOSEEK) { 251 1.1 justin KLOCK_WRAP(nn = readv(fd, iov, iovlen)); 252 1.1 justin } else { 253 1.5 ozaki #ifdef HAVE_PREADV 254 1.5 ozaki KLOCK_WRAP(nn = preadv(fd, iov, iovlen, off)); 255 1.5 ozaki #else 256 1.1 justin int nlocks; 257 1.1 justin 258 1.1 justin rumpkern_unsched(&nlocks, NULL); 259 1.1 justin if (lseek(fd, off, SEEK_SET) == off) { 260 1.1 justin nn = readv(fd, iov, iovlen); 261 1.1 justin } else { 262 1.1 justin nn = -1; 263 1.1 justin } 264 1.1 justin rumpkern_sched(nlocks, NULL); 265 1.5 ozaki #endif 266 1.1 justin } 267 1.1 justin 268 1.1 justin if (nn == -1) { 269 1.1 justin rv = errno; 270 1.1 justin } else { 271 1.1 justin *retp = (size_t)nn; 272 1.1 justin rv = 0; 273 1.1 justin } 274 1.1 justin 275 1.1 justin ET(rv); 276 1.1 justin } 277 1.1 justin 278 1.1 justin int 279 1.1 justin rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen, 280 1.1 justin int64_t roff, size_t *retp) 281 1.1 justin { 282 1.1 justin const struct iovec *iov = (const struct iovec *)ruiov; 283 1.1 justin off_t off = (off_t)roff; 284 1.1 justin ssize_t nn; 285 1.1 justin int rv; 286 1.1 justin 287 1.1 justin if (off == RUMPUSER_IOV_NOSEEK) { 288 1.1 justin KLOCK_WRAP(nn = writev(fd, iov, iovlen)); 289 1.1 justin } else { 290 1.5 ozaki #ifdef HAVE_PWRITEV 291 1.5 ozaki KLOCK_WRAP(nn = pwritev(fd, iov, iovlen, off)); 292 1.5 ozaki #else 293 1.1 justin int nlocks; 294 1.1 justin 295 1.1 justin rumpkern_unsched(&nlocks, NULL); 296 1.1 justin if (lseek(fd, off, SEEK_SET) == off) { 297 1.1 justin nn = writev(fd, iov, iovlen); 298 1.1 justin } else { 299 1.1 justin nn = -1; 300 1.1 justin } 301 1.1 justin rumpkern_sched(nlocks, NULL); 302 1.5 ozaki #endif 303 1.1 justin } 304 1.1 justin 305 1.1 justin if (nn == -1) { 306 1.1 justin rv = errno; 307 1.1 justin } else { 308 1.1 justin *retp = (size_t)nn; 309 1.1 justin rv = 0; 310 1.1 justin } 311 1.1 justin 312 1.1 justin ET(rv); 313 1.1 justin } 314 1.1 justin 315 1.1 justin int 316 1.1 justin rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len) 317 1.1 justin { 318 1.1 justin int rv = 0; 319 1.1 justin 320 1.1 justin /* 321 1.1 justin * For now, assume fd is regular file and does not care 322 1.1 justin * about read syncing 323 1.1 justin */ 324 1.1 justin if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) { 325 1.1 justin rv = EINVAL; 326 1.1 justin goto out; 327 1.1 justin } 328 1.1 justin if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) { 329 1.1 justin rv = 0; 330 1.1 justin goto out; 331 1.1 justin } 332 1.1 justin 333 1.3 pooka #if defined(HAVE_FSYNC_RANGE) 334 1.1 justin { 335 1.1 justin int fsflags = FDATASYNC; 336 1.1 justin 337 1.1 justin if (fsflags & RUMPUSER_SYNCFD_SYNC) 338 1.1 justin fsflags |= FDISKSYNC; 339 1.1 justin if (fsync_range(fd, fsflags, start, len) == -1) 340 1.1 justin rv = errno; 341 1.1 justin } 342 1.1 justin #else 343 1.1 justin /* el-simplo */ 344 1.1 justin if (fsync(fd) == -1) 345 1.1 justin rv = errno; 346 1.1 justin #endif 347 1.1 justin 348 1.1 justin out: 349 1.1 justin ET(rv); 350 1.1 justin } 351