1 /* $NetBSD: rumpuser_file.c,v 1.2 2014/08/24 14:37:31 pooka 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.2 2014/08/24 14:37:31 pooka 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 #ifdef __NetBSD__ 44 #include <sys/disk.h> 45 #include <sys/disklabel.h> 46 #include <sys/dkio.h> 47 #endif 48 49 #if defined(__NetBSD__) || defined(__FreeBSD__) || \ 50 defined(__DragonFly__) || defined(__APPLE__) 51 #define __BSD__ 52 #endif 53 54 #if defined(__BSD__) 55 #include <sys/sysctl.h> 56 #endif 57 58 #include <assert.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <netdb.h> 62 #include <stdarg.h> 63 #include <stdint.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <unistd.h> 68 69 #include <rump/rumpuser.h> 70 71 #include "rumpuser_int.h" 72 73 int 74 rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp) 75 { 76 struct stat sb; 77 uint64_t size = 0; 78 int needsdev = 0, rv = 0, ft = 0; 79 int fd = -1; 80 81 if (stat(path, &sb) == -1) { 82 rv = errno; 83 goto out; 84 } 85 86 switch (sb.st_mode & S_IFMT) { 87 case S_IFDIR: 88 ft = RUMPUSER_FT_DIR; 89 break; 90 case S_IFREG: 91 ft = RUMPUSER_FT_REG; 92 break; 93 case S_IFBLK: 94 ft = RUMPUSER_FT_BLK; 95 needsdev = 1; 96 break; 97 case S_IFCHR: 98 ft = RUMPUSER_FT_CHR; 99 needsdev = 1; 100 break; 101 default: 102 ft = RUMPUSER_FT_OTHER; 103 break; 104 } 105 106 if (!needsdev) { 107 size = sb.st_size; 108 } else if (sizep) { 109 /* 110 * Welcome to the jungle. Of course querying the kernel 111 * for a device partition size is supposed to be far from 112 * trivial. On NetBSD we use ioctl. On $other platform 113 * we have a problem. We try "the lseek trick" and just 114 * fail if that fails. Platform specific code can later 115 * be written here if appropriate. 116 * 117 * On NetBSD we hope and pray that for block devices nobody 118 * else is holding them open, because otherwise the kernel 119 * will not permit us to open it. Thankfully, this is 120 * usually called only in bootstrap and then we can 121 * forget about it. 122 */ 123 #ifndef __NetBSD__ 124 off_t off; 125 126 fd = open(path, O_RDONLY); 127 if (fd == -1) { 128 rv = errno; 129 goto out; 130 } 131 132 off = lseek(fd, 0, SEEK_END); 133 if (off != 0) { 134 size = off; 135 goto out; 136 } 137 fprintf(stderr, "error: device size query not implemented on " 138 "this platform\n"); 139 rv = EOPNOTSUPP; 140 goto out; 141 #else 142 struct disklabel lab; 143 struct partition *parta; 144 struct dkwedge_info dkw; 145 146 fd = open(path, O_RDONLY); 147 if (fd == -1) { 148 rv = errno; 149 goto out; 150 } 151 152 if (ioctl(fd, DIOCGDINFO, &lab) == 0) { 153 parta = &lab.d_partitions[DISKPART(sb.st_rdev)]; 154 size = (uint64_t)lab.d_secsize * parta->p_size; 155 goto out; 156 } 157 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 rv = errno; 171 #endif /* __NetBSD__ */ 172 } 173 174 out: 175 if (rv == 0 && sizep) 176 *sizep = size; 177 if (rv == 0 && ftp) 178 *ftp = ft; 179 if (fd != -1) 180 close(fd); 181 182 ET(rv); 183 } 184 185 int 186 rumpuser_open(const char *path, int ruflags, int *fdp) 187 { 188 int fd, flags, rv; 189 190 switch (ruflags & RUMPUSER_OPEN_ACCMODE) { 191 case RUMPUSER_OPEN_RDONLY: 192 flags = O_RDONLY; 193 break; 194 case RUMPUSER_OPEN_WRONLY: 195 flags = O_WRONLY; 196 break; 197 case RUMPUSER_OPEN_RDWR: 198 flags = O_RDWR; 199 break; 200 default: 201 rv = EINVAL; 202 goto out; 203 } 204 205 #define TESTSET(_ru_, _h_) if (ruflags & _ru_) flags |= _h_; 206 TESTSET(RUMPUSER_OPEN_CREATE, O_CREAT); 207 TESTSET(RUMPUSER_OPEN_EXCL, O_EXCL); 208 #undef TESTSET 209 210 KLOCK_WRAP(fd = open(path, flags, 0644)); 211 if (fd == -1) { 212 rv = errno; 213 } else { 214 *fdp = fd; 215 rv = 0; 216 } 217 218 out: 219 ET(rv); 220 } 221 222 int 223 rumpuser_close(int fd) 224 { 225 int nlocks; 226 227 rumpkern_unsched(&nlocks, NULL); 228 fsync(fd); 229 close(fd); 230 rumpkern_sched(nlocks, NULL); 231 232 ET(0); 233 } 234 235 /* 236 * Assume "struct rumpuser_iovec" and "struct iovec" are the same. 237 * If you encounter POSIX platforms where they aren't, add some 238 * translation for iovlen > 1. 239 */ 240 int 241 rumpuser_iovread(int fd, struct rumpuser_iovec *ruiov, size_t iovlen, 242 int64_t roff, size_t *retp) 243 { 244 struct iovec *iov = (struct iovec *)ruiov; 245 off_t off = (off_t)roff; 246 ssize_t nn; 247 int rv; 248 249 if (off == RUMPUSER_IOV_NOSEEK) { 250 KLOCK_WRAP(nn = readv(fd, iov, iovlen)); 251 } else { 252 int nlocks; 253 254 rumpkern_unsched(&nlocks, NULL); 255 if (lseek(fd, off, SEEK_SET) == off) { 256 nn = readv(fd, iov, iovlen); 257 } else { 258 nn = -1; 259 } 260 rumpkern_sched(nlocks, NULL); 261 } 262 263 if (nn == -1) { 264 rv = errno; 265 } else { 266 *retp = (size_t)nn; 267 rv = 0; 268 } 269 270 ET(rv); 271 } 272 273 int 274 rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen, 275 int64_t roff, size_t *retp) 276 { 277 const struct iovec *iov = (const struct iovec *)ruiov; 278 off_t off = (off_t)roff; 279 ssize_t nn; 280 int rv; 281 282 if (off == RUMPUSER_IOV_NOSEEK) { 283 KLOCK_WRAP(nn = writev(fd, iov, iovlen)); 284 } else { 285 int nlocks; 286 287 rumpkern_unsched(&nlocks, NULL); 288 if (lseek(fd, off, SEEK_SET) == off) { 289 nn = writev(fd, iov, iovlen); 290 } else { 291 nn = -1; 292 } 293 rumpkern_sched(nlocks, NULL); 294 } 295 296 if (nn == -1) { 297 rv = errno; 298 } else { 299 *retp = (size_t)nn; 300 rv = 0; 301 } 302 303 ET(rv); 304 } 305 306 int 307 rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len) 308 { 309 int rv = 0; 310 311 /* 312 * For now, assume fd is regular file and does not care 313 * about read syncing 314 */ 315 if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) { 316 rv = EINVAL; 317 goto out; 318 } 319 if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) { 320 rv = 0; 321 goto out; 322 } 323 324 #ifdef __NetBSD__ 325 { 326 int fsflags = FDATASYNC; 327 328 if (fsflags & RUMPUSER_SYNCFD_SYNC) 329 fsflags |= FDISKSYNC; 330 if (fsync_range(fd, fsflags, start, len) == -1) 331 rv = errno; 332 } 333 #else 334 /* el-simplo */ 335 if (fsync(fd) == -1) 336 rv = errno; 337 #endif 338 339 out: 340 ET(rv); 341 } 342