32c627a68d4632530dbe65d51cc631870d4a15dd
[openafs.git] / src / afs / FBSD / osi_vnodeops.c
1 /*
2  * A large chunk of this file appears to be copied directly from
3  * sys/nfsclient/nfs_bio.c, which has the following license:
4  */
5 /*
6  * Copyright (c) 1989, 1993
7  *      The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Rick Macklem at The University of Guelph.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *      @(#)nfs_bio.c   8.9 (Berkeley) 3/30/95
41  */
42 /*
43  * Pursuant to a statement of U.C. Berkeley dated 1999-07-22, this license
44  * is amended to drop clause (3) above.
45  */
46
47 #include <afsconfig.h>
48 #include <afs/param.h>
49
50
51 #include <afs/sysincludes.h>    /* Standard vendor system headers */
52 #include <afsincludes.h>        /* Afs-based standard headers */
53 #include <afs/afs_stats.h>      /* statistics */
54 #include <sys/malloc.h>
55 #include <sys/namei.h>
56 #include <sys/unistd.h>
57 #if __FreeBSD_version >= 1000030
58 #include <sys/rwlock.h>
59 #endif
60 #include <vm/vm_page.h>
61 #include <vm/vm_object.h>
62 #include <vm/vm_pager.h>
63 #include <vm/vnode_pager.h>
64 extern int afs_pbuf_freecnt;
65
66 static vop_access_t     afs_vop_access;
67 static vop_advlock_t    afs_vop_advlock;
68 static vop_close_t      afs_vop_close;
69 static vop_create_t     afs_vop_create;
70 static vop_fsync_t      afs_vop_fsync;
71 static vop_getattr_t    afs_vop_getattr;
72 static vop_getpages_t   afs_vop_getpages;
73 static vop_inactive_t   afs_vop_inactive;
74 static vop_ioctl_t      afs_vop_ioctl;
75 static vop_link_t       afs_vop_link;
76 static vop_lookup_t     afs_vop_lookup;
77 static vop_mkdir_t      afs_vop_mkdir;
78 static vop_mknod_t      afs_vop_mknod;
79 static vop_open_t       afs_vop_open;
80 static vop_pathconf_t   afs_vop_pathconf;
81 static vop_print_t      afs_vop_print;
82 static vop_putpages_t   afs_vop_putpages;
83 static vop_read_t       afs_vop_read;
84 static vop_readdir_t    afs_vop_readdir;
85 static vop_readlink_t   afs_vop_readlink;
86 static vop_reclaim_t    afs_vop_reclaim;
87 static vop_remove_t     afs_vop_remove;
88 static vop_rename_t     afs_vop_rename;
89 static vop_rmdir_t      afs_vop_rmdir;
90 static vop_setattr_t    afs_vop_setattr;
91 static vop_strategy_t   afs_vop_strategy;
92 static vop_symlink_t    afs_vop_symlink;
93 static vop_write_t      afs_vop_write;
94
95 struct vop_vector afs_vnodeops = {
96         .vop_default =          &default_vnodeops,
97         .vop_access =           afs_vop_access,
98         .vop_advlock =          afs_vop_advlock,
99         .vop_close =            afs_vop_close,
100         .vop_create =           afs_vop_create,
101         .vop_fsync =            afs_vop_fsync,
102         .vop_getattr =          afs_vop_getattr,
103         .vop_getpages =         afs_vop_getpages,
104         .vop_inactive =         afs_vop_inactive,
105         .vop_ioctl =            afs_vop_ioctl,
106         .vop_link =             afs_vop_link,
107         .vop_lookup =           afs_vop_lookup,
108         .vop_mkdir =            afs_vop_mkdir,
109         .vop_mknod =            afs_vop_mknod,
110         .vop_open =             afs_vop_open,
111         .vop_pathconf =         afs_vop_pathconf,
112         .vop_print =            afs_vop_print,
113         .vop_putpages =         afs_vop_putpages,
114         .vop_read =             afs_vop_read,
115         .vop_readdir =          afs_vop_readdir,
116         .vop_readlink =         afs_vop_readlink,
117         .vop_reclaim =          afs_vop_reclaim,
118         .vop_remove =           afs_vop_remove,
119         .vop_rename =           afs_vop_rename,
120         .vop_rmdir =            afs_vop_rmdir,
121         .vop_setattr =          afs_vop_setattr,
122         .vop_strategy =         afs_vop_strategy,
123         .vop_symlink =          afs_vop_symlink,
124         .vop_write =            afs_vop_write,
125 };
126
127 #define GETNAME()       \
128     struct componentname *cnp = ap->a_cnp; \
129     char *name; \
130     MALLOC(name, char *, cnp->cn_namelen+1, M_TEMP, M_WAITOK); \
131     memcpy(name, cnp->cn_nameptr, cnp->cn_namelen); \
132     name[cnp->cn_namelen] = '\0'
133
134 #define DROPNAME() FREE(name, M_TEMP)
135
136 /*
137  * Here we define compatibility functions/macros for interfaces that
138  * have changed between different FreeBSD versions.
139  */
140 #if defined(AFS_FBSD90_ENV)
141 static __inline void ma_vm_page_lock_queues(void) {};
142 static __inline void ma_vm_page_unlock_queues(void) {};
143 static __inline void ma_vm_page_lock(vm_page_t m) { vm_page_lock(m); };
144 static __inline void ma_vm_page_unlock(vm_page_t m) { vm_page_unlock(m); };
145 #else
146 static __inline void ma_vm_page_lock_queues(void) { vm_page_lock_queues(); };
147 static __inline void ma_vm_page_unlock_queues(void) { vm_page_unlock_queues(); };
148 static __inline void ma_vm_page_lock(vm_page_t m) {};
149 static __inline void ma_vm_page_unlock(vm_page_t m) {};
150 #endif
151
152 #define ma_vn_lock(vp, flags, p) (vn_lock(vp, flags))
153 #define MA_VOP_LOCK(vp, flags, p) (VOP_LOCK(vp, flags))
154 #define MA_VOP_UNLOCK(vp, flags, p) (VOP_UNLOCK(vp, flags))
155
156 #define MA_PCPU_INC(c) PCPU_INC(c)
157 #define MA_PCPU_ADD(c, n) PCPU_ADD(c, n)
158
159 #if __FreeBSD_version >= 1000030
160 #define AFS_VM_OBJECT_WLOCK(o)  VM_OBJECT_WLOCK(o)
161 #define AFS_VM_OBJECT_WUNLOCK(o)        VM_OBJECT_WUNLOCK(o)
162 #else
163 #define AFS_VM_OBJECT_WLOCK(o)  VM_OBJECT_LOCK(o)
164 #define AFS_VM_OBJECT_WUNLOCK(o)        VM_OBJECT_UNLOCK(o)
165 #endif
166
167 /*
168  * Mosty copied from sys/ufs/ufs/ufs_vnops.c:ufs_pathconf().
169  * We should know the correct answers to these questions with
170  * respect to the AFS protocol (which may differ from the UFS
171  * values) but for the moment this will do.
172  */
173 static int
174 afs_vop_pathconf(struct vop_pathconf_args *ap)
175 {
176         int error;
177
178         error = 0;
179         switch (ap->a_name) {
180         case _PC_LINK_MAX:
181                 *ap->a_retval = LINK_MAX;
182                 break;
183         case _PC_NAME_MAX:
184                 *ap->a_retval = NAME_MAX;
185                 break;
186         case _PC_PATH_MAX:
187                 *ap->a_retval = PATH_MAX;
188                 break;
189         case _PC_PIPE_BUF:
190                 *ap->a_retval = PIPE_BUF;
191                 break;
192         case _PC_CHOWN_RESTRICTED:
193                 *ap->a_retval = 1;
194                 break;
195         case _PC_NO_TRUNC:
196                 *ap->a_retval = 1;
197                 break;
198 #ifdef _PC_ACL_EXTENDED
199         case _PC_ACL_EXTENDED:
200                 *ap->a_retval = 0;
201                 break;
202         case _PC_ACL_PATH_MAX:
203                 *ap->a_retval = 3;
204                 break;
205 #endif
206 #ifdef _PC_MAC_PRESENT
207         case _PC_MAC_PRESENT:
208                 *ap->a_retval = 0;
209                 break;
210 #endif
211 #ifdef _PC_ASYNC_IO
212         case _PC_ASYNC_IO:
213                 /* _PC_ASYNC_IO should have been handled by upper layers. */
214                 KASSERT(0, ("_PC_ASYNC_IO should not get here"));
215                 error = EINVAL;
216                 break;
217         case _PC_PRIO_IO:
218                 *ap->a_retval = 0;
219                 break;
220         case _PC_SYNC_IO:
221                 *ap->a_retval = 0;
222                 break;
223 #endif
224 #ifdef _PC_ALLOC_SIZE_MIN
225         case _PC_ALLOC_SIZE_MIN:
226                 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_bsize;
227                 break;
228 #endif
229 #ifdef _PC_FILESIZEBITS
230         case _PC_FILESIZEBITS:
231                 *ap->a_retval = 32; /* XXX */
232                 break;
233 #endif
234 #ifdef _PC_REC_INCR_XFER_SIZE
235         case _PC_REC_INCR_XFER_SIZE:
236                 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
237                 break;
238         case _PC_REC_MAX_XFER_SIZE:
239                 *ap->a_retval = -1; /* means ``unlimited'' */
240                 break;
241         case _PC_REC_MIN_XFER_SIZE:
242                 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
243                 break;
244         case _PC_REC_XFER_ALIGN:
245                 *ap->a_retval = PAGE_SIZE;
246                 break;
247 #endif
248 #ifdef _PC_SYMLINK_MAX
249         case _PC_SYMLINK_MAX:
250                 *ap->a_retval = MAXPATHLEN;
251                 break;
252 #endif
253         default:
254                 error = EINVAL;
255                 break;
256         }
257         return (error);
258 }
259
260 int
261 afs_vop_lookup(ap)
262      struct vop_lookup_args     /* {
263                                  * struct vnodeop_desc * a_desc;
264                                  * struct vnode *a_dvp;
265                                  * struct vnode **a_vpp;
266                                  * struct componentname *a_cnp;
267                                  * } */ *ap;
268 {
269     int error;
270     struct vcache *vcp;
271     struct vnode *vp, *dvp;
272     int flags = ap->a_cnp->cn_flags;
273     int lockparent;             /* 1 => lockparent flag is set */
274     int wantparent;             /* 1 => wantparent or lockparent flag */
275
276     dvp = ap->a_dvp;
277     if (dvp->v_type != VDIR) {
278         return ENOTDIR;
279     }
280
281     if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT))
282         return EIO;
283
284     GETNAME();
285
286     lockparent = flags & LOCKPARENT;
287     wantparent = flags & (LOCKPARENT | WANTPARENT);
288
289 #if __FreeBSD_version < 1000021
290     cnp->cn_flags |= MPSAFE; /* steel */
291 #endif
292
293     if (flags & ISDOTDOT)
294         MA_VOP_UNLOCK(dvp, 0, p);
295
296     AFS_GLOCK();
297     error = afs_lookup(VTOAFS(dvp), name, &vcp, cnp->cn_cred);
298     AFS_GUNLOCK();
299
300     if (error) {
301         if (flags & ISDOTDOT)
302             MA_VOP_LOCK(dvp, LK_EXCLUSIVE | LK_RETRY, p);
303         if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)
304             && (flags & ISLASTCN) && error == ENOENT)
305             error = EJUSTRETURN;
306         if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
307             cnp->cn_flags |= SAVENAME;
308         DROPNAME();
309         *ap->a_vpp = 0;
310         return (error);
311     }
312     vp = AFSTOV(vcp);           /* always get a node if no error */
313
314     /* The parent directory comes in locked.  We unlock it on return
315      * unless the caller wants it left locked.
316      * we also always return the vnode locked. */
317
318     if (flags & ISDOTDOT) {
319         /* vp before dvp since we go root to leaf, and .. comes first */
320         ma_vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
321         ma_vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
322         /* always return the child locked */
323         if (lockparent && (flags & ISLASTCN)
324             && (error = ma_vn_lock(dvp, LK_EXCLUSIVE, p))) {
325             vput(vp);
326             DROPNAME();
327             return (error);
328         }
329     } else if (vp == dvp) {
330         /* they're the same; afs_lookup() already ref'ed the leaf.
331          * It came in locked, so we don't need to ref OR lock it */
332     } else {
333         ma_vn_lock(vp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, p);
334         /* always return the child locked */
335     }
336     *ap->a_vpp = vp;
337
338     if ((cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN))
339         || (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)))
340         cnp->cn_flags |= SAVENAME;
341
342     DROPNAME();
343     return error;
344 }
345
346 int
347 afs_vop_create(ap)
348      struct vop_create_args     /* {
349                                  * struct vnode *a_dvp;
350                                  * struct vnode **a_vpp;
351                                  * struct componentname *a_cnp;
352                                  * struct vattr *a_vap;
353                                  * } */ *ap;
354 {
355     int error = 0;
356     struct vcache *vcp;
357     struct vnode *dvp = ap->a_dvp;
358     GETNAME();
359
360     AFS_GLOCK();
361     error =
362         afs_create(VTOAFS(dvp), name, ap->a_vap,
363                    ap->a_vap->va_vaflags & VA_EXCLUSIVE ? EXCL : NONEXCL,
364                    ap->a_vap->va_mode, &vcp, cnp->cn_cred);
365     AFS_GUNLOCK();
366     if (error) {
367         DROPNAME();
368         return (error);
369     }
370
371     if (vcp) {
372         *ap->a_vpp = AFSTOV(vcp);
373         ma_vn_lock(AFSTOV(vcp), LK_EXCLUSIVE | LK_RETRY, p);
374     } else
375         *ap->a_vpp = 0;
376
377     DROPNAME();
378     return error;
379 }
380
381 int
382 afs_vop_mknod(ap)
383      struct vop_mknod_args      /* {
384                                  * struct vnode *a_dvp;
385                                  * struct vnode **a_vpp;
386                                  * struct componentname *a_cnp;
387                                  * struct vattr *a_vap;
388                                  * } */ *ap;
389 {
390     return (ENODEV);
391 }
392
393 int
394 afs_vop_open(ap)
395      struct vop_open_args       /* {
396                                  * struct vnode *a_vp;
397                                  * int  a_mode;
398                                  * struct ucred *a_cred;
399                                  * struct thread *a_td;
400                                  * struct file *a_fp;
401                                  * } */ *ap;
402 {
403     int error;
404     struct vcache *vc = VTOAFS(ap->a_vp);
405
406     AFS_GLOCK();
407     error = afs_open(&vc, ap->a_mode, ap->a_cred);
408 #ifdef DIAGNOSTIC
409     if (AFSTOV(vc) != ap->a_vp)
410         panic("AFS open changed vnode!");
411 #endif
412     AFS_GUNLOCK();
413     vnode_create_vobject(ap->a_vp, vc->f.m.Length, ap->a_td);
414     osi_FlushPages(vc, ap->a_cred);
415     return error;
416 }
417
418 int
419 afs_vop_close(ap)
420      struct vop_close_args      /* {
421                                  * struct vnode *a_vp;
422                                  * int  a_fflag;
423                                  * struct ucred *a_cred;
424                                  * struct thread *a_td;
425                                  * } */ *ap;
426 {
427     int code, iflag;
428     struct vnode *vp = ap->a_vp;
429     struct vcache *avc = VTOAFS(vp);
430
431     VI_LOCK(vp);
432     iflag = vp->v_iflag & VI_DOOMED;
433     VI_UNLOCK(vp);
434     if (iflag & VI_DOOMED) {
435         /* osi_FlushVCache (correctly) calls vgone() on recycled vnodes, we don't
436          * have an afs_close to process, in that case */
437         if (avc->opens != 0)
438             panic("afs_vop_close: doomed vnode %p has vcache %p with non-zero opens %d\n",
439                   vp, avc, avc->opens);
440         return 0;
441     }
442
443     AFS_GLOCK();
444     if (ap->a_cred)
445         code = afs_close(avc, ap->a_fflag, ap->a_cred);
446     else
447         code = afs_close(avc, ap->a_fflag, afs_osi_credp);
448     osi_FlushPages(avc, ap->a_cred);    /* hold GLOCK, but not basic vnode lock */
449     AFS_GUNLOCK();
450     return code;
451 }
452
453 int
454 afs_vop_access(ap)
455      struct vop_access_args     /* {
456                                  * struct vnode *a_vp;
457                                  * accmode_t a_accmode;
458                                  * struct ucred *a_cred;
459                                  * struct thread *a_td;
460                                  * } */ *ap;
461 {
462     int code;
463     AFS_GLOCK();
464     code = afs_access(VTOAFS(ap->a_vp), ap->a_accmode, ap->a_cred);
465     AFS_GUNLOCK();
466     return code;
467 }
468
469 int
470 afs_vop_getattr(ap)
471      struct vop_getattr_args    /* {
472                                  * struct vnode *a_vp;
473                                  * struct vattr *a_vap;
474                                  * struct ucred *a_cred;
475                                  * } */ *ap;
476 {
477     int code;
478
479     AFS_GLOCK();
480     code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, ap->a_cred);
481     AFS_GUNLOCK();
482
483     return code;
484 }
485
486 int
487 afs_vop_setattr(ap)
488      struct vop_setattr_args    /* {
489                                  * struct vnode *a_vp;
490                                  * struct vattr *a_vap;
491                                  * struct ucred *a_cred;
492                                  * } */ *ap;
493 {
494     int code;
495     AFS_GLOCK();
496     code = afs_setattr(VTOAFS(ap->a_vp), ap->a_vap, ap->a_cred);
497     AFS_GUNLOCK();
498     return code;
499 }
500
501 int
502 afs_vop_read(ap)
503      struct vop_read_args       /* {
504                                  * struct vnode *a_vp;
505                                  * struct uio *a_uio;
506                                  * int a_ioflag;
507                                  * struct ucred *a_cred;
508                                  * 
509                                  * } */ *ap;
510 {
511     int code;
512     struct vcache *avc = VTOAFS(ap->a_vp);
513     AFS_GLOCK();
514     osi_FlushPages(avc, ap->a_cred);    /* hold GLOCK, but not basic vnode lock */
515     code = afs_read(avc, ap->a_uio, ap->a_cred, 0);
516     AFS_GUNLOCK();
517     return code;
518 }
519
520 /* struct vop_getpages_args {
521  *      struct vnode *a_vp;
522  *      vm_page_t *a_m;
523  *      int a_count;
524  *      int *a_rbehind;
525  *      int *a_rahead;
526  * };
527  */
528 int
529 afs_vop_getpages(struct vop_getpages_args *ap)
530 {
531     int code;
532     int i, nextoff, size, toff, npages, count;
533     struct uio uio;
534     struct iovec iov;
535     struct buf *bp;
536     vm_offset_t kva;
537     vm_object_t object;
538     vm_page_t *pages;
539     struct vnode *vp;
540     struct vcache *avc;
541
542     memset(&uio, 0, sizeof(uio));
543     memset(&iov, 0, sizeof(iov));
544
545     vp = ap->a_vp;
546     avc = VTOAFS(vp);
547     pages = ap->a_m;
548 #ifdef FBSD_VOP_GETPAGES_BUSIED
549     npages = ap->a_count;
550     if (ap->a_rbehind)
551         *ap->a_rbehind = 0;
552     if (ap->a_rahead)
553         *ap->a_rahead = 0;
554 #else
555     npages = btoc(ap->a_count);
556 #endif
557
558     if ((object = vp->v_object) == NULL) {
559         printf("afs_getpages: called with non-merged cache vnode??\n");
560         return VM_PAGER_ERROR;
561     }
562
563     /*
564      * If the requested page is partially valid, just return it and
565      * allow the pager to zero-out the blanks.  Partially valid pages
566      * can only occur at the file EOF.
567      */
568     {
569 #ifdef FBSD_VOP_GETPAGES_BUSIED
570         AFS_VM_OBJECT_WLOCK(object);
571         ma_vm_page_lock_queues();
572         if(pages[npages - 1]->valid != 0) {
573             if (--npages == 0) {
574                 ma_vm_page_unlock_queues();
575                 AFS_VM_OBJECT_WUNLOCK(object);
576                 return (VM_PAGER_OK);
577             }
578         }
579 #else
580         vm_page_t m = pages[ap->a_reqpage];
581         AFS_VM_OBJECT_WLOCK(object);
582         ma_vm_page_lock_queues();
583         if (m->valid != 0) {
584             /* handled by vm_fault now        */
585             /* vm_page_zero_invalid(m, TRUE); */
586             for (i = 0; i < npages; ++i) {
587                 if (i != ap->a_reqpage) {
588                     ma_vm_page_lock(pages[i]);
589                     vm_page_free(pages[i]);
590                     ma_vm_page_unlock(pages[i]);
591                 }
592             }
593             ma_vm_page_unlock_queues();
594             AFS_VM_OBJECT_WUNLOCK(object);
595             return (0);
596         }
597 #endif
598         ma_vm_page_unlock_queues();
599         AFS_VM_OBJECT_WUNLOCK(object);
600     }
601     bp = getpbuf(&afs_pbuf_freecnt);
602
603     kva = (vm_offset_t) bp->b_data;
604     pmap_qenter(kva, pages, npages);
605     MA_PCPU_INC(cnt.v_vnodein);
606     MA_PCPU_ADD(cnt.v_vnodepgsin, npages);
607
608 #ifdef FBSD_VOP_GETPAGES_BUSIED
609     count = ctob(npages);
610 #else
611     count = ap->a_count;
612 #endif
613     iov.iov_base = (caddr_t) kva;
614     iov.iov_len = count;
615     uio.uio_iov = &iov;
616     uio.uio_iovcnt = 1;
617     uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
618     uio.uio_resid = count;
619     uio.uio_segflg = UIO_SYSSPACE;
620     uio.uio_rw = UIO_READ;
621     uio.uio_td = curthread;
622
623     AFS_GLOCK();
624     osi_FlushPages(avc, osi_curcred()); /* hold GLOCK, but not basic vnode lock */
625     code = afs_read(avc, &uio, osi_curcred(), 0);
626     AFS_GUNLOCK();
627     pmap_qremove(kva, npages);
628
629     relpbuf(bp, &afs_pbuf_freecnt);
630
631     if (code && (uio.uio_resid == count)) {
632 #ifndef FBSD_VOP_GETPAGES_BUSIED
633         AFS_VM_OBJECT_WLOCK(object);
634         ma_vm_page_lock_queues();
635         for (i = 0; i < npages; ++i) {
636             if (i != ap->a_reqpage)
637                 vm_page_free(pages[i]);
638         }
639         ma_vm_page_unlock_queues();
640         AFS_VM_OBJECT_WUNLOCK(object);
641 #endif
642         return VM_PAGER_ERROR;
643     }
644
645     size = count - uio.uio_resid;
646     AFS_VM_OBJECT_WLOCK(object);
647     ma_vm_page_lock_queues();
648     for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
649         vm_page_t m;
650         nextoff = toff + PAGE_SIZE;
651         m = pages[i];
652
653         /* XXX not in nfsclient? */
654         m->flags &= ~PG_ZERO;
655
656         if (nextoff <= size) {
657             /*
658              * Read operation filled an entire page
659              */
660             m->valid = VM_PAGE_BITS_ALL;
661             KASSERT(m->dirty == 0, ("afs_getpages: page %p is dirty", m));
662         } else if (size > toff) {
663             /*
664              * Read operation filled a partial page.
665              */
666             m->valid = 0;
667             vm_page_set_validclean(m, 0, size - toff);
668             KASSERT(m->dirty == 0, ("afs_getpages: page %p is dirty", m));
669         }
670
671 #ifndef FBSD_VOP_GETPAGES_BUSIED
672         if (i != ap->a_reqpage) {
673 #if __FreeBSD_version >= 1000042
674             vm_page_readahead_finish(m);
675 #else
676             /*
677              * Whether or not to leave the page activated is up in
678              * the air, but we should put the page on a page queue
679              * somewhere (it already is in the object).  Result:
680              * It appears that emperical results show that
681              * deactivating pages is best.
682              */
683
684             /*
685              * Just in case someone was asking for this page we
686              * now tell them that it is ok to use.
687              */
688             if (!code) {
689                 if (m->oflags & VPO_WANTED) {
690                     ma_vm_page_lock(m);
691                     vm_page_activate(m);
692                     ma_vm_page_unlock(m);
693                 }
694                 else {
695                     ma_vm_page_lock(m);
696                     vm_page_deactivate(m);
697                     ma_vm_page_unlock(m);
698                 }
699                 vm_page_wakeup(m);
700             } else {
701                 ma_vm_page_lock(m);
702                 vm_page_free(m);
703                 ma_vm_page_unlock(m);
704             }
705 #endif  /* __FreeBSD_version 1000042 */
706         }
707 #endif   /* ndef FBSD_VOP_GETPAGES_BUSIED */
708     }
709     ma_vm_page_unlock_queues();
710     AFS_VM_OBJECT_WUNLOCK(object);
711     return VM_PAGER_OK;
712 }
713
714 int
715 afs_vop_write(ap)
716      struct vop_write_args      /* {
717                                  * struct vnode *a_vp;
718                                  * struct uio *a_uio;
719                                  * int a_ioflag;
720                                  * struct ucred *a_cred;
721                                  * } */ *ap;
722 {
723     int code;
724     struct vcache *avc = VTOAFS(ap->a_vp);
725     AFS_GLOCK();
726     osi_FlushPages(avc, ap->a_cred);    /* hold GLOCK, but not basic vnode lock */
727     code =
728         afs_write(VTOAFS(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_cred, 0);
729     AFS_GUNLOCK();
730     return code;
731 }
732
733 /*-
734  * struct vop_putpages_args {
735  *      struct vnode *a_vp;
736  *      vm_page_t *a_m;
737  *      int a_count;
738  *      int a_sync;
739  *      int *a_rtvals;
740  *      vm_oofset_t a_offset;
741  * };
742  */
743 /*
744  * All of the pages passed to us in ap->a_m[] are already marked as busy,
745  * so there is no additional locking required to set their flags.  -GAW
746  */
747 int
748 afs_vop_putpages(struct vop_putpages_args *ap)
749 {
750     int code;
751     int i, size, npages, sync;
752     struct uio uio;
753     struct iovec iov;
754     struct buf *bp;
755     vm_offset_t kva;
756     struct vnode *vp;
757     struct vcache *avc;
758
759     memset(&uio, 0, sizeof(uio));
760     memset(&iov, 0, sizeof(iov));
761
762     vp = ap->a_vp;
763     avc = VTOAFS(vp);
764     /* Perhaps these two checks should just be KASSERTs instead... */
765     if (vp->v_object == NULL) {
766         printf("afs_putpages: called with non-merged cache vnode??\n");
767         return VM_PAGER_ERROR;  /* XXX I think this is insufficient */
768     }
769     if (vType(avc) != VREG) {
770         printf("afs_putpages: not VREG");
771         return VM_PAGER_ERROR;  /* XXX I think this is insufficient */
772     }
773     npages = btoc(ap->a_count);
774     for (i = 0; i < npages; i++)
775         ap->a_rtvals[i] = VM_PAGER_AGAIN;
776     bp = getpbuf(&afs_pbuf_freecnt);
777
778     kva = (vm_offset_t) bp->b_data;
779     pmap_qenter(kva, ap->a_m, npages);
780     MA_PCPU_INC(cnt.v_vnodeout);
781     MA_PCPU_ADD(cnt.v_vnodepgsout, ap->a_count);
782
783     iov.iov_base = (caddr_t) kva;
784     iov.iov_len = ap->a_count;
785     uio.uio_iov = &iov;
786     uio.uio_iovcnt = 1;
787     uio.uio_offset = IDX_TO_OFF(ap->a_m[0]->pindex);
788     uio.uio_resid = ap->a_count;
789     uio.uio_segflg = UIO_SYSSPACE;
790     uio.uio_rw = UIO_WRITE;
791     uio.uio_td = curthread;
792     sync = IO_VMIO;
793     if (ap->a_sync & VM_PAGER_PUT_SYNC)
794         sync |= IO_SYNC;
795     /*if (ap->a_sync & VM_PAGER_PUT_INVAL)
796      * sync |= IO_INVAL; */
797
798     AFS_GLOCK();
799     code = afs_write(avc, &uio, sync, osi_curcred(), 0);
800     AFS_GUNLOCK();
801
802     pmap_qremove(kva, npages);
803     relpbuf(bp, &afs_pbuf_freecnt);
804
805     if (!code) {
806         size = ap->a_count - uio.uio_resid;
807         for (i = 0; i < round_page(size) / PAGE_SIZE; i++) {
808             ap->a_rtvals[i] = VM_PAGER_OK;
809             vm_page_undirty(ap->a_m[i]);
810         }
811     }
812     return ap->a_rtvals[0];
813 }
814
815 int
816 afs_vop_ioctl(ap)
817      struct vop_ioctl_args      /* {
818                                  * struct vnode *a_vp;
819                                  * u_long a_command;
820                                  * void *a_data;
821                                  * int  a_fflag;
822                                  * struct ucred *a_cred;
823                                  * struct thread *a_td;
824                                  * } */ *ap;
825 {
826     struct vcache *tvc = VTOAFS(ap->a_vp);
827     int error = 0;
828
829     /* in case we ever get in here... */
830
831     AFS_STATCNT(afs_ioctl);
832     if (((ap->a_command >> 8) & 0xff) == 'V') {
833         /* This is a VICEIOCTL call */
834         AFS_GLOCK();
835         error = HandleIoctl(tvc, ap->a_command, ap->a_data);
836         AFS_GUNLOCK();
837         return (error);
838     } else {
839         /* No-op call; just return. */
840         return (ENOTTY);
841     }
842 }
843
844 int
845 afs_vop_fsync(ap)
846      struct vop_fsync_args      /* {
847                                  * struct vnode *a_vp;
848                                  * int a_waitfor;
849                                  * struct thread *td;
850                                  * } */ *ap;
851 {
852     int error;
853     struct vnode *vp = ap->a_vp;
854
855     AFS_GLOCK();
856     /*vflushbuf(vp, wait); */
857     error = afs_fsync(VTOAFS(vp), ap->a_td->td_ucred);
858     AFS_GUNLOCK();
859     return error;
860 }
861
862 int
863 afs_vop_remove(ap)
864      struct vop_remove_args     /* {
865                                  * struct vnode *a_dvp;
866                                  * struct vnode *a_vp;
867                                  * struct componentname *a_cnp;
868                                  * } */ *ap;
869 {
870     int error = 0;
871     struct vnode *vp = ap->a_vp;
872     struct vnode *dvp = ap->a_dvp;
873
874     GETNAME();
875     AFS_GLOCK();
876     error = afs_remove(VTOAFS(dvp), name, cnp->cn_cred);
877     AFS_GUNLOCK();
878     cache_purge(vp);
879     DROPNAME();
880     return error;
881 }
882
883 int
884 afs_vop_link(ap)
885      struct vop_link_args       /* {
886                                  * struct vnode *a_vp;
887                                  * struct vnode *a_tdvp;
888                                  * struct componentname *a_cnp;
889                                  * } */ *ap;
890 {
891     int error = 0;
892     struct vnode *dvp = ap->a_tdvp;
893     struct vnode *vp = ap->a_vp;
894
895     GETNAME();
896     if (dvp->v_mount != vp->v_mount) {
897         error = EXDEV;
898         goto out;
899     }
900     if (vp->v_type == VDIR) {
901         error = EISDIR;
902         goto out;
903     }
904     if ((error = ma_vn_lock(vp, LK_CANRECURSE | LK_EXCLUSIVE, p)) != 0) {
905         goto out;
906     }
907     AFS_GLOCK();
908     error = afs_link(VTOAFS(vp), VTOAFS(dvp), name, cnp->cn_cred);
909     AFS_GUNLOCK();
910     if (dvp != vp)
911         MA_VOP_UNLOCK(vp, 0, p);
912   out:
913     DROPNAME();
914     return error;
915 }
916
917 int
918 afs_vop_rename(ap)
919      struct vop_rename_args     /* {
920                                  * struct vnode *a_fdvp;
921                                  * struct vnode *a_fvp;
922                                  * struct componentname *a_fcnp;
923                                  * struct vnode *a_tdvp;
924                                  * struct vnode *a_tvp;
925                                  * struct componentname *a_tcnp;
926                                  * } */ *ap;
927 {
928     int error = 0;
929     struct componentname *fcnp = ap->a_fcnp;
930     char *fname;
931     struct componentname *tcnp = ap->a_tcnp;
932     char *tname;
933     struct vnode *tvp = ap->a_tvp;
934     struct vnode *tdvp = ap->a_tdvp;
935     struct vnode *fvp = ap->a_fvp;
936     struct vnode *fdvp = ap->a_fdvp;
937
938     /*
939      * Check for cross-device rename.
940      */
941     if ((fvp->v_mount != tdvp->v_mount)
942         || (tvp && (fvp->v_mount != tvp->v_mount))) {
943         error = EXDEV;
944       abortit:
945         if (tdvp == tvp)
946             vrele(tdvp);
947         else
948             vput(tdvp);
949         if (tvp)
950             vput(tvp);
951         vrele(fdvp);
952         vrele(fvp);
953         return (error);
954     }
955     /*
956      * if fvp == tvp, we're just removing one name of a pair of
957      * directory entries for the same element.  convert call into rename.
958      ( (pinched from FreeBSD 4.4's ufs_rename())
959      
960      */
961     if (fvp == tvp) {
962         if (fvp->v_type == VDIR) {
963             error = EINVAL;
964             goto abortit;
965         }
966
967         /* Release destination completely. */
968         vput(tdvp);
969         vput(tvp);
970
971         /* Delete source. */
972         vrele(fdvp);
973         vrele(fvp);
974         fcnp->cn_flags &= ~MODMASK;
975         fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
976         if ((fcnp->cn_flags & SAVESTART) == 0)
977             panic("afs_rename: lost from startdir");
978         fcnp->cn_nameiop = DELETE;
979         VREF(fdvp);
980         error = relookup(fdvp, &fvp, fcnp);
981         if (error == 0)
982             vrele(fdvp);
983         if (fvp == NULL) {
984             return (ENOENT);
985         }
986
987         error = VOP_REMOVE(fdvp, fvp, fcnp);
988         if (fdvp == fvp)
989             vrele(fdvp);
990         else
991             vput(fdvp);
992         vput(fvp);
993         return (error);
994     }
995     if ((error = ma_vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
996         goto abortit;
997
998     MALLOC(fname, char *, fcnp->cn_namelen + 1, M_TEMP, M_WAITOK);
999     memcpy(fname, fcnp->cn_nameptr, fcnp->cn_namelen);
1000     fname[fcnp->cn_namelen] = '\0';
1001     MALLOC(tname, char *, tcnp->cn_namelen + 1, M_TEMP, M_WAITOK);
1002     memcpy(tname, tcnp->cn_nameptr, tcnp->cn_namelen);
1003     tname[tcnp->cn_namelen] = '\0';
1004
1005
1006     AFS_GLOCK();
1007     /* XXX use "from" or "to" creds? NFS uses "to" creds */
1008     error =
1009         afs_rename(VTOAFS(fdvp), fname, VTOAFS(tdvp), tname, tcnp->cn_cred);
1010     AFS_GUNLOCK();
1011
1012     FREE(fname, M_TEMP);
1013     FREE(tname, M_TEMP);
1014     if (tdvp == tvp)
1015         vrele(tdvp);
1016     else
1017         vput(tdvp);
1018     if (tvp)
1019         vput(tvp);
1020     vrele(fdvp);
1021     vput(fvp);
1022     return error;
1023 }
1024
1025 int
1026 afs_vop_mkdir(ap)
1027      struct vop_mkdir_args      /* {
1028                                  * struct vnode *a_dvp;
1029                                  * struct vnode **a_vpp;
1030                                  * struct componentname *a_cnp;
1031                                  * struct vattr *a_vap;
1032                                  * } */ *ap;
1033 {
1034     struct vnode *dvp = ap->a_dvp;
1035     struct vattr *vap = ap->a_vap;
1036     int error = 0;
1037     struct vcache *vcp;
1038
1039     GETNAME();
1040 #ifdef DIAGNOSTIC
1041     if ((cnp->cn_flags & HASBUF) == 0)
1042         panic("afs_vop_mkdir: no name");
1043 #endif
1044     AFS_GLOCK();
1045     error = afs_mkdir(VTOAFS(dvp), name, vap, &vcp, cnp->cn_cred);
1046     AFS_GUNLOCK();
1047     if (error) {
1048         DROPNAME();
1049         return (error);
1050     }
1051     if (vcp) {
1052         *ap->a_vpp = AFSTOV(vcp);
1053         ma_vn_lock(AFSTOV(vcp), LK_EXCLUSIVE | LK_RETRY, p);
1054     } else
1055         *ap->a_vpp = 0;
1056     DROPNAME();
1057     return error;
1058 }
1059
1060 int
1061 afs_vop_rmdir(ap)
1062      struct vop_rmdir_args      /* {
1063                                  * struct vnode *a_dvp;
1064                                  * struct vnode *a_vp;
1065                                  * struct componentname *a_cnp;
1066                                  * } */ *ap;
1067 {
1068     int error = 0;
1069     struct vnode *dvp = ap->a_dvp;
1070
1071     GETNAME();
1072     AFS_GLOCK();
1073     error = afs_rmdir(VTOAFS(dvp), name, cnp->cn_cred);
1074     AFS_GUNLOCK();
1075     DROPNAME();
1076     return error;
1077 }
1078
1079 /* struct vop_symlink_args {
1080  *      struct vnode *a_dvp;
1081  *      struct vnode **a_vpp;
1082  *      struct componentname *a_cnp;
1083  *      struct vattr *a_vap;
1084  *      char *a_target;
1085  * };
1086  */
1087 int
1088 afs_vop_symlink(struct vop_symlink_args *ap)
1089 {
1090     struct vnode *dvp;
1091     struct vnode *newvp;
1092     struct vcache *vcp;
1093     int error;
1094
1095     GETNAME();
1096     AFS_GLOCK();
1097
1098     dvp = ap->a_dvp;
1099     newvp = NULL;
1100
1101     error =
1102         afs_symlink(VTOAFS(dvp), name, ap->a_vap, ap->a_target, NULL,
1103                     cnp->cn_cred);
1104     if (error == 0) {
1105         error = afs_lookup(VTOAFS(dvp), name, &vcp, cnp->cn_cred);
1106         if (error == 0) {
1107             newvp = AFSTOV(vcp);
1108             ma_vn_lock(newvp, LK_EXCLUSIVE | LK_RETRY, cnp->cn_thread);
1109         }
1110     }
1111     AFS_GUNLOCK();
1112     DROPNAME();
1113     *(ap->a_vpp) = newvp;
1114     return error;
1115 }
1116
1117 int
1118 afs_vop_readdir(ap)
1119      struct vop_readdir_args    /* {
1120                                  * struct vnode *a_vp;
1121                                  * struct uio *a_uio;
1122                                  * struct ucred *a_cred;
1123                                  * int *a_eofflag;
1124                                  * u_long *a_cookies;
1125                                  * int ncookies;
1126                                  * } */ *ap;
1127 {
1128     int error;
1129     off_t off;
1130 /*    printf("readdir %x cookies %x ncookies %d\n", ap->a_vp, ap->a_cookies,
1131            ap->a_ncookies); */
1132     off = ap->a_uio->uio_offset;
1133     AFS_GLOCK();
1134     error =
1135         afs_readdir(VTOAFS(ap->a_vp), ap->a_uio, ap->a_cred, ap->a_eofflag);
1136     AFS_GUNLOCK();
1137     if (!error && ap->a_ncookies != NULL) {
1138         struct uio *uio = ap->a_uio;
1139         const struct dirent *dp, *dp_start, *dp_end;
1140         int ncookies;
1141         u_long *cookies, *cookiep;
1142
1143         if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
1144             panic("afs_readdir: burned cookies");
1145         dp = (const struct dirent *)
1146             ((const char *)uio->uio_iov->iov_base - (uio->uio_offset - off));
1147
1148         dp_end = (const struct dirent *)uio->uio_iov->iov_base;
1149         for (dp_start = dp, ncookies = 0; dp < dp_end;
1150              dp = (const struct dirent *)((const char *)dp + dp->d_reclen))
1151             ncookies++;
1152
1153         MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
1154                M_WAITOK);
1155         for (dp = dp_start, cookiep = cookies; dp < dp_end;
1156              dp = (const struct dirent *)((const char *)dp + dp->d_reclen)) {
1157             off += dp->d_reclen;
1158             *cookiep++ = off;
1159         }
1160         *ap->a_cookies = cookies;
1161         *ap->a_ncookies = ncookies;
1162     }
1163
1164     return error;
1165 }
1166
1167 int
1168 afs_vop_readlink(ap)
1169      struct vop_readlink_args   /* {
1170                                  * struct vnode *a_vp;
1171                                  * struct uio *a_uio;
1172                                  * struct ucred *a_cred;
1173                                  * } */ *ap;
1174 {
1175     int error;
1176 /*    printf("readlink %x\n", ap->a_vp);*/
1177     AFS_GLOCK();
1178     error = afs_readlink(VTOAFS(ap->a_vp), ap->a_uio, ap->a_cred);
1179     AFS_GUNLOCK();
1180     return error;
1181 }
1182
1183 int
1184 afs_vop_inactive(ap)
1185      struct vop_inactive_args   /* {
1186                                  * struct vnode *a_vp;
1187                                  * struct thread *td;
1188                                  * } */ *ap;
1189 {
1190     struct vnode *vp = ap->a_vp;
1191
1192     AFS_GLOCK();
1193     afs_InactiveVCache(VTOAFS(vp), 0);  /* decrs ref counts */
1194     AFS_GUNLOCK();
1195     return 0;
1196 }
1197
1198 /*
1199  * struct vop_reclaim_args {
1200  *      struct vnode *a_vp;
1201  * };
1202  */
1203 int
1204 afs_vop_reclaim(struct vop_reclaim_args *ap)
1205 {
1206     /* copied from ../OBSD/osi_vnodeops.c:afs_nbsd_reclaim() */
1207     int code, slept;
1208     struct vnode *vp = ap->a_vp;
1209     struct vcache *avc = VTOAFS(vp);
1210     int haveGlock = ISAFS_GLOCK();
1211     int haveVlock = CheckLock(&afs_xvcache);
1212
1213     if (!haveGlock)
1214         AFS_GLOCK();
1215     if (!haveVlock)
1216         ObtainWriteLock(&afs_xvcache, 901);
1217     /* reclaim the vnode and the in-memory vcache, but keep the on-disk vcache */
1218     code = afs_FlushVCache(avc, &slept);
1219
1220     if (avc->f.states & CVInit) {
1221         avc->f.states &= ~CVInit;
1222         afs_osi_Wakeup(&avc->f.states);
1223     }
1224
1225     if (!haveVlock)
1226         ReleaseWriteLock(&afs_xvcache);
1227     if (!haveGlock)
1228         AFS_GUNLOCK();
1229
1230     if (code) {
1231         afs_warn("afs_vop_reclaim: afs_FlushVCache failed code %d vnode\n", code);
1232         VOP_PRINT(vp);
1233     }
1234
1235     /* basically, it must not fail */
1236     vnode_destroy_vobject(vp);
1237     vp->v_data = 0;
1238
1239     return 0;
1240 }
1241
1242 int
1243 afs_vop_strategy(ap)
1244      struct vop_strategy_args   /* {
1245                                  * struct buf *a_bp;
1246                                  * } */ *ap;
1247 {
1248     int error;
1249     AFS_GLOCK();
1250     error = afs_ustrategy(ap->a_bp, osi_curcred());
1251     AFS_GUNLOCK();
1252     return error;
1253 }
1254
1255 int
1256 afs_vop_print(ap)
1257      struct vop_print_args      /* {
1258                                  * struct vnode *a_vp;
1259                                  * } */ *ap;
1260 {
1261     struct vnode *vp = ap->a_vp;
1262     struct vcache *vc = VTOAFS(ap->a_vp);
1263     int s = vc->f.states;
1264
1265     printf("vc %p vp %p tag %s, fid: %d.%d.%d.%d, opens %d, writers %d", vc, vp, vp->v_tag,
1266            (int)vc->f.fid.Cell, (u_int) vc->f.fid.Fid.Volume,
1267            (u_int) vc->f.fid.Fid.Vnode, (u_int) vc->f.fid.Fid.Unique, vc->opens,
1268            vc->execsOrWriters);
1269     printf("\n  states%s%s%s%s%s", (s & CStatd) ? " statd" : "",
1270            (s & CRO) ? " readonly" : "", (s & CDirty) ? " dirty" : "",
1271            (s & CMAPPED) ? " mapped" : "",
1272            (s & CVFlushed) ? " flush in progress" : "");
1273     printf("\n");
1274     return 0;
1275 }
1276
1277 /*
1278  * Advisory record locking support (fcntl() POSIX style)
1279  */
1280 int
1281 afs_vop_advlock(ap)
1282      struct vop_advlock_args    /* {
1283                                  * struct vnode *a_vp;
1284                                  * caddr_t  a_id;
1285                                  * int  a_op;
1286                                  * struct flock *a_fl;
1287                                  * int  a_flags;
1288                                  * } */ *ap;
1289 {
1290     int error, a_op;
1291     struct ucred cr = *osi_curcred();
1292
1293     a_op = ap->a_op;
1294     if (a_op == F_UNLCK) {
1295         /*
1296          * When a_fl->type is F_UNLCK, FreeBSD passes in an a_op of F_UNLCK.
1297          * This is (confusingly) different than how you actually release a lock
1298          * with fcntl(), which is done with an a_op of F_SETLK and an l_type of
1299          * F_UNLCK. Pretend we were given an a_op of F_SETLK in this case,
1300          * since this is what afs_lockctl expects.
1301          */
1302         a_op = F_SETLK;
1303     }
1304
1305     AFS_GLOCK();
1306     error =
1307         afs_lockctl(VTOAFS(ap->a_vp),
1308                 ap->a_fl,
1309                 a_op, &cr,
1310                 (int)(intptr_t)ap->a_id);       /* XXX: no longer unique! */
1311     AFS_GUNLOCK();
1312     return error;
1313 }