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