rumpfs.c revision 1.59 1 /* $NetBSD: rumpfs.c,v 1.59 2010/06/30 15:40:30 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.59 2010/06/30 15:40:30 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 PNBUF_PUT(cnp->cn_pnbuf);
784 vput(dvp);
785 return rv;
786 }
787
788 static int
789 rump_vop_create(void *v)
790 {
791 struct vop_create_args /* {
792 struct vnode *a_dvp;
793 struct vnode **a_vpp;
794 struct componentname *a_cnp;
795 struct vattr *a_vap;
796 }; */ *ap = v;
797 struct vnode *dvp = ap->a_dvp;
798 struct vnode **vpp = ap->a_vpp;
799 struct componentname *cnp = ap->a_cnp;
800 struct vattr *va = ap->a_vap;
801 struct rumpfs_node *rnd = dvp->v_data, *rn;
802 int rv;
803
804 if (va->va_type != VSOCK) {
805 rv = EOPNOTSUPP;
806 goto out;
807 }
808 rn = makeprivate(VSOCK, NODEV, DEV_BSIZE);
809 rv = makevnode(dvp->v_mount, rn, vpp);
810 if (rv)
811 goto out;
812
813 makedir(rnd, cnp, rn);
814
815 out:
816 PNBUF_PUT(cnp->cn_pnbuf);
817 vput(dvp);
818 return rv;
819 }
820
821 static int
822 rump_vop_symlink(void *v)
823 {
824 struct vop_symlink_args /* {
825 struct vnode *a_dvp;
826 struct vnode **a_vpp;
827 struct componentname *a_cnp;
828 struct vattr *a_vap;
829 char *a_target;
830 }; */ *ap = v;
831 struct vnode *dvp = ap->a_dvp;
832 struct vnode **vpp = ap->a_vpp;
833 struct componentname *cnp = ap->a_cnp;
834 struct rumpfs_node *rnd = dvp->v_data, *rn;
835 const char *target = ap->a_target;
836 size_t linklen;
837 int rv;
838
839 linklen = strlen(target);
840 KASSERT(linklen < MAXPATHLEN);
841 rn = makeprivate(VLNK, NODEV, linklen);
842 rv = makevnode(dvp->v_mount, rn, vpp);
843 if (rv)
844 goto out;
845
846 makedir(rnd, cnp, rn);
847
848 KASSERT(linklen < MAXPATHLEN);
849 rn->rn_linktarg = PNBUF_GET();
850 rn->rn_linklen = linklen;
851 strcpy(rn->rn_linktarg, target);
852
853 out:
854 vput(dvp);
855 return rv;
856 }
857
858 static int
859 rump_vop_readlink(void *v)
860 {
861 struct vop_readlink_args /* {
862 struct vnode *a_vp;
863 struct uio *a_uio;
864 kauth_cred_t a_cred;
865 }; */ *ap = v;
866 struct vnode *vp = ap->a_vp;
867 struct rumpfs_node *rn = vp->v_data;
868 struct uio *uio = ap->a_uio;
869
870 return uiomove(rn->rn_linktarg, rn->rn_linklen, uio);
871 }
872
873 static int
874 rump_vop_open(void *v)
875 {
876 struct vop_open_args /* {
877 struct vnode *a_vp;
878 int a_mode;
879 kauth_cred_t a_cred;
880 } */ *ap = v;
881 struct vnode *vp = ap->a_vp;
882 struct rumpfs_node *rn = vp->v_data;
883 int mode = ap->a_mode;
884 int error = EINVAL;
885
886 if (vp->v_type != VREG)
887 return 0;
888
889 if (mode & FREAD) {
890 if (rn->rn_readfd != -1)
891 return 0;
892 rn->rn_readfd = rumpuser_open(rn->rn_hostpath,
893 O_RDONLY, &error);
894 }
895
896 if (mode & FWRITE) {
897 if (rn->rn_writefd != -1)
898 return 0;
899 rn->rn_writefd = rumpuser_open(rn->rn_hostpath,
900 O_WRONLY, &error);
901 }
902
903 return error;
904 }
905
906 /* simple readdir. event omits dotstuff and periods */
907 static int
908 rump_vop_readdir(void *v)
909 {
910 struct vop_readdir_args /* {
911 struct vnode *a_vp;
912 struct uio *a_uio;
913 kauth_cred_t a_cred;
914 int *a_eofflag;
915 off_t **a_cookies;
916 int *a_ncookies;
917 } */ *ap = v;
918 struct vnode *vp = ap->a_vp;
919 struct uio *uio = ap->a_uio;
920 struct rumpfs_node *rnd = vp->v_data;
921 struct rumpfs_dent *rdent;
922 unsigned i;
923 int rv = 0;
924
925 /* seek to current entry */
926 for (i = 0, rdent = LIST_FIRST(&rnd->rn_dir);
927 (i < uio->uio_offset) && rdent;
928 i++, rdent = LIST_NEXT(rdent, rd_entries))
929 continue;
930 if (!rdent)
931 goto out;
932
933 /* copy entries */
934 for (; rdent && uio->uio_resid > 0;
935 rdent = LIST_NEXT(rdent, rd_entries), i++) {
936 struct dirent dent;
937
938 dent.d_fileno = rdent->rd_node->rn_va.va_fileid;
939 strlcpy(dent.d_name, rdent->rd_name, sizeof(dent.d_name));
940 dent.d_namlen = strlen(dent.d_name);
941 dent.d_type = vtype2dt(rdent->rd_node->rn_va.va_type);
942 dent.d_reclen = _DIRENT_RECLEN(&dent, dent.d_namlen);
943
944 if (uio->uio_resid < dent.d_reclen) {
945 i--;
946 break;
947 }
948
949 rv = uiomove(&dent, dent.d_reclen, uio);
950 if (rv) {
951 i--;
952 break;
953 }
954 }
955
956 out:
957 if (ap->a_cookies) {
958 *ap->a_ncookies = 0;
959 *ap->a_cookies = NULL;
960 }
961 if (rdent)
962 *ap->a_eofflag = 0;
963 else
964 *ap->a_eofflag = 1;
965 uio->uio_offset = i;
966
967 return rv;
968 }
969
970 static int
971 rump_vop_read(void *v)
972 {
973 struct vop_read_args /* {
974 struct vnode *a_vp;
975 struct uio *a_uio;
976 int ioflags a_ioflag;
977 kauth_cred_t a_cred;
978 }; */ *ap = v;
979 struct vnode *vp = ap->a_vp;
980 struct rumpfs_node *rn = vp->v_data;
981 struct uio *uio = ap->a_uio;
982 uint8_t *buf;
983 size_t bufsize;
984 ssize_t n;
985 int error = 0;
986
987 bufsize = uio->uio_resid;
988 buf = kmem_alloc(bufsize, KM_SLEEP);
989 if ((n = rumpuser_pread(rn->rn_readfd, buf, bufsize,
990 uio->uio_offset + rn->rn_offset, &error)) == -1)
991 goto out;
992 KASSERT(n <= bufsize);
993 error = uiomove(buf, n, uio);
994
995 out:
996 kmem_free(buf, bufsize);
997 return error;
998 }
999
1000 static int
1001 rump_vop_write(void *v)
1002 {
1003 struct vop_read_args /* {
1004 struct vnode *a_vp;
1005 struct uio *a_uio;
1006 int ioflags a_ioflag;
1007 kauth_cred_t a_cred;
1008 }; */ *ap = v;
1009 struct vnode *vp = ap->a_vp;
1010 struct rumpfs_node *rn = vp->v_data;
1011 struct uio *uio = ap->a_uio;
1012 uint8_t *buf;
1013 size_t bufsize;
1014 ssize_t n;
1015 int error = 0;
1016
1017 bufsize = uio->uio_resid;
1018 buf = kmem_alloc(bufsize, KM_SLEEP);
1019 error = uiomove(buf, bufsize, uio);
1020 if (error)
1021 goto out;
1022 KASSERT(uio->uio_resid == 0);
1023 n = rumpuser_pwrite(rn->rn_writefd, buf, bufsize,
1024 (uio->uio_offset-bufsize) + rn->rn_offset, &error);
1025 if (n >= 0) {
1026 KASSERT(n <= bufsize);
1027 uio->uio_resid = bufsize - n;
1028 }
1029
1030 out:
1031 kmem_free(buf, bufsize);
1032 return error;
1033 }
1034
1035 static int
1036 rump_vop_success(void *v)
1037 {
1038
1039 return 0;
1040 }
1041
1042 static int
1043 rump_vop_inactive(void *v)
1044 {
1045 struct vop_inactive_args /* {
1046 struct vnode *a_vp;
1047 bool *a_recycle;
1048 } */ *ap = v;
1049 struct vnode *vp = ap->a_vp;
1050 struct rumpfs_node *rn = vp->v_data;
1051 int error;
1052
1053 if (vp->v_type == VREG) {
1054 if (rn->rn_readfd != -1) {
1055 rumpuser_close(rn->rn_readfd, &error);
1056 rn->rn_readfd = -1;
1057 }
1058 if (rn->rn_writefd != -1) {
1059 rumpuser_close(rn->rn_writefd, &error);
1060 rn->rn_writefd = -1;
1061 }
1062 }
1063 *ap->a_recycle = (rn->rn_flags & RUMPNODE_CANRECLAIM) ? true : false;
1064
1065 VOP_UNLOCK(vp);
1066 return 0;
1067 }
1068
1069 static int
1070 rump_vop_reclaim(void *v)
1071 {
1072 struct vop_reclaim_args /* {
1073 struct vnode *a_vp;
1074 } */ *ap = v;
1075 struct vnode *vp = ap->a_vp;
1076 struct rumpfs_node *rn = vp->v_data;
1077
1078 mutex_enter(&reclock);
1079 rn->rn_vp = NULL;
1080 mutex_exit(&reclock);
1081 vp->v_data = NULL;
1082
1083 if (rn->rn_flags & RUMPNODE_CANRECLAIM) {
1084 if (vp->v_type == VLNK)
1085 PNBUF_PUT(rn->rn_linktarg);
1086 if (rn->rn_hostpath)
1087 free(rn->rn_hostpath, M_TEMP);
1088 kmem_free(rn, sizeof(*rn));
1089 }
1090
1091 return 0;
1092 }
1093
1094 static int
1095 rump_vop_spec(void *v)
1096 {
1097 struct vop_generic_args *ap = v;
1098 int (**opvec)(void *);
1099
1100 switch (ap->a_desc->vdesc_offset) {
1101 case VOP_ACCESS_DESCOFFSET:
1102 case VOP_GETATTR_DESCOFFSET:
1103 case VOP_LOCK_DESCOFFSET:
1104 case VOP_UNLOCK_DESCOFFSET:
1105 case VOP_RECLAIM_DESCOFFSET:
1106 opvec = rump_vnodeop_p;
1107 break;
1108 default:
1109 opvec = spec_vnodeop_p;
1110 break;
1111 }
1112
1113 return VOCALL(opvec, ap->a_desc->vdesc_offset, v);
1114 }
1115
1116 /*
1117 * Begin vfs-level stuff
1118 */
1119
1120 VFS_PROTOS(rumpfs);
1121 struct vfsops rumpfs_vfsops = {
1122 .vfs_name = MOUNT_RUMPFS,
1123 .vfs_min_mount_data = 0,
1124 .vfs_mount = rumpfs_mount,
1125 .vfs_start = (void *)nullop,
1126 .vfs_unmount = rumpfs_unmount,
1127 .vfs_root = rumpfs_root,
1128 .vfs_quotactl = (void *)eopnotsupp,
1129 .vfs_statvfs = genfs_statvfs,
1130 .vfs_sync = (void *)nullop,
1131 .vfs_vget = rumpfs_vget,
1132 .vfs_fhtovp = (void *)eopnotsupp,
1133 .vfs_vptofh = (void *)eopnotsupp,
1134 .vfs_init = rumpfs_init,
1135 .vfs_reinit = NULL,
1136 .vfs_done = rumpfs_done,
1137 .vfs_mountroot = rumpfs_mountroot,
1138 .vfs_snapshot = (void *)eopnotsupp,
1139 .vfs_extattrctl = (void *)eopnotsupp,
1140 .vfs_suspendctl = (void *)eopnotsupp,
1141 .vfs_opv_descs = rump_opv_descs,
1142 /* vfs_refcount */
1143 /* vfs_list */
1144 };
1145
1146 int
1147 rumpfs_mount(struct mount *mp, const char *mntpath, void *arg, size_t *alen)
1148 {
1149
1150 return EOPNOTSUPP;
1151 }
1152
1153 int
1154 rumpfs_unmount(struct mount *mp, int flags)
1155 {
1156
1157 /* if going for it, just lie about it */
1158 if (panicstr)
1159 return 0;
1160
1161 return EOPNOTSUPP; /* ;) */
1162 }
1163
1164 int
1165 rumpfs_root(struct mount *mp, struct vnode **vpp)
1166 {
1167 struct rumpfs_mount *rfsmp = mp->mnt_data;
1168
1169 vget(rfsmp->rfsmp_rvp, LK_EXCLUSIVE | LK_RETRY);
1170 *vpp = rfsmp->rfsmp_rvp;
1171 return 0;
1172 }
1173
1174 int
1175 rumpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
1176 {
1177
1178 return EOPNOTSUPP;
1179 }
1180
1181 void
1182 rumpfs_init()
1183 {
1184
1185 CTASSERT(RUMP_ETFS_SIZE_ENDOFF == RUMPBLK_SIZENOTSET);
1186
1187 mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE);
1188 mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE);
1189 }
1190
1191 void
1192 rumpfs_done()
1193 {
1194
1195 mutex_destroy(&reclock);
1196 mutex_destroy(&etfs_lock);
1197 }
1198
1199 int
1200 rumpfs_mountroot()
1201 {
1202 struct mount *mp;
1203 struct rumpfs_mount *rfsmp;
1204 struct rumpfs_node *rn;
1205 int error;
1206
1207 if ((error = vfs_rootmountalloc(MOUNT_RUMPFS, "rootdev", &mp)) != 0) {
1208 vrele(rootvp);
1209 return error;
1210 }
1211
1212 rfsmp = kmem_alloc(sizeof(*rfsmp), KM_SLEEP);
1213
1214 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
1215 error = makevnode(mp, rn, &rfsmp->rfsmp_rvp);
1216 if (error)
1217 panic("could not create root vnode: %d", error);
1218 rfsmp->rfsmp_rvp->v_vflag |= VV_ROOT;
1219 VOP_UNLOCK(rfsmp->rfsmp_rvp);
1220
1221 mutex_enter(&mountlist_lock);
1222 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
1223 mutex_exit(&mountlist_lock);
1224
1225 mp->mnt_data = rfsmp;
1226 mp->mnt_stat.f_namemax = MAXNAMLEN;
1227 mp->mnt_stat.f_iosize = 512;
1228 mp->mnt_flag |= MNT_LOCAL;
1229 mp->mnt_iflag |= IMNT_MPSAFE;
1230 vfs_getnewfsid(mp);
1231
1232 error = set_statvfs_info("/", UIO_SYSSPACE, "rumpfs", UIO_SYSSPACE,
1233 mp->mnt_op->vfs_name, mp, curlwp);
1234 if (error)
1235 panic("set statvfsinfo for rootfs failed");
1236
1237 vfs_unbusy(mp, false, NULL);
1238
1239 return 0;
1240 }
1241