5bf9367d8dfcacc4ef6ea44d3c8a3bf9ff03e015
[openafs.git] / src / afs / VNOPS / afs_vnop_flock.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Implements:
12  *
13  */
14
15 #include <afsconfig.h>
16 #include "afs/param.h"
17
18
19 #include "afs/sysincludes.h"    /* Standard vendor system headers */
20 #include "afsincludes.h"        /* Afs-based standard headers */
21 #include "afs/afs_stats.h"      /* statistics */
22 #include "afs/afs_cbqueue.h"
23 #include "afs/nfsclient.h"
24 #include "afs/afs_osidnlc.h"
25 #include "afs/unified_afs.h"
26
27
28
29
30
31 /* Static prototypes */
32 static int HandleGetLock(register struct vcache *avc,
33                          register struct AFS_FLOCK *af,
34                          register struct vrequest *areq, int clid);
35 static int GetFlockCount(struct vcache *avc, struct vrequest *areq);
36 static int lockIdcmp2(struct AFS_FLOCK *flock1, struct vcache *vp,
37                       register struct SimpleLocks *alp, int onlymine,
38                       int clid);
39 static void DoLockWarning(afs_ucred_t * acred);
40
41 /* int clid;  * non-zero on SGI, OSF, SunOS, Darwin, xBSD ** XXX ptr type */
42
43 #if defined(AFS_SUN5_ENV)
44 void
45 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
46 {
47     proc_t *procp = ttoproc(curthread);
48
49     if (slp) {
50 # ifdef AFS_SUN53_ENV
51         slp->sysid = 0;
52         slp->pid = procp->p_pid;
53 # else
54         slp->sysid = procp->p_sysid;
55         slp->pid = procp->p_epid;
56 # endif
57     } else {
58 # ifdef AFS_SUN53_ENV
59         flock->l_sysid = 0;
60         flock->l_pid = procp->p_pid;
61 # else
62         flock->l_sysid = procp->p_sysid;
63         flock->l_pid = procp->p_epid;
64 # endif
65     }
66 }
67 #elif defined(AFS_SGI_ENV)
68 void
69 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
70 {
71 # if defined(AFS_SGI65_ENV)
72     flid_t flid;
73     get_current_flid(&flid);
74 # else
75     afs_proc_t *procp = OSI_GET_CURRENT_PROCP();
76 # endif
77
78     if (slp) {
79 # ifdef AFS_SGI65_ENV
80         slp->sysid = flid.fl_sysid;
81 # else
82         slp->sysid = OSI_GET_CURRENT_SYSID();
83 # endif
84         slp->pid = clid;
85     } else {
86 # ifdef AFS_SGI65_ENV
87         flock->l_sysid = flid.fl_sysid;
88 # else
89         flock->l_sysid = OSI_GET_CURRENT_SYSID();
90 # endif
91         flock->l_pid = clid;
92     }
93 }
94 #elif defined(AFS_AIX_ENV)
95 void
96 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
97 {
98 # if !defined(AFS_AIX32_ENV)
99     afs_proc_t *procp = u.u_procp;
100 # endif
101
102     if (slp) {
103 # if defined(AFS_AIX41_ENV)
104         slp->sysid = 0;
105         slp->pid = getpid();
106 # elif defined(AFS_AIX32_ENV)
107         slp->sysid = u.u_sysid;
108         slp->pid = u.u_epid;
109 # else
110         slp->sysid = procp->p_sysid;
111         slp->pid = prcop->p_epid;
112 # endif
113     } else {
114 # if defined(AFS_AIX41_ENV)
115         flock->l_sysid = 0;
116         flock->l_pid = getpid();
117 # elif defined(AFS_AIX32_ENV)
118         flock->l_sysid = u.u_sysid;
119         flock->l_pid = u.u_epid;
120 # else
121         flock->l_sysid = procp->p_sysid;
122         flock->l_pid = procp->p_epid;
123 # endif
124     }
125 }
126 #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
127 void
128 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
129 {
130     if (slp) {
131         slp->pid = clid;
132     } else {
133         flock->l_pid = clid;
134     }
135 }
136 #elif defined(AFS_LINUX20_ENV) || defined(AFS_HPUX_ENV)
137 void
138 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
139 {
140     if (slp) {
141         slp->pid = getpid();
142     } else {
143         flock->l_pid = getpid();
144     }
145 }
146 #elif defined(UKERNEL)
147 void
148 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
149 {
150     if (slp) {
151         slp->pid = get_user_struct()->u_procp->p_pid;
152     } else {
153         flock->l_pid = get_user_struct()->u_procp->p_pid;
154     }
155 }
156 #else
157 void
158 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
159 {
160     if (slp) {
161         slp->pid = u.u_procp->p_pid;
162     } else {
163         flock->l_pid = u.u_procp->p_pid;
164     }
165 }
166 #endif
167
168 /* return 1 (true) if specified flock does not match alp (if 
169  * specified), or any of the slp structs (if alp == 0) 
170  */
171 /* I'm not sure that the comparsion of flock->pid to p_ppid
172  * is correct.  Should that be a comparision of alp (or slp) ->pid  
173  * to p_ppid?  Especially in the context of the lower loop, where
174  * the repeated comparison doesn't make much sense...
175  */
176 /* onlymine - don't match any locks which are held by my parent */
177 /* clid - only irix 6.5 */
178
179 static int
180 lockIdcmp2(struct AFS_FLOCK *flock1, struct vcache *vp,
181            register struct SimpleLocks *alp, int onlymine, int clid)
182 {
183     register struct SimpleLocks *slp;
184 #if     defined(AFS_SUN5_ENV)
185     register proc_t *procp = ttoproc(curthread);
186 #else
187 #if !defined(AFS_AIX41_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_SGI65_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
188 #ifdef AFS_SGI64_ENV
189     afs_proc_t *procp = curprocp;
190 #elif defined(UKERNEL)
191     afs_proc_t *procp = get_user_struct()->u_procp;
192 #else
193     afs_proc_t *procp = u.u_procp;
194 #endif /* AFS_SGI64_ENV */
195 #endif
196 #endif
197
198     if (alp) {
199 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
200         if (flock1->l_sysid != alp->sysid) {
201             return 1;
202         }
203 #endif
204         if ((flock1->l_pid == alp->pid) ||
205 #if defined(AFS_AIX41_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_HPUX_ENV)
206             (!onlymine && (flock1->l_pid == getppid()))
207 #else
208 #if defined(AFS_SGI65_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
209             /* XXX check this. used to be *only* irix for some reason. */
210             (!onlymine && (flock1->l_pid == clid))
211 #else
212             (!onlymine && (flock1->l_pid == procp->p_ppid))
213 #endif
214 #endif
215             ) {
216             return 0;
217         }
218         return 1;
219     }
220
221     for (slp = vp->slocks; slp; slp = slp->next) {
222 #if defined(AFS_HAVE_FLOCK_SYSID)
223         if (flock1->l_sysid != slp->sysid) {
224             continue;
225         }
226 #endif
227         if (flock1->l_pid == slp->pid) {
228             return 0;
229         }
230     }
231     return (1);                 /* failure */
232 }
233
234
235 /* we don't send multiple read flocks to the server, but rather just count
236     them up ourselves.  Of course, multiple write locks are incompatible.
237     
238     Note that we should always try to release a lock, even if we have
239     a network problem sending the release command through, since often
240     a lock is released on a close call, when the user can't retry anyway.
241     
242     After we remove it from our structure, the lock will no longer be
243     kept alive, and the server should time it out within a few minutes.
244     
245     94.04.13 add "force" parameter.  If a child explicitly unlocks a
246     file, I guess we'll permit it.  however, we don't want simple,
247     innocent closes by children to unlock files in the parent process.
248
249     If called when disconnected support is unabled, the discon_lock must
250     be held
251 */
252 /* clid - nonzero on sgi sunos osf1 only */
253 int
254 HandleFlock(register struct vcache *avc, int acom, struct vrequest *areq,
255             pid_t clid, int onlymine)
256 {
257     struct afs_conn *tc;
258     struct SimpleLocks *slp, *tlp, **slpp;
259     afs_int32 code;
260     struct AFSVolSync tsync;
261     afs_int32 lockType;
262     struct AFS_FLOCK flock;
263     XSTATS_DECLS;
264     AFS_STATCNT(HandleFlock);
265     code = 0;                   /* default when we don't make any network calls */
266     lockIdSet(&flock, NULL, clid);
267
268 #if defined(AFS_SGI_ENV)
269     osi_Assert(valusema(&avc->vc_rwlock) <= 0);
270     osi_Assert(OSI_GET_LOCKID() == avc->vc_rwlockid);
271 #endif
272     ObtainWriteLock(&avc->lock, 118);
273     if (acom & LOCK_UN) {
274
275 /* defect 3083 */
276
277 #ifdef AFS_AIX_ENV
278         /* If the lock is held exclusive, then only the owning process
279          * or a child can unlock it. Use pid and ppid because they are
280          * unique identifiers.
281          */
282         if ((avc->flockCount < 0) && (getpid() != avc->ownslock)) {
283 #ifdef  AFS_AIX41_ENV
284             if (onlymine || (getppid() != avc->ownslock)) {
285 #else
286             if (onlymine || (u.u_procp->p_ppid != avc->ownslock)) {
287 #endif
288                 ReleaseWriteLock(&avc->lock);
289                 return 0;
290             }
291         }
292 #endif
293         if (lockIdcmp2(&flock, avc, NULL, onlymine, clid)) {
294             ReleaseWriteLock(&avc->lock);
295             return 0;
296         }
297 #ifdef AFS_AIX_ENV
298         avc->ownslock = 0;
299 #endif
300         if (avc->flockCount == 0) {
301             ReleaseWriteLock(&avc->lock);
302             return 0 /*ENOTTY*/;
303             /* no lock held */
304         }
305         /* unlock the lock */
306         if (avc->flockCount > 0) {
307             slpp = &avc->slocks;
308             for (slp = *slpp; slp;) {
309                 if (!lockIdcmp2(&flock, avc, slp, onlymine, clid)) {
310                     avc->flockCount--;
311                     tlp = *slpp = slp->next;
312                     osi_FreeSmallSpace(slp);
313                     slp = tlp;
314                 } else {
315                     slpp = &slp->next;
316                     slp = *slpp;
317                 }
318             }
319         } else if (avc->flockCount == -1) {
320             afs_StoreAllSegments(avc, areq, AFS_ASYNC); /* fsync file early */
321             avc->flockCount = 0;
322             /* And remove the (only) exclusive lock entry from the list... */
323             osi_FreeSmallSpace(avc->slocks);
324             avc->slocks = 0;
325         }
326         if (avc->flockCount == 0) {
327             if (!AFS_IS_DISCONNECTED) {
328                 do {
329                     tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
330                     if (tc) {
331                         XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
332                         RX_AFS_GUNLOCK();
333                         code = RXAFS_ReleaseLock(tc->id, (struct AFSFid *)
334                                                  &avc->f.fid.Fid, &tsync);
335                         RX_AFS_GLOCK();
336                         XSTATS_END_TIME;
337                     } else
338                     code = -1;
339                 } while (afs_Analyze
340                          (tc, code, &avc->f.fid, areq,
341                           AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL));
342             } else {
343                 /*printf("Network is dooooooowwwwwwwnnnnnnn\n");*/
344                code = ENETDOWN;
345             }
346         }
347     } else {
348         while (1) {             /* set a new lock */
349             /*
350              * Upgrading from shared locks to Exclusive and vice versa
351              * is a bit tricky and we don't really support it yet. But
352              * we try to support the common used one which is upgrade
353              * a shared lock to an exclusive for the same process...
354              */
355             if ((avc->flockCount > 0 && (acom & LOCK_EX))
356                 || (avc->flockCount == -1 && (acom & LOCK_SH))) {
357                 /*
358                  * Upgrading from shared locks to an exclusive one:
359                  * For now if all the shared locks belong to the
360                  * same process then we unlock them on the server
361                  * and proceed with the upgrade.  Unless we change the
362                  * server's locking interface impl we prohibit from
363                  * unlocking other processes's shared locks...
364                  * Upgrading from an exclusive lock to a shared one:
365                  * Again only allowed to be done by the same process.
366                  */
367                 slpp = &avc->slocks;
368                 for (slp = *slpp; slp;) {
369                     if (!lockIdcmp2
370                         (&flock, avc, slp, 1 /*!onlymine */ , clid)) {
371                         if (acom & LOCK_EX)
372                             avc->flockCount--;
373                         else
374                             avc->flockCount = 0;
375                         tlp = *slpp = slp->next;
376                         osi_FreeSmallSpace(slp);
377                         slp = tlp;
378                     } else {
379                         code = EWOULDBLOCK;
380                         slpp = &slp->next;
381                         slp = *slpp;
382                     }
383                 }
384                 if (!code && avc->flockCount == 0) {
385                     if (!AFS_IS_DISCONNECTED) {
386                         do {
387                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
388                             if (tc) {
389                                 XSTATS_START_TIME
390                                     (AFS_STATS_FS_RPCIDX_RELEASELOCK);
391                                 RX_AFS_GUNLOCK();
392                                 code =
393                                     RXAFS_ReleaseLock(tc->id,
394                                                       (struct AFSFid *)&avc->
395                                                       f.fid.Fid, &tsync);
396                                 RX_AFS_GLOCK();
397                                XSTATS_END_TIME;
398                             } else
399                                 code = -1;
400                         } while (afs_Analyze
401                                  (tc, code, &avc->f.fid, areq,
402                                   AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK,
403                                   NULL));
404                     }
405                 }
406             } else if (avc->flockCount == -1 && (acom & LOCK_EX)) {
407                 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
408                     code = EWOULDBLOCK;
409                 } else
410                     code = 0;
411             }
412             if (code == 0) {
413                 /* compatible here, decide if needs to go to file server.  If
414                  * we've already got the file locked (and thus read-locked, since
415                  * we've already checked for compatibility), we shouldn't send
416                  * the call through to the server again */
417                 if (avc->flockCount == 0) {
418                     /* we're the first on our block, send the call through */
419                     lockType = ((acom & LOCK_EX) ? LockWrite : LockRead);
420                     if (!AFS_IS_DISCONNECTED) {
421                         do {
422                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
423                             if (tc) {
424                                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
425                                 RX_AFS_GUNLOCK();
426                                 code = RXAFS_SetLock(tc->id, (struct AFSFid *)
427                                                      &avc->f.fid.Fid, lockType,
428                                                      &tsync);
429                                 RX_AFS_GLOCK();
430                                 XSTATS_END_TIME;
431                             } else
432                                 code = -1;
433                         } while (afs_Analyze
434                                  (tc, code, &avc->f.fid, areq,
435                                   AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK,
436                                   NULL));
437                     } else
438                         /* XXX - Should probably try and log this when we're
439                          * XXX - running with logging enabled. But it's horrid
440                          */
441                         code = 0; /* pretend we worked - ick!!! */
442                 } else
443                     code = 0;   /* otherwise, pretend things worked */
444             }
445             if (code == 0) {
446                 slp = (struct SimpleLocks *)
447                     osi_AllocSmallSpace(sizeof(struct SimpleLocks));
448                 if (acom & LOCK_EX) {
449
450 /* defect 3083 */
451
452 #ifdef AFS_AIX_ENV
453                     /* Record unique id of process owning exclusive lock. */
454                     avc->ownslock = getpid();
455 #endif
456
457                     slp->type = LockWrite;
458                     slp->next = NULL;
459                     avc->slocks = slp;
460                     avc->flockCount = -1;
461                 } else {
462                     slp->type = LockRead;
463                     slp->next = avc->slocks;
464                     avc->slocks = slp;
465                     avc->flockCount++;
466                 }
467
468                 lockIdSet(&flock, slp, clid);
469                 break;
470             }
471             /* now, if we got EWOULDBLOCK, and we're supposed to wait, we do */
472             if (((code == EWOULDBLOCK) || (code == EAGAIN) || 
473                  (code == UAEWOULDBLOCK) || (code == UAEAGAIN))
474                 && !(acom & LOCK_NB)) {
475                 /* sleep for a second, allowing interrupts */
476                 ReleaseWriteLock(&avc->lock);
477 #if defined(AFS_SGI_ENV)
478                 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
479 #endif
480                 code = afs_osi_Wait(1000, NULL, 1);
481 #if defined(AFS_SGI_ENV)
482                 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
483 #endif
484                 ObtainWriteLock(&avc->lock, 120);
485                 if (code) {
486                     code = EINTR;       /* return this if ^C typed */
487                     break;
488                 }
489             } else
490                 break;
491         }                       /* while loop */
492     }
493     ReleaseWriteLock(&avc->lock);
494     code = afs_CheckCode(code, areq, 1);        /* defeat a buggy AIX optimization */
495     return code;
496 }
497
498
499 /* warn a user that a lock has been ignored */
500 afs_int32 lastWarnTime = 0;     /* this is used elsewhere */
501 static afs_int32 lastWarnPid = 0;
502 static void
503 DoLockWarning(afs_ucred_t * acred)
504 {
505     register afs_int32 now;
506     pid_t pid = MyPidxx2Pid(MyPidxx);
507     char *procname;
508
509     now = osi_Time();
510
511     AFS_STATCNT(DoLockWarning);
512     /* check if we've already warned this user recently */
513     if (!((now < lastWarnTime + 120) && (lastWarnPid == pid))) {
514         procname = afs_osi_Alloc(256);
515
516         if (!procname)
517             return;
518
519         /* Copies process name to allocated procname, see osi_machdeps for details of macro */
520         osi_procname(procname, 256);
521         procname[255] = '\0';
522
523         /* otherwise, it is time to nag the user */
524         lastWarnTime = now;
525         lastWarnPid = pid;
526 #ifdef AFS_LINUX26_ENV
527         afs_warnuser
528             ("afs: byte-range locks only enforced for processes on this machine (pid %d (%s), user %ld).\n", pid, procname, afs_cr_uid(acred));
529 #else
530         afs_warnuser
531             ("afs: byte-range lock/unlock ignored; make sure no one else is running this program (pid %d (%s), user %ld).\n", pid, procname, afs_cr_uid(acred));
532 #endif
533         afs_osi_Free(procname, 256);
534     }
535     return;
536 }
537
538
539 #if defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
540 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
541                 afs_ucred_t * acred, pid_t clid)
542 #else
543 u_int clid = 0;
544 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
545                 afs_ucred_t * acred)
546 #endif
547 {
548     struct vrequest treq;
549     afs_int32 code;
550     struct afs_fakestat_state fakestate;
551
552     AFS_STATCNT(afs_lockctl);
553     if ((code = afs_InitReq(&treq, acred)))
554         return code;
555     afs_InitFakeStat(&fakestate);
556
557     AFS_DISCON_LOCK();
558
559     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
560     if (code) {
561         goto done;
562     }
563 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
564     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
565 #else
566     if (acmd == F_GETLK) {
567 #endif
568         if (af->l_type == F_UNLCK) {
569             code = 0;
570             goto done;
571         }
572         code = HandleGetLock(avc, af, &treq, clid);
573         code = afs_CheckCode(code, &treq, 2);   /* defeat buggy AIX optimz */
574         goto done;
575     } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
576 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
577                || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
578 #else
579         ) {
580 #endif
581     /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU */
582     if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7fffffffffffffffLL))
583         af->l_len = 0;
584     /* next line makes byte range locks always succeed,
585      * even when they should block */
586     if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
587         DoLockWarning(acred);
588         code = 0;
589         goto done;
590     }
591     /* otherwise we can turn this into a whole-file flock */
592     if (af->l_type == F_RDLCK)
593         code = LOCK_SH;
594     else if (af->l_type == F_WRLCK)
595         code = LOCK_EX;
596     else if (af->l_type == F_UNLCK)
597         code = LOCK_UN;
598     else {
599         afs_PutFakeStat(&fakestate);
600         return EINVAL;          /* unknown lock type */
601     }
602     if (((acmd == F_SETLK)
603 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
604          || (acmd == F_RSETLK)
605 #endif
606         ) && code != LOCK_UN)
607         code |= LOCK_NB;        /* non-blocking, s.v.p. */
608 #if defined(AFS_DARWIN_ENV)
609     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
610 #elif defined(AFS_SGI_ENV)
611     AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
612     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
613     AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
614 #else
615     code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
616 #endif
617     code = afs_CheckCode(code, &treq, 3);       /* defeat AIX -O bug */
618     goto done;
619     }
620     code = EINVAL;
621 done:
622     afs_PutFakeStat(&fakestate);
623     AFS_DISCON_UNLOCK();
624     return code;
625 }
626
627
628 /*
629  * Get a description of the first lock which would
630  * block the lock specified.  If the specified lock
631  * would succeed, fill in the lock structure with 'F_UNLCK'.
632  *
633  * To do that, we have to ask the server for the lock
634  * count if:
635  *    1. The file is not locked by this machine.
636  *    2. Asking for write lock, and only the current
637  *       PID has the file read locked.
638  */
639 static int
640 HandleGetLock(register struct vcache *avc, register struct AFS_FLOCK *af,
641               register struct vrequest *areq, int clid)
642 {
643     register afs_int32 code;
644     struct AFS_FLOCK flock;
645
646     lockIdSet(&flock, NULL, clid);
647
648     ObtainWriteLock(&avc->lock, 122);
649     if (avc->flockCount == 0) {
650         /*
651          * We don't know ourselves, so ask the server. Unfortunately, we
652          * don't know the pid.  Not even the server knows the pid.  Besides,
653          * the process with the lock is on another machine
654          */
655         code = GetFlockCount(avc, areq);
656         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
657             af->l_type = F_UNLCK;
658             goto unlck_leave;
659         }
660         if (code > 0)
661             af->l_type = F_RDLCK;
662         else
663             af->l_type = F_WRLCK;
664
665         af->l_pid = 0;
666 #if defined(AFS_HAVE_FLOCK_SYSID)
667         af->l_sysid = 0;
668 #endif
669         goto done;
670     }
671
672     if (af->l_type == F_RDLCK) {
673         /*
674          * We want a read lock.  If there are only
675          * read locks, or we are the one with the
676          * write lock, say it is unlocked.
677          */
678         if (avc->flockCount > 0 ||      /* only read locks */
679             !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
680             af->l_type = F_UNLCK;
681             goto unlck_leave;
682         }
683
684         /* one write lock, but who? */
685         af->l_type = F_WRLCK;   /* not us, so lock would block */
686         if (avc->slocks) {      /* we know who, so tell */
687             af->l_pid = avc->slocks->pid;
688 #if defined(AFS_HAVE_FLOCK_SYSID)
689             af->l_sysid = avc->slocks->sysid;
690 #endif
691         } else {
692             af->l_pid = 0;      /* XXX can't happen?? */
693 #if defined(AFS_HAVE_FLOCK_SYSID)
694             af->l_sysid = 0;
695 #endif
696         }
697         goto done;
698     }
699
700     /*
701      * Ok, we want a write lock.  If there is a write lock
702      * already, and it is not this process, we fail.
703      */
704     if (avc->flockCount < 0) {
705         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
706             af->l_type = F_WRLCK;
707             if (avc->slocks) {
708                 af->l_pid = avc->slocks->pid;
709 #if defined(AFS_HAVE_FLOCK_SYSID)
710                 af->l_sysid = avc->slocks->sysid;
711 #endif
712             } else {
713                 af->l_pid = 0;  /* XXX can't happen?? */
714 #if defined(AFS_HAVE_FLOCK_SYSID)
715                 af->l_sysid = 0;
716 #endif
717             }
718             goto done;
719         }
720         /* we are the one with the write lock */
721         af->l_type = F_UNLCK;
722         goto unlck_leave;
723     }
724
725     /*
726      * Want a write lock, and we know there are read locks.
727      * If there is more than one, or it isn't us, we cannot lock.
728      */
729     if ((avc->flockCount > 1)
730         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
731         struct SimpleLocks *slp;
732
733         af->l_type = F_RDLCK;
734         af->l_pid = 0;
735 #if defined(AFS_HAVE_FLOCK_SYSID)
736         af->l_sysid = 0;
737 #endif
738         /* find a pid that isn't our own */
739         for (slp = avc->slocks; slp; slp = slp->next) {
740             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
741                 af->l_pid = slp->pid;
742 #if defined(AFS_HAVE_FLOCK_SYSID)
743                 af->l_sysid = avc->slocks->sysid;
744 #endif
745                 break;
746             }
747         }
748         goto done;
749     }
750
751     /*
752      * Ok, we want a write lock.  If there is a write lock
753      * already, and it is not this process, we fail.
754      */
755     if (avc->flockCount < 0) {
756         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
757             af->l_type = F_WRLCK;
758             if (avc->slocks) {
759                 af->l_pid = avc->slocks->pid;
760 #if defined(AFS_HAVE_FLOCK_SYSID)
761                 af->l_sysid = avc->slocks->sysid;
762 #endif
763             } else {
764                 af->l_pid = 0;  /* XXX can't happen?? */
765 #if defined(AFS_HAVE_FLOCK_SYSID)
766                 af->l_sysid = 0;
767 #endif
768             }
769             goto done;
770         }
771         /* we are the one with the write lock */
772         af->l_type = F_UNLCK;
773         goto unlck_leave;
774     }
775
776     /*
777      * Want a write lock, and we know there are read locks.
778      * If there is more than one, or it isn't us, we cannot lock.
779      */
780     if ((avc->flockCount > 1)
781         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
782         struct SimpleLocks *slp;
783         af->l_type = F_RDLCK;
784         af->l_pid = 0;
785 #if defined(AFS_HAVE_FLOCK_SYSID)
786         af->l_sysid = 0;
787 #endif
788         /* find a pid that isn't our own */
789         for (slp = avc->slocks; slp; slp = slp->next) {
790             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
791                 af->l_pid = slp->pid;
792 #if defined(AFS_HAVE_FLOCK_SYSID)
793                 af->l_sysid = avc->slocks->sysid;
794 #endif
795                 break;
796             }
797         }
798         goto done;
799     }
800
801     /*
802      * Want a write lock, and there is just one read lock, and it
803      * is this process with a read lock.  Ask the server if there
804      * are any more processes with the file locked.
805      */
806     code = GetFlockCount(avc, areq);
807     if (code == 0 || code == 1) {
808         af->l_type = F_UNLCK;
809         goto unlck_leave;
810     }
811     if (code > 0)
812         af->l_type = F_RDLCK;
813     else
814         af->l_type = F_WRLCK;
815     af->l_pid = 0;
816 #if defined(AFS_HAVE_FLOCK_SYSID)
817     af->l_sysid = 0;
818 #endif
819
820   done:
821     af->l_whence = 0;
822     af->l_start = 0;
823     af->l_len = 0;              /* to end of file */
824
825   unlck_leave:
826     ReleaseWriteLock(&avc->lock);
827     return 0;
828 }
829
830 /* Get the 'flock' count from the server.  This comes back in a 'spare'
831  * field from a GetStatus RPC.  If we have any problems with the RPC,
832  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
833  * the spare field will be a zero, saying the file is unlocked.  This is
834  * OK, as a further 'lock' request will do the right thing.
835  */
836 static int
837 GetFlockCount(struct vcache *avc, struct vrequest *areq)
838 {
839     register struct afs_conn *tc;
840     register afs_int32 code;
841     struct AFSFetchStatus OutStatus;
842     struct AFSCallBack CallBack;
843     struct AFSVolSync tsync;
844     int temp;
845     XSTATS_DECLS;
846     temp = areq->flags & O_NONBLOCK;
847     areq->flags |= O_NONBLOCK;
848
849     /* If we're disconnected, lie and say that we've got no locks. Ick */
850     if (AFS_IS_DISCONNECTED)
851         return 0;
852         
853     do {
854         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
855         if (tc) {
856             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
857             RX_AFS_GUNLOCK();
858             code =
859                 RXAFS_FetchStatus(tc->id, (struct AFSFid *)&avc->f.fid.Fid,
860                                   &OutStatus, &CallBack, &tsync);
861             RX_AFS_GLOCK();
862             XSTATS_END_TIME;
863         } else
864             code = -1;
865     } while (afs_Analyze
866              (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
867               SHARED_LOCK, NULL));
868
869     if (temp)
870         areq->flags &= ~O_NONBLOCK;
871
872     if (code) {
873         return (0);             /* failed, say it is 'unlocked' */
874     } else {
875         return ((int)OutStatus.lockCount);
876     }
877 }
878