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