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