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