puffs_vnops.c revision 1.3 1 /* $NetBSD: puffs_vnops.c,v 1.3 2006/10/25 18:15:39 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006 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 * 3. The name of the company nor the name of the author may be used to
19 * endorse or promote products derived from this software without specific
20 * prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.3 2006/10/25 18:15:39 pooka Exp $");
37
38 #include <sys/param.h>
39 #include <sys/vnode.h>
40 #include <sys/mount.h>
41 #include <sys/malloc.h>
42 #include <sys/namei.h>
43
44 #include <fs/puffs/puffs_msgif.h>
45 #include <fs/puffs/puffs_sys.h>
46
47 #include <miscfs/genfs/genfs.h>
48
49 int puffs_lookup(void *);
50 int puffs_create(void *);
51 int puffs_access(void *);
52 int puffs_mknod(void *);
53 int puffs_open(void *);
54 int puffs_close(void *);
55 int puffs_getattr(void *);
56 int puffs_setattr(void *);
57 int puffs_revoke(void *);
58 int puffs_reclaim(void *);
59 int puffs_readdir(void *);
60 int puffs_poll(void *);
61 int puffs_fsync(void *);
62 int puffs_seek(void *);
63 int puffs_remove(void *);
64 int puffs_mkdir(void *);
65 int puffs_rmdir(void *);
66 int puffs_link(void *);
67 int puffs_readlink(void *);
68 int puffs_symlink(void *);
69 int puffs_rename(void *);
70 int puffs_read(void *);
71 int puffs_write(void *);
72 int puffs_fcntl(void *);
73 int puffs_ioctl(void *);
74 int puffs_inactive(void *);
75 int puffs_print(void *);
76 int puffs_pathconf(void *);
77 int puffs_advlock(void *);
78
79
80 /* Need to support */
81 #define puffs_putpages puffs_generic
82 #define puffs_getpages puffs_generic
83
84 /* VOP_LEASE() not included */
85
86 int puffs_generic(void *);
87
88 #if 0
89 #define puffs_lock genfs_lock
90 #define puffs_unlock genfs_unlock
91 #define puffs_islocked genfs_islocked
92 #else
93 int puffs_lock(void *);
94 int puffs_unlock(void *);
95 int puffs_islocked(void *);
96 #endif
97
98 int (**puffs_vnodeop_p)(void *);
99 const struct vnodeopv_entry_desc puffs_vnodeop_entries[] = {
100 { &vop_default_desc, vn_default_error },
101 { &vop_lookup_desc, puffs_lookup }, /* lookup */
102 { &vop_create_desc, puffs_create }, /* create */
103 { &vop_mknod_desc, puffs_mknod }, /* mknod */
104 { &vop_open_desc, puffs_open }, /* open */
105 { &vop_close_desc, puffs_close }, /* close */
106 { &vop_access_desc, puffs_access }, /* access */
107 { &vop_getattr_desc, puffs_getattr }, /* getattr */
108 { &vop_setattr_desc, puffs_setattr }, /* setattr */
109 { &vop_read_desc, puffs_read }, /* read */
110 { &vop_write_desc, puffs_write }, /* write */
111 { &vop_fcntl_desc, puffs_fcntl }, /* fcntl */
112 { &vop_ioctl_desc, puffs_ioctl }, /* ioctl */
113 { &vop_revoke_desc, puffs_revoke }, /* revoke */
114 { &vop_fsync_desc, puffs_fsync }, /* fsync */
115 { &vop_seek_desc, puffs_seek }, /* seek */
116 { &vop_remove_desc, puffs_remove }, /* remove */
117 { &vop_link_desc, puffs_link }, /* link */
118 { &vop_rename_desc, puffs_rename }, /* rename */
119 { &vop_mkdir_desc, puffs_mkdir }, /* mkdir */
120 { &vop_rmdir_desc, puffs_rmdir }, /* rmdir */
121 { &vop_symlink_desc, puffs_symlink }, /* symlink */
122 { &vop_readdir_desc, puffs_readdir }, /* readdir */
123 { &vop_readlink_desc, puffs_readlink }, /* readlink */
124 { &vop_abortop_desc, genfs_abortop }, /* abortop */
125 { &vop_inactive_desc, puffs_inactive }, /* inactive */
126 { &vop_reclaim_desc, puffs_reclaim }, /* reclaim */
127 { &vop_lock_desc, puffs_lock }, /* lock */
128 { &vop_unlock_desc, puffs_unlock }, /* unlock */
129 { &vop_bmap_desc, genfs_eopnotsupp }, /* bmap */
130 { &vop_strategy_desc, genfs_eopnotsupp }, /* strategy */
131 { &vop_print_desc, puffs_print }, /* print */
132 { &vop_islocked_desc, puffs_islocked }, /* islocked */
133 { &vop_pathconf_desc, puffs_pathconf }, /* pathconf */
134 { &vop_advlock_desc, puffs_advlock }, /* advlock */
135 { &vop_bwrite_desc, genfs_nullop }, /* bwrite */
136 #if 0
137 { &vop_getpages_desc, puffs_getpages }, /* getpages */
138 #endif
139 { &vop_putpages_desc, genfs_null_putpages }, /* putpages */
140
141 { &vop_poll_desc, genfs_eopnotsupp }, /* poll XXX */
142 { &vop_poll_desc, genfs_eopnotsupp }, /* kqfilter XXX */
143 { &vop_mmap_desc, genfs_eopnotsupp }, /* mmap XXX */
144 { NULL, NULL }
145 };
146 const struct vnodeopv_desc puffs_vnodeop_opv_desc =
147 { &puffs_vnodeop_p, puffs_vnodeop_entries };
148
149 #define LOCKEDVP(a) (VOP_ISLOCKED(a) ? (a) : NULL)
150
151
152 int
153 puffs_lookup(void *v)
154 {
155 struct vop_lookup_args /* {
156 struct vnode * a_dvp;
157 struct vnode ** a_vpp;
158 struct componentname * a_cnp;
159 } */ *ap = v;
160 struct puffs_mount *pmp;
161 struct componentname *cnp;
162 struct vnode *vp, *dvp;
163 int wantpunlock, isdot;
164 int error;
165
166 PUFFS_VNREQ(lookup);
167
168 pmp = MPTOPUFFSMP(ap->a_dvp->v_mount);
169 cnp = ap->a_cnp;
170 dvp = ap->a_dvp;
171 *ap->a_vpp = NULL;
172
173 /* first things first: check access */
174 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_lwp);
175 if (error)
176 return error;
177
178 wantpunlock = ~cnp->cn_flags & (LOCKPARENT | ISLASTCN);
179 isdot = cnp->cn_namelen == 1 && *cnp->cn_nameptr == '.';
180
181 DPRINTF(("puffs_lookup: \"%s\", parent vnode %p, op: %lx\n",
182 cnp->cn_nameptr, dvp, cnp->cn_nameiop));
183
184 /*
185 * Do sanity checks we can do without consulting userland.
186 */
187
188 /*
189 * last component check & ro fs
190 *
191 * hmmm... why doesn't this check for create?
192 */
193 if ((cnp->cn_flags & ISLASTCN)
194 && (ap->a_dvp->v_mount->mnt_flag & MNT_RDONLY)
195 && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
196 DPRINTF(("puffs_lookup: write lookup for read-only fs!\n"));
197 return EROFS;
198 }
199
200 /*
201 * Check if someone fed it into the cache
202 */
203 error = cache_lookup(dvp, ap->a_vpp, cnp);
204 if (error >= 0)
205 return error;
206
207 if (isdot) {
208 vp = ap->a_dvp;
209 vref(vp);
210 *ap->a_vpp = vp;
211 return 0;
212 }
213
214 puffs_makecn(&lookup_arg.pvnr_cn, cnp);
215
216 if (cnp->cn_flags & ISDOTDOT)
217 VOP_UNLOCK(dvp, 0);
218
219 error = puffs_vntouser(pmp, PUFFS_VN_LOOKUP,
220 &lookup_arg, sizeof(lookup_arg), VPTOPNC(dvp), LOCKEDVP(dvp), NULL);
221 DPRINTF(("puffs_lookup: return of the userspace, part %d\n", error));
222
223 /*
224 * In case of error, leave parent locked. There is no new
225 * vnode to play with, so be happy with the NULL value given
226 * to vpp in the beginning.
227 */
228 if (error) {
229 if (error == -1) {
230 if ((cnp->cn_flags & ISLASTCN)
231 && (cnp->cn_nameiop == CREATE
232 || cnp->cn_nameiop == RENAME)) {
233 cnp->cn_flags |= SAVENAME;
234 error = EJUSTRETURN;
235 } else
236 /* userspace is on crack */
237 error = ENOENT;
238 }
239 *ap->a_vpp = NULL;
240 if (cnp->cn_flags & ISDOTDOT)
241 if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0)
242 cnp->cn_flags |= PDIRUNLOCK;
243 return error;
244 }
245
246 vp = puffs_pnode2vnode(pmp, lookup_arg.pvnr_newnode);
247 if (!vp) {
248 error = puffs_getvnode(dvp->v_mount,
249 lookup_arg.pvnr_newnode, &vp);
250 if (error) {
251 if (cnp->cn_flags & ISDOTDOT)
252 if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0)
253 cnp->cn_flags |= PDIRUNLOCK;
254 return error;
255 }
256 vp->v_type = lookup_arg.pvnr_vtype;
257 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
258 }
259
260 if (cnp->cn_flags & ISDOTDOT) {
261 if (cnp->cn_flags & LOCKPARENT &&
262 cnp->cn_flags & ISLASTCN) {
263 if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0) {
264 cnp->cn_flags |= PDIRUNLOCK;
265 }
266 }
267 } else {
268 if (wantpunlock) {
269 VOP_UNLOCK(dvp, 0);
270 cnp->cn_flags |= PDIRUNLOCK;
271 }
272 }
273
274 if (cnp->cn_flags & MAKEENTRY)
275 cache_enter(dvp, vp, cnp);
276 *ap->a_vpp = vp;
277
278 return 0;
279 }
280
281 int
282 puffs_create(void *v)
283 {
284 struct vop_create_args /* {
285 const struct vnodeop_desc *a_desc;
286 struct vnode *a_dvp;
287 struct vnode **a_vpp;
288 struct componentname *a_cnp;
289 struct vattr *a_vap;
290 } */ *ap = v;
291 int error;
292
293 PUFFS_VNREQ(create);
294
295 puffs_makecn(&create_arg.pvnr_cn, ap->a_cnp);
296 create_arg.pvnr_va = *ap->a_vap;
297
298 error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_CREATE,
299 &create_arg, sizeof(create_arg), VPTOPNC(ap->a_dvp),
300 ap->a_dvp, NULL);
301 if (error)
302 return error;
303
304 return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
305 create_arg.pvnr_newnode, ap->a_cnp, ap->a_vap->va_type);
306 }
307
308 int
309 puffs_mknod(void *v)
310 {
311 struct vop_mknod_args /* {
312 const struct vnodeop_desc *a_desc;
313 struct vnode *a_dvp;
314 struct vnode **a_vpp;
315 struct componentname *a_cnp;
316 struct vattr *a_vap;
317 } */ *ap = v;
318 int error;
319
320 PUFFS_VNREQ(mknod);
321
322 puffs_makecn(&mknod_arg.pvnr_cn, ap->a_cnp);
323 mknod_arg.pvnr_va = *ap->a_vap;
324
325 error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_MKNOD,
326 &mknod_arg, sizeof(mknod_arg), VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
327 if (error)
328 return error;
329
330 return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
331 mknod_arg.pvnr_newnode, ap->a_cnp, ap->a_vap->va_type);
332 }
333
334 int
335 puffs_open(void *v)
336 {
337 struct vop_open_args /* {
338 const struct vnodeop_desc *a_desc;
339 struct vnode *a_vp;
340 int a_mode;
341 kauth_cred_t a_cred;
342 struct lwp *a_l;
343 } */ *ap = v;
344
345 PUFFS_VNREQ(open);
346
347 open_arg.pvnr_mode = ap->a_mode;
348 puffs_credcvt(&open_arg.pvnr_cred, ap->a_cred);
349 open_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
350
351 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_OPEN,
352 &open_arg, sizeof(open_arg), VPTOPNC(ap->a_vp), ap->a_vp, NULL);
353 }
354
355 int
356 puffs_close(void *v)
357 {
358 struct vop_close_args /* {
359 const struct vnodeop_desc *a_desc;
360 struct vnode *a_vp;
361 int a_fflag;
362 kauth_cred_t a_cred;
363 struct lwp *a_l;
364 } */ *ap = v;
365
366 PUFFS_VNREQ(close);
367
368 close_arg.pvnr_fflag = ap->a_fflag;
369 puffs_credcvt(&close_arg.pvnr_cred, ap->a_cred);
370 close_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
371
372 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_CLOSE,
373 &close_arg, sizeof(close_arg), VPTOPNC(ap->a_vp), ap->a_vp, NULL);
374 }
375
376 int
377 puffs_access(void *v)
378 {
379 struct vop_access_args /* {
380 const struct vnodeop_desc *a_desc;
381 struct vnode *a_vp;
382 int a_mode;
383 kauth_cred_t a_cred;
384 struct lwp *a_l;
385 } */ *ap = v;
386 int error;
387
388 PUFFS_VNREQ(access);
389
390 access_arg.pvnr_mode = ap->a_mode;
391 access_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
392 puffs_credcvt(&access_arg.pvnr_cred, ap->a_cred);
393
394 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_ACCESS,
395 &access_arg, sizeof(access_arg), VPTOPNC(ap->a_vp), ap->a_vp, NULL);
396 if (error)
397 return error;
398
399 /*
400 * XXXtothepeople: no execute permissions yet. Otherwise
401 * all hell will break loose if we try to execute a file
402 * without VOP_GETPAGES support. It is forthcoming, just
403 * not there yet ...
404 */
405 if (ap->a_mode == VEXEC && ap->a_vp->v_type != VDIR)
406 return EACCES;
407
408 return 0;
409 }
410
411 int
412 puffs_getattr(void *v)
413 {
414 struct vop_getattr_args /* {
415 const struct vnodeop_desc *a_desc;
416 struct vnode *a_vp;
417 struct vattr *a_vap;
418 kauth_cred_t a_cred;
419 struct lwp *a_l;
420 } */ *ap = v;
421 int error;
422
423 PUFFS_VNREQ(getattr);
424
425 vattr_null(&getattr_arg.pvnr_va);
426 puffs_credcvt(&getattr_arg.pvnr_cred, ap->a_cred);
427 getattr_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
428
429 /*
430 * XXX + XX (dos equis): this can't go through the unlock/lock
431 * cycle, since it can be called from uvn_attach(), which fiddles
432 * around with VXLOCK and therefore breaks vn_lock(). Proper
433 * fix pending.
434 */
435 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_GETATTR,
436 &getattr_arg, sizeof(getattr_arg), VPTOPNC(ap->a_vp),
437 NULL /* XXXseeabove: should be LOCKEDVP(ap->a_vp) */, NULL);
438 if (error)
439 return error;
440
441 (void)memcpy(ap->a_vap, &getattr_arg.pvnr_va, sizeof(struct vattr));
442
443 /*
444 * XXXtothepeople: adjust the return value so that we don't
445 * advertise execute bits. Otherwise all hell will break
446 * loose if we try to execute a file without VOP_GETPAGES
447 * support. It is forthcoming, just not there yet ...
448 */
449 if (ap->a_vp->v_type != VDIR)
450 ap->a_vap->va_mode &= ~0111;
451
452 return 0;
453 }
454
455 int
456 puffs_setattr(void *v)
457 {
458 struct vop_getattr_args /* {
459 const struct vnodeop_desc *a_desc;
460 struct vnode *a_vp;
461 struct vattr *a_vap;
462 kauth_cred_t a_cred;
463 struct lwp *a_l;
464 } */ *ap = v;
465
466 PUFFS_VNREQ(setattr);
467
468 (void)memcpy(&setattr_arg.pvnr_va, ap->a_vap, sizeof(struct vattr));
469 puffs_credcvt(&setattr_arg.pvnr_cred, ap->a_cred);
470 setattr_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
471
472 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_SETATTR,
473 &setattr_arg, sizeof(setattr_arg), VPTOPNC(ap->a_vp),
474 ap->a_vp, NULL);
475 }
476
477 int
478 puffs_revoke(void *v)
479 {
480 struct vop_revoke_args /* {
481 const struct vnodeop_desc *a_desc;
482 struct vnode *a_vp;
483 int a_flags;
484 } */ *ap = v;
485 PUFFS_VNREQ(revoke);
486
487 revoke_arg.pvnr_flags = ap->a_flags;
488
489 /* don't really care if userspace doesn't want to play along */
490 puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_REVOKE,
491 &revoke_arg, sizeof(revoke_arg), VPTOPNC(ap->a_vp), NULL, NULL);
492
493 return genfs_revoke(v);
494 }
495
496 int
497 puffs_inactive(void *v)
498 {
499 struct vop_inactive_args /* {
500 const struct vnodeop_desc *a_desc;
501 struct vnode *a_vp;
502 struct lwp *a_l;
503 } */ *ap = v;
504 struct puffs_node *pnode;
505 int rv, vnrefs;
506
507 PUFFS_VNREQ(inactive);
508
509 /*
510 * XXX: think about this after we really start unlocking
511 * when going to userspace
512 */
513 pnode = ap->a_vp->v_data;
514 pnode->pn_stat |= PNODE_INACTIVE;
515
516 inactive_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
517
518 rv = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_INACTIVE,
519 &inactive_arg, sizeof(inactive_arg), VPTOPNC(ap->a_vp),
520 ap->a_vp, NULL);
521
522 /* can't trust userspace return value? simulate safe answer */
523 if (rv)
524 vnrefs = 1;
525 else
526 vnrefs = inactive_arg.pvnr_backendrefs;
527
528 VOP_UNLOCK(ap->a_vp, 0);
529
530 /*
531 * user server thinks it's gone? then don't be afraid care,
532 * node's life was already all it would ever be
533 */
534 if (vnrefs == 0)
535 vrecycle(ap->a_vp, NULL, ap->a_l);
536
537 return 0;
538 }
539
540 int
541 puffs_reclaim(void *v)
542 {
543 struct vop_reclaim_args /* {
544 const struct vnodeop_desc *a_desc;
545 struct vnode *a_vp;
546 struct lwp *a_l;
547 } */ *ap = v;
548 struct puffs_mount *pmp;
549 int error;
550
551 PUFFS_VNREQ(reclaim);
552
553 /*
554 * first things first: check if someone is trying to reclaim the
555 * root vnode. do not allow that to travel to userspace.
556 * Note that we don't need to take the lock similarly to
557 * puffs_root(), since there is only one of us.
558 */
559 if (ap->a_vp->v_flag & VROOT) {
560 pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
561 #ifdef DIAGNOSTIC
562 simple_lock(&pmp->pmp_lock);
563 if (pmp->pmp_root == NULL)
564 panic("puffs_reclaim: releasing root vnode (%p) twice",
565 ap->a_vp);
566 simple_unlock(&pmp->pmp_lock);
567 #endif
568 pmp->pmp_root = NULL;
569 puffs_putvnode(ap->a_vp);
570 return 0;
571 }
572
573 reclaim_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
574
575 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_RECLAIM,
576 &reclaim_arg, sizeof(reclaim_arg), VPTOPNC(ap->a_vp), NULL, NULL);
577 #if 0
578 /*
579 * XXX: if reclaim fails for any other reason than the userspace
580 * being dead, we should consider unmounting the filesystem, since
581 * we can't trust it to be in a consistent state anymore. But for
582 * now, just ignore all errors.
583 */
584 if (error)
585 return error;
586 #endif
587
588 puffs_putvnode(ap->a_vp);
589
590 return 0;
591 }
592
593 int
594 puffs_readdir(void *v)
595 {
596 struct vop_readdir_args /* {
597 const struct vnodeop_desc *a_desc;
598 struct vnode *a_vp;
599 struct uio *a_uio;
600 kauth_cred_t a_cred;
601 int *a_eofflag;
602 off_t **a_cookies;
603 int *a_ncookies;
604 } */ *ap = v;
605 struct puffs_vnreq_readdir *readdir_argp;
606 size_t argsize;
607 struct uio *uio = ap->a_uio;
608 size_t howmuch;
609 int error;
610
611 /* worry about these later */
612 if (!(ap->a_cookies == NULL && ap->a_ncookies == NULL))
613 return EOPNOTSUPP;
614
615 argsize = sizeof(struct puffs_vnreq_readdir);
616 readdir_argp = malloc(argsize, M_PUFFS, M_ZERO | M_WAITOK);
617
618 puffs_credcvt(&readdir_argp->pvnr_cred, ap->a_cred);
619 readdir_argp->pvnr_offset = uio->uio_offset;
620 readdir_argp->pvnr_resid = uio->uio_resid;
621
622 error = puffs_vntouser_adjbuf(MPTOPUFFSMP(ap->a_vp->v_mount),
623 PUFFS_VN_READDIR, (void **)&readdir_argp, &argsize,
624 sizeof(struct puffs_vnreq_readdir),
625 VPTOPNC(ap->a_vp), ap->a_vp, NULL);
626 if (error)
627 goto out;
628
629 /* userspace is cheating? */
630 if (readdir_argp->pvnr_resid > uio->uio_resid) {
631 error = EINVAL;
632 goto out;
633 }
634
635 /* bouncy-wouncy with the directory data */
636 howmuch = uio->uio_resid - readdir_argp->pvnr_resid;
637 error = uiomove(readdir_argp->pvnr_dent, howmuch, uio);
638 if (error)
639 goto out;
640 uio->uio_offset = readdir_argp->pvnr_offset;
641
642 out:
643 free(readdir_argp, M_PUFFS);
644 return error;
645 }
646
647 int
648 puffs_poll(void *v)
649 {
650 struct vop_poll_args /* {
651 const struct vnodeop_desc *a_desc;
652 struct vnode *a_vp;
653 int a_events;
654 struct lwp *a_l;
655 }*/ *ap = v;
656
657 PUFFS_VNREQ(poll);
658
659 poll_arg.pvnr_events = ap->a_events;
660 poll_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
661
662 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_POLL,
663 &poll_arg, sizeof(poll_arg), VPTOPNC(ap->a_vp), NULL, NULL);
664 }
665
666 int
667 puffs_fsync(void *v)
668 {
669 struct vop_fsync_args /* {
670 const struct vnodeop_desc *a_desc;
671 struct vnode *a_vp;
672 kauth_cred_t a_cred;
673 int a_flags;
674 off_t a_offlo;
675 off_t a_offhi;
676 struct lwp *a_l;
677 } */ *ap = v;
678
679 PUFFS_VNREQ(fsync);
680
681 puffs_credcvt(&fsync_arg.pvnr_cred, ap->a_cred);
682 fsync_arg.pvnr_flags = ap->a_flags;
683 fsync_arg.pvnr_offlo = ap->a_offlo;
684 fsync_arg.pvnr_offhi = ap->a_offhi;
685 fsync_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
686
687 /*
688 * XXX: see comment at puffs_getattr about locking
689 */
690 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_FSYNC,
691 &fsync_arg, sizeof(fsync_arg), VPTOPNC(ap->a_vp),
692 NULL /* XXXshouldbe: ap->a_vp */, NULL);
693 }
694
695 int
696 puffs_seek(void *v)
697 {
698 struct vop_seek_args /* {
699 const struct vnodeop_desc *a_desc;
700 struct vnode *a_vp;
701 off_t a_oldoff;
702 off_t a_newoff;
703 kauth_cred_t a_cred;
704 } */ *ap = v;
705
706 PUFFS_VNREQ(seek);
707
708 seek_arg.pvnr_oldoff = ap->a_oldoff;
709 seek_arg.pvnr_newoff = ap->a_newoff;
710 puffs_credcvt(&seek_arg.pvnr_cred, ap->a_cred);
711
712 /*
713 * XXX: seems like seek is called with an unlocked vp, but
714 * it can't hurt to play safe
715 */
716 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_SEEK,
717 &seek_arg, sizeof(seek_arg), VPTOPNC(ap->a_vp),
718 LOCKEDVP(ap->a_vp), NULL);
719 }
720
721 int
722 puffs_remove(void *v)
723 {
724 struct vop_remove_args /* {
725 const struct vnodeop_desc *a_desc;
726 struct vnode *a_dvp;
727 struct vnode *a_vp;
728 struct componentname *a_cnp;
729 } */ *ap = v;
730 int error;
731
732 PUFFS_VNREQ(remove);
733
734 remove_arg.pvnr_cookie_targ = VPTOPNC(ap->a_vp);
735 puffs_makecn(&remove_arg.pvnr_cn, ap->a_cnp);
736
737 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_REMOVE,
738 &remove_arg, sizeof(remove_arg), VPTOPNC(ap->a_dvp),
739 ap->a_dvp, ap->a_vp);
740
741 vput(ap->a_vp);
742 vput(ap->a_dvp);
743
744 return error;
745 }
746
747 int
748 puffs_mkdir(void *v)
749 {
750 struct vop_mkdir_args /* {
751 const struct vnodeop_desc *a_desc;
752 struct vnode *a_dvp;
753 struct vnode **a_vpp;
754 struct componentname *a_cnp;
755 struct vattr *a_vap;
756 } */ *ap = v;
757 int error;
758
759 PUFFS_VNREQ(mkdir);
760
761 puffs_makecn(&mkdir_arg.pvnr_cn, ap->a_cnp);
762 mkdir_arg.pvnr_va = *ap->a_vap;
763
764 /* XXX: wouldn't need to relock dvp, but that's life */
765 error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_MKDIR,
766 &mkdir_arg, sizeof(mkdir_arg), VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
767 if (error)
768 return error;
769
770 return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
771 mkdir_arg.pvnr_newnode, ap->a_cnp, VDIR);
772 }
773
774 int
775 puffs_rmdir(void *v)
776 {
777 struct vop_rmdir_args /* {
778 const struct vnodeop_desc *a_desc;
779 struct vnode *a_dvp;
780 struct vnode *a_vp;
781 struct componentname *a_cnp;
782 } */ *ap = v;
783 int error;
784
785 PUFFS_VNREQ(rmdir);
786
787 rmdir_arg.pvnr_cookie_targ = VPTOPNC(ap->a_vp);
788 puffs_makecn(&rmdir_arg.pvnr_cn, ap->a_cnp);
789
790 error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_RMDIR,
791 &rmdir_arg, sizeof(rmdir_arg), VPTOPNC(ap->a_dvp),
792 ap->a_dvp, ap->a_vp);
793
794 vput(ap->a_dvp);
795 vput(ap->a_vp);
796
797 return error;
798 }
799
800 int
801 puffs_link(void *v)
802 {
803 struct vop_link_args /* {
804 const struct vnodeop_desc *a_desc;
805 struct vnode *a_dvp;
806 struct vnode *a_vp;
807 struct componentname *a_cnp;
808 }*/ *ap = v;
809 int error;
810
811 PUFFS_VNREQ(link);
812
813 link_arg.pvnr_cookie_targ = VPTOPNC(ap->a_vp);
814 puffs_makecn(&link_arg.pvnr_cn, ap->a_cnp);
815
816 error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_LINK,
817 &link_arg, sizeof(link_arg), VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
818
819 vput(ap->a_dvp);
820
821 return error;
822 }
823
824 int
825 puffs_symlink(void *v)
826 {
827 struct vop_symlink_args /* {
828 const struct vnodeop_desc *a_desc;
829 struct vnode *a_dvp;
830 struct vnode **a_vpp;
831 struct componentname *a_cnp;
832 struct vattr *a_vap;
833 char *a_target;
834 }*/ *ap = v;
835 int error;
836
837 PUFFS_VNREQ(symlink); /* XXX: large structure */
838
839 *ap->a_vpp = NULL;
840
841 puffs_makecn(&symlink_arg.pvnr_cn, ap->a_cnp);
842 symlink_arg.pvnr_va = *ap->a_vap;
843 (void)strlcpy(symlink_arg.pvnr_link, ap->a_target,
844 sizeof(symlink_arg.pvnr_link));
845
846 /* XXX: don't need to relock parent */
847 error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount),
848 PUFFS_VN_SYMLINK, &symlink_arg, sizeof(symlink_arg),
849 VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
850 if (error)
851 return error;
852
853 return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
854 symlink_arg.pvnr_newnode, ap->a_cnp, VLNK);
855 }
856
857 int
858 puffs_readlink(void *v)
859 {
860 struct vop_readlink_args /* {
861 const struct vnodeop_desc *a_desc;
862 struct vnode *a_vp;
863 struct uio *a_uio;
864 kauth_cred_t a_cred;
865 } */ *ap = v;
866 int error;
867
868 PUFFS_VNREQ(readlink);
869
870 puffs_credcvt(&readlink_arg.pvnr_cred, ap->a_cred);
871 readlink_arg.pvnr_linklen = sizeof(readlink_arg.pvnr_link);
872
873 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount),
874 PUFFS_VN_READLINK, &readlink_arg, sizeof(readlink_arg),
875 VPTOPNC(ap->a_vp), ap->a_vp, NULL);
876 if (error)
877 return error;
878
879 readlink_arg.pvnr_link[readlink_arg.pvnr_linklen] = '\0';
880 return uiomove(&readlink_arg.pvnr_link, readlink_arg.pvnr_linklen,
881 ap->a_uio);
882 }
883
884 /* XXXXXXX: think about locking & userspace op delocking... */
885 int
886 puffs_rename(void *v)
887 {
888 struct vop_rename_args /* {
889 const struct vnodeop_desc *a_desc;
890 struct vnode *a_fdvp;
891 struct vnode *a_fvp;
892 struct componentname *a_fcnp;
893 struct vnode *a_tdvp;
894 struct vnode *a_tvp;
895 struct componentname *a_tcnp;
896 }*/ *ap = v;
897 int error;
898
899 PUFFS_VNREQ(rename);
900
901 /*
902 * participate in the duck hunt
903 * (I could do with some canard a la presse, so hopefully
904 * this is succesful)
905 */
906 KASSERT(ap->a_tdvp != ap->a_tvp);
907
908 if (ap->a_fvp->v_mount != ap->a_tdvp->v_mount) {
909 error = EXDEV;
910 goto out;
911 }
912
913 rename_arg.pvnr_cookie_src = VPTOPNC(ap->a_fvp);
914 rename_arg.pvnr_cookie_targdir = VPTOPNC(ap->a_tdvp);
915 if (ap->a_tvp)
916 rename_arg.pvnr_cookie_targ = VPTOPNC(ap->a_tvp);
917 else
918 rename_arg.pvnr_cookie_targ = NULL;
919 puffs_makecn(&rename_arg.pvnr_cn_src, ap->a_fcnp);
920 puffs_makecn(&rename_arg.pvnr_cn_targ, ap->a_tcnp);
921
922 error = puffs_vntouser(MPTOPUFFSMP(ap->a_fdvp->v_mount),
923 PUFFS_VN_RENAME, &rename_arg, sizeof(rename_arg),
924 VPTOPNC(ap->a_fdvp), NULL, NULL);
925
926 out:
927 vput(ap->a_tdvp);
928 if (ap->a_tvp != NULL)
929 vput(ap->a_tvp);
930
931 vrele(ap->a_fdvp);
932 vrele(ap->a_fvp);
933
934 return error;
935 }
936
937 int
938 puffs_read(void *v)
939 {
940 struct vop_read_args /* {
941 const struct vnodeop_desc *a_desc;
942 struct vnode *a_vp;
943 struct uio *a_uio;
944 int a_ioflag;
945 kauth_cred_t a_cred;
946 } */ *ap = v;
947 struct puffs_vnreq_read *read_argp;
948 struct puffs_mount *pmp;
949 struct uio *uio;
950 size_t tomove, argsize;
951 int error;
952
953 uio = ap->a_uio;
954
955 pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
956 tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
957 argsize = sizeof(struct puffs_vnreq_read);
958 read_argp = malloc(argsize, M_PUFFS, M_WAITOK | M_ZERO);
959
960 error = 0;
961 while (uio->uio_resid > 0) {
962 read_argp->pvnr_ioflag = ap->a_ioflag;
963 read_argp->pvnr_resid = tomove;
964 read_argp->pvnr_offset = uio->uio_offset;
965 puffs_credcvt(&read_argp->pvnr_cred, ap->a_cred);
966
967 argsize = sizeof(struct puffs_vnreq_read);
968 error = puffs_vntouser_adjbuf(pmp, PUFFS_VN_READ,
969 (void **)&read_argp, &argsize,
970 sizeof(struct puffs_vnreq_read), VPTOPNC(ap->a_vp),
971 ap->a_vp, NULL);
972 if (error)
973 goto out;
974
975 if (read_argp->pvnr_resid > tomove) {
976 error = EINVAL;
977 goto out;
978 }
979
980 error = uiomove(read_argp->pvnr_data,
981 tomove - read_argp->pvnr_resid, uio);
982
983 /*
984 * in case the file is out of juice, resid from userspace
985 * is != 0. and the error-case is quite obvious
986 */
987 if (error || read_argp->pvnr_resid)
988 goto out;
989
990 tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
991 }
992
993 out:
994 free(read_argp, M_PUFFS);
995 return error;
996 }
997
998 int
999 puffs_write(void *v)
1000 {
1001 struct vop_write_args /* {
1002 const struct vnodeop_desc *a_desc;
1003 struct vnode *a_vp;
1004 struct uio *a_uio;
1005 int a_ioflag;
1006 kauth_cred_t a_cred;
1007 }*/ *ap = v;
1008 struct puffs_vnreq_write *write_argp;
1009 struct puffs_mount *pmp;
1010 struct uio *uio;
1011 size_t tomove, argsize;
1012 int error;
1013
1014 uio = ap->a_uio;
1015
1016 pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
1017 tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
1018 argsize = sizeof(struct puffs_vnreq_write) + tomove;
1019 write_argp = malloc(argsize, M_PUFFS, M_WAITOK | M_ZERO);
1020
1021 error = 0;
1022 while (uio->uio_resid > 0) {
1023 write_argp->pvnr_ioflag = ap->a_ioflag;
1024 write_argp->pvnr_resid = tomove;
1025 write_argp->pvnr_offset = uio->uio_offset;
1026 puffs_credcvt(&write_argp->pvnr_cred, ap->a_cred);
1027 error = uiomove(write_argp->pvnr_data, tomove, ap->a_uio);
1028 if (error)
1029 goto out;
1030
1031 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount),
1032 PUFFS_VN_WRITE, write_argp, argsize, VPTOPNC(ap->a_vp),
1033 ap->a_vp, NULL);
1034 if (error) {
1035 /* restore uiomove */
1036 uio->uio_resid += tomove;
1037 uio->uio_offset -= tomove;
1038 goto out;
1039 }
1040 if (write_argp->pvnr_resid > tomove) {
1041 error = EINVAL;
1042 goto out;
1043 }
1044
1045 /* didn't move everything? bad userspace. bail */
1046 if (write_argp->pvnr_resid != 0) {
1047 uio->uio_resid += write_argp->pvnr_resid;
1048 uio->uio_offset -= write_argp->pvnr_resid;
1049 error = EIO;
1050 break;
1051 }
1052
1053 tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
1054 }
1055
1056 out:
1057 free(write_argp, M_PUFFS);
1058 return error;
1059 }
1060
1061 static int puffs_fcnioctl(struct vop_ioctl_args * /*XXX*/, int);
1062
1063 #define FCNIOCTL_ARG_MAX 1<<16
1064 int
1065 puffs_fcnioctl(struct vop_ioctl_args *ap, int puffsop)
1066 {
1067 /* struct vop_ioctl_args {
1068 const struct vnodeop_desc *a_desc;
1069 struct vnode *a_vp;
1070 u_long a_command;
1071 void *a_data;
1072 int a_fflag;
1073 kauth_cred_t a_cred;
1074 struct lwp *a_l;
1075 }*ap = v; */
1076 struct puffs_mount *pmp;
1077 struct puffs_sizepark pspark;
1078 void *kernbuf;
1079 size_t copylen;
1080 int error;
1081
1082 PUFFS_VNREQ(fcnioctl);
1083
1084 /*
1085 * Since this op gives the filesystem (almost) complete control on
1086 * how much it is allowed to copy from the calling process
1087 * address space, do not enable it by default, since it would
1088 * be a whopping security hole.
1089 */
1090 pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
1091 if ((pmp->pmp_args.pa_flags & PUFFS_FLAG_ALLOWCTL) == 0)
1092 return EINVAL; /* only shoe that fits */
1093
1094 /* fill in sizereq and store it */
1095 pspark.pkso_reqid = puffs_getreqid(pmp);
1096 pspark.pkso_reqtype = PUFFS_SIZEOPREQ_BUF_IN;
1097 pspark.pkso_copybuf = ap->a_data;
1098 pspark.pkso_bufsize = FCNIOCTL_ARG_MAX;
1099 TAILQ_INSERT_TAIL(&pmp->pmp_req_sizepark, &pspark, pkso_entries);
1100
1101 /* then fill in actual request and shoot it off */
1102 fcnioctl_arg.pvnr_command = ap->a_command;
1103 fcnioctl_arg.pvnr_fflag = ap->a_fflag;
1104 puffs_credcvt(&fcnioctl_arg.pvnr_cred, ap->a_cred);
1105 fcnioctl_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
1106
1107 error = puffs_vntouser_req(MPTOPUFFSMP(ap->a_vp->v_mount), puffsop,
1108 &fcnioctl_arg, sizeof(fcnioctl_arg), VPTOPNC(ap->a_vp),
1109 pspark.pkso_reqid, NULL, NULL);
1110
1111 /* if we don't need to copy data, we're done */
1112 if (error || !fcnioctl_arg.pvnr_copyback)
1113 return error;
1114
1115 copylen = MIN(FCNIOCTL_ARG_MAX, fcnioctl_arg.pvnr_datalen);
1116 kernbuf = malloc(copylen, M_PUFFS, M_WAITOK);
1117 error = copyin(fcnioctl_arg.pvnr_data, kernbuf, copylen);
1118 if (error)
1119 goto out;
1120 error = copyout(kernbuf, ap->a_data, copylen);
1121
1122 out:
1123 free(kernbuf, M_PUFFS);
1124 return error;
1125 }
1126
1127 int
1128 puffs_ioctl(void *v)
1129 {
1130
1131 return puffs_fcnioctl(v, PUFFS_VN_IOCTL);
1132 }
1133
1134 int
1135 puffs_fcntl(void *v)
1136 {
1137
1138 return puffs_fcnioctl(v, PUFFS_VN_FCNTL);
1139 }
1140
1141 int
1142 puffs_print(void *v)
1143 {
1144 struct vop_print_args /* {
1145 struct vnode *a_vp;
1146 } */ *ap = v;
1147 struct vnode *vp = ap->a_vp;
1148 struct puffs_node *pn = vp->v_data;
1149
1150 PUFFS_VNREQ(print);
1151
1152 /* kernel portion */
1153 printf("tag VT_PUFFS, vnode %p, puffs node: %p,\n"
1154 " userspace cookie: %p\n", vp, pn, pn->pn_cookie);
1155 lockmgr_printinfo(&vp->v_lock);
1156
1157 /* userspace portion */
1158 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_PRINT,
1159 &print_arg, sizeof(print_arg), VPTOPNC(ap->a_vp),
1160 LOCKEDVP(ap->a_vp), NULL);
1161 }
1162
1163 int
1164 puffs_pathconf(void *v)
1165 {
1166 struct vop_pathconf_args /* {
1167 const struct vnodeop_desc *a_desc;
1168 struct vnode *a_vp;
1169 int a_name;
1170 register_t *a_retval;
1171 } */ *ap = v;
1172 int error;
1173
1174 PUFFS_VNREQ(pathconf);
1175
1176 pathconf_arg.pvnr_name = ap->a_name;
1177
1178 error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount),
1179 PUFFS_VN_PATHCONF, &pathconf_arg, sizeof(pathconf_arg),
1180 VPTOPNC(ap->a_vp), ap->a_vp, NULL);
1181 if (error)
1182 return error;
1183
1184 *ap->a_retval = pathconf_arg.pvnr_retval;
1185
1186 return 0;
1187 }
1188
1189 int
1190 puffs_advlock(void *v)
1191 {
1192 struct vop_advlock_args /* {
1193 const struct vnodeop_desc *a_desc;
1194 struct vnode *a_vp;
1195 void *a_id;
1196 int a_op;
1197 struct flock *a_fl;
1198 int a_flags;
1199 } */ *ap = v;
1200 int error;
1201
1202 PUFFS_VNREQ(advlock);
1203
1204 error = copyin(ap->a_fl, &advlock_arg.pvnr_fl, sizeof(struct flock));
1205 if (error)
1206 return error;
1207 advlock_arg.pvnr_id = ap->a_id;
1208 advlock_arg.pvnr_op = ap->a_op;
1209 advlock_arg.pvnr_flags = ap->a_flags;
1210
1211 return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_ADVLOCK,
1212 &advlock_arg, sizeof(advlock_arg), VPTOPNC(ap->a_vp), NULL, NULL);
1213 }
1214
1215 /*
1216 * The rest don't get a free trip to userspace and back, they
1217 * have to stay within the kernel.
1218 */
1219
1220 /*
1221 * moreXXX: yes, todo
1222 */
1223 int
1224 puffs_lock(void *v)
1225 {
1226 struct vop_lock_args /* {
1227 struct vnode *a_vp;
1228 int a_flags;
1229 }*/ *ap = v;
1230 struct vnode *vp = ap->a_vp;
1231
1232 #if 0
1233 DPRINTF(("puffs_lock: lock %p, args 0x%x\n", vp, ap->a_flags));
1234 #endif
1235
1236 return lockmgr(&vp->v_lock, ap->a_flags, &vp->v_interlock);
1237 }
1238
1239 int
1240 puffs_unlock(void *v)
1241 {
1242 struct vop_unlock_args /* {
1243 struct vnode *a_vp;
1244 int a_flags;
1245 } */ *ap = v;
1246 struct vnode *vp = ap->a_vp;
1247
1248 #if 0
1249 DPRINTF(("puffs_unlock: lock %p, args 0x%x\n", vp, ap->a_flags));
1250 #endif
1251
1252 return lockmgr(&vp->v_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock);
1253 }
1254
1255 int
1256 puffs_islocked(void *v)
1257 {
1258 struct vop_islocked_args *ap = v;
1259 int rv;
1260
1261 rv = lockstatus(&ap->a_vp->v_lock);
1262 return rv;
1263 }
1264
1265 #if 0
1266 int
1267 puffs_getpages(void *v)
1268 {
1269 struct vop_getpages_args /* {
1270 const struct vnodeop_desc *a_desc;
1271 struct vnode *a_vp;
1272 voff_t a_offset;
1273 struct vm_page **a_m;
1274 int *a_count;
1275 int a_centeridx;
1276 vm_prot_t a_access_type;
1277 int a_advice;
1278 int a_flags;
1279 } */ *ap = v;
1280
1281
1282 }
1283 #endif
1284
1285 int
1286 puffs_generic(void *v)
1287 {
1288 struct vop_generic_args *ap = v;
1289
1290 (void)ap;
1291 DPRINTF(("puffs_generic: ap->a_desc = %s\n", ap->a_desc->vdesc_name));
1292
1293 return EOPNOTSUPP;
1294 }
1295