* [gentoo-portage-dev] [PATCH] locks: handle lock file removal on NFS (bug 636798)
@ 2019-02-07 0:00 Zac Medico
0 siblings, 0 replies; only message in thread
From: Zac Medico @ 2019-02-07 0:00 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
Handle cases where a lock file on NFS has been removed by a concurrent
process that held the lock earlier. Since stat is not reliable for
removed files on NFS with the default file attribute cache behavior
('ac' mount option), create a temporary hardlink in order to prove
that the file path exists on the NFS server.
Bug: https://bugs.gentoo.org/636798
Signed-off-by: Zac Medico <zmedico@gentoo.org>
---
lib/portage/locks.py | 76 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 64 insertions(+), 12 deletions(-)
diff --git a/lib/portage/locks.py b/lib/portage/locks.py
index a4e7ec53f..563f3fa0a 100644
--- a/lib/portage/locks.py
+++ b/lib/portage/locks.py
@@ -20,6 +20,7 @@ from portage.exception import (DirectoryNotFound, FileNotFound,
InvalidData, TryAgain, OperationNotPermitted, PermissionDenied,
ReadOnlyFileSystem)
from portage.util import writemsg
+from portage.util.install_mask import _raise_exc
from portage.localization import _
if sys.hexversion >= 0x3000000:
@@ -148,18 +149,17 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0,
preexisting = os.path.exists(lockfilename)
old_mask = os.umask(000)
try:
- try:
- myfd = os.open(lockfilename, os.O_CREAT|os.O_RDWR, 0o660)
- except OSError as e:
- func_call = "open('%s')" % lockfilename
- if e.errno == OperationNotPermitted.errno:
- raise OperationNotPermitted(func_call)
- elif e.errno == PermissionDenied.errno:
- raise PermissionDenied(func_call)
- elif e.errno == ReadOnlyFileSystem.errno:
- raise ReadOnlyFileSystem(func_call)
+ while True:
+ try:
+ myfd = os.open(lockfilename, os.O_CREAT|os.O_RDWR, 0o660)
+ except OSError as e:
+ if e.errno in (errno.ENOENT, errno.ESTALE) and os.path.isdir(os.path.dirname(lockfilename)):
+ # Retry required for NFS (see bug 636798).
+ continue
+ else:
+ _raise_exc(e)
else:
- raise
+ break
if not preexisting:
try:
@@ -273,7 +273,7 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0,
if isinstance(lockfilename, basestring) and \
- myfd != HARDLINK_FD and _fstat_nlink(myfd) == 0:
+ myfd != HARDLINK_FD and _lockfile_was_removed(myfd, lockfilename):
# The file was deleted on us... Keep trying to make one...
os.close(myfd)
writemsg(_("lockfile recurse\n"), 1)
@@ -298,6 +298,58 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0,
writemsg(str((lockfilename, myfd, unlinkfile)) + "\n", 1)
return (lockfilename, myfd, unlinkfile, locking_method)
+
+def _lockfile_was_removed(lock_fd, lock_path):
+ """
+ Check if lock_fd still refers to a file located at lock_path, since
+ the file may have been removed by a concurrent process that held the
+ lock earlier. This implementation includes support for NFS, where
+ stat is not reliable for removed files due to the default file
+ attribute cache behavior ('ac' mount option).
+
+ @param lock_fd: an open file descriptor for a lock file
+ @type lock_fd: int
+ @param lock_path: path of lock file
+ @type lock_path: str
+ @rtype: bool
+ @return: True if lock_path exists and corresponds to lock_fd, False otherwise
+ """
+ try:
+ fstat_st = os.fstat(lock_fd)
+ except OSError as e:
+ if e.errno not in (errno.ENOENT, errno.ESTALE):
+ _raise_exc(e)
+ return True
+
+ # Since stat is not reliable for removed files on NFS with the default
+ # file attribute cache behavior ('ac' mount option), create a temporary
+ # hardlink in order to prove that the file path exists on the NFS server.
+ hardlink_path = hardlock_name(lock_path)
+ try:
+ os.unlink(hardlink_path)
+ except OSError as e:
+ if e.errno not in (errno.ENOENT, errno.ESTALE):
+ _raise_exc(e)
+ try:
+ try:
+ os.link(lock_path, hardlink_path)
+ except OSError as e:
+ if e.errno not in (errno.ENOENT, errno.ESTALE):
+ _raise_exc(e)
+ return True
+
+ hardlink_stat = os.stat(hardlink_path)
+ if hardlink_stat.st_ino != fstat_st.st_ino or hardlink_stat.st_dev != fstat_st.st_dev:
+ return True
+ finally:
+ try:
+ os.unlink(hardlink_path)
+ except OSError as e:
+ if e.errno not in (errno.ENOENT, errno.ESTALE):
+ _raise_exc(e)
+ return False
+
+
def _fstat_nlink(fd):
"""
@param fd: an open file descriptor
--
2.18.1
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2019-02-07 0:01 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-02-07 0:00 [gentoo-portage-dev] [PATCH] locks: handle lock file removal on NFS (bug 636798) Zac Medico
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox