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