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