linux-byte-range-locks-sensibility-20090526
[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     If called when disconnected support is unabled, the discon_lock must
224     be held
225 */
226 /* clid - nonzero on sgi sunos osf1 only */
227 int
228 HandleFlock(register struct vcache *avc, int acom, struct vrequest *areq,
229             pid_t clid, int onlymine)
230 {
231     struct afs_conn *tc;
232     struct SimpleLocks *slp, *tlp, **slpp;
233     afs_int32 code;
234     struct AFSVolSync tsync;
235     afs_int32 lockType;
236     struct AFS_FLOCK flock;
237     XSTATS_DECLS;
238     AFS_STATCNT(HandleFlock);
239     code = 0;                   /* default when we don't make any network calls */
240     lockIdSet(&flock, NULL, clid);
241
242 #if defined(AFS_SGI_ENV)
243     osi_Assert(valusema(&avc->vc_rwlock) <= 0);
244     osi_Assert(OSI_GET_LOCKID() == avc->vc_rwlockid);
245 #endif
246     ObtainWriteLock(&avc->lock, 118);
247     if (acom & LOCK_UN) {
248
249 /* defect 3083 */
250
251 #ifdef AFS_AIX_ENV
252         /* If the lock is held exclusive, then only the owning process
253          * or a child can unlock it. Use pid and ppid because they are
254          * unique identifiers.
255          */
256         if ((avc->flockCount < 0) && (getpid() != avc->ownslock)) {
257 #ifdef  AFS_AIX41_ENV
258             if (onlymine || (getppid() != avc->ownslock)) {
259 #else
260             if (onlymine || (u.u_procp->p_ppid != avc->ownslock)) {
261 #endif
262                 ReleaseWriteLock(&avc->lock);
263                 return 0;
264             }
265         }
266 #endif
267         if (lockIdcmp2(&flock, avc, NULL, onlymine, clid)) {
268             ReleaseWriteLock(&avc->lock);
269             return 0;
270         }
271 #ifdef AFS_AIX_ENV
272         avc->ownslock = 0;
273 #endif
274         if (avc->flockCount == 0) {
275             ReleaseWriteLock(&avc->lock);
276             return 0 /*ENOTTY*/;
277             /* no lock held */
278         }
279         /* unlock the lock */
280         if (avc->flockCount > 0) {
281             slpp = &avc->slocks;
282             for (slp = *slpp; slp;) {
283                 if (!lockIdcmp2(&flock, avc, slp, onlymine, clid)) {
284                     avc->flockCount--;
285                     tlp = *slpp = slp->next;
286                     osi_FreeSmallSpace(slp);
287                     slp = tlp;
288                 } else {
289                     slpp = &slp->next;
290                     slp = *slpp;
291                 }
292             }
293         } else if (avc->flockCount == -1) {
294             afs_StoreAllSegments(avc, areq, AFS_ASYNC); /* fsync file early */
295             avc->flockCount = 0;
296             /* And remove the (only) exclusive lock entry from the list... */
297             osi_FreeSmallSpace(avc->slocks);
298             avc->slocks = 0;
299         }
300         if (avc->flockCount == 0) {
301             if (!AFS_IS_DISCONNECTED) {
302                 do {
303                     tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
304                     if (tc) {
305                         XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
306                         RX_AFS_GUNLOCK();
307                         code = RXAFS_ReleaseLock(tc->id, (struct AFSFid *)
308                                                  &avc->f.fid.Fid, &tsync);
309                         RX_AFS_GLOCK();
310                         XSTATS_END_TIME;
311                     } else
312                     code = -1;
313                 } while (afs_Analyze
314                          (tc, code, &avc->f.fid, areq,
315                           AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL));
316             } else {
317                 /*printf("Network is dooooooowwwwwwwnnnnnnn\n");*/
318                code = ENETDOWN;
319             }
320         }
321     } else {
322         while (1) {             /* set a new lock */
323             /*
324              * Upgrading from shared locks to Exclusive and vice versa
325              * is a bit tricky and we don't really support it yet. But
326              * we try to support the common used one which is upgrade
327              * a shared lock to an exclusive for the same process...
328              */
329             if ((avc->flockCount > 0 && (acom & LOCK_EX))
330                 || (avc->flockCount == -1 && (acom & LOCK_SH))) {
331                 /*
332                  * Upgrading from shared locks to an exclusive one:
333                  * For now if all the shared locks belong to the
334                  * same process then we unlock them on the server
335                  * and proceed with the upgrade.  Unless we change the
336                  * server's locking interface impl we prohibit from
337                  * unlocking other processes's shared locks...
338                  * Upgrading from an exclusive lock to a shared one:
339                  * Again only allowed to be done by the same process.
340                  */
341                 slpp = &avc->slocks;
342                 for (slp = *slpp; slp;) {
343                     if (!lockIdcmp2
344                         (&flock, avc, slp, 1 /*!onlymine */ , clid)) {
345                         if (acom & LOCK_EX)
346                             avc->flockCount--;
347                         else
348                             avc->flockCount = 0;
349                         tlp = *slpp = slp->next;
350                         osi_FreeSmallSpace(slp);
351                         slp = tlp;
352                     } else {
353                         code = EWOULDBLOCK;
354                         slpp = &slp->next;
355                         slp = *slpp;
356                     }
357                 }
358                 if (!code && avc->flockCount == 0) {
359                     if (!AFS_IS_DISCONNECTED) {
360                         do {
361                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
362                             if (tc) {
363                                 XSTATS_START_TIME
364                                     (AFS_STATS_FS_RPCIDX_RELEASELOCK);
365                                 RX_AFS_GUNLOCK();
366                                 code =
367                                     RXAFS_ReleaseLock(tc->id,
368                                                       (struct AFSFid *)&avc->
369                                                       f.fid.Fid, &tsync);
370                                 RX_AFS_GLOCK();
371                                XSTATS_END_TIME;
372                             } else
373                                 code = -1;
374                         } while (afs_Analyze
375                                  (tc, code, &avc->f.fid, areq,
376                                   AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK,
377                                   NULL));
378                     }
379                 }
380             } else if (avc->flockCount == -1 && (acom & LOCK_EX)) {
381                 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
382                     code = EWOULDBLOCK;
383                 } else
384                     code = 0;
385             }
386             if (code == 0) {
387                 /* compatible here, decide if needs to go to file server.  If
388                  * we've already got the file locked (and thus read-locked, since
389                  * we've already checked for compatibility), we shouldn't send
390                  * the call through to the server again */
391                 if (avc->flockCount == 0) {
392                     /* we're the first on our block, send the call through */
393                     lockType = ((acom & LOCK_EX) ? LockWrite : LockRead);
394                     if (!AFS_IS_DISCONNECTED) {
395                         do {
396                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
397                             if (tc) {
398                                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
399                                 RX_AFS_GUNLOCK();
400                                 code = RXAFS_SetLock(tc->id, (struct AFSFid *)
401                                                      &avc->f.fid.Fid, lockType,
402                                                      &tsync);
403                                 RX_AFS_GLOCK();
404                                 XSTATS_END_TIME;
405                             } else
406                                 code = -1;
407                         } while (afs_Analyze
408                                  (tc, code, &avc->f.fid, areq,
409                                   AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK,
410                                   NULL));
411                     } else
412                         /* XXX - Should probably try and log this when we're
413                          * XXX - running with logging enabled. But it's horrid
414                          */
415                         code = 0; /* pretend we worked - ick!!! */
416                 } else
417                     code = 0;   /* otherwise, pretend things worked */
418             }
419             if (code == 0) {
420                 slp = (struct SimpleLocks *)
421                     osi_AllocSmallSpace(sizeof(struct SimpleLocks));
422                 if (acom & LOCK_EX) {
423
424 /* defect 3083 */
425
426 #ifdef AFS_AIX_ENV
427                     /* Record unique id of process owning exclusive lock. */
428                     avc->ownslock = getpid();
429 #endif
430
431                     slp->type = LockWrite;
432                     slp->next = NULL;
433                     avc->slocks = slp;
434                     avc->flockCount = -1;
435                 } else {
436                     slp->type = LockRead;
437                     slp->next = avc->slocks;
438                     avc->slocks = slp;
439                     avc->flockCount++;
440                 }
441
442                 lockIdSet(&flock, slp, clid);
443                 break;
444             }
445             /* now, if we got EWOULDBLOCK, and we're supposed to wait, we do */
446             if (((code == EWOULDBLOCK) || (code == EAGAIN) || 
447                  (code == UAEWOULDBLOCK) || (code == UAEAGAIN))
448                 && !(acom & LOCK_NB)) {
449                 /* sleep for a second, allowing interrupts */
450                 ReleaseWriteLock(&avc->lock);
451 #if defined(AFS_SGI_ENV)
452                 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
453 #endif
454                 code = afs_osi_Wait(1000, NULL, 1);
455 #if defined(AFS_SGI_ENV)
456                 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
457 #endif
458                 ObtainWriteLock(&avc->lock, 120);
459                 if (code) {
460                     code = EINTR;       /* return this if ^C typed */
461                     break;
462                 }
463             } else
464                 break;
465         }                       /* while loop */
466     }
467     ReleaseWriteLock(&avc->lock);
468     code = afs_CheckCode(code, areq, 1);        /* defeat a buggy AIX optimization */
469     return code;
470 }
471
472
473 /* warn a user that a lock has been ignored */
474 afs_int32 lastWarnTime = 0;     /* this is used elsewhere */
475 static void
476 DoLockWarning(void)
477 {
478     register afs_int32 now;
479     now = osi_Time();
480
481     AFS_STATCNT(DoLockWarning);
482     /* check if we've already warned someone recently */
483     if (now < lastWarnTime + 120)
484         return;
485
486     /* otherwise, it is time to nag the user */
487     lastWarnTime = now;
488 #ifdef AFS_LINUX26_ENV
489     afs_warn
490         ("afs: byte-range locks only enforced for processes on this machine.\n");
491 #else
492     afs_warn
493         ("afs: byte-range lock/unlock ignored; make sure no one else is running this program.\n");
494 #endif
495 }
496
497
498 #ifdef  AFS_OSF_ENV
499 int afs_lockctl(struct vcache * avc, struct eflock * af, int flag,
500                 struct AFS_UCRED * acred, pid_t clid, off_t offset)
501 #elif defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
502 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
503                 struct AFS_UCRED * acred, pid_t clid)
504 #else
505 u_int clid = 0;
506 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
507                 struct AFS_UCRED * acred)
508 #endif
509 {
510     struct vrequest treq;
511     afs_int32 code;
512 #ifdef  AFS_OSF_ENV
513     int acmd = 0;
514 #endif
515     struct afs_fakestat_state fakestate;
516
517     AFS_STATCNT(afs_lockctl);
518     if ((code = afs_InitReq(&treq, acred)))
519         return code;
520     afs_InitFakeStat(&fakestate);
521
522     AFS_DISCON_LOCK();
523
524     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
525     if (code) {
526         goto done;
527     }
528 #ifdef  AFS_OSF_ENV
529     if (flag & VNOFLCK) {
530         code = 0;
531         goto done;
532     }
533     if (flag & CLNFLCK) {
534         acmd = LOCK_UN;
535     } else if ((flag & GETFLCK) || (flag & RGETFLCK)) {
536         acmd = F_GETLK;
537     } else if ((flag & SETFLCK) || (flag & RSETFLCK)) {
538         acmd = F_SETLK;
539     }
540 #endif
541 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
542     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
543 #else
544     if (acmd == F_GETLK) {
545 #endif
546         if (af->l_type == F_UNLCK) {
547             code = 0;
548             goto done;
549         }
550 #ifndef AFS_OSF_ENV             /* getlock is a no-op for osf (for now) */
551         code = HandleGetLock(avc, af, &treq, clid);
552 #endif
553         code = afs_CheckCode(code, &treq, 2);   /* defeat buggy AIX optimz */
554         goto done;
555     } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
556 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
557                || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
558 #else
559         ) {
560 #endif
561     /* this next check is safer when left out, but more applications work
562      * with it in.  However, they fail in race conditions.  The question is
563      * what to do for people who don't have source to their application;
564      * this way at least, they can get work done */
565 #ifdef AFS_LINUX24_ENV
566     if (af->l_len == OFFSET_MAX)
567         af->l_len = 0;          /* since some systems indicate it as EOF */
568 #else
569     if (af->l_len == 0x7fffffff)
570         af->l_len = 0;          /* since some systems indicate it as EOF */
571 #ifdef AFS_LINUX_64BIT_KERNEL
572     if (af->l_len == LONG_MAX)
573         af->l_len = 0;          /* since some systems indicate it as EOF */
574 #endif
575 #endif
576     /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU; bottom 32 bits
577      * sometimes get masked off by OS */
578     if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7ffffffffffffffeLL))
579         af->l_len = 0;
580     /* next line makes byte range locks always succeed,
581      * even when they should block */
582     if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
583         DoLockWarning();
584         code = 0;
585         goto done;
586     }
587     /* otherwise we can turn this into a whole-file flock */
588     if (af->l_type == F_RDLCK)
589         code = LOCK_SH;
590     else if (af->l_type == F_WRLCK)
591         code = LOCK_EX;
592     else if (af->l_type == F_UNLCK)
593         code = LOCK_UN;
594     else {
595         afs_PutFakeStat(&fakestate);
596         return EINVAL;          /* unknown lock type */
597     }
598     if (((acmd == F_SETLK)
599 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
600          || (acmd == F_RSETLK)
601 #endif
602         ) && code != LOCK_UN)
603         code |= LOCK_NB;        /* non-blocking, s.v.p. */
604 #if     defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV)
605     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
606 #elif defined(AFS_SGI_ENV)
607     AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
608     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
609     AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
610 #else
611     code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
612 #endif
613     code = afs_CheckCode(code, &treq, 3);       /* defeat AIX -O bug */
614     goto done;
615     }
616     code = EINVAL;
617 done:
618     afs_PutFakeStat(&fakestate);
619     AFS_DISCON_UNLOCK();
620     return code;
621 }
622
623
624 /*
625  * Get a description of the first lock which would
626  * block the lock specified.  If the specified lock
627  * would succeed, fill in the lock structure with 'F_UNLCK'.
628  *
629  * To do that, we have to ask the server for the lock
630  * count if:
631  *    1. The file is not locked by this machine.
632  *    2. Asking for write lock, and only the current
633  *       PID has the file read locked.
634  */
635 #ifndef AFS_OSF_ENV             /* getlock is a no-op for osf (for now) */
636 static int
637 HandleGetLock(register struct vcache *avc, register struct AFS_FLOCK *af,
638               register struct vrequest *areq, int clid)
639 {
640     register afs_int32 code;
641     struct AFS_FLOCK flock;
642
643     lockIdSet(&flock, NULL, clid);
644
645     ObtainWriteLock(&avc->lock, 122);
646     if (avc->flockCount == 0) {
647         /*
648          * We don't know ourselves, so ask the server. Unfortunately, we
649          * don't know the pid.  Not even the server knows the pid.  Besides,
650          * the process with the lock is on another machine
651          */
652         code = GetFlockCount(avc, areq);
653         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
654             af->l_type = F_UNLCK;
655             goto unlck_leave;
656         }
657         if (code > 0)
658             af->l_type = F_RDLCK;
659         else
660             af->l_type = F_WRLCK;
661
662         af->l_pid = 0;
663 #if defined(AFS_HAVE_FLOCK_SYSID)
664         af->l_sysid = 0;
665 #endif
666         goto done;
667     }
668
669     if (af->l_type == F_RDLCK) {
670         /*
671          * We want a read lock.  If there are only
672          * read locks, or we are the one with the
673          * write lock, say it is unlocked.
674          */
675         if (avc->flockCount > 0 ||      /* only read locks */
676             !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
677             af->l_type = F_UNLCK;
678             goto unlck_leave;
679         }
680
681         /* one write lock, but who? */
682         af->l_type = F_WRLCK;   /* not us, so lock would block */
683         if (avc->slocks) {      /* we know who, so tell */
684             af->l_pid = avc->slocks->pid;
685 #if defined(AFS_HAVE_FLOCK_SYSID)
686             af->l_sysid = avc->slocks->sysid;
687 #endif
688         } else {
689             af->l_pid = 0;      /* XXX can't happen?? */
690 #if defined(AFS_HAVE_FLOCK_SYSID)
691             af->l_sysid = 0;
692 #endif
693         }
694         goto done;
695     }
696
697     /*
698      * Ok, we want a write lock.  If there is a write lock
699      * already, and it is not this process, we fail.
700      */
701     if (avc->flockCount < 0) {
702         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
703             af->l_type = F_WRLCK;
704             if (avc->slocks) {
705                 af->l_pid = avc->slocks->pid;
706 #if defined(AFS_HAVE_FLOCK_SYSID)
707                 af->l_sysid = avc->slocks->sysid;
708 #endif
709             } else {
710                 af->l_pid = 0;  /* XXX can't happen?? */
711 #if defined(AFS_HAVE_FLOCK_SYSID)
712                 af->l_sysid = 0;
713 #endif
714             }
715             goto done;
716         }
717         /* we are the one with the write lock */
718         af->l_type = F_UNLCK;
719         goto unlck_leave;
720     }
721
722     /*
723      * Want a write lock, and we know there are read locks.
724      * If there is more than one, or it isn't us, we cannot lock.
725      */
726     if ((avc->flockCount > 1)
727         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
728         struct SimpleLocks *slp;
729
730         af->l_type = F_RDLCK;
731         af->l_pid = 0;
732 #if defined(AFS_HAVE_FLOCK_SYSID)
733         af->l_sysid = 0;
734 #endif
735         /* find a pid that isn't our own */
736         for (slp = avc->slocks; slp; slp = slp->next) {
737             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
738                 af->l_pid = slp->pid;
739 #if defined(AFS_HAVE_FLOCK_SYSID)
740                 af->l_sysid = avc->slocks->sysid;
741 #endif
742                 break;
743             }
744         }
745         goto done;
746     }
747
748     /*
749      * Ok, we want a write lock.  If there is a write lock
750      * already, and it is not this process, we fail.
751      */
752     if (avc->flockCount < 0) {
753         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
754             af->l_type = F_WRLCK;
755             if (avc->slocks) {
756                 af->l_pid = avc->slocks->pid;
757 #if defined(AFS_HAVE_FLOCK_SYSID)
758                 af->l_sysid = avc->slocks->sysid;
759 #endif
760             } else {
761                 af->l_pid = 0;  /* XXX can't happen?? */
762 #if defined(AFS_HAVE_FLOCK_SYSID)
763                 af->l_sysid = 0;
764 #endif
765             }
766             goto done;
767         }
768         /* we are the one with the write lock */
769         af->l_type = F_UNLCK;
770         goto unlck_leave;
771     }
772
773     /*
774      * Want a write lock, and we know there are read locks.
775      * If there is more than one, or it isn't us, we cannot lock.
776      */
777     if ((avc->flockCount > 1)
778         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
779         struct SimpleLocks *slp;
780         af->l_type = F_RDLCK;
781         af->l_pid = 0;
782 #if defined(AFS_HAVE_FLOCK_SYSID)
783         af->l_sysid = 0;
784 #endif
785         /* find a pid that isn't our own */
786         for (slp = avc->slocks; slp; slp = slp->next) {
787             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
788                 af->l_pid = slp->pid;
789 #if defined(AFS_HAVE_FLOCK_SYSID)
790                 af->l_sysid = avc->slocks->sysid;
791 #endif
792                 break;
793             }
794         }
795         goto done;
796     }
797
798     /*
799      * Want a write lock, and there is just one read lock, and it
800      * is this process with a read lock.  Ask the server if there
801      * are any more processes with the file locked.
802      */
803     code = GetFlockCount(avc, areq);
804     if (code == 0 || code == 1) {
805         af->l_type = F_UNLCK;
806         goto unlck_leave;
807     }
808     if (code > 0)
809         af->l_type = F_RDLCK;
810     else
811         af->l_type = F_WRLCK;
812     af->l_pid = 0;
813 #if defined(AFS_HAVE_FLOCK_SYSID)
814     af->l_sysid = 0;
815 #endif
816
817   done:
818     af->l_whence = 0;
819     af->l_start = 0;
820     af->l_len = 0;              /* to end of file */
821
822   unlck_leave:
823     ReleaseWriteLock(&avc->lock);
824     return 0;
825 }
826
827 /* Get the 'flock' count from the server.  This comes back in a 'spare'
828  * field from a GetStatus RPC.  If we have any problems with the RPC,
829  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
830  * the spare field will be a zero, saying the file is unlocked.  This is
831  * OK, as a further 'lock' request will do the right thing.
832  */
833 static int
834 GetFlockCount(struct vcache *avc, struct vrequest *areq)
835 {
836     register struct afs_conn *tc;
837     register afs_int32 code;
838     struct AFSFetchStatus OutStatus;
839     struct AFSCallBack CallBack;
840     struct AFSVolSync tsync;
841     int temp;
842     XSTATS_DECLS;
843     temp = areq->flags & O_NONBLOCK;
844     areq->flags |= O_NONBLOCK;
845
846     /* If we're disconnected, lie and say that we've got no locks. Ick */
847     if (AFS_IS_DISCONNECTED)
848         return 0;
849         
850     do {
851         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
852         if (tc) {
853             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
854             RX_AFS_GUNLOCK();
855             code =
856                 RXAFS_FetchStatus(tc->id, (struct AFSFid *)&avc->f.fid.Fid,
857                                   &OutStatus, &CallBack, &tsync);
858             RX_AFS_GLOCK();
859             XSTATS_END_TIME;
860         } else
861             code = -1;
862     } while (afs_Analyze
863              (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
864               SHARED_LOCK, NULL));
865
866     if (temp)
867         areq->flags &= ~O_NONBLOCK;
868
869     if (code) {
870         return (0);             /* failed, say it is 'unlocked' */
871     } else {
872         return ((int)OutStatus.lockCount);
873     }
874 }
875 #endif
876
877
878 #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)
879 /* Flock not support on System V systems */
880 #ifdef AFS_OSF_ENV
881 extern struct fileops afs_fileops;
882
883 int
884 afs_xflock(struct proc *p, void *args, int *retval)
885 #else /* AFS_OSF_ENV */
886 int
887 afs_xflock(void)
888 #endif
889 {
890     int code = 0;
891     struct a {
892         int fd;
893         int com;
894     } *uap;
895     struct file *fd;
896     struct vrequest treq;
897     struct vcache *tvc;
898     int flockDone;
899     struct afs_fakestat_state fakestate;
900
901     afs_InitFakeStat(&fakestate);
902     AFS_STATCNT(afs_xflock);
903     flockDone = 0;
904 #ifdef AFS_OSF_ENV
905     uap = (struct a *)args;
906     getf(&fd, uap->fd, FILE_FLAGS_NULL, &u.u_file_state);
907 #else /* AFS_OSF_ENV */
908     uap = (struct a *)u.u_ap;
909     fd = getf(uap->fd);
910 #endif
911     if (!fd) {
912         afs_PutFakeStat(&fakestate);
913         return;
914     }
915
916     if (flockDone = afs_InitReq(&treq, u.u_cred)) {
917         afs_PutFakeStat(&fakestate);
918         return flockDone;
919     }
920
921     AFS_DISCON_LOCK();
922     
923     /* first determine whether this is any sort of vnode */
924     if (fd->f_type == DTYPE_VNODE) {
925         /* good, this is a vnode; next see if it is an AFS vnode */
926         tvc = VTOAFS(fd->f_data);       /* valid, given a vnode */
927         if (IsAfsVnode(AFSTOV(tvc))) {
928             /* This is an AFS vnode, so do the work */
929             code = afs_EvalFakeStat(&tvc, &fakestate, &treq);
930             if (code) {
931                 AFS_DISCON_UNLOCK();
932                 afs_PutFakeStat(&fakestate);
933                 return code;
934             }
935             if ((fd->f_flag & (FEXLOCK | FSHLOCK)) && !(uap->com & LOCK_UN)) {
936                 /* First, if fd already has lock, release it for relock path */
937 #if defined(AFS_SGI_ENV) || defined(AFS_OSF_ENV)
938                 HandleFlock(tvc, LOCK_UN, &treq, u.u_procp->p_pid,
939                             0 /*!onlymine */ );
940 #else
941                 HandleFlock(tvc, LOCK_UN, &treq, 0, 0 /*!onlymine */ );
942 #endif
943                 fd->f_flag &= ~(FEXLOCK | FSHLOCK);
944             }
945             /* now try the requested operation */
946
947 #if defined(AFS_SGI_ENV) || defined(AFS_OSF_ENV)
948             code =
949                 HandleFlock(tvc, uap->com, &treq, u.u_procp->p_pid,
950                             0 /*!onlymine */ );
951 #else
952             code = HandleFlock(tvc, uap->com, &treq, 0, 0 /*!onlymine */ );
953 #endif
954 #ifndef AFS_OSF_ENV
955             u.u_error = code;
956 #endif
957
958             if (uap->com & LOCK_UN) {
959                 /* gave up lock */
960                 fd->f_flag &= ~(FEXLOCK | FSHLOCK);
961             } else {
962 #ifdef AFS_OSF_ENV
963                 if (!code) {
964 #else /* AFS_OSF_ENV */
965                 if (!u.u_error) {
966 #endif
967                     if (uap->com & LOCK_SH)
968                         fd->f_flag |= FSHLOCK;
969                     else if (uap->com & LOCK_EX)
970                         fd->f_flag |= FEXLOCK;
971                 }
972             }
973             flockDone = 1;
974             fd->f_ops = &afs_fileops;
975         }
976     }
977 #ifdef  AFS_OSF_ENV
978     if (!flockDone)
979         code = flock(p, args, retval);
980 #ifdef  AFS_OSF30_ENV
981     FP_UNREF_ALWAYS(fd);
982 #else
983     FP_UNREF(fd);
984 #endif
985     AFS_DISCON_UNLOCK();
986     afs_PutFakeStat(&fakestate);
987     return code;
988 #else /* AFS_OSF_ENV */
989     if (!flockDone)
990         flock();
991     AFS_DISCON_UNLOCK();
992     afs_PutFakeStat(&fakestate);
993     return;
994 #endif
995 }
996 #endif /* !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV) && !defined(AFS_SUN5_ENV) && !defined(UKERNEL)  && !defined(AFS_LINUX20_ENV) */