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