xref: /netbsd-src/sys/fs/puffs/puffs_vnops.c (revision d25ffa98a4bfca1fe272f3c182496ec9934faac7)
1 /*	$NetBSD: puffs_vnops.c,v 1.153 2011/06/12 03:35:54 rmind Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Google Summer of Code program and the Ulla Tuominen Foundation.
8  * The Google SoC project was mentored by Bill Studenmund.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.153 2011/06/12 03:35:54 rmind Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/buf.h>
37 #include <sys/lockf.h>
38 #include <sys/malloc.h>
39 #include <sys/mount.h>
40 #include <sys/namei.h>
41 #include <sys/vnode.h>
42 #include <sys/proc.h>
43 
44 #include <uvm/uvm.h>
45 
46 #include <fs/puffs/puffs_msgif.h>
47 #include <fs/puffs/puffs_sys.h>
48 
49 #include <miscfs/fifofs/fifo.h>
50 #include <miscfs/genfs/genfs.h>
51 #include <miscfs/specfs/specdev.h>
52 
53 int	puffs_vnop_lookup(void *);
54 int	puffs_vnop_create(void *);
55 int	puffs_vnop_access(void *);
56 int	puffs_vnop_mknod(void *);
57 int	puffs_vnop_open(void *);
58 int	puffs_vnop_close(void *);
59 int	puffs_vnop_getattr(void *);
60 int	puffs_vnop_setattr(void *);
61 int	puffs_vnop_reclaim(void *);
62 int	puffs_vnop_readdir(void *);
63 int	puffs_vnop_poll(void *);
64 int	puffs_vnop_fsync(void *);
65 int	puffs_vnop_seek(void *);
66 int	puffs_vnop_remove(void *);
67 int	puffs_vnop_mkdir(void *);
68 int	puffs_vnop_rmdir(void *);
69 int	puffs_vnop_link(void *);
70 int	puffs_vnop_readlink(void *);
71 int	puffs_vnop_symlink(void *);
72 int	puffs_vnop_rename(void *);
73 int	puffs_vnop_read(void *);
74 int	puffs_vnop_write(void *);
75 int	puffs_vnop_fcntl(void *);
76 int	puffs_vnop_ioctl(void *);
77 int	puffs_vnop_inactive(void *);
78 int	puffs_vnop_print(void *);
79 int	puffs_vnop_pathconf(void *);
80 int	puffs_vnop_advlock(void *);
81 int	puffs_vnop_strategy(void *);
82 int	puffs_vnop_bmap(void *);
83 int	puffs_vnop_mmap(void *);
84 int	puffs_vnop_getpages(void *);
85 int	puffs_vnop_abortop(void *);
86 int	puffs_vnop_getextattr(void *);
87 int	puffs_vnop_setextattr(void *);
88 int	puffs_vnop_listextattr(void *);
89 int	puffs_vnop_deleteextattr(void *);
90 
91 int	puffs_vnop_spec_read(void *);
92 int	puffs_vnop_spec_write(void *);
93 int	puffs_vnop_fifo_read(void *);
94 int	puffs_vnop_fifo_write(void *);
95 
96 int	puffs_vnop_checkop(void *);
97 
98 #define puffs_vnop_lock genfs_lock
99 #define puffs_vnop_unlock genfs_unlock
100 #define puffs_vnop_islocked genfs_islocked
101 
102 int (**puffs_vnodeop_p)(void *);
103 const struct vnodeopv_entry_desc puffs_vnodeop_entries[] = {
104 	{ &vop_default_desc, vn_default_error },
105 	{ &vop_lookup_desc, puffs_vnop_lookup },	/* REAL lookup */
106 	{ &vop_create_desc, puffs_vnop_checkop },	/* create */
107         { &vop_mknod_desc, puffs_vnop_checkop },	/* mknod */
108         { &vop_open_desc, puffs_vnop_open },		/* REAL open */
109         { &vop_close_desc, puffs_vnop_checkop },	/* close */
110         { &vop_access_desc, puffs_vnop_access },	/* REAL access */
111         { &vop_getattr_desc, puffs_vnop_checkop },	/* getattr */
112         { &vop_setattr_desc, puffs_vnop_checkop },	/* setattr */
113         { &vop_read_desc, puffs_vnop_checkop },		/* read */
114         { &vop_write_desc, puffs_vnop_checkop },	/* write */
115         { &vop_fsync_desc, puffs_vnop_fsync },		/* REAL fsync */
116         { &vop_seek_desc, puffs_vnop_checkop },		/* seek */
117         { &vop_remove_desc, puffs_vnop_checkop },	/* remove */
118         { &vop_link_desc, puffs_vnop_checkop },		/* link */
119         { &vop_rename_desc, puffs_vnop_checkop },	/* rename */
120         { &vop_mkdir_desc, puffs_vnop_checkop },	/* mkdir */
121         { &vop_rmdir_desc, puffs_vnop_checkop },	/* rmdir */
122         { &vop_symlink_desc, puffs_vnop_checkop },	/* symlink */
123         { &vop_readdir_desc, puffs_vnop_checkop },	/* readdir */
124         { &vop_readlink_desc, puffs_vnop_checkop },	/* readlink */
125         { &vop_getpages_desc, puffs_vnop_checkop },	/* getpages */
126         { &vop_putpages_desc, genfs_putpages },		/* REAL putpages */
127         { &vop_pathconf_desc, puffs_vnop_checkop },	/* pathconf */
128         { &vop_advlock_desc, puffs_vnop_advlock },	/* advlock */
129         { &vop_strategy_desc, puffs_vnop_strategy },	/* REAL strategy */
130         { &vop_revoke_desc, genfs_revoke },		/* REAL revoke */
131         { &vop_abortop_desc, puffs_vnop_abortop },	/* REAL abortop */
132         { &vop_inactive_desc, puffs_vnop_inactive },	/* REAL inactive */
133         { &vop_reclaim_desc, puffs_vnop_reclaim },	/* REAL reclaim */
134         { &vop_lock_desc, puffs_vnop_lock },		/* REAL lock */
135         { &vop_unlock_desc, puffs_vnop_unlock },	/* REAL unlock */
136         { &vop_bmap_desc, puffs_vnop_bmap },		/* REAL bmap */
137         { &vop_print_desc, puffs_vnop_print },		/* REAL print */
138         { &vop_islocked_desc, puffs_vnop_islocked },	/* REAL islocked */
139         { &vop_bwrite_desc, genfs_nullop },		/* REAL bwrite */
140         { &vop_mmap_desc, puffs_vnop_mmap },		/* REAL mmap */
141         { &vop_poll_desc, puffs_vnop_poll },		/* REAL poll */
142 	{ &vop_getextattr_desc, puffs_vnop_getextattr },	/* getextattr */
143 	{ &vop_setextattr_desc, puffs_vnop_setextattr },	/* setextattr */
144 	{ &vop_listextattr_desc, puffs_vnop_listextattr },	/* listextattr */
145 	{ &vop_deleteextattr_desc, puffs_vnop_deleteextattr },/* deleteextattr */
146 #if 0
147 	{ &vop_openextattr_desc, puffs_vnop_checkop },	/* openextattr */
148 	{ &vop_closeextattr_desc, puffs_vnop_checkop },	/* closeextattr */
149 #endif
150         { &vop_kqfilter_desc, genfs_eopnotsupp },	/* kqfilter XXX */
151 	{ NULL, NULL }
152 };
153 const struct vnodeopv_desc puffs_vnodeop_opv_desc =
154 	{ &puffs_vnodeop_p, puffs_vnodeop_entries };
155 
156 
157 int (**puffs_specop_p)(void *);
158 const struct vnodeopv_entry_desc puffs_specop_entries[] = {
159 	{ &vop_default_desc, vn_default_error },
160 	{ &vop_lookup_desc, spec_lookup },		/* lookup, ENOTDIR */
161 	{ &vop_create_desc, spec_create },		/* genfs_badop */
162 	{ &vop_mknod_desc, spec_mknod },		/* genfs_badop */
163 	{ &vop_open_desc, spec_open },			/* spec_open */
164 	{ &vop_close_desc, spec_close },		/* spec_close */
165 	{ &vop_access_desc, puffs_vnop_checkop },	/* access */
166 	{ &vop_getattr_desc, puffs_vnop_checkop },	/* getattr */
167 	{ &vop_setattr_desc, puffs_vnop_checkop },	/* setattr */
168 	{ &vop_read_desc, puffs_vnop_spec_read },	/* update, read */
169 	{ &vop_write_desc, puffs_vnop_spec_write },	/* update, write */
170 	{ &vop_ioctl_desc, spec_ioctl },		/* spec_ioctl */
171 	{ &vop_fcntl_desc, genfs_fcntl },		/* dummy */
172 	{ &vop_poll_desc, spec_poll },			/* spec_poll */
173 	{ &vop_kqfilter_desc, spec_kqfilter },		/* spec_kqfilter */
174 	{ &vop_revoke_desc, spec_revoke },		/* genfs_revoke */
175 	{ &vop_mmap_desc, spec_mmap },			/* spec_mmap */
176 	{ &vop_fsync_desc, spec_fsync },		/* vflushbuf */
177 	{ &vop_seek_desc, spec_seek },			/* genfs_nullop */
178 	{ &vop_remove_desc, spec_remove },		/* genfs_badop */
179 	{ &vop_link_desc, spec_link },			/* genfs_badop */
180 	{ &vop_rename_desc, spec_rename },		/* genfs_badop */
181 	{ &vop_mkdir_desc, spec_mkdir },		/* genfs_badop */
182 	{ &vop_rmdir_desc, spec_rmdir },		/* genfs_badop */
183 	{ &vop_symlink_desc, spec_symlink },		/* genfs_badop */
184 	{ &vop_readdir_desc, spec_readdir },		/* genfs_badop */
185 	{ &vop_readlink_desc, spec_readlink },		/* genfs_badop */
186 	{ &vop_abortop_desc, spec_abortop },		/* genfs_badop */
187 	{ &vop_inactive_desc, puffs_vnop_inactive },	/* REAL inactive */
188 	{ &vop_reclaim_desc, puffs_vnop_reclaim },	/* REAL reclaim */
189 	{ &vop_lock_desc, puffs_vnop_lock },		/* REAL lock */
190 	{ &vop_unlock_desc, puffs_vnop_unlock },	/* REAL unlock */
191 	{ &vop_bmap_desc, spec_bmap },			/* dummy */
192 	{ &vop_strategy_desc, spec_strategy },		/* dev strategy */
193 	{ &vop_print_desc, puffs_vnop_print },		/* REAL print */
194 	{ &vop_islocked_desc, puffs_vnop_islocked },	/* REAL islocked */
195 	{ &vop_pathconf_desc, spec_pathconf },		/* pathconf */
196 	{ &vop_advlock_desc, spec_advlock },		/* lf_advlock */
197 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
198 	{ &vop_getpages_desc, spec_getpages },		/* genfs_getpages */
199 	{ &vop_putpages_desc, spec_putpages },		/* genfs_putpages */
200 	{ &vop_getextattr_desc, puffs_vnop_checkop },	/* getextattr */
201 	{ &vop_setextattr_desc, puffs_vnop_checkop },	/* setextattr */
202 	{ &vop_listextattr_desc, puffs_vnop_checkop },	/* listextattr */
203 	{ &vop_deleteextattr_desc, puffs_vnop_checkop },/* deleteextattr */
204 #if 0
205 	{ &vop_openextattr_desc, _openextattr },	/* openextattr */
206 	{ &vop_closeextattr_desc, _closeextattr },	/* closeextattr */
207 #endif
208 	{ NULL, NULL }
209 };
210 const struct vnodeopv_desc puffs_specop_opv_desc =
211 	{ &puffs_specop_p, puffs_specop_entries };
212 
213 
214 int (**puffs_fifoop_p)(void *);
215 const struct vnodeopv_entry_desc puffs_fifoop_entries[] = {
216 	{ &vop_default_desc, vn_default_error },
217 	{ &vop_lookup_desc, vn_fifo_bypass },		/* lookup, ENOTDIR */
218 	{ &vop_create_desc, vn_fifo_bypass },		/* genfs_badop */
219 	{ &vop_mknod_desc, vn_fifo_bypass },		/* genfs_badop */
220 	{ &vop_open_desc, vn_fifo_bypass },		/* open */
221 	{ &vop_close_desc, vn_fifo_bypass },		/* close */
222 	{ &vop_access_desc, puffs_vnop_checkop },	/* access */
223 	{ &vop_getattr_desc, puffs_vnop_checkop },	/* getattr */
224 	{ &vop_setattr_desc, puffs_vnop_checkop },	/* setattr */
225 	{ &vop_read_desc, puffs_vnop_fifo_read },	/* read, update */
226 	{ &vop_write_desc, puffs_vnop_fifo_write },	/* write, update */
227 	{ &vop_ioctl_desc, vn_fifo_bypass },		/* ioctl */
228 	{ &vop_fcntl_desc, genfs_fcntl },		/* dummy */
229 	{ &vop_poll_desc, vn_fifo_bypass },		/* poll */
230 	{ &vop_kqfilter_desc, vn_fifo_bypass },		/* kqfilter */
231 	{ &vop_revoke_desc, vn_fifo_bypass },		/* genfs_revoke */
232 	{ &vop_mmap_desc, vn_fifo_bypass },		/* genfs_badop */
233 	{ &vop_fsync_desc, vn_fifo_bypass },		/* genfs_nullop*/
234 	{ &vop_seek_desc, vn_fifo_bypass },		/* genfs_badop */
235 	{ &vop_remove_desc, vn_fifo_bypass },		/* genfs_badop */
236 	{ &vop_link_desc, vn_fifo_bypass },		/* genfs_badop */
237 	{ &vop_rename_desc, vn_fifo_bypass },		/* genfs_badop */
238 	{ &vop_mkdir_desc, vn_fifo_bypass },		/* genfs_badop */
239 	{ &vop_rmdir_desc, vn_fifo_bypass },		/* genfs_badop */
240 	{ &vop_symlink_desc, vn_fifo_bypass },		/* genfs_badop */
241 	{ &vop_readdir_desc, vn_fifo_bypass },		/* genfs_badop */
242 	{ &vop_readlink_desc, vn_fifo_bypass },		/* genfs_badop */
243 	{ &vop_abortop_desc, vn_fifo_bypass },		/* genfs_badop */
244 	{ &vop_inactive_desc, puffs_vnop_inactive },	/* REAL inactive */
245 	{ &vop_reclaim_desc, puffs_vnop_reclaim },	/* REAL reclaim */
246 	{ &vop_lock_desc, puffs_vnop_lock },		/* REAL lock */
247 	{ &vop_unlock_desc, puffs_vnop_unlock },	/* REAL unlock */
248 	{ &vop_bmap_desc, vn_fifo_bypass },		/* dummy */
249 	{ &vop_strategy_desc, vn_fifo_bypass },		/* genfs_badop */
250 	{ &vop_print_desc, puffs_vnop_print },		/* REAL print */
251 	{ &vop_islocked_desc, puffs_vnop_islocked },	/* REAL islocked */
252 	{ &vop_pathconf_desc, vn_fifo_bypass },		/* pathconf */
253 	{ &vop_advlock_desc, vn_fifo_bypass },		/* genfs_einval */
254 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
255 	{ &vop_putpages_desc, vn_fifo_bypass }, 	/* genfs_null_putpages*/
256 #if 0
257 	{ &vop_openextattr_desc, _openextattr },	/* openextattr */
258 	{ &vop_closeextattr_desc, _closeextattr },	/* closeextattr */
259 #endif
260 	{ &vop_getextattr_desc, puffs_vnop_checkop },		/* getextattr */
261 	{ &vop_setextattr_desc, puffs_vnop_checkop },		/* setextattr */
262 	{ &vop_listextattr_desc, puffs_vnop_checkop },	/* listextattr */
263 	{ &vop_deleteextattr_desc, puffs_vnop_checkop },	/* deleteextattr */
264 	{ NULL, NULL }
265 };
266 const struct vnodeopv_desc puffs_fifoop_opv_desc =
267 	{ &puffs_fifoop_p, puffs_fifoop_entries };
268 
269 
270 /* "real" vnode operations */
271 int (**puffs_msgop_p)(void *);
272 const struct vnodeopv_entry_desc puffs_msgop_entries[] = {
273 	{ &vop_default_desc, vn_default_error },
274 	{ &vop_create_desc, puffs_vnop_create },	/* create */
275         { &vop_mknod_desc, puffs_vnop_mknod },		/* mknod */
276         { &vop_open_desc, puffs_vnop_open },		/* open */
277         { &vop_close_desc, puffs_vnop_close },		/* close */
278         { &vop_access_desc, puffs_vnop_access },	/* access */
279         { &vop_getattr_desc, puffs_vnop_getattr },	/* getattr */
280         { &vop_setattr_desc, puffs_vnop_setattr },	/* setattr */
281         { &vop_read_desc, puffs_vnop_read },		/* read */
282         { &vop_write_desc, puffs_vnop_write },		/* write */
283         { &vop_seek_desc, puffs_vnop_seek },		/* seek */
284         { &vop_remove_desc, puffs_vnop_remove },	/* remove */
285         { &vop_link_desc, puffs_vnop_link },		/* link */
286         { &vop_rename_desc, puffs_vnop_rename },	/* rename */
287         { &vop_mkdir_desc, puffs_vnop_mkdir },		/* mkdir */
288         { &vop_rmdir_desc, puffs_vnop_rmdir },		/* rmdir */
289         { &vop_symlink_desc, puffs_vnop_symlink },	/* symlink */
290         { &vop_readdir_desc, puffs_vnop_readdir },	/* readdir */
291         { &vop_readlink_desc, puffs_vnop_readlink },	/* readlink */
292         { &vop_print_desc, puffs_vnop_print },		/* print */
293         { &vop_islocked_desc, puffs_vnop_islocked },	/* islocked */
294         { &vop_pathconf_desc, puffs_vnop_pathconf },	/* pathconf */
295         { &vop_getpages_desc, puffs_vnop_getpages },	/* getpages */
296 	{ NULL, NULL }
297 };
298 const struct vnodeopv_desc puffs_msgop_opv_desc =
299 	{ &puffs_msgop_p, puffs_msgop_entries };
300 
301 
302 #define ERROUT(err)							\
303 do {									\
304 	error = err;							\
305 	goto out;							\
306 } while (/*CONSTCOND*/0)
307 
308 /*
309  * This is a generic vnode operation handler.  It checks if the necessary
310  * operations for the called vnode operation are implemented by userspace
311  * and either returns a dummy return value or proceeds to call the real
312  * vnode operation from puffs_msgop_v.
313  *
314  * XXX: this should described elsewhere and autogenerated, the complexity
315  * of the vnode operations vectors and their interrelationships is also
316  * getting a bit out of hand.  Another problem is that we need this same
317  * information in the fs server code, so keeping the two in sync manually
318  * is not a viable (long term) plan.
319  */
320 
321 /* not supported, handle locking protocol */
322 #define CHECKOP_NOTSUPP(op)						\
323 case VOP_##op##_DESCOFFSET:						\
324 	if (pmp->pmp_vnopmask[PUFFS_VN_##op] == 0)			\
325 		return genfs_eopnotsupp(v);				\
326 	break
327 
328 /* always succeed, no locking */
329 #define CHECKOP_SUCCESS(op)						\
330 case VOP_##op##_DESCOFFSET:						\
331 	if (pmp->pmp_vnopmask[PUFFS_VN_##op] == 0)			\
332 		return 0;						\
333 	break
334 
335 int
336 puffs_vnop_checkop(void *v)
337 {
338 	struct vop_generic_args /* {
339 		struct vnodeop_desc *a_desc;
340 		spooky mystery contents;
341 	} */ *ap = v;
342 	struct vnodeop_desc *desc = ap->a_desc;
343 	struct puffs_mount *pmp;
344 	struct vnode *vp;
345 	int offset, rv;
346 
347 	offset = ap->a_desc->vdesc_vp_offsets[0];
348 #ifdef DIAGNOSTIC
349 	if (offset == VDESC_NO_OFFSET)
350 		panic("puffs_checkop: no vnode, why did you call me?");
351 #endif
352 	vp = *VOPARG_OFFSETTO(struct vnode **, offset, ap);
353 	pmp = MPTOPUFFSMP(vp->v_mount);
354 
355 	DPRINTF_VERBOSE(("checkop call %s (%d), vp %p\n",
356 	    ap->a_desc->vdesc_name, ap->a_desc->vdesc_offset, vp));
357 
358 	if (!ALLOPS(pmp)) {
359 		switch (desc->vdesc_offset) {
360 			CHECKOP_NOTSUPP(CREATE);
361 			CHECKOP_NOTSUPP(MKNOD);
362 			CHECKOP_NOTSUPP(GETATTR);
363 			CHECKOP_NOTSUPP(SETATTR);
364 			CHECKOP_NOTSUPP(READ);
365 			CHECKOP_NOTSUPP(WRITE);
366 			CHECKOP_NOTSUPP(FCNTL);
367 			CHECKOP_NOTSUPP(IOCTL);
368 			CHECKOP_NOTSUPP(REMOVE);
369 			CHECKOP_NOTSUPP(LINK);
370 			CHECKOP_NOTSUPP(RENAME);
371 			CHECKOP_NOTSUPP(MKDIR);
372 			CHECKOP_NOTSUPP(RMDIR);
373 			CHECKOP_NOTSUPP(SYMLINK);
374 			CHECKOP_NOTSUPP(READDIR);
375 			CHECKOP_NOTSUPP(READLINK);
376 			CHECKOP_NOTSUPP(PRINT);
377 			CHECKOP_NOTSUPP(PATHCONF);
378 			CHECKOP_NOTSUPP(GETEXTATTR);
379 			CHECKOP_NOTSUPP(SETEXTATTR);
380 			CHECKOP_NOTSUPP(LISTEXTATTR);
381 			CHECKOP_NOTSUPP(DELETEEXTATTR);
382 
383 			CHECKOP_SUCCESS(ACCESS);
384 			CHECKOP_SUCCESS(CLOSE);
385 			CHECKOP_SUCCESS(SEEK);
386 
387 		case VOP_GETPAGES_DESCOFFSET:
388 			if (!EXISTSOP(pmp, READ))
389 				return genfs_eopnotsupp(v);
390 			break;
391 
392 		default:
393 			panic("puffs_checkop: unhandled vnop %d",
394 			    desc->vdesc_offset);
395 		}
396 	}
397 
398 	rv = VOCALL(puffs_msgop_p, ap->a_desc->vdesc_offset, v);
399 
400 	DPRINTF_VERBOSE(("checkop return %s (%d), vp %p: %d\n",
401 	    ap->a_desc->vdesc_name, ap->a_desc->vdesc_offset, vp, rv));
402 
403 	return rv;
404 }
405 
406 static int callremove(struct puffs_mount *, puffs_cookie_t, puffs_cookie_t,
407 			    struct componentname *);
408 static int callrmdir(struct puffs_mount *, puffs_cookie_t, puffs_cookie_t,
409 			   struct componentname *);
410 static void callinactive(struct puffs_mount *, puffs_cookie_t, int);
411 static void callreclaim(struct puffs_mount *, puffs_cookie_t);
412 static int  flushvncache(struct vnode *, off_t, off_t, bool);
413 
414 
415 #define PUFFS_ABORT_LOOKUP	1
416 #define PUFFS_ABORT_CREATE	2
417 #define PUFFS_ABORT_MKNOD	3
418 #define PUFFS_ABORT_MKDIR	4
419 #define PUFFS_ABORT_SYMLINK	5
420 
421 /*
422  * Press the pani^Wabort button!  Kernel resource allocation failed.
423  */
424 static void
425 puffs_abortbutton(struct puffs_mount *pmp, int what,
426 	puffs_cookie_t dck, puffs_cookie_t ck, struct componentname *cnp)
427 {
428 
429 	switch (what) {
430 	case PUFFS_ABORT_CREATE:
431 	case PUFFS_ABORT_MKNOD:
432 	case PUFFS_ABORT_SYMLINK:
433 		callremove(pmp, dck, ck, cnp);
434 		break;
435 	case PUFFS_ABORT_MKDIR:
436 		callrmdir(pmp, dck, ck, cnp);
437 		break;
438 	}
439 
440 	callinactive(pmp, ck, 0);
441 	callreclaim(pmp, ck);
442 }
443 
444 /*
445  * Begin vnode operations.
446  *
447  * A word from the keymaster about locks: generally we don't want
448  * to use the vnode locks at all: it creates an ugly dependency between
449  * the userlandia file server and the kernel.  But we'll play along with
450  * the kernel vnode locks for now.  However, even currently we attempt
451  * to release locks as early as possible.  This is possible for some
452  * operations which a) don't need a locked vnode after the userspace op
453  * and b) return with the vnode unlocked.  Theoretically we could
454  * unlock-do op-lock for others and order the graph in userspace, but I
455  * don't want to think of the consequences for the time being.
456  */
457 
458 int
459 puffs_vnop_lookup(void *v)
460 {
461         struct vop_lookup_args /* {
462 		const struct vnodeop_desc *a_desc;
463 		struct vnode *a_dvp;
464 		struct vnode **a_vpp;
465 		struct componentname *a_cnp;
466         } */ *ap = v;
467 	PUFFS_MSG_VARS(vn, lookup);
468 	struct puffs_mount *pmp;
469 	struct componentname *cnp;
470 	struct vnode *vp, *dvp;
471 	struct puffs_node *dpn;
472 	int isdot;
473 	int error;
474 
475 	pmp = MPTOPUFFSMP(ap->a_dvp->v_mount);
476 	cnp = ap->a_cnp;
477 	dvp = ap->a_dvp;
478 	*ap->a_vpp = NULL;
479 
480 	/* r/o fs?  we check create later to handle EEXIST */
481 	if ((cnp->cn_flags & ISLASTCN)
482 	    && (dvp->v_mount->mnt_flag & MNT_RDONLY)
483 	    && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
484 		return EROFS;
485 
486 	isdot = cnp->cn_namelen == 1 && *cnp->cn_nameptr == '.';
487 
488 	DPRINTF(("puffs_lookup: \"%s\", parent vnode %p, op: %x\n",
489 	    cnp->cn_nameptr, dvp, cnp->cn_nameiop));
490 
491 	/*
492 	 * Check if someone fed it into the cache
493 	 */
494 	if (PUFFS_USE_NAMECACHE(pmp)) {
495 		error = cache_lookup(dvp, ap->a_vpp, cnp);
496 
497 		if (error >= 0)
498 			return error;
499 	}
500 
501 	if (isdot) {
502 		/* deal with rename lookup semantics */
503 		if (cnp->cn_nameiop == RENAME && (cnp->cn_flags & ISLASTCN))
504 			return EISDIR;
505 
506 		vp = ap->a_dvp;
507 		vref(vp);
508 		*ap->a_vpp = vp;
509 		return 0;
510 	}
511 
512 	PUFFS_MSG_ALLOC(vn, lookup);
513 	puffs_makecn(&lookup_msg->pvnr_cn, &lookup_msg->pvnr_cn_cred,
514 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
515 
516 	if (cnp->cn_flags & ISDOTDOT)
517 		VOP_UNLOCK(dvp);
518 
519 	puffs_msg_setinfo(park_lookup, PUFFSOP_VN,
520 	    PUFFS_VN_LOOKUP, VPTOPNC(dvp));
521 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_lookup, dvp->v_data, NULL, error);
522 	DPRINTF(("puffs_lookup: return of the userspace, part %d\n", error));
523 
524 	/*
525 	 * In case of error, there is no new vnode to play with, so be
526 	 * happy with the NULL value given to vpp in the beginning.
527 	 * Also, check if this really was an error or the target was not
528 	 * present.  Either treat it as a non-error for CREATE/RENAME or
529 	 * enter the component into the negative name cache (if desired).
530 	 */
531 	if (error) {
532 		error = checkerr(pmp, error, __func__);
533 		if (error == ENOENT) {
534 			/* don't allow to create files on r/o fs */
535 			if ((dvp->v_mount->mnt_flag & MNT_RDONLY)
536 			    && cnp->cn_nameiop == CREATE) {
537 				error = EROFS;
538 
539 			/* adjust values if we are creating */
540 			} else if ((cnp->cn_flags & ISLASTCN)
541 			    && (cnp->cn_nameiop == CREATE
542 			      || cnp->cn_nameiop == RENAME)) {
543 				error = EJUSTRETURN;
544 
545 			/* save negative cache entry */
546 			} else {
547 				if ((cnp->cn_flags & MAKEENTRY)
548 				    && PUFFS_USE_NAMECACHE(pmp))
549 					cache_enter(dvp, NULL, cnp);
550 			}
551 		}
552 		goto out;
553 	}
554 
555 	/*
556 	 * Check that we don't get our parent node back, that would cause
557 	 * a pretty obvious deadlock.
558 	 */
559 	dpn = dvp->v_data;
560 	if (lookup_msg->pvnr_newnode == dpn->pn_cookie) {
561 		puffs_senderr(pmp, PUFFS_ERR_LOOKUP, EINVAL,
562 		    "lookup produced parent cookie", lookup_msg->pvnr_newnode);
563 		error = EPROTO;
564 		goto out;
565 	}
566 
567 	error = puffs_cookie2vnode(pmp, lookup_msg->pvnr_newnode, 1, 1, &vp);
568 	if (error == PUFFS_NOSUCHCOOKIE) {
569 		error = puffs_getvnode(dvp->v_mount,
570 		    lookup_msg->pvnr_newnode, lookup_msg->pvnr_vtype,
571 		    lookup_msg->pvnr_size, lookup_msg->pvnr_rdev, &vp);
572 		if (error) {
573 			puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp),
574 			    lookup_msg->pvnr_newnode, ap->a_cnp);
575 			goto out;
576 		}
577 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
578 	} else if (error) {
579 		puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp),
580 		    lookup_msg->pvnr_newnode, ap->a_cnp);
581 		goto out;
582 	}
583 
584 	*ap->a_vpp = vp;
585 
586 	if ((cnp->cn_flags & MAKEENTRY) != 0 && PUFFS_USE_NAMECACHE(pmp))
587 		cache_enter(dvp, vp, cnp);
588 
589 	/* XXX */
590 	if ((lookup_msg->pvnr_cn.pkcn_flags & REQUIREDIR) == 0)
591 		cnp->cn_flags &= ~REQUIREDIR;
592 	if (lookup_msg->pvnr_cn.pkcn_consume)
593 		cnp->cn_consume = MIN(lookup_msg->pvnr_cn.pkcn_consume,
594 		    strlen(cnp->cn_nameptr) - cnp->cn_namelen);
595 
596  out:
597 	if (cnp->cn_flags & ISDOTDOT)
598 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
599 
600 	DPRINTF(("puffs_lookup: returning %d %p\n", error, *ap->a_vpp));
601 	PUFFS_MSG_RELEASE(lookup);
602 	return error;
603 }
604 
605 #define REFPN_AND_UNLOCKVP(a, b)					\
606 do {									\
607 	mutex_enter(&b->pn_mtx);					\
608 	puffs_referencenode(b);						\
609 	mutex_exit(&b->pn_mtx);						\
610 	VOP_UNLOCK(a);						\
611 } while (/*CONSTCOND*/0)
612 
613 #define REFPN(b)							\
614 do {									\
615 	mutex_enter(&b->pn_mtx);					\
616 	puffs_referencenode(b);						\
617 	mutex_exit(&b->pn_mtx);						\
618 } while (/*CONSTCOND*/0)
619 
620 #define RELEPN_AND_VP(a, b)						\
621 do {									\
622 	puffs_releasenode(b);						\
623 	vrele(a);							\
624 } while (/*CONSTCOND*/0)
625 
626 int
627 puffs_vnop_create(void *v)
628 {
629 	struct vop_create_args /* {
630 		const struct vnodeop_desc *a_desc;
631 		struct vnode *a_dvp;
632 		struct vnode **a_vpp;
633 		struct componentname *a_cnp;
634 		struct vattr *a_vap;
635 	} */ *ap = v;
636 	PUFFS_MSG_VARS(vn, create);
637 	struct vnode *dvp = ap->a_dvp;
638 	struct puffs_node *dpn = VPTOPP(dvp);
639 	struct componentname *cnp = ap->a_cnp;
640 	struct mount *mp = dvp->v_mount;
641 	struct puffs_mount *pmp = MPTOPUFFSMP(mp);
642 	int error;
643 
644 	DPRINTF(("puffs_create: dvp %p, cnp: %s\n",
645 	    dvp, ap->a_cnp->cn_nameptr));
646 
647 	PUFFS_MSG_ALLOC(vn, create);
648 	puffs_makecn(&create_msg->pvnr_cn, &create_msg->pvnr_cn_cred,
649 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
650 	create_msg->pvnr_va = *ap->a_vap;
651 	puffs_msg_setinfo(park_create, PUFFSOP_VN,
652 	    PUFFS_VN_CREATE, VPTOPNC(dvp));
653 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_create, dvp->v_data, NULL, error);
654 
655 	error = checkerr(pmp, error, __func__);
656 	if (error)
657 		goto out;
658 
659 	error = puffs_newnode(mp, dvp, ap->a_vpp,
660 	    create_msg->pvnr_newnode, cnp, ap->a_vap->va_type, 0);
661 	if (error)
662 		puffs_abortbutton(pmp, PUFFS_ABORT_CREATE, dpn->pn_cookie,
663 		    create_msg->pvnr_newnode, cnp);
664 
665  out:
666 	vput(dvp);
667 
668 	DPRINTF(("puffs_create: return %d\n", error));
669 	PUFFS_MSG_RELEASE(create);
670 	return error;
671 }
672 
673 int
674 puffs_vnop_mknod(void *v)
675 {
676 	struct vop_mknod_args /* {
677 		const struct vnodeop_desc *a_desc;
678 		struct vnode *a_dvp;
679 		struct vnode **a_vpp;
680 		struct componentname *a_cnp;
681 		struct vattr *a_vap;
682 	} */ *ap = v;
683 	PUFFS_MSG_VARS(vn, mknod);
684 	struct vnode *dvp = ap->a_dvp;
685 	struct puffs_node *dpn = VPTOPP(dvp);
686 	struct componentname *cnp = ap->a_cnp;
687 	struct mount *mp = dvp->v_mount;
688 	struct puffs_mount *pmp = MPTOPUFFSMP(mp);
689 	int error;
690 
691 	PUFFS_MSG_ALLOC(vn, mknod);
692 	puffs_makecn(&mknod_msg->pvnr_cn, &mknod_msg->pvnr_cn_cred,
693 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
694 	mknod_msg->pvnr_va = *ap->a_vap;
695 	puffs_msg_setinfo(park_mknod, PUFFSOP_VN,
696 	    PUFFS_VN_MKNOD, VPTOPNC(dvp));
697 
698 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mknod, dvp->v_data, NULL, error);
699 
700 	error = checkerr(pmp, error, __func__);
701 	if (error)
702 		goto out;
703 
704 	error = puffs_newnode(mp, dvp, ap->a_vpp,
705 	    mknod_msg->pvnr_newnode, cnp, ap->a_vap->va_type,
706 	    ap->a_vap->va_rdev);
707 	if (error)
708 		puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, dpn->pn_cookie,
709 		    mknod_msg->pvnr_newnode, cnp);
710 
711  out:
712 	vput(dvp);
713 	PUFFS_MSG_RELEASE(mknod);
714 	return error;
715 }
716 
717 int
718 puffs_vnop_open(void *v)
719 {
720 	struct vop_open_args /* {
721 		const struct vnodeop_desc *a_desc;
722 		struct vnode *a_vp;
723 		int a_mode;
724 		kauth_cred_t a_cred;
725 	} */ *ap = v;
726 	PUFFS_MSG_VARS(vn, open);
727 	struct vnode *vp = ap->a_vp;
728 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
729 	int mode = ap->a_mode;
730 	int error;
731 
732 	DPRINTF(("puffs_open: vp %p, mode 0x%x\n", vp, mode));
733 
734 	if (vp->v_type == VREG && mode & FWRITE && !EXISTSOP(pmp, WRITE))
735 		ERROUT(EROFS);
736 
737 	if (!EXISTSOP(pmp, OPEN))
738 		ERROUT(0);
739 
740 	PUFFS_MSG_ALLOC(vn, open);
741 	open_msg->pvnr_mode = mode;
742 	puffs_credcvt(&open_msg->pvnr_cred, ap->a_cred);
743 	puffs_msg_setinfo(park_open, PUFFSOP_VN,
744 	    PUFFS_VN_OPEN, VPTOPNC(vp));
745 
746 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_open, vp->v_data, NULL, error);
747 	error = checkerr(pmp, error, __func__);
748 
749  out:
750 	DPRINTF(("puffs_open: returning %d\n", error));
751 	PUFFS_MSG_RELEASE(open);
752 	return error;
753 }
754 
755 int
756 puffs_vnop_close(void *v)
757 {
758 	struct vop_close_args /* {
759 		const struct vnodeop_desc *a_desc;
760 		struct vnode *a_vp;
761 		int a_fflag;
762 		kauth_cred_t a_cred;
763 	} */ *ap = v;
764 	PUFFS_MSG_VARS(vn, close);
765 	struct vnode *vp = ap->a_vp;
766 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
767 
768 	PUFFS_MSG_ALLOC(vn, close);
769 	puffs_msg_setfaf(park_close);
770 	close_msg->pvnr_fflag = ap->a_fflag;
771 	puffs_credcvt(&close_msg->pvnr_cred, ap->a_cred);
772 	puffs_msg_setinfo(park_close, PUFFSOP_VN,
773 	    PUFFS_VN_CLOSE, VPTOPNC(vp));
774 
775 	puffs_msg_enqueue(pmp, park_close);
776 	PUFFS_MSG_RELEASE(close);
777 	return 0;
778 }
779 
780 int
781 puffs_vnop_access(void *v)
782 {
783 	struct vop_access_args /* {
784 		const struct vnodeop_desc *a_desc;
785 		struct vnode *a_vp;
786 		int a_mode;
787 		kauth_cred_t a_cred;
788 	} */ *ap = v;
789 	PUFFS_MSG_VARS(vn, access);
790 	struct vnode *vp = ap->a_vp;
791 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
792 	int mode = ap->a_mode;
793 	int error;
794 
795 	if (mode & VWRITE) {
796 		switch (vp->v_type) {
797 		case VDIR:
798 		case VLNK:
799 		case VREG:
800 			if ((vp->v_mount->mnt_flag & MNT_RDONLY)
801 			    || !EXISTSOP(pmp, WRITE))
802 				return EROFS;
803 			break;
804 		default:
805 			break;
806 		}
807 	}
808 
809 	if (!EXISTSOP(pmp, ACCESS))
810 		return 0;
811 
812 	PUFFS_MSG_ALLOC(vn, access);
813 	access_msg->pvnr_mode = ap->a_mode;
814 	puffs_credcvt(&access_msg->pvnr_cred, ap->a_cred);
815 	puffs_msg_setinfo(park_access, PUFFSOP_VN,
816 	    PUFFS_VN_ACCESS, VPTOPNC(vp));
817 
818 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_access, vp->v_data, NULL, error);
819 	error = checkerr(pmp, error, __func__);
820 	PUFFS_MSG_RELEASE(access);
821 
822 	return error;
823 }
824 
825 int
826 puffs_vnop_getattr(void *v)
827 {
828 	struct vop_getattr_args /* {
829 		const struct vnodeop_desc *a_desc;
830 		struct vnode *a_vp;
831 		struct vattr *a_vap;
832 		kauth_cred_t a_cred;
833 	} */ *ap = v;
834 	PUFFS_MSG_VARS(vn, getattr);
835 	struct vnode *vp = ap->a_vp;
836 	struct mount *mp = vp->v_mount;
837 	struct puffs_mount *pmp = MPTOPUFFSMP(mp);
838 	struct vattr *vap, *rvap;
839 	struct puffs_node *pn = VPTOPP(vp);
840 	int error = 0;
841 
842 	REFPN(pn);
843 	vap = ap->a_vap;
844 
845 	PUFFS_MSG_ALLOC(vn, getattr);
846 	vattr_null(&getattr_msg->pvnr_va);
847 	puffs_credcvt(&getattr_msg->pvnr_cred, ap->a_cred);
848 	puffs_msg_setinfo(park_getattr, PUFFSOP_VN,
849 	    PUFFS_VN_GETATTR, VPTOPNC(vp));
850 
851 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_getattr, vp->v_data, NULL, error);
852 	error = checkerr(pmp, error, __func__);
853 	if (error)
854 		goto out;
855 
856 	rvap = &getattr_msg->pvnr_va;
857 	/*
858 	 * Don't listen to the file server regarding special device
859 	 * size info, the file server doesn't know anything about them.
860 	 */
861 	if (vp->v_type == VBLK || vp->v_type == VCHR)
862 		rvap->va_size = vp->v_size;
863 
864 	/* Ditto for blocksize (ufs comment: this doesn't belong here) */
865 	if (vp->v_type == VBLK)
866 		rvap->va_blocksize = BLKDEV_IOSIZE;
867 	else if (vp->v_type == VCHR)
868 		rvap->va_blocksize = MAXBSIZE;
869 
870 	(void) memcpy(vap, rvap, sizeof(struct vattr));
871 	vap->va_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
872 
873 	if (pn->pn_stat & PNODE_METACACHE_ATIME)
874 		vap->va_atime = pn->pn_mc_atime;
875 	if (pn->pn_stat & PNODE_METACACHE_CTIME)
876 		vap->va_ctime = pn->pn_mc_ctime;
877 	if (pn->pn_stat & PNODE_METACACHE_MTIME)
878 		vap->va_mtime = pn->pn_mc_mtime;
879 	if (pn->pn_stat & PNODE_METACACHE_SIZE) {
880 		vap->va_size = pn->pn_mc_size;
881 	} else {
882 		if (rvap->va_size != VNOVAL
883 		    && vp->v_type != VBLK && vp->v_type != VCHR) {
884 			uvm_vnp_setsize(vp, rvap->va_size);
885 			pn->pn_serversize = rvap->va_size;
886 		}
887 	}
888 
889  out:
890 	puffs_releasenode(pn);
891 	PUFFS_MSG_RELEASE(getattr);
892 	return error;
893 }
894 
895 #define SETATTR_CHSIZE	0x01
896 #define SETATTR_ASYNC	0x02
897 static int
898 dosetattr(struct vnode *vp, struct vattr *vap, kauth_cred_t cred, int flags)
899 {
900 	PUFFS_MSG_VARS(vn, setattr);
901 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
902 	struct puffs_node *pn = vp->v_data;
903 	int error = 0;
904 
905 	if ((vp->v_mount->mnt_flag & MNT_RDONLY) &&
906 	    (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL
907 	    || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL
908 	    || vap->va_mode != (mode_t)VNOVAL))
909 		return EROFS;
910 
911 	if ((vp->v_mount->mnt_flag & MNT_RDONLY)
912 	    && vp->v_type == VREG && vap->va_size != VNOVAL)
913 		return EROFS;
914 
915 	/*
916 	 * Flush metacache first.  If we are called with some explicit
917 	 * parameters, treat them as information overriding metacache
918 	 * information.
919 	 */
920 	if (pn->pn_stat & PNODE_METACACHE_MASK) {
921 		if ((pn->pn_stat & PNODE_METACACHE_ATIME)
922 		    && vap->va_atime.tv_sec == VNOVAL)
923 			vap->va_atime = pn->pn_mc_atime;
924 		if ((pn->pn_stat & PNODE_METACACHE_CTIME)
925 		    && vap->va_ctime.tv_sec == VNOVAL)
926 			vap->va_ctime = pn->pn_mc_ctime;
927 		if ((pn->pn_stat & PNODE_METACACHE_MTIME)
928 		    && vap->va_mtime.tv_sec == VNOVAL)
929 			vap->va_mtime = pn->pn_mc_mtime;
930 		if ((pn->pn_stat & PNODE_METACACHE_SIZE)
931 		    && vap->va_size == VNOVAL)
932 			vap->va_size = pn->pn_mc_size;
933 
934 		pn->pn_stat &= ~PNODE_METACACHE_MASK;
935 	}
936 
937 	PUFFS_MSG_ALLOC(vn, setattr);
938 	(void)memcpy(&setattr_msg->pvnr_va, vap, sizeof(struct vattr));
939 	puffs_credcvt(&setattr_msg->pvnr_cred, cred);
940 	puffs_msg_setinfo(park_setattr, PUFFSOP_VN,
941 	    PUFFS_VN_SETATTR, VPTOPNC(vp));
942 	if (flags & SETATTR_ASYNC)
943 		puffs_msg_setfaf(park_setattr);
944 
945 	puffs_msg_enqueue(pmp, park_setattr);
946 	if ((flags & SETATTR_ASYNC) == 0)
947 		error = puffs_msg_wait2(pmp, park_setattr, vp->v_data, NULL);
948 	PUFFS_MSG_RELEASE(setattr);
949 	if ((flags & SETATTR_ASYNC) == 0) {
950 		error = checkerr(pmp, error, __func__);
951 		if (error)
952 			return error;
953 	} else {
954 		error = 0;
955 	}
956 
957 	if (vap->va_size != VNOVAL) {
958 		pn->pn_serversize = vap->va_size;
959 		if (flags & SETATTR_CHSIZE)
960 			uvm_vnp_setsize(vp, vap->va_size);
961 	}
962 
963 	return 0;
964 }
965 
966 int
967 puffs_vnop_setattr(void *v)
968 {
969 	struct vop_getattr_args /* {
970 		const struct vnodeop_desc *a_desc;
971 		struct vnode *a_vp;
972 		struct vattr *a_vap;
973 		kauth_cred_t a_cred;
974 	} */ *ap = v;
975 
976 	return dosetattr(ap->a_vp, ap->a_vap, ap->a_cred, SETATTR_CHSIZE);
977 }
978 
979 static __inline int
980 doinact(struct puffs_mount *pmp, int iaflag)
981 {
982 
983 	if (EXISTSOP(pmp, INACTIVE))
984 		if (pmp->pmp_flags & PUFFS_KFLAG_IAONDEMAND)
985 			if (iaflag || ALLOPS(pmp))
986 				return 1;
987 			else
988 				return 0;
989 		else
990 			return 1;
991 	else
992 		return 0;
993 }
994 
995 static void
996 callinactive(struct puffs_mount *pmp, puffs_cookie_t ck, int iaflag)
997 {
998 	int error;
999 	PUFFS_MSG_VARS(vn, inactive);
1000 
1001 	if (doinact(pmp, iaflag)) {
1002 		PUFFS_MSG_ALLOC(vn, inactive);
1003 		puffs_msg_setinfo(park_inactive, PUFFSOP_VN,
1004 		    PUFFS_VN_INACTIVE, ck);
1005 
1006 		PUFFS_MSG_ENQUEUEWAIT(pmp, park_inactive, error);
1007 		PUFFS_MSG_RELEASE(inactive);
1008 	}
1009 }
1010 
1011 /* XXX: callinactive can't setback */
1012 int
1013 puffs_vnop_inactive(void *v)
1014 {
1015 	struct vop_inactive_args /* {
1016 		const struct vnodeop_desc *a_desc;
1017 		struct vnode *a_vp;
1018 	} */ *ap = v;
1019 	PUFFS_MSG_VARS(vn, inactive);
1020 	struct vnode *vp = ap->a_vp;
1021 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1022 	struct puffs_node *pnode;
1023 	int error;
1024 
1025 	pnode = vp->v_data;
1026 
1027 	if (doinact(pmp, pnode->pn_stat & PNODE_DOINACT)) {
1028 		flushvncache(vp, 0, 0, false);
1029 		PUFFS_MSG_ALLOC(vn, inactive);
1030 		puffs_msg_setinfo(park_inactive, PUFFSOP_VN,
1031 		    PUFFS_VN_INACTIVE, VPTOPNC(vp));
1032 
1033 		PUFFS_MSG_ENQUEUEWAIT2(pmp, park_inactive, vp->v_data,
1034 		    NULL, error);
1035 		PUFFS_MSG_RELEASE(inactive);
1036 	}
1037 	pnode->pn_stat &= ~PNODE_DOINACT;
1038 
1039 	/*
1040 	 * file server thinks it's gone?  then don't be afraid care,
1041 	 * node's life was already all it would ever be
1042 	 */
1043 	if (pnode->pn_stat & PNODE_NOREFS) {
1044 		pnode->pn_stat |= PNODE_DYING;
1045 		*ap->a_recycle = true;
1046 	}
1047 
1048 	VOP_UNLOCK(vp);
1049 
1050 	return 0;
1051 }
1052 
1053 static void
1054 callreclaim(struct puffs_mount *pmp, puffs_cookie_t ck)
1055 {
1056 	PUFFS_MSG_VARS(vn, reclaim);
1057 
1058 	if (!EXISTSOP(pmp, RECLAIM))
1059 		return;
1060 
1061 	PUFFS_MSG_ALLOC(vn, reclaim);
1062 	puffs_msg_setfaf(park_reclaim);
1063 	puffs_msg_setinfo(park_reclaim, PUFFSOP_VN, PUFFS_VN_RECLAIM, ck);
1064 
1065 	puffs_msg_enqueue(pmp, park_reclaim);
1066 	PUFFS_MSG_RELEASE(reclaim);
1067 }
1068 
1069 /*
1070  * always FAF, we don't really care if the server wants to fail to
1071  * reclaim the node or not
1072  */
1073 int
1074 puffs_vnop_reclaim(void *v)
1075 {
1076 	struct vop_reclaim_args /* {
1077 		const struct vnodeop_desc *a_desc;
1078 		struct vnode *a_vp;
1079 	} */ *ap = v;
1080 	struct vnode *vp = ap->a_vp;
1081 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1082 	struct puffs_node *pnode = vp->v_data;
1083 	bool notifyserver = true;
1084 
1085 	/*
1086 	 * first things first: check if someone is trying to reclaim the
1087 	 * root vnode.  do not allow that to travel to userspace.
1088 	 * Note that we don't need to take the lock similarly to
1089 	 * puffs_root(), since there is only one of us.
1090 	 */
1091 	if (vp->v_vflag & VV_ROOT) {
1092 		mutex_enter(&pmp->pmp_lock);
1093 		KASSERT(pmp->pmp_root != NULL);
1094 		pmp->pmp_root = NULL;
1095 		mutex_exit(&pmp->pmp_lock);
1096 		notifyserver = false;
1097 	}
1098 
1099 	/*
1100 	 * purge info from kernel before issueing FAF, since we
1101 	 * don't really know when we'll get around to it after
1102 	 * that and someone might race us into node creation
1103 	 */
1104 	mutex_enter(&pmp->pmp_lock);
1105 	LIST_REMOVE(pnode, pn_hashent);
1106 	mutex_exit(&pmp->pmp_lock);
1107 
1108 	if (notifyserver)
1109 		callreclaim(MPTOPUFFSMP(vp->v_mount), VPTOPNC(vp));
1110 
1111 	puffs_putvnode(vp);
1112 	vp->v_data = NULL;
1113 
1114 	return 0;
1115 }
1116 
1117 #define CSIZE sizeof(**ap->a_cookies)
1118 int
1119 puffs_vnop_readdir(void *v)
1120 {
1121 	struct vop_readdir_args /* {
1122 		const struct vnodeop_desc *a_desc;
1123 		struct vnode *a_vp;
1124 		struct uio *a_uio;
1125 		kauth_cred_t a_cred;
1126 		int *a_eofflag;
1127 		off_t **a_cookies;
1128 		int *a_ncookies;
1129 	} */ *ap = v;
1130 	PUFFS_MSG_VARS(vn, readdir);
1131 	struct vnode *vp = ap->a_vp;
1132 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1133 	size_t argsize, tomove, cookiemem, cookiesmax;
1134 	struct uio *uio = ap->a_uio;
1135 	size_t howmuch, resid;
1136 	int error;
1137 
1138 	/*
1139 	 * ok, so we need: resid + cookiemem = maxreq
1140 	 * => resid + cookiesize * (resid/minsize) = maxreq
1141 	 * => resid + cookiesize/minsize * resid = maxreq
1142 	 * => (cookiesize/minsize + 1) * resid = maxreq
1143 	 * => resid = maxreq / (cookiesize/minsize + 1)
1144 	 *
1145 	 * Since cookiesize <= minsize and we're not very big on floats,
1146 	 * we approximate that to be 1.  Therefore:
1147 	 *
1148 	 * resid = maxreq / 2;
1149 	 *
1150 	 * Well, at least we didn't have to use differential equations
1151 	 * or the Gram-Schmidt process.
1152 	 *
1153 	 * (yes, I'm very afraid of this)
1154 	 */
1155 	KASSERT(CSIZE <= _DIRENT_MINSIZE((struct dirent *)0));
1156 
1157 	if (ap->a_cookies) {
1158 		KASSERT(ap->a_ncookies != NULL);
1159 		if (pmp->pmp_args.pa_fhsize == 0)
1160 			return EOPNOTSUPP;
1161 		resid = PUFFS_TOMOVE(uio->uio_resid, pmp) / 2;
1162 		cookiesmax = resid/_DIRENT_MINSIZE((struct dirent *)0);
1163 		cookiemem = ALIGN(cookiesmax*CSIZE); /* play safe */
1164 	} else {
1165 		resid = PUFFS_TOMOVE(uio->uio_resid, pmp);
1166 		cookiesmax = 0;
1167 		cookiemem = 0;
1168 	}
1169 
1170 	argsize = sizeof(struct puffs_vnmsg_readdir);
1171 	tomove = resid + cookiemem;
1172 	puffs_msgmem_alloc(argsize + tomove, &park_readdir,
1173 	    (void *)&readdir_msg, 1);
1174 
1175 	puffs_credcvt(&readdir_msg->pvnr_cred, ap->a_cred);
1176 	readdir_msg->pvnr_offset = uio->uio_offset;
1177 	readdir_msg->pvnr_resid = resid;
1178 	readdir_msg->pvnr_ncookies = cookiesmax;
1179 	readdir_msg->pvnr_eofflag = 0;
1180 	readdir_msg->pvnr_dentoff = cookiemem;
1181 	puffs_msg_setinfo(park_readdir, PUFFSOP_VN,
1182 	    PUFFS_VN_READDIR, VPTOPNC(vp));
1183 	puffs_msg_setdelta(park_readdir, tomove);
1184 
1185 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readdir, vp->v_data, NULL, error);
1186 	error = checkerr(pmp, error, __func__);
1187 	if (error)
1188 		goto out;
1189 
1190 	/* userspace is cheating? */
1191 	if (readdir_msg->pvnr_resid > resid) {
1192 		puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG,
1193 		    "resid grew", VPTOPNC(vp));
1194 		ERROUT(EPROTO);
1195 	}
1196 	if (readdir_msg->pvnr_ncookies > cookiesmax) {
1197 		puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG,
1198 		    "too many cookies", VPTOPNC(vp));
1199 		ERROUT(EPROTO);
1200 	}
1201 
1202 	/* check eof */
1203 	if (readdir_msg->pvnr_eofflag)
1204 		*ap->a_eofflag = 1;
1205 
1206 	/* bouncy-wouncy with the directory data */
1207 	howmuch = resid - readdir_msg->pvnr_resid;
1208 
1209 	/* force eof if no data was returned (getcwd() needs this) */
1210 	if (howmuch == 0) {
1211 		*ap->a_eofflag = 1;
1212 		goto out;
1213 	}
1214 
1215 	error = uiomove(readdir_msg->pvnr_data + cookiemem, howmuch, uio);
1216 	if (error)
1217 		goto out;
1218 
1219 	/* provide cookies to caller if so desired */
1220 	if (ap->a_cookies) {
1221 		*ap->a_cookies = malloc(readdir_msg->pvnr_ncookies*CSIZE,
1222 		    M_TEMP, M_WAITOK);
1223 		*ap->a_ncookies = readdir_msg->pvnr_ncookies;
1224 		memcpy(*ap->a_cookies, readdir_msg->pvnr_data,
1225 		    *ap->a_ncookies*CSIZE);
1226 	}
1227 
1228 	/* next readdir starts here */
1229 	uio->uio_offset = readdir_msg->pvnr_offset;
1230 
1231  out:
1232 	puffs_msgmem_release(park_readdir);
1233 	return error;
1234 }
1235 #undef CSIZE
1236 
1237 /*
1238  * poll works by consuming the bitmask in pn_revents.  If there are
1239  * events available, poll returns immediately.  If not, it issues a
1240  * poll to userspace, selrecords itself and returns with no available
1241  * events.  When the file server returns, it executes puffs_parkdone_poll(),
1242  * where available events are added to the bitmask.  selnotify() is
1243  * then also executed by that function causing us to enter here again
1244  * and hopefully find the missing bits (unless someone got them first,
1245  * in which case it starts all over again).
1246  */
1247 int
1248 puffs_vnop_poll(void *v)
1249 {
1250 	struct vop_poll_args /* {
1251 		const struct vnodeop_desc *a_desc;
1252 		struct vnode *a_vp;
1253 		int a_events;
1254 	} */ *ap = v;
1255 	PUFFS_MSG_VARS(vn, poll);
1256 	struct vnode *vp = ap->a_vp;
1257 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1258 	struct puffs_node *pn = vp->v_data;
1259 	int events, error;
1260 
1261 	if (EXISTSOP(pmp, POLL)) {
1262 		mutex_enter(&pn->pn_mtx);
1263 		events = pn->pn_revents & ap->a_events;
1264 		if (events & ap->a_events) {
1265 			pn->pn_revents &= ~ap->a_events;
1266 			mutex_exit(&pn->pn_mtx);
1267 
1268 			return events;
1269 		} else {
1270 			puffs_referencenode(pn);
1271 			mutex_exit(&pn->pn_mtx);
1272 
1273 			PUFFS_MSG_ALLOC(vn, poll);
1274 			poll_msg->pvnr_events = ap->a_events;
1275 			puffs_msg_setinfo(park_poll, PUFFSOP_VN,
1276 			    PUFFS_VN_POLL, VPTOPNC(vp));
1277 			puffs_msg_setcall(park_poll, puffs_parkdone_poll, pn);
1278 			selrecord(curlwp, &pn->pn_sel);
1279 
1280 			PUFFS_MSG_ENQUEUEWAIT2(pmp, park_poll, vp->v_data,
1281 			    NULL, error);
1282 			PUFFS_MSG_RELEASE(poll);
1283 
1284 			return 0;
1285 		}
1286 	} else {
1287 		return genfs_poll(v);
1288 	}
1289 }
1290 
1291 static int
1292 flushvncache(struct vnode *vp, off_t offlo, off_t offhi, bool wait)
1293 {
1294 	struct puffs_node *pn = VPTOPP(vp);
1295 	struct vattr va;
1296 	int pflags, error;
1297 
1298 	/* flush out information from our metacache, see vop_setattr */
1299 	if (pn->pn_stat & PNODE_METACACHE_MASK
1300 	    && (pn->pn_stat & PNODE_DYING) == 0) {
1301 		vattr_null(&va);
1302 		error = dosetattr(vp, &va, FSCRED,
1303 		    SETATTR_CHSIZE | (wait ? 0 : SETATTR_ASYNC));
1304 		if (error)
1305 			return error;
1306 	}
1307 
1308 	/*
1309 	 * flush pages to avoid being overly dirty
1310 	 */
1311 	pflags = PGO_CLEANIT;
1312 	if (wait)
1313 		pflags |= PGO_SYNCIO;
1314 	mutex_enter(vp->v_interlock);
1315 	return VOP_PUTPAGES(vp, trunc_page(offlo), round_page(offhi), pflags);
1316 }
1317 
1318 int
1319 puffs_vnop_fsync(void *v)
1320 {
1321 	struct vop_fsync_args /* {
1322 		const struct vnodeop_desc *a_desc;
1323 		struct vnode *a_vp;
1324 		kauth_cred_t a_cred;
1325 		int a_flags;
1326 		off_t a_offlo;
1327 		off_t a_offhi;
1328 	} */ *ap = v;
1329 	PUFFS_MSG_VARS(vn, fsync);
1330 	struct vnode *vp = ap->a_vp;
1331 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1332 	struct puffs_node *pn = VPTOPP(vp);
1333 	int error, dofaf;
1334 
1335 	error = flushvncache(vp, ap->a_offlo, ap->a_offhi,
1336 	    (ap->a_flags & FSYNC_WAIT) == FSYNC_WAIT);
1337 	if (error)
1338 		return error;
1339 
1340 	/*
1341 	 * HELLO!  We exit already here if the user server does not
1342 	 * support fsync OR if we should call fsync for a node which
1343 	 * has references neither in the kernel or the fs server.
1344 	 * Otherwise we continue to issue fsync() forward.
1345 	 */
1346 	if (!EXISTSOP(pmp, FSYNC) || (pn->pn_stat & PNODE_DYING))
1347 		return 0;
1348 
1349 	dofaf = (ap->a_flags & FSYNC_WAIT) == 0 || ap->a_flags == FSYNC_LAZY;
1350 	/*
1351 	 * We abuse VXLOCK to mean "vnode is going to die", so we issue
1352 	 * only FAFs for those.  Otherwise there's a danger of deadlock,
1353 	 * since the execution context here might be the user server
1354 	 * doing some operation on another fs, which in turn caused a
1355 	 * vnode to be reclaimed from the freelist for this fs.
1356 	 */
1357 	if (dofaf == 0) {
1358 		mutex_enter(vp->v_interlock);
1359 		if (vp->v_iflag & VI_XLOCK)
1360 			dofaf = 1;
1361 		mutex_exit(vp->v_interlock);
1362 	}
1363 
1364 	PUFFS_MSG_ALLOC(vn, fsync);
1365 	if (dofaf)
1366 		puffs_msg_setfaf(park_fsync);
1367 
1368 	puffs_credcvt(&fsync_msg->pvnr_cred, ap->a_cred);
1369 	fsync_msg->pvnr_flags = ap->a_flags;
1370 	fsync_msg->pvnr_offlo = ap->a_offlo;
1371 	fsync_msg->pvnr_offhi = ap->a_offhi;
1372 	puffs_msg_setinfo(park_fsync, PUFFSOP_VN,
1373 	    PUFFS_VN_FSYNC, VPTOPNC(vp));
1374 
1375 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_fsync, vp->v_data, NULL, error);
1376 	PUFFS_MSG_RELEASE(fsync);
1377 
1378 	error = checkerr(pmp, error, __func__);
1379 
1380 	return error;
1381 }
1382 
1383 int
1384 puffs_vnop_seek(void *v)
1385 {
1386 	struct vop_seek_args /* {
1387 		const struct vnodeop_desc *a_desc;
1388 		struct vnode *a_vp;
1389 		off_t a_oldoff;
1390 		off_t a_newoff;
1391 		kauth_cred_t a_cred;
1392 	} */ *ap = v;
1393 	PUFFS_MSG_VARS(vn, seek);
1394 	struct vnode *vp = ap->a_vp;
1395 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1396 	int error;
1397 
1398 	PUFFS_MSG_ALLOC(vn, seek);
1399 	seek_msg->pvnr_oldoff = ap->a_oldoff;
1400 	seek_msg->pvnr_newoff = ap->a_newoff;
1401 	puffs_credcvt(&seek_msg->pvnr_cred, ap->a_cred);
1402 	puffs_msg_setinfo(park_seek, PUFFSOP_VN,
1403 	    PUFFS_VN_SEEK, VPTOPNC(vp));
1404 
1405 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_seek, vp->v_data, NULL, error);
1406 	PUFFS_MSG_RELEASE(seek);
1407 	return checkerr(pmp, error, __func__);
1408 }
1409 
1410 static int
1411 callremove(struct puffs_mount *pmp, puffs_cookie_t dck, puffs_cookie_t ck,
1412 	struct componentname *cnp)
1413 {
1414 	PUFFS_MSG_VARS(vn, remove);
1415 	int error;
1416 
1417 	PUFFS_MSG_ALLOC(vn, remove);
1418 	remove_msg->pvnr_cookie_targ = ck;
1419 	puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred,
1420 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
1421 	puffs_msg_setinfo(park_remove, PUFFSOP_VN, PUFFS_VN_REMOVE, dck);
1422 
1423 	PUFFS_MSG_ENQUEUEWAIT(pmp, park_remove, error);
1424 	PUFFS_MSG_RELEASE(remove);
1425 
1426 	return checkerr(pmp, error, __func__);
1427 }
1428 
1429 /*
1430  * XXX: can't use callremove now because can't catch setbacks with
1431  * it due to lack of a pnode argument.
1432  */
1433 int
1434 puffs_vnop_remove(void *v)
1435 {
1436 	struct vop_remove_args /* {
1437 		const struct vnodeop_desc *a_desc;
1438 		struct vnode *a_dvp;
1439 		struct vnode *a_vp;
1440 		struct componentname *a_cnp;
1441 	} */ *ap = v;
1442 	PUFFS_MSG_VARS(vn, remove);
1443 	struct vnode *dvp = ap->a_dvp;
1444 	struct vnode *vp = ap->a_vp;
1445 	struct puffs_node *dpn = VPTOPP(dvp);
1446 	struct puffs_node *pn = VPTOPP(vp);
1447 	struct componentname *cnp = ap->a_cnp;
1448 	struct mount *mp = dvp->v_mount;
1449 	struct puffs_mount *pmp = MPTOPUFFSMP(mp);
1450 	int error;
1451 
1452 	PUFFS_MSG_ALLOC(vn, remove);
1453 	remove_msg->pvnr_cookie_targ = VPTOPNC(vp);
1454 	puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred,
1455 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
1456 	puffs_msg_setinfo(park_remove, PUFFSOP_VN,
1457 	    PUFFS_VN_REMOVE, VPTOPNC(dvp));
1458 
1459 	puffs_msg_enqueue(pmp, park_remove);
1460 	REFPN_AND_UNLOCKVP(dvp, dpn);
1461 	if (dvp == vp)
1462 		REFPN(pn);
1463 	else
1464 		REFPN_AND_UNLOCKVP(vp, pn);
1465 	error = puffs_msg_wait2(pmp, park_remove, dpn, pn);
1466 
1467 	PUFFS_MSG_RELEASE(remove);
1468 
1469 	RELEPN_AND_VP(dvp, dpn);
1470 	RELEPN_AND_VP(vp, pn);
1471 
1472 	error = checkerr(pmp, error, __func__);
1473 	return error;
1474 }
1475 
1476 int
1477 puffs_vnop_mkdir(void *v)
1478 {
1479 	struct vop_mkdir_args /* {
1480 		const struct vnodeop_desc *a_desc;
1481 		struct vnode *a_dvp;
1482 		struct vnode **a_vpp;
1483 		struct componentname *a_cnp;
1484 		struct vattr *a_vap;
1485 	} */ *ap = v;
1486 	PUFFS_MSG_VARS(vn, mkdir);
1487 	struct vnode *dvp = ap->a_dvp;
1488 	struct puffs_node *dpn = VPTOPP(dvp);
1489 	struct componentname *cnp = ap->a_cnp;
1490 	struct mount *mp = dvp->v_mount;
1491 	struct puffs_mount *pmp = MPTOPUFFSMP(mp);
1492 	int error;
1493 
1494 	PUFFS_MSG_ALLOC(vn, mkdir);
1495 	puffs_makecn(&mkdir_msg->pvnr_cn, &mkdir_msg->pvnr_cn_cred,
1496 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
1497 	mkdir_msg->pvnr_va = *ap->a_vap;
1498 	puffs_msg_setinfo(park_mkdir, PUFFSOP_VN,
1499 	    PUFFS_VN_MKDIR, VPTOPNC(dvp));
1500 
1501 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mkdir, dvp->v_data, NULL, error);
1502 
1503 	error = checkerr(pmp, error, __func__);
1504 	if (error)
1505 		goto out;
1506 
1507 	error = puffs_newnode(mp, dvp, ap->a_vpp,
1508 	    mkdir_msg->pvnr_newnode, cnp, VDIR, 0);
1509 	if (error)
1510 		puffs_abortbutton(pmp, PUFFS_ABORT_MKDIR, dpn->pn_cookie,
1511 		    mkdir_msg->pvnr_newnode, cnp);
1512 
1513  out:
1514 	vput(dvp);
1515 	PUFFS_MSG_RELEASE(mkdir);
1516 	return error;
1517 }
1518 
1519 static int
1520 callrmdir(struct puffs_mount *pmp, puffs_cookie_t dck, puffs_cookie_t ck,
1521 	struct componentname *cnp)
1522 {
1523 	PUFFS_MSG_VARS(vn, rmdir);
1524 	int error;
1525 
1526 	PUFFS_MSG_ALLOC(vn, rmdir);
1527 	rmdir_msg->pvnr_cookie_targ = ck;
1528 	puffs_makecn(&rmdir_msg->pvnr_cn, &rmdir_msg->pvnr_cn_cred,
1529 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
1530 	puffs_msg_setinfo(park_rmdir, PUFFSOP_VN, PUFFS_VN_RMDIR, dck);
1531 
1532 	PUFFS_MSG_ENQUEUEWAIT(pmp, park_rmdir, error);
1533 	PUFFS_MSG_RELEASE(rmdir);
1534 
1535 	return checkerr(pmp, error, __func__);
1536 }
1537 
1538 int
1539 puffs_vnop_rmdir(void *v)
1540 {
1541 	struct vop_rmdir_args /* {
1542 		const struct vnodeop_desc *a_desc;
1543 		struct vnode *a_dvp;
1544 		struct vnode *a_vp;
1545 		struct componentname *a_cnp;
1546 	} */ *ap = v;
1547 	PUFFS_MSG_VARS(vn, rmdir);
1548 	struct vnode *dvp = ap->a_dvp;
1549 	struct vnode *vp = ap->a_vp;
1550 	struct puffs_node *dpn = VPTOPP(dvp);
1551 	struct puffs_node *pn = VPTOPP(vp);
1552 	struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
1553 	struct componentname *cnp = ap->a_cnp;
1554 	int error;
1555 
1556 	PUFFS_MSG_ALLOC(vn, rmdir);
1557 	rmdir_msg->pvnr_cookie_targ = VPTOPNC(vp);
1558 	puffs_makecn(&rmdir_msg->pvnr_cn, &rmdir_msg->pvnr_cn_cred,
1559 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
1560 	puffs_msg_setinfo(park_rmdir, PUFFSOP_VN,
1561 	    PUFFS_VN_RMDIR, VPTOPNC(dvp));
1562 
1563 	puffs_msg_enqueue(pmp, park_rmdir);
1564 	REFPN_AND_UNLOCKVP(dvp, dpn);
1565 	REFPN_AND_UNLOCKVP(vp, pn);
1566 	error = puffs_msg_wait2(pmp, park_rmdir, dpn, pn);
1567 
1568 	PUFFS_MSG_RELEASE(rmdir);
1569 
1570 	/* XXX: some call cache_purge() *for both vnodes* here, investigate */
1571 	RELEPN_AND_VP(dvp, dpn);
1572 	RELEPN_AND_VP(vp, pn);
1573 
1574 	return error;
1575 }
1576 
1577 int
1578 puffs_vnop_link(void *v)
1579 {
1580 	struct vop_link_args /* {
1581 		const struct vnodeop_desc *a_desc;
1582 		struct vnode *a_dvp;
1583 		struct vnode *a_vp;
1584 		struct componentname *a_cnp;
1585 	} */ *ap = v;
1586 	PUFFS_MSG_VARS(vn, link);
1587 	struct vnode *dvp = ap->a_dvp;
1588 	struct vnode *vp = ap->a_vp;
1589 	struct puffs_node *dpn = VPTOPP(dvp);
1590 	struct puffs_node *pn = VPTOPP(vp);
1591 	struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
1592 	struct componentname *cnp = ap->a_cnp;
1593 	int error;
1594 
1595 	PUFFS_MSG_ALLOC(vn, link);
1596 	link_msg->pvnr_cookie_targ = VPTOPNC(vp);
1597 	puffs_makecn(&link_msg->pvnr_cn, &link_msg->pvnr_cn_cred,
1598 	    cnp, PUFFS_USE_FULLPNBUF(pmp));
1599 	puffs_msg_setinfo(park_link, PUFFSOP_VN,
1600 	    PUFFS_VN_LINK, VPTOPNC(dvp));
1601 
1602 	puffs_msg_enqueue(pmp, park_link);
1603 	REFPN_AND_UNLOCKVP(dvp, dpn);
1604 	REFPN(pn);
1605 	error = puffs_msg_wait2(pmp, park_link, dpn, pn);
1606 
1607 	PUFFS_MSG_RELEASE(link);
1608 
1609 	error = checkerr(pmp, error, __func__);
1610 
1611 	/*
1612 	 * XXX: stay in touch with the cache.  I don't like this, but
1613 	 * don't have a better solution either.  See also puffs_rename().
1614 	 */
1615 	if (error == 0)
1616 		puffs_updatenode(pn, PUFFS_UPDATECTIME, 0);
1617 
1618 	RELEPN_AND_VP(dvp, dpn);
1619 	puffs_releasenode(pn);
1620 
1621 	return error;
1622 }
1623 
1624 int
1625 puffs_vnop_symlink(void *v)
1626 {
1627 	struct vop_symlink_args /* {
1628 		const struct vnodeop_desc *a_desc;
1629 		struct vnode *a_dvp;
1630 		struct vnode **a_vpp;
1631 		struct componentname *a_cnp;
1632 		struct vattr *a_vap;
1633 		char *a_target;
1634 	} */ *ap = v;
1635 	PUFFS_MSG_VARS(vn, symlink);
1636 	struct vnode *dvp = ap->a_dvp;
1637 	struct puffs_node *dpn = VPTOPP(dvp);
1638 	struct mount *mp = dvp->v_mount;
1639 	struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
1640 	struct componentname *cnp = ap->a_cnp;
1641 	int error;
1642 
1643 	*ap->a_vpp = NULL;
1644 
1645 	PUFFS_MSG_ALLOC(vn, symlink);
1646 	puffs_makecn(&symlink_msg->pvnr_cn, &symlink_msg->pvnr_cn_cred,
1647 		cnp, PUFFS_USE_FULLPNBUF(pmp));
1648 	symlink_msg->pvnr_va = *ap->a_vap;
1649 	(void)strlcpy(symlink_msg->pvnr_link, ap->a_target,
1650 	    sizeof(symlink_msg->pvnr_link));
1651 	puffs_msg_setinfo(park_symlink, PUFFSOP_VN,
1652 	    PUFFS_VN_SYMLINK, VPTOPNC(dvp));
1653 
1654 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_symlink, dvp->v_data, NULL, error);
1655 
1656 	error = checkerr(pmp, error, __func__);
1657 	if (error)
1658 		goto out;
1659 
1660 	error = puffs_newnode(mp, dvp, ap->a_vpp,
1661 	    symlink_msg->pvnr_newnode, cnp, VLNK, 0);
1662 	if (error)
1663 		puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, dpn->pn_cookie,
1664 		    symlink_msg->pvnr_newnode, cnp);
1665 
1666  out:
1667 	vput(dvp);
1668 	PUFFS_MSG_RELEASE(symlink);
1669 
1670 	return error;
1671 }
1672 
1673 int
1674 puffs_vnop_readlink(void *v)
1675 {
1676 	struct vop_readlink_args /* {
1677 		const struct vnodeop_desc *a_desc;
1678 		struct vnode *a_vp;
1679 		struct uio *a_uio;
1680 		kauth_cred_t a_cred;
1681 	} */ *ap = v;
1682 	PUFFS_MSG_VARS(vn, readlink);
1683 	struct vnode *vp = ap->a_vp;
1684 	struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
1685 	size_t linklen;
1686 	int error;
1687 
1688 	PUFFS_MSG_ALLOC(vn, readlink);
1689 	puffs_credcvt(&readlink_msg->pvnr_cred, ap->a_cred);
1690 	linklen = sizeof(readlink_msg->pvnr_link);
1691 	readlink_msg->pvnr_linklen = linklen;
1692 	puffs_msg_setinfo(park_readlink, PUFFSOP_VN,
1693 	    PUFFS_VN_READLINK, VPTOPNC(vp));
1694 
1695 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readlink, vp->v_data, NULL, error);
1696 	error = checkerr(pmp, error, __func__);
1697 	if (error)
1698 		goto out;
1699 
1700 	/* bad bad user file server */
1701 	if (readlink_msg->pvnr_linklen > linklen) {
1702 		puffs_senderr(pmp, PUFFS_ERR_READLINK, E2BIG,
1703 		    "linklen too big", VPTOPNC(ap->a_vp));
1704 		error = EPROTO;
1705 		goto out;
1706 	}
1707 
1708 	error = uiomove(&readlink_msg->pvnr_link, readlink_msg->pvnr_linklen,
1709 	    ap->a_uio);
1710  out:
1711 	PUFFS_MSG_RELEASE(readlink);
1712 	return error;
1713 }
1714 
1715 int
1716 puffs_vnop_rename(void *v)
1717 {
1718 	struct vop_rename_args /* {
1719 		const struct vnodeop_desc *a_desc;
1720 		struct vnode *a_fdvp;
1721 		struct vnode *a_fvp;
1722 		struct componentname *a_fcnp;
1723 		struct vnode *a_tdvp;
1724 		struct vnode *a_tvp;
1725 		struct componentname *a_tcnp;
1726 	} */ *ap = v;
1727 	PUFFS_MSG_VARS(vn, rename);
1728 	struct vnode *fdvp = ap->a_fdvp, *fvp = ap->a_fvp;
1729 	struct vnode *tdvp = ap->a_tdvp, *tvp = ap->a_tvp;
1730 	struct puffs_node *fpn = ap->a_fvp->v_data;
1731 	struct puffs_mount *pmp = MPTOPUFFSMP(fdvp->v_mount);
1732 	int error;
1733 	bool doabort = true;
1734 
1735 	if ((fvp->v_mount != tdvp->v_mount) ||
1736 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
1737 		ERROUT(EXDEV);
1738 	}
1739 
1740 	PUFFS_MSG_ALLOC(vn, rename);
1741 	rename_msg->pvnr_cookie_src = VPTOPNC(fvp);
1742 	rename_msg->pvnr_cookie_targdir = VPTOPNC(tdvp);
1743 	if (tvp)
1744 		rename_msg->pvnr_cookie_targ = VPTOPNC(tvp);
1745 	else
1746 		rename_msg->pvnr_cookie_targ = NULL;
1747 	puffs_makecn(&rename_msg->pvnr_cn_src, &rename_msg->pvnr_cn_src_cred,
1748 	    ap->a_fcnp, PUFFS_USE_FULLPNBUF(pmp));
1749 	puffs_makecn(&rename_msg->pvnr_cn_targ, &rename_msg->pvnr_cn_targ_cred,
1750 	    ap->a_tcnp, PUFFS_USE_FULLPNBUF(pmp));
1751 	puffs_msg_setinfo(park_rename, PUFFSOP_VN,
1752 	    PUFFS_VN_RENAME, VPTOPNC(fdvp));
1753 
1754 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rename, fdvp->v_data, NULL, error);
1755 	doabort = false;
1756 	PUFFS_MSG_RELEASE(rename);
1757 	error = checkerr(pmp, error, __func__);
1758 
1759 	/*
1760 	 * XXX: stay in touch with the cache.  I don't like this, but
1761 	 * don't have a better solution either.  See also puffs_link().
1762 	 */
1763 	if (error == 0)
1764 		puffs_updatenode(fpn, PUFFS_UPDATECTIME, 0);
1765 
1766  out:
1767 	if (doabort)
1768 		VOP_ABORTOP(tdvp, ap->a_tcnp);
1769 	if (tvp != NULL)
1770 		vput(tvp);
1771 	if (tdvp == tvp)
1772 		vrele(tdvp);
1773 	else
1774 		vput(tdvp);
1775 
1776 	if (doabort)
1777 		VOP_ABORTOP(fdvp, ap->a_fcnp);
1778 	vrele(fdvp);
1779 	vrele(fvp);
1780 
1781 	return error;
1782 }
1783 
1784 #define RWARGS(cont, iofl, move, offset, creds)				\
1785 	(cont)->pvnr_ioflag = (iofl);					\
1786 	(cont)->pvnr_resid = (move);					\
1787 	(cont)->pvnr_offset = (offset);					\
1788 	puffs_credcvt(&(cont)->pvnr_cred, creds)
1789 
1790 int
1791 puffs_vnop_read(void *v)
1792 {
1793 	struct vop_read_args /* {
1794 		const struct vnodeop_desc *a_desc;
1795 		struct vnode *a_vp;
1796 		struct uio *a_uio;
1797 		int a_ioflag;
1798 		kauth_cred_t a_cred;
1799 	} */ *ap = v;
1800 	PUFFS_MSG_VARS(vn, read);
1801 	struct vnode *vp = ap->a_vp;
1802 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1803 	struct uio *uio = ap->a_uio;
1804 	size_t tomove, argsize;
1805 	vsize_t bytelen;
1806 	int error;
1807 
1808 	read_msg = NULL;
1809 	error = 0;
1810 
1811 	/* std sanity */
1812 	if (uio->uio_resid == 0)
1813 		return 0;
1814 	if (uio->uio_offset < 0)
1815 		return EINVAL;
1816 
1817 	if (vp->v_type == VREG && PUFFS_USE_PAGECACHE(pmp)) {
1818 		const int advice = IO_ADV_DECODE(ap->a_ioflag);
1819 
1820 		while (uio->uio_resid > 0) {
1821 			bytelen = MIN(uio->uio_resid,
1822 			    vp->v_size - uio->uio_offset);
1823 			if (bytelen == 0)
1824 				break;
1825 
1826 			error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
1827 			    UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
1828 			if (error)
1829 				break;
1830 		}
1831 
1832 		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
1833 			puffs_updatenode(VPTOPP(vp), PUFFS_UPDATEATIME, 0);
1834 	} else {
1835 		/*
1836 		 * in case it's not a regular file or we're operating
1837 		 * uncached, do read in the old-fashioned style,
1838 		 * i.e. explicit read operations
1839 		 */
1840 
1841 		tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
1842 		argsize = sizeof(struct puffs_vnmsg_read);
1843 		puffs_msgmem_alloc(argsize + tomove, &park_read,
1844 		    (void *)&read_msg, 1);
1845 
1846 		error = 0;
1847 		while (uio->uio_resid > 0) {
1848 			tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
1849 			memset(read_msg, 0, argsize); /* XXX: touser KASSERT */
1850 			RWARGS(read_msg, ap->a_ioflag, tomove,
1851 			    uio->uio_offset, ap->a_cred);
1852 			puffs_msg_setinfo(park_read, PUFFSOP_VN,
1853 			    PUFFS_VN_READ, VPTOPNC(vp));
1854 			puffs_msg_setdelta(park_read, tomove);
1855 
1856 			PUFFS_MSG_ENQUEUEWAIT2(pmp, park_read, vp->v_data,
1857 			    NULL, error);
1858 			error = checkerr(pmp, error, __func__);
1859 			if (error)
1860 				break;
1861 
1862 			if (read_msg->pvnr_resid > tomove) {
1863 				puffs_senderr(pmp, PUFFS_ERR_READ,
1864 				    E2BIG, "resid grew", VPTOPNC(ap->a_vp));
1865 				error = EPROTO;
1866 				break;
1867 			}
1868 
1869 			error = uiomove(read_msg->pvnr_data,
1870 			    tomove - read_msg->pvnr_resid, uio);
1871 
1872 			/*
1873 			 * in case the file is out of juice, resid from
1874 			 * userspace is != 0.  and the error-case is
1875 			 * quite obvious
1876 			 */
1877 			if (error || read_msg->pvnr_resid)
1878 				break;
1879 		}
1880 
1881 		puffs_msgmem_release(park_read);
1882 	}
1883 
1884 	return error;
1885 }
1886 
1887 /*
1888  * XXX: in case of a failure, this leaves uio in a bad state.
1889  * We could theoretically copy the uio and iovecs and "replay"
1890  * them the right amount after the userspace trip, but don't
1891  * bother for now.
1892  */
1893 int
1894 puffs_vnop_write(void *v)
1895 {
1896 	struct vop_write_args /* {
1897 		const struct vnodeop_desc *a_desc;
1898 		struct vnode *a_vp;
1899 		struct uio *a_uio;
1900 		int a_ioflag;
1901 		kauth_cred_t a_cred;
1902 	} */ *ap = v;
1903 	PUFFS_MSG_VARS(vn, write);
1904 	struct vnode *vp = ap->a_vp;
1905 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1906 	struct uio *uio = ap->a_uio;
1907 	size_t tomove, argsize;
1908 	off_t oldoff, newoff, origoff;
1909 	vsize_t bytelen;
1910 	int error, uflags;
1911 	int ubcflags;
1912 
1913 	error = uflags = 0;
1914 	write_msg = NULL;
1915 
1916 	if (vp->v_type == VREG && PUFFS_USE_PAGECACHE(pmp)) {
1917 		ubcflags = UBC_WRITE | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp);
1918 
1919 		/*
1920 		 * userspace *should* be allowed to control this,
1921 		 * but with UBC it's a bit unclear how to handle it
1922 		 */
1923 		if (ap->a_ioflag & IO_APPEND)
1924 			uio->uio_offset = vp->v_size;
1925 
1926 		origoff = uio->uio_offset;
1927 		while (uio->uio_resid > 0) {
1928 			uflags |= PUFFS_UPDATECTIME;
1929 			uflags |= PUFFS_UPDATEMTIME;
1930 			oldoff = uio->uio_offset;
1931 			bytelen = uio->uio_resid;
1932 
1933 			newoff = oldoff + bytelen;
1934 			if (vp->v_size < newoff) {
1935 				uvm_vnp_setwritesize(vp, newoff);
1936 			}
1937 			error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
1938 			    UVM_ADV_RANDOM, ubcflags);
1939 
1940 			/*
1941 			 * In case of a ubc_uiomove() error,
1942 			 * opt to not extend the file at all and
1943 			 * return an error.  Otherwise, if we attempt
1944 			 * to clear the memory we couldn't fault to,
1945 			 * we might generate a kernel page fault.
1946 			 */
1947 			if (vp->v_size < newoff) {
1948 				if (error == 0) {
1949 					uflags |= PUFFS_UPDATESIZE;
1950 					uvm_vnp_setsize(vp, newoff);
1951 				} else {
1952 					uvm_vnp_setwritesize(vp, vp->v_size);
1953 				}
1954 			}
1955 			if (error)
1956 				break;
1957 
1958 			/*
1959 			 * If we're writing large files, flush to file server
1960 			 * every 64k.  Otherwise we can very easily exhaust
1961 			 * kernel and user memory, as the file server cannot
1962 			 * really keep up with our writing speed.
1963 			 *
1964 			 * Note: this does *NOT* honor MNT_ASYNC, because
1965 			 * that gives userland too much say in the kernel.
1966 			 */
1967 			if (oldoff >> 16 != uio->uio_offset >> 16) {
1968 				mutex_enter(vp->v_interlock);
1969 				error = VOP_PUTPAGES(vp, oldoff & ~0xffff,
1970 				    uio->uio_offset & ~0xffff,
1971 				    PGO_CLEANIT | PGO_SYNCIO);
1972 				if (error)
1973 					break;
1974 			}
1975 		}
1976 
1977 		/* synchronous I/O? */
1978 		if (error == 0 && ap->a_ioflag & IO_SYNC) {
1979 			mutex_enter(vp->v_interlock);
1980 			error = VOP_PUTPAGES(vp, trunc_page(origoff),
1981 			    round_page(uio->uio_offset),
1982 			    PGO_CLEANIT | PGO_SYNCIO);
1983 
1984 		/* write through page cache? */
1985 		} else if (error == 0 && pmp->pmp_flags & PUFFS_KFLAG_WTCACHE) {
1986 			mutex_enter(vp->v_interlock);
1987 			error = VOP_PUTPAGES(vp, trunc_page(origoff),
1988 			    round_page(uio->uio_offset), PGO_CLEANIT);
1989 		}
1990 
1991 		puffs_updatenode(VPTOPP(vp), uflags, vp->v_size);
1992 	} else {
1993 		/* tomove is non-increasing */
1994 		tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
1995 		argsize = sizeof(struct puffs_vnmsg_write) + tomove;
1996 		puffs_msgmem_alloc(argsize, &park_write, (void *)&write_msg,1);
1997 
1998 		while (uio->uio_resid > 0) {
1999 			/* move data to buffer */
2000 			tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
2001 			memset(write_msg, 0, argsize); /* XXX: touser KASSERT */
2002 			RWARGS(write_msg, ap->a_ioflag, tomove,
2003 			    uio->uio_offset, ap->a_cred);
2004 			error = uiomove(write_msg->pvnr_data, tomove, uio);
2005 			if (error)
2006 				break;
2007 
2008 			/* move buffer to userspace */
2009 			puffs_msg_setinfo(park_write, PUFFSOP_VN,
2010 			    PUFFS_VN_WRITE, VPTOPNC(vp));
2011 			PUFFS_MSG_ENQUEUEWAIT2(pmp, park_write, vp->v_data,
2012 			    NULL, error);
2013 			error = checkerr(pmp, error, __func__);
2014 			if (error)
2015 				break;
2016 
2017 			if (write_msg->pvnr_resid > tomove) {
2018 				puffs_senderr(pmp, PUFFS_ERR_WRITE,
2019 				    E2BIG, "resid grew", VPTOPNC(ap->a_vp));
2020 				error = EPROTO;
2021 				break;
2022 			}
2023 
2024 			/* adjust file size */
2025 			if (vp->v_size < uio->uio_offset)
2026 				uvm_vnp_setsize(vp, uio->uio_offset);
2027 
2028 			/* didn't move everything?  bad userspace.  bail */
2029 			if (write_msg->pvnr_resid != 0) {
2030 				error = EIO;
2031 				break;
2032 			}
2033 		}
2034 		puffs_msgmem_release(park_write);
2035 	}
2036 
2037 	return error;
2038 }
2039 
2040 int
2041 puffs_vnop_print(void *v)
2042 {
2043 	struct vop_print_args /* {
2044 		struct vnode *a_vp;
2045 	} */ *ap = v;
2046 	PUFFS_MSG_VARS(vn, print);
2047 	struct vnode *vp = ap->a_vp;
2048 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2049 	struct puffs_node *pn = vp->v_data;
2050 	int error;
2051 
2052 	/* kernel portion */
2053 	printf("tag VT_PUFFS, vnode %p, puffs node: %p,\n"
2054 	    "\tuserspace cookie: %p", vp, pn, pn->pn_cookie);
2055 	if (vp->v_type == VFIFO)
2056 		VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v);
2057 	printf("\n");
2058 
2059 	/* userspace portion */
2060 	if (EXISTSOP(pmp, PRINT)) {
2061 		PUFFS_MSG_ALLOC(vn, print);
2062 		puffs_msg_setinfo(park_print, PUFFSOP_VN,
2063 		    PUFFS_VN_PRINT, VPTOPNC(vp));
2064 		PUFFS_MSG_ENQUEUEWAIT2(pmp, park_print, vp->v_data,
2065 		    NULL, error);
2066 		PUFFS_MSG_RELEASE(print);
2067 	}
2068 
2069 	return 0;
2070 }
2071 
2072 int
2073 puffs_vnop_pathconf(void *v)
2074 {
2075 	struct vop_pathconf_args /* {
2076 		const struct vnodeop_desc *a_desc;
2077 		struct vnode *a_vp;
2078 		int a_name;
2079 		register_t *a_retval;
2080 	} */ *ap = v;
2081 	PUFFS_MSG_VARS(vn, pathconf);
2082 	struct vnode *vp = ap->a_vp;
2083 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2084 	int error;
2085 
2086 	PUFFS_MSG_ALLOC(vn, pathconf);
2087 	pathconf_msg->pvnr_name = ap->a_name;
2088 	puffs_msg_setinfo(park_pathconf, PUFFSOP_VN,
2089 	    PUFFS_VN_PATHCONF, VPTOPNC(vp));
2090 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_pathconf, vp->v_data, NULL, error);
2091 	error = checkerr(pmp, error, __func__);
2092 	if (!error)
2093 		*ap->a_retval = pathconf_msg->pvnr_retval;
2094 	PUFFS_MSG_RELEASE(pathconf);
2095 
2096 	return error;
2097 }
2098 
2099 int
2100 puffs_vnop_advlock(void *v)
2101 {
2102 	struct vop_advlock_args /* {
2103 		const struct vnodeop_desc *a_desc;
2104 		struct vnode *a_vp;
2105 		void *a_id;
2106 		int a_op;
2107 		struct flock *a_fl;
2108 		int a_flags;
2109 	} */ *ap = v;
2110 	PUFFS_MSG_VARS(vn, advlock);
2111 	struct vnode *vp = ap->a_vp;
2112 	struct puffs_node *pn = VPTOPP(vp);
2113 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2114 	int error;
2115 
2116 	if (!EXISTSOP(pmp, ADVLOCK))
2117 		return lf_advlock(ap, &pn->pn_lockf, vp->v_size);
2118 
2119 	PUFFS_MSG_ALLOC(vn, advlock);
2120 	(void)memcpy(&advlock_msg->pvnr_fl, ap->a_fl,
2121 		     sizeof(advlock_msg->pvnr_fl));
2122 	advlock_msg->pvnr_id = ap->a_id;
2123 	advlock_msg->pvnr_op = ap->a_op;
2124 	advlock_msg->pvnr_flags = ap->a_flags;
2125 	puffs_msg_setinfo(park_advlock, PUFFSOP_VN,
2126 	    PUFFS_VN_ADVLOCK, VPTOPNC(vp));
2127 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_advlock, vp->v_data, NULL, error);
2128 	error = checkerr(pmp, error, __func__);
2129 	PUFFS_MSG_RELEASE(advlock);
2130 
2131 	return error;
2132 }
2133 
2134 int
2135 puffs_vnop_abortop(void *v)
2136 {
2137 	struct vop_abortop_args /* {
2138 		struct vnode *a_dvp;
2139 		struct componentname *a_cnp;
2140 	}; */ *ap = v;
2141 	PUFFS_MSG_VARS(vn, abortop);
2142 	struct vnode *dvp = ap->a_dvp;
2143 	struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
2144 	struct componentname *cnp = ap->a_cnp;
2145 
2146 	if (EXISTSOP(pmp, ABORTOP)) {
2147 		PUFFS_MSG_ALLOC(vn, abortop);
2148 		puffs_makecn(&abortop_msg->pvnr_cn, &abortop_msg->pvnr_cn_cred,
2149 		    cnp, PUFFS_USE_FULLPNBUF(pmp));
2150 		puffs_msg_setfaf(park_abortop);
2151 		puffs_msg_setinfo(park_abortop, PUFFSOP_VN,
2152 		    PUFFS_VN_ABORTOP, VPTOPNC(dvp));
2153 
2154 		puffs_msg_enqueue(pmp, park_abortop);
2155 		PUFFS_MSG_RELEASE(abortop);
2156 	}
2157 
2158 	return genfs_abortop(v);
2159 }
2160 
2161 #define BIOASYNC(bp) (bp->b_flags & B_ASYNC)
2162 
2163 /*
2164  * This maps itself to PUFFS_VN_READ/WRITE for data transfer.
2165  */
2166 int
2167 puffs_vnop_strategy(void *v)
2168 {
2169 	struct vop_strategy_args /* {
2170 		const struct vnodeop_desc *a_desc;
2171 		struct vnode *a_vp;
2172 		struct buf *a_bp;
2173 	} */ *ap = v;
2174 	PUFFS_MSG_VARS(vn, rw);
2175 	struct vnode *vp = ap->a_vp;
2176 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2177 	struct puffs_node *pn;
2178 	struct buf *bp;
2179 	size_t argsize;
2180 	size_t tomove, moved;
2181 	int error, dofaf, dobiodone;
2182 
2183 	pmp = MPTOPUFFSMP(vp->v_mount);
2184 	bp = ap->a_bp;
2185 	error = 0;
2186 	dofaf = 0;
2187 	pn = VPTOPP(vp);
2188 	park_rw = NULL; /* explicit */
2189 	dobiodone = 1;
2190 
2191 	if ((BUF_ISREAD(bp) && !EXISTSOP(pmp, READ))
2192 	    || (BUF_ISWRITE(bp) && !EXISTSOP(pmp, WRITE)))
2193 		ERROUT(EOPNOTSUPP);
2194 
2195 	/*
2196 	 * Short-circuit optimization: don't flush buffer in between
2197 	 * VOP_INACTIVE and VOP_RECLAIM in case the node has no references.
2198 	 */
2199 	if (pn->pn_stat & PNODE_DYING) {
2200 		KASSERT(BUF_ISWRITE(bp));
2201 		bp->b_resid = 0;
2202 		goto out;
2203 	}
2204 
2205 #ifdef DIAGNOSTIC
2206 	if (bp->b_bcount > pmp->pmp_msg_maxsize - PUFFS_MSGSTRUCT_MAX)
2207 		panic("puffs_strategy: wildly inappropriate buf bcount %d",
2208 		    bp->b_bcount);
2209 #endif
2210 
2211 	/*
2212 	 * See explanation for the necessity of a FAF in puffs_fsync.
2213 	 *
2214 	 * Also, do FAF in case we're suspending.
2215 	 * See puffs_vfsops.c:pageflush()
2216 	 */
2217 	if (BUF_ISWRITE(bp)) {
2218 		mutex_enter(vp->v_interlock);
2219 		if (vp->v_iflag & VI_XLOCK)
2220 			dofaf = 1;
2221 		if (pn->pn_stat & PNODE_FAF)
2222 			dofaf = 1;
2223 		mutex_exit(vp->v_interlock);
2224 	}
2225 
2226 #ifdef DIAGNOSTIC
2227 		if (curlwp == uvm.pagedaemon_lwp)
2228 			KASSERT(dofaf || BIOASYNC(bp));
2229 #endif
2230 
2231 	/* allocate transport structure */
2232 	tomove = PUFFS_TOMOVE(bp->b_bcount, pmp);
2233 	argsize = sizeof(struct puffs_vnmsg_rw);
2234 	error = puffs_msgmem_alloc(argsize + tomove, &park_rw,
2235 	    (void *)&rw_msg, dofaf ? 0 : 1);
2236 	if (error)
2237 		goto out;
2238 	RWARGS(rw_msg, 0, tomove, bp->b_blkno << DEV_BSHIFT, FSCRED);
2239 
2240 	/* 2x2 cases: read/write, faf/nofaf */
2241 	if (BUF_ISREAD(bp)) {
2242 		puffs_msg_setinfo(park_rw, PUFFSOP_VN,
2243 		    PUFFS_VN_READ, VPTOPNC(vp));
2244 		puffs_msg_setdelta(park_rw, tomove);
2245 		if (BIOASYNC(bp)) {
2246 			puffs_msg_setcall(park_rw,
2247 			    puffs_parkdone_asyncbioread, bp);
2248 			puffs_msg_enqueue(pmp, park_rw);
2249 			dobiodone = 0;
2250 		} else {
2251 			PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rw, vp->v_data,
2252 			    NULL, error);
2253 			error = checkerr(pmp, error, __func__);
2254 			if (error)
2255 				goto out;
2256 
2257 			if (rw_msg->pvnr_resid > tomove) {
2258 				puffs_senderr(pmp, PUFFS_ERR_READ,
2259 				    E2BIG, "resid grew", VPTOPNC(vp));
2260 				ERROUT(EPROTO);
2261 			}
2262 
2263 			moved = tomove - rw_msg->pvnr_resid;
2264 
2265 			(void)memcpy(bp->b_data, rw_msg->pvnr_data, moved);
2266 			bp->b_resid = bp->b_bcount - moved;
2267 		}
2268 	} else {
2269 		puffs_msg_setinfo(park_rw, PUFFSOP_VN,
2270 		    PUFFS_VN_WRITE, VPTOPNC(vp));
2271 		/*
2272 		 * make pages read-only before we write them if we want
2273 		 * write caching info
2274 		 */
2275 		if (PUFFS_WCACHEINFO(pmp)) {
2276 			struct uvm_object *uobj = &vp->v_uobj;
2277 			int npages = (bp->b_bcount + PAGE_SIZE-1) >> PAGE_SHIFT;
2278 			struct vm_page *vmp;
2279 			int i;
2280 
2281 			for (i = 0; i < npages; i++) {
2282 				vmp= uvm_pageratop((vaddr_t)bp->b_data
2283 				    + (i << PAGE_SHIFT));
2284 				DPRINTF(("puffs_strategy: write-protecting "
2285 				    "vp %p page %p, offset %" PRId64"\n",
2286 				    vp, vmp, vmp->offset));
2287 				mutex_enter(uobj->vmobjlock);
2288 				vmp->flags |= PG_RDONLY;
2289 				pmap_page_protect(vmp, VM_PROT_READ);
2290 				mutex_exit(uobj->vmobjlock);
2291 			}
2292 		}
2293 
2294 		(void)memcpy(&rw_msg->pvnr_data, bp->b_data, tomove);
2295 		if (dofaf) {
2296 			puffs_msg_setfaf(park_rw);
2297 		} else if (BIOASYNC(bp)) {
2298 			puffs_msg_setcall(park_rw,
2299 			    puffs_parkdone_asyncbiowrite, bp);
2300 			dobiodone = 0;
2301 		}
2302 
2303 		PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rw, vp->v_data, NULL, error);
2304 
2305 		if (dobiodone == 0)
2306 			goto out;
2307 
2308 		/*
2309 		 * XXXXXXXX: wrong, but kernel can't survive strategy
2310 		 * failure currently.  Here, have one more X: X.
2311 		 */
2312 		if (error != ENOMEM)
2313 			error = 0;
2314 
2315 		error = checkerr(pmp, error, __func__);
2316 		if (error)
2317 			goto out;
2318 
2319 		if (rw_msg->pvnr_resid > tomove) {
2320 			puffs_senderr(pmp, PUFFS_ERR_WRITE,
2321 			    E2BIG, "resid grew", VPTOPNC(vp));
2322 			ERROUT(EPROTO);
2323 		}
2324 
2325 		/*
2326 		 * FAF moved everything.  Frankly, we don't
2327 		 * really have a choice.
2328 		 */
2329 		if (dofaf && error == 0)
2330 			moved = tomove;
2331 		else
2332 			moved = tomove - rw_msg->pvnr_resid;
2333 
2334 		bp->b_resid = bp->b_bcount - moved;
2335 		if (bp->b_resid != 0) {
2336 			ERROUT(EIO);
2337 		}
2338 	}
2339 
2340  out:
2341 	if (park_rw)
2342 		puffs_msgmem_release(park_rw);
2343 
2344 	if (error)
2345 		bp->b_error = error;
2346 
2347 	if (error || dobiodone)
2348 		biodone(bp);
2349 
2350 	return error;
2351 }
2352 
2353 int
2354 puffs_vnop_mmap(void *v)
2355 {
2356 	struct vop_mmap_args /* {
2357 		const struct vnodeop_desc *a_desc;
2358 		struct vnode *a_vp;
2359 		vm_prot_t a_prot;
2360 		kauth_cred_t a_cred;
2361 	} */ *ap = v;
2362 	PUFFS_MSG_VARS(vn, mmap);
2363 	struct vnode *vp = ap->a_vp;
2364 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2365 	int error;
2366 
2367 	if (!PUFFS_USE_PAGECACHE(pmp))
2368 		return genfs_eopnotsupp(v);
2369 
2370 	if (EXISTSOP(pmp, MMAP)) {
2371 		PUFFS_MSG_ALLOC(vn, mmap);
2372 		mmap_msg->pvnr_prot = ap->a_prot;
2373 		puffs_credcvt(&mmap_msg->pvnr_cred, ap->a_cred);
2374 		puffs_msg_setinfo(park_mmap, PUFFSOP_VN,
2375 		    PUFFS_VN_MMAP, VPTOPNC(vp));
2376 
2377 		PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mmap, vp->v_data, NULL, error);
2378 		error = checkerr(pmp, error, __func__);
2379 		PUFFS_MSG_RELEASE(mmap);
2380 	} else {
2381 		error = genfs_mmap(v);
2382 	}
2383 
2384 	return error;
2385 }
2386 
2387 
2388 /*
2389  * The rest don't get a free trip to userspace and back, they
2390  * have to stay within the kernel.
2391  */
2392 
2393 /*
2394  * bmap doesn't really make any sense for puffs, so just 1:1 map it.
2395  * well, maybe somehow, somewhere, some day ....
2396  */
2397 int
2398 puffs_vnop_bmap(void *v)
2399 {
2400 	struct vop_bmap_args /* {
2401 		const struct vnodeop_desc *a_desc;
2402 		struct vnode *a_vp;
2403 		daddr_t a_bn;
2404 		struct vnode **a_vpp;
2405 		daddr_t *a_bnp;
2406 		int *a_runp;
2407 	} */ *ap = v;
2408 	struct puffs_mount *pmp;
2409 
2410 	pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
2411 
2412 	if (ap->a_vpp)
2413 		*ap->a_vpp = ap->a_vp;
2414 	if (ap->a_bnp)
2415 		*ap->a_bnp = ap->a_bn;
2416 	if (ap->a_runp)
2417 		*ap->a_runp
2418 		    = (PUFFS_TOMOVE(pmp->pmp_msg_maxsize, pmp)>>DEV_BSHIFT) - 1;
2419 
2420 	return 0;
2421 }
2422 
2423 /*
2424  * Handle getpages faults in puffs.  We let genfs_getpages() do most
2425  * of the dirty work, but we come in this route to do accounting tasks.
2426  * If the user server has specified functions for cache notifications
2427  * about reads and/or writes, we record which type of operation we got,
2428  * for which page range, and proceed to issue a FAF notification to the
2429  * server about it.
2430  */
2431 int
2432 puffs_vnop_getpages(void *v)
2433 {
2434 	struct vop_getpages_args /* {
2435 		const struct vnodeop_desc *a_desc;
2436 		struct vnode *a_vp;
2437 		voff_t a_offset;
2438 		struct vm_page **a_m;
2439 		int *a_count;
2440 		int a_centeridx;
2441 		vm_prot_t a_access_type;
2442 		int a_advice;
2443 		int a_flags;
2444 	} */ *ap = v;
2445 	struct puffs_mount *pmp;
2446 	struct puffs_node *pn;
2447 	struct vnode *vp;
2448 	struct vm_page **pgs;
2449 	struct puffs_cacheinfo *pcinfo = NULL;
2450 	struct puffs_cacherun *pcrun;
2451 	void *parkmem = NULL;
2452 	size_t runsizes;
2453 	int i, npages, si, streakon;
2454 	int error, locked, write;
2455 
2456 	pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
2457 	npages = *ap->a_count;
2458 	pgs = ap->a_m;
2459 	vp = ap->a_vp;
2460 	pn = vp->v_data;
2461 	locked = (ap->a_flags & PGO_LOCKED) != 0;
2462 	write = (ap->a_access_type & VM_PROT_WRITE) != 0;
2463 
2464 	/* ccg xnaht - gets Wuninitialized wrong */
2465 	pcrun = NULL;
2466 	runsizes = 0;
2467 
2468 	/*
2469 	 * Check that we aren't trying to fault in pages which our file
2470 	 * server doesn't know about.  This happens if we extend a file by
2471 	 * skipping some pages and later try to fault in pages which
2472 	 * are between pn_serversize and vp_size.  This check optimizes
2473 	 * away the common case where a file is being extended.
2474 	 */
2475 	if (ap->a_offset >= pn->pn_serversize && ap->a_offset < vp->v_size) {
2476 		struct vattr va;
2477 
2478 		/* try again later when we can block */
2479 		if (locked)
2480 			ERROUT(EBUSY);
2481 
2482 		mutex_exit(vp->v_interlock);
2483 		vattr_null(&va);
2484 		va.va_size = vp->v_size;
2485 		error = dosetattr(vp, &va, FSCRED, 0);
2486 		if (error)
2487 			ERROUT(error);
2488 		mutex_enter(vp->v_interlock);
2489 	}
2490 
2491 	if (write && PUFFS_WCACHEINFO(pmp)) {
2492 #ifdef notnowjohn
2493 		/* allocate worst-case memory */
2494 		runsizes = ((npages / 2) + 1) * sizeof(struct puffs_cacherun);
2495 		pcinfo = kmem_zalloc(sizeof(struct puffs_cacheinfo) + runsize,
2496 		    locked ? KM_NOSLEEP : KM_SLEEP);
2497 
2498 		/*
2499 		 * can't block if we're locked and can't mess up caching
2500 		 * information for fs server.  so come back later, please
2501 		 */
2502 		if (pcinfo == NULL)
2503 			ERROUT(ENOMEM);
2504 
2505 		parkmem = puffs_park_alloc(locked == 0);
2506 		if (parkmem == NULL)
2507 			ERROUT(ENOMEM);
2508 
2509 		pcrun = pcinfo->pcache_runs;
2510 #else
2511 		(void)parkmem;
2512 #endif
2513 	}
2514 
2515 	error = genfs_getpages(v);
2516 	if (error)
2517 		goto out;
2518 
2519 	if (PUFFS_WCACHEINFO(pmp) == 0)
2520 		goto out;
2521 
2522 	/*
2523 	 * Let's see whose fault it was and inform the user server of
2524 	 * possibly read/written pages.  Map pages from read faults
2525 	 * strictly read-only, since otherwise we might miss info on
2526 	 * when the page is actually write-faulted to.
2527 	 */
2528 	if (!locked)
2529 		mutex_enter(vp->v_uobj.vmobjlock);
2530 	for (i = 0, si = 0, streakon = 0; i < npages; i++) {
2531 		if (pgs[i] == NULL || pgs[i] == PGO_DONTCARE) {
2532 			if (streakon && write) {
2533 				streakon = 0;
2534 				pcrun[si].pcache_runend
2535 				    = trunc_page(pgs[i]->offset) + PAGE_MASK;
2536 				si++;
2537 			}
2538 			continue;
2539 		}
2540 		if (streakon == 0 && write) {
2541 			streakon = 1;
2542 			pcrun[si].pcache_runstart = pgs[i]->offset;
2543 		}
2544 
2545 		if (!write)
2546 			pgs[i]->flags |= PG_RDONLY;
2547 	}
2548 	/* was the last page part of our streak? */
2549 	if (streakon) {
2550 		pcrun[si].pcache_runend
2551 		    = trunc_page(pgs[i-1]->offset) + PAGE_MASK;
2552 		si++;
2553 	}
2554 	if (!locked)
2555 		mutex_exit(vp->v_uobj.vmobjlock);
2556 
2557 	KASSERT(si <= (npages / 2) + 1);
2558 
2559 #ifdef notnowjohn
2560 	/* send results to userspace */
2561 	if (write)
2562 		puffs_cacheop(pmp, parkmem, pcinfo,
2563 		    sizeof(struct puffs_cacheinfo) + runsizes, VPTOPNC(vp));
2564 #endif
2565 
2566  out:
2567 	if (error) {
2568 		if (pcinfo != NULL)
2569 			kmem_free(pcinfo,
2570 			    sizeof(struct puffs_cacheinfo) + runsizes);
2571 #ifdef notnowjohn
2572 		if (parkmem != NULL)
2573 			puffs_park_release(parkmem, 1);
2574 #endif
2575 	}
2576 
2577 	return error;
2578 }
2579 
2580 /*
2581  * Extended attribute support.
2582  */
2583 
2584 int
2585 puffs_vnop_getextattr(void *v)
2586 {
2587 	struct vop_getextattr_args /*
2588 		struct vnode *a_vp;
2589 		int a_attrnamespace;
2590 		const char *a_name;
2591 		struct uio *a_uio;
2592 		size_t *a_size;
2593 		kauth_cred_t a_cred;
2594 	}; */ *ap = v;
2595 	PUFFS_MSG_VARS(vn, getextattr);
2596 	struct vnode *vp = ap->a_vp;
2597 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2598 	int attrnamespace = ap->a_attrnamespace;
2599 	const char *name = ap->a_name;
2600 	struct uio *uio = ap->a_uio;
2601 	size_t *sizep = ap->a_size;
2602 	size_t tomove, resid;
2603 	int error;
2604 
2605 	if (uio)
2606 		resid = uio->uio_resid;
2607 	else
2608 		resid = 0;
2609 
2610 	tomove = PUFFS_TOMOVE(resid, pmp);
2611 	if (tomove != resid) {
2612 		error = E2BIG;
2613 		goto out;
2614 	}
2615 
2616 	puffs_msgmem_alloc(sizeof(struct puffs_vnmsg_getextattr) + tomove,
2617 	    &park_getextattr, (void *)&getextattr_msg, 1);
2618 
2619 	getextattr_msg->pvnr_attrnamespace = attrnamespace;
2620 	strlcpy(getextattr_msg->pvnr_attrname, name,
2621 	    sizeof(getextattr_msg->pvnr_attrname));
2622 	puffs_credcvt(&getextattr_msg->pvnr_cred, ap->a_cred);
2623 	if (sizep)
2624 		getextattr_msg->pvnr_datasize = 1;
2625 	getextattr_msg->pvnr_resid = tomove;
2626 
2627 	puffs_msg_setinfo(park_getextattr,
2628 	    PUFFSOP_VN, PUFFS_VN_GETEXTATTR, VPTOPNC(vp));
2629 	puffs_msg_setdelta(park_getextattr, tomove);
2630 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_getextattr, vp->v_data, NULL, error);
2631 
2632 	error = checkerr(pmp, error, __func__);
2633 	if (error)
2634 		goto out;
2635 
2636 	resid = getextattr_msg->pvnr_resid;
2637 	if (resid > tomove) {
2638 		puffs_senderr(pmp, PUFFS_ERR_GETEXTATTR, E2BIG,
2639 		    "resid grew", VPTOPNC(vp));
2640 		error = EPROTO;
2641 		goto out;
2642 	}
2643 
2644 	if (sizep)
2645 		*sizep = getextattr_msg->pvnr_datasize;
2646 	if (uio)
2647 		error = uiomove(getextattr_msg->pvnr_data, tomove - resid, uio);
2648 
2649  out:
2650 	PUFFS_MSG_RELEASE(getextattr);
2651 	return error;
2652 }
2653 
2654 int
2655 puffs_vnop_setextattr(void *v)
2656 {
2657 	struct vop_setextattr_args /* {
2658 		struct vnode *a_vp;
2659 		int a_attrnamespace;
2660 		const char *a_name;
2661 		struct uio *a_uio;
2662 		kauth_cred_t a_cred;
2663 	}; */ *ap = v;
2664 	PUFFS_MSG_VARS(vn, setextattr);
2665 	struct vnode *vp = ap->a_vp;
2666 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2667 	int attrnamespace = ap->a_attrnamespace;
2668 	const char *name = ap->a_name;
2669 	struct uio *uio = ap->a_uio;
2670 	size_t tomove, resid;
2671 	int error;
2672 
2673 	if (uio)
2674 		resid = uio->uio_resid;
2675 	else
2676 		resid = 0;
2677 
2678 	tomove = PUFFS_TOMOVE(resid, pmp);
2679 	if (tomove != resid) {
2680 		error = E2BIG;
2681 		goto out;
2682 	}
2683 
2684 	puffs_msgmem_alloc(sizeof(struct puffs_vnmsg_setextattr) + tomove,
2685 	    &park_setextattr, (void *)&setextattr_msg, 1);
2686 
2687 	setextattr_msg->pvnr_attrnamespace = attrnamespace;
2688 	strlcpy(setextattr_msg->pvnr_attrname, name,
2689 	    sizeof(setextattr_msg->pvnr_attrname));
2690 	puffs_credcvt(&setextattr_msg->pvnr_cred, ap->a_cred);
2691 	setextattr_msg->pvnr_resid = tomove;
2692 
2693 	if (uio) {
2694 		error = uiomove(setextattr_msg->pvnr_data, tomove, uio);
2695 		if (error)
2696 			goto out;
2697 	}
2698 
2699 	puffs_msg_setinfo(park_setextattr,
2700 	    PUFFSOP_VN, PUFFS_VN_SETEXTATTR, VPTOPNC(vp));
2701 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_setextattr, vp->v_data, NULL, error);
2702 
2703 	error = checkerr(pmp, error, __func__);
2704 	if (error)
2705 		goto out;
2706 
2707 	if (setextattr_msg->pvnr_resid != 0)
2708 		error = EIO;
2709 
2710  out:
2711 	PUFFS_MSG_RELEASE(setextattr);
2712 
2713 	return error;
2714 }
2715 
2716 int
2717 puffs_vnop_listextattr(void *v)
2718 {
2719 	struct vop_listextattr_args /* {
2720 		struct vnode *a_vp;
2721 		int a_attrnamespace;
2722 		struct uio *a_uio;
2723 		size_t *a_size;
2724 		kauth_cred_t a_cred;
2725 	}; */ *ap = v;
2726 	PUFFS_MSG_VARS(vn, listextattr);
2727 	struct vnode *vp = ap->a_vp;
2728 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2729 	int attrnamespace = ap->a_attrnamespace;
2730 	struct uio *uio = ap->a_uio;
2731 	size_t *sizep = ap->a_size;
2732 	size_t tomove, resid;
2733 	int error;
2734 
2735 	if (uio)
2736 		resid = uio->uio_resid;
2737 	else
2738 		resid = 0;
2739 
2740 	tomove = PUFFS_TOMOVE(resid, pmp);
2741 	if (tomove != resid) {
2742 		error = E2BIG;
2743 		goto out;
2744 	}
2745 
2746 	puffs_msgmem_alloc(sizeof(struct puffs_vnmsg_listextattr) + tomove,
2747 	    &park_listextattr, (void *)&listextattr_msg, 1);
2748 
2749 	listextattr_msg->pvnr_attrnamespace = attrnamespace;
2750 	puffs_credcvt(&listextattr_msg->pvnr_cred, ap->a_cred);
2751 	listextattr_msg->pvnr_resid = tomove;
2752 	if (sizep)
2753 		listextattr_msg->pvnr_datasize = 1;
2754 
2755 	puffs_msg_setinfo(park_listextattr,
2756 	    PUFFSOP_VN, PUFFS_VN_LISTEXTATTR, VPTOPNC(vp));
2757 	puffs_msg_setdelta(park_listextattr, tomove);
2758 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_listextattr, vp->v_data, NULL, error);
2759 
2760 	error = checkerr(pmp, error, __func__);
2761 	if (error)
2762 		goto out;
2763 
2764 	resid = listextattr_msg->pvnr_resid;
2765 	if (resid > tomove) {
2766 		puffs_senderr(pmp, PUFFS_ERR_LISTEXTATTR, E2BIG,
2767 		    "resid grew", VPTOPNC(vp));
2768 		error = EPROTO;
2769 		goto out;
2770 	}
2771 
2772 	if (sizep)
2773 		*sizep = listextattr_msg->pvnr_datasize;
2774 	if (uio)
2775 		error = uiomove(listextattr_msg->pvnr_data, tomove-resid, uio);
2776 
2777  out:
2778 	PUFFS_MSG_RELEASE(listextattr);
2779 	return error;
2780 }
2781 
2782 int
2783 puffs_vnop_deleteextattr(void *v)
2784 {
2785 	struct vop_deleteextattr_args /* {
2786 		struct vnode *a_vp;
2787 		int a_attrnamespace;
2788 		const char *a_name;
2789 		kauth_cred_t a_cred;
2790 	}; */ *ap = v;
2791 	PUFFS_MSG_VARS(vn, deleteextattr);
2792 	struct vnode *vp = ap->a_vp;
2793 	struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
2794 	int attrnamespace = ap->a_attrnamespace;
2795 	const char *name = ap->a_name;
2796 	int error;
2797 
2798 	PUFFS_MSG_ALLOC(vn, deleteextattr);
2799 	deleteextattr_msg->pvnr_attrnamespace = attrnamespace;
2800 	strlcpy(deleteextattr_msg->pvnr_attrname, name,
2801 	    sizeof(deleteextattr_msg->pvnr_attrname));
2802 	puffs_credcvt(&deleteextattr_msg->pvnr_cred, ap->a_cred);
2803 
2804 	puffs_msg_setinfo(park_deleteextattr,
2805 	    PUFFSOP_VN, PUFFS_VN_DELETEEXTATTR, VPTOPNC(vp));
2806 	PUFFS_MSG_ENQUEUEWAIT2(pmp, park_deleteextattr,
2807 	    vp->v_data, NULL, error);
2808 
2809 	error = checkerr(pmp, error, __func__);
2810 
2811 	PUFFS_MSG_RELEASE(deleteextattr);
2812 	return error;
2813 }
2814 
2815 /*
2816  * spec & fifo.  These call the miscfs spec and fifo vectors, but issue
2817  * FAF update information for the puffs node first.
2818  */
2819 int
2820 puffs_vnop_spec_read(void *v)
2821 {
2822 	struct vop_read_args /* {
2823 		const struct vnodeop_desc *a_desc;
2824 		struct vnode *a_vp;
2825 		struct uio *a_uio;
2826 		int a_ioflag;
2827 		kauth_cred_t a_cred;
2828 	} */ *ap = v;
2829 
2830 	puffs_updatenode(VPTOPP(ap->a_vp), PUFFS_UPDATEATIME, 0);
2831 	return VOCALL(spec_vnodeop_p, VOFFSET(vop_read), v);
2832 }
2833 
2834 int
2835 puffs_vnop_spec_write(void *v)
2836 {
2837 	struct vop_write_args /* {
2838 		const struct vnodeop_desc *a_desc;
2839 		struct vnode *a_vp;
2840 		struct uio *a_uio;
2841 		int a_ioflag;
2842 		kauth_cred_t a_cred;
2843 	} */ *ap = v;
2844 
2845 	puffs_updatenode(VPTOPP(ap->a_vp), PUFFS_UPDATEMTIME, 0);
2846 	return VOCALL(spec_vnodeop_p, VOFFSET(vop_write), v);
2847 }
2848 
2849 int
2850 puffs_vnop_fifo_read(void *v)
2851 {
2852 	struct vop_read_args /* {
2853 		const struct vnodeop_desc *a_desc;
2854 		struct vnode *a_vp;
2855 		struct uio *a_uio;
2856 		int a_ioflag;
2857 		kauth_cred_t a_cred;
2858 	} */ *ap = v;
2859 
2860 	puffs_updatenode(VPTOPP(ap->a_vp), PUFFS_UPDATEATIME, 0);
2861 	return VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), v);
2862 }
2863 
2864 int
2865 puffs_vnop_fifo_write(void *v)
2866 {
2867 	struct vop_write_args /* {
2868 		const struct vnodeop_desc *a_desc;
2869 		struct vnode *a_vp;
2870 		struct uio *a_uio;
2871 		int a_ioflag;
2872 		kauth_cred_t a_cred;
2873 	} */ *ap = v;
2874 
2875 	puffs_updatenode(VPTOPP(ap->a_vp), PUFFS_UPDATEMTIME, 0);
2876 	return VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), v);
2877 }
2878