2 * Copyright 2000, International Business Machines Corporation and others.
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
15 #include <afsconfig.h>
16 #include "afs/param.h"
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"
31 /* Static prototypes */
32 static int HandleGetLock(register struct vcache *avc,
33 register struct AFS_FLOCK *af,
34 register struct vrequest *areq, int clid);
35 static int GetFlockCount(struct vcache *avc, struct vrequest *areq);
36 static int lockIdcmp2(struct AFS_FLOCK *flock1, struct vcache *vp,
37 register struct SimpleLocks *alp, int onlymine,
39 static void DoLockWarning(void);
41 /* int clid; * non-zero on SGI, OSF, SunOS, Darwin, xBSD ** XXX ptr type */
43 #if defined(AFS_SUN5_ENV)
45 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
47 proc_t *procp = ttoproc(curthread);
52 slp->pid = procp->p_pid;
54 slp->sysid = procp->p_sysid;
55 slp->pid = procp->p_epid;
60 flock->l_pid = procp->p_pid;
62 flock->l_sysid = procp->p_sysid;
63 flock->l_pid = procp->p_epid;
67 #elif defined(AFS_SGI_ENV)
69 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
71 # if defined(AFS_SGI65_ENV)
73 get_current_flid(&flid);
75 afs_proc_t *procp = OSI_GET_CURRENT_PROCP();
80 slp->sysid = flid.fl_sysid;
82 slp->sysid = OSI_GET_CURRENT_SYSID();
87 flock->l_sysid = flid.fl_sysid;
89 flock->l_sysid = OSI_GET_CURRENT_SYSID();
94 #elif defined(AFS_AIX_ENV)
96 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
98 # if !defined(AFS_AIX32_ENV)
99 afs_proc_t *procp = u.u_procp;
103 # if defined(AFS_AIX41_ENV)
106 # elif defined(AFS_AIX32_ENV)
107 slp->sysid = u.u_sysid;
110 slp->sysid = procp->p_sysid;
111 slp->pid = prcop->p_epid;
114 # if defined(AFS_AIX41_ENV)
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;
121 flock->l_sysid = procp->p_sysid;
122 flock->l_pid = procp->p_epid;
126 #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
128 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
136 #elif defined(AFS_LINUX20_ENV) || defined(AFS_HPUX_ENV)
138 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
143 flock->l_pid = getpid();
148 lockIdSet(struct AFS_FLOCK *flock, struct SimpleLocks *slp, int clid)
151 slp->pid = u.u_procp->p_pid;
153 flock->l_pid = u.u_procp->p_pid;
158 /* return 1 (true) if specified flock does not match alp (if
159 * specified), or any of the slp structs (if alp == 0)
161 /* I'm not sure that the comparsion of flock->pid to p_ppid
162 * is correct. Should that be a comparision of alp (or slp) ->pid
163 * to p_ppid? Especially in the context of the lower loop, where
164 * the repeated comparison doesn't make much sense...
166 /* onlymine - don't match any locks which are held by my parent */
167 /* clid - only irix 6.5 */
170 lockIdcmp2(struct AFS_FLOCK *flock1, struct vcache *vp,
171 register struct SimpleLocks *alp, int onlymine, int clid)
173 register struct SimpleLocks *slp;
174 #if defined(AFS_SUN5_ENV)
175 register proc_t *procp = ttoproc(curthread);
177 #if !defined(AFS_AIX41_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_SGI65_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
179 afs_proc_t *procp = curprocp;
180 #else /* AFS_SGI64_ENV */
181 afs_proc_t *procp = u.u_procp;
182 #endif /* AFS_SGI64_ENV */
187 #if defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
188 if (flock1->l_sysid != alp->sysid) {
192 if ((flock1->l_pid == alp->pid) ||
193 #if defined(AFS_AIX41_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_HPUX_ENV)
194 (!onlymine && (flock1->l_pid == getppid()))
196 #if defined(AFS_SGI65_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
197 /* XXX check this. used to be *only* irix for some reason. */
198 (!onlymine && (flock1->l_pid == clid))
200 (!onlymine && (flock1->l_pid == procp->p_ppid))
209 for (slp = vp->slocks; slp; slp = slp->next) {
210 #if defined(AFS_HAVE_FLOCK_SYSID)
211 if (flock1->l_sysid != slp->sysid) {
215 if (flock1->l_pid == slp->pid) {
219 return (1); /* failure */
223 /* we don't send multiple read flocks to the server, but rather just count
224 them up ourselves. Of course, multiple write locks are incompatible.
226 Note that we should always try to release a lock, even if we have
227 a network problem sending the release command through, since often
228 a lock is released on a close call, when the user can't retry anyway.
230 After we remove it from our structure, the lock will no longer be
231 kept alive, and the server should time it out within a few minutes.
233 94.04.13 add "force" parameter. If a child explicitly unlocks a
234 file, I guess we'll permit it. however, we don't want simple,
235 innocent closes by children to unlock files in the parent process.
237 If called when disconnected support is unabled, the discon_lock must
240 /* clid - nonzero on sgi sunos osf1 only */
242 HandleFlock(register struct vcache *avc, int acom, struct vrequest *areq,
243 pid_t clid, int onlymine)
246 struct SimpleLocks *slp, *tlp, **slpp;
248 struct AFSVolSync tsync;
250 struct AFS_FLOCK flock;
252 AFS_STATCNT(HandleFlock);
253 code = 0; /* default when we don't make any network calls */
254 lockIdSet(&flock, NULL, clid);
256 #if defined(AFS_SGI_ENV)
257 osi_Assert(valusema(&avc->vc_rwlock) <= 0);
258 osi_Assert(OSI_GET_LOCKID() == avc->vc_rwlockid);
260 ObtainWriteLock(&avc->lock, 118);
261 if (acom & LOCK_UN) {
266 /* If the lock is held exclusive, then only the owning process
267 * or a child can unlock it. Use pid and ppid because they are
268 * unique identifiers.
270 if ((avc->flockCount < 0) && (getpid() != avc->ownslock)) {
272 if (onlymine || (getppid() != avc->ownslock)) {
274 if (onlymine || (u.u_procp->p_ppid != avc->ownslock)) {
276 ReleaseWriteLock(&avc->lock);
281 if (lockIdcmp2(&flock, avc, NULL, onlymine, clid)) {
282 ReleaseWriteLock(&avc->lock);
288 if (avc->flockCount == 0) {
289 ReleaseWriteLock(&avc->lock);
293 /* unlock the lock */
294 if (avc->flockCount > 0) {
296 for (slp = *slpp; slp;) {
297 if (!lockIdcmp2(&flock, avc, slp, onlymine, clid)) {
299 tlp = *slpp = slp->next;
300 osi_FreeSmallSpace(slp);
307 } else if (avc->flockCount == -1) {
308 afs_StoreAllSegments(avc, areq, AFS_ASYNC); /* fsync file early */
310 /* And remove the (only) exclusive lock entry from the list... */
311 osi_FreeSmallSpace(avc->slocks);
314 if (avc->flockCount == 0) {
315 if (!AFS_IS_DISCONNECTED) {
317 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
319 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
321 code = RXAFS_ReleaseLock(tc->id, (struct AFSFid *)
322 &avc->f.fid.Fid, &tsync);
328 (tc, code, &avc->f.fid, areq,
329 AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL));
331 /*printf("Network is dooooooowwwwwwwnnnnnnn\n");*/
336 while (1) { /* set a new lock */
338 * Upgrading from shared locks to Exclusive and vice versa
339 * is a bit tricky and we don't really support it yet. But
340 * we try to support the common used one which is upgrade
341 * a shared lock to an exclusive for the same process...
343 if ((avc->flockCount > 0 && (acom & LOCK_EX))
344 || (avc->flockCount == -1 && (acom & LOCK_SH))) {
346 * Upgrading from shared locks to an exclusive one:
347 * For now if all the shared locks belong to the
348 * same process then we unlock them on the server
349 * and proceed with the upgrade. Unless we change the
350 * server's locking interface impl we prohibit from
351 * unlocking other processes's shared locks...
352 * Upgrading from an exclusive lock to a shared one:
353 * Again only allowed to be done by the same process.
356 for (slp = *slpp; slp;) {
358 (&flock, avc, slp, 1 /*!onlymine */ , clid)) {
363 tlp = *slpp = slp->next;
364 osi_FreeSmallSpace(slp);
372 if (!code && avc->flockCount == 0) {
373 if (!AFS_IS_DISCONNECTED) {
375 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
378 (AFS_STATS_FS_RPCIDX_RELEASELOCK);
381 RXAFS_ReleaseLock(tc->id,
382 (struct AFSFid *)&avc->
389 (tc, code, &avc->f.fid, areq,
390 AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK,
394 } else if (avc->flockCount == -1 && (acom & LOCK_EX)) {
395 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
401 /* compatible here, decide if needs to go to file server. If
402 * we've already got the file locked (and thus read-locked, since
403 * we've already checked for compatibility), we shouldn't send
404 * the call through to the server again */
405 if (avc->flockCount == 0) {
406 /* we're the first on our block, send the call through */
407 lockType = ((acom & LOCK_EX) ? LockWrite : LockRead);
408 if (!AFS_IS_DISCONNECTED) {
410 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
412 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
414 code = RXAFS_SetLock(tc->id, (struct AFSFid *)
415 &avc->f.fid.Fid, lockType,
422 (tc, code, &avc->f.fid, areq,
423 AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK,
426 /* XXX - Should probably try and log this when we're
427 * XXX - running with logging enabled. But it's horrid
429 code = 0; /* pretend we worked - ick!!! */
431 code = 0; /* otherwise, pretend things worked */
434 slp = (struct SimpleLocks *)
435 osi_AllocSmallSpace(sizeof(struct SimpleLocks));
436 if (acom & LOCK_EX) {
441 /* Record unique id of process owning exclusive lock. */
442 avc->ownslock = getpid();
445 slp->type = LockWrite;
448 avc->flockCount = -1;
450 slp->type = LockRead;
451 slp->next = avc->slocks;
456 lockIdSet(&flock, slp, clid);
459 /* now, if we got EWOULDBLOCK, and we're supposed to wait, we do */
460 if (((code == EWOULDBLOCK) || (code == EAGAIN) ||
461 (code == UAEWOULDBLOCK) || (code == UAEAGAIN))
462 && !(acom & LOCK_NB)) {
463 /* sleep for a second, allowing interrupts */
464 ReleaseWriteLock(&avc->lock);
465 #if defined(AFS_SGI_ENV)
466 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
468 code = afs_osi_Wait(1000, NULL, 1);
469 #if defined(AFS_SGI_ENV)
470 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
472 ObtainWriteLock(&avc->lock, 120);
474 code = EINTR; /* return this if ^C typed */
481 ReleaseWriteLock(&avc->lock);
482 code = afs_CheckCode(code, areq, 1); /* defeat a buggy AIX optimization */
487 /* warn a user that a lock has been ignored */
488 afs_int32 lastWarnTime = 0; /* this is used elsewhere */
492 register afs_int32 now;
495 AFS_STATCNT(DoLockWarning);
496 /* check if we've already warned someone recently */
497 if (now < lastWarnTime + 120)
500 /* otherwise, it is time to nag the user */
502 #ifdef AFS_LINUX26_ENV
504 ("afs: byte-range locks only enforced for processes on this machine.\n");
507 ("afs: byte-range lock/unlock ignored; make sure no one else is running this program.\n");
512 #if defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
513 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
514 afs_ucred_t * acred, pid_t clid)
517 int afs_lockctl(struct vcache * avc, struct AFS_FLOCK * af, int acmd,
521 struct vrequest treq;
523 struct afs_fakestat_state fakestate;
525 AFS_STATCNT(afs_lockctl);
526 if ((code = afs_InitReq(&treq, acred)))
528 afs_InitFakeStat(&fakestate);
532 code = afs_EvalFakeStat(&avc, &fakestate, &treq);
536 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
537 if ((acmd == F_GETLK) || (acmd == F_RGETLK)) {
539 if (acmd == F_GETLK) {
541 if (af->l_type == F_UNLCK) {
545 code = HandleGetLock(avc, af, &treq, clid);
546 code = afs_CheckCode(code, &treq, 2); /* defeat buggy AIX optimz */
548 } else if ((acmd == F_SETLK) || (acmd == F_SETLKW)
549 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
550 || (acmd == F_RSETLK) || (acmd == F_RSETLKW)) {
554 /* this next check is safer when left out, but more applications work
555 * with it in. However, they fail in race conditions. The question is
556 * what to do for people who don't have source to their application;
557 * this way at least, they can get work done */
558 #ifdef AFS_LINUX24_ENV
559 if (af->l_len == OFFSET_MAX)
560 af->l_len = 0; /* since some systems indicate it as EOF */
562 if (af->l_len == 0x7fffffff)
563 af->l_len = 0; /* since some systems indicate it as EOF */
564 #ifdef AFS_LINUX_64BIT_KERNEL
565 if (af->l_len == LONG_MAX)
566 af->l_len = 0; /* since some systems indicate it as EOF */
569 /* Java VMs ask for l_len=(long)-1 regardless of OS/CPU; bottom 32 bits
570 * sometimes get masked off by OS */
571 if ((sizeof(af->l_len) == 8) && (af->l_len == 0x7ffffffffffffffeLL))
573 /* next line makes byte range locks always succeed,
574 * even when they should block */
575 if (af->l_whence != 0 || af->l_start != 0 || af->l_len != 0) {
580 /* otherwise we can turn this into a whole-file flock */
581 if (af->l_type == F_RDLCK)
583 else if (af->l_type == F_WRLCK)
585 else if (af->l_type == F_UNLCK)
588 afs_PutFakeStat(&fakestate);
589 return EINVAL; /* unknown lock type */
591 if (((acmd == F_SETLK)
592 #if (defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)) && !defined(AFS_SUN58_ENV)
593 || (acmd == F_RSETLK)
595 ) && code != LOCK_UN)
596 code |= LOCK_NB; /* non-blocking, s.v.p. */
597 #if defined(AFS_DARWIN_ENV)
598 code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
599 #elif defined(AFS_SGI_ENV)
600 AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE);
601 code = HandleFlock(avc, code, &treq, clid, 0 /*!onlymine */ );
602 AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE);
604 code = HandleFlock(avc, code, &treq, 0, 0 /*!onlymine */ );
606 code = afs_CheckCode(code, &treq, 3); /* defeat AIX -O bug */
611 afs_PutFakeStat(&fakestate);
618 * Get a description of the first lock which would
619 * block the lock specified. If the specified lock
620 * would succeed, fill in the lock structure with 'F_UNLCK'.
622 * To do that, we have to ask the server for the lock
624 * 1. The file is not locked by this machine.
625 * 2. Asking for write lock, and only the current
626 * PID has the file read locked.
629 HandleGetLock(register struct vcache *avc, register struct AFS_FLOCK *af,
630 register struct vrequest *areq, int clid)
632 register afs_int32 code;
633 struct AFS_FLOCK flock;
635 lockIdSet(&flock, NULL, clid);
637 ObtainWriteLock(&avc->lock, 122);
638 if (avc->flockCount == 0) {
640 * We don't know ourselves, so ask the server. Unfortunately, we
641 * don't know the pid. Not even the server knows the pid. Besides,
642 * the process with the lock is on another machine
644 code = GetFlockCount(avc, areq);
645 if (code == 0 || (af->l_type == F_RDLCK && code > 0)) {
646 af->l_type = F_UNLCK;
650 af->l_type = F_RDLCK;
652 af->l_type = F_WRLCK;
655 #if defined(AFS_HAVE_FLOCK_SYSID)
661 if (af->l_type == F_RDLCK) {
663 * We want a read lock. If there are only
664 * read locks, or we are the one with the
665 * write lock, say it is unlocked.
667 if (avc->flockCount > 0 || /* only read locks */
668 !lockIdcmp2(&flock, avc, NULL, 1, clid)) {
669 af->l_type = F_UNLCK;
673 /* one write lock, but who? */
674 af->l_type = F_WRLCK; /* not us, so lock would block */
675 if (avc->slocks) { /* we know who, so tell */
676 af->l_pid = avc->slocks->pid;
677 #if defined(AFS_HAVE_FLOCK_SYSID)
678 af->l_sysid = avc->slocks->sysid;
681 af->l_pid = 0; /* XXX can't happen?? */
682 #if defined(AFS_HAVE_FLOCK_SYSID)
690 * Ok, we want a write lock. If there is a write lock
691 * already, and it is not this process, we fail.
693 if (avc->flockCount < 0) {
694 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
695 af->l_type = F_WRLCK;
697 af->l_pid = avc->slocks->pid;
698 #if defined(AFS_HAVE_FLOCK_SYSID)
699 af->l_sysid = avc->slocks->sysid;
702 af->l_pid = 0; /* XXX can't happen?? */
703 #if defined(AFS_HAVE_FLOCK_SYSID)
709 /* we are the one with the write lock */
710 af->l_type = F_UNLCK;
715 * Want a write lock, and we know there are read locks.
716 * If there is more than one, or it isn't us, we cannot lock.
718 if ((avc->flockCount > 1)
719 || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
720 struct SimpleLocks *slp;
722 af->l_type = F_RDLCK;
724 #if defined(AFS_HAVE_FLOCK_SYSID)
727 /* find a pid that isn't our own */
728 for (slp = avc->slocks; slp; slp = slp->next) {
729 if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
730 af->l_pid = slp->pid;
731 #if defined(AFS_HAVE_FLOCK_SYSID)
732 af->l_sysid = avc->slocks->sysid;
741 * Ok, we want a write lock. If there is a write lock
742 * already, and it is not this process, we fail.
744 if (avc->flockCount < 0) {
745 if (lockIdcmp2(&flock, avc, NULL, 1, clid)) {
746 af->l_type = F_WRLCK;
748 af->l_pid = avc->slocks->pid;
749 #if defined(AFS_HAVE_FLOCK_SYSID)
750 af->l_sysid = avc->slocks->sysid;
753 af->l_pid = 0; /* XXX can't happen?? */
754 #if defined(AFS_HAVE_FLOCK_SYSID)
760 /* we are the one with the write lock */
761 af->l_type = F_UNLCK;
766 * Want a write lock, and we know there are read locks.
767 * If there is more than one, or it isn't us, we cannot lock.
769 if ((avc->flockCount > 1)
770 || lockIdcmp2(&flock, avc, NULL, 1, clid)) {
771 struct SimpleLocks *slp;
772 af->l_type = F_RDLCK;
774 #if defined(AFS_HAVE_FLOCK_SYSID)
777 /* find a pid that isn't our own */
778 for (slp = avc->slocks; slp; slp = slp->next) {
779 if (lockIdcmp2(&flock, NULL, slp, 1, clid)) {
780 af->l_pid = slp->pid;
781 #if defined(AFS_HAVE_FLOCK_SYSID)
782 af->l_sysid = avc->slocks->sysid;
791 * Want a write lock, and there is just one read lock, and it
792 * is this process with a read lock. Ask the server if there
793 * are any more processes with the file locked.
795 code = GetFlockCount(avc, areq);
796 if (code == 0 || code == 1) {
797 af->l_type = F_UNLCK;
801 af->l_type = F_RDLCK;
803 af->l_type = F_WRLCK;
805 #if defined(AFS_HAVE_FLOCK_SYSID)
812 af->l_len = 0; /* to end of file */
815 ReleaseWriteLock(&avc->lock);
819 /* Get the 'flock' count from the server. This comes back in a 'spare'
820 * field from a GetStatus RPC. If we have any problems with the RPC,
821 * we lie and say the file is unlocked. If we ask any 'old' fileservers,
822 * the spare field will be a zero, saying the file is unlocked. This is
823 * OK, as a further 'lock' request will do the right thing.
826 GetFlockCount(struct vcache *avc, struct vrequest *areq)
828 register struct afs_conn *tc;
829 register afs_int32 code;
830 struct AFSFetchStatus OutStatus;
831 struct AFSCallBack CallBack;
832 struct AFSVolSync tsync;
835 temp = areq->flags & O_NONBLOCK;
836 areq->flags |= O_NONBLOCK;
838 /* If we're disconnected, lie and say that we've got no locks. Ick */
839 if (AFS_IS_DISCONNECTED)
843 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
845 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
848 RXAFS_FetchStatus(tc->id, (struct AFSFid *)&avc->f.fid.Fid,
849 &OutStatus, &CallBack, &tsync);
855 (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
859 areq->flags &= ~O_NONBLOCK;
862 return (0); /* failed, say it is 'unlocked' */
864 return ((int)OutStatus.lockCount);