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