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