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