summaryrefslogtreecommitdiffstats
path: root/pym/portage/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'pym/portage/__init__.py')
-rw-r--r--pym/portage/__init__.py53
1 files changed, 47 insertions, 6 deletions
diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py
index 186363235..2fcb4b143 100644
--- a/pym/portage/__init__.py
+++ b/pym/portage/__init__.py
@@ -5958,7 +5958,8 @@ def _movefile(src, dest, **kwargs):
raise portage.exception.PortageException(
"mv '%s' '%s'" % (src, dest))
-def movefile(src,dest,newmtime=None,sstat=None,mysettings=None):
+def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
+ hardlink_candidates=None):
"""moves a file from src to dest, preserving all permissions and attributes; mtime will
be preserved even when moving across filesystems. Returns true on success and false on
failure. Move is atomic."""
@@ -6030,8 +6031,45 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None):
print "!!!",e
return None
+ hardlinked = False
+ # Since identical files might be merged to multiple filesystems,
+ # so os.link() calls might fail for some paths, so try them all.
+ # For atomic replacement, first create the link as a temp file
+ # and them use os.rename() to replace the destination.
+ if hardlink_candidates is not None:
+ head, tail = os.path.split(dest)
+ hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \
+ (tail, os.getpid()))
+ try:
+ os.unlink(hardlink_tmp)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ writemsg("!!! Failed to remove hardlink temp file: %s\n" % \
+ (hardlink_tmp,), noiselevel=-1)
+ writemsg("!!! %s\n" % (e,), noiselevel=-1)
+ return None
+ del e
+ for hardlink_src in hardlink_candidates:
+ try:
+ os.link(hardlink_src, hardlink_tmp)
+ except OSError:
+ continue
+ else:
+ try:
+ os.rename(hardlink_tmp, dest)
+ except OSError, e:
+ writemsg("!!! Failed to rename %s to %s\n" % \
+ (hardlink_tmp, dest), noiselevel=-1)
+ writemsg("!!! %s\n" % (e,), noiselevel=-1)
+ return None
+ hardlinked = True
+ break
+
renamefailed=1
- if sstat[stat.ST_DEV]==dstat[stat.ST_DEV] or selinux_enabled:
+ if hardlinked:
+ renamefailed = False
+ if not hardlinked and \
+ (selinux_enabled or sstat[stat.ST_DEV] == dstat[stat.ST_DEV]):
try:
if selinux_enabled:
ret=selinux.secure_rename(src,dest)
@@ -6092,11 +6130,14 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None):
return None
try:
- if newmtime is not None:
- os.utime(dest, (newmtime, newmtime))
+ if hardlinked:
+ newmtime = long(os.stat(dest).st_mtime)
else:
- os.utime(dest, (sstat.st_atime, sstat.st_mtime))
- newmtime = long(sstat.st_mtime)
+ if newmtime is not None:
+ os.utime(dest, (newmtime, newmtime))
+ else:
+ os.utime(dest, (sstat.st_atime, sstat.st_mtime))
+ newmtime = long(sstat.st_mtime)
except OSError:
# The utime can fail here with EPERM even though the move succeeded.
# Instead of failing, use stat to return the mtime if possible.