7143181c86ea6fd95ba59b3c1616e3ae582e2be1
[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(void);
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 void
502 DoLockWarning(void)
503 {
504     register afs_int32 now;
505     now = osi_Time();
506
507     AFS_STATCNT(DoLockWarning);
508     /* check if we've already warned someone recently */
509     if (now < lastWarnTime + 120)
510         return;
511
512     /* otherwise, it is time to nag the user */
513     lastWarnTime = now;
514 #ifdef AFS_LINUX26_ENV
515     afs_warn
516         ("afs: byte-range locks only enforced for processes on this machine.\n");
517 #else
518     afs_warn
519         ("afs: byte-range lock/unlock ignored; make sure no one else is running this program.\n");
520 #endif
521 }
522
523
524 #if defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
525 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
526                 afs_ucred_t * acred, pid_t clid)
527 #else
528 u_int clid = 0;
529 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
530                 afs_ucred_t * acred)
531 #endif
532 {
533     struct vrequest treq;
534     afs_int32 code;
535     struct afs_fakestat_state fakestate;
536
537     AFS_STATCNT(afs_lockctl);
538     if ((code = afs_InitReq(&treq, acred)))
539         return code;
540     afs_InitFakeStat(&fakestate);
541
542     AFS_DISCON_LOCK();
543
544     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
545     if (code) {
546         goto done;
547     }
548 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
549     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
550 #else
551     if (acmd == F_GETLK) {
552 #endif
553         if (af->l_type == F_UNLCK) {
554             code = 0;
555             goto done;
556         }
557         code = HandleGetLock(avc, af, &treq, clid);
558         code = afs_CheckCode(code, &treq, 2);   /* defeat buggy AIX optimz */
559         goto done;
560     } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
561 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
562                || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
563 #else
564         ) {
565 #endif
566     /* this next check is safer when left out, but more applications work
567      * with it in.  However, they fail in race conditions.  The question is
568      * what to do for people who don't have source to their application;
569      * this way at least, they can get work done */
570 #ifdef AFS_LINUX24_ENV
571     if (af->l_len == OFFSET_MAX)
572         af->l_len = 0;          /* since some systems indicate it as EOF */
573 #else
574     if (af->l_len == 0x7fffffff)
575         af->l_len = 0;          /* since some systems indicate it as EOF */
576 #ifdef AFS_LINUX_64BIT_KERNEL
577     if (af->l_len == LONG_MAX)
578         af->l_len = 0;          /* since some systems indicate it as EOF */
579 #endif
580 #endif
581     /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU; bottom 32 bits
582      * sometimes get masked off by OS */
583     if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7ffffffffffffffeLL))
584         af->l_len = 0;
585     /* next line makes byte range locks always succeed,
586      * even when they should block */
587     if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
588         DoLockWarning();
589         code = 0;
590         goto done;
591     }
592     /* otherwise we can turn this into a whole-file flock */
593     if (af->l_type == F_RDLCK)
594         code = LOCK_SH;
595     else if (af->l_type == F_WRLCK)
596         code = LOCK_EX;
597     else if (af->l_type == F_UNLCK)
598         code = LOCK_UN;
599     else {
600         afs_PutFakeStat(&fakestate);
601         return EINVAL;          /* unknown lock type */
602     }
603     if (((acmd == F_SETLK)
604 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
605          || (acmd == F_RSETLK)
606 #endif
607         ) && code != LOCK_UN)
608         code |= LOCK_NB;        /* non-blocking, s.v.p. */
609 #if defined(AFS_DARWIN_ENV)
610     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
611 #elif defined(AFS_SGI_ENV)
612     AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
613     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
614     AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
615 #else
616     code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
617 #endif
618     code = afs_CheckCode(code, &treq, 3);       /* defeat AIX -O bug */
619     goto done;
620     }
621     code = EINVAL;
622 done:
623     afs_PutFakeStat(&fakestate);
624     AFS_DISCON_UNLOCK();
625     return code;
626 }
627
628
629 /*
630  * Get a description of the first lock which would
631  * block the lock specified.  If the specified lock
632  * would succeed, fill in the lock structure with 'F_UNLCK'.
633  *
634  * To do that, we have to ask the server for the lock
635  * count if:
636  *    1. The file is not locked by this machine.
637  *    2. Asking for write lock, and only the current
638  *       PID has the file read locked.
639  */
640 static int
641 HandleGetLock(register struct vcache *avc, register struct AFS_FLOCK *af,
642               register struct vrequest *areq, int clid)
643 {
644     register afs_int32 code;
645     struct AFS_FLOCK flock;
646
647     lockIdSet(&flock, NULL, clid);
648
649     ObtainWriteLock(&avc->lock, 122);
650     if (avc->flockCount == 0) {
651         /*
652          * We don't know ourselves, so ask the server. Unfortunately, we
653          * don't know the pid.  Not even the server knows the pid.  Besides,
654          * the process with the lock is on another machine
655          */
656         code = GetFlockCount(avc, areq);
657         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
658             af->l_type = F_UNLCK;
659             goto unlck_leave;
660         }
661         if (code > 0)
662             af->l_type = F_RDLCK;
663         else
664             af->l_type = F_WRLCK;
665
666         af->l_pid = 0;
667 #if defined(AFS_HAVE_FLOCK_SYSID)
668         af->l_sysid = 0;
669 #endif
670         goto done;
671     }
672
673     if (af->l_type == F_RDLCK) {
674         /*
675          * We want a read lock.  If there are only
676          * read locks, or we are the one with the
677          * write lock, say it is unlocked.
678          */
679         if (avc->flockCount > 0 ||      /* only read locks */
680             !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
681             af->l_type = F_UNLCK;
682             goto unlck_leave;
683         }
684
685         /* one write lock, but who? */
686         af->l_type = F_WRLCK;   /* not us, so lock would block */
687         if (avc->slocks) {      /* we know who, so tell */
688             af->l_pid = avc->slocks->pid;
689 #if defined(AFS_HAVE_FLOCK_SYSID)
690             af->l_sysid = avc->slocks->sysid;
691 #endif
692         } else {
693             af->l_pid = 0;      /* XXX can't happen?? */
694 #if defined(AFS_HAVE_FLOCK_SYSID)
695             af->l_sysid = 0;
696 #endif
697         }
698         goto done;
699     }
700
701     /*
702      * Ok, we want a write lock.  If there is a write lock
703      * already, and it is not this process, we fail.
704      */
705     if (avc->flockCount < 0) {
706         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
707             af->l_type = F_WRLCK;
708             if (avc->slocks) {
709                 af->l_pid = avc->slocks->pid;
710 #if defined(AFS_HAVE_FLOCK_SYSID)
711                 af->l_sysid = avc->slocks->sysid;
712 #endif
713             } else {
714                 af->l_pid = 0;  /* XXX can't happen?? */
715 #if defined(AFS_HAVE_FLOCK_SYSID)
716                 af->l_sysid = 0;
717 #endif
718             }
719             goto done;
720         }
721         /* we are the one with the write lock */
722         af->l_type = F_UNLCK;
723         goto unlck_leave;
724     }
725
726     /*
727      * Want a write lock, and we know there are read locks.
728      * If there is more than one, or it isn't us, we cannot lock.
729      */
730     if ((avc->flockCount > 1)
731         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
732         struct SimpleLocks *slp;
733
734         af->l_type = F_RDLCK;
735         af->l_pid = 0;
736 #if defined(AFS_HAVE_FLOCK_SYSID)
737         af->l_sysid = 0;
738 #endif
739         /* find a pid that isn't our own */
740         for (slp = avc->slocks; slp; slp = slp->next) {
741             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
742                 af->l_pid = slp->pid;
743 #if defined(AFS_HAVE_FLOCK_SYSID)
744                 af->l_sysid = avc->slocks->sysid;
745 #endif
746                 break;
747             }
748         }
749         goto done;
750     }
751
752     /*
753      * Ok, we want a write lock.  If there is a write lock
754      * already, and it is not this process, we fail.
755      */
756     if (avc->flockCount < 0) {
757         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
758             af->l_type = F_WRLCK;
759             if (avc->slocks) {
760                 af->l_pid = avc->slocks->pid;
761 #if defined(AFS_HAVE_FLOCK_SYSID)
762                 af->l_sysid = avc->slocks->sysid;
763 #endif
764             } else {
765                 af->l_pid = 0;  /* XXX can't happen?? */
766 #if defined(AFS_HAVE_FLOCK_SYSID)
767                 af->l_sysid = 0;
768 #endif
769             }
770             goto done;
771         }
772         /* we are the one with the write lock */
773         af->l_type = F_UNLCK;
774         goto unlck_leave;
775     }
776
777     /*
778      * Want a write lock, and we know there are read locks.
779      * If there is more than one, or it isn't us, we cannot lock.
780      */
781     if ((avc->flockCount > 1)
782         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
783         struct SimpleLocks *slp;
784         af->l_type = F_RDLCK;
785         af->l_pid = 0;
786 #if defined(AFS_HAVE_FLOCK_SYSID)
787         af->l_sysid = 0;
788 #endif
789         /* find a pid that isn't our own */
790         for (slp = avc->slocks; slp; slp = slp->next) {
791             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
792                 af->l_pid = slp->pid;
793 #if defined(AFS_HAVE_FLOCK_SYSID)
794                 af->l_sysid = avc->slocks->sysid;
795 #endif
796                 break;
797             }
798         }
799         goto done;
800     }
801
802     /*
803      * Want a write lock, and there is just one read lock, and it
804      * is this process with a read lock.  Ask the server if there
805      * are any more processes with the file locked.
806      */
807     code = GetFlockCount(avc, areq);
808     if (code == 0 || code == 1) {
809         af->l_type = F_UNLCK;
810         goto unlck_leave;
811     }
812     if (code > 0)
813         af->l_type = F_RDLCK;
814     else
815         af->l_type = F_WRLCK;
816     af->l_pid = 0;
817 #if defined(AFS_HAVE_FLOCK_SYSID)
818     af->l_sysid = 0;
819 #endif
820
821   done:
822     af->l_whence = 0;
823     af->l_start = 0;
824     af->l_len = 0;              /* to end of file */
825
826   unlck_leave:
827     ReleaseWriteLock(&avc->lock);
828     return 0;
829 }
830
831 /* Get the 'flock' count from the server.  This comes back in a 'spare'
832  * field from a GetStatus RPC.  If we have any problems with the RPC,
833  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
834  * the spare field will be a zero, saying the file is unlocked.  This is
835  * OK, as a further 'lock' request will do the right thing.
836  */
837 static int
838 GetFlockCount(struct vcache *avc, struct vrequest *areq)
839 {
840     register struct afs_conn *tc;
841     register afs_int32 code;
842     struct AFSFetchStatus OutStatus;
843     struct AFSCallBack CallBack;
844     struct AFSVolSync tsync;
845     int temp;
846     XSTATS_DECLS;
847     temp = areq->flags & O_NONBLOCK;
848     areq->flags |= O_NONBLOCK;
849
850     /* If we're disconnected, lie and say that we've got no locks. Ick */
851     if (AFS_IS_DISCONNECTED)
852         return 0;
853         
854     do {
855         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
856         if (tc) {
857             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
858             RX_AFS_GUNLOCK();
859             code =
860                 RXAFS_FetchStatus(tc->id, (struct AFSFid *)&avc->f.fid.Fid,
861                                   &OutStatus, &CallBack, &tsync);
862             RX_AFS_GLOCK();
863             XSTATS_END_TIME;
864         } else
865             code = -1;
866     } while (afs_Analyze
867              (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
868               SHARED_LOCK, NULL));
869
870     if (temp)
871         areq->flags &= ~O_NONBLOCK;
872
873     if (code) {
874         return (0);             /* failed, say it is 'unlocked' */
875     } else {
876         return ((int)OutStatus.lockCount);
877     }
878 }
879