d7698f8418d34e65f1ed892597af77ea7b5bde04
[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(struct vcache *avc,
33                          struct AFS_FLOCK *af,
34                          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                       struct SimpleLocks *alp, int onlymine,
38                       int clid);
39 static void DoLockWarning(afs_ucred_t * acred);
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     afs_proc_t *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     afs_proc_t *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 }
126 #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
127 void
128 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
129 {
130     if (slp) {
131         slp->pid = clid;
132     } else {
133         flock->l_pid = clid;
134     }
135 }
136 #elif defined(AFS_LINUX20_ENV) || defined(AFS_HPUX_ENV)
137 void
138 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
139 {
140     if (slp) {
141         slp->pid = getpid();
142     } else {
143         flock->l_pid = getpid();
144     }
145 }
146 #elif defined(UKERNEL)
147 void
148 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
149 {
150     if (slp) {
151         slp->pid = get_user_struct()->u_procp->p_pid;
152     } else {
153         flock->l_pid = get_user_struct()->u_procp->p_pid;
154     }
155 }
156 #else
157 void
158 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
159 {
160     if (slp) {
161         slp->pid = u.u_procp->p_pid;
162     } else {
163         flock->l_pid = u.u_procp->p_pid;
164     }
165 }
166 #endif
167
168 /* return 1 (true) if specified flock does not match alp (if 
169  * specified), or any of the slp structs (if alp == 0) 
170  */
171 /* I'm not sure that the comparsion of flock->pid to p_ppid
172  * is correct.  Should that be a comparision of alp (or slp) ->pid  
173  * to p_ppid?  Especially in the context of the lower loop, where
174  * the repeated comparison doesn't make much sense...
175  */
176 /* onlymine - don't match any locks which are held by my parent */
177 /* clid - only irix 6.5 */
178
179 static int
180 lockIdcmp2(struct AFS_FLOCK *flock1, struct vcache *vp,
181            struct SimpleLocks *alp, int onlymine, int clid)
182 {
183     struct SimpleLocks *slp;
184 #if     defined(AFS_SUN5_ENV)
185     proc_t *procp = ttoproc(curthread);
186 #else
187 #if !defined(AFS_AIX41_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_SGI65_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
188 #ifdef AFS_SGI64_ENV
189     afs_proc_t *procp = curprocp;
190 #elif defined(UKERNEL)
191     afs_proc_t *procp = get_user_struct()->u_procp;
192 #else
193     afs_proc_t *procp = u.u_procp;
194 #endif /* AFS_SGI64_ENV */
195 #endif
196 #endif
197
198     if (alp) {
199 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
200         if (flock1->l_sysid != alp->sysid) {
201             return 1;
202         }
203 #endif
204         if ((flock1->l_pid == alp->pid) ||
205 #if defined(AFS_AIX41_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_HPUX_ENV)
206             (!onlymine && (flock1->l_pid == getppid()))
207 #else
208 #if defined(AFS_SGI65_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
209             /* XXX check this. used to be *only* irix for some reason. */
210             (!onlymine && (flock1->l_pid == clid))
211 #else
212             (!onlymine && (flock1->l_pid == procp->p_ppid))
213 #endif
214 #endif
215             ) {
216             return 0;
217         }
218         return 1;
219     }
220
221     for (slp = vp->slocks; slp; slp = slp->next) {
222 #if defined(AFS_HAVE_FLOCK_SYSID)
223         if (flock1->l_sysid != slp->sysid) {
224             continue;
225         }
226 #endif
227         if (flock1->l_pid == slp->pid) {
228             return 0;
229         }
230     }
231     return (1);                 /* failure */
232 }
233
234
235 /* we don't send multiple read flocks to the server, but rather just count
236     them up ourselves.  Of course, multiple write locks are incompatible.
237     
238     Note that we should always try to release a lock, even if we have
239     a network problem sending the release command through, since often
240     a lock is released on a close call, when the user can't retry anyway.
241     
242     After we remove it from our structure, the lock will no longer be
243     kept alive, and the server should time it out within a few minutes.
244     
245     94.04.13 add "force" parameter.  If a child explicitly unlocks a
246     file, I guess we'll permit it.  however, we don't want simple,
247     innocent closes by children to unlock files in the parent process.
248
249     If called when disconnected support is unabled, the discon_lock must
250     be held
251 */
252 /* clid - nonzero on sgi sunos osf1 only */
253 int
254 HandleFlock(struct vcache *avc, int acom, struct vrequest *areq,
255             pid_t clid, int onlymine)
256 {
257     struct afs_conn *tc;
258     struct SimpleLocks *slp, *tlp, **slpp;
259     afs_int32 code;
260     struct AFSVolSync tsync;
261     afs_int32 lockType;
262     struct AFS_FLOCK flock;
263     XSTATS_DECLS;
264     AFS_STATCNT(HandleFlock);
265     code = 0;                   /* default when we don't make any network calls */
266     lockIdSet(&flock, NULL, clid);
267
268 #if defined(AFS_SGI_ENV)
269     osi_Assert(valusema(&avc->vc_rwlock) <= 0);
270     osi_Assert(OSI_GET_LOCKID() == avc->vc_rwlockid);
271 #endif
272     ObtainWriteLock(&avc->lock, 118);
273     if (acom & LOCK_UN) {
274
275 /* defect 3083 */
276
277 #ifdef AFS_AIX_ENV
278         /* If the lock is held exclusive, then only the owning process
279          * or a child can unlock it. Use pid and ppid because they are
280          * unique identifiers.
281          */
282         if ((avc->flockCount < 0) && (getpid() != avc->ownslock)) {
283 #ifdef  AFS_AIX41_ENV
284             if (onlymine || (getppid() != avc->ownslock)) {
285 #else
286             if (onlymine || (u.u_procp->p_ppid != avc->ownslock)) {
287 #endif
288                 ReleaseWriteLock(&avc->lock);
289                 return 0;
290             }
291         }
292 #endif
293         if (lockIdcmp2(&flock, avc, NULL, onlymine, clid)) {
294             ReleaseWriteLock(&avc->lock);
295             return 0;
296         }
297 #ifdef AFS_AIX_ENV
298         avc->ownslock = 0;
299 #endif
300         if (avc->flockCount == 0) {
301             ReleaseWriteLock(&avc->lock);
302             return 0 /*ENOTTY*/;
303             /* no lock held */
304         }
305         /* unlock the lock */
306         if (avc->flockCount > 0) {
307             slpp = &avc->slocks;
308             for (slp = *slpp; slp;) {
309                 if (!lockIdcmp2(&flock, avc, slp, onlymine, clid)) {
310                     avc->flockCount--;
311                     tlp = *slpp = slp->next;
312                     osi_FreeSmallSpace(slp);
313                     slp = tlp;
314                 } else {
315                     slpp = &slp->next;
316                     slp = *slpp;
317                 }
318             }
319         } else if (avc->flockCount == -1) {
320             afs_StoreAllSegments(avc, areq, AFS_SYNC | AFS_VMSYNC);     /* fsync file early */
321             avc->flockCount = 0;
322             /* And remove the (only) exclusive lock entry from the list... */
323             osi_FreeSmallSpace(avc->slocks);
324             avc->slocks = 0;
325         }
326         if (avc->flockCount == 0) {
327             if (!AFS_IS_DISCONNECTED) {
328                 do {
329                     tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
330                     if (tc) {
331                         XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
332                         RX_AFS_GUNLOCK();
333                         code = RXAFS_ReleaseLock(tc->id, (struct AFSFid *)
334                                                  &avc->f.fid.Fid, &tsync);
335                         RX_AFS_GLOCK();
336                         XSTATS_END_TIME;
337                     } else
338                     code = -1;
339                 } while (afs_Analyze
340                          (tc, code, &avc->f.fid, areq,
341                           AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL));
342             } else {
343                 /*printf("Network is dooooooowwwwwwwnnnnnnn\n");*/
344                code = ENETDOWN;
345             }
346         }
347     } else {
348         while (1) {             /* set a new lock */
349             /*
350              * Upgrading from shared locks to Exclusive and vice versa
351              * is a bit tricky and we don't really support it yet. But
352              * we try to support the common used one which is upgrade
353              * a shared lock to an exclusive for the same process...
354              */
355             if ((avc->flockCount > 0 && (acom & LOCK_EX))
356                 || (avc->flockCount == -1 && (acom & LOCK_SH))) {
357                 /*
358                  * Upgrading from shared locks to an exclusive one:
359                  * For now if all the shared locks belong to the
360                  * same process then we unlock them on the server
361                  * and proceed with the upgrade.  Unless we change the
362                  * server's locking interface impl we prohibit from
363                  * unlocking other processes's shared locks...
364                  * Upgrading from an exclusive lock to a shared one:
365                  * Again only allowed to be done by the same process.
366                  */
367                 slpp = &avc->slocks;
368                 for (slp = *slpp; slp;) {
369                     if (!lockIdcmp2
370                         (&flock, avc, slp, 1 /*!onlymine */ , clid)) {
371                         if (acom & LOCK_EX)
372                             avc->flockCount--;
373                         else
374                             avc->flockCount = 0;
375                         tlp = *slpp = slp->next;
376                         osi_FreeSmallSpace(slp);
377                         slp = tlp;
378                     } else {
379                         code = EWOULDBLOCK;
380                         slpp = &slp->next;
381                         slp = *slpp;
382                     }
383                 }
384                 if (!code && avc->flockCount == 0) {
385                     if (!AFS_IS_DISCONNECTED) {
386                         do {
387                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
388                             if (tc) {
389                                 XSTATS_START_TIME
390                                     (AFS_STATS_FS_RPCIDX_RELEASELOCK);
391                                 RX_AFS_GUNLOCK();
392                                 code =
393                                     RXAFS_ReleaseLock(tc->id,
394                                                       (struct AFSFid *)&avc->
395                                                       f.fid.Fid, &tsync);
396                                 RX_AFS_GLOCK();
397                                XSTATS_END_TIME;
398                             } else
399                                 code = -1;
400                         } while (afs_Analyze
401                                  (tc, code, &avc->f.fid, areq,
402                                   AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK,
403                                   NULL));
404                     }
405                 }
406             } else if (avc->flockCount == -1 && (acom & LOCK_EX)) {
407                 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
408                     code = EWOULDBLOCK;
409                 } else
410                     code = 0;
411             }
412             if (code == 0) {
413                 /* compatible here, decide if needs to go to file server.  If
414                  * we've already got the file locked (and thus read-locked, since
415                  * we've already checked for compatibility), we shouldn't send
416                  * the call through to the server again */
417                 if (avc->flockCount == 0) {
418                     /* we're the first on our block, send the call through */
419                     lockType = ((acom & LOCK_EX) ? LockWrite : LockRead);
420                     if (!AFS_IS_DISCONNECTED) {
421                         do {
422                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
423                             if (tc) {
424                                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
425                                 RX_AFS_GUNLOCK();
426                                 code = RXAFS_SetLock(tc->id, (struct AFSFid *)
427                                                      &avc->f.fid.Fid, lockType,
428                                                      &tsync);
429                                 RX_AFS_GLOCK();
430                                 XSTATS_END_TIME;
431                             } else
432                                 code = -1;
433                         } while (afs_Analyze
434                                  (tc, code, &avc->f.fid, areq,
435                                   AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK,
436                                   NULL));
437                         if ((lockType == LockWrite) && (code == VREADONLY))
438                             code = EBADF; /* per POSIX; VREADONLY == EROFS */
439                     } else
440                         /* XXX - Should probably try and log this when we're
441                          * XXX - running with logging enabled. But it's horrid
442                          */
443                         code = 0; /* pretend we worked - ick!!! */
444                 } else
445                     code = 0;   /* otherwise, pretend things worked */
446             }
447             if (code == 0) {
448                 slp = (struct SimpleLocks *)
449                     osi_AllocSmallSpace(sizeof(struct SimpleLocks));
450                 if (acom & LOCK_EX) {
451
452 /* defect 3083 */
453
454 #ifdef AFS_AIX_ENV
455                     /* Record unique id of process owning exclusive lock. */
456                     avc->ownslock = getpid();
457 #endif
458
459                     slp->type = LockWrite;
460                     slp->next = NULL;
461                     avc->slocks = slp;
462                     avc->flockCount = -1;
463                 } else {
464                     slp->type = LockRead;
465                     slp->next = avc->slocks;
466                     avc->slocks = slp;
467                     avc->flockCount++;
468                 }
469
470                 lockIdSet(&flock, slp, clid);
471                 break;
472             }
473             /* now, if we got EWOULDBLOCK, and we're supposed to wait, we do */
474             if (((code == EWOULDBLOCK) || (code == EAGAIN) || 
475                  (code == UAEWOULDBLOCK) || (code == UAEAGAIN))
476                 && !(acom & LOCK_NB)) {
477                 /* sleep for a second, allowing interrupts */
478                 ReleaseWriteLock(&avc->lock);
479 #if defined(AFS_SGI_ENV)
480                 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
481 #endif
482                 code = afs_osi_Wait(1000, NULL, 1);
483 #if defined(AFS_SGI_ENV)
484                 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
485 #endif
486                 ObtainWriteLock(&avc->lock, 120);
487                 if (code) {
488                     code = EINTR;       /* return this if ^C typed */
489                     break;
490                 }
491             } else
492                 break;
493         }                       /* while loop */
494     }
495     ReleaseWriteLock(&avc->lock);
496     code = afs_CheckCode(code, areq, 1);        /* defeat a buggy AIX optimization */
497     return code;
498 }
499
500
501 /* warn a user that a lock has been ignored */
502 afs_int32 lastWarnTime = 0;     /* this is used elsewhere */
503 static afs_int32 lastWarnPid = 0;
504 static void
505 DoLockWarning(afs_ucred_t * acred)
506 {
507     afs_int32 now;
508     pid_t pid = MyPidxx2Pid(MyPidxx);
509     char *procname;
510
511     now = osi_Time();
512
513     AFS_STATCNT(DoLockWarning);
514     /* check if we've already warned this user recently */
515     if (!((now < lastWarnTime + 120) && (lastWarnPid == pid))) {
516         procname = afs_osi_Alloc(256);
517
518         if (!procname)
519             return;
520
521         /* Copies process name to allocated procname, see osi_machdeps for details of macro */
522         osi_procname(procname, 256);
523         procname[255] = '\0';
524
525         /* otherwise, it is time to nag the user */
526         lastWarnTime = now;
527         lastWarnPid = pid;
528 #ifdef AFS_LINUX26_ENV
529         afs_warnuser
530             ("afs: byte-range locks only enforced for processes on this machine (pid %d (%s), user %ld).\n", pid, procname, (long)afs_cr_uid(acred));
531 #else
532         afs_warnuser
533             ("afs: byte-range lock/unlock ignored; make sure no one else is running this program (pid %d (%s), user %ld).\n", pid, procname, afs_cr_uid(acred));
534 #endif
535         afs_osi_Free(procname, 256);
536     }
537     return;
538 }
539
540
541 #if defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
542 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
543                 afs_ucred_t * acred, pid_t clid)
544 #else
545 u_int clid = 0;
546 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
547                 afs_ucred_t * acred)
548 #endif
549 {
550     struct vrequest treq;
551     afs_int32 code;
552     struct afs_fakestat_state fakestate;
553
554     AFS_STATCNT(afs_lockctl);
555     if ((code = afs_InitReq(&treq, acred)))
556         return code;
557     afs_InitFakeStat(&fakestate);
558
559     AFS_DISCON_LOCK();
560
561     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
562     if (code) {
563         goto done;
564     }
565 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
566     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
567 #else
568     if (acmd == F_GETLK) {
569 #endif
570         if (af->l_type == F_UNLCK) {
571             code = 0;
572             goto done;
573         }
574         code = HandleGetLock(avc, af, &treq, clid);
575         code = afs_CheckCode(code, &treq, 2);   /* defeat buggy AIX optimz */
576         goto done;
577     } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
578 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
579                || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
580 #else
581         ) {
582 #endif
583     /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU */
584     if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7fffffffffffffffLL))
585         af->l_len = 0;
586     /* next line makes byte range locks always succeed,
587      * even when they should block */
588     if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
589         DoLockWarning(acred);
590         code = 0;
591         goto done;
592     }
593     /* otherwise we can turn this into a whole-file flock */
594     if (af->l_type == F_RDLCK)
595         code = LOCK_SH;
596     else if (af->l_type == F_WRLCK)
597         code = LOCK_EX;
598     else if (af->l_type == F_UNLCK)
599         code = LOCK_UN;
600     else {
601         afs_PutFakeStat(&fakestate);
602         return EINVAL;          /* unknown lock type */
603     }
604     if (((acmd == F_SETLK)
605 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
606          || (acmd == F_RSETLK)
607 #endif
608         ) && code != LOCK_UN)
609         code |= LOCK_NB;        /* non-blocking, s.v.p. */
610 #if defined(AFS_DARWIN_ENV)
611     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
612 #elif defined(AFS_SGI_ENV)
613     AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
614     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
615     AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
616 #else
617     code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
618 #endif
619     code = afs_CheckCode(code, &treq, 3);       /* defeat AIX -O bug */
620     goto done;
621     }
622     code = EINVAL;
623 done:
624     afs_PutFakeStat(&fakestate);
625     AFS_DISCON_UNLOCK();
626     return code;
627 }
628
629
630 /*
631  * Get a description of the first lock which would
632  * block the lock specified.  If the specified lock
633  * would succeed, fill in the lock structure with 'F_UNLCK'.
634  *
635  * To do that, we have to ask the server for the lock
636  * count if:
637  *    1. The file is not locked by this machine.
638  *    2. Asking for write lock, and only the current
639  *       PID has the file read locked.
640  */
641 static int
642 HandleGetLock(struct vcache *avc, struct AFS_FLOCK *af,
643               struct vrequest *areq, int clid)
644 {
645     afs_int32 code;
646     struct AFS_FLOCK flock;
647
648     lockIdSet(&flock, NULL, clid);
649
650     ObtainWriteLock(&avc->lock, 122);
651     if (avc->flockCount == 0) {
652         /*
653          * We don't know ourselves, so ask the server. Unfortunately, we
654          * don't know the pid.  Not even the server knows the pid.  Besides,
655          * the process with the lock is on another machine
656          */
657         code = GetFlockCount(avc, areq);
658         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
659             af->l_type = F_UNLCK;
660             goto unlck_leave;
661         }
662         if (code > 0)
663             af->l_type = F_RDLCK;
664         else
665             af->l_type = F_WRLCK;
666
667         af->l_pid = 0;
668 #if defined(AFS_HAVE_FLOCK_SYSID)
669         af->l_sysid = 0;
670 #endif
671         goto done;
672     }
673
674     if (af->l_type == F_RDLCK) {
675         /*
676          * We want a read lock.  If there are only
677          * read locks, or we are the one with the
678          * write lock, say it is unlocked.
679          */
680         if (avc->flockCount > 0 ||      /* only read locks */
681             !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
682             af->l_type = F_UNLCK;
683             goto unlck_leave;
684         }
685
686         /* one write lock, but who? */
687         af->l_type = F_WRLCK;   /* not us, so lock would block */
688         if (avc->slocks) {      /* we know who, so tell */
689             af->l_pid = avc->slocks->pid;
690 #if defined(AFS_HAVE_FLOCK_SYSID)
691             af->l_sysid = avc->slocks->sysid;
692 #endif
693         } else {
694             af->l_pid = 0;      /* XXX can't happen?? */
695 #if defined(AFS_HAVE_FLOCK_SYSID)
696             af->l_sysid = 0;
697 #endif
698         }
699         goto done;
700     }
701
702     /*
703      * Ok, we want a write lock.  If there is a write lock
704      * already, and it is not this process, we fail.
705      */
706     if (avc->flockCount < 0) {
707         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
708             af->l_type = F_WRLCK;
709             if (avc->slocks) {
710                 af->l_pid = avc->slocks->pid;
711 #if defined(AFS_HAVE_FLOCK_SYSID)
712                 af->l_sysid = avc->slocks->sysid;
713 #endif
714             } else {
715                 af->l_pid = 0;  /* XXX can't happen?? */
716 #if defined(AFS_HAVE_FLOCK_SYSID)
717                 af->l_sysid = 0;
718 #endif
719             }
720             goto done;
721         }
722         /* we are the one with the write lock */
723         af->l_type = F_UNLCK;
724         goto unlck_leave;
725     }
726
727     /*
728      * Want a write lock, and we know there are read locks.
729      * If there is more than one, or it isn't us, we cannot lock.
730      */
731     if ((avc->flockCount > 1)
732         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
733         struct SimpleLocks *slp;
734
735         af->l_type = F_RDLCK;
736         af->l_pid = 0;
737 #if defined(AFS_HAVE_FLOCK_SYSID)
738         af->l_sysid = 0;
739 #endif
740         /* find a pid that isn't our own */
741         for (slp = avc->slocks; slp; slp = slp->next) {
742             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
743                 af->l_pid = slp->pid;
744 #if defined(AFS_HAVE_FLOCK_SYSID)
745                 af->l_sysid = avc->slocks->sysid;
746 #endif
747                 break;
748             }
749         }
750         goto done;
751     }
752
753     /*
754      * Ok, we want a write lock.  If there is a write lock
755      * already, and it is not this process, we fail.
756      */
757     if (avc->flockCount < 0) {
758         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
759             af->l_type = F_WRLCK;
760             if (avc->slocks) {
761                 af->l_pid = avc->slocks->pid;
762 #if defined(AFS_HAVE_FLOCK_SYSID)
763                 af->l_sysid = avc->slocks->sysid;
764 #endif
765             } else {
766                 af->l_pid = 0;  /* XXX can't happen?? */
767 #if defined(AFS_HAVE_FLOCK_SYSID)
768                 af->l_sysid = 0;
769 #endif
770             }
771             goto done;
772         }
773         /* we are the one with the write lock */
774         af->l_type = F_UNLCK;
775         goto unlck_leave;
776     }
777
778     /*
779      * Want a write lock, and we know there are read locks.
780      * If there is more than one, or it isn't us, we cannot lock.
781      */
782     if ((avc->flockCount > 1)
783         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
784         struct SimpleLocks *slp;
785         af->l_type = F_RDLCK;
786         af->l_pid = 0;
787 #if defined(AFS_HAVE_FLOCK_SYSID)
788         af->l_sysid = 0;
789 #endif
790         /* find a pid that isn't our own */
791         for (slp = avc->slocks; slp; slp = slp->next) {
792             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
793                 af->l_pid = slp->pid;
794 #if defined(AFS_HAVE_FLOCK_SYSID)
795                 af->l_sysid = avc->slocks->sysid;
796 #endif
797                 break;
798             }
799         }
800         goto done;
801     }
802
803     /*
804      * Want a write lock, and there is just one read lock, and it
805      * is this process with a read lock.  Ask the server if there
806      * are any more processes with the file locked.
807      */
808     code = GetFlockCount(avc, areq);
809     if (code == 0 || code == 1) {
810         af->l_type = F_UNLCK;
811         goto unlck_leave;
812     }
813     if (code > 0)
814         af->l_type = F_RDLCK;
815     else
816         af->l_type = F_WRLCK;
817     af->l_pid = 0;
818 #if defined(AFS_HAVE_FLOCK_SYSID)
819     af->l_sysid = 0;
820 #endif
821
822   done:
823     af->l_whence = 0;
824     af->l_start = 0;
825     af->l_len = 0;              /* to end of file */
826
827   unlck_leave:
828     ReleaseWriteLock(&avc->lock);
829     return 0;
830 }
831
832 /* Get the 'flock' count from the server.  This comes back in a 'spare'
833  * field from a GetStatus RPC.  If we have any problems with the RPC,
834  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
835  * the spare field will be a zero, saying the file is unlocked.  This is
836  * OK, as a further 'lock' request will do the right thing.
837  */
838 static int
839 GetFlockCount(struct vcache *avc, struct vrequest *areq)
840 {
841     struct afs_conn *tc;
842     afs_int32 code;
843     struct AFSFetchStatus OutStatus;
844     struct AFSCallBack CallBack;
845     struct AFSVolSync tsync;
846     int temp;
847     XSTATS_DECLS;
848     temp = areq->flags & O_NONBLOCK;
849     areq->flags |= O_NONBLOCK;
850
851     /* If we're disconnected, lie and say that we've got no locks. Ick */
852     if (AFS_IS_DISCONNECTED)
853         return 0;
854         
855     do {
856         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
857         if (tc) {
858             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
859             RX_AFS_GUNLOCK();
860             code =
861                 RXAFS_FetchStatus(tc->id, (struct AFSFid *)&avc->f.fid.Fid,
862                                   &OutStatus, &CallBack, &tsync);
863             RX_AFS_GLOCK();
864             XSTATS_END_TIME;
865         } else
866             code = -1;
867     } while (afs_Analyze
868              (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
869               SHARED_LOCK, NULL));
870
871     if (temp)
872         areq->flags &= ~O_NONBLOCK;
873
874     if (code) {
875         return (0);             /* failed, say it is 'unlocked' */
876     } else {
877         return ((int)OutStatus.lockCount);
878     }
879 }
880