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