xref: /netbsd-src/lib/librumpuser/rumpuser.c (revision 4e6df137e8e14049b5a701d249962c480449c141)
1 /*	$NetBSD: rumpuser.c,v 1.1 2010/02/26 18:54:20 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 #include <sys/cdefs.h>
29 #if !defined(lint)
30 __RCSID("$NetBSD: rumpuser.c,v 1.1 2010/02/26 18:54:20 pooka Exp $");
31 #endif /* !lint */
32 
33 /* thank the maker for this */
34 #ifdef __linux__
35 #define _XOPEN_SOURCE 500
36 #define _BSD_SOURCE
37 #define _FILE_OFFSET_BITS 64
38 #include <features.h>
39 #endif
40 
41 #include <sys/param.h>
42 #include <sys/event.h>
43 #include <sys/ioctl.h>
44 #include <sys/mman.h>
45 #include <sys/uio.h>
46 
47 #ifdef __NetBSD__
48 #include <sys/disklabel.h>
49 #endif
50 
51 #include <assert.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <poll.h>
56 #include <stdarg.h>
57 #include <stdint.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 #include <unistd.h>
63 
64 #include <rump/rumpuser.h>
65 
66 #include "rumpuser_int.h"
67 
68 int
69 rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp, int *error)
70 {
71 	struct stat sb;
72 	uint64_t size;
73 	int needsdev = 0, rv = 0, ft;
74 
75 	if (stat(path, &sb) == -1) {
76 		*error = errno;
77 		return -1;
78 	}
79 
80 	switch (sb.st_mode & S_IFMT) {
81 	case S_IFDIR:
82 		ft = RUMPUSER_FT_DIR;
83 		break;
84 	case S_IFREG:
85 		ft = RUMPUSER_FT_REG;
86 		break;
87 	case S_IFBLK:
88 		ft = RUMPUSER_FT_BLK;
89 		needsdev = 1;
90 		break;
91 	case S_IFCHR:
92 		ft = RUMPUSER_FT_CHR;
93 		needsdev = 1;
94 		break;
95 	default:
96 		ft = RUMPUSER_FT_OTHER;
97 		break;
98 	}
99 
100 	if (!needsdev) {
101 		size = sb.st_size;
102 	} else if (sizep) {
103 		/*
104 		 * Welcome to the jungle.  Of course querying the kernel
105 		 * for a device partition size is supposed to be far from
106 		 * trivial.  On NetBSD we use ioctl.  On $other platform
107 		 * we have a problem.  We try "the lseek trick" and just
108 		 * fail if that fails.  Platform specific code can later
109 		 * be written here if appropriate.
110 		 *
111 		 * On NetBSD we hope and pray that for block devices nobody
112 		 * else is holding them open, because otherwise the kernel
113 		 * will not permit us to open it.  Thankfully, this is
114 		 * usually called only in bootstrap and then we can
115 		 * forget about it.
116 		 */
117 #ifndef __NetBSD__
118 		off_t off;
119 		int fd;
120 
121 		fd = open(path, O_RDONLY);
122 		if (fd == -1) {
123 			*error = errno;
124 			rv = -1;
125 			goto out;
126 		}
127 
128 		off = lseek(fd, 0, SEEK_END);
129 		close(fd);
130 		if (off != 0) {
131 			size = off;
132 			goto out;
133 		}
134 		fprintf(stderr, "error: device size query not implemented on "
135 		    "this platform\n");
136 		*error = EOPNOTSUPP;
137 		rv = -1;
138 		goto out;
139 #else
140 		struct disklabel lab;
141 		struct partition *parta;
142 		int fd;
143 
144 		fd = open(path, O_RDONLY);
145 		if (fd == -1) {
146 			*error = errno;
147 			rv = -1;
148 			goto out;
149 		}
150 
151 		if (ioctl(fd, DIOCGDINFO, &lab) == -1) {
152 			*error = errno;
153 			rv = -1;
154 			goto out;
155 		}
156 		close(fd);
157 
158 		parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
159 		size = (uint64_t)lab.d_secsize * parta->p_size;
160 #endif /* __NetBSD__ */
161 	}
162 
163  out:
164 	if (rv == 0 && sizep)
165 		*sizep = size;
166 	if (rv == 0 && ftp)
167 		*ftp = ft;
168 
169 	return rv;
170 }
171 
172 int
173 rumpuser_nanosleep(uint64_t *sec, uint64_t *nsec, int *error)
174 {
175 	struct timespec rqt, rmt;
176 	int rv;
177 
178 	/*LINTED*/
179 	rqt.tv_sec = *sec;
180 	/*LINTED*/
181 	rqt.tv_nsec = *nsec;
182 
183 	KLOCK_WRAP(rv = nanosleep(&rqt, &rmt));
184 	if (rv == -1)
185 		*error = errno;
186 
187 	*sec = rmt.tv_sec;
188 	*nsec = rmt.tv_nsec;
189 
190 	return rv;
191 }
192 
193 void *
194 rumpuser__malloc(size_t howmuch, int canfail, const char *func, int line)
195 {
196 	void *rv;
197 
198 	rv = malloc(howmuch);
199 	if (rv == NULL && canfail == 0) {
200 		warn("malloc failed %s (%d)", func, line);
201 		abort();
202 	}
203 
204 	return rv;
205 }
206 
207 void *
208 rumpuser__realloc(void *ptr, size_t howmuch, int canfail,
209 	const char *func, int line)
210 {
211 	void *rv;
212 
213 	rv = realloc(ptr, howmuch);
214 	if (rv == NULL && canfail == 0) {
215 		warn("realloc failed %s (%d)", func, line);
216 		abort();
217 	}
218 
219 	return rv;
220 }
221 
222 void
223 rumpuser_free(void *ptr)
224 {
225 
226 	free(ptr);
227 }
228 
229 void *
230 rumpuser_anonmmap(size_t size, int alignbit, int exec, int *error)
231 {
232 	void *rv;
233 	int prot;
234 
235 	prot = PROT_READ|PROT_WRITE;
236 	if (exec)
237 		prot |= PROT_EXEC;
238 	/* XXX: MAP_ALIGNED() is not portable */
239 	rv = mmap(NULL, size, prot, MAP_ANON | MAP_ALIGNED(alignbit), -1, 0);
240 	if (rv == MAP_FAILED) {
241 		*error = errno;
242 		return NULL;
243 	}
244 	return rv;
245 }
246 
247 void
248 rumpuser_unmap(void *addr, size_t len)
249 {
250 	int rv;
251 
252 	rv = munmap(addr, len);
253 	assert(rv == 0);
254 }
255 
256 void *
257 rumpuser_filemmap(int fd, off_t offset, size_t len, int flags, int *error)
258 {
259 	void *rv;
260 	int mmflags, prot;
261 
262 	if (flags & RUMPUSER_FILEMMAP_TRUNCATE)
263 		ftruncate(fd, offset + len);
264 
265 	mmflags = MAP_FILE;
266 	if (flags & RUMPUSER_FILEMMAP_SHARED)
267 		mmflags |= MAP_SHARED;
268 	else
269 		mmflags |= MAP_PRIVATE;
270 
271 	prot = 0;
272 	if (flags & RUMPUSER_FILEMMAP_READ)
273 		prot |= PROT_READ;
274 	if (flags & RUMPUSER_FILEMMAP_WRITE)
275 		prot |= PROT_WRITE;
276 
277 	rv = mmap(NULL, len, PROT_READ|PROT_WRITE, mmflags, fd, offset);
278 	if (rv == MAP_FAILED) {
279 		*error = errno;
280 		return NULL;
281 	}
282 
283 	*error = 0;
284 	return rv;
285 }
286 
287 int
288 rumpuser_memsync(void *addr, size_t len, int *error)
289 {
290 
291 	DOCALL_KLOCK(int, (msync(addr, len, MS_SYNC)));
292 }
293 
294 int
295 rumpuser_open(const char *path, int flags, int *error)
296 {
297 
298 	DOCALL(int, (open(path, flags, 0644)));
299 }
300 
301 int
302 rumpuser_ioctl(int fd, u_long cmd, void *data, int *error)
303 {
304 
305 	DOCALL_KLOCK(int, (ioctl(fd, cmd, data)));
306 }
307 
308 int
309 rumpuser_close(int fd, int *error)
310 {
311 
312 	DOCALL(int, close(fd));
313 }
314 
315 int
316 rumpuser_fsync(int fd, int *error)
317 {
318 
319 	DOCALL_KLOCK(int, fsync(fd));
320 }
321 
322 ssize_t
323 rumpuser_read(int fd, void *data, size_t size, int *error)
324 {
325 	ssize_t rv;
326 
327 	KLOCK_WRAP(rv = read(fd, data, size));
328 	if (rv == -1)
329 		*error = errno;
330 
331 	return rv;
332 }
333 
334 ssize_t
335 rumpuser_pread(int fd, void *data, size_t size, off_t offset, int *error)
336 {
337 	ssize_t rv;
338 
339 	KLOCK_WRAP(rv = pread(fd, data, size, offset));
340 	if (rv == -1)
341 		*error = errno;
342 
343 	return rv;
344 }
345 
346 void
347 rumpuser_read_bio(int fd, void *data, size_t size, off_t offset,
348 	rump_biodone_fn biodone, void *biodonecookie)
349 {
350 	ssize_t rv;
351 	int error = 0;
352 
353 	rv = rumpuser_pread(fd, data, size, offset, &error);
354 	/* check against <0 instead of ==-1 to get typing below right */
355 	if (rv < 0)
356 		rv = 0;
357 
358 	/* LINTED: see above */
359 	biodone(biodonecookie, rv, error);
360 }
361 
362 ssize_t
363 rumpuser_write(int fd, const void *data, size_t size, int *error)
364 {
365 	ssize_t rv;
366 
367 	KLOCK_WRAP(rv = write(fd, data, size));
368 	if (rv == -1)
369 		*error = errno;
370 
371 	return rv;
372 }
373 
374 ssize_t
375 rumpuser_pwrite(int fd, const void *data, size_t size, off_t offset, int *error)
376 {
377 	ssize_t rv;
378 
379 	KLOCK_WRAP(rv = pwrite(fd, data, size, offset));
380 	if (rv == -1)
381 		*error = errno;
382 
383 	return rv;
384 }
385 
386 void
387 rumpuser_write_bio(int fd, const void *data, size_t size, off_t offset,
388 	rump_biodone_fn biodone, void *biodonecookie)
389 {
390 	ssize_t rv;
391 	int error = 0;
392 
393 	rv = rumpuser_pwrite(fd, data, size, offset, &error);
394 	/* check against <0 instead of ==-1 to get typing below right */
395 	if (rv < 0)
396 		rv = 0;
397 
398 	/* LINTED: see above */
399 	biodone(biodonecookie, rv, error);
400 }
401 
402 ssize_t
403 rumpuser_readv(int fd, const struct rumpuser_iovec *riov, int iovcnt,
404 	int *error)
405 {
406 	struct iovec *iovp;
407 	ssize_t rv;
408 	int i;
409 
410 	iovp = malloc(iovcnt * sizeof(struct iovec));
411 	if (iovp == NULL) {
412 		*error = ENOMEM;
413 		return -1;
414 	}
415 	for (i = 0; i < iovcnt; i++) {
416 		iovp[i].iov_base = riov[i].iov_base;
417 		/*LINTED*/
418 		iovp[i].iov_len = riov[i].iov_len;
419 	}
420 
421 	KLOCK_WRAP(rv = readv(fd, iovp, iovcnt));
422 	if (rv == -1)
423 		*error = errno;
424 	free(iovp);
425 
426 	return rv;
427 }
428 
429 ssize_t
430 rumpuser_writev(int fd, const struct rumpuser_iovec *riov, int iovcnt,
431 	int *error)
432 {
433 	struct iovec *iovp;
434 	ssize_t rv;
435 	int i;
436 
437 	iovp = malloc(iovcnt * sizeof(struct iovec));
438 	if (iovp == NULL) {
439 		*error = ENOMEM;
440 		return -1;
441 	}
442 	for (i = 0; i < iovcnt; i++) {
443 		iovp[i].iov_base = riov[i].iov_base;
444 		/*LINTED*/
445 		iovp[i].iov_len = riov[i].iov_len;
446 	}
447 
448 	KLOCK_WRAP(rv = writev(fd, iovp, iovcnt));
449 	if (rv == -1)
450 		*error = errno;
451 	free(iovp);
452 
453 	return rv;
454 }
455 
456 int
457 rumpuser_gettime(uint64_t *sec, uint64_t *nsec, int *error)
458 {
459 	struct timeval tv;
460 	int rv;
461 
462 	rv = gettimeofday(&tv, NULL);
463 	if (rv == -1) {
464 		*error = errno;
465 		return rv;
466 	}
467 
468 	*sec = tv.tv_sec;
469 	*nsec = tv.tv_usec * 1000;
470 
471 	return 0;
472 }
473 
474 int
475 rumpuser_getenv(const char *name, char *buf, size_t blen, int *error)
476 {
477 
478 	DOCALL(int, getenv_r(name, buf, blen));
479 }
480 
481 int
482 rumpuser_gethostname(char *name, size_t namelen, int *error)
483 {
484 
485 	DOCALL(int, (gethostname(name, namelen)));
486 }
487 
488 int
489 rumpuser_poll(struct pollfd *fds, int nfds, int timeout, int *error)
490 {
491 
492 	DOCALL_KLOCK(int, (poll(fds, (nfds_t)nfds, timeout)));
493 }
494 
495 int
496 rumpuser_putchar(int c, int *error)
497 {
498 
499 	DOCALL(int, (putchar(c)));
500 }
501 
502 void
503 rumpuser_exit(int rv)
504 {
505 
506 	if (rv == RUMPUSER_PANIC)
507 		abort();
508 	else
509 		exit(rv);
510 }
511 
512 void
513 rumpuser_seterrno(int error)
514 {
515 
516 	errno = error;
517 }
518 
519 int
520 rumpuser_writewatchfile_setup(int kq, int fd, intptr_t opaque, int *error)
521 {
522 	struct kevent kev;
523 
524 	if (kq == -1) {
525 		kq = kqueue();
526 		if (kq == -1) {
527 			*error = errno;
528 			return -1;
529 		}
530 	}
531 
532 	EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR,
533 	    NOTE_WRITE, 0, opaque);
534 	if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
535 		*error = errno;
536 		return -1;
537 	}
538 
539 	return kq;
540 }
541 
542 int
543 rumpuser_writewatchfile_wait(int kq, intptr_t *opaque, int *error)
544 {
545 	struct kevent kev;
546 	int rv;
547 
548 	KLOCK_WRAP(rv = kevent(kq, NULL, 0, &kev, 1, NULL));
549 	if (rv == -1) {
550 		*error = errno;
551 		return -1;
552 	}
553 
554 	if (opaque)
555 		*opaque = kev.udata;
556 	return rv;
557 }
558 
559 /*
560  * This is meant for safe debugging prints from the kernel.
561  */
562 int
563 rumpuser_dprintf(const char *format, ...)
564 {
565 	va_list ap;
566 	int rv;
567 
568 	va_start(ap, format);
569 	rv = vprintf(format, ap);
570 	va_end(ap);
571 
572 	return rv;
573 }
574