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