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