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