d6113f2d0d32e7d1edc468218cb4f66efc91e1e6
[dragonfly.git] / sys / vfs / puffs / puffs_vnops.c
1 /*      $NetBSD: puffs_vnops.c,v 1.154 2011/07/04 08:07:30 manu 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/param.h>
33 #include <sys/buf.h>
34 #include <sys/lockf.h>
35 #include <sys/malloc.h>
36 #include <sys/mount.h>
37 #include <sys/namei.h>
38 #include <sys/vnode.h>
39 #include <sys/proc.h>
40
41 #include <vfs/fifofs/fifo.h>
42
43 #include <vfs/puffs/puffs_msgif.h>
44 #include <vfs/puffs/puffs_sys.h>
45
46 int (**puffs_vnodeop_p)(void *);
47
48 #define ERROUT(err)                                                     \
49 do {                                                                    \
50         error = err;                                                    \
51         goto out;                                                       \
52 } while (/*CONSTCOND*/0)
53
54 static int callremove(struct puffs_mount *, puffs_cookie_t, puffs_cookie_t,
55                             struct namecache *, struct ucred *);
56 static int callrmdir(struct puffs_mount *, puffs_cookie_t, puffs_cookie_t,
57                            struct namecache *, struct ucred *);
58 static void callinactive(struct puffs_mount *, puffs_cookie_t, int);
59 static void callreclaim(struct puffs_mount *, puffs_cookie_t);
60 static int  flushvncache(struct vnode *, int);
61
62
63 #define PUFFS_ABORT_LOOKUP      1
64 #define PUFFS_ABORT_CREATE      2
65 #define PUFFS_ABORT_MKNOD       3
66 #define PUFFS_ABORT_MKDIR       4
67 #define PUFFS_ABORT_SYMLINK     5
68
69 /*
70  * Press the pani^Wabort button!  Kernel resource allocation failed.
71  */
72 static void
73 puffs_abortbutton(struct puffs_mount *pmp, int what,
74         puffs_cookie_t dck, puffs_cookie_t ck,
75         struct namecache *ncp, struct ucred *cred)
76 {
77
78         switch (what) {
79         case PUFFS_ABORT_CREATE:
80         case PUFFS_ABORT_MKNOD:
81         case PUFFS_ABORT_SYMLINK:
82                 callremove(pmp, dck, ck, ncp, cred);
83                 break;
84         case PUFFS_ABORT_MKDIR:
85                 callrmdir(pmp, dck, ck, ncp, cred);
86                 break;
87         }
88
89         callinactive(pmp, ck, 0);
90         callreclaim(pmp, ck);
91 }
92
93 /*
94  * Begin vnode operations.
95  *
96  * A word from the keymaster about locks: generally we don't want
97  * to use the vnode locks at all: it creates an ugly dependency between
98  * the userlandia file server and the kernel.  But we'll play along with
99  * the kernel vnode locks for now.  However, even currently we attempt
100  * to release locks as early as possible.  This is possible for some
101  * operations which a) don't need a locked vnode after the userspace op
102  * and b) return with the vnode unlocked.  Theoretically we could
103  * unlock-do op-lock for others and order the graph in userspace, but I
104  * don't want to think of the consequences for the time being.
105  */
106 static int
107 puffs_vnop_lookup(struct vop_nresolve_args *ap)
108 {
109         PUFFS_MSG_VARS(vn, lookup);
110         struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_dvp->v_mount);
111         struct nchandle *nch = ap->a_nch;
112         struct namecache *ncp = nch->ncp;
113         struct ucred *cred = ap->a_cred;
114         struct vnode *vp = NULL, *dvp = ap->a_dvp;
115         struct puffs_node *dpn;
116         int error;
117
118         DPRINTF(("puffs_lookup: \"%s\", parent vnode %p\n",
119             ncp->nc_name, dvp));
120
121         PUFFS_MSG_ALLOC(vn, lookup);
122         puffs_makecn(&lookup_msg->pvnr_cn, &lookup_msg->pvnr_cn_cred,
123             ncp, cred);
124
125         puffs_msg_setinfo(park_lookup, PUFFSOP_VN,
126             PUFFS_VN_LOOKUP, VPTOPNC(dvp));
127         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_lookup, dvp->v_data, NULL, error);
128         DPRINTF(("puffs_lookup: return of the userspace, part %d\n", error));
129         if (error) {
130                 error = checkerr(pmp, error, __func__);
131                 if (error == ENOENT)
132                         cache_setvp(nch, NULL);
133                 goto out;
134         }
135
136         /*
137          * Check that we don't get our parent node back, that would cause
138          * a pretty obvious deadlock.
139          */
140         dpn = VPTOPP(dvp);
141         if (lookup_msg->pvnr_newnode == dpn->pn_cookie) {
142                 puffs_senderr(pmp, PUFFS_ERR_LOOKUP, EINVAL,
143                     "lookup produced parent cookie", lookup_msg->pvnr_newnode);
144                 error = EPROTO;
145                 goto out;
146         }
147
148         error = puffs_cookie2vnode(pmp, lookup_msg->pvnr_newnode, 1, &vp);
149         if (error == PUFFS_NOSUCHCOOKIE) {
150                 error = puffs_getvnode(dvp->v_mount,
151                     lookup_msg->pvnr_newnode, lookup_msg->pvnr_vtype,
152                     lookup_msg->pvnr_size, &vp);
153                 if (error) {
154                         puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp),
155                             lookup_msg->pvnr_newnode, ncp, cred);
156                         goto out;
157                 }
158         } else if (error) {
159                 puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp),
160                     lookup_msg->pvnr_newnode, ncp, cred);
161                 goto out;
162         }
163
164  out:
165         if (!error && vp != NULL) {
166                 vn_unlock(vp);
167                 cache_setvp(nch, vp);
168                 vrele(vp);
169         }
170         DPRINTF(("puffs_lookup: returning %d\n", error));
171         PUFFS_MSG_RELEASE(lookup);
172         return error;
173 }
174
175 static int
176 puffs_vnop_lookupdotdot(struct vop_nlookupdotdot_args *ap)
177 {
178         PUFFS_MSG_VARS(vn, lookupdotdot);
179         struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_dvp->v_mount);
180         struct ucred *cred = ap->a_cred;
181         struct vnode *vp, *dvp = ap->a_dvp;
182         struct puffs_node *dpn;
183         int error;
184
185         *ap->a_vpp = NULL;
186
187         DPRINTF(("puffs_lookupdotdot: vnode %p\n", dvp));
188
189         PUFFS_MSG_ALLOC(vn, lookupdotdot);
190         puffs_credcvt(&lookupdotdot_msg->pvnr_cred, cred);
191
192         puffs_msg_setinfo(park_lookupdotdot, PUFFSOP_VN,
193             PUFFS_VN_LOOKUPDOTDOT, VPTOPNC(dvp));
194         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_lookupdotdot, dvp->v_data, NULL,
195             error);
196         DPRINTF(("puffs_lookupdotdot: return of the userspace, part %d\n",
197             error));
198         if (error) {
199                 error = checkerr(pmp, error, __func__);
200                 goto out;
201         }
202
203         /*
204          * Check that we don't get our node back, that would cause
205          * a pretty obvious deadlock.
206          */
207         dpn = VPTOPP(dvp);
208         if (lookupdotdot_msg->pvnr_newnode == dpn->pn_cookie) {
209                 puffs_senderr(pmp, PUFFS_ERR_LOOKUP, EINVAL,
210                     "lookupdotdot produced the same cookie",
211                     lookupdotdot_msg->pvnr_newnode);
212                 error = EPROTO;
213                 goto out;
214         }
215
216         error = puffs_cookie2vnode(pmp, lookupdotdot_msg->pvnr_newnode,
217             1, &vp);
218         if (error == PUFFS_NOSUCHCOOKIE) {
219                 error = puffs_getvnode(dvp->v_mount,
220                     lookupdotdot_msg->pvnr_newnode, VDIR, 0, &vp);
221                 if (error) {
222                         puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp),
223                             lookupdotdot_msg->pvnr_newnode, NULL, cred);
224                         goto out;
225                 }
226         } else if (error) {
227                 puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp),
228                     lookupdotdot_msg->pvnr_newnode, NULL, cred);
229                 goto out;
230         }
231
232         *ap->a_vpp = vp;
233         vn_unlock(vp);
234
235  out:
236         DPRINTF(("puffs_lookupdotdot: returning %d %p\n", error, *ap->a_vpp));
237         PUFFS_MSG_RELEASE(lookupdotdot);
238         return error;
239 }
240
241 static int
242 puffs_vnop_create(struct vop_ncreate_args *ap)
243 {
244         PUFFS_MSG_VARS(vn, create);
245         struct vnode *dvp = ap->a_dvp;
246         struct vattr *vap = ap->a_vap;
247         struct puffs_node *dpn = VPTOPP(dvp);
248         struct nchandle *nch = ap->a_nch;
249         struct namecache *ncp = nch->ncp;
250         struct ucred *cred = ap->a_cred;
251         struct mount *mp = dvp->v_mount;
252         struct puffs_mount *pmp = MPTOPUFFSMP(mp);
253         int error;
254
255         if (!EXISTSOP(pmp, CREATE))
256                 return EOPNOTSUPP;
257
258         DPRINTF(("puffs_create: dvp %p, name: %s\n",
259             dvp, ncp->nc_name));
260
261         if (vap->va_type != VREG && vap->va_type != VSOCK)
262                 return EINVAL;
263
264         if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
265                 DPRINTF(("puffs_vnop_create: EAGAIN on ncp %p %s\n",
266                     ncp, ncp->nc_name));
267                 return EAGAIN;
268         }
269
270         PUFFS_MSG_ALLOC(vn, create);
271         puffs_makecn(&create_msg->pvnr_cn, &create_msg->pvnr_cn_cred,
272             ncp, cred);
273         create_msg->pvnr_va = *ap->a_vap;
274         puffs_msg_setinfo(park_create, PUFFSOP_VN,
275             PUFFS_VN_CREATE, VPTOPNC(dvp));
276         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_create, dvp->v_data, NULL, error);
277
278         error = checkerr(pmp, error, __func__);
279         if (error)
280                 goto out;
281
282         error = puffs_newnode(mp, dvp, ap->a_vpp,
283             create_msg->pvnr_newnode, vap->va_type);
284         if (error)
285                 puffs_abortbutton(pmp, PUFFS_ABORT_CREATE, dpn->pn_cookie,
286                     create_msg->pvnr_newnode, ncp, cred);
287
288  out:
289         DPRINTF(("puffs_create: return %d\n", error));
290         vput(dvp);
291         if (!error) {
292                 cache_setunresolved(nch);
293                 cache_setvp(nch, *ap->a_vpp);
294         }
295         PUFFS_MSG_RELEASE(create);
296         return error;
297 }
298
299 static int
300 puffs_vnop_mknod(struct vop_nmknod_args *ap)
301 {
302         PUFFS_MSG_VARS(vn, mknod);
303         struct vnode *dvp = ap->a_dvp;
304         struct vattr *vap = ap->a_vap;
305         struct puffs_node *dpn = VPTOPP(dvp);
306         struct nchandle *nch = ap->a_nch;
307         struct namecache *ncp = nch->ncp;
308         struct ucred *cred = ap->a_cred;
309         struct mount *mp = dvp->v_mount;
310         struct puffs_mount *pmp = MPTOPUFFSMP(mp);
311         int error;
312
313         if (!EXISTSOP(pmp, MKNOD))
314                 return EOPNOTSUPP;
315
316         DPRINTF(("puffs_mknod: dvp %p, name: %s\n",
317             dvp, ncp->nc_name));
318
319         if (vap->va_type != VFIFO)
320                 return EINVAL;
321
322         if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
323                 DPRINTF(("puffs_vnop_mknod: EAGAIN on ncp %p %s\n",
324                     ncp, ncp->nc_name));
325                 return EAGAIN;
326         }
327
328         PUFFS_MSG_ALLOC(vn, mknod);
329         puffs_makecn(&mknod_msg->pvnr_cn, &mknod_msg->pvnr_cn_cred,
330             ncp, cred);
331         mknod_msg->pvnr_va = *ap->a_vap;
332         puffs_msg_setinfo(park_mknod, PUFFSOP_VN,
333             PUFFS_VN_MKNOD, VPTOPNC(dvp));
334
335         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mknod, dvp->v_data, NULL, error);
336
337         error = checkerr(pmp, error, __func__);
338         if (error)
339                 goto out;
340
341         error = puffs_newnode(mp, dvp, ap->a_vpp,
342             mknod_msg->pvnr_newnode, vap->va_type);
343         if (error)
344                 puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, dpn->pn_cookie,
345                     mknod_msg->pvnr_newnode, ncp, cred);
346
347  out:
348         vput(dvp);
349         if (!error) {
350                 cache_setunresolved(nch);
351                 cache_setvp(nch, *ap->a_vpp);
352         }
353         PUFFS_MSG_RELEASE(mknod);
354         return error;
355 }
356
357 static int
358 puffs_vnop_open(struct vop_open_args *ap)
359 {
360         PUFFS_MSG_VARS(vn, open);
361         struct vnode *vp = ap->a_vp;
362         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
363         int mode = ap->a_mode;
364         int error;
365
366         DPRINTF(("puffs_open: vp %p, mode 0x%x\n", vp, mode));
367
368         if (vp->v_type == VREG && mode & FWRITE && !EXISTSOP(pmp, WRITE))
369                 ERROUT(EROFS);
370
371         if (!EXISTSOP(pmp, OPEN))
372                 ERROUT(0);
373
374         PUFFS_MSG_ALLOC(vn, open);
375         open_msg->pvnr_mode = mode;
376         puffs_credcvt(&open_msg->pvnr_cred, ap->a_cred);
377         puffs_msg_setinfo(park_open, PUFFSOP_VN,
378             PUFFS_VN_OPEN, VPTOPNC(vp));
379
380         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_open, vp->v_data, NULL, error);
381         error = checkerr(pmp, error, __func__);
382
383  out:
384         DPRINTF(("puffs_open: returning %d\n", error));
385         PUFFS_MSG_RELEASE(open);
386         if (error)
387                 return error;
388         return vop_stdopen(ap);
389 }
390
391 static int
392 puffs_vnop_close(struct vop_close_args *ap)
393 {
394         PUFFS_MSG_VARS(vn, close);
395         struct vnode *vp = ap->a_vp;
396         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
397
398         if (!EXISTSOP(pmp, CLOSE))
399                 return vop_stdclose(ap);
400
401         PUFFS_MSG_ALLOC(vn, close);
402         puffs_msg_setfaf(park_close);
403         close_msg->pvnr_fflag = ap->a_fflag;
404         puffs_msg_setinfo(park_close, PUFFSOP_VN,
405             PUFFS_VN_CLOSE, VPTOPNC(vp));
406
407         puffs_msg_enqueue(pmp, park_close);
408         PUFFS_MSG_RELEASE(close);
409         return vop_stdclose(ap);
410 }
411
412 static int
413 puffs_vnop_access(struct vop_access_args *ap)
414 {
415         PUFFS_MSG_VARS(vn, access);
416         struct vnode *vp = ap->a_vp;
417         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
418         int mode = ap->a_mode;
419         int error;
420
421         if (mode & VWRITE) {
422                 switch (vp->v_type) {
423                 case VDIR:
424                 case VLNK:
425                 case VREG:
426                         if ((vp->v_mount->mnt_flag & MNT_RDONLY)
427                             || !EXISTSOP(pmp, WRITE))
428                                 return EROFS;
429                         break;
430                 default:
431                         break;
432                 }
433         }
434
435         if (!EXISTSOP(pmp, ACCESS))
436                 return 0;
437
438         PUFFS_MSG_ALLOC(vn, access);
439         access_msg->pvnr_mode = ap->a_mode;
440         puffs_credcvt(&access_msg->pvnr_cred, ap->a_cred);
441         puffs_msg_setinfo(park_access, PUFFSOP_VN,
442             PUFFS_VN_ACCESS, VPTOPNC(vp));
443
444         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_access, vp->v_data, NULL, error);
445         error = checkerr(pmp, error, __func__);
446         PUFFS_MSG_RELEASE(access);
447
448         return error;
449 }
450
451 static int
452 puffs_vnop_getattr(struct vop_getattr_args *ap)
453 {
454         PUFFS_MSG_VARS(vn, getattr);
455         struct vnode *vp = ap->a_vp;
456         struct mount *mp = vp->v_mount;
457         struct puffs_mount *pmp = MPTOPUFFSMP(mp);
458         struct vattr *vap, *rvap;
459         struct puffs_node *pn = VPTOPP(vp);
460         int error = 0;
461
462         if (vp->v_type == VBLK || vp->v_type == VCHR)
463                 return ENOTSUP;
464
465         vap = ap->a_vap;
466
467         PUFFS_MSG_ALLOC(vn, getattr);
468         vattr_null(&getattr_msg->pvnr_va);
469         puffs_credcvt(&getattr_msg->pvnr_cred, curproc->p_ucred);
470         puffs_msg_setinfo(park_getattr, PUFFSOP_VN,
471             PUFFS_VN_GETATTR, VPTOPNC(vp));
472
473         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_getattr, vp->v_data, NULL, error);
474         error = checkerr(pmp, error, __func__);
475         if (error)
476                 goto out;
477
478         rvap = &getattr_msg->pvnr_va;
479
480         (void) memcpy(vap, rvap, sizeof(struct vattr));
481         vap->va_fsid = mp->mnt_stat.f_fsid.val[0];
482
483         if (pn->pn_stat & PNODE_METACACHE_ATIME)
484                 vap->va_atime = pn->pn_mc_atime;
485         if (pn->pn_stat & PNODE_METACACHE_CTIME)
486                 vap->va_ctime = pn->pn_mc_ctime;
487         if (pn->pn_stat & PNODE_METACACHE_MTIME)
488                 vap->va_mtime = pn->pn_mc_mtime;
489         if (pn->pn_stat & PNODE_METACACHE_SIZE) {
490                 vap->va_size = pn->pn_mc_size;
491         } else {
492                 if (rvap->va_size != VNOVAL
493                     && vp->v_type != VBLK && vp->v_type != VCHR) {
494                         pn->pn_serversize = rvap->va_size;
495                         if (vp->v_type == VREG)
496                                 puffs_meta_setsize(vp, rvap->va_size, 0);
497                 }
498         }
499
500  out:
501         PUFFS_MSG_RELEASE(getattr);
502         return error;
503 }
504
505 #define SETATTR_CHSIZE  0x01
506 #define SETATTR_ASYNC   0x02
507 static int
508 dosetattr(struct vnode *vp, struct vattr *vap, struct ucred *cred, int flags)
509 {
510         PUFFS_MSG_VARS(vn, setattr);
511         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
512         struct puffs_node *pn = VPTOPP(vp);
513         int error = 0;
514
515         if ((vp->v_mount->mnt_flag & MNT_RDONLY) &&
516             (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL
517             || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL
518             || vap->va_mode != (mode_t)VNOVAL))
519                 return EROFS;
520
521         if ((vp->v_mount->mnt_flag & MNT_RDONLY)
522             && vp->v_type == VREG && vap->va_size != VNOVAL)
523                 return EROFS;
524
525         /*
526          * Flush metacache first.  If we are called with some explicit
527          * parameters, treat them as information overriding metacache
528          * information.
529          */
530         if (pn->pn_stat & PNODE_METACACHE_MASK) {
531                 if ((pn->pn_stat & PNODE_METACACHE_ATIME)
532                     && vap->va_atime.tv_sec == VNOVAL)
533                         vap->va_atime = pn->pn_mc_atime;
534                 if ((pn->pn_stat & PNODE_METACACHE_CTIME)
535                     && vap->va_ctime.tv_sec == VNOVAL)
536                         vap->va_ctime = pn->pn_mc_ctime;
537                 if ((pn->pn_stat & PNODE_METACACHE_MTIME)
538                     && vap->va_mtime.tv_sec == VNOVAL)
539                         vap->va_mtime = pn->pn_mc_mtime;
540                 if ((pn->pn_stat & PNODE_METACACHE_SIZE)
541                     && vap->va_size == VNOVAL)
542                         vap->va_size = pn->pn_mc_size;
543
544                 pn->pn_stat &= ~PNODE_METACACHE_MASK;
545         }
546
547         PUFFS_MSG_ALLOC(vn, setattr);
548         (void)memcpy(&setattr_msg->pvnr_va, vap, sizeof(struct vattr));
549         puffs_credcvt(&setattr_msg->pvnr_cred, cred);
550         puffs_msg_setinfo(park_setattr, PUFFSOP_VN,
551             PUFFS_VN_SETATTR, VPTOPNC(vp));
552         if (flags & SETATTR_ASYNC)
553                 puffs_msg_setfaf(park_setattr);
554
555         puffs_msg_enqueue(pmp, park_setattr);
556         if ((flags & SETATTR_ASYNC) == 0)
557                 error = puffs_msg_wait2(pmp, park_setattr, vp->v_data, NULL);
558         PUFFS_MSG_RELEASE(setattr);
559         if ((flags & SETATTR_ASYNC) == 0) {
560                 error = checkerr(pmp, error, __func__);
561                 if (error)
562                         return error;
563         } else {
564                 error = 0;
565         }
566
567         if (vap->va_size != VNOVAL) {
568                 pn->pn_serversize = vap->va_size;
569                 if (flags & SETATTR_CHSIZE)
570                         puffs_meta_setsize(vp, vap->va_size, 0);
571         }
572
573         return 0;
574 }
575
576 static int
577 puffs_vnop_setattr(struct vop_setattr_args *ap)
578 {
579         return dosetattr(ap->a_vp, ap->a_vap, ap->a_cred, SETATTR_CHSIZE);
580 }
581
582 static __inline int
583 doinact(struct puffs_mount *pmp, int iaflag)
584 {
585
586         if (EXISTSOP(pmp, INACTIVE))
587                 if (pmp->pmp_flags & PUFFS_KFLAG_IAONDEMAND)
588                         if (iaflag || ALLOPS(pmp))
589                                 return 1;
590                         else
591                                 return 0;
592                 else
593                         return 1;
594         else
595                 return 0;
596 }
597
598 static void
599 callinactive(struct puffs_mount *pmp, puffs_cookie_t ck, int iaflag)
600 {
601         int error;
602         PUFFS_MSG_VARS(vn, inactive);
603
604         if (doinact(pmp, iaflag)) {
605                 PUFFS_MSG_ALLOC(vn, inactive);
606                 puffs_msg_setinfo(park_inactive, PUFFSOP_VN,
607                     PUFFS_VN_INACTIVE, ck);
608
609                 PUFFS_MSG_ENQUEUEWAIT(pmp, park_inactive, error);
610                 PUFFS_MSG_RELEASE(inactive);
611         }
612 }
613
614 /* XXX: callinactive can't setback */
615 static int
616 puffs_vnop_inactive(struct vop_inactive_args *ap)
617 {
618         PUFFS_MSG_VARS(vn, inactive);
619         struct vnode *vp = ap->a_vp;
620         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
621         struct puffs_node *pnode = VPTOPP(vp);
622
623         flushvncache(vp, MNT_NOWAIT);
624
625         if (doinact(pmp, pnode->pn_stat & PNODE_DOINACT)) {
626                 /*
627                  * do not wait for reply from userspace, otherwise it may
628                  * deadlock.
629                  */
630
631                 PUFFS_MSG_ALLOC(vn, inactive);
632                 puffs_msg_setfaf(park_inactive);
633                 puffs_msg_setinfo(park_inactive, PUFFSOP_VN,
634                     PUFFS_VN_INACTIVE, VPTOPNC(vp));
635
636                 puffs_msg_enqueue(pmp, park_inactive);
637                 PUFFS_MSG_RELEASE(inactive);
638         }
639         pnode->pn_stat &= ~PNODE_DOINACT;
640
641         /*
642          * file server thinks it's gone?  then don't be afraid care,
643          * node's life was already all it would ever be
644          */
645         if (pnode->pn_stat & PNODE_NOREFS) {
646                 pnode->pn_stat |= PNODE_DYING;
647                 vrecycle(vp);
648         }
649
650         return 0;
651 }
652
653 static void
654 callreclaim(struct puffs_mount *pmp, puffs_cookie_t ck)
655 {
656         PUFFS_MSG_VARS(vn, reclaim);
657
658         if (!EXISTSOP(pmp, RECLAIM))
659                 return;
660
661         PUFFS_MSG_ALLOC(vn, reclaim);
662         puffs_msg_setfaf(park_reclaim);
663         puffs_msg_setinfo(park_reclaim, PUFFSOP_VN, PUFFS_VN_RECLAIM, ck);
664
665         puffs_msg_enqueue(pmp, park_reclaim);
666         PUFFS_MSG_RELEASE(reclaim);
667 }
668
669 /*
670  * always FAF, we don't really care if the server wants to fail to
671  * reclaim the node or not
672  */
673 static int
674 puffs_vnop_reclaim(struct vop_reclaim_args *ap)
675 {
676         struct vnode *vp = ap->a_vp;
677         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
678         struct puffs_node *pnode = VPTOPP(vp);
679         boolean_t notifyserver = TRUE;
680
681         vinvalbuf(vp, V_SAVE, 0, 0);
682
683         /*
684          * first things first: check if someone is trying to reclaim the
685          * root vnode.  do not allow that to travel to userspace.
686          * Note that we don't need to take the lock similarly to
687          * puffs_root(), since there is only one of us.
688          */
689         if (vp->v_flag & VROOT) {
690                 lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
691                 KKASSERT(pmp->pmp_root != NULL);
692                 pmp->pmp_root = NULL;
693                 lockmgr(&pmp->pmp_lock, LK_RELEASE);
694                 notifyserver = FALSE;
695         }
696
697         /*
698          * purge info from kernel before issueing FAF, since we
699          * don't really know when we'll get around to it after
700          * that and someone might race us into node creation
701          */
702         lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
703         LIST_REMOVE(pnode, pn_hashent);
704         lockmgr(&pmp->pmp_lock, LK_RELEASE);
705
706         if (notifyserver)
707                 callreclaim(MPTOPUFFSMP(vp->v_mount), VPTOPNC(vp));
708
709         puffs_putvnode(vp);
710         vp->v_data = NULL;
711
712         return 0;
713 }
714
715 #define CSIZE sizeof(**ap->a_cookies)
716 static int
717 puffs_vnop_readdir(struct vop_readdir_args *ap)
718 {
719         PUFFS_MSG_VARS(vn, readdir);
720         struct vnode *vp = ap->a_vp;
721         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
722         size_t argsize, tomove, cookiemem, cookiesmax;
723         struct uio *uio = ap->a_uio;
724         size_t howmuch, resid;
725         int error;
726
727         if (!EXISTSOP(pmp, READDIR))
728                 return EOPNOTSUPP;
729
730         /*
731          * ok, so we need: resid + cookiemem = maxreq
732          * => resid + cookiesize * (resid/minsize) = maxreq
733          * => resid + cookiesize/minsize * resid = maxreq
734          * => (cookiesize/minsize + 1) * resid = maxreq
735          * => resid = maxreq / (cookiesize/minsize + 1)
736          *
737          * Since cookiesize <= minsize and we're not very big on floats,
738          * we approximate that to be 1.  Therefore:
739          *
740          * resid = maxreq / 2;
741          *
742          * Well, at least we didn't have to use differential equations
743          * or the Gram-Schmidt process.
744          *
745          * (yes, I'm very afraid of this)
746          */
747         KKASSERT(CSIZE <= _DIRENT_RECLEN(1));
748
749         if (ap->a_cookies) {
750                 KKASSERT(ap->a_ncookies != NULL);
751                 if (pmp->pmp_args.pa_fhsize == 0)
752                         return EOPNOTSUPP;
753                 resid = PUFFS_TOMOVE(uio->uio_resid, pmp) / 2;
754                 cookiesmax = resid/_DIRENT_RECLEN(1);
755                 cookiemem = ALIGN(cookiesmax*CSIZE); /* play safe */
756         } else {
757                 resid = PUFFS_TOMOVE(uio->uio_resid, pmp);
758                 cookiesmax = 0;
759                 cookiemem = 0;
760         }
761
762         argsize = sizeof(struct puffs_vnmsg_readdir);
763         tomove = resid + cookiemem;
764         puffs_msgmem_alloc(argsize + tomove, &park_readdir,
765             (void *)&readdir_msg, 1);
766
767         puffs_credcvt(&readdir_msg->pvnr_cred, ap->a_cred);
768         readdir_msg->pvnr_offset = uio->uio_offset;
769         readdir_msg->pvnr_resid = resid;
770         readdir_msg->pvnr_ncookies = cookiesmax;
771         readdir_msg->pvnr_eofflag = 0;
772         readdir_msg->pvnr_dentoff = cookiemem;
773         puffs_msg_setinfo(park_readdir, PUFFSOP_VN,
774             PUFFS_VN_READDIR, VPTOPNC(vp));
775         puffs_msg_setdelta(park_readdir, tomove);
776
777         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readdir, vp->v_data, NULL, error);
778         error = checkerr(pmp, error, __func__);
779         if (error)
780                 goto out;
781
782         /* userspace is cheating? */
783         if (readdir_msg->pvnr_resid > resid) {
784                 puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG,
785                     "resid grew", VPTOPNC(vp));
786                 ERROUT(EPROTO);
787         }
788         if (readdir_msg->pvnr_ncookies > cookiesmax) {
789                 puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG,
790                     "too many cookies", VPTOPNC(vp));
791                 ERROUT(EPROTO);
792         }
793
794         /* check eof */
795         if (readdir_msg->pvnr_eofflag)
796                 *ap->a_eofflag = 1;
797
798         /* bouncy-wouncy with the directory data */
799         howmuch = resid - readdir_msg->pvnr_resid;
800
801         /* force eof if no data was returned (getcwd() needs this) */
802         if (howmuch == 0) {
803                 *ap->a_eofflag = 1;
804                 goto out;
805         }
806
807         error = uiomove(readdir_msg->pvnr_data + cookiemem, howmuch, uio);
808         if (error)
809                 goto out;
810
811         /* provide cookies to caller if so desired */
812         if (ap->a_cookies) {
813                 *ap->a_cookies = kmalloc(readdir_msg->pvnr_ncookies*CSIZE,
814                     M_TEMP, M_WAITOK);
815                 *ap->a_ncookies = readdir_msg->pvnr_ncookies;
816                 memcpy(*ap->a_cookies, readdir_msg->pvnr_data,
817                     *ap->a_ncookies*CSIZE);
818         }
819
820         /* next readdir starts here */
821         uio->uio_offset = readdir_msg->pvnr_offset;
822
823  out:
824         puffs_msgmem_release(park_readdir);
825         return error;
826 }
827 #undef CSIZE
828
829 static int
830 flushvncache(struct vnode *vp, int waitfor)
831 {
832         struct puffs_node *pn = VPTOPP(vp);
833         struct vattr va;
834         int error = 0;
835
836         /* flush out information from our metacache, see vop_setattr */
837         if (pn->pn_stat & PNODE_METACACHE_MASK
838             && (pn->pn_stat & PNODE_DYING) == 0) {
839                 vattr_null(&va);
840                 error = dosetattr(vp, &va, FSCRED, SETATTR_CHSIZE |
841                     (waitfor == MNT_NOWAIT ? 0 : SETATTR_ASYNC));
842                 if (error)
843                         return error;
844         }
845
846         /*
847          * flush pages to avoid being overly dirty
848          */
849         vfsync(vp, waitfor, 0, NULL, NULL);
850
851         return error;
852 }
853
854 static int
855 puffs_vnop_fsync(struct vop_fsync_args *ap)
856 {
857         PUFFS_MSG_VARS(vn, fsync);
858         struct vnode *vp = ap->a_vp;
859         int waitfor = ap->a_waitfor;
860         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
861         struct puffs_node *pn = VPTOPP(vp);
862         int error, dofaf;
863
864         error = flushvncache(vp, waitfor);
865         if (error)
866                 return error;
867
868         /*
869          * HELLO!  We exit already here if the user server does not
870          * support fsync OR if we should call fsync for a node which
871          * has references neither in the kernel or the fs server.
872          * Otherwise we continue to issue fsync() forward.
873          */
874         if (!EXISTSOP(pmp, FSYNC) || (pn->pn_stat & PNODE_DYING))
875                 return 0;
876
877         dofaf = (waitfor & MNT_WAIT) == 0 || (waitfor & MNT_LAZY) != 0;
878
879         PUFFS_MSG_ALLOC(vn, fsync);
880         if (dofaf)
881                 puffs_msg_setfaf(park_fsync);
882
883         fsync_msg->pvnr_flags = ap->a_flags;
884         puffs_msg_setinfo(park_fsync, PUFFSOP_VN,
885             PUFFS_VN_FSYNC, VPTOPNC(vp));
886
887         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_fsync, vp->v_data, NULL, error);
888         PUFFS_MSG_RELEASE(fsync);
889
890         error = checkerr(pmp, error, __func__);
891
892         return error;
893 }
894
895 static int
896 callremove(struct puffs_mount *pmp, puffs_cookie_t dck, puffs_cookie_t ck,
897         struct namecache *ncp, struct ucred *cred)
898 {
899         PUFFS_MSG_VARS(vn, remove);
900         int error;
901
902         PUFFS_MSG_ALLOC(vn, remove);
903         remove_msg->pvnr_cookie_targ = ck;
904         puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred,
905             ncp, cred);
906         puffs_msg_setinfo(park_remove, PUFFSOP_VN, PUFFS_VN_REMOVE, dck);
907
908         PUFFS_MSG_ENQUEUEWAIT(pmp, park_remove, error);
909         PUFFS_MSG_RELEASE(remove);
910
911         return checkerr(pmp, error, __func__);
912 }
913
914 /*
915  * XXX: can't use callremove now because can't catch setbacks with
916  * it due to lack of a pnode argument.
917  */
918 static int
919 puffs_vnop_remove(struct vop_nremove_args *ap)
920 {
921         PUFFS_MSG_VARS(vn, remove);
922         struct vnode *dvp = ap->a_dvp;
923         struct vnode *vp;
924         struct puffs_node *dpn = VPTOPP(dvp);
925         struct puffs_node *pn;
926         struct nchandle *nch = ap->a_nch;
927         struct namecache *ncp = nch->ncp;
928         struct ucred *cred = ap->a_cred;
929         struct mount *mp = dvp->v_mount;
930         struct puffs_mount *pmp = MPTOPUFFSMP(mp);
931         int error;
932
933         if (!EXISTSOP(pmp, REMOVE))
934                 return EOPNOTSUPP;
935
936         error = vget(dvp, LK_EXCLUSIVE);
937         if (error != 0) {
938                 DPRINTF(("puffs_vnop_remove: EAGAIN on parent vnode %p %s\n",
939                     dvp, ncp->nc_name));
940                 return EAGAIN;
941         }
942
943         error = cache_vget(nch, cred, LK_EXCLUSIVE, &vp);
944         if (error != 0) {
945                 DPRINTF(("puffs_vnop_remove: cache_vget error: %p %s\n",
946                     dvp, ncp->nc_name));
947                 return EAGAIN;
948         }
949         if (vp->v_type == VDIR) {
950                 error = EISDIR;
951                 goto out;
952         }
953
954         pn = VPTOPP(vp);
955         PUFFS_MSG_ALLOC(vn, remove);
956         remove_msg->pvnr_cookie_targ = VPTOPNC(vp);
957         puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred,
958             ncp, cred);
959         puffs_msg_setinfo(park_remove, PUFFSOP_VN,
960             PUFFS_VN_REMOVE, VPTOPNC(dvp));
961
962         puffs_msg_enqueue(pmp, park_remove);
963         error = puffs_msg_wait2(pmp, park_remove, dpn, pn);
964
965         PUFFS_MSG_RELEASE(remove);
966
967         error = checkerr(pmp, error, __func__);
968
969  out:
970         vput(dvp);
971         vn_unlock(vp);
972         if (!error) {
973                 cache_setunresolved(nch);
974                 cache_setvp(nch, NULL);
975         }
976         vrele(vp);
977         return error;
978 }
979
980 static int
981 puffs_vnop_mkdir(struct vop_nmkdir_args *ap)
982 {
983         PUFFS_MSG_VARS(vn, mkdir);
984         struct vnode *dvp = ap->a_dvp;
985         struct puffs_node *dpn = VPTOPP(dvp);
986         struct nchandle *nch = ap->a_nch;
987         struct namecache *ncp = nch->ncp;
988         struct ucred *cred = ap->a_cred;
989         struct mount *mp = dvp->v_mount;
990         struct puffs_mount *pmp = MPTOPUFFSMP(mp);
991         int error;
992
993         if (!EXISTSOP(pmp, MKDIR))
994                 return EOPNOTSUPP;
995
996         if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
997                 DPRINTF(("puffs_vnop_mkdir: EAGAIN on ncp %p %s\n",
998                     ncp, ncp->nc_name));
999                 return EAGAIN;
1000         }
1001
1002         PUFFS_MSG_ALLOC(vn, mkdir);
1003         puffs_makecn(&mkdir_msg->pvnr_cn, &mkdir_msg->pvnr_cn_cred,
1004             ncp, cred);
1005         mkdir_msg->pvnr_va = *ap->a_vap;
1006         puffs_msg_setinfo(park_mkdir, PUFFSOP_VN,
1007             PUFFS_VN_MKDIR, VPTOPNC(dvp));
1008
1009         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mkdir, dvp->v_data, NULL, error);
1010
1011         error = checkerr(pmp, error, __func__);
1012         if (error)
1013                 goto out;
1014
1015         error = puffs_newnode(mp, dvp, ap->a_vpp,
1016             mkdir_msg->pvnr_newnode, VDIR);
1017         if (error)
1018                 puffs_abortbutton(pmp, PUFFS_ABORT_MKDIR, dpn->pn_cookie,
1019                     mkdir_msg->pvnr_newnode, ncp, cred);
1020
1021  out:
1022         vput(dvp);
1023         if (!error) {
1024                 cache_setunresolved(nch);
1025                 cache_setvp(nch, *ap->a_vpp);
1026         }
1027         PUFFS_MSG_RELEASE(mkdir);
1028         return error;
1029 }
1030
1031 static int
1032 callrmdir(struct puffs_mount *pmp, puffs_cookie_t dck, puffs_cookie_t ck,
1033         struct namecache *ncp, struct ucred *cred)
1034 {
1035         PUFFS_MSG_VARS(vn, rmdir);
1036         int error;
1037
1038         PUFFS_MSG_ALLOC(vn, rmdir);
1039         rmdir_msg->pvnr_cookie_targ = ck;
1040         puffs_makecn(&rmdir_msg->pvnr_cn, &rmdir_msg->pvnr_cn_cred,
1041             ncp, cred);
1042         puffs_msg_setinfo(park_rmdir, PUFFSOP_VN, PUFFS_VN_RMDIR, dck);
1043
1044         PUFFS_MSG_ENQUEUEWAIT(pmp, park_rmdir, error);
1045         PUFFS_MSG_RELEASE(rmdir);
1046
1047         return checkerr(pmp, error, __func__);
1048 }
1049
1050 static int
1051 puffs_vnop_rmdir(struct vop_nrmdir_args *ap)
1052 {
1053         PUFFS_MSG_VARS(vn, rmdir);
1054         struct vnode *dvp = ap->a_dvp;
1055         struct vnode *vp;
1056         struct puffs_node *dpn = VPTOPP(dvp);
1057         struct puffs_node *pn;
1058         struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
1059         struct nchandle *nch = ap->a_nch;
1060         struct namecache *ncp = nch->ncp;
1061         struct ucred *cred = ap->a_cred;
1062         int error;
1063
1064         if (!EXISTSOP(pmp, RMDIR))
1065                 return EOPNOTSUPP;
1066
1067         error = vget(dvp, LK_EXCLUSIVE);
1068         if (error != 0) {
1069                 DPRINTF(("puffs_vnop_rmdir: EAGAIN on parent vnode %p %s\n",
1070                     dvp, ncp->nc_name));
1071                 return EAGAIN;
1072         }
1073         error = cache_vget(nch, cred, LK_EXCLUSIVE, &vp);
1074         if (error != 0) {
1075                 DPRINTF(("puffs_vnop_rmdir: cache_vget error: %p %s\n",
1076                     dvp, ncp->nc_name));
1077                 return EAGAIN;
1078         }
1079         if (vp->v_type != VDIR) {
1080                 error = ENOTDIR;
1081                 goto out;
1082         }
1083
1084         pn = VPTOPP(vp);
1085         PUFFS_MSG_ALLOC(vn, rmdir);
1086         rmdir_msg->pvnr_cookie_targ = VPTOPNC(vp);
1087         puffs_makecn(&rmdir_msg->pvnr_cn, &rmdir_msg->pvnr_cn_cred,
1088             ncp, cred);
1089         puffs_msg_setinfo(park_rmdir, PUFFSOP_VN,
1090             PUFFS_VN_RMDIR, VPTOPNC(dvp));
1091
1092         puffs_msg_enqueue(pmp, park_rmdir);
1093         error = puffs_msg_wait2(pmp, park_rmdir, dpn, pn);
1094
1095         PUFFS_MSG_RELEASE(rmdir);
1096
1097         error = checkerr(pmp, error, __func__);
1098
1099  out:
1100         vput(dvp);
1101         vn_unlock(vp);
1102         if (!error) {
1103                 cache_setunresolved(nch);
1104                 cache_setvp(nch, NULL);
1105         }
1106         vrele(vp);
1107         return error;
1108 }
1109
1110 static int
1111 puffs_vnop_link(struct vop_nlink_args *ap)
1112 {
1113         PUFFS_MSG_VARS(vn, link);
1114         struct vnode *dvp = ap->a_dvp;
1115         struct vnode *vp = ap->a_vp;
1116         struct puffs_node *dpn = VPTOPP(dvp);
1117         struct puffs_node *pn = VPTOPP(vp);
1118         struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
1119         struct nchandle *nch = ap->a_nch;
1120         struct namecache *ncp = nch->ncp;
1121         struct ucred *cred = ap->a_cred;
1122         int error;
1123
1124         if (!EXISTSOP(pmp, LINK))
1125                 return EOPNOTSUPP;
1126
1127         if (vp->v_mount != dvp->v_mount)
1128                 return EXDEV;
1129
1130         if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
1131                 DPRINTF(("puffs_vnop_link: EAGAIN on ncp %p %s\n",
1132                     ncp, ncp->nc_name));
1133                 return EAGAIN;
1134         }
1135
1136         PUFFS_MSG_ALLOC(vn, link);
1137         link_msg->pvnr_cookie_targ = VPTOPNC(vp);
1138         puffs_makecn(&link_msg->pvnr_cn, &link_msg->pvnr_cn_cred,
1139             ncp, cred);
1140         puffs_msg_setinfo(park_link, PUFFSOP_VN,
1141             PUFFS_VN_LINK, VPTOPNC(dvp));
1142
1143         puffs_msg_enqueue(pmp, park_link);
1144         error = puffs_msg_wait2(pmp, park_link, dpn, pn);
1145
1146         PUFFS_MSG_RELEASE(link);
1147
1148         error = checkerr(pmp, error, __func__);
1149
1150         /*
1151          * XXX: stay in touch with the cache.  I don't like this, but
1152          * don't have a better solution either.  See also puffs_rename().
1153          */
1154         if (error == 0) {
1155                 puffs_updatenode(pn, PUFFS_UPDATECTIME);
1156         }
1157
1158         vput(dvp);
1159         if (error == 0) {
1160                 cache_setunresolved(nch);
1161                 cache_setvp(nch, vp);
1162         }
1163         return error;
1164 }
1165
1166 static int
1167 puffs_vnop_symlink(struct vop_nsymlink_args *ap)
1168 {
1169         PUFFS_MSG_VARS(vn, symlink);
1170         struct vnode *dvp = ap->a_dvp;
1171         struct puffs_node *dpn = VPTOPP(dvp);
1172         struct mount *mp = dvp->v_mount;
1173         struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
1174         struct nchandle *nch = ap->a_nch;
1175         struct namecache *ncp = nch->ncp;
1176         struct ucred *cred = ap->a_cred;
1177         int error;
1178
1179         if (!EXISTSOP(pmp, SYMLINK))
1180                 return EOPNOTSUPP;
1181
1182         if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
1183                 DPRINTF(("puffs_vnop_symlink: EAGAIN on ncp %p %s\n",
1184                     ncp, ncp->nc_name));
1185                 return EAGAIN;
1186         }
1187
1188         *ap->a_vpp = NULL;
1189
1190         PUFFS_MSG_ALLOC(vn, symlink);
1191         puffs_makecn(&symlink_msg->pvnr_cn, &symlink_msg->pvnr_cn_cred,
1192                 ncp, cred);
1193         symlink_msg->pvnr_va = *ap->a_vap;
1194         (void)strlcpy(symlink_msg->pvnr_link, ap->a_target,
1195             sizeof(symlink_msg->pvnr_link));
1196         puffs_msg_setinfo(park_symlink, PUFFSOP_VN,
1197             PUFFS_VN_SYMLINK, VPTOPNC(dvp));
1198
1199         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_symlink, dvp->v_data, NULL, error);
1200
1201         error = checkerr(pmp, error, __func__);
1202         if (error)
1203                 goto out;
1204
1205         error = puffs_newnode(mp, dvp, ap->a_vpp,
1206             symlink_msg->pvnr_newnode, VLNK);
1207         if (error)
1208                 puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, dpn->pn_cookie,
1209                     symlink_msg->pvnr_newnode, ncp, cred);
1210
1211  out:
1212         vput(dvp);
1213         PUFFS_MSG_RELEASE(symlink);
1214         if (!error) {
1215                 cache_setunresolved(nch);
1216                 cache_setvp(nch, *ap->a_vpp);
1217         }
1218         return error;
1219 }
1220
1221 static int
1222 puffs_vnop_readlink(struct vop_readlink_args *ap)
1223 {
1224         PUFFS_MSG_VARS(vn, readlink);
1225         struct vnode *vp = ap->a_vp;
1226         struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
1227         size_t linklen;
1228         int error;
1229
1230         if (!EXISTSOP(pmp, READLINK))
1231                 return EOPNOTSUPP;
1232
1233         PUFFS_MSG_ALLOC(vn, readlink);
1234         puffs_credcvt(&readlink_msg->pvnr_cred, ap->a_cred);
1235         linklen = sizeof(readlink_msg->pvnr_link);
1236         readlink_msg->pvnr_linklen = linklen;
1237         puffs_msg_setinfo(park_readlink, PUFFSOP_VN,
1238             PUFFS_VN_READLINK, VPTOPNC(vp));
1239
1240         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readlink, vp->v_data, NULL, error);
1241         error = checkerr(pmp, error, __func__);
1242         if (error)
1243                 goto out;
1244
1245         /* bad bad user file server */
1246         if (readlink_msg->pvnr_linklen > linklen) {
1247                 puffs_senderr(pmp, PUFFS_ERR_READLINK, E2BIG,
1248                     "linklen too big", VPTOPNC(ap->a_vp));
1249                 error = EPROTO;
1250                 goto out;
1251         }
1252
1253         error = uiomove(readlink_msg->pvnr_link, readlink_msg->pvnr_linklen,
1254             ap->a_uio);
1255  out:
1256         PUFFS_MSG_RELEASE(readlink);
1257         return error;
1258 }
1259
1260 static int
1261 puffs_vnop_rename(struct vop_nrename_args *ap)
1262 {
1263         PUFFS_MSG_VARS(vn, rename);
1264         struct nchandle *fnch = ap->a_fnch;
1265         struct nchandle *tnch = ap->a_tnch;
1266         struct vnode *fdvp = ap->a_fdvp;
1267         struct vnode *fvp = fnch->ncp->nc_vp;
1268         struct vnode *tdvp = ap->a_tdvp;
1269         struct vnode *tvp = tnch->ncp->nc_vp;
1270         struct ucred *cred = ap->a_cred;
1271         struct puffs_mount *pmp = MPTOPUFFSMP(fdvp->v_mount);
1272         int error;
1273
1274         if (!EXISTSOP(pmp, RENAME))
1275                 return EOPNOTSUPP;
1276
1277         error = vget(tdvp, LK_EXCLUSIVE);
1278         if (error != 0) {
1279                 DPRINTF(("puffs_vnop_rename: EAGAIN on tdvp vnode %p %s\n",
1280                     tdvp, tnch->ncp->nc_name));
1281                 return EAGAIN;
1282         }
1283         if (tvp != NULL) {
1284                 error = vget(tvp, LK_EXCLUSIVE);
1285                 if (error != 0) {
1286                         DPRINTF(("puffs_vnop_rename: EAGAIN on tvp vnode %p %s\n",
1287                             tvp, tnch->ncp->nc_name));
1288                         vput(tdvp);
1289                         return EAGAIN;
1290                 }
1291         }
1292
1293         if ((fvp->v_mount != tdvp->v_mount) ||
1294             (tvp && (fvp->v_mount != tvp->v_mount))) {
1295                 error = EXDEV;
1296                 goto out;
1297         }
1298
1299         if (tvp) {
1300                 if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
1301                         error = ENOTDIR;
1302                         goto out;
1303                 } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
1304                         error = EISDIR;
1305                         goto out;
1306                 }
1307         }
1308
1309         PUFFS_MSG_ALLOC(vn, rename);
1310         rename_msg->pvnr_cookie_src = VPTOPNC(fvp);
1311         rename_msg->pvnr_cookie_targdir = VPTOPNC(tdvp);
1312         if (tvp)
1313                 rename_msg->pvnr_cookie_targ = VPTOPNC(tvp);
1314         else
1315                 rename_msg->pvnr_cookie_targ = NULL;
1316         puffs_makecn(&rename_msg->pvnr_cn_src, &rename_msg->pvnr_cn_src_cred,
1317             fnch->ncp, cred);
1318         puffs_makecn(&rename_msg->pvnr_cn_targ, &rename_msg->pvnr_cn_targ_cred,
1319             tnch->ncp, cred);
1320         puffs_msg_setinfo(park_rename, PUFFSOP_VN,
1321             PUFFS_VN_RENAME, VPTOPNC(fdvp));
1322
1323         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rename, fdvp->v_data, NULL, error);
1324         PUFFS_MSG_RELEASE(rename);
1325         error = checkerr(pmp, error, __func__);
1326
1327         if (error == 0)
1328                 puffs_updatenode(VPTOPP(fvp), PUFFS_UPDATECTIME);
1329
1330  out:
1331         if (tvp != NULL)
1332                 vn_unlock(tvp);
1333         if (tdvp != tvp)
1334                 vn_unlock(tdvp);
1335         if (error == 0)
1336                 cache_rename(fnch, tnch);
1337         if (tvp != NULL)
1338                 vrele(tvp);
1339         vrele(tdvp);
1340
1341         return error;
1342 }
1343
1344 static int
1345 puffs_vnop_read(struct vop_read_args *ap)
1346 {
1347         struct vnode *vp = ap->a_vp;
1348         struct uio *uio = ap->a_uio;
1349         int ioflag = ap->a_ioflag;
1350         struct ucred * cred = ap->a_cred;
1351         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1352         int error;
1353
1354         if (!EXISTSOP(pmp, READ))
1355                 return EOPNOTSUPP;
1356
1357         if (vp->v_type == VDIR)
1358                 return EISDIR;
1359         else if (vp->v_type != VREG)
1360                 return EINVAL;
1361
1362         if (PUFFS_USE_PAGECACHE(pmp))
1363                 error = puffs_bioread(vp, uio, ioflag, cred);
1364         else
1365                 error = puffs_directread(vp, uio, ioflag, cred);
1366
1367         return error;
1368 }
1369
1370 static int
1371 puffs_vnop_write(struct vop_write_args *ap)
1372 {
1373         struct vnode *vp = ap->a_vp;
1374         struct uio *uio = ap->a_uio;
1375         int ioflag = ap->a_ioflag;
1376         struct ucred * cred = ap->a_cred;
1377         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1378         int error;
1379
1380         if (!EXISTSOP(pmp, WRITE))
1381                 return EOPNOTSUPP;
1382
1383         if (vp->v_type == VDIR)
1384                 return EISDIR;
1385         else if (vp->v_type != VREG)
1386                 return EINVAL;
1387
1388         if (PUFFS_USE_PAGECACHE(pmp))
1389                 error = puffs_biowrite(vp, uio, ioflag, cred);
1390         else
1391                 error = puffs_directwrite(vp, uio, ioflag, cred);
1392
1393         return error;
1394 }
1395
1396 static int
1397 puffs_vnop_print(struct vop_print_args *ap)
1398 {
1399         PUFFS_MSG_VARS(vn, print);
1400         struct vnode *vp = ap->a_vp;
1401         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1402         struct puffs_node *pn = VPTOPP(vp);
1403         int error;
1404
1405         /* kernel portion */
1406         kprintf("tag VT_PUFFS, vnode %p, puffs node: %p,\n"
1407             "\tuserspace cookie: %p", vp, pn, pn->pn_cookie);
1408         if (vp->v_type == VFIFO)
1409                 fifo_printinfo(vp);
1410         kprintf("\n");
1411
1412         /* userspace portion */
1413         if (EXISTSOP(pmp, PRINT)) {
1414                 PUFFS_MSG_ALLOC(vn, print);
1415                 puffs_msg_setinfo(park_print, PUFFSOP_VN,
1416                     PUFFS_VN_PRINT, VPTOPNC(vp));
1417                 PUFFS_MSG_ENQUEUEWAIT2(pmp, park_print, vp->v_data,
1418                     NULL, error);
1419                 PUFFS_MSG_RELEASE(print);
1420         }
1421
1422         return 0;
1423 }
1424
1425 static int
1426 puffs_vnop_pathconf(struct vop_pathconf_args *ap)
1427 {
1428         PUFFS_MSG_VARS(vn, pathconf);
1429         struct vnode *vp = ap->a_vp;
1430         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1431         int error;
1432
1433         if (!EXISTSOP(pmp, PATHCONF))
1434                 return EOPNOTSUPP;
1435
1436         PUFFS_MSG_ALLOC(vn, pathconf);
1437         pathconf_msg->pvnr_name = ap->a_name;
1438         puffs_msg_setinfo(park_pathconf, PUFFSOP_VN,
1439             PUFFS_VN_PATHCONF, VPTOPNC(vp));
1440         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_pathconf, vp->v_data, NULL, error);
1441         error = checkerr(pmp, error, __func__);
1442         if (!error)
1443                 *ap->a_retval = pathconf_msg->pvnr_retval;
1444         PUFFS_MSG_RELEASE(pathconf);
1445
1446         return error;
1447 }
1448
1449 static int
1450 puffs_vnop_advlock(struct vop_advlock_args *ap)
1451 {
1452         PUFFS_MSG_VARS(vn, advlock);
1453         struct vnode *vp = ap->a_vp;
1454         struct puffs_node *pn = VPTOPP(vp);
1455         struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
1456         int error;
1457
1458         if (!EXISTSOP(pmp, ADVLOCK))
1459                 return lf_advlock(ap, &pn->pn_lockf, vp->v_filesize);
1460
1461         PUFFS_MSG_ALLOC(vn, advlock);
1462         (void)memcpy(&advlock_msg->pvnr_fl, ap->a_fl,
1463                      sizeof(advlock_msg->pvnr_fl));
1464         advlock_msg->pvnr_id = ap->a_id;
1465         advlock_msg->pvnr_op = ap->a_op;
1466         advlock_msg->pvnr_flags = ap->a_flags;
1467         puffs_msg_setinfo(park_advlock, PUFFSOP_VN,
1468             PUFFS_VN_ADVLOCK, VPTOPNC(vp));
1469         PUFFS_MSG_ENQUEUEWAIT2(pmp, park_advlock, vp->v_data, NULL, error);
1470         error = checkerr(pmp, error, __func__);
1471         PUFFS_MSG_RELEASE(advlock);
1472
1473         return error;
1474 }
1475
1476 static int
1477 puffs_vnop_bmap(struct vop_bmap_args *ap)
1478 {
1479         if (ap->a_doffsetp != NULL)
1480                 *ap->a_doffsetp = ap->a_loffset;
1481         if (ap->a_runp != NULL)
1482                 *ap->a_runp = 0;
1483         if (ap->a_runb != NULL)
1484                 *ap->a_runb = 0;
1485         return (0);
1486 }
1487
1488 static int
1489 puffs_vnop_mmap(struct vop_mmap_args *ap)
1490 {
1491         return EINVAL;
1492 }
1493
1494
1495 static int
1496 puffs_vnop_strategy(struct vop_strategy_args *ap)
1497 {
1498         return puffs_doio(ap->a_vp, ap->a_bio, curthread);
1499 }
1500
1501 struct vop_ops puffs_fifo_vops = {
1502         .vop_default =                  fifo_vnoperate,
1503         .vop_access =                   puffs_vnop_access,
1504         .vop_getattr =                  puffs_vnop_getattr,
1505         .vop_setattr =                  puffs_vnop_setattr,
1506         .vop_inactive =                 puffs_vnop_inactive,
1507         .vop_reclaim =                  puffs_vnop_reclaim,
1508         .vop_print =                    puffs_vnop_print,
1509 };
1510
1511 struct vop_ops puffs_vnode_vops = {
1512         .vop_default =                  vop_defaultop,
1513         .vop_nresolve =                 puffs_vnop_lookup,
1514         .vop_nlookupdotdot =            puffs_vnop_lookupdotdot,
1515         .vop_ncreate =                  puffs_vnop_create,
1516         .vop_nmkdir =                   puffs_vnop_mkdir,
1517         .vop_nrmdir =                   puffs_vnop_rmdir,
1518         .vop_nremove =                  puffs_vnop_remove,
1519         .vop_nrename =                  puffs_vnop_rename,
1520         .vop_nlink =                    puffs_vnop_link,
1521         .vop_nsymlink =                 puffs_vnop_symlink,
1522         .vop_nmknod =                   puffs_vnop_mknod,
1523         .vop_access =                   puffs_vnop_access,
1524         .vop_getattr =                  puffs_vnop_getattr,
1525         .vop_setattr =                  puffs_vnop_setattr,
1526         .vop_readdir =                  puffs_vnop_readdir,
1527         .vop_open =                     puffs_vnop_open,
1528         .vop_close =                    puffs_vnop_close,
1529         .vop_read =                     puffs_vnop_read,
1530         .vop_write =                    puffs_vnop_write,
1531         .vop_readlink =                 puffs_vnop_readlink,
1532         .vop_advlock =                  puffs_vnop_advlock,
1533         .vop_bmap =                     puffs_vnop_bmap,
1534         .vop_mmap =                     puffs_vnop_mmap,
1535         .vop_strategy =                 puffs_vnop_strategy,
1536         .vop_getpages =                 vop_stdgetpages,
1537         .vop_putpages =                 vop_stdputpages,
1538         .vop_fsync =                    puffs_vnop_fsync,
1539         .vop_inactive =                 puffs_vnop_inactive,
1540         .vop_reclaim =                  puffs_vnop_reclaim,
1541         .vop_pathconf =                 puffs_vnop_pathconf,
1542         .vop_print =                    puffs_vnop_print,
1543 };