rumpfs.c revision 1.30 1 /* $NetBSD: rumpfs.c,v 1.30 2009/11/26 20:58:51 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2009 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 __KERNEL_RCSID(0, "$NetBSD: rumpfs.c,v 1.30 2009/11/26 20:58:51 pooka Exp $");
30
31 #include <sys/param.h>
32 #include <sys/atomic.h>
33 #include <sys/filedesc.h>
34 #include <sys/errno.h>
35 #include <sys/fcntl.h>
36 #include <sys/kauth.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mount.h>
40 #include <sys/namei.h>
41 #include <sys/lock.h>
42 #include <sys/lockf.h>
43 #include <sys/queue.h>
44 #include <sys/stat.h>
45 #include <sys/syscallargs.h>
46 #include <sys/vnode.h>
47
48 #include <miscfs/fifofs/fifo.h>
49 #include <miscfs/specfs/specdev.h>
50 #include <miscfs/genfs/genfs.h>
51
52 #include <rump/rumpuser.h>
53
54 #include "rump_private.h"
55 #include "rump_vfs_private.h"
56
57 static int rump_vop_lookup(void *);
58 static int rump_vop_getattr(void *);
59 static int rump_vop_mkdir(void *);
60 static int rump_vop_mknod(void *);
61 static int rump_vop_create(void *);
62 static int rump_vop_inactive(void *);
63 static int rump_vop_reclaim(void *);
64 static int rump_vop_success(void *);
65 static int rump_vop_spec(void *);
66 static int rump_vop_read(void *);
67 static int rump_vop_write(void *);
68 static int rump_vop_open(void *);
69
70 int (**fifo_vnodeop_p)(void *);
71 const struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
72 { &vop_default_desc, vn_default_error },
73 { NULL, NULL }
74 };
75 const struct vnodeopv_desc fifo_vnodeop_opv_desc =
76 { &fifo_vnodeop_p, fifo_vnodeop_entries };
77
78 int (**rump_vnodeop_p)(void *);
79 const struct vnodeopv_entry_desc rump_vnodeop_entries[] = {
80 { &vop_default_desc, vn_default_error },
81 { &vop_lookup_desc, rump_vop_lookup },
82 { &vop_getattr_desc, rump_vop_getattr },
83 { &vop_mkdir_desc, rump_vop_mkdir },
84 { &vop_mknod_desc, rump_vop_mknod },
85 { &vop_create_desc, rump_vop_create },
86 { &vop_access_desc, rump_vop_success },
87 { &vop_read_desc, rump_vop_read },
88 { &vop_write_desc, rump_vop_write },
89 { &vop_open_desc, rump_vop_open },
90 { &vop_putpages_desc, genfs_null_putpages },
91 { &vop_fsync_desc, rump_vop_success },
92 { &vop_lock_desc, genfs_lock },
93 { &vop_unlock_desc, genfs_unlock },
94 { &vop_inactive_desc, rump_vop_inactive },
95 { &vop_reclaim_desc, rump_vop_reclaim },
96 { NULL, NULL }
97 };
98 const struct vnodeopv_desc rump_vnodeop_opv_desc =
99 { &rump_vnodeop_p, rump_vnodeop_entries };
100
101 int (**rump_specop_p)(void *);
102 const struct vnodeopv_entry_desc rump_specop_entries[] = {
103 { &vop_default_desc, rump_vop_spec },
104 { NULL, NULL }
105 };
106 const struct vnodeopv_desc rump_specop_opv_desc =
107 { &rump_specop_p, rump_specop_entries };
108
109 const struct vnodeopv_desc * const rump_opv_descs[] = {
110 &rump_vnodeop_opv_desc,
111 &rump_specop_opv_desc,
112 NULL
113 };
114
115 struct rumpfs_dent {
116 char *rd_name;
117 struct rumpfs_node *rd_node;
118
119 LIST_ENTRY(rumpfs_dent) rd_entries;
120 };
121
122 struct rumpfs_node {
123 struct vattr rn_va;
124 struct vnode *rn_vp;
125
126 union {
127 struct {
128 char *hostpath; /* VREG */
129 int readfd;
130 int writefd;
131 uint64_t offset;
132 } reg;
133 LIST_HEAD(, rumpfs_dent) dir; /* VDIR */
134 } rn_u;
135 };
136 #define rn_hostpath rn_u.reg.hostpath
137 #define rn_readfd rn_u.reg.readfd
138 #define rn_writefd rn_u.reg.writefd
139 #define rn_offset rn_u.reg.offset
140 #define rn_dir rn_u.dir
141
142 struct rumpfs_mount {
143 struct vnode *rfsmp_rvp;
144 };
145
146 static struct rumpfs_node *makeprivate(enum vtype, dev_t, off_t);
147
148 /*
149 * Extra Terrestrial stuff. We map a given key (pathname) to a file on
150 * the host FS. ET phones home only from the root node of rumpfs.
151 *
152 * When an etfs node is removed, a vnode potentially behind it is not
153 * immediately recycled.
154 */
155
156 struct etfs {
157 char et_key[MAXPATHLEN];
158 size_t et_keylen;
159
160 LIST_ENTRY(etfs) et_entries;
161
162 struct rumpfs_node *et_rn;
163 };
164 static kmutex_t etfs_lock;
165 static LIST_HEAD(, etfs) etfs_list = LIST_HEAD_INITIALIZER(etfs_list);
166
167 static enum vtype
168 ettype_to_vtype(enum rump_etfs_type et)
169 {
170 enum vtype vt;
171
172 switch (et) {
173 case RUMP_ETFS_REG:
174 vt = VREG;
175 break;
176 case RUMP_ETFS_BLK:
177 vt = VBLK;
178 break;
179 case RUMP_ETFS_CHR:
180 vt = VCHR;
181 break;
182 default:
183 panic("invalid et type: %d", et);
184 }
185
186 return vt;
187 }
188
189 static bool
190 etfs_find(const char *key, struct rumpfs_node **rnp)
191 {
192 struct etfs *et;
193 size_t keylen = strlen(key);
194 bool rv = false;
195
196 KASSERT(mutex_owned(&etfs_lock));
197
198 LIST_FOREACH(et, &etfs_list, et_entries) {
199 if (keylen == et->et_keylen && strcmp(key, et->et_key) == 0) {
200 *rnp = et->et_rn;
201 rv = true;
202 break;
203 }
204 }
205
206 return rv;
207 }
208
209 static int
210 doregister(const char *key, const char *hostpath,
211 enum rump_etfs_type ftype, uint64_t begin, uint64_t size)
212 {
213 struct etfs *et;
214 struct rumpfs_node *rn_dummy, *rn;
215 uint64_t fsize;
216 dev_t rdev = NODEV;
217 devminor_t dmin;
218 int hft, error;
219
220 if (rumpuser_getfileinfo(hostpath, &fsize, &hft, &error))
221 return error;
222
223 /* check that we give sensible arguments */
224 if (begin > fsize)
225 return EINVAL;
226 if (size == RUMP_ETFS_SIZE_ENDOFF)
227 size = fsize - begin;
228 if (begin + size > fsize)
229 return EINVAL;
230
231 if (ftype == RUMP_ETFS_BLK || ftype == RUMP_ETFS_CHR) {
232 error = rumpblk_register(hostpath, &dmin, begin, size);
233 if (error != 0) {
234 return error;
235 }
236 rdev = makedev(RUMPBLK, dmin);
237 }
238
239 et = kmem_alloc(sizeof(*et), KM_SLEEP);
240 strcpy(et->et_key, key);
241 et->et_keylen = strlen(et->et_key);
242 et->et_rn = rn = makeprivate(ettype_to_vtype(ftype), rdev, size);
243 if (ftype == RUMP_ETFS_REG) {
244 size_t len = strlen(hostpath)+1;
245
246 rn->rn_hostpath = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
247 memcpy(rn->rn_hostpath, hostpath, len);
248 rn->rn_offset = begin;
249 }
250
251 mutex_enter(&etfs_lock);
252 if (etfs_find(key, &rn_dummy)) {
253 mutex_exit(&etfs_lock);
254 kmem_free(et, sizeof(*et));
255 /* XXX: rumpblk_deregister(hostpath); */
256 return EEXIST;
257 }
258 LIST_INSERT_HEAD(&etfs_list, et, et_entries);
259 mutex_exit(&etfs_lock);
260
261 return 0;
262 }
263
264 int
265 rump_etfs_register(const char *key, const char *hostpath,
266 enum rump_etfs_type ftype)
267 {
268
269 return doregister(key, hostpath, ftype, 0, RUMP_ETFS_SIZE_ENDOFF);
270 }
271
272 int
273 rump_etfs_register_withsize(const char *key, const char *hostpath,
274 enum rump_etfs_type ftype, uint64_t begin, uint64_t size)
275 {
276
277 /*
278 * Check that we're mapping at block offsets. I guess this
279 * is not technically necessary except for BLK/CHR backends
280 * (i.e. what getfileinfo() returns, not ftype) and can be
281 * removed later if there are problems.
282 */
283 if ((begin & (DEV_BSIZE-1)) != 0)
284 return EINVAL;
285 if (size != RUMP_ETFS_SIZE_ENDOFF && (size & (DEV_BSIZE-1)) != 0)
286 return EINVAL;
287
288 return doregister(key, hostpath, ftype, begin, size);
289 }
290
291 int
292 rump_etfs_remove(const char *key)
293 {
294 struct etfs *et;
295 size_t keylen = strlen(key);
296
297 mutex_enter(&etfs_lock);
298 LIST_FOREACH(et, &etfs_list, et_entries) {
299 if (keylen == et->et_keylen && strcmp(et->et_key, key) == 0) {
300 LIST_REMOVE(et, et_entries);
301 kmem_free(et, sizeof(*et));
302 break;
303 }
304 }
305 mutex_exit(&etfs_lock);
306
307 if (!et)
308 return ENOENT;
309 return 0;
310 }
311
312 /*
313 * rumpfs
314 */
315
316 static int lastino = 1;
317 static kmutex_t reclock;
318
319 static struct rumpfs_node *
320 makeprivate(enum vtype vt, dev_t rdev, off_t size)
321 {
322 struct rumpfs_node *rn;
323 struct vattr *va;
324 struct timespec ts;
325
326 rn = kmem_zalloc(sizeof(*rn), KM_SLEEP);
327
328 switch (vt) {
329 case VDIR:
330 LIST_INIT(&rn->rn_dir);
331 break;
332 case VREG:
333 rn->rn_readfd = -1;
334 rn->rn_writefd = -1;
335 break;
336 default:
337 break;
338 }
339
340 nanotime(&ts);
341
342 va = &rn->rn_va;
343 va->va_type = vt;
344 va->va_mode = 0755;
345 if (vt == VDIR)
346 va->va_nlink = 2;
347 else
348 va->va_nlink = 1;
349 va->va_uid = 0;
350 va->va_gid = 0;
351 va->va_fsid =
352 va->va_fileid = atomic_inc_uint_nv(&lastino);
353 va->va_size = size;
354 va->va_blocksize = 512;
355 va->va_atime = ts;
356 va->va_mtime = ts;
357 va->va_ctime = ts;
358 va->va_birthtime = ts;
359 va->va_gen = 0;
360 va->va_flags = 0;
361 va->va_rdev = rdev;
362 va->va_bytes = 512;
363 va->va_filerev = 0;
364 va->va_vaflags = 0;
365
366 return rn;
367 }
368
369 static int
370 makevnode(struct mount *mp, struct rumpfs_node *rn, struct vnode **vpp)
371 {
372 struct vnode *vp;
373 int (**vpops)(void *);
374 struct vattr *va = &rn->rn_va;
375 int rv;
376
377 KASSERT(mutex_owned(&reclock));
378
379 if (va->va_type == VCHR || va->va_type == VBLK) {
380 vpops = rump_specop_p;
381 } else {
382 vpops = rump_vnodeop_p;
383 }
384 if (vpops != rump_specop_p && va->va_type != VDIR
385 && !(va->va_type == VREG && rn->rn_hostpath != NULL)
386 && va->va_type != VSOCK)
387 return EOPNOTSUPP;
388
389 rv = getnewvnode(VT_RUMP, mp, vpops, &vp);
390 if (rv)
391 return rv;
392
393 vp->v_size = vp->v_writesize = va->va_size;
394 vp->v_type = va->va_type;
395
396 if (vpops == rump_specop_p) {
397 spec_node_init(vp, va->va_rdev);
398 }
399 vp->v_data = rn;
400
401 vn_lock(vp, LK_RETRY | LK_EXCLUSIVE);
402 rn->rn_vp = vp;
403 *vpp = vp;
404
405 return 0;
406 }
407
408 /*
409 * Simple lookup for faking lookup of device entry for rump file systems
410 * and for locating/creating directories. Yes, this will panic if you
411 * call it with the wrong arguments.
412 *
413 * uhm, this is twisted. C F C C, hope of C C F C looming
414 */
415 static int
416 rump_vop_lookup(void *v)
417 {
418 struct vop_lookup_args /* {
419 struct vnode *a_dvp;
420 struct vnode **a_vpp;
421 struct componentname *a_cnp;
422 }; */ *ap = v;
423 struct componentname *cnp = ap->a_cnp;
424 struct vnode *dvp = ap->a_dvp;
425 struct vnode **vpp = ap->a_vpp;
426 struct vnode *vp;
427 struct rumpfs_node *rnd = dvp->v_data, *rn;
428 struct rumpfs_dent *rd = NULL;
429 int rv;
430
431 /* we handle only some "non-special" cases */
432 if (!(((cnp->cn_flags & ISLASTCN) == 0)
433 || (cnp->cn_nameiop == LOOKUP || cnp->cn_nameiop == CREATE)))
434 return EOPNOTSUPP;
435 if (!((cnp->cn_flags & ISDOTDOT) == 0))
436 return EOPNOTSUPP;
437 if (!(cnp->cn_namelen != 0 && cnp->cn_pnbuf[0] != '.'))
438 return EOPNOTSUPP;
439
440 /* check if we are returning a faked block device */
441 if (dvp == rootvnode && cnp->cn_nameiop == LOOKUP) {
442 mutex_enter(&etfs_lock);
443 if (etfs_find(cnp->cn_pnbuf, &rn)) {
444 mutex_exit(&etfs_lock);
445 cnp->cn_consume = strlen(cnp->cn_nameptr
446 + cnp->cn_namelen);
447 cnp->cn_flags &= ~REQUIREDIR;
448 goto getvnode;
449 }
450 mutex_exit(&etfs_lock);
451 }
452
453 if (!rd) {
454 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
455 if (strncmp(rd->rd_name, cnp->cn_nameptr,
456 cnp->cn_namelen) == 0)
457 break;
458 }
459 }
460
461 if (!rd && ((cnp->cn_flags & ISLASTCN) == 0||cnp->cn_nameiop != CREATE))
462 return ENOENT;
463
464 if (!rd && (cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
465 cnp->cn_flags |= SAVENAME;
466 return EJUSTRETURN;
467 }
468 rn = rd->rd_node;
469 rd = NULL;
470
471 getvnode:
472 KASSERT(rn);
473 mutex_enter(&reclock);
474 if ((vp = rn->rn_vp)) {
475 mutex_enter(&vp->v_interlock);
476 mutex_exit(&reclock);
477 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
478 goto getvnode;
479 *vpp = vp;
480 } else {
481 rv = makevnode(dvp->v_mount, rn, vpp);
482 rn->rn_vp = *vpp;
483 mutex_exit(&reclock);
484 if (rv)
485 return rv;
486 }
487
488 return 0;
489 }
490
491 static int
492 rump_vop_getattr(void *v)
493 {
494 struct vop_getattr_args /* {
495 struct vnode *a_vp;
496 struct vattr *a_vap;
497 kauth_cred_t a_cred;
498 } */ *ap = v;
499 struct rumpfs_node *rn = ap->a_vp->v_data;
500
501 memcpy(ap->a_vap, &rn->rn_va, sizeof(struct vattr));
502 return 0;
503 }
504
505 static int
506 rump_vop_mkdir(void *v)
507 {
508 struct vop_mkdir_args /* {
509 struct vnode *a_dvp;
510 struct vnode **a_vpp;
511 struct componentname *a_cnp;
512 struct vattr *a_vap;
513 }; */ *ap = v;
514 struct vnode *dvp = ap->a_dvp;
515 struct vnode **vpp = ap->a_vpp;
516 struct componentname *cnp = ap->a_cnp;
517 struct rumpfs_node *rnd = dvp->v_data, *rn;
518 struct rumpfs_dent *rdent;
519 int rv = 0;
520
521 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
522 mutex_enter(&reclock);
523 rv = makevnode(dvp->v_mount, rn, vpp);
524 mutex_exit(&reclock);
525 if (rv)
526 goto out;
527
528 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
529 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
530 rdent->rd_node = (*vpp)->v_data;
531 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
532
533 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
534
535 out:
536 vput(dvp);
537 return rv;
538 }
539
540 static int
541 rump_vop_mknod(void *v)
542 {
543 struct vop_mknod_args /* {
544 struct vnode *a_dvp;
545 struct vnode **a_vpp;
546 struct componentname *a_cnp;
547 struct vattr *a_vap;
548 }; */ *ap = v;
549 struct vnode *dvp = ap->a_dvp;
550 struct vnode **vpp = ap->a_vpp;
551 struct componentname *cnp = ap->a_cnp;
552 struct vattr *va = ap->a_vap;
553 struct rumpfs_node *rnd = dvp->v_data, *rn;
554 struct rumpfs_dent *rdent;
555 int rv;
556
557 rn = makeprivate(va->va_type, va->va_rdev, DEV_BSIZE);
558 mutex_enter(&reclock);
559 rv = makevnode(dvp->v_mount, rn, vpp);
560 mutex_exit(&reclock);
561 if (rv)
562 goto out;
563
564 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
565 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
566 rdent->rd_node = (*vpp)->v_data;
567 rdent->rd_node->rn_va.va_rdev = va->va_rdev;
568 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
569
570 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
571
572 out:
573 vput(dvp);
574 return rv;
575 }
576
577 static int
578 rump_vop_create(void *v)
579 {
580 struct vop_create_args /* {
581 struct vnode *a_dvp;
582 struct vnode **a_vpp;
583 struct componentname *a_cnp;
584 struct vattr *a_vap;
585 }; */ *ap = v;
586 struct vnode *dvp = ap->a_dvp;
587 struct vnode **vpp = ap->a_vpp;
588 struct componentname *cnp = ap->a_cnp;
589 struct vattr *va = ap->a_vap;
590 struct rumpfs_node *rnd = dvp->v_data, *rn;
591 struct rumpfs_dent *rdent;
592 int rv;
593
594 if (va->va_type != VSOCK) {
595 rv = EOPNOTSUPP;
596 goto out;
597 }
598 rn = makeprivate(VSOCK, NODEV, DEV_BSIZE);
599 mutex_enter(&reclock);
600 rv = makevnode(dvp->v_mount, rn, vpp);
601 mutex_exit(&reclock);
602 if (rv)
603 goto out;
604
605 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
606 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
607 rdent->rd_node = (*vpp)->v_data;
608 rdent->rd_node->rn_va.va_rdev = NODEV;
609 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
610
611 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
612
613 out:
614 vput(dvp);
615 return rv;
616 }
617
618 static int
619 rump_vop_open(void *v)
620 {
621 struct vop_open_args /* {
622 struct vnode *a_vp;
623 int a_mode;
624 kauth_cred_t a_cred;
625 } */ *ap = v;
626 struct vnode *vp = ap->a_vp;
627 struct rumpfs_node *rn = vp->v_data;
628 int mode = ap->a_mode;
629 int error = EINVAL;
630
631 if (vp->v_type != VREG)
632 return 0;
633
634 if (mode & FREAD) {
635 if (rn->rn_readfd != -1)
636 return 0;
637 rn->rn_readfd = rumpuser_open(rn->rn_hostpath,
638 O_RDONLY, &error);
639 } else if (mode & FWRITE) {
640 if (rn->rn_writefd != -1)
641 return 0;
642 rn->rn_writefd = rumpuser_open(rn->rn_hostpath,
643 O_WRONLY, &error);
644 }
645
646 return error;
647 }
648
649 static int
650 rump_vop_read(void *v)
651 {
652 struct vop_read_args /* {
653 struct vnode *a_vp;
654 struct uio *a_uio;
655 int ioflags a_ioflag;
656 kauth_cred_t a_cred;
657 }; */ *ap = v;
658 struct vnode *vp = ap->a_vp;
659 struct rumpfs_node *rn = vp->v_data;
660 struct uio *uio = ap->a_uio;
661 uint8_t *buf;
662 size_t bufsize;
663 int error = 0;
664
665 bufsize = uio->uio_resid;
666 buf = kmem_alloc(bufsize, KM_SLEEP);
667 if (rumpuser_pread(rn->rn_readfd, buf, bufsize,
668 uio->uio_offset + rn->rn_offset, &error) == -1)
669 goto out;
670 error = uiomove(buf, bufsize, uio);
671
672 out:
673 kmem_free(buf, bufsize);
674 return error;
675 }
676
677 static int
678 rump_vop_write(void *v)
679 {
680 struct vop_read_args /* {
681 struct vnode *a_vp;
682 struct uio *a_uio;
683 int ioflags a_ioflag;
684 kauth_cred_t a_cred;
685 }; */ *ap = v;
686 struct vnode *vp = ap->a_vp;
687 struct rumpfs_node *rn = vp->v_data;
688 struct uio *uio = ap->a_uio;
689 uint8_t *buf;
690 size_t bufsize;
691 int error = 0;
692
693 bufsize = uio->uio_resid;
694 buf = kmem_alloc(bufsize, KM_SLEEP);
695 error = uiomove(buf, bufsize, uio);
696 if (error)
697 goto out;
698 KASSERT(uio->uio_resid == 0);
699 rumpuser_pwrite(rn->rn_writefd, buf, bufsize,
700 uio->uio_offset + rn->rn_offset, &error);
701
702 out:
703 kmem_free(buf, bufsize);
704 return error;
705 }
706
707 static int
708 rump_vop_success(void *v)
709 {
710
711 return 0;
712 }
713
714 static int
715 rump_vop_inactive(void *v)
716 {
717 struct vop_inactive_args *ap = v;
718 struct vnode *vp = ap->a_vp;
719 struct rumpfs_node *rn = vp->v_data;
720 int error;
721
722 if (vp->v_type == VREG) {
723 if (rn->rn_readfd != -1) {
724 rumpuser_close(rn->rn_readfd, &error);
725 rn->rn_readfd = -1;
726 }
727 if (rn->rn_writefd != -1) {
728 rumpuser_close(rn->rn_writefd, &error);
729 rn->rn_writefd = -1;
730 }
731 }
732
733 VOP_UNLOCK(vp, 0);
734 return 0;
735 }
736
737 static int
738 rump_vop_reclaim(void *v)
739 {
740 struct vop_reclaim_args /* {
741 struct vnode *a_vp;
742 } */ *ap = v;
743 struct vnode *vp = ap->a_vp;
744 struct rumpfs_node *rn = vp->v_data;
745
746 mutex_enter(&reclock);
747 rn->rn_vp = NULL;
748 mutex_exit(&reclock);
749 vp->v_data = NULL;
750
751 return 0;
752 }
753
754 static int
755 rump_vop_spec(void *v)
756 {
757 struct vop_generic_args *ap = v;
758 int (**opvec)(void *);
759
760 switch (ap->a_desc->vdesc_offset) {
761 case VOP_ACCESS_DESCOFFSET:
762 case VOP_GETATTR_DESCOFFSET:
763 case VOP_LOCK_DESCOFFSET:
764 case VOP_UNLOCK_DESCOFFSET:
765 opvec = rump_vnodeop_p;
766 break;
767 default:
768 opvec = spec_vnodeop_p;
769 break;
770 }
771
772 return VOCALL(opvec, ap->a_desc->vdesc_offset, v);
773 }
774
775 /*
776 * Begin vfs-level stuff
777 */
778
779 VFS_PROTOS(rumpfs);
780 struct vfsops rumpfs_vfsops = {
781 .vfs_name = MOUNT_RUMPFS,
782 .vfs_min_mount_data = 0,
783 .vfs_mount = rumpfs_mount,
784 .vfs_start = (void *)nullop,
785 .vfs_unmount = rumpfs_unmount,
786 .vfs_root = rumpfs_root,
787 .vfs_quotactl = (void *)eopnotsupp,
788 .vfs_sync = (void *)nullop,
789 .vfs_vget = rumpfs_vget,
790 .vfs_fhtovp = (void *)eopnotsupp,
791 .vfs_vptofh = (void *)eopnotsupp,
792 .vfs_init = rumpfs_init,
793 .vfs_reinit = NULL,
794 .vfs_done = rumpfs_done,
795 .vfs_mountroot = rumpfs_mountroot,
796 .vfs_snapshot = (void *)eopnotsupp,
797 .vfs_extattrctl = (void *)eopnotsupp,
798 .vfs_suspendctl = (void *)eopnotsupp,
799 .vfs_opv_descs = rump_opv_descs,
800 /* vfs_refcount */
801 /* vfs_list */
802 };
803
804 int
805 rumpfs_mount(struct mount *mp, const char *mntpath, void *arg, size_t *alen)
806 {
807
808 return EOPNOTSUPP;
809 }
810
811 int
812 rumpfs_unmount(struct mount *mp, int flags)
813 {
814
815 return EOPNOTSUPP; /* ;) */
816 }
817
818 int
819 rumpfs_root(struct mount *mp, struct vnode **vpp)
820 {
821 struct rumpfs_mount *rfsmp = mp->mnt_data;
822
823 vget(rfsmp->rfsmp_rvp, LK_EXCLUSIVE | LK_RETRY);
824 *vpp = rfsmp->rfsmp_rvp;
825 return 0;
826 }
827
828 int
829 rumpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
830 {
831
832 return EOPNOTSUPP;
833 }
834
835 void
836 rumpfs_init()
837 {
838
839 CTASSERT(RUMP_ETFS_SIZE_ENDOFF == RUMPBLK_SIZENOTSET);
840
841 mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE);
842 mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE);
843 }
844
845 void
846 rumpfs_done()
847 {
848
849 mutex_destroy(&reclock);
850 mutex_destroy(&etfs_lock);
851 }
852
853 int
854 rumpfs_mountroot()
855 {
856 struct mount *mp;
857 struct rumpfs_mount *rfsmp;
858 struct rumpfs_node *rn;
859 int error;
860
861 if ((error = vfs_rootmountalloc(MOUNT_RUMPFS, "rootdev", &mp)) != 0) {
862 vrele(rootvp);
863 return error;
864 }
865
866 rfsmp = kmem_alloc(sizeof(*rfsmp), KM_SLEEP);
867
868 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
869 mutex_enter(&reclock);
870 error = makevnode(mp, rn, &rfsmp->rfsmp_rvp);
871 mutex_exit(&reclock);
872 rfsmp->rfsmp_rvp->v_vflag |= VV_ROOT;
873 if (error)
874 panic("could not create root vnode: %d", error);
875 mp->mnt_data = rfsmp;
876 VOP_UNLOCK(rfsmp->rfsmp_rvp, 0);
877
878 mp->mnt_flag |= MNT_ROOTFS;
879 rumpfs_vfsops.vfs_refcount++;
880
881 mutex_enter(&mountlist_lock);
882 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
883 mutex_exit(&mountlist_lock);
884
885 vfs_unbusy(mp, false, NULL);
886
887 return 0;
888 }
889
890 MODULE(MODULE_CLASS_VFS, rumpfs, NULL);
891
892 static int
893 rumpfs_modcmd(modcmd_t cmd, void *arg)
894 {
895
896 switch (cmd) {
897 case MODULE_CMD_INIT:
898 return vfs_attach(&rumpfs_vfsops);
899 case MODULE_CMD_FINI:
900 return vfs_detach(&rumpfs_vfsops);
901 default:
902 return ENOTTY;
903 }
904 }
905