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