xref: /netbsd-src/lib/libp2k/p2k.c (revision cac8e449158efc7261bebc8657cbb0125a2cfdde)
1 /*	$NetBSD: p2k.c,v 1.2 2008/07/30 18:10:38 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by Google Summer of Code.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 /*
31  * puffs 2k, i.e. puffs 2 kernel.  Converts the puffs protocol to
32  * the kernel vfs protocol and vice versa.
33  *
34  * A word about reference counting: puffs in the kernel is the king of
35  * reference counting.  We must maintain a vnode alive and kicking
36  * until the kernel tells us to reclaim it.  Therefore we make sure
37  * we never accidentally lose a vnode.  Before calling operations which
38  * decrease the refcount we always bump the refcount up to compensate.
39  * Come inactive, if the file system thinks that the vnode should be
40  * put out of its misery, it will set the recycle flag.  We use this
41  * to tell the kernel to reclaim the vnode.  Only in reclaim do we
42  * really nuke the last reference.
43  */
44 
45 #include <sys/mount.h>
46 #include <sys/param.h>
47 #include <sys/vnode.h>
48 #include <sys/lock.h>
49 #include <sys/namei.h>
50 #include <sys/dirent.h>
51 
52 #include <assert.h>
53 #include <errno.h>
54 #include <puffs.h>
55 #include <stdlib.h>
56 
57 #include <rump/rump.h>
58 #include <rump/p2k.h>
59 #include <rump/ukfs.h>
60 
61 PUFFSOP_PROTOS(p2k)
62 
63 static kauth_cred_t
64 cred_create(const struct puffs_cred *pcr)
65 {
66 	gid_t groups[NGROUPS];
67 	uid_t uid;
68 	gid_t gid;
69 	short ngroups = 0;
70 
71 	if (puffs_cred_getuid(pcr, &uid) == -1)
72 		uid = 0;
73 	if (puffs_cred_getgid(pcr, &gid) == -1)
74 		gid = 0;
75 	puffs_cred_getgroups(pcr, groups, &ngroups);
76 
77 	/* LINTED: ngroups is ok */
78 	return rump_cred_create(uid, gid, ngroups, groups);
79 }
80 
81 static __inline void
82 cred_destroy(kauth_cred_t cred)
83 {
84 
85 	rump_cred_destroy(cred);
86 }
87 
88 static struct componentname *
89 makecn(const struct puffs_cn *pcn)
90 {
91 	kauth_cred_t cred;
92 
93 	cred = cred_create(pcn->pcn_cred);
94 	/* LINTED: prehistoric types in first two args */
95 	return rump_makecn(pcn->pcn_nameiop, pcn->pcn_flags, pcn->pcn_name,
96 	    pcn->pcn_namelen, cred, curlwp);
97 }
98 
99 static __inline void
100 freecn(struct componentname *cnp, int flags)
101 {
102 
103 	rump_freecn(cnp, flags | RUMPCN_FREECRED);
104 }
105 
106 static void
107 makelwp(struct puffs_usermount *pu)
108 {
109 	pid_t pid;
110 	lwpid_t lid;
111 
112 	puffs_cc_getcaller(puffs_cc_getcc(pu), &pid, &lid);
113 	rump_setup_curlwp(pid, lid, 1);
114 }
115 
116 /*ARGSUSED*/
117 static void
118 clearlwp(struct puffs_usermount *pu)
119 {
120 
121 	rump_clear_curlwp();
122 }
123 
124 int
125 p2k_run_fs(const char *vfsname, const char *devpath, const char *mountpath,
126 	int mntflags, void *arg, size_t alen, uint32_t puffs_flags)
127 {
128 	char typebuf[PUFFS_TYPELEN];
129 	struct puffs_ops *pops;
130 	struct puffs_usermount *pu;
131 	struct puffs_node *pn_root;
132 	struct vnode *rvp;
133 	struct ukfs *ukfs;
134 	extern int puffs_fakecc;
135 	int rv, sverrno;
136 
137 	rv = -1;
138 	ukfs_init();
139 	ukfs = ukfs_mount(vfsname, devpath, mountpath, mntflags, arg, alen);
140 	if (ukfs == NULL)
141 		return -1;
142 
143 	PUFFSOP_INIT(pops);
144 
145 	PUFFSOP_SET(pops, p2k, fs, statvfs);
146 	PUFFSOP_SET(pops, p2k, fs, unmount);
147 	PUFFSOP_SET(pops, p2k, fs, sync);
148 	PUFFSOP_SET(pops, p2k, fs, fhtonode);
149 	PUFFSOP_SET(pops, p2k, fs, nodetofh);
150 
151 	PUFFSOP_SET(pops, p2k, node, lookup);
152 	PUFFSOP_SET(pops, p2k, node, create);
153 	PUFFSOP_SET(pops, p2k, node, mknod);
154 	PUFFSOP_SET(pops, p2k, node, open);
155 	PUFFSOP_SET(pops, p2k, node, close);
156 	PUFFSOP_SET(pops, p2k, node, access);
157 	PUFFSOP_SET(pops, p2k, node, getattr);
158 	PUFFSOP_SET(pops, p2k, node, setattr);
159 #if 0
160 	PUFFSOP_SET(pops, p2k, node, poll);
161 #endif
162 	PUFFSOP_SET(pops, p2k, node, mmap);
163 	PUFFSOP_SET(pops, p2k, node, fsync);
164 	PUFFSOP_SET(pops, p2k, node, seek);
165 	PUFFSOP_SET(pops, p2k, node, remove);
166 	PUFFSOP_SET(pops, p2k, node, link);
167 	PUFFSOP_SET(pops, p2k, node, rename);
168 	PUFFSOP_SET(pops, p2k, node, mkdir);
169 	PUFFSOP_SET(pops, p2k, node, rmdir);
170 	PUFFSOP_SET(pops, p2k, node, symlink);
171 	PUFFSOP_SET(pops, p2k, node, readdir);
172 	PUFFSOP_SET(pops, p2k, node, readlink);
173 	PUFFSOP_SET(pops, p2k, node, read);
174 	PUFFSOP_SET(pops, p2k, node, write);
175 
176 	PUFFSOP_SET(pops, p2k, node, inactive);
177 	PUFFSOP_SET(pops, p2k, node, reclaim);
178 
179 	strcpy(typebuf, "p2k|");
180 	if (strcmp(vfsname, "puffs") == 0) { /* XXX */
181 		struct puffs_kargs *args = arg;
182 		strlcat(typebuf, args->pa_typename, sizeof(typebuf));
183 	} else {
184 		strlcat(typebuf, vfsname, sizeof(typebuf));
185 	}
186 
187 	pu = puffs_init(pops, devpath, typebuf, ukfs_getmp(ukfs), puffs_flags);
188 	if (pu == NULL)
189 		goto out;
190 
191 	rvp = ukfs_getrvp(ukfs);
192 	pn_root = puffs_pn_new(pu, rvp);
193 	puffs_setroot(pu, pn_root);
194 	puffs_setfhsize(pu, 0, PUFFS_FHFLAG_PASSTHROUGH);
195 	puffs_setstacksize(pu, PUFFS_STACKSIZE_MIN);
196 	puffs_fakecc = 1;
197 
198 	puffs_set_prepost(pu, makelwp, clearlwp);
199 
200 	if ((rv = puffs_mount(pu, mountpath, mntflags, rvp))== -1)
201 		goto out;
202 	rv = puffs_mainloop(pu);
203 
204  out:
205 	sverrno = errno;
206 	ukfs_release(ukfs, UKFS_RELFLAG_NOUNMOUNT);
207 	if (rv) {
208 		errno = sverrno;
209 		rv = -1;
210 	}
211 
212 	return rv;
213 }
214 
215 /* XXX: vn_lock() */
216 #define VLE(a) RUMP_VOP_LOCK(a, LK_EXCLUSIVE)
217 #define VLS(a) RUMP_VOP_LOCK(a, LK_SHARED)
218 #define VUL(a) RUMP_VOP_UNLOCK(a, 0);
219 #define AUL(a) assert(RUMP_VOP_ISLOCKED(a) == 0)
220 
221 int
222 p2k_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp)
223 {
224 	struct mount *mp = puffs_getspecific(pu);
225 
226 	return rump_vfs_statvfs(mp, sbp);
227 }
228 
229 int
230 p2k_fs_unmount(struct puffs_usermount *pu, int flags)
231 {
232 	struct mount *mp = puffs_getspecific(pu);
233 	struct puffs_node *pn_root = puffs_getroot(pu);
234 	struct vnode *rvp = pn_root->pn_data, *rvp2;
235 	int rv;
236 
237 	/*
238 	 * We recycle the root node already here (god knows how
239 	 * many references it has due to lookup).  This is good
240 	 * because VFS_UNMOUNT would do it anyway.  But it is
241 	 * very bad if VFS_UNMOUNT fails for a reason or another
242 	 * (puffs guards against busy fs, but there might be other
243 	 * reasons).
244 	 *
245 	 * Theoretically we're going south, sinking fast & dying
246 	 * out here because the old vnode will be freed and we are
247 	 * unlikely to get a vnode at the same address.  But try
248 	 * anyway.
249 	 *
250 	 * XXX: reallyfixmesomeday.  either introduce VFS_ROOT to
251 	 * puffs (blah) or check the cookie in every routine
252 	 * against the root cookie, which might change (blah2).
253 	 */
254 	rump_vp_recycle_nokidding(rvp);
255 	rv = rump_vfs_unmount(mp, flags);
256 	if (rv) {
257 		int rv2;
258 
259 		rv2 = rump_vfs_root(mp, &rvp2, 0);
260 		assert(rv2 == 0 && rvp == rvp2);
261 	}
262 	return rv;
263 }
264 
265 int
266 p2k_fs_sync(struct puffs_usermount *pu, int waitfor,
267 	const struct puffs_cred *pcr)
268 {
269 	struct mount *mp = puffs_getspecific(pu);
270 	kauth_cred_t cred;
271 	int rv;
272 
273 	cred = cred_create(pcr);
274 	rv = rump_vfs_sync(mp, waitfor, (kauth_cred_t)cred);
275 	cred_destroy(cred);
276 
277 	rump_bioops_sync();
278 
279 	return rv;
280 }
281 
282 /*ARGSUSED*/
283 int
284 p2k_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
285 	struct puffs_newinfo *pni)
286 {
287 	struct mount *mp = puffs_getspecific(pu);
288 	struct vnode *vp;
289 	enum vtype vtype;
290 	voff_t vsize;
291 	dev_t rdev;
292 	int rv;
293 
294 	rv = rump_vfs_fhtovp(mp, fid, &vp);
295 	if (rv)
296 		return rv;
297 
298 	puffs_newinfo_setcookie(pni, vp);
299 	rump_getvninfo(vp, &vtype, &vsize, &rdev);
300 	puffs_newinfo_setvtype(pni, vtype);
301 	puffs_newinfo_setsize(pni, vsize);
302 	puffs_newinfo_setrdev(pni, rdev);
303 
304 	return 0;
305 }
306 
307 /*ARGSUSED*/
308 int
309 p2k_fs_nodetofh(struct puffs_usermount *pu, void *cookie, void *fid,
310 	size_t *fidsize)
311 {
312 	struct vnode *vp = cookie;
313 
314 	return rump_vfs_vptofh(vp, fid, fidsize);
315 }
316 
317 /*ARGSUSED*/
318 int
319 p2k_node_lookup(struct puffs_usermount *pu, void *opc,
320 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
321 {
322 	struct componentname *cn;
323 	struct vnode *vp;
324 	enum vtype vtype;
325 	voff_t vsize;
326 	dev_t rdev;
327 	int rv;
328 
329 	cn = makecn(pcn);
330 	VLE(opc);
331 	rv = RUMP_VOP_LOOKUP(opc, &vp, cn);
332 	VUL(opc);
333 	freecn(cn, RUMPCN_ISLOOKUP);
334 	if (rv) {
335 		if (rv == EJUSTRETURN)
336 			rv = ENOENT;
337 		return rv;
338 	}
339 	VUL(vp);
340 
341 	puffs_newinfo_setcookie(pni, vp);
342 	rump_getvninfo(vp, &vtype, &vsize, &rdev);
343 	puffs_newinfo_setvtype(pni, vtype);
344 	puffs_newinfo_setsize(pni, vsize);
345 	puffs_newinfo_setrdev(pni, rdev);
346 
347 	return 0;
348 }
349 
350 /*ARGSUSED*/
351 int
352 p2k_node_create(struct puffs_usermount *pu, void *opc,
353 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
354 	const struct vattr *vap)
355 {
356 	struct componentname *cn;
357 	struct vnode *vp;
358 	int rv;
359 
360 	cn = makecn(pcn);
361 	VLE(opc);
362 	rump_vp_incref(opc);
363 	rv = RUMP_VOP_CREATE(opc, &vp, cn, __UNCONST(vap));
364 	AUL(opc);
365 	freecn(cn, 0);
366 	if (rv == 0) {
367 		VUL(vp);
368 		puffs_newinfo_setcookie(pni, vp);
369 	}
370 
371 	return rv;
372 }
373 
374 /*ARGSUSED*/
375 int
376 p2k_node_mknod(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
377 	const struct puffs_cn *pcn, const struct vattr *vap)
378 {
379 	struct componentname *cn;
380 	struct vnode *vp;
381 	int rv;
382 
383 	cn = makecn(pcn);
384 	VLE(opc);
385 	rump_vp_incref(opc);
386 	rv = RUMP_VOP_MKNOD(opc, &vp, cn, __UNCONST(vap));
387 	AUL(opc);
388 	freecn(cn, 0);
389 	if (rv == 0) {
390 		VUL(vp);
391 		puffs_newinfo_setcookie(pni, vp);
392 	}
393 
394 	return rv;
395 }
396 
397 /*ARGSUSED*/
398 int
399 p2k_node_open(struct puffs_usermount *pu, void *opc, int mode,
400 	const struct puffs_cred *pcr)
401 {
402 	kauth_cred_t cred;
403 	int rv;
404 
405 	cred = cred_create(pcr);
406 	VLE(opc);
407 	rv = RUMP_VOP_OPEN(opc, mode, cred);
408 	VUL(opc);
409 	cred_destroy(cred);
410 
411 	return rv;
412 }
413 
414 /*ARGSUSED*/
415 int
416 p2k_node_close(struct puffs_usermount *pu, void *opc, int flags,
417 	const struct puffs_cred *pcr)
418 {
419 	kauth_cred_t cred;
420 
421 	cred = cred_create(pcr);
422 	VLE(opc);
423 	RUMP_VOP_CLOSE(opc, flags, cred);
424 	VUL(opc);
425 	cred_destroy(cred);
426 
427 	return 0;
428 }
429 
430 /*ARGSUSED*/
431 int
432 p2k_node_access(struct puffs_usermount *pu, void *opc, int mode,
433 	const struct puffs_cred *pcr)
434 {
435 	kauth_cred_t cred;
436 	int rv;
437 
438 	cred = cred_create(pcr);
439 	VLE(opc);
440 	rv = RUMP_VOP_ACCESS(opc, mode, cred);
441 	VUL(opc);
442 	cred_destroy(cred);
443 
444 	return rv;
445 }
446 
447 /*ARGSUSED*/
448 int
449 p2k_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *vap,
450 	const struct puffs_cred *pcr)
451 {
452 	kauth_cred_t cred;
453 	int rv;
454 
455 	cred = cred_create(pcr);
456 	VLE(opc);
457 	rv = RUMP_VOP_GETATTR(opc, vap, cred);
458 	VUL(opc);
459 	cred_destroy(cred);
460 
461 	return rv;
462 }
463 
464 /*ARGSUSED*/
465 int
466 p2k_node_setattr(struct puffs_usermount *pu, void *opc, const struct vattr *vap,
467 	const struct puffs_cred *pcr)
468 {
469 	kauth_cred_t cred;
470 	int rv;
471 
472 	cred = cred_create(pcr);
473 	VLE(opc);
474 	rv = RUMP_VOP_SETATTR(opc, __UNCONST(vap), cred);
475 	VUL(opc);
476 	cred_destroy(cred);
477 
478 	return rv;
479 }
480 
481 /*ARGSUSED*/
482 int
483 p2k_node_fsync(struct puffs_usermount *pu, void *opc,
484 	const struct puffs_cred *pcr, int flags, off_t offlo, off_t offhi)
485 {
486 	kauth_cred_t cred;
487 	int rv;
488 
489 	cred = cred_create(pcr);
490 	VLE(opc);
491 	rv = RUMP_VOP_FSYNC(opc, cred, flags, offlo, offhi);
492 	VUL(opc);
493 	cred_destroy(cred);
494 
495 	return rv;
496 }
497 
498 /*ARGSUSED*/
499 int
500 p2k_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t flags,
501 	const struct puffs_cred *pcr)
502 {
503 	kauth_cred_t cred;
504 	int rv;
505 
506 	cred = cred_create(pcr);
507 	rv = RUMP_VOP_MMAP(opc, flags, cred);
508 	cred_destroy(cred);
509 
510 	return rv;
511 }
512 
513 /*ARGSUSED*/
514 int
515 p2k_node_seek(struct puffs_usermount *pu, void *opc, off_t oldoff, off_t newoff,
516 	const struct puffs_cred *pcr)
517 {
518 	kauth_cred_t cred;
519 	int rv;
520 
521 	cred = cred_create(pcr);
522 	VLE(opc);
523 	rv = RUMP_VOP_SEEK(opc, oldoff, newoff, cred);
524 	VUL(opc);
525 	cred_destroy(cred);
526 
527 	return rv;
528 }
529 
530 /*ARGSUSED*/
531 int
532 p2k_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
533 	const struct puffs_cn *pcn)
534 {
535 	struct componentname *cn;
536 	int rv;
537 
538 	cn = makecn(pcn);
539 	VLE(opc);
540 	rump_vp_incref(opc);
541 	VLE(targ);
542 	rump_vp_incref(targ);
543 	rv = RUMP_VOP_REMOVE(opc, targ, cn);
544 	AUL(opc);
545 	AUL(targ);
546 	freecn(cn, 0);
547 
548 	return rv;
549 }
550 
551 /*ARGSUSED*/
552 int
553 p2k_node_link(struct puffs_usermount *pu, void *opc, void *targ,
554 	const struct puffs_cn *pcn)
555 {
556 	struct componentname *cn;
557 	int rv;
558 
559 	cn = makecn(pcn);
560 	VLE(opc);
561 	rump_vp_incref(opc);
562 	rv = RUMP_VOP_LINK(opc, targ, cn);
563 	freecn(cn, 0);
564 
565 	return rv;
566 }
567 
568 /*ARGSUSED*/
569 int
570 p2k_node_rename(struct puffs_usermount *pu, void *src_dir, void *src,
571 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
572 	const struct puffs_cn *pcn_targ)
573 {
574 	struct componentname *cn_src, *cn_targ;
575 	int rv;
576 
577 	cn_src = makecn(pcn_src);
578 	cn_targ = makecn(pcn_targ);
579 	rump_vp_incref(src_dir);
580 	rump_vp_incref(src);
581 	VLE(targ_dir);
582 	rump_vp_incref(targ_dir);
583 	if (targ) {
584 		VLE(targ);
585 		rump_vp_incref(targ);
586 	}
587 	rv = RUMP_VOP_RENAME(src_dir, src, cn_src, targ_dir, targ, cn_targ);
588 	AUL(targ_dir);
589 	if (targ)
590 		AUL(targ);
591 	freecn(cn_src, 0);
592 	freecn(cn_targ, 0);
593 
594 	return rv;
595 }
596 
597 /*ARGSUSED*/
598 int
599 p2k_node_mkdir(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
600 	const struct puffs_cn *pcn, const struct vattr *vap)
601 {
602 	struct componentname *cn;
603 	struct vnode *vp;
604 	int rv;
605 
606 	cn = makecn(pcn);
607 	VLE(opc);
608 	rump_vp_incref(opc);
609 	rv = RUMP_VOP_MKDIR(opc, &vp, cn, __UNCONST(vap));
610 	AUL(opc);
611 	freecn(cn, 0);
612 	if (rv == 0) {
613 		VUL(vp);
614 		puffs_newinfo_setcookie(pni, vp);
615 	}
616 
617 	return rv;
618 }
619 
620 /*ARGSUSED*/
621 int
622 p2k_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
623 	const struct puffs_cn *pcn)
624 {
625 	struct componentname *cn;
626 	int rv;
627 
628 	cn = makecn(pcn);
629 	VLE(opc);
630 	rump_vp_incref(opc);
631 	VLE(targ);
632 	rump_vp_incref(targ);
633 	rv = RUMP_VOP_RMDIR(opc, targ, cn);
634 	AUL(targ);
635 	AUL(opc);
636 	freecn(cn, 0);
637 
638 	return rv;
639 }
640 
641 /*ARGSUSED*/
642 int
643 p2k_node_symlink(struct puffs_usermount *pu, void *opc,
644 	struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
645 	const struct vattr *vap, const char *link_target)
646 {
647 	struct componentname *cn;
648 	struct vnode *vp;
649 	int rv;
650 
651 	cn = makecn(pcn_src);
652 	VLE(opc);
653 	rump_vp_incref(opc);
654 	rv = RUMP_VOP_SYMLINK(opc, &vp, cn,
655 	    __UNCONST(vap), __UNCONST(link_target));
656 	AUL(opc);
657 	freecn(cn, 0);
658 	if (rv == 0) {
659 		VUL(vp);
660 		puffs_newinfo_setcookie(pni, vp);
661 	}
662 
663 	return rv;
664 }
665 
666 /*ARGSUSED*/
667 int
668 p2k_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent,
669 	off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
670 	int *eofflag, off_t *cookies, size_t *ncookies)
671 {
672 	kauth_cred_t cred;
673 	struct uio *uio;
674 	off_t *vop_cookies;
675 	int vop_ncookies;
676 	int rv;
677 
678 	cred = cred_create(pcr);
679 	uio = rump_uio_setup(dent, *reslen, *readoff, RUMPUIO_READ);
680 	VLS(opc);
681 	if (cookies) {
682 		rv = RUMP_VOP_READDIR(opc, uio, cred, eofflag,
683 		    &vop_cookies, &vop_ncookies);
684 		memcpy(cookies, vop_cookies, vop_ncookies * sizeof(*cookies));
685 		*ncookies = vop_ncookies;
686 		free(vop_cookies);
687 	} else {
688 		rv = RUMP_VOP_READDIR(opc, uio, cred, eofflag, NULL, NULL);
689 	}
690 	VUL(opc);
691 	if (rv == 0) {
692 		*reslen = rump_uio_getresid(uio);
693 		*readoff = rump_uio_getoff(uio);
694 	}
695 	rump_uio_free(uio);
696 	cred_destroy(cred);
697 
698 	return rv;
699 }
700 
701 /*ARGSUSED*/
702 int
703 p2k_node_readlink(struct puffs_usermount *pu, void *opc,
704 	const struct puffs_cred *pcr, char *linkname, size_t *linklen)
705 {
706 	kauth_cred_t cred;
707 	struct uio *uio;
708 	int rv;
709 
710 	cred = cred_create(pcr);
711 	uio = rump_uio_setup(linkname, *linklen, 0, RUMPUIO_READ);
712 	VLE(opc);
713 	rv = RUMP_VOP_READLINK(opc, uio, cred);
714 	VUL(opc);
715 	*linklen -= rump_uio_free(uio);
716 	cred_destroy(cred);
717 
718 	return rv;
719 }
720 
721 /*ARGSUSED*/
722 int
723 p2k_node_read(struct puffs_usermount *pu, void *opc,
724 	uint8_t *buf, off_t offset, size_t *resid,
725 	const struct puffs_cred *pcr, int ioflag)
726 {
727 	kauth_cred_t cred;
728 	struct uio *uio;
729 	int rv;
730 
731 	cred = cred_create(pcr);
732 	uio = rump_uio_setup(buf, *resid, offset, RUMPUIO_READ);
733 	VLS(opc);
734 	rv = RUMP_VOP_READ(opc, uio, ioflag, cred);
735 	VUL(opc);
736 	*resid = rump_uio_free(uio);
737 	cred_destroy(cred);
738 
739 	return rv;
740 }
741 
742 /*ARGSUSED*/
743 int
744 p2k_node_write(struct puffs_usermount *pu, void *opc,
745 	uint8_t *buf, off_t offset, size_t *resid,
746 	const struct puffs_cred *pcr, int ioflag)
747 {
748 	kauth_cred_t cred;
749 	struct uio *uio;
750 	int rv;
751 
752 	cred = cred_create(pcr);
753 	uio = rump_uio_setup(buf, *resid, offset, RUMPUIO_WRITE);
754 	VLE(opc);
755 	rv = RUMP_VOP_WRITE(opc, uio, ioflag, cred);
756 	VUL(opc);
757 	*resid = rump_uio_free(uio);
758 	cred_destroy(cred);
759 
760 	return rv;
761 }
762 
763 int
764 p2k_node_inactive(struct puffs_usermount *pu, void *opc)
765 {
766 	struct vnode *vp = opc;
767 	bool recycle;
768 	int rv;
769 
770 	rump_vp_interlock(vp);
771 	(void) RUMP_VOP_PUTPAGES(vp, 0, 0, PGO_ALLPAGES);
772 	VLE(vp);
773 	rv = RUMP_VOP_INACTIVE(vp, &recycle);
774 	if (recycle)
775 		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
776 
777 	return rv;
778 }
779 
780 /*ARGSUSED*/
781 int
782 p2k_node_reclaim(struct puffs_usermount *pu, void *opc)
783 {
784 
785 	rump_vp_recycle_nokidding(opc);
786 	return 0;
787 }
788