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