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
rumpuser_getfileinfo(const char * path,uint64_t * sizep,int * ftp)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
rumpuser_open(const char * path,int ruflags,int * fdp)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
rumpuser_close(int fd)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
rumpuser_iovread(int fd,struct rumpuser_iovec * ruiov,size_t iovlen,int64_t roff,size_t * retp)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
rumpuser_iovwrite(int fd,const struct rumpuser_iovec * ruiov,size_t iovlen,int64_t roff,size_t * retp)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
rumpuser_syncfd(int fd,int flags,uint64_t start,uint64_t len)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