rumpfs.c revision 1.36.2.1 1 /* $NetBSD: rumpfs.c,v 1.36.2.1 2010/04/30 14:44:31 uebayasi 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.36.2.1 2010/04/30 14:44:31 uebayasi 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 char *offset;
523
524 offset = strstr(cnp->cn_pnbuf, et->et_key);
525 KASSERT(offset);
526
527 rn = et->et_rn;
528 cnp->cn_consume += et->et_keylen
529 - (cnp->cn_nameptr - offset) - cnp->cn_namelen;
530 if (rn->rn_va.va_type != VDIR)
531 cnp->cn_flags &= ~REQUIREDIR;
532 goto getvnode;
533 }
534 }
535
536 if (rnd->rn_flags & RUMPNODE_DIR_ET) {
537 uint64_t fsize;
538 char *newpath;
539 size_t newpathlen;
540 int hft, error;
541
542 newpathlen = strlen(rnd->rn_hostpath) + 1 + cnp->cn_namelen + 1;
543 newpath = malloc(newpathlen, M_TEMP, M_WAITOK);
544
545 strlcpy(newpath, rnd->rn_hostpath, newpathlen);
546 strlcat(newpath, "/", newpathlen);
547 strlcat(newpath, cnp->cn_nameptr, newpathlen);
548
549 if (rumpuser_getfileinfo(newpath, &fsize, &hft, &error)) {
550 free(newpath, M_TEMP);
551 return error;
552 }
553
554 /* allow only dirs and regular files */
555 if (hft != RUMPUSER_FT_REG && hft != RUMPUSER_FT_DIR) {
556 free(newpath, M_TEMP);
557 return ENOENT;
558 }
559
560 rn = makeprivate(hft_to_vtype(hft), NODEV, fsize);
561 rn->rn_flags |= RUMPNODE_CANRECLAIM;
562 if (rnd->rn_flags & RUMPNODE_DIR_ETSUBS) {
563 rn->rn_flags |= RUMPNODE_DIR_ET | RUMPNODE_DIR_ETSUBS;
564 }
565 rn->rn_hostpath = newpath;
566
567 goto getvnode;
568 } else {
569 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
570 if (strlen(rd->rd_name) == cnp->cn_namelen &&
571 strncmp(rd->rd_name, cnp->cn_nameptr,
572 cnp->cn_namelen) == 0)
573 break;
574 }
575 }
576
577 if (!rd && ((cnp->cn_flags & ISLASTCN) == 0||cnp->cn_nameiop != CREATE))
578 return ENOENT;
579
580 if (!rd && (cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
581 cnp->cn_flags |= SAVENAME;
582 return EJUSTRETURN;
583 }
584 rn = rd->rd_node;
585
586 getvnode:
587 KASSERT(rn);
588 mutex_enter(&reclock);
589 if ((vp = rn->rn_vp)) {
590 mutex_enter(&vp->v_interlock);
591 mutex_exit(&reclock);
592 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
593 goto getvnode;
594 *vpp = vp;
595 } else {
596 rv = makevnode(dvp->v_mount, rn, vpp);
597 rn->rn_vp = *vpp;
598 mutex_exit(&reclock);
599 if (rv)
600 return rv;
601 }
602
603 out:
604 return 0;
605 }
606
607 static int
608 rump_vop_getattr(void *v)
609 {
610 struct vop_getattr_args /* {
611 struct vnode *a_vp;
612 struct vattr *a_vap;
613 kauth_cred_t a_cred;
614 } */ *ap = v;
615 struct rumpfs_node *rn = ap->a_vp->v_data;
616
617 memcpy(ap->a_vap, &rn->rn_va, sizeof(struct vattr));
618 return 0;
619 }
620
621 static int
622 rump_vop_mkdir(void *v)
623 {
624 struct vop_mkdir_args /* {
625 struct vnode *a_dvp;
626 struct vnode **a_vpp;
627 struct componentname *a_cnp;
628 struct vattr *a_vap;
629 }; */ *ap = v;
630 struct vnode *dvp = ap->a_dvp;
631 struct vnode **vpp = ap->a_vpp;
632 struct componentname *cnp = ap->a_cnp;
633 struct rumpfs_node *rnd = dvp->v_data, *rn;
634 struct rumpfs_dent *rdent;
635 int rv = 0;
636
637 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
638 mutex_enter(&reclock);
639 rv = makevnode(dvp->v_mount, rn, vpp);
640 mutex_exit(&reclock);
641 if (rv)
642 goto out;
643
644 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
645 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
646 rdent->rd_node = (*vpp)->v_data;
647 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
648
649 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
650
651 out:
652 vput(dvp);
653 return rv;
654 }
655
656 static int
657 rump_vop_mknod(void *v)
658 {
659 struct vop_mknod_args /* {
660 struct vnode *a_dvp;
661 struct vnode **a_vpp;
662 struct componentname *a_cnp;
663 struct vattr *a_vap;
664 }; */ *ap = v;
665 struct vnode *dvp = ap->a_dvp;
666 struct vnode **vpp = ap->a_vpp;
667 struct componentname *cnp = ap->a_cnp;
668 struct vattr *va = ap->a_vap;
669 struct rumpfs_node *rnd = dvp->v_data, *rn;
670 struct rumpfs_dent *rdent;
671 int rv;
672
673 rn = makeprivate(va->va_type, va->va_rdev, DEV_BSIZE);
674 mutex_enter(&reclock);
675 rv = makevnode(dvp->v_mount, rn, vpp);
676 mutex_exit(&reclock);
677 if (rv)
678 goto out;
679
680 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
681 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
682 rdent->rd_node = (*vpp)->v_data;
683 rdent->rd_node->rn_va.va_rdev = va->va_rdev;
684 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
685
686 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
687
688 out:
689 vput(dvp);
690 return rv;
691 }
692
693 static int
694 rump_vop_create(void *v)
695 {
696 struct vop_create_args /* {
697 struct vnode *a_dvp;
698 struct vnode **a_vpp;
699 struct componentname *a_cnp;
700 struct vattr *a_vap;
701 }; */ *ap = v;
702 struct vnode *dvp = ap->a_dvp;
703 struct vnode **vpp = ap->a_vpp;
704 struct componentname *cnp = ap->a_cnp;
705 struct vattr *va = ap->a_vap;
706 struct rumpfs_node *rnd = dvp->v_data, *rn;
707 struct rumpfs_dent *rdent;
708 int rv;
709
710 if (va->va_type != VSOCK) {
711 rv = EOPNOTSUPP;
712 goto out;
713 }
714 rn = makeprivate(VSOCK, NODEV, DEV_BSIZE);
715 mutex_enter(&reclock);
716 rv = makevnode(dvp->v_mount, rn, vpp);
717 mutex_exit(&reclock);
718 if (rv)
719 goto out;
720
721 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
722 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
723 rdent->rd_node = (*vpp)->v_data;
724 rdent->rd_node->rn_va.va_rdev = NODEV;
725 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
726
727 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
728
729 out:
730 vput(dvp);
731 return rv;
732 }
733
734 static int
735 rump_vop_open(void *v)
736 {
737 struct vop_open_args /* {
738 struct vnode *a_vp;
739 int a_mode;
740 kauth_cred_t a_cred;
741 } */ *ap = v;
742 struct vnode *vp = ap->a_vp;
743 struct rumpfs_node *rn = vp->v_data;
744 int mode = ap->a_mode;
745 int error = EINVAL;
746
747 if (vp->v_type != VREG)
748 return 0;
749
750 if (mode & FREAD) {
751 if (rn->rn_readfd != -1)
752 return 0;
753 rn->rn_readfd = rumpuser_open(rn->rn_hostpath,
754 O_RDONLY, &error);
755 } else if (mode & FWRITE) {
756 if (rn->rn_writefd != -1)
757 return 0;
758 rn->rn_writefd = rumpuser_open(rn->rn_hostpath,
759 O_WRONLY, &error);
760 }
761
762 return error;
763 }
764
765 static int
766 rump_vop_read(void *v)
767 {
768 struct vop_read_args /* {
769 struct vnode *a_vp;
770 struct uio *a_uio;
771 int ioflags a_ioflag;
772 kauth_cred_t a_cred;
773 }; */ *ap = v;
774 struct vnode *vp = ap->a_vp;
775 struct rumpfs_node *rn = vp->v_data;
776 struct uio *uio = ap->a_uio;
777 uint8_t *buf;
778 size_t bufsize;
779 int error = 0;
780
781 bufsize = uio->uio_resid;
782 buf = kmem_alloc(bufsize, KM_SLEEP);
783 if (rumpuser_pread(rn->rn_readfd, buf, bufsize,
784 uio->uio_offset + rn->rn_offset, &error) == -1)
785 goto out;
786 error = uiomove(buf, bufsize, uio);
787
788 out:
789 kmem_free(buf, bufsize);
790 return error;
791 }
792
793 static int
794 rump_vop_write(void *v)
795 {
796 struct vop_read_args /* {
797 struct vnode *a_vp;
798 struct uio *a_uio;
799 int ioflags a_ioflag;
800 kauth_cred_t a_cred;
801 }; */ *ap = v;
802 struct vnode *vp = ap->a_vp;
803 struct rumpfs_node *rn = vp->v_data;
804 struct uio *uio = ap->a_uio;
805 uint8_t *buf;
806 size_t bufsize;
807 int error = 0;
808
809 bufsize = uio->uio_resid;
810 buf = kmem_alloc(bufsize, KM_SLEEP);
811 error = uiomove(buf, bufsize, uio);
812 if (error)
813 goto out;
814 KASSERT(uio->uio_resid == 0);
815 rumpuser_pwrite(rn->rn_writefd, buf, bufsize,
816 uio->uio_offset + rn->rn_offset, &error);
817
818 out:
819 kmem_free(buf, bufsize);
820 return error;
821 }
822
823 static int
824 rump_vop_success(void *v)
825 {
826
827 return 0;
828 }
829
830 static int
831 rump_vop_inactive(void *v)
832 {
833 struct vop_inactive_args *ap = v;
834 struct vnode *vp = ap->a_vp;
835 struct rumpfs_node *rn = vp->v_data;
836 int error;
837
838 if (vp->v_type == VREG) {
839 if (rn->rn_readfd != -1) {
840 rumpuser_close(rn->rn_readfd, &error);
841 rn->rn_readfd = -1;
842 }
843 if (rn->rn_writefd != -1) {
844 rumpuser_close(rn->rn_writefd, &error);
845 rn->rn_writefd = -1;
846 }
847 }
848
849 VOP_UNLOCK(vp, 0);
850 return 0;
851 }
852
853 static int
854 rump_vop_reclaim(void *v)
855 {
856 struct vop_reclaim_args /* {
857 struct vnode *a_vp;
858 } */ *ap = v;
859 struct vnode *vp = ap->a_vp;
860 struct rumpfs_node *rn = vp->v_data;
861
862 mutex_enter(&reclock);
863 rn->rn_vp = NULL;
864 mutex_exit(&reclock);
865 vp->v_data = NULL;
866
867 if (rn->rn_flags & RUMPNODE_CANRECLAIM) {
868 if (rn->rn_hostpath)
869 free(rn->rn_hostpath, M_TEMP);
870 kmem_free(rn, sizeof(*rn));
871 }
872
873 return 0;
874 }
875
876 static int
877 rump_vop_spec(void *v)
878 {
879 struct vop_generic_args *ap = v;
880 int (**opvec)(void *);
881
882 switch (ap->a_desc->vdesc_offset) {
883 case VOP_ACCESS_DESCOFFSET:
884 case VOP_GETATTR_DESCOFFSET:
885 case VOP_LOCK_DESCOFFSET:
886 case VOP_UNLOCK_DESCOFFSET:
887 opvec = rump_vnodeop_p;
888 break;
889 default:
890 opvec = spec_vnodeop_p;
891 break;
892 }
893
894 return VOCALL(opvec, ap->a_desc->vdesc_offset, v);
895 }
896
897 /*
898 * Begin vfs-level stuff
899 */
900
901 VFS_PROTOS(rumpfs);
902 struct vfsops rumpfs_vfsops = {
903 .vfs_name = MOUNT_RUMPFS,
904 .vfs_min_mount_data = 0,
905 .vfs_mount = rumpfs_mount,
906 .vfs_start = (void *)nullop,
907 .vfs_unmount = rumpfs_unmount,
908 .vfs_root = rumpfs_root,
909 .vfs_quotactl = (void *)eopnotsupp,
910 .vfs_statvfs = genfs_statvfs,
911 .vfs_sync = (void *)nullop,
912 .vfs_vget = rumpfs_vget,
913 .vfs_fhtovp = (void *)eopnotsupp,
914 .vfs_vptofh = (void *)eopnotsupp,
915 .vfs_init = rumpfs_init,
916 .vfs_reinit = NULL,
917 .vfs_done = rumpfs_done,
918 .vfs_mountroot = rumpfs_mountroot,
919 .vfs_snapshot = (void *)eopnotsupp,
920 .vfs_extattrctl = (void *)eopnotsupp,
921 .vfs_suspendctl = (void *)eopnotsupp,
922 .vfs_opv_descs = rump_opv_descs,
923 /* vfs_refcount */
924 /* vfs_list */
925 };
926
927 int
928 rumpfs_mount(struct mount *mp, const char *mntpath, void *arg, size_t *alen)
929 {
930
931 return EOPNOTSUPP;
932 }
933
934 int
935 rumpfs_unmount(struct mount *mp, int flags)
936 {
937
938 /* if going for it, just lie about it */
939 if (panicstr)
940 return 0;
941
942 return EOPNOTSUPP; /* ;) */
943 }
944
945 int
946 rumpfs_root(struct mount *mp, struct vnode **vpp)
947 {
948 struct rumpfs_mount *rfsmp = mp->mnt_data;
949
950 vget(rfsmp->rfsmp_rvp, LK_EXCLUSIVE | LK_RETRY);
951 *vpp = rfsmp->rfsmp_rvp;
952 return 0;
953 }
954
955 int
956 rumpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
957 {
958
959 return EOPNOTSUPP;
960 }
961
962 void
963 rumpfs_init()
964 {
965
966 CTASSERT(RUMP_ETFS_SIZE_ENDOFF == RUMPBLK_SIZENOTSET);
967
968 mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE);
969 mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE);
970 }
971
972 void
973 rumpfs_done()
974 {
975
976 mutex_destroy(&reclock);
977 mutex_destroy(&etfs_lock);
978 }
979
980 int
981 rumpfs_mountroot()
982 {
983 struct mount *mp;
984 struct rumpfs_mount *rfsmp;
985 struct rumpfs_node *rn;
986 int error;
987
988 if ((error = vfs_rootmountalloc(MOUNT_RUMPFS, "rootdev", &mp)) != 0) {
989 vrele(rootvp);
990 return error;
991 }
992
993 rfsmp = kmem_alloc(sizeof(*rfsmp), KM_SLEEP);
994
995 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
996 mutex_enter(&reclock);
997 error = makevnode(mp, rn, &rfsmp->rfsmp_rvp);
998 mutex_exit(&reclock);
999 if (error)
1000 panic("could not create root vnode: %d", error);
1001 rfsmp->rfsmp_rvp->v_vflag |= VV_ROOT;
1002 VOP_UNLOCK(rfsmp->rfsmp_rvp, 0);
1003
1004 mutex_enter(&mountlist_lock);
1005 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
1006 mutex_exit(&mountlist_lock);
1007
1008 mp->mnt_data = rfsmp;
1009 mp->mnt_stat.f_namemax = MAXNAMLEN;
1010 mp->mnt_stat.f_iosize = 512;
1011 mp->mnt_flag |= MNT_LOCAL;
1012 mp->mnt_iflag |= IMNT_MPSAFE;
1013 vfs_getnewfsid(mp);
1014
1015 error = set_statvfs_info("/", UIO_SYSSPACE, "rumpfs", UIO_SYSSPACE,
1016 mp->mnt_op->vfs_name, mp, curlwp);
1017 if (error)
1018 panic("set statvfsinfo for rootfs failed");
1019
1020 vfs_unbusy(mp, false, NULL);
1021
1022 return 0;
1023 }
1024