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