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