commit:     d87fa22e70513915cebe79a1923b65ea0a6fc55e
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Mar 25 05:35:17 2020 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Mar 25 07:39:33 2020 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=d87fa22e

lockfile: raise TryAgain if inode is already locked (bug 714480)

Raise TryAgain if the current inode is already locked by the current
process, since in this case the lock will not behave as intended with
the default fcntl.lockf function. With the alternative fcntl.flock
function, TryAgain is raised earlier.

Bug: https://bugs.gentoo.org/714480
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>

 lib/portage/locks.py                          | 12 ++++++++++--
 lib/portage/tests/locks/test_lock_nonblock.py | 16 +++++++++++++++-
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/lib/portage/locks.py b/lib/portage/locks.py
index 2459b12b6..71321e769 100644
--- a/lib/portage/locks.py
+++ b/lib/portage/locks.py
@@ -89,9 +89,17 @@ _open_inodes = {}
 
 class _lock_manager(object):
        __slots__ = ('fd', 'inode_key')
-       def __init__(self, fd, fstat_result):
+       def __init__(self, fd, fstat_result, path):
                self.fd = fd
                self.inode_key = (fstat_result.st_dev, fstat_result.st_ino)
+               if self.inode_key in _open_inodes:
+                       # This means that the lock is already held by the 
current
+                       # process, so the caller will have to try again. This 
case
+                       # is encountered with the default fcntl.lockf function, 
and
+                       # with the alternative fcntl.flock function TryAgain is
+                       # raised earlier.
+                       os.close(fd)
+                       raise TryAgain(path)
                _open_fds[fd] = self
                _open_inodes[self.inode_key] = self
        def close(self):
@@ -336,7 +344,7 @@ def _lockfile_iteration(mypath, wantnewlockfile=False, 
unlinkfile=False,
                                fcntl.fcntl(myfd, fcntl.F_SETFD,
                                        fcntl.fcntl(myfd, fcntl.F_GETFD) | 
fcntl.FD_CLOEXEC)
 
-               _lock_manager(myfd, os.fstat(myfd) if fstat_result is None else 
fstat_result)
+               _lock_manager(myfd, os.fstat(myfd) if fstat_result is None else 
fstat_result, mypath)
 
        writemsg(str((lockfilename, myfd, unlinkfile)) + "\n", 1)
        return (lockfilename, myfd, unlinkfile, locking_method)

diff --git a/lib/portage/tests/locks/test_lock_nonblock.py 
b/lib/portage/tests/locks/test_lock_nonblock.py
index 2ff7b3527..02ba16ad9 100644
--- a/lib/portage/tests/locks/test_lock_nonblock.py
+++ b/lib/portage/tests/locks/test_lock_nonblock.py
@@ -1,4 +1,4 @@
-# Copyright 2011 Gentoo Foundation
+# Copyright 2011-2020 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import tempfile
@@ -7,6 +7,7 @@ import traceback
 import portage
 from portage import os
 from portage import shutil
+from portage.exception import TryAgain
 from portage.tests import TestCase
 
 class LockNonblockTestCase(TestCase):
@@ -60,3 +61,16 @@ class LockNonblockTestCase(TestCase):
                        if prev_state is not None:
                                os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] = 
prev_state
 
+       def test_competition_with_same_process(self):
+               """
+               Test that at attempt to lock the same file multiple times in the
+               same process will behave as intended (bug 714480).
+               """
+               tempdir = tempfile.mkdtemp()
+               try:
+                       path = os.path.join(tempdir, 'lock_me')
+                       lock = portage.locks.lockfile(path)
+                       self.assertRaises(TryAgain, portage.locks.lockfile, 
path, flags=os.O_NONBLOCK)
+                       portage.locks.unlockfile(lock)
+               finally:
+                       shutil.rmtree(tempdir)

Reply via email to