931a72c64e4d7084e8f7d6325beaa419da3a92ff
[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                     /* We've just re-grabbed an exclusive lock, so we don't
412                      * need to contact the fileserver, and we don't need to
413                      * add the lock to avc->slocks (since we already have a
414                      * lock there). So, we are done. */
415                     break;
416                 }
417             }
418             if (code == 0) {
419                 /* compatible here, decide if needs to go to file server.  If
420                  * we've already got the file locked (and thus read-locked, since
421                  * we've already checked for compatibility), we shouldn't send
422                  * the call through to the server again */
423                 if (avc->flockCount == 0) {
424                     /* we're the first on our block, send the call through */
425                     lockType = ((acom & LOCK_EX) ? LockWrite : LockRead);
426                     if (!AFS_IS_DISCONNECTED) {
427                         do {
428                             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
429                             if (tc) {
430                                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
431                                 RX_AFS_GUNLOCK();
432                                 code = RXAFS_SetLock(tc->id, (struct AFSFid *)
433                                                      &avc->f.fid.Fid, lockType,
434                                                      &tsync);
435                                 RX_AFS_GLOCK();
436                                 XSTATS_END_TIME;
437                             } else
438                                 code = -1;
439                         } while (afs_Analyze
440                                  (tc, code, &avc->f.fid, areq,
441                                   AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK,
442                                   NULL));
443                         if ((lockType == LockWrite) && (code == VREADONLY))
444                             code = EBADF; /* per POSIX; VREADONLY == EROFS */
445                     } else
446                         /* XXX - Should probably try and log this when we're
447                          * XXX - running with logging enabled. But it's horrid
448                          */
449                         code = 0; /* pretend we worked - ick!!! */
450                 } else
451                     code = 0;   /* otherwise, pretend things worked */
452             }
453             if (code == 0) {
454                 slp = (struct SimpleLocks *)
455                     osi_AllocSmallSpace(sizeof(struct SimpleLocks));
456                 if (acom & LOCK_EX) {
457
458 /* defect 3083 */
459
460 #ifdef AFS_AIX_ENV
461                     /* Record unique id of process owning exclusive lock. */
462                     avc->ownslock = getpid();
463 #endif
464
465                     slp->type = LockWrite;
466                     slp->next = NULL;
467                     avc->slocks = slp;
468                     avc->flockCount = -1;
469                 } else {
470                     slp->type = LockRead;
471                     slp->next = avc->slocks;
472                     avc->slocks = slp;
473                     avc->flockCount++;
474                 }
475
476                 lockIdSet(&flock, slp, clid);
477                 break;
478             }
479             /* now, if we got EWOULDBLOCK, and we're supposed to wait, we do */
480             if (((code == EWOULDBLOCK) || (code == EAGAIN) || 
481                  (code == UAEWOULDBLOCK) || (code == UAEAGAIN))
482                 && !(acom & LOCK_NB)) {
483                 /* sleep for a second, allowing interrupts */
484                 ReleaseWriteLock(&avc->lock);
485 #if defined(AFS_SGI_ENV)
486                 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
487 #endif
488                 code = afs_osi_Wait(1000, NULL, 1);
489 #if defined(AFS_SGI_ENV)
490                 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
491 #endif
492                 ObtainWriteLock(&avc->lock, 120);
493                 if (code) {
494                     code = EINTR;       /* return this if ^C typed */
495                     break;
496                 }
497             } else
498                 break;
499         }                       /* while loop */
500     }
501     ReleaseWriteLock(&avc->lock);
502     code = afs_CheckCode(code, areq, 1);        /* defeat a buggy AIX optimization */
503     return code;
504 }
505
506
507 /* warn a user that a lock has been ignored */
508 afs_int32 lastWarnTime = 0;     /* this is used elsewhere */
509 static afs_int32 lastWarnPid = 0;
510 static void
511 DoLockWarning(afs_ucred_t * acred)
512 {
513     afs_int32 now;
514     pid_t pid = MyPidxx2Pid(MyPidxx);
515     char *procname;
516
517     now = osi_Time();
518
519     AFS_STATCNT(DoLockWarning);
520     /* check if we've already warned this user recently */
521     if (!((now < lastWarnTime + 120) && (lastWarnPid == pid))) {
522         procname = afs_osi_Alloc(256);
523
524         if (!procname)
525             return;
526
527         /* Copies process name to allocated procname, see osi_machdeps for details of macro */
528         osi_procname(procname, 256);
529         procname[255] = '\0';
530
531         /* otherwise, it is time to nag the user */
532         lastWarnTime = now;
533         lastWarnPid = pid;
534 #ifdef AFS_LINUX26_ENV
535         afs_warnuser
536             ("afs: byte-range locks only enforced for processes on this machine (pid %d (%s), user %ld).\n", pid, procname, (long)afs_cr_uid(acred));
537 #else
538         afs_warnuser
539             ("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));
540 #endif
541         afs_osi_Free(procname, 256);
542     }
543     return;
544 }
545
546
547 #if defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
548 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
549                 afs_ucred_t * acred, pid_t clid)
550 #else
551 u_int clid = 0;
552 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
553                 afs_ucred_t * acred)
554 #endif
555 {
556     struct vrequest treq;
557     afs_int32 code;
558     struct afs_fakestat_state fakestate;
559
560     AFS_STATCNT(afs_lockctl);
561     if ((code = afs_InitReq(&treq, acred)))
562         return code;
563     afs_InitFakeStat(&fakestate);
564
565     AFS_DISCON_LOCK();
566
567     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
568     if (code) {
569         goto done;
570     }
571 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
572     if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
573 #else
574     if (acmd == F_GETLK) {
575 #endif
576         if (af->l_type == F_UNLCK) {
577             code = 0;
578             goto done;
579         }
580         code = HandleGetLock(avc, af, &treq, clid);
581         code = afs_CheckCode(code, &treq, 2);   /* defeat buggy AIX optimz */
582         goto done;
583     } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
584 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
585                || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
586 #else
587         ) {
588 #endif
589     /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU */
590     if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7fffffffffffffffLL))
591         af->l_len = 0;
592     /* next line makes byte range locks always succeed,
593      * even when they should block */
594     if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
595         DoLockWarning(acred);
596         code = 0;
597         goto done;
598     }
599     /* otherwise we can turn this into a whole-file flock */
600     if (af->l_type == F_RDLCK)
601         code = LOCK_SH;
602     else if (af->l_type == F_WRLCK)
603         code = LOCK_EX;
604     else if (af->l_type == F_UNLCK)
605         code = LOCK_UN;
606     else {
607         afs_PutFakeStat(&fakestate);
608         return EINVAL;          /* unknown lock type */
609     }
610     if (((acmd == F_SETLK)
611 #if     (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
612          || (acmd == F_RSETLK)
613 #endif
614         ) && code != LOCK_UN)
615         code |= LOCK_NB;        /* non-blocking, s.v.p. */
616 #if defined(AFS_DARWIN_ENV)
617     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
618 #elif defined(AFS_SGI_ENV)
619     AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
620     code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
621     AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
622 #else
623     code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
624 #endif
625     code = afs_CheckCode(code, &treq, 3);       /* defeat AIX -O bug */
626     goto done;
627     }
628     code = EINVAL;
629 done:
630     afs_PutFakeStat(&fakestate);
631     AFS_DISCON_UNLOCK();
632     return code;
633 }
634
635
636 /*
637  * Get a description of the first lock which would
638  * block the lock specified.  If the specified lock
639  * would succeed, fill in the lock structure with 'F_UNLCK'.
640  *
641  * To do that, we have to ask the server for the lock
642  * count if:
643  *    1. The file is not locked by this machine.
644  *    2. Asking for write lock, and only the current
645  *       PID has the file read locked.
646  */
647 static int
648 HandleGetLock(struct vcache *avc, struct AFS_FLOCK *af,
649               struct vrequest *areq, int clid)
650 {
651     afs_int32 code;
652     struct AFS_FLOCK flock;
653
654     lockIdSet(&flock, NULL, clid);
655
656     ObtainWriteLock(&avc->lock, 122);
657     if (avc->flockCount == 0) {
658         /*
659          * We don't know ourselves, so ask the server. Unfortunately, we
660          * don't know the pid.  Not even the server knows the pid.  Besides,
661          * the process with the lock is on another machine
662          */
663         code = GetFlockCount(avc, areq);
664         if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
665             af->l_type = F_UNLCK;
666             goto unlck_leave;
667         }
668         if (code > 0)
669             af->l_type = F_RDLCK;
670         else
671             af->l_type = F_WRLCK;
672
673         af->l_pid = 0;
674 #if defined(AFS_HAVE_FLOCK_SYSID)
675         af->l_sysid = 0;
676 #endif
677         goto done;
678     }
679
680     if (af->l_type == F_RDLCK) {
681         /*
682          * We want a read lock.  If there are only
683          * read locks, or we are the one with the
684          * write lock, say it is unlocked.
685          */
686         if (avc->flockCount > 0 ||      /* only read locks */
687             !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
688             af->l_type = F_UNLCK;
689             goto unlck_leave;
690         }
691
692         /* one write lock, but who? */
693         af->l_type = F_WRLCK;   /* not us, so lock would block */
694         if (avc->slocks) {      /* we know who, so tell */
695             af->l_pid = avc->slocks->pid;
696 #if defined(AFS_HAVE_FLOCK_SYSID)
697             af->l_sysid = avc->slocks->sysid;
698 #endif
699         } else {
700             af->l_pid = 0;      /* XXX can't happen?? */
701 #if defined(AFS_HAVE_FLOCK_SYSID)
702             af->l_sysid = 0;
703 #endif
704         }
705         goto done;
706     }
707
708     /*
709      * Ok, we want a write lock.  If there is a write lock
710      * already, and it is not this process, we fail.
711      */
712     if (avc->flockCount < 0) {
713         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
714             af->l_type = F_WRLCK;
715             if (avc->slocks) {
716                 af->l_pid = avc->slocks->pid;
717 #if defined(AFS_HAVE_FLOCK_SYSID)
718                 af->l_sysid = avc->slocks->sysid;
719 #endif
720             } else {
721                 af->l_pid = 0;  /* XXX can't happen?? */
722 #if defined(AFS_HAVE_FLOCK_SYSID)
723                 af->l_sysid = 0;
724 #endif
725             }
726             goto done;
727         }
728         /* we are the one with the write lock */
729         af->l_type = F_UNLCK;
730         goto unlck_leave;
731     }
732
733     /*
734      * Want a write lock, and we know there are read locks.
735      * If there is more than one, or it isn't us, we cannot lock.
736      */
737     if ((avc->flockCount > 1)
738         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
739         struct SimpleLocks *slp;
740
741         af->l_type = F_RDLCK;
742         af->l_pid = 0;
743 #if defined(AFS_HAVE_FLOCK_SYSID)
744         af->l_sysid = 0;
745 #endif
746         /* find a pid that isn't our own */
747         for (slp = avc->slocks; slp; slp = slp->next) {
748             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
749                 af->l_pid = slp->pid;
750 #if defined(AFS_HAVE_FLOCK_SYSID)
751                 af->l_sysid = avc->slocks->sysid;
752 #endif
753                 break;
754             }
755         }
756         goto done;
757     }
758
759     /*
760      * Ok, we want a write lock.  If there is a write lock
761      * already, and it is not this process, we fail.
762      */
763     if (avc->flockCount < 0) {
764         if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
765             af->l_type = F_WRLCK;
766             if (avc->slocks) {
767                 af->l_pid = avc->slocks->pid;
768 #if defined(AFS_HAVE_FLOCK_SYSID)
769                 af->l_sysid = avc->slocks->sysid;
770 #endif
771             } else {
772                 af->l_pid = 0;  /* XXX can't happen?? */
773 #if defined(AFS_HAVE_FLOCK_SYSID)
774                 af->l_sysid = 0;
775 #endif
776             }
777             goto done;
778         }
779         /* we are the one with the write lock */
780         af->l_type = F_UNLCK;
781         goto unlck_leave;
782     }
783
784     /*
785      * Want a write lock, and we know there are read locks.
786      * If there is more than one, or it isn't us, we cannot lock.
787      */
788     if ((avc->flockCount > 1)
789         || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
790         struct SimpleLocks *slp;
791         af->l_type = F_RDLCK;
792         af->l_pid = 0;
793 #if defined(AFS_HAVE_FLOCK_SYSID)
794         af->l_sysid = 0;
795 #endif
796         /* find a pid that isn't our own */
797         for (slp = avc->slocks; slp; slp = slp->next) {
798             if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
799                 af->l_pid = slp->pid;
800 #if defined(AFS_HAVE_FLOCK_SYSID)
801                 af->l_sysid = avc->slocks->sysid;
802 #endif
803                 break;
804             }
805         }
806         goto done;
807     }
808
809     /*
810      * Want a write lock, and there is just one read lock, and it
811      * is this process with a read lock.  Ask the server if there
812      * are any more processes with the file locked.
813      */
814     code = GetFlockCount(avc, areq);
815     if (code == 0 || code == 1) {
816         af->l_type = F_UNLCK;
817         goto unlck_leave;
818     }
819     if (code > 0)
820         af->l_type = F_RDLCK;
821     else
822         af->l_type = F_WRLCK;
823     af->l_pid = 0;
824 #if defined(AFS_HAVE_FLOCK_SYSID)
825     af->l_sysid = 0;
826 #endif
827
828   done:
829     af->l_whence = 0;
830     af->l_start = 0;
831     af->l_len = 0;              /* to end of file */
832
833   unlck_leave:
834     ReleaseWriteLock(&avc->lock);
835     return 0;
836 }
837
838 /* Get the 'flock' count from the server.  This comes back in a 'spare'
839  * field from a GetStatus RPC.  If we have any problems with the RPC,
840  * we lie and say the file is unlocked.  If we ask any 'old' fileservers,
841  * the spare field will be a zero, saying the file is unlocked.  This is
842  * OK, as a further 'lock' request will do the right thing.
843  */
844 static int
845 GetFlockCount(struct vcache *avc, struct vrequest *areq)
846 {
847     struct afs_conn *tc;
848     afs_int32 code;
849     struct AFSFetchStatus OutStatus;
850     struct AFSCallBack CallBack;
851     struct AFSVolSync tsync;
852     int temp;
853     XSTATS_DECLS;
854     temp = areq->flags & O_NONBLOCK;
855     areq->flags |= O_NONBLOCK;
856
857     /* If we're disconnected, lie and say that we've got no locks. Ick */
858     if (AFS_IS_DISCONNECTED)
859         return 0;
860         
861     do {
862         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
863         if (tc) {
864             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
865             RX_AFS_GUNLOCK();
866             code =
867                 RXAFS_FetchStatus(tc->id, (struct AFSFid *)&avc->f.fid.Fid,
868                                   &OutStatus, &CallBack, &tsync);
869             RX_AFS_GLOCK();
870             XSTATS_END_TIME;
871         } else
872             code = -1;
873     } while (afs_Analyze
874              (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
875               SHARED_LOCK, NULL));
876
877     if (temp)
878         areq->flags &= ~O_NONBLOCK;
879
880     if (code) {
881         return (0);             /* failed, say it is 'unlocked' */
882     } else {
883         return ((int)OutStatus.lockCount);
884     }
885 }
886