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