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