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