a2e588400b97e37959d4f0e80188115b3a86b43d
[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     struct afs_fakestat_state fakestate;
507
508     AFS_STATCNT(afs_lockctl);
509     if (code = afs_InitReq(&treq, acred)) return code;
510     afs_InitFakeStat(&fakestate);
511     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
512     if (code) {
513         afs_PutFakeStat(&fakestate);
514         return code;
515     }
516 #ifdef  AFS_OSF_ENV
517     if (flag & VNOFLCK) {
518         afs_PutFakeStat(&fakestate);
519         return 0;
520     }
521     if (flag & CLNFLCK) {
522         acmd = LOCK_UN;
523     } else if ((flag & GETFLCK) || (flag & RGETFLCK)) {
524         acmd = F_GETLK;
525     } else if ((flag & SETFLCK) || (flag & RSETFLCK)) {
526         acmd = F_SETLK;
527     }
528 #endif
529 #if (defined(AFS_SUN_ENV) || defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
530     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
531 #else
532     if (acmd == F_GETLK) {
533 #endif
534         if (af->l_type == F_UNLCK) {
535             afs_PutFakeStat(&fakestate);
536             return 0;
537         }
538 #ifndef AFS_OSF_ENV     /* getlock is a no-op for osf (for now) */
539         code = HandleGetLock(avc, af, &treq, clid);
540 #endif
541         code = afs_CheckCode(code, &treq, 2); /* defeat buggy AIX optimz */
542         afs_PutFakeStat(&fakestate);
543         return code;
544     }
545     else if ((acmd == F_SETLK) || (acmd == F_SETLKW) 
546 #if (defined(AFS_SUN_ENV) || defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
547              || (acmd == F_RSETLK)|| (acmd == F_RSETLKW)) {
548 #else
549         ) {
550 #endif
551         /* this next check is safer when left out, but more applications work
552            with it in.  However, they fail in race conditions.  The question is
553            what to do for people who don't have source to their application;
554            this way at least, they can get work done */
555 #ifdef AFS_LINUX24_ENV
556        if (af->l_len == OFFSET_MAX)
557            af->l_len = 0;      /* since some systems indicate it as EOF */
558 #else
559         if (af->l_len == 0x7fffffff)
560             af->l_len = 0;      /* since some systems indicate it as EOF */
561 #ifdef AFS_LINUX_64BIT_KERNEL
562         if (af->l_len == LONG_MAX)
563             af->l_len = 0;      /* since some systems indicate it as EOF */
564 #endif
565 #endif
566         /* next line makes byte range locks always succeed,
567            even when they should block */
568         if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
569             DoLockWarning();
570             afs_PutFakeStat(&fakestate);
571             return 0;
572         }
573         /* otherwise we can turn this into a whole-file flock */
574         if (af->l_type == F_RDLCK) code = LOCK_SH;
575         else if (af->l_type == F_WRLCK) code = LOCK_EX;
576         else if (af->l_type == F_UNLCK) code = LOCK_UN;
577         else {
578             afs_PutFakeStat(&fakestate);
579             return EINVAL; /* unknown lock type */
580         }
581         if (((acmd == F_SETLK) 
582 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN_ENV)) && !defined(AFS_SUN58_ENV)
583         || (acmd == F_RSETLK) 
584 #endif
585         ) && code != LOCK_UN)
586             code |= LOCK_NB;    /* non-blocking, s.v.p. */
587 #if     defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV) || defined(AFS_OSF_ENV) 
588         code = HandleFlock(avc, code, &treq, clid, 0/*!onlymine*/);
589 #else
590 #if defined(AFS_SGI_ENV)
591         AFS_RWLOCK((vnode_t *)avc, VRWLOCK_WRITE);
592         code = HandleFlock(avc, code, &treq, clid, 0/*!onlymine*/);
593         AFS_RWUNLOCK((vnode_t *)avc, VRWLOCK_WRITE);
594 #else
595         code = HandleFlock(avc, code, &treq, 0, 0/*!onlymine*/);
596 #endif
597 #endif
598         code = afs_CheckCode(code, &treq, 3); /* defeat AIX -O bug */
599         afs_PutFakeStat(&fakestate);
600         return code;
601     }
602     afs_PutFakeStat(&fakestate);
603     return EINVAL;
604 }
605
606
607 /*
608  * Get a description of the first lock which would
609  * block the lock specified.  If the specified lock
610  * would succeed, fill in the lock structure with 'F_UNLCK'.
611  *
612  * To do that, we have to ask the server for the lock
613  * count if:
614  *    1. The file is not locked by this machine.
615  *    2. Asking for write lock, and only the current
616  *       PID has the file read locked.
617  */
618 #ifndef AFS_OSF_ENV     /* getlock is a no-op for osf (for now) */
619 HandleGetLock(avc, af, areq, clid)
620     int clid;           /* not used by some OSes */
621     register struct vcache *avc;
622     register struct vrequest *areq;
623     register struct AFS_FLOCK *af; 
624 {
625     register afs_int32 code;
626     struct AFS_FLOCK flock;
627
628     lockIdSet(&flock, (struct SimpleLocks *)0, clid);
629
630     ObtainWriteLock(&avc->lock,122);
631     if (avc->flockCount == 0) {
632         /* We don't know ourselves, so ask the server. Unfortunately, we don't know the pid.
633          * Not even the server knows the pid.  Besides, the process with the lock is on another machine
634          */
635         code = GetFlockCount(avc, areq);
636         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
637             af->l_type = F_UNLCK;
638             goto unlck_leave;
639         }
640         if (code > 0)
641             af->l_type = F_RDLCK;
642         else
643             af->l_type = F_WRLCK;
644
645         af->l_pid = 0;
646 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
647         af->l_sysid = 0;
648 #endif
649         goto done;
650     }
651
652     if (af->l_type == F_RDLCK) {
653         /*
654          * We want a read lock.  If there are only
655          * read locks, or we are the one with the
656          * write lock, say it is unlocked.
657          */
658         if (avc->flockCount > 0 ||      /* only read locks */
659             !lockIdcmp2(&flock, avc, (struct SimpleLocks *)0, 1, clid)) {
660             af->l_type = F_UNLCK;
661             goto unlck_leave;
662         }
663
664         /* one write lock, but who? */
665         af->l_type = F_WRLCK;           /* not us, so lock would block */
666         if (avc->slocks) {              /* we know who, so tell */
667             af->l_pid = avc->slocks->pid;
668 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
669             af->l_sysid = avc->slocks->sysid;
670 #endif
671         } else {
672             af->l_pid = 0;      /* XXX can't happen?? */
673 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
674             af->l_sysid = 0;
675 #endif
676         }
677         goto done;
678     }
679
680     /*
681      * Ok, we want a write lock.  If there is a write lock
682      * already, and it is not this process, we fail.
683      */
684     if (avc->flockCount < 0) {
685         if (lockIdcmp2(&flock, avc, (struct SimpleLocks *)0, 1, clid)) {
686             af->l_type = F_WRLCK;
687             if (avc->slocks) {
688                 af->l_pid = avc->slocks->pid;
689 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
690                 af->l_sysid = avc->slocks->sysid;
691 #endif
692             } else {
693                 af->l_pid = 0;  /* XXX can't happen?? */
694 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
695                 af->l_sysid = 0;
696 #endif
697             }
698             goto done;
699         }
700        /* we are the one with the write lock */
701         af->l_type = F_UNLCK;
702         goto unlck_leave;
703     }
704
705     /*
706      * Want a write lock, and we know there are read locks.
707      * If there is more than one, or it isn't us, we cannot lock.
708      */
709     if ((avc->flockCount > 1)
710         || lockIdcmp2(&flock, avc, (struct SimpleLocks *)0, 1, clid)) {
711         struct SimpleLocks *slp;
712         
713         af->l_type = F_RDLCK;
714         af->l_pid = 0;
715 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
716         af->l_sysid = 0;
717 #endif
718         /* find a pid that isn't our own */
719         for (slp = avc->slocks; slp; slp = slp->next) {
720            if (lockIdcmp2(&flock, (struct vcache *)0, slp, 1, clid)) {
721                af->l_pid = slp->pid;
722 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
723                af->l_sysid = avc->slocks->sysid;
724 #endif
725                 break;
726            } 
727        }
728         goto done;
729     }
730
731        /*
732         * Ok, we want a write lock.  If there is a write lock
733         * already, and it is not this process, we fail.
734         */
735         if (avc->flockCount < 0) {
736             if (lockIdcmp2(&flock, avc, (struct SimpleLocks *)0, 1, clid)) {
737                 af->l_type = F_WRLCK;
738                 if (avc->slocks) {
739                     af->l_pid = avc->slocks->pid;
740 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
741                     af->l_sysid = avc->slocks->sysid;
742 #endif
743                 } else {
744                     af->l_pid = 0;  /* XXX can't happen?? */
745 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
746                     af->l_sysid = 0;
747 #endif
748                 }
749                 goto done;
750             }
751             /* we are the one with the write lock */
752             af->l_type = F_UNLCK;
753             goto unlck_leave;
754         }
755
756         /*
757          * Want a write lock, and we know there are read locks.
758          * If there is more than one, or it isn't us, we cannot lock.
759          */
760         if ((avc->flockCount > 1)
761             || lockIdcmp2(&flock, avc, (struct SimpleLocks *)0, 1, clid)) {
762             struct SimpleLocks *slp;
763             af->l_type = F_RDLCK;
764             af->l_pid = 0;
765 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
766             af->l_sysid = 0;
767 #endif
768             /* find a pid that isn't our own */
769             for (slp = avc->slocks; slp; slp = slp->next) {
770                 if (lockIdcmp2(&flock, (struct vcache *)0, slp, 1, clid)) {
771                     af->l_pid = slp->pid;
772 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
773                     af->l_sysid = avc->slocks->sysid;
774 #endif
775                     break;
776                 }
777             }
778             goto done;
779         }
780         
781         /*
782          * Want a write lock, and there is just one read lock, and it
783          * is this process with a read lock.  Ask the server if there
784          * are any more processes with the file locked.
785          */
786         code = GetFlockCount(avc, areq);
787         if (code == 0 || code == 1) {
788             af->l_type = F_UNLCK;
789             goto unlck_leave;
790         }
791         if (code > 0)
792             af->l_type = F_RDLCK;
793         else
794             af->l_type = F_WRLCK;
795         af->l_pid = 0;
796 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
797         af->l_sysid = 0;
798 #endif
799
800 done:
801         af->l_whence = 0;
802         af->l_start = 0;
803         af->l_len = 0;    /* to end of file */
804
805 unlck_leave:
806         ReleaseWriteLock(&avc->lock);
807         return 0;
808 }
809
810 /* Get the 'flock' count from the server.  This comes back in a 'spare'
811  * field from a GetStatus RPC.  If we have any problems with the RPC,
812  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
813  * the spare field will be a zero, saying the file is unlocked.  This is
814  * OK, as a further 'lock' request will do the right thing.
815  */
816 static int GetFlockCount(struct vcache *avc, struct vrequest *areq)
817 {
818     register struct conn *tc;
819     register afs_int32 code;
820     struct AFSFetchStatus OutStatus;
821     struct AFSCallBack CallBack;
822     struct AFSVolSync tsync;
823     int temp;
824     XSTATS_DECLS
825
826     temp = areq->flags & O_NONBLOCK;
827     areq->flags |= O_NONBLOCK;
828
829     do {
830         tc = afs_Conn(&avc->fid, areq, SHARED_LOCK); 
831         if (tc){
832           XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
833 #ifdef RX_ENABLE_LOCKS
834           AFS_GUNLOCK();
835 #endif /* RX_ENABLE_LOCKS */
836           code = RXAFS_FetchStatus(tc->id, (struct AFSFid *) &avc->fid.Fid,
837                                      &OutStatus, &CallBack, &tsync);
838 #ifdef RX_ENABLE_LOCKS
839           AFS_GLOCK();
840 #endif /* RX_ENABLE_LOCKS */
841           XSTATS_END_TIME;
842         } else code = -1;
843     } while
844       (afs_Analyze(tc, code, &avc->fid, areq,
845                    AFS_STATS_FS_RPCIDX_FETCHSTATUS,
846                    SHARED_LOCK, (struct cell *)0));
847
848     if (temp)
849         areq->flags &= ~O_NONBLOCK;
850
851     if (code) {
852         return(0);              /* failed, say it is 'unlocked' */
853     } else {
854         return((int)OutStatus.lockCount);
855     }
856 }
857 #endif
858
859
860 #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)
861 /* Flock not support on System V systems */
862 #ifdef AFS_OSF_ENV
863 extern struct fileops afs_fileops;
864 afs_xflock (p, args, retval) 
865         struct proc *p;
866         void *args;
867         int *retval;
868 {
869 #else /* AFS_OSF_ENV */
870 afs_xflock () {
871 #endif
872     int code = 0;
873     struct a {
874         int fd;
875         int com;
876     } *uap;
877     struct file *fd;
878     struct vrequest treq;
879     struct vcache *tvc;
880     int flockDone;
881     struct afs_fakestat_state fakestate;
882
883     afs_InitFakeStat(&fakestate);
884     AFS_STATCNT(afs_xflock);
885     flockDone = 0;
886 #ifdef AFS_OSF_ENV
887     uap = (struct a *)args;
888     getf(&fd, uap->fd, FILE_FLAGS_NULL, &u.u_file_state);
889 #else /* AFS_OSF_ENV */
890     uap = (struct a *)u.u_ap;
891     fd = getf(uap->fd);
892 #endif
893     if (!fd) {
894         afs_PutFakeStat(&fakestate);
895         return;
896     }
897
898     if (flockDone = afs_InitReq(&treq, u.u_cred)) {
899         afs_PutFakeStat(&fakestate);
900         return flockDone;
901     }
902     /* first determine whether this is any sort of vnode */
903     if (fd->f_type == DTYPE_VNODE) {
904         /* good, this is a vnode; next see if it is an AFS vnode */
905         tvc = VTOAFS(fd->f_data);       /* valid, given a vnode */
906         if (IsAfsVnode(AFSTOV(tvc))) {
907             /* This is an AFS vnode, so do the work */
908 #ifdef AFS_DEC_ENV
909             /* find real vcache entry; shouldn't be null if gnode ref count
910              * is greater than 0.
911              */
912             tvc = VTOAFS(afs_gntovn)(tvc);
913             if (!tvc) {
914                 u.u_error = ENOENT;
915                 afs_PutFakeStat(&fakestate);
916                 return;
917             }
918 #endif
919             code = afs_EvalFakeStat(&fakestate, &tvc, &treq);
920             if (code) {
921                 afs_PutFakeStat(&fakestate);
922                 return code;
923             }
924             if ((fd->f_flag & (FEXLOCK | FSHLOCK)) && !(uap->com & LOCK_UN)) {
925                 /* First, if fd already has lock, release it for relock path */
926 #if defined(AFS_SGI_ENV) || defined(AFS_OSF_ENV) || (defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV))
927                 HandleFlock(tvc, LOCK_UN, &treq, u.u_procp->p_pid, 0/*!onlymine*/);
928 #else
929                 HandleFlock(tvc, LOCK_UN, &treq, 0, 0/*!onlymine*/);
930 #endif
931                 fd->f_flag &= ~(FEXLOCK | FSHLOCK);
932             }
933             /* now try the requested operation */
934
935 #if defined(AFS_SGI_ENV) || defined(AFS_OSF_ENV) || (defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV))
936             code = HandleFlock(tvc, uap->com, &treq,
937                                u.u_procp->p_pid, 0/*!onlymine*/);
938 #else
939             code = HandleFlock(tvc, uap->com, &treq, 0, 0/*!onlymine*/);
940 #endif
941 #ifndef AFS_OSF_ENV
942             u.u_error = code;
943 #endif
944
945             if (uap->com & LOCK_UN) {
946                 /* gave up lock */
947                 fd->f_flag &= ~(FEXLOCK | FSHLOCK);
948             }
949             else {
950 #ifdef AFS_OSF_ENV
951                 if (!code) {
952 #else /* AFS_OSF_ENV */
953                 if (!u.u_error) {
954 #endif
955                     if (uap->com & LOCK_SH) fd->f_flag |= FSHLOCK;
956                     else if (uap->com & LOCK_EX) fd->f_flag |= FEXLOCK;
957                 }
958             }
959             flockDone = 1;
960             fd->f_ops = &afs_fileops;
961         }
962     }
963 #ifdef  AFS_OSF_ENV
964     if (!flockDone)
965         code = flock(p, args, retval);
966 #ifdef  AFS_OSF30_ENV
967     FP_UNREF_ALWAYS(fd);
968 #else
969     FP_UNREF(fd);
970 #endif
971     afs_PutFakeStat(&fakestate);
972     return code;
973 #else   /* AFS_OSF_ENV */
974     if (!flockDone)
975 #ifdef DYNEL
976         (*afs_longcall_procs.LC_flock)();
977 #else
978         flock();
979 #endif
980     afs_PutFakeStat(&fakestate);
981     return;
982 #endif
983 }
984 #endif /* !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV) && !defined(AFS_SUN5_ENV) && !defined(UKERNEL)  && !defined(AFS_LINUX20_ENV) */
985