5b91a5765e0761a22d20f23b52a448beb13eb0a2
[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         int stored_segments = 0;
275      retry_unlock:
276
277 /* defect 3083 */
278
279 #ifdef AFS_AIX_ENV
280         /* If the lock is held exclusive, then only the owning process
281          * or a child can unlock it. Use pid and ppid because they are
282          * unique identifiers.
283          */
284         if ((avc->flockCount < 0) && (getpid() != avc->ownslock)) {
285 #ifdef  AFS_AIX41_ENV
286             if (onlymine || (getppid() != avc->ownslock)) {
287 #else
288             if (onlymine || (u.u_procp->p_ppid != avc->ownslock)) {
289 #endif
290                 ReleaseWriteLock(&avc->lock);
291                 return 0;
292             }
293         }
294 #endif
295         if (lockIdcmp2(&flock, avc, NULL, onlymine, clid)) {
296             ReleaseWriteLock(&avc->lock);
297             return 0;
298         }
299 #ifdef AFS_AIX_ENV
300         avc->ownslock = 0;
301 #endif
302         if (avc->flockCount == 0) {
303             ReleaseWriteLock(&avc->lock);
304             return 0 /*ENOTTY*/;
305             /* no lock held */
306         }
307         /* unlock the lock */
308         if (avc->flockCount > 0) {
309             slpp = &avc->slocks;
310             for (slp = *slpp; slp;) {
311                 if (!lockIdcmp2(&flock, avc, slp, onlymine, clid)) {
312                     avc->flockCount--;
313                     tlp = *slpp = slp->next;
314                     osi_FreeSmallSpace(slp);
315                     slp = tlp;
316                 } else {
317                     slpp = &slp->next;
318                     slp = *slpp;
319                 }
320             }
321         } else if (avc->flockCount == -1) {
322             if (!stored_segments) {
323                 afs_StoreAllSegments(avc, areq, AFS_SYNC | AFS_VMSYNC); /* fsync file early */
324                 /* afs_StoreAllSegments can drop and reacquire the write lock
325                  * on avc and GLOCK, so the flocks may be completely different
326                  * now. Go back and perform all checks again. */
327                  stored_segments = 1;
328                  goto retry_unlock;
329             }
330             avc->flockCount = 0;
331             /* And remove the (only) exclusive lock entry from the list... */
332             osi_FreeSmallSpace(avc->slocks);
333             avc->slocks = 0;
334         }
335         if (avc->flockCount == 0) {
336             if (!AFS_IS_DISCONNECTED) {
337                 struct rx_connection *rxconn;
338                 do {
339                     tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
340                     if (tc) {
341                         XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
342                         RX_AFS_GUNLOCK();
343                         code = RXAFS_ReleaseLock(rxconn, (struct AFSFid *)
344                                                  &avc->f.fid.Fid, &tsync);
345                         RX_AFS_GLOCK();
346                         XSTATS_END_TIME;
347                     } else
348                     code = -1;
349                 } while (afs_Analyze
350                          (tc, rxconn, code, &avc->f.fid, areq,
351                           AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL));
352             } else {
353                 /*printf("Network is dooooooowwwwwwwnnnnnnn\n");*/
354                code = ENETDOWN;
355             }
356         }
357     } else {
358         while (1) {             /* set a new lock */
359             /*
360              * Upgrading from shared locks to Exclusive and vice versa
361              * is a bit tricky and we don't really support it yet. But
362              * we try to support the common used one which is upgrade
363              * a shared lock to an exclusive for the same process...
364              */
365             if ((avc->flockCount > 0 && (acom & LOCK_EX))
366                 || (avc->flockCount == -1 && (acom & LOCK_SH))) {
367                 /*
368                  * Upgrading from shared locks to an exclusive one:
369                  * For now if all the shared locks belong to the
370                  * same process then we unlock them on the server
371                  * and proceed with the upgrade.  Unless we change the
372                  * server's locking interface impl we prohibit from
373                  * unlocking other processes's shared locks...
374                  * Upgrading from an exclusive lock to a shared one:
375                  * Again only allowed to be done by the same process.
376                  */
377                 slpp = &avc->slocks;
378                 for (slp = *slpp; slp;) {
379                     if (!lockIdcmp2
380                         (&flock, avc, slp, 1 /*!onlymine */ , clid)) {
381                         if (acom & LOCK_EX)
382                             avc->flockCount--;
383                         else
384                             avc->flockCount = 0;
385                         tlp = *slpp = slp->next;
386                         osi_FreeSmallSpace(slp);
387                         slp = tlp;
388                     } else {
389                         code = EWOULDBLOCK;
390                         slpp = &slp->next;
391                         slp = *slpp;
392                     }
393                 }
394                 if (!code && avc->flockCount == 0) {
395                     if (!AFS_IS_DISCONNECTED) {
396                         struct rx_connection *rxconn;
397                         do {
398                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
399                             if (tc) {
400                                 XSTATS_START_TIME
401                                     (AFS_STATS_FS_RPCIDX_RELEASELOCK);
402                                 RX_AFS_GUNLOCK();
403                                 code =
404                                     RXAFS_ReleaseLock(rxconn,
405                                                       (struct AFSFid *)&avc->
406                                                       f.fid.Fid, &tsync);
407                                 RX_AFS_GLOCK();
408                                XSTATS_END_TIME;
409                             } else
410                                 code = -1;
411                         } while (afs_Analyze
412                                  (tc, rxconn, code, &avc->f.fid, areq,
413                                   AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK,
414                                   NULL));
415                     }
416                 }
417             } else if (avc->flockCount == -1 && (acom & LOCK_EX)) {
418                 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
419                     code = EWOULDBLOCK;
420                 } else {
421                     code = 0;
422                     /* We've just re-grabbed an exclusive lock, so we don't
423                      * need to contact the fileserver, and we don't need to
424                      * add the lock to avc->slocks (since we already have a
425                      * lock there). So, we are done. */
426                     break;
427                 }
428             }
429             if (code == 0) {
430                 /* compatible here, decide if needs to go to file server.  If
431                  * we've already got the file locked (and thus read-locked, since
432                  * we've already checked for compatibility), we shouldn't send
433                  * the call through to the server again */
434                 if (avc->flockCount == 0) {
435                     struct rx_connection *rxconn;
436                     /* we're the first on our block, send the call through */
437                     lockType = ((acom & LOCK_EX) ? LockWrite : LockRead);
438                     if (!AFS_IS_DISCONNECTED) {
439                         do {
440                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
441                             if (tc) {
442                                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
443                                 RX_AFS_GUNLOCK();
444                                 code = RXAFS_SetLock(rxconn, (struct AFSFid *)
445                                                      &avc->f.fid.Fid, lockType,
446                                                      &tsync);
447                                 RX_AFS_GLOCK();
448                                 XSTATS_END_TIME;
449                             } else
450                                 code = -1;
451                         } while (afs_Analyze
452                                  (tc, rxconn, code, &avc->f.fid, areq,
453                                   AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK,
454                                   NULL));
455                         if ((lockType == LockWrite) && (code == VREADONLY))
456                             code = EBADF; /* per POSIX; VREADONLY == EROFS */
457                     } else
458                         /* XXX - Should probably try and log this when we're
459                          * XXX - running with logging enabled. But it's horrid
460                          */
461                         code = 0; /* pretend we worked - ick!!! */
462                 } else
463                     code = 0;   /* otherwise, pretend things worked */
464             }
465             if (code == 0) {
466                 slp = (struct SimpleLocks *)
467                     osi_AllocSmallSpace(sizeof(struct SimpleLocks));
468                 if (acom & LOCK_EX) {
469
470 /* defect 3083 */
471
472 #ifdef AFS_AIX_ENV
473                     /* Record unique id of process owning exclusive lock. */
474                     avc->ownslock = getpid();
475 #endif
476
477                     slp->type = LockWrite;
478                     slp->next = NULL;
479                     avc->slocks = slp;
480                     avc->flockCount = -1;
481                 } else {
482                     slp->type = LockRead;
483                     slp->next = avc->slocks;
484                     avc->slocks = slp;
485                     avc->flockCount++;
486                 }
487
488                 lockIdSet(&flock, slp, clid);
489                 break;
490             }
491             /* now, if we got EWOULDBLOCK, and we're supposed to wait, we do */
492             if (((code == EWOULDBLOCK) || (code == EAGAIN) || 
493                  (code == UAEWOULDBLOCK) || (code == UAEAGAIN))
494                 && !(acom & LOCK_NB)) {
495                 /* sleep for a second, allowing interrupts */
496                 ReleaseWriteLock(&avc->lock);
497 #if defined(AFS_SGI_ENV)
498                 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
499 #endif
500                 code = afs_osi_Wait(1000, NULL, 1);
501 #if defined(AFS_SGI_ENV)
502                 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
503 #endif
504                 ObtainWriteLock(&avc->lock, 120);
505                 if (code) {
506                     code = EINTR;       /* return this if ^C typed */
507                     break;
508                 }
509             } else
510                 break;
511         }                       /* while loop */
512     }
513     ReleaseWriteLock(&avc->lock);
514     code = afs_CheckCode(code, areq, 1);        /* defeat a buggy AIX optimization */
515     return code;
516 }
517
518
519 /* warn a user that a lock has been ignored */
520 afs_int32 lastWarnTime = 0;     /* this is used elsewhere */
521 static afs_int32 lastWarnPid = 0;
522 static void
523 DoLockWarning(afs_ucred_t * acred)
524 {
525     afs_int32 now;
526     pid_t pid = MyPidxx2Pid(MyPidxx);
527     char *procname;
528
529     now = osi_Time();
530
531     AFS_STATCNT(DoLockWarning);
532     /* check if we've already warned this user recently */
533     if (!((now < lastWarnTime + 120) && (lastWarnPid == pid))) {
534         procname = afs_osi_Alloc(256);
535
536         if (!procname)
537             return;
538
539         /* Copies process name to allocated procname, see osi_machdeps for details of macro */
540         osi_procname(procname, 256);
541         procname[255] = '\0';
542
543         /* otherwise, it is time to nag the user */
544         lastWarnTime = now;
545         lastWarnPid = pid;
546 #ifdef AFS_LINUX26_ENV
547         afs_warnuser
548             ("afs: byte-range locks only enforced for processes on this machine (pid %d (%s), user %ld).\n", pid, procname, (long)afs_cr_uid(acred));
549 #else
550         afs_warnuser
551             ("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));
552 #endif
553         afs_osi_Free(procname, 256);
554     }
555     return;
556 }
557
558
559 #if defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
560 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
561                 afs_ucred_t * acred, pid_t clid)
562 #else
563 u_int clid = 0;
564 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
565                 afs_ucred_t * acred)
566 #endif
567 {
568     struct vrequest treq;
569     afs_int32 code;
570     struct afs_fakestat_state fakestate;
571
572     AFS_STATCNT(afs_lockctl);
573     if ((code = afs_InitReq(&treq, acred)))
574         return code;
575     afs_InitFakeStat(&fakestate);
576
577     AFS_DISCON_LOCK();
578
579     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
580     if (code) {
581         goto done;
582     }
583 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
584     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
585 #else
586     if (acmd == F_GETLK) {
587 #endif
588         if (af->l_type == F_UNLCK) {
589             code = 0;
590             goto done;
591         }
592         code = HandleGetLock(avc, af, &treq, clid);
593         code = afs_CheckCode(code, &treq, 2);   /* defeat buggy AIX optimz */
594         goto done;
595     } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
596 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
597                || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
598 #else
599         ) {
600 #endif
601     /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU */
602     if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7fffffffffffffffLL))
603         af->l_len = 0;
604     /* next line makes byte range locks always succeed,
605      * even when they should block */
606     if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
607         DoLockWarning(acred);
608         code = 0;
609         goto done;
610     }
611     /* otherwise we can turn this into a whole-file flock */
612     if (af->l_type == F_RDLCK)
613         code = LOCK_SH;
614     else if (af->l_type == F_WRLCK)
615         code = LOCK_EX;
616     else if (af->l_type == F_UNLCK)
617         code = LOCK_UN;
618     else {
619         afs_PutFakeStat(&fakestate);
620         return EINVAL;          /* unknown lock type */
621     }
622     if (((acmd == F_SETLK)
623 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
624          || (acmd == F_RSETLK)
625 #endif
626         ) && code != LOCK_UN)
627         code |= LOCK_NB;        /* non-blocking, s.v.p. */
628 #if defined(AFS_DARWIN_ENV)
629     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
630 #elif defined(AFS_SGI_ENV)
631     AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
632     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
633     AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
634 #else
635     code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
636 #endif
637     code = afs_CheckCode(code, &treq, 3);       /* defeat AIX -O bug */
638     goto done;
639     }
640     code = EINVAL;
641 done:
642     afs_PutFakeStat(&fakestate);
643     AFS_DISCON_UNLOCK();
644     return code;
645 }
646
647
648 /*
649  * Get a description of the first lock which would
650  * block the lock specified.  If the specified lock
651  * would succeed, fill in the lock structure with 'F_UNLCK'.
652  *
653  * To do that, we have to ask the server for the lock
654  * count if:
655  *    1. The file is not locked by this machine.
656  *    2. Asking for write lock, and only the current
657  *       PID has the file read locked.
658  */
659 static int
660 HandleGetLock(struct vcache *avc, struct AFS_FLOCK *af,
661               struct vrequest *areq, int clid)
662 {
663     afs_int32 code;
664     struct AFS_FLOCK flock;
665
666     lockIdSet(&flock, NULL, clid);
667
668     ObtainWriteLock(&avc->lock, 122);
669     if (avc->flockCount == 0) {
670         /*
671          * We don't know ourselves, so ask the server. Unfortunately, we
672          * don't know the pid.  Not even the server knows the pid.  Besides,
673          * the process with the lock is on another machine
674          */
675         code = GetFlockCount(avc, areq);
676         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
677             af->l_type = F_UNLCK;
678             goto unlck_leave;
679         }
680         if (code > 0)
681             af->l_type = F_RDLCK;
682         else
683             af->l_type = F_WRLCK;
684
685         af->l_pid = 0;
686 #if defined(AFS_HAVE_FLOCK_SYSID)
687         af->l_sysid = 0;
688 #endif
689         goto done;
690     }
691
692     if (af->l_type == F_RDLCK) {
693         /*
694          * We want a read lock.  If there are only
695          * read locks, or we are the one with the
696          * write lock, say it is unlocked.
697          */
698         if (avc->flockCount > 0 ||      /* only read locks */
699             !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
700             af->l_type = F_UNLCK;
701             goto unlck_leave;
702         }
703
704         /* one write lock, but who? */
705         af->l_type = F_WRLCK;   /* not us, so lock would block */
706         if (avc->slocks) {      /* we know who, so tell */
707             af->l_pid = avc->slocks->pid;
708 #if defined(AFS_HAVE_FLOCK_SYSID)
709             af->l_sysid = avc->slocks->sysid;
710 #endif
711         } else {
712             af->l_pid = 0;      /* XXX can't happen?? */
713 #if defined(AFS_HAVE_FLOCK_SYSID)
714             af->l_sysid = 0;
715 #endif
716         }
717         goto done;
718     }
719
720     /*
721      * Ok, we want a write lock.  If there is a write lock
722      * already, and it is not this process, we fail.
723      */
724     if (avc->flockCount < 0) {
725         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
726             af->l_type = F_WRLCK;
727             if (avc->slocks) {
728                 af->l_pid = avc->slocks->pid;
729 #if defined(AFS_HAVE_FLOCK_SYSID)
730                 af->l_sysid = avc->slocks->sysid;
731 #endif
732             } else {
733                 af->l_pid = 0;  /* XXX can't happen?? */
734 #if defined(AFS_HAVE_FLOCK_SYSID)
735                 af->l_sysid = 0;
736 #endif
737             }
738             goto done;
739         }
740         /* we are the one with the write lock */
741         af->l_type = F_UNLCK;
742         goto unlck_leave;
743     }
744
745     /*
746      * Want a write lock, and we know there are read locks.
747      * If there is more than one, or it isn't us, we cannot lock.
748      */
749     if ((avc->flockCount > 1)
750         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
751         struct SimpleLocks *slp;
752
753         af->l_type = F_RDLCK;
754         af->l_pid = 0;
755 #if defined(AFS_HAVE_FLOCK_SYSID)
756         af->l_sysid = 0;
757 #endif
758         /* find a pid that isn't our own */
759         for (slp = avc->slocks; slp; slp = slp->next) {
760             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
761                 af->l_pid = slp->pid;
762 #if defined(AFS_HAVE_FLOCK_SYSID)
763                 af->l_sysid = avc->slocks->sysid;
764 #endif
765                 break;
766             }
767         }
768         goto done;
769     }
770
771     /*
772      * Ok, we want a write lock.  If there is a write lock
773      * already, and it is not this process, we fail.
774      */
775     if (avc->flockCount < 0) {
776         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
777             af->l_type = F_WRLCK;
778             if (avc->slocks) {
779                 af->l_pid = avc->slocks->pid;
780 #if defined(AFS_HAVE_FLOCK_SYSID)
781                 af->l_sysid = avc->slocks->sysid;
782 #endif
783             } else {
784                 af->l_pid = 0;  /* XXX can't happen?? */
785 #if defined(AFS_HAVE_FLOCK_SYSID)
786                 af->l_sysid = 0;
787 #endif
788             }
789             goto done;
790         }
791         /* we are the one with the write lock */
792         af->l_type = F_UNLCK;
793         goto unlck_leave;
794     }
795
796     /*
797      * Want a write lock, and we know there are read locks.
798      * If there is more than one, or it isn't us, we cannot lock.
799      */
800     if ((avc->flockCount > 1)
801         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
802         struct SimpleLocks *slp;
803         af->l_type = F_RDLCK;
804         af->l_pid = 0;
805 #if defined(AFS_HAVE_FLOCK_SYSID)
806         af->l_sysid = 0;
807 #endif
808         /* find a pid that isn't our own */
809         for (slp = avc->slocks; slp; slp = slp->next) {
810             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
811                 af->l_pid = slp->pid;
812 #if defined(AFS_HAVE_FLOCK_SYSID)
813                 af->l_sysid = avc->slocks->sysid;
814 #endif
815                 break;
816             }
817         }
818         goto done;
819     }
820
821     /*
822      * Want a write lock, and there is just one read lock, and it
823      * is this process with a read lock.  Ask the server if there
824      * are any more processes with the file locked.
825      */
826     code = GetFlockCount(avc, areq);
827     if (code == 0 || code == 1) {
828         af->l_type = F_UNLCK;
829         goto unlck_leave;
830     }
831     if (code > 0)
832         af->l_type = F_RDLCK;
833     else
834         af->l_type = F_WRLCK;
835     af->l_pid = 0;
836 #if defined(AFS_HAVE_FLOCK_SYSID)
837     af->l_sysid = 0;
838 #endif
839
840   done:
841     af->l_whence = 0;
842     af->l_start = 0;
843     af->l_len = 0;              /* to end of file */
844
845   unlck_leave:
846     ReleaseWriteLock(&avc->lock);
847     return 0;
848 }
849
850 /* Get the 'flock' count from the server.  This comes back in a 'spare'
851  * field from a GetStatus RPC.  If we have any problems with the RPC,
852  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
853  * the spare field will be a zero, saying the file is unlocked.  This is
854  * OK, as a further 'lock' request will do the right thing.
855  */
856 static int
857 GetFlockCount(struct vcache *avc, struct vrequest *areq)
858 {
859     struct afs_conn *tc;
860     afs_int32 code;
861     struct AFSFetchStatus OutStatus;
862     struct AFSCallBack CallBack;
863     struct AFSVolSync tsync;
864     struct rx_connection *rxconn;
865     int temp;
866     XSTATS_DECLS;
867     temp = areq->flags & O_NONBLOCK;
868     areq->flags |= O_NONBLOCK;
869
870     /* If we're disconnected, lie and say that we've got no locks. Ick */
871     if (AFS_IS_DISCONNECTED)
872         return 0;
873         
874     do {
875         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
876         if (tc) {
877             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
878             RX_AFS_GUNLOCK();
879             code =
880                 RXAFS_FetchStatus(rxconn, (struct AFSFid *)&avc->f.fid.Fid,
881                                   &OutStatus, &CallBack, &tsync);
882             RX_AFS_GLOCK();
883             XSTATS_END_TIME;
884         } else
885             code = -1;
886     } while (afs_Analyze
887              (tc, rxconn, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
888               SHARED_LOCK, NULL));
889
890     if (temp)
891         areq->flags &= ~O_NONBLOCK;
892
893     if (code) {
894         return (0);             /* failed, say it is 'unlocked' */
895     } else {
896         return ((int)OutStatus.lockCount);
897     }
898 }
899