commit:     895a7417f41c4f6617e22dc0ebf001de526fc666
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Mar 25 05:13:43 2020 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Mar 25 07:25:00 2020 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=895a7417

_lockfile_iteration: index locked files by inode (bug 714480)

Index each locked file by inode so that it's possible to detect when
there's an attempt to lock the same inode twice, since in that case
the lock will not behave as intended with the default fcntl.lockf
function.

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

 lib/portage/locks.py | 35 +++++++++++++++++++++++------------
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/lib/portage/locks.py b/lib/portage/locks.py
index 7bb0542dd..2459b12b6 100644
--- a/lib/portage/locks.py
+++ b/lib/portage/locks.py
@@ -84,7 +84,21 @@ def _get_lock_fn():
        return _lock_fn
 
 
-_open_fds = set()
+_open_fds = {}
+_open_inodes = {}
+
+class _lock_manager(object):
+       __slots__ = ('fd', 'inode_key')
+       def __init__(self, fd, fstat_result):
+               self.fd = fd
+               self.inode_key = (fstat_result.st_dev, fstat_result.st_ino)
+               _open_fds[fd] = self
+               _open_inodes[self.inode_key] = self
+       def close(self):
+               os.close(self.fd)
+               del _open_fds[self.fd]
+               del _open_inodes[self.inode_key]
+
 
 def _close_fds():
        """
@@ -93,8 +107,8 @@ def _close_fds():
        safely after a fork without exec, unlike the _setup_pipes close_fds
        behavior.
        """
-       while _open_fds:
-               os.close(_open_fds.pop())
+       for fd in list(_open_fds.values()):
+               fd.close()
 
 def lockdir(mydir, flags=0):
        return lockfile(mydir, wantnewlockfile=1, flags=flags)
@@ -296,6 +310,7 @@ def _lockfile_iteration(mypath, wantnewlockfile=False, 
unlinkfile=False,
                else:
                        raise
 
+       fstat_result = None
        if isinstance(lockfilename, basestring) and myfd != HARDLINK_FD and 
unlinkfile:
                try:
                        (removed, fstat_result) = _lockfile_was_removed(myfd, 
lockfilename)
@@ -321,7 +336,7 @@ def _lockfile_iteration(mypath, wantnewlockfile=False, 
unlinkfile=False,
                                fcntl.fcntl(myfd, fcntl.F_SETFD,
                                        fcntl.fcntl(myfd, fcntl.F_GETFD) | 
fcntl.FD_CLOEXEC)
 
-               _open_fds.add(myfd)
+               _lock_manager(myfd, os.fstat(myfd) if fstat_result is None else 
fstat_result)
 
        writemsg(str((lockfilename, myfd, unlinkfile)) + "\n", 1)
        return (lockfilename, myfd, unlinkfile, locking_method)
@@ -442,8 +457,7 @@ def unlockfile(mytuple):
                not os.path.exists(lockfilename):
                writemsg(_("lockfile does not exist '%s'\n") % lockfilename, 1)
                if myfd is not None:
-                       os.close(myfd)
-                       _open_fds.remove(myfd)
+                       _open_fds[myfd].close()
                return False
 
        try:
@@ -453,8 +467,7 @@ def unlockfile(mytuple):
                locking_method(myfd, fcntl.LOCK_UN)
        except OSError:
                if isinstance(lockfilename, basestring):
-                       os.close(myfd)
-                       _open_fds.remove(myfd)
+                       _open_fds[myfd].close()
                raise IOError(_("Failed to unlock file '%s'\n") % lockfilename)
 
        try:
@@ -475,8 +488,7 @@ def unlockfile(mytuple):
                                locking_method(myfd, fcntl.LOCK_UN)
                        else:
                                writemsg(_("lockfile does not exist '%s'\n") % 
lockfilename, 1)
-                               os.close(myfd)
-                               _open_fds.remove(myfd)
+                               _open_fds[myfd].close()
                                return False
        except SystemExit:
                raise
@@ -488,8 +500,7 @@ def unlockfile(mytuple):
        # fd originally, and the caller might not like having their
        # open fd closed automatically on them.
        if isinstance(lockfilename, basestring):
-               os.close(myfd)
-               _open_fds.remove(myfd)
+               _open_fds[myfd].close()
 
        return True
 

Reply via email to