rumpfs.c revision 1.74 1 /* $NetBSD: rumpfs.c,v 1.74 2010/11/22 15:15:35 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2009, 2010 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.74 2010/11/22 15:15:35 pooka Exp $");
30
31 #include <sys/param.h>
32 #include <sys/atomic.h>
33 #include <sys/buf.h>
34 #include <sys/dirent.h>
35 #include <sys/errno.h>
36 #include <sys/filedesc.h>
37 #include <sys/fcntl.h>
38 #include <sys/kauth.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
41 #include <sys/mount.h>
42 #include <sys/namei.h>
43 #include <sys/lock.h>
44 #include <sys/lockf.h>
45 #include <sys/queue.h>
46 #include <sys/stat.h>
47 #include <sys/syscallargs.h>
48 #include <sys/vnode.h>
49 #include <sys/unistd.h>
50
51 #include <miscfs/fifofs/fifo.h>
52 #include <miscfs/specfs/specdev.h>
53 #include <miscfs/genfs/genfs.h>
54 #include <miscfs/genfs/genfs_node.h>
55
56 #include <uvm/uvm_extern.h>
57
58 #include <rump/rumpuser.h>
59
60 #include "rump_private.h"
61 #include "rump_vfs_private.h"
62
63 static int rump_vop_lookup(void *);
64 static int rump_vop_getattr(void *);
65 static int rump_vop_setattr(void *);
66 static int rump_vop_mkdir(void *);
67 static int rump_vop_rmdir(void *);
68 static int rump_vop_remove(void *);
69 static int rump_vop_mknod(void *);
70 static int rump_vop_create(void *);
71 static int rump_vop_inactive(void *);
72 static int rump_vop_reclaim(void *);
73 static int rump_vop_success(void *);
74 static int rump_vop_readdir(void *);
75 static int rump_vop_spec(void *);
76 static int rump_vop_read(void *);
77 static int rump_vop_write(void *);
78 static int rump_vop_open(void *);
79 static int rump_vop_symlink(void *);
80 static int rump_vop_readlink(void *);
81 static int rump_vop_whiteout(void *);
82 static int rump_vop_pathconf(void *);
83 static int rump_vop_bmap(void *);
84 static int rump_vop_strategy(void *);
85
86 int (**fifo_vnodeop_p)(void *);
87 const struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
88 { &vop_default_desc, vn_default_error },
89 { NULL, NULL }
90 };
91 const struct vnodeopv_desc fifo_vnodeop_opv_desc =
92 { &fifo_vnodeop_p, fifo_vnodeop_entries };
93
94 int (**rump_vnodeop_p)(void *);
95 const struct vnodeopv_entry_desc rump_vnodeop_entries[] = {
96 { &vop_default_desc, vn_default_error },
97 { &vop_lookup_desc, rump_vop_lookup },
98 { &vop_getattr_desc, rump_vop_getattr },
99 { &vop_setattr_desc, rump_vop_setattr },
100 { &vop_mkdir_desc, rump_vop_mkdir },
101 { &vop_rmdir_desc, rump_vop_rmdir },
102 { &vop_remove_desc, rump_vop_remove },
103 { &vop_mknod_desc, rump_vop_mknod },
104 { &vop_create_desc, rump_vop_create },
105 { &vop_symlink_desc, rump_vop_symlink },
106 { &vop_readlink_desc, rump_vop_readlink },
107 { &vop_access_desc, rump_vop_success },
108 { &vop_readdir_desc, rump_vop_readdir },
109 { &vop_read_desc, rump_vop_read },
110 { &vop_write_desc, rump_vop_write },
111 { &vop_open_desc, rump_vop_open },
112 { &vop_close_desc, genfs_nullop },
113 { &vop_seek_desc, genfs_seek },
114 { &vop_getpages_desc, genfs_getpages },
115 { &vop_putpages_desc, genfs_putpages },
116 { &vop_whiteout_desc, rump_vop_whiteout },
117 { &vop_fsync_desc, rump_vop_success },
118 { &vop_lock_desc, genfs_lock },
119 { &vop_unlock_desc, genfs_unlock },
120 { &vop_islocked_desc, genfs_islocked },
121 { &vop_inactive_desc, rump_vop_inactive },
122 { &vop_reclaim_desc, rump_vop_reclaim },
123 { &vop_link_desc, genfs_eopnotsupp },
124 { &vop_pathconf_desc, rump_vop_pathconf },
125 { &vop_bmap_desc, rump_vop_bmap },
126 { &vop_strategy_desc, rump_vop_strategy },
127 { NULL, NULL }
128 };
129 const struct vnodeopv_desc rump_vnodeop_opv_desc =
130 { &rump_vnodeop_p, rump_vnodeop_entries };
131
132 int (**rump_specop_p)(void *);
133 const struct vnodeopv_entry_desc rump_specop_entries[] = {
134 { &vop_default_desc, rump_vop_spec },
135 { NULL, NULL }
136 };
137 const struct vnodeopv_desc rump_specop_opv_desc =
138 { &rump_specop_p, rump_specop_entries };
139
140 const struct vnodeopv_desc * const rump_opv_descs[] = {
141 &rump_vnodeop_opv_desc,
142 &rump_specop_opv_desc,
143 NULL
144 };
145
146 #define RUMPFS_WHITEOUT NULL
147 #define RDENT_ISWHITEOUT(rdp) (rdp->rd_node == RUMPFS_WHITEOUT)
148 struct rumpfs_dent {
149 char *rd_name;
150 int rd_namelen;
151 struct rumpfs_node *rd_node;
152
153 LIST_ENTRY(rumpfs_dent) rd_entries;
154 };
155
156 struct genfs_ops rumpfs_genfsops = {
157 .gop_size = genfs_size,
158 .gop_write = genfs_gop_write,
159
160 /* optional */
161 .gop_alloc = NULL,
162 .gop_markupdate = NULL,
163 };
164
165 struct rumpfs_node {
166 struct genfs_node rn_gn;
167 struct vattr rn_va;
168 struct vnode *rn_vp;
169 char *rn_hostpath;
170 int rn_flags;
171
172 union {
173 struct { /* VREG */
174 int readfd;
175 int writefd;
176 uint64_t offset;
177 } reg;
178 struct {
179 void *data;
180 size_t dlen;
181 } reg_noet;
182 struct { /* VDIR */
183 LIST_HEAD(, rumpfs_dent) dents;
184 struct rumpfs_node *parent;
185 int flags;
186 } dir;
187 struct {
188 char *target;
189 size_t len;
190 } link;
191 } rn_u;
192 };
193 #define rn_readfd rn_u.reg.readfd
194 #define rn_writefd rn_u.reg.writefd
195 #define rn_offset rn_u.reg.offset
196 #define rn_data rn_u.reg_noet.data
197 #define rn_dlen rn_u.reg_noet.dlen
198 #define rn_dir rn_u.dir.dents
199 #define rn_parent rn_u.dir.parent
200 #define rn_linktarg rn_u.link.target
201 #define rn_linklen rn_u.link.len
202
203 #define RUMPNODE_CANRECLAIM 0x01
204 #define RUMPNODE_DIR_ET 0x02
205 #define RUMPNODE_DIR_ETSUBS 0x04
206 #define RUMPNODE_ET_PHONE_HOST 0x10
207
208 struct rumpfs_mount {
209 struct vnode *rfsmp_rvp;
210 };
211
212 static struct rumpfs_node *makeprivate(enum vtype, dev_t, off_t, bool);
213
214 /*
215 * Extra Terrestrial stuff. We map a given key (pathname) to a file on
216 * the host FS. ET phones home only from the root node of rumpfs.
217 *
218 * When an etfs node is removed, a vnode potentially behind it is not
219 * immediately recycled.
220 */
221
222 struct etfs {
223 char et_key[MAXPATHLEN];
224 size_t et_keylen;
225 bool et_prefixkey;
226 bool et_removing;
227 devminor_t et_blkmin;
228
229 LIST_ENTRY(etfs) et_entries;
230
231 struct rumpfs_node *et_rn;
232 };
233 static kmutex_t etfs_lock;
234 static LIST_HEAD(, etfs) etfs_list = LIST_HEAD_INITIALIZER(etfs_list);
235
236 static enum vtype
237 ettype_to_vtype(enum rump_etfs_type et)
238 {
239 enum vtype vt;
240
241 switch (et) {
242 case RUMP_ETFS_REG:
243 vt = VREG;
244 break;
245 case RUMP_ETFS_BLK:
246 vt = VBLK;
247 break;
248 case RUMP_ETFS_CHR:
249 vt = VCHR;
250 break;
251 case RUMP_ETFS_DIR:
252 vt = VDIR;
253 break;
254 case RUMP_ETFS_DIR_SUBDIRS:
255 vt = VDIR;
256 break;
257 default:
258 panic("invalid et type: %d", et);
259 }
260
261 return vt;
262 }
263
264 static enum vtype
265 hft_to_vtype(int hft)
266 {
267 enum vtype vt;
268
269 switch (hft) {
270 case RUMPUSER_FT_OTHER:
271 vt = VNON;
272 break;
273 case RUMPUSER_FT_DIR:
274 vt = VDIR;
275 break;
276 case RUMPUSER_FT_REG:
277 vt = VREG;
278 break;
279 case RUMPUSER_FT_BLK:
280 vt = VBLK;
281 break;
282 case RUMPUSER_FT_CHR:
283 vt = VCHR;
284 break;
285 default:
286 vt = VNON;
287 break;
288 }
289
290 return vt;
291 }
292
293 static bool
294 etfs_find(const char *key, struct etfs **etp, bool forceprefix)
295 {
296 struct etfs *et;
297 size_t keylen = strlen(key);
298
299 KASSERT(mutex_owned(&etfs_lock));
300
301 LIST_FOREACH(et, &etfs_list, et_entries) {
302 if ((keylen == et->et_keylen || et->et_prefixkey || forceprefix)
303 && strncmp(key, et->et_key, et->et_keylen) == 0) {
304 if (etp)
305 *etp = et;
306 return true;
307 }
308 }
309
310 return false;
311 }
312
313 #define REGDIR(ftype) \
314 ((ftype) == RUMP_ETFS_DIR || (ftype) == RUMP_ETFS_DIR_SUBDIRS)
315 static int
316 doregister(const char *key, const char *hostpath,
317 enum rump_etfs_type ftype, uint64_t begin, uint64_t size)
318 {
319 char buf[9];
320 struct etfs *et;
321 struct rumpfs_node *rn;
322 uint64_t fsize;
323 dev_t rdev = NODEV;
324 devminor_t dmin = -1;
325 int hft, error;
326
327 if (rumpuser_getfileinfo(hostpath, &fsize, &hft, &error))
328 return error;
329
330 /* etfs directory requires a directory on the host */
331 if (REGDIR(ftype)) {
332 if (hft != RUMPUSER_FT_DIR)
333 return ENOTDIR;
334 if (begin != 0)
335 return EISDIR;
336 if (size != RUMP_ETFS_SIZE_ENDOFF)
337 return EISDIR;
338 size = fsize;
339 } else {
340 if (begin > fsize)
341 return EINVAL;
342 if (size == RUMP_ETFS_SIZE_ENDOFF)
343 size = fsize - begin;
344 if (begin + size > fsize)
345 return EINVAL;
346 }
347
348 if (ftype == RUMP_ETFS_BLK || ftype == RUMP_ETFS_CHR) {
349 error = rumpblk_register(hostpath, &dmin, begin, size);
350 if (error != 0) {
351 return error;
352 }
353 rdev = makedev(RUMPBLK_DEVMAJOR, dmin);
354 }
355
356 et = kmem_alloc(sizeof(*et), KM_SLEEP);
357 strcpy(et->et_key, key);
358 et->et_keylen = strlen(et->et_key);
359 et->et_rn = rn = makeprivate(ettype_to_vtype(ftype), rdev, size, true);
360 et->et_removing = false;
361 et->et_blkmin = dmin;
362
363 rn->rn_flags |= RUMPNODE_ET_PHONE_HOST;
364
365 if (ftype == RUMP_ETFS_REG || REGDIR(ftype) || et->et_blkmin != -1) {
366 size_t len = strlen(hostpath)+1;
367
368 rn->rn_hostpath = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
369 memcpy(rn->rn_hostpath, hostpath, len);
370 rn->rn_offset = begin;
371 }
372
373 if (REGDIR(ftype)) {
374 rn->rn_flags |= RUMPNODE_DIR_ET;
375 et->et_prefixkey = true;
376 } else {
377 et->et_prefixkey = false;
378 }
379
380 if (ftype == RUMP_ETFS_DIR_SUBDIRS)
381 rn->rn_flags |= RUMPNODE_DIR_ETSUBS;
382
383 mutex_enter(&etfs_lock);
384 if (etfs_find(key, NULL, REGDIR(ftype))) {
385 mutex_exit(&etfs_lock);
386 if (et->et_blkmin != -1)
387 rumpblk_deregister(hostpath);
388 if (et->et_rn->rn_hostpath != NULL)
389 free(et->et_rn->rn_hostpath, M_TEMP);
390 kmem_free(et->et_rn, sizeof(*et->et_rn));
391 kmem_free(et, sizeof(*et));
392 return EEXIST;
393 }
394 LIST_INSERT_HEAD(&etfs_list, et, et_entries);
395 mutex_exit(&etfs_lock);
396
397 if (ftype == RUMP_ETFS_BLK) {
398 format_bytes(buf, sizeof(buf), size);
399 aprint_verbose("%s: hostpath %s (%s)\n", key, hostpath, buf);
400 }
401
402 return 0;
403 }
404 #undef REGDIR
405
406 int
407 rump_etfs_register(const char *key, const char *hostpath,
408 enum rump_etfs_type ftype)
409 {
410
411 return doregister(key, hostpath, ftype, 0, RUMP_ETFS_SIZE_ENDOFF);
412 }
413
414 int
415 rump_etfs_register_withsize(const char *key, const char *hostpath,
416 enum rump_etfs_type ftype, uint64_t begin, uint64_t size)
417 {
418
419 return doregister(key, hostpath, ftype, begin, size);
420 }
421
422 /* remove etfs mapping. caller's responsibility to make sure it's not in use */
423 int
424 rump_etfs_remove(const char *key)
425 {
426 struct etfs *et;
427 size_t keylen = strlen(key);
428 int rv;
429
430 mutex_enter(&etfs_lock);
431 LIST_FOREACH(et, &etfs_list, et_entries) {
432 if (keylen == et->et_keylen && strcmp(et->et_key, key) == 0) {
433 if (et->et_removing)
434 et = NULL;
435 else
436 et->et_removing = true;
437 break;
438 }
439 }
440 mutex_exit(&etfs_lock);
441 if (!et)
442 return ENOENT;
443
444 /*
445 * ok, we know what we want to remove and have signalled there
446 * actually are men at work. first, unregister from rumpblk
447 */
448 if (et->et_blkmin != -1) {
449 rv = rumpblk_deregister(et->et_rn->rn_hostpath);
450 } else {
451 rv = 0;
452 }
453 KASSERT(rv == 0);
454
455 /* then do the actual removal */
456 mutex_enter(&etfs_lock);
457 LIST_REMOVE(et, et_entries);
458 mutex_exit(&etfs_lock);
459
460 /* node is unreachable, safe to nuke all device copies */
461 if (et->et_blkmin != -1)
462 vdevgone(RUMPBLK_DEVMAJOR, et->et_blkmin, et->et_blkmin, VBLK);
463
464 if (et->et_rn->rn_hostpath != NULL)
465 free(et->et_rn->rn_hostpath, M_TEMP);
466 kmem_free(et->et_rn, sizeof(*et->et_rn));
467 kmem_free(et, sizeof(*et));
468
469 return 0;
470 }
471
472 /*
473 * rumpfs
474 */
475
476 #define INO_WHITEOUT 1
477 static int lastino = 2;
478 static kmutex_t reclock;
479
480 static struct rumpfs_node *
481 makeprivate(enum vtype vt, dev_t rdev, off_t size, bool et)
482 {
483 struct rumpfs_node *rn;
484 struct vattr *va;
485 struct timespec ts;
486
487 rn = kmem_zalloc(sizeof(*rn), KM_SLEEP);
488
489 switch (vt) {
490 case VDIR:
491 LIST_INIT(&rn->rn_dir);
492 break;
493 case VREG:
494 if (et) {
495 rn->rn_readfd = -1;
496 rn->rn_writefd = -1;
497 }
498 break;
499 default:
500 break;
501 }
502
503 nanotime(&ts);
504
505 va = &rn->rn_va;
506 va->va_type = vt;
507 va->va_mode = 0755;
508 if (vt == VDIR)
509 va->va_nlink = 2;
510 else
511 va->va_nlink = 1;
512 va->va_uid = 0;
513 va->va_gid = 0;
514 va->va_fsid =
515 va->va_fileid = atomic_inc_uint_nv(&lastino);
516 va->va_size = size;
517 va->va_blocksize = 512;
518 va->va_atime = ts;
519 va->va_mtime = ts;
520 va->va_ctime = ts;
521 va->va_birthtime = ts;
522 va->va_gen = 0;
523 va->va_flags = 0;
524 va->va_rdev = rdev;
525 va->va_bytes = 512;
526 va->va_filerev = 0;
527 va->va_vaflags = 0;
528
529 return rn;
530 }
531
532 static int
533 makevnode(struct mount *mp, struct rumpfs_node *rn, struct vnode **vpp)
534 {
535 struct vnode *vp;
536 int (**vpops)(void *);
537 struct vattr *va = &rn->rn_va;
538 int rv;
539
540 KASSERT(!mutex_owned(&reclock));
541
542 if (va->va_type == VCHR || va->va_type == VBLK) {
543 vpops = rump_specop_p;
544 } else {
545 vpops = rump_vnodeop_p;
546 }
547
548 rv = getnewvnode(VT_RUMP, mp, vpops, &vp);
549 if (rv)
550 return rv;
551
552 vp->v_size = vp->v_writesize = va->va_size;
553 vp->v_type = va->va_type;
554
555 if (vpops == rump_specop_p) {
556 spec_node_init(vp, va->va_rdev);
557 }
558 vp->v_data = rn;
559
560 genfs_node_init(vp, &rumpfs_genfsops);
561 vn_lock(vp, LK_RETRY | LK_EXCLUSIVE);
562 mutex_enter(&reclock);
563 rn->rn_vp = vp;
564 mutex_exit(&reclock);
565
566 *vpp = vp;
567
568 return 0;
569 }
570
571
572 static void
573 makedir(struct rumpfs_node *rnd,
574 struct componentname *cnp, struct rumpfs_node *rn)
575 {
576 struct rumpfs_dent *rdent;
577
578 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
579 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
580 rdent->rd_node = rn;
581 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
582 rdent->rd_namelen = strlen(rdent->rd_name);
583
584 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
585 }
586
587 static void
588 freedir(struct rumpfs_node *rnd, struct componentname *cnp)
589 {
590 struct rumpfs_dent *rd = NULL;
591
592 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
593 if (rd->rd_namelen == cnp->cn_namelen &&
594 strncmp(rd->rd_name, cnp->cn_nameptr,
595 cnp->cn_namelen) == 0)
596 break;
597 }
598 if (rd == NULL)
599 panic("could not find directory entry: %s", cnp->cn_nameptr);
600
601 LIST_REMOVE(rd, rd_entries);
602 kmem_free(rd->rd_name, rd->rd_namelen+1);
603 kmem_free(rd, sizeof(*rd));
604 }
605
606 /*
607 * Simple lookup for rump file systems.
608 *
609 * uhm, this is twisted. C F C C, hope of C C F C looming
610 */
611 static int
612 rump_vop_lookup(void *v)
613 {
614 struct vop_lookup_args /* {
615 struct vnode *a_dvp;
616 struct vnode **a_vpp;
617 struct componentname *a_cnp;
618 }; */ *ap = v;
619 struct componentname *cnp = ap->a_cnp;
620 struct vnode *dvp = ap->a_dvp;
621 struct vnode **vpp = ap->a_vpp;
622 struct vnode *vp;
623 struct rumpfs_node *rnd = dvp->v_data, *rn;
624 struct rumpfs_dent *rd = NULL;
625 struct etfs *et;
626 bool dotdot = (cnp->cn_flags & ISDOTDOT) != 0;
627 int rv = 0;
628
629 /* check for dot, return directly if the case */
630 if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
631 vref(dvp);
632 *vpp = dvp;
633 return 0;
634 }
635
636 /* we don't do rename */
637 if (!(((cnp->cn_flags & ISLASTCN) == 0) || (cnp->cn_nameiop != RENAME)))
638 return EOPNOTSUPP;
639
640 /* check for etfs */
641 if (dvp == rootvnode && cnp->cn_nameiop == LOOKUP) {
642 bool found;
643 mutex_enter(&etfs_lock);
644 found = etfs_find(cnp->cn_pnbuf, &et, false);
645 mutex_exit(&etfs_lock);
646
647 if (found) {
648 char *offset;
649
650 offset = strstr(cnp->cn_pnbuf, et->et_key);
651 KASSERT(offset);
652
653 rn = et->et_rn;
654 cnp->cn_consume += et->et_keylen
655 - (cnp->cn_nameptr - offset) - cnp->cn_namelen;
656 if (rn->rn_va.va_type != VDIR)
657 cnp->cn_flags &= ~REQUIREDIR;
658 goto getvnode;
659 }
660 }
661
662 if (rnd->rn_flags & RUMPNODE_DIR_ET) {
663 uint64_t fsize;
664 char *newpath;
665 size_t newpathlen;
666 int hft, error;
667
668 if (dotdot)
669 return EOPNOTSUPP;
670
671 newpathlen = strlen(rnd->rn_hostpath) + 1 + cnp->cn_namelen + 1;
672 newpath = malloc(newpathlen, M_TEMP, M_WAITOK);
673
674 strlcpy(newpath, rnd->rn_hostpath, newpathlen);
675 strlcat(newpath, "/", newpathlen);
676 strlcat(newpath, cnp->cn_nameptr, newpathlen);
677
678 if (rumpuser_getfileinfo(newpath, &fsize, &hft, &error)) {
679 free(newpath, M_TEMP);
680 return error;
681 }
682
683 /* allow only dirs and regular files */
684 if (hft != RUMPUSER_FT_REG && hft != RUMPUSER_FT_DIR) {
685 free(newpath, M_TEMP);
686 return ENOENT;
687 }
688
689 rn = makeprivate(hft_to_vtype(hft), NODEV, fsize, true);
690 rn->rn_flags |= RUMPNODE_CANRECLAIM;
691 if (rnd->rn_flags & RUMPNODE_DIR_ETSUBS) {
692 rn->rn_flags |= RUMPNODE_DIR_ET | RUMPNODE_DIR_ETSUBS;
693 rn->rn_flags |= RUMPNODE_ET_PHONE_HOST;
694 }
695 rn->rn_hostpath = newpath;
696
697 goto getvnode;
698 } else {
699 if (dotdot) {
700 rn = rnd->rn_parent;
701 goto getvnode;
702 } else {
703 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
704 if (rd->rd_namelen == cnp->cn_namelen &&
705 strncmp(rd->rd_name, cnp->cn_nameptr,
706 cnp->cn_namelen) == 0)
707 break;
708 }
709 }
710 }
711
712 if (!rd && ((cnp->cn_flags & ISLASTCN) == 0||cnp->cn_nameiop != CREATE))
713 return ENOENT;
714
715 if (!rd && (cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
716 cnp->cn_flags |= SAVENAME;
717 return EJUSTRETURN;
718 }
719 if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == DELETE)
720 cnp->cn_flags |= SAVENAME;
721
722 rn = rd->rd_node;
723
724 getvnode:
725 KASSERT(rn);
726 if (dotdot)
727 VOP_UNLOCK(dvp);
728 mutex_enter(&reclock);
729 if ((vp = rn->rn_vp)) {
730 mutex_enter(&vp->v_interlock);
731 mutex_exit(&reclock);
732 if (vget(vp, LK_EXCLUSIVE)) {
733 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
734 goto getvnode;
735 }
736 *vpp = vp;
737 } else {
738 mutex_exit(&reclock);
739 rv = makevnode(dvp->v_mount, rn, vpp);
740 }
741 if (dotdot)
742 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
743
744 return rv;
745 }
746
747 static int
748 rump_vop_getattr(void *v)
749 {
750 struct vop_getattr_args /* {
751 struct vnode *a_vp;
752 struct vattr *a_vap;
753 kauth_cred_t a_cred;
754 } */ *ap = v;
755 struct rumpfs_node *rn = ap->a_vp->v_data;
756
757 memcpy(ap->a_vap, &rn->rn_va, sizeof(struct vattr));
758 return 0;
759 }
760
761 static int
762 rump_vop_setattr(void *v)
763 {
764 struct vop_getattr_args /* {
765 struct vnode *a_vp;
766 struct vattr *a_vap;
767 kauth_cred_t a_cred;
768 } */ *ap = v;
769 struct rumpfs_node *rn = ap->a_vp->v_data;
770
771 memcpy(&rn->rn_va, ap->a_vap, sizeof(struct vattr));
772 return 0;
773 }
774
775 static int
776 rump_vop_mkdir(void *v)
777 {
778 struct vop_mkdir_args /* {
779 struct vnode *a_dvp;
780 struct vnode **a_vpp;
781 struct componentname *a_cnp;
782 struct vattr *a_vap;
783 }; */ *ap = v;
784 struct vnode *dvp = ap->a_dvp;
785 struct vnode **vpp = ap->a_vpp;
786 struct componentname *cnp = ap->a_cnp;
787 struct rumpfs_node *rnd = dvp->v_data, *rn;
788 int rv = 0;
789
790 rn = makeprivate(VDIR, NODEV, DEV_BSIZE, false);
791 rn->rn_parent = rnd;
792 rv = makevnode(dvp->v_mount, rn, vpp);
793 if (rv)
794 goto out;
795
796 makedir(rnd, cnp, rn);
797
798 out:
799 PNBUF_PUT(cnp->cn_pnbuf);
800 vput(dvp);
801 return rv;
802 }
803
804 static int
805 rump_vop_rmdir(void *v)
806 {
807 struct vop_rmdir_args /* {
808 struct vnode *a_dvp;
809 struct vnode *a_vp;
810 struct componentname *a_cnp;
811 }; */ *ap = v;
812 struct vnode *dvp = ap->a_dvp;
813 struct vnode *vp = ap->a_vp;
814 struct componentname *cnp = ap->a_cnp;
815 struct rumpfs_node *rnd = dvp->v_data;
816 struct rumpfs_node *rn = vp->v_data;
817 int rv = 0;
818
819 if (!LIST_EMPTY(&rn->rn_dir)) {
820 rv = ENOTEMPTY;
821 goto out;
822 }
823
824 freedir(rnd, cnp);
825 rn->rn_flags |= RUMPNODE_CANRECLAIM;
826
827 out:
828 PNBUF_PUT(cnp->cn_pnbuf);
829 vput(dvp);
830 vput(vp);
831
832 return rv;
833 }
834
835 static int
836 rump_vop_remove(void *v)
837 {
838 struct vop_rmdir_args /* {
839 struct vnode *a_dvp;
840 struct vnode *a_vp;
841 struct componentname *a_cnp;
842 }; */ *ap = v;
843 struct vnode *dvp = ap->a_dvp;
844 struct vnode *vp = ap->a_vp;
845 struct componentname *cnp = ap->a_cnp;
846 struct rumpfs_node *rnd = dvp->v_data;
847 struct rumpfs_node *rn = vp->v_data;
848 int rv = 0;
849
850 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST)
851 return EOPNOTSUPP;
852
853 if (vp->v_type == VREG) {
854 rump_hyperfree(rn->rn_data, rn->rn_dlen);
855 }
856
857 freedir(rnd, cnp);
858 rn->rn_flags |= RUMPNODE_CANRECLAIM;
859
860 PNBUF_PUT(cnp->cn_pnbuf);
861 vput(dvp);
862 vput(vp);
863
864 return rv;
865 }
866
867 static int
868 rump_vop_mknod(void *v)
869 {
870 struct vop_mknod_args /* {
871 struct vnode *a_dvp;
872 struct vnode **a_vpp;
873 struct componentname *a_cnp;
874 struct vattr *a_vap;
875 }; */ *ap = v;
876 struct vnode *dvp = ap->a_dvp;
877 struct vnode **vpp = ap->a_vpp;
878 struct componentname *cnp = ap->a_cnp;
879 struct vattr *va = ap->a_vap;
880 struct rumpfs_node *rnd = dvp->v_data, *rn;
881 int rv;
882
883 rn = makeprivate(va->va_type, va->va_rdev, DEV_BSIZE, false);
884 rv = makevnode(dvp->v_mount, rn, vpp);
885 if (rv)
886 goto out;
887
888 makedir(rnd, cnp, rn);
889
890 out:
891 PNBUF_PUT(cnp->cn_pnbuf);
892 vput(dvp);
893 return rv;
894 }
895
896 static int
897 rump_vop_create(void *v)
898 {
899 struct vop_create_args /* {
900 struct vnode *a_dvp;
901 struct vnode **a_vpp;
902 struct componentname *a_cnp;
903 struct vattr *a_vap;
904 }; */ *ap = v;
905 struct vnode *dvp = ap->a_dvp;
906 struct vnode **vpp = ap->a_vpp;
907 struct componentname *cnp = ap->a_cnp;
908 struct vattr *va = ap->a_vap;
909 struct rumpfs_node *rnd = dvp->v_data, *rn;
910 off_t newsize;
911 int rv;
912
913 newsize = va->va_type == VSOCK ? DEV_BSIZE : 0;
914 rn = makeprivate(va->va_type, NODEV, newsize, false);
915 rv = makevnode(dvp->v_mount, rn, vpp);
916 if (rv)
917 goto out;
918
919 makedir(rnd, cnp, rn);
920
921 out:
922 PNBUF_PUT(cnp->cn_pnbuf);
923 vput(dvp);
924 return rv;
925 }
926
927 static int
928 rump_vop_symlink(void *v)
929 {
930 struct vop_symlink_args /* {
931 struct vnode *a_dvp;
932 struct vnode **a_vpp;
933 struct componentname *a_cnp;
934 struct vattr *a_vap;
935 char *a_target;
936 }; */ *ap = v;
937 struct vnode *dvp = ap->a_dvp;
938 struct vnode **vpp = ap->a_vpp;
939 struct componentname *cnp = ap->a_cnp;
940 struct rumpfs_node *rnd = dvp->v_data, *rn;
941 const char *target = ap->a_target;
942 size_t linklen;
943 int rv;
944
945 linklen = strlen(target);
946 KASSERT(linklen < MAXPATHLEN);
947 rn = makeprivate(VLNK, NODEV, linklen, false);
948 rv = makevnode(dvp->v_mount, rn, vpp);
949 if (rv)
950 goto out;
951
952 makedir(rnd, cnp, rn);
953
954 KASSERT(linklen < MAXPATHLEN);
955 rn->rn_linktarg = PNBUF_GET();
956 rn->rn_linklen = linklen;
957 strcpy(rn->rn_linktarg, target);
958
959 out:
960 vput(dvp);
961 return rv;
962 }
963
964 static int
965 rump_vop_readlink(void *v)
966 {
967 struct vop_readlink_args /* {
968 struct vnode *a_vp;
969 struct uio *a_uio;
970 kauth_cred_t a_cred;
971 }; */ *ap = v;
972 struct vnode *vp = ap->a_vp;
973 struct rumpfs_node *rn = vp->v_data;
974 struct uio *uio = ap->a_uio;
975
976 return uiomove(rn->rn_linktarg, rn->rn_linklen, uio);
977 }
978
979 static int
980 rump_vop_whiteout(void *v)
981 {
982 struct vop_whiteout_args /* {
983 struct vnode *a_dvp;
984 struct componentname *a_cnp;
985 int a_flags;
986 } */ *ap = v;
987 struct vnode *dvp = ap->a_dvp;
988 struct rumpfs_node *rnd = dvp->v_data;
989 struct componentname *cnp = ap->a_cnp;
990 int flags = ap->a_flags;
991
992 switch (flags) {
993 case LOOKUP:
994 break;
995 case CREATE:
996 makedir(rnd, cnp, RUMPFS_WHITEOUT);
997 break;
998 case DELETE:
999 cnp->cn_flags &= ~DOWHITEOUT; /* cargo culting never fails ? */
1000 freedir(rnd, cnp);
1001 break;
1002 default:
1003 panic("unknown whiteout op %d", flags);
1004 }
1005
1006 return 0;
1007 }
1008
1009 static int
1010 rump_vop_open(void *v)
1011 {
1012 struct vop_open_args /* {
1013 struct vnode *a_vp;
1014 int a_mode;
1015 kauth_cred_t a_cred;
1016 } */ *ap = v;
1017 struct vnode *vp = ap->a_vp;
1018 struct rumpfs_node *rn = vp->v_data;
1019 int mode = ap->a_mode;
1020 int error = EINVAL;
1021
1022 if (vp->v_type != VREG || (rn->rn_flags & RUMPNODE_ET_PHONE_HOST) == 0)
1023 return 0;
1024
1025 if (mode & FREAD) {
1026 if (rn->rn_readfd != -1)
1027 return 0;
1028 rn->rn_readfd = rumpuser_open(rn->rn_hostpath,
1029 O_RDONLY, &error);
1030 }
1031
1032 if (mode & FWRITE) {
1033 if (rn->rn_writefd != -1)
1034 return 0;
1035 rn->rn_writefd = rumpuser_open(rn->rn_hostpath,
1036 O_WRONLY, &error);
1037 }
1038
1039 return error;
1040 }
1041
1042 /* simple readdir. event omits dotstuff and periods */
1043 static int
1044 rump_vop_readdir(void *v)
1045 {
1046 struct vop_readdir_args /* {
1047 struct vnode *a_vp;
1048 struct uio *a_uio;
1049 kauth_cred_t a_cred;
1050 int *a_eofflag;
1051 off_t **a_cookies;
1052 int *a_ncookies;
1053 } */ *ap = v;
1054 struct vnode *vp = ap->a_vp;
1055 struct uio *uio = ap->a_uio;
1056 struct rumpfs_node *rnd = vp->v_data;
1057 struct rumpfs_dent *rdent;
1058 unsigned i;
1059 int rv = 0;
1060
1061 /* seek to current entry */
1062 for (i = 0, rdent = LIST_FIRST(&rnd->rn_dir);
1063 (i < uio->uio_offset) && rdent;
1064 i++, rdent = LIST_NEXT(rdent, rd_entries))
1065 continue;
1066 if (!rdent)
1067 goto out;
1068
1069 /* copy entries */
1070 for (; rdent && uio->uio_resid > 0;
1071 rdent = LIST_NEXT(rdent, rd_entries), i++) {
1072 struct dirent dent;
1073
1074 strlcpy(dent.d_name, rdent->rd_name, sizeof(dent.d_name));
1075 dent.d_namlen = strlen(dent.d_name);
1076 dent.d_reclen = _DIRENT_RECLEN(&dent, dent.d_namlen);
1077
1078 if (__predict_false(RDENT_ISWHITEOUT(rdent))) {
1079 dent.d_fileno = INO_WHITEOUT;
1080 dent.d_type = DT_WHT;
1081 } else {
1082 dent.d_fileno = rdent->rd_node->rn_va.va_fileid;
1083 dent.d_type = vtype2dt(rdent->rd_node->rn_va.va_type);
1084 }
1085
1086 if (uio->uio_resid < dent.d_reclen) {
1087 i--;
1088 break;
1089 }
1090
1091 rv = uiomove(&dent, dent.d_reclen, uio);
1092 if (rv) {
1093 i--;
1094 break;
1095 }
1096 }
1097
1098 out:
1099 if (ap->a_cookies) {
1100 *ap->a_ncookies = 0;
1101 *ap->a_cookies = NULL;
1102 }
1103 if (rdent)
1104 *ap->a_eofflag = 0;
1105 else
1106 *ap->a_eofflag = 1;
1107 uio->uio_offset = i;
1108
1109 return rv;
1110 }
1111
1112 static int
1113 etread(struct rumpfs_node *rn, struct uio *uio)
1114 {
1115 uint8_t *buf;
1116 size_t bufsize;
1117 ssize_t n;
1118 int error = 0;
1119
1120 bufsize = uio->uio_resid;
1121 buf = kmem_alloc(bufsize, KM_SLEEP);
1122 if ((n = rumpuser_pread(rn->rn_readfd, buf, bufsize,
1123 uio->uio_offset + rn->rn_offset, &error)) == -1)
1124 goto out;
1125 KASSERT(n <= bufsize);
1126 error = uiomove(buf, n, uio);
1127
1128 out:
1129 kmem_free(buf, bufsize);
1130 return error;
1131
1132 }
1133
1134 static int
1135 rump_vop_read(void *v)
1136 {
1137 struct vop_read_args /* {
1138 struct vnode *a_vp;
1139 struct uio *a_uio;
1140 int ioflags a_ioflag;
1141 kauth_cred_t a_cred;
1142 }; */ *ap = v;
1143 struct vnode *vp = ap->a_vp;
1144 struct rumpfs_node *rn = vp->v_data;
1145 struct uio *uio = ap->a_uio;
1146 const int advice = IO_ADV_DECODE(ap->a_ioflag);
1147 off_t chunk;
1148 int error = 0;
1149
1150 /* et op? */
1151 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST)
1152 return etread(rn, uio);
1153
1154 /* otherwise, it's off to ubc with us */
1155 while (uio->uio_resid > 0) {
1156 chunk = MIN(uio->uio_resid, (off_t)rn->rn_dlen-uio->uio_offset);
1157 if (chunk == 0)
1158 break;
1159 error = ubc_uiomove(&vp->v_uobj, uio, chunk, advice,
1160 UBC_READ | UBC_PARTIALOK | UBC_WANT_UNMAP(vp)?UBC_UNMAP:0);
1161 if (error)
1162 break;
1163 }
1164
1165 return error;
1166 }
1167
1168 static int
1169 etwrite(struct rumpfs_node *rn, struct uio *uio)
1170 {
1171 uint8_t *buf;
1172 size_t bufsize;
1173 ssize_t n;
1174 int error = 0;
1175
1176 bufsize = uio->uio_resid;
1177 buf = kmem_alloc(bufsize, KM_SLEEP);
1178 error = uiomove(buf, bufsize, uio);
1179 if (error)
1180 goto out;
1181 KASSERT(uio->uio_resid == 0);
1182 n = rumpuser_pwrite(rn->rn_writefd, buf, bufsize,
1183 (uio->uio_offset-bufsize) + rn->rn_offset, &error);
1184 if (n >= 0) {
1185 KASSERT(n <= bufsize);
1186 uio->uio_resid = bufsize - n;
1187 }
1188
1189 out:
1190 kmem_free(buf, bufsize);
1191 return error;
1192 }
1193
1194 static int
1195 rump_vop_write(void *v)
1196 {
1197 struct vop_read_args /* {
1198 struct vnode *a_vp;
1199 struct uio *a_uio;
1200 int ioflags a_ioflag;
1201 kauth_cred_t a_cred;
1202 }; */ *ap = v;
1203 struct vnode *vp = ap->a_vp;
1204 struct rumpfs_node *rn = vp->v_data;
1205 struct uio *uio = ap->a_uio;
1206 const int advice = IO_ADV_DECODE(ap->a_ioflag);
1207 void *olddata;
1208 size_t oldlen, newlen;
1209 off_t chunk;
1210 int error = 0;
1211 bool allocd = false;
1212
1213 /* consult et? */
1214 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST)
1215 return etwrite(rn, uio);
1216
1217 /*
1218 * Otherwise, it's a case of ubcmove.
1219 */
1220
1221 /*
1222 * First, make sure we have enough storage.
1223 *
1224 * No, you don't need to tell me it's not very efficient.
1225 * No, it doesn't really support sparse files, just fakes it.
1226 */
1227 newlen = uio->uio_offset + uio->uio_resid;
1228 oldlen = 0; /* XXXgcc */
1229 olddata = NULL;
1230 if (rn->rn_dlen < newlen) {
1231 oldlen = rn->rn_dlen;
1232 olddata = rn->rn_data;
1233
1234 rn->rn_data = rump_hypermalloc(newlen, 0, true, "rumpfs");
1235 rn->rn_dlen = newlen;
1236 memset(rn->rn_data, 0, newlen);
1237 memcpy(rn->rn_data, olddata, oldlen);
1238 allocd = true;
1239 uvm_vnp_setsize(vp, newlen);
1240 }
1241
1242 /* ok, we have enough stooorage. write */
1243 while (uio->uio_resid > 0) {
1244 chunk = MIN(uio->uio_resid, (off_t)rn->rn_dlen-uio->uio_offset);
1245 if (chunk == 0)
1246 break;
1247 error = ubc_uiomove(&vp->v_uobj, uio, chunk, advice,
1248 UBC_WRITE | UBC_PARTIALOK | UBC_WANT_UNMAP(vp)?UBC_UNMAP:0);
1249 if (error)
1250 break;
1251 }
1252
1253 if (allocd) {
1254 if (error) {
1255 rump_hyperfree(rn->rn_data, newlen);
1256 rn->rn_data = olddata;
1257 rn->rn_dlen = oldlen;
1258 uvm_vnp_setsize(vp, oldlen);
1259 } else {
1260 rump_hyperfree(olddata, oldlen);
1261 }
1262 }
1263
1264 return error;
1265 }
1266
1267 static int
1268 rump_vop_bmap(void *v)
1269 {
1270 struct vop_bmap_args /* {
1271 struct vnode *a_vp;
1272 daddr_t a_bn;
1273 struct vnode **a_vpp;
1274 daddr_t *a_bnp;
1275 int *a_runp;
1276 } */ *ap = v;
1277
1278 /* 1:1 mapping */
1279 if (ap->a_vpp)
1280 *ap->a_vpp = ap->a_vp;
1281 if (ap->a_bnp)
1282 *ap->a_bnp = ap->a_bn;
1283 if (ap->a_runp)
1284 *ap->a_runp = 16;
1285
1286 return 0;
1287 }
1288
1289 static int
1290 rump_vop_strategy(void *v)
1291 {
1292 struct vop_strategy_args /* {
1293 struct vnode *a_vp;
1294 struct buf *a_bp;
1295 } */ *ap = v;
1296 struct vnode *vp = ap->a_vp;
1297 struct rumpfs_node *rn = vp->v_data;
1298 struct buf *bp = ap->a_bp;
1299 off_t copylen, copyoff;
1300 int error;
1301
1302 if (vp->v_type != VREG || rn->rn_flags & RUMPNODE_ET_PHONE_HOST) {
1303 error = EINVAL;
1304 goto out;
1305 }
1306
1307 copyoff = bp->b_blkno << DEV_BSHIFT;
1308 copylen = MIN(rn->rn_dlen - copyoff, bp->b_bcount);
1309 if (BUF_ISWRITE(bp)) {
1310 memcpy((uint8_t *)rn->rn_data + copyoff, bp->b_data, copylen);
1311 } else {
1312 memset((uint8_t*)bp->b_data + copylen, 0, bp->b_bcount-copylen);
1313 memcpy(bp->b_data, (uint8_t *)rn->rn_data + copyoff, copylen);
1314 }
1315 bp->b_resid = 0;
1316 error = 0;
1317
1318 out:
1319 bp->b_error = error;
1320 biodone(bp);
1321 return 0;
1322 }
1323
1324 static int
1325 rump_vop_pathconf(void *v)
1326 {
1327 struct vop_pathconf_args /* {
1328 struct vnode *a_vp;
1329 int a_name;
1330 register_t *a_retval;
1331 }; */ *ap = v;
1332 int name = ap->a_name;
1333 register_t *retval = ap->a_retval;
1334
1335 switch (name) {
1336 case _PC_LINK_MAX:
1337 *retval = LINK_MAX;
1338 return 0;
1339 case _PC_NAME_MAX:
1340 *retval = NAME_MAX;
1341 return 0;
1342 case _PC_PATH_MAX:
1343 *retval = PATH_MAX;
1344 return 0;
1345 case _PC_PIPE_BUF:
1346 *retval = PIPE_BUF;
1347 return 0;
1348 case _PC_CHOWN_RESTRICTED:
1349 *retval = 1;
1350 return 0;
1351 case _PC_NO_TRUNC:
1352 *retval = 1;
1353 return 0;
1354 case _PC_SYNC_IO:
1355 *retval = 1;
1356 return 0;
1357 case _PC_FILESIZEBITS:
1358 *retval = 43; /* this one goes to 11 */
1359 return 0;
1360 case _PC_SYMLINK_MAX:
1361 *retval = MAXPATHLEN;
1362 return 0;
1363 case _PC_2_SYMLINKS:
1364 *retval = 1;
1365 return 0;
1366 default:
1367 return EINVAL;
1368 }
1369 }
1370
1371 static int
1372 rump_vop_success(void *v)
1373 {
1374
1375 return 0;
1376 }
1377
1378 static int
1379 rump_vop_inactive(void *v)
1380 {
1381 struct vop_inactive_args /* {
1382 struct vnode *a_vp;
1383 bool *a_recycle;
1384 } */ *ap = v;
1385 struct vnode *vp = ap->a_vp;
1386 struct rumpfs_node *rn = vp->v_data;
1387 int error;
1388
1389 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST && vp->v_type == VREG) {
1390 if (rn->rn_readfd != -1) {
1391 rumpuser_close(rn->rn_readfd, &error);
1392 rn->rn_readfd = -1;
1393 }
1394 if (rn->rn_writefd != -1) {
1395 rumpuser_close(rn->rn_writefd, &error);
1396 rn->rn_writefd = -1;
1397 }
1398 }
1399 *ap->a_recycle = (rn->rn_flags & RUMPNODE_CANRECLAIM) ? true : false;
1400
1401 VOP_UNLOCK(vp);
1402 return 0;
1403 }
1404
1405 static int
1406 rump_vop_reclaim(void *v)
1407 {
1408 struct vop_reclaim_args /* {
1409 struct vnode *a_vp;
1410 } */ *ap = v;
1411 struct vnode *vp = ap->a_vp;
1412 struct rumpfs_node *rn = vp->v_data;
1413
1414 mutex_enter(&reclock);
1415 rn->rn_vp = NULL;
1416 mutex_exit(&reclock);
1417 genfs_node_destroy(vp);
1418 vp->v_data = NULL;
1419
1420 if (rn->rn_flags & RUMPNODE_CANRECLAIM) {
1421 if (vp->v_type == VLNK)
1422 PNBUF_PUT(rn->rn_linktarg);
1423 if (rn->rn_hostpath)
1424 free(rn->rn_hostpath, M_TEMP);
1425 kmem_free(rn, sizeof(*rn));
1426 }
1427
1428 return 0;
1429 }
1430
1431 static int
1432 rump_vop_spec(void *v)
1433 {
1434 struct vop_generic_args *ap = v;
1435 int (**opvec)(void *);
1436
1437 switch (ap->a_desc->vdesc_offset) {
1438 case VOP_ACCESS_DESCOFFSET:
1439 case VOP_GETATTR_DESCOFFSET:
1440 case VOP_SETATTR_DESCOFFSET:
1441 case VOP_LOCK_DESCOFFSET:
1442 case VOP_UNLOCK_DESCOFFSET:
1443 case VOP_RECLAIM_DESCOFFSET:
1444 opvec = rump_vnodeop_p;
1445 break;
1446 default:
1447 opvec = spec_vnodeop_p;
1448 break;
1449 }
1450
1451 return VOCALL(opvec, ap->a_desc->vdesc_offset, v);
1452 }
1453
1454 /*
1455 * Begin vfs-level stuff
1456 */
1457
1458 VFS_PROTOS(rumpfs);
1459 struct vfsops rumpfs_vfsops = {
1460 .vfs_name = MOUNT_RUMPFS,
1461 .vfs_min_mount_data = 0,
1462 .vfs_mount = rumpfs_mount,
1463 .vfs_start = (void *)nullop,
1464 .vfs_unmount = rumpfs_unmount,
1465 .vfs_root = rumpfs_root,
1466 .vfs_quotactl = (void *)eopnotsupp,
1467 .vfs_statvfs = genfs_statvfs,
1468 .vfs_sync = (void *)nullop,
1469 .vfs_vget = rumpfs_vget,
1470 .vfs_fhtovp = (void *)eopnotsupp,
1471 .vfs_vptofh = (void *)eopnotsupp,
1472 .vfs_init = rumpfs_init,
1473 .vfs_reinit = NULL,
1474 .vfs_done = rumpfs_done,
1475 .vfs_mountroot = rumpfs_mountroot,
1476 .vfs_snapshot = (void *)eopnotsupp,
1477 .vfs_extattrctl = (void *)eopnotsupp,
1478 .vfs_suspendctl = (void *)eopnotsupp,
1479 .vfs_renamelock_enter = genfs_renamelock_enter,
1480 .vfs_renamelock_exit = genfs_renamelock_exit,
1481 .vfs_opv_descs = rump_opv_descs,
1482 /* vfs_refcount */
1483 /* vfs_list */
1484 };
1485
1486 static int
1487 rumpfs_mountfs(struct mount *mp)
1488 {
1489 struct rumpfs_mount *rfsmp;
1490 struct rumpfs_node *rn;
1491 int error;
1492
1493 rfsmp = kmem_alloc(sizeof(*rfsmp), KM_SLEEP);
1494
1495 rn = makeprivate(VDIR, NODEV, DEV_BSIZE, false);
1496 rn->rn_parent = rn;
1497 if ((error = makevnode(mp, rn, &rfsmp->rfsmp_rvp)) != 0)
1498 return error;
1499
1500 rfsmp->rfsmp_rvp->v_vflag |= VV_ROOT;
1501 VOP_UNLOCK(rfsmp->rfsmp_rvp);
1502
1503 mp->mnt_data = rfsmp;
1504 mp->mnt_stat.f_namemax = MAXNAMLEN;
1505 mp->mnt_stat.f_iosize = 512;
1506 mp->mnt_flag |= MNT_LOCAL;
1507 mp->mnt_iflag |= IMNT_MPSAFE;
1508 mp->mnt_fs_bshift = DEV_BSHIFT;
1509 vfs_getnewfsid(mp);
1510
1511 return 0;
1512 }
1513
1514 int
1515 rumpfs_mount(struct mount *mp, const char *mntpath, void *arg, size_t *alen)
1516 {
1517 int error;
1518
1519 error = set_statvfs_info(mntpath, UIO_USERSPACE, "rumpfs", UIO_SYSSPACE,
1520 mp->mnt_op->vfs_name, mp, curlwp);
1521 if (error)
1522 return error;
1523
1524 return rumpfs_mountfs(mp);
1525 }
1526
1527 int
1528 rumpfs_unmount(struct mount *mp, int mntflags)
1529 {
1530 struct rumpfs_mount *rfsmp = mp->mnt_data;
1531 int flags = 0, error;
1532
1533 if (panicstr || mntflags & MNT_FORCE)
1534 flags |= FORCECLOSE;
1535
1536 if ((error = vflush(mp, rfsmp->rfsmp_rvp, flags)) != 0)
1537 return error;
1538 vgone(rfsmp->rfsmp_rvp); /* XXX */
1539
1540 kmem_free(rfsmp, sizeof(*rfsmp));
1541
1542 return 0;
1543 }
1544
1545 int
1546 rumpfs_root(struct mount *mp, struct vnode **vpp)
1547 {
1548 struct rumpfs_mount *rfsmp = mp->mnt_data;
1549
1550 vref(rfsmp->rfsmp_rvp);
1551 vn_lock(rfsmp->rfsmp_rvp, LK_EXCLUSIVE | LK_RETRY);
1552 *vpp = rfsmp->rfsmp_rvp;
1553 return 0;
1554 }
1555
1556 int
1557 rumpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
1558 {
1559
1560 return EOPNOTSUPP;
1561 }
1562
1563 void
1564 rumpfs_init()
1565 {
1566
1567 CTASSERT(RUMP_ETFS_SIZE_ENDOFF == RUMPBLK_SIZENOTSET);
1568
1569 mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE);
1570 mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE);
1571 }
1572
1573 void
1574 rumpfs_done()
1575 {
1576
1577 mutex_destroy(&reclock);
1578 mutex_destroy(&etfs_lock);
1579 }
1580
1581 int
1582 rumpfs_mountroot()
1583 {
1584 struct mount *mp;
1585 int error;
1586
1587 if ((error = vfs_rootmountalloc(MOUNT_RUMPFS, "rootdev", &mp)) != 0) {
1588 vrele(rootvp);
1589 return error;
1590 }
1591
1592 if ((error = rumpfs_mountfs(mp)) != 0)
1593 panic("mounting rootfs failed: %d", error);
1594
1595 mutex_enter(&mountlist_lock);
1596 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
1597 mutex_exit(&mountlist_lock);
1598
1599 error = set_statvfs_info("/", UIO_SYSSPACE, "rumpfs", UIO_SYSSPACE,
1600 mp->mnt_op->vfs_name, mp, curlwp);
1601 if (error)
1602 panic("set_statvfs_info failed for rootfs: %d", error);
1603
1604 vfs_unbusy(mp, false, NULL);
1605
1606 return 0;
1607 }
1608