xref: /netbsd-src/lib/librumpuser/rumpuser_file.c (revision b5159575adf315bed658e6d48d1655857c2d6f1f)
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