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