Example #1
0
def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
		hardlink_candidates=None, encoding=_encodings['fs']):
	"""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."""
	#print "movefile("+str(src)+","+str(dest)+","+str(newmtime)+","+str(sstat)+")"

	if mysettings is None:
		mysettings = portage.settings

	selinux_enabled = mysettings.selinux_enabled()
	if selinux_enabled:
		selinux = _unicode_module_wrapper(_selinux, encoding=encoding)

	lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding)
	os = _unicode_module_wrapper(_os,
		encoding=encoding, overrides=_os_overrides)
	shutil = _unicode_module_wrapper(_shutil, encoding=encoding)

	try:
		if not sstat:
			sstat=os.lstat(src)

	except SystemExit as e:
		raise
	except Exception as e:
		print(_("!!! Stating source file failed... movefile()"))
		print("!!!",e)
		return None

	destexists=1
	try:
		dstat=os.lstat(dest)
	except (OSError, IOError):
		dstat=os.lstat(os.path.dirname(dest))
		destexists=0

	if bsd_chflags:
		if destexists and dstat.st_flags != 0:
			bsd_chflags.lchflags(dest, 0)
		# Use normal stat/chflags for the parent since we want to
		# follow any symlinks to the real parent directory.
		pflags = os.stat(os.path.dirname(dest)).st_flags
		if pflags != 0:
			bsd_chflags.chflags(os.path.dirname(dest), 0)

	if destexists:
		if stat.S_ISLNK(dstat[stat.ST_MODE]):
			try:
				os.unlink(dest)
				destexists=0
			except SystemExit as e:
				raise
			except Exception as e:
				pass

	if stat.S_ISLNK(sstat[stat.ST_MODE]):
		try:
			target=os.readlink(src)
			if mysettings and mysettings["D"]:
				if target.find(mysettings["D"])==0:
					target=target[len(mysettings["D"]):]
			if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
				os.unlink(dest)
			if selinux_enabled:
				selinux.symlink(target, dest, src)
			else:
				os.symlink(target,dest)
			lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
			# utime() only works on the target of a symlink, so it's not
			# possible to perserve mtime on symlinks.
			return os.lstat(dest)[stat.ST_MTIME]
		except SystemExit as e:
			raise
		except Exception as e:
			print(_("!!! failed to properly create symlink:"))
			print("!!!",dest,"->",target)
			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:
		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 as 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 as 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 hardlinked:
		renamefailed = False
	if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
		try:
			if selinux_enabled:
				selinux.rename(src, dest)
			else:
				os.rename(src,dest)
			renamefailed=0
		except OSError as e:
			if e.errno != errno.EXDEV:
				# Some random error.
				print(_("!!! Failed to move %(src)s to %(dest)s") % {"src": src, "dest": dest})
				print("!!!",e)
				return None
			# Invalid cross-device-link 'bind' mounted or actually Cross-Device
	if renamefailed:
		didcopy=0
		if stat.S_ISREG(sstat[stat.ST_MODE]):
			try: # For safety copy then move it over.
				if selinux_enabled:
					selinux.copyfile(src, dest + "#new")
					selinux.rename(dest + "#new", dest)
				else:
					shutil.copyfile(src,dest+"#new")
					os.rename(dest+"#new",dest)
				didcopy=1
			except SystemExit as e:
				raise
			except Exception as e:
				print(_('!!! copy %(src)s -> %(dest)s failed.') % {"src": src, "dest": dest})
				print("!!!",e)
				return None
		else:
			#we don't yet handle special, so we need to fall back to /bin/mv
			a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
			if a != os.EX_OK:
				writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1)
				writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
					{"src": _unicode_decode(src, encoding=encoding),
					"dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
				writemsg("!!! %s\n" % a, noiselevel=-1)
				return None # failure
		try:
			if didcopy:
				if stat.S_ISLNK(sstat[stat.ST_MODE]):
					lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
				else:
					os.chown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
				os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
				os.unlink(src)
		except SystemExit as e:
			raise
		except Exception as e:
			print(_("!!! Failed to chown/chmod/unlink in movefile()"))
			print("!!!",dest)
			print("!!!",e)
			return None

	# Always use stat_obj[stat.ST_MTIME] for the integral timestamp which
	# is returned, since the stat_obj.st_mtime float attribute rounds *up*
	# if the nanosecond part of the timestamp is 999999881 ns or greater.
	try:
		if hardlinked:
			newmtime = os.stat(dest)[stat.ST_MTIME]
		else:
			# Note: It is not possible to preserve nanosecond precision
			# (supported in POSIX.1-2008 via utimensat) with the IEEE 754
			# double precision float which only has a 53 bit significand.
			if newmtime is not None:
				os.utime(dest, (newmtime, newmtime))
			else:
				newmtime = sstat[stat.ST_MTIME]
				if renamefailed:
					# If rename succeeded then timestamps are automatically
					# preserved with complete precision because the source
					# and destination inode are the same. Otherwise, round
					# down to the nearest whole second since python's float
					# st_mtime cannot be used to preserve the st_mtim.tv_nsec
					# field with complete precision. Note that we have to use
					# stat_obj[stat.ST_MTIME] here because the float
					# stat_obj.st_mtime rounds *up* sometimes.
					os.utime(dest, (newmtime, newmtime))
	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.
		try:
			newmtime = os.stat(dest)[stat.ST_MTIME]
		except OSError as e:
			writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
			writemsg("!!! %s\n" % dest, noiselevel=-1)
			writemsg("!!! %s\n" % str(e), noiselevel=-1)
			return None

	if bsd_chflags:
		# Restore the flags we saved before moving
		if pflags:
			bsd_chflags.chflags(os.path.dirname(dest), pflags)

	return newmtime
Example #2
0
def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
		hardlink_candidates=None, encoding=_encodings['fs']):
	"""moves a file from src to dest, preserving all permissions and attributes; mtime will
	be preserved even when moving across filesystems.  Returns mtime as integer on success
	and None on failure.  mtime is expressed in seconds in Python <3.3 and nanoseconds in
	Python >=3.3.  Move is atomic."""

	if mysettings is None:
		mysettings = portage.settings

	src_bytes = _unicode_encode(src, encoding=encoding, errors='strict')
	dest_bytes = _unicode_encode(dest, encoding=encoding, errors='strict')
	xattr_enabled = "xattr" in mysettings.features
	selinux_enabled = mysettings.selinux_enabled()
	if selinux_enabled:
		selinux = _unicode_module_wrapper(_selinux, encoding=encoding)
		_copyfile = selinux.copyfile
		_rename = selinux.rename
	else:
		_copyfile = _shutil.copyfile
		_rename = _os.rename

	lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding)
	os = _unicode_module_wrapper(_os,
		encoding=encoding, overrides=_os_overrides)

	try:
		if not sstat:
			sstat = os.lstat(src)

	except SystemExit as e:
		raise
	except Exception as e:
		writemsg("!!! %s\n" % _("Stating source file failed... movefile()"),
			noiselevel=-1)
		writemsg("!!! %s\n" % (e,), noiselevel=-1)
		return None

	destexists = 1
	try:
		dstat = os.lstat(dest)
	except (OSError, IOError):
		dstat = os.lstat(os.path.dirname(dest))
		destexists = 0

	if bsd_chflags:
		if destexists and dstat.st_flags != 0:
			bsd_chflags.lchflags(dest, 0)
		# Use normal stat/chflags for the parent since we want to
		# follow any symlinks to the real parent directory.
		pflags = os.stat(os.path.dirname(dest)).st_flags
		if pflags != 0:
			bsd_chflags.chflags(os.path.dirname(dest), 0)

	if destexists:
		if stat.S_ISLNK(dstat[stat.ST_MODE]):
			try:
				os.unlink(dest)
				destexists = 0
			except SystemExit as e:
				raise
			except Exception as e:
				pass

	if stat.S_ISLNK(sstat[stat.ST_MODE]):
		try:
			target = os.readlink(src)
			if mysettings and "D" in mysettings and \
				target.startswith(mysettings["D"]):
				target = target[len(mysettings["D"])-1:]
			if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
				os.unlink(dest)
			try:
				if selinux_enabled:
					selinux.symlink(target, dest, src)
				else:
					os.symlink(target, dest)
			except OSError as e:
				# Some programs will create symlinks automatically, so we have
				# to tolerate these links being recreated during the merge
				# process. In any case, if the link is pointing at the right
				# place, we're in good shape.
				if e.errno not in (errno.ENOENT, errno.EEXIST) or \
					target != os.readlink(dest):
					raise
			lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])

			try:
				_os.unlink(src_bytes)
			except OSError:
				pass

			if sys.hexversion >= 0x3030000:
				try:
					os.utime(dest, ns=(sstat.st_mtime_ns, sstat.st_mtime_ns), follow_symlinks=False)
				except NotImplementedError:
					# utimensat() and lutimes() missing in libc.
					return os.stat(dest, follow_symlinks=False).st_mtime_ns
				else:
					return sstat.st_mtime_ns
			else:
				# utime() in Python <3.3 only works on the target of a symlink, so it's not
				# possible to preserve mtime on symlinks.
				return os.lstat(dest)[stat.ST_MTIME]
		except SystemExit as e:
			raise
		except Exception as e:
			writemsg("!!! %s\n" % _("failed to properly create symlink:"),
				noiselevel=-1)
			writemsg("!!! %s -> %s\n" % (dest, target), noiselevel=-1)
			writemsg("!!! %s\n" % (e,), noiselevel=-1)
			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:
		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 as 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 as e:
					writemsg(_("!!! Failed to rename %s to %s\n") % \
						(hardlink_tmp, dest), noiselevel=-1)
					writemsg("!!! %s\n" % (e,), noiselevel=-1)
					return None
				hardlinked = True
				try:
					_os.unlink(src_bytes)
				except OSError:
					pass
				break

	renamefailed = 1
	if hardlinked:
		renamefailed = False
	if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
		try:
			if selinux_enabled:
				selinux.rename(src, dest)
			else:
				os.rename(src, dest)
			renamefailed = 0
		except OSError as e:
			if e.errno != errno.EXDEV:
				# Some random error.
				writemsg("!!! %s\n" % _("Failed to move %(src)s to %(dest)s") %
					{"src": src, "dest": dest}, noiselevel=-1)
				writemsg("!!! %s\n" % (e,), noiselevel=-1)
				return None
			# Invalid cross-device-link 'bind' mounted or actually Cross-Device
	if renamefailed:
		if stat.S_ISREG(sstat[stat.ST_MODE]):
			dest_tmp = dest + "#new"
			dest_tmp_bytes = _unicode_encode(dest_tmp, encoding=encoding,
				errors='strict')
			try: # For safety copy then move it over.
				_copyfile(src_bytes, dest_tmp_bytes)
				if xattr_enabled:
					try:
						_copyxattr(src_bytes, dest_tmp_bytes,
							exclude=mysettings.get("PORTAGE_XATTR_EXCLUDE", ""))
					except SystemExit:
						raise
					except:
						msg = _("Failed to copy extended attributes. "
							"In order to avoid this error, set "
							"FEATURES=\"-xattr\" in make.conf.")
						msg = textwrap.wrap(msg, 65)
						for line in msg:
							writemsg("!!! %s\n" % (line,), noiselevel=-1)
						raise
				_apply_stat(sstat, dest_tmp_bytes)
				_rename(dest_tmp_bytes, dest_bytes)
				_os.unlink(src_bytes)
			except SystemExit as e:
				raise
			except Exception as e:
				writemsg("!!! %s\n" % _('copy %(src)s -> %(dest)s failed.') %
					{"src": src, "dest": dest}, noiselevel=-1)
				writemsg("!!! %s\n" % (e,), noiselevel=-1)
				return None
		else:
			#we don't yet handle special, so we need to fall back to /bin/mv
			a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
			if a != os.EX_OK:
				writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1)
				writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
					{"src": _unicode_decode(src, encoding=encoding),
					"dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
				writemsg("!!! %s\n" % a, noiselevel=-1)
				return None # failure

	# In Python <3.3 always use stat_obj[stat.ST_MTIME] for the integral timestamp
	# which is returned, since the stat_obj.st_mtime float attribute rounds *up*
	# if the nanosecond part of the timestamp is 999999881 ns or greater.
	try:
		if hardlinked:
			if sys.hexversion >= 0x3030000:
				newmtime = os.stat(dest).st_mtime_ns
			else:
				newmtime = os.stat(dest)[stat.ST_MTIME]
		else:
			# Note: It is not possible to preserve nanosecond precision
			# (supported in POSIX.1-2008 via utimensat) with the IEEE 754
			# double precision float which only has a 53 bit significand.
			if newmtime is not None:
				if sys.hexversion >= 0x3030000:
					os.utime(dest, ns=(newmtime, newmtime))
				else:
					os.utime(dest, (newmtime, newmtime))
			else:
				if sys.hexversion >= 0x3030000:
					newmtime = sstat.st_mtime_ns
				else:
					newmtime = sstat[stat.ST_MTIME]
				if renamefailed:
					if sys.hexversion >= 0x3030000:
						# If rename succeeded then timestamps are automatically
						# preserved with complete precision because the source
						# and destination inodes are the same. Otherwise, manually
						# update timestamps with nanosecond precision.
						os.utime(dest, ns=(newmtime, newmtime))
					else:
						# If rename succeeded then timestamps are automatically
						# preserved with complete precision because the source
						# and destination inodes are the same. Otherwise, round
						# down to the nearest whole second since python's float
						# st_mtime cannot be used to preserve the st_mtim.tv_nsec
						# field with complete precision. Note that we have to use
						# stat_obj[stat.ST_MTIME] here because the float
						# stat_obj.st_mtime rounds *up* sometimes.
						os.utime(dest, (newmtime, newmtime))
	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.
		try:
			if sys.hexversion >= 0x3030000:
				newmtime = os.stat(dest).st_mtime_ns
			else:
				newmtime = os.stat(dest)[stat.ST_MTIME]
		except OSError as e:
			writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
			writemsg("!!! %s\n" % dest, noiselevel=-1)
			writemsg("!!! %s\n" % str(e), noiselevel=-1)
			return None

	if bsd_chflags:
		# Restore the flags we saved before moving
		if pflags:
			bsd_chflags.chflags(os.path.dirname(dest), pflags)

	return newmtime
Example #3
0
def movefile(src,
             dest,
             newmtime=None,
             sstat=None,
             mysettings=None,
             hardlink_candidates=None,
             encoding=_encodings['fs']):
    """moves a file from src to dest, preserving all permissions and attributes; mtime will
	be preserved even when moving across filesystems.  Returns mtime as integer on success
	and None on failure.  mtime is expressed in seconds in Python <3.3 and nanoseconds in
	Python >=3.3.  Move is atomic."""

    if mysettings is None:
        mysettings = portage.settings

    src_bytes = _unicode_encode(src, encoding=encoding, errors='strict')
    dest_bytes = _unicode_encode(dest, encoding=encoding, errors='strict')
    xattr_enabled = "xattr" in mysettings.features
    selinux_enabled = mysettings.selinux_enabled()
    if selinux_enabled:
        selinux = _unicode_module_wrapper(_selinux, encoding=encoding)
        _copyfile = selinux.copyfile
        _rename = selinux.rename
    else:
        _copyfile = _shutil.copyfile
        _rename = _os.rename

    lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding)
    os = _unicode_module_wrapper(_os,
                                 encoding=encoding,
                                 overrides=_os_overrides)

    try:
        if not sstat:
            sstat = os.lstat(src)

    except SystemExit as e:
        raise
    except Exception as e:
        writemsg("!!! %s\n" % _("Stating source file failed... movefile()"),
                 noiselevel=-1)
        writemsg("!!! %s\n" % (e, ), noiselevel=-1)
        return None

    destexists = 1
    try:
        dstat = os.lstat(dest)
    except (OSError, IOError):
        dstat = os.lstat(os.path.dirname(dest))
        destexists = 0

    if bsd_chflags:
        if destexists and dstat.st_flags != 0:
            bsd_chflags.lchflags(dest, 0)
        # Use normal stat/chflags for the parent since we want to
        # follow any symlinks to the real parent directory.
        pflags = os.stat(os.path.dirname(dest)).st_flags
        if pflags != 0:
            bsd_chflags.chflags(os.path.dirname(dest), 0)

    if destexists:
        if stat.S_ISLNK(dstat[stat.ST_MODE]):
            try:
                os.unlink(dest)
                destexists = 0
            except SystemExit as e:
                raise
            except Exception as e:
                pass

    if stat.S_ISLNK(sstat[stat.ST_MODE]):
        try:
            target = os.readlink(src)
            if mysettings and "D" in mysettings and \
             target.startswith(mysettings["D"]):
                target = target[len(mysettings["D"]) - 1:]
            if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
                os.unlink(dest)
            try:
                if selinux_enabled:
                    selinux.symlink(target, dest, src)
                else:
                    os.symlink(target, dest)
            except OSError as e:
                # Some programs will create symlinks automatically, so we have
                # to tolerate these links being recreated during the merge
                # process. In any case, if the link is pointing at the right
                # place, we're in good shape.
                if e.errno not in (errno.ENOENT, errno.EEXIST) or \
                 target != os.readlink(dest):
                    raise
            lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])

            try:
                _os.unlink(src_bytes)
            except OSError:
                pass

            if sys.hexversion >= 0x3030000:
                try:
                    os.utime(dest,
                             ns=(sstat.st_mtime_ns, sstat.st_mtime_ns),
                             follow_symlinks=False)
                except NotImplementedError:
                    # utimensat() and lutimes() missing in libc.
                    return os.stat(dest, follow_symlinks=False).st_mtime_ns
                else:
                    return sstat.st_mtime_ns
            else:
                # utime() in Python <3.3 only works on the target of a symlink, so it's not
                # possible to preserve mtime on symlinks.
                return os.lstat(dest)[stat.ST_MTIME]
        except SystemExit as e:
            raise
        except Exception as e:
            writemsg("!!! %s\n" % _("failed to properly create symlink:"),
                     noiselevel=-1)
            writemsg("!!! %s -> %s\n" % (dest, target), noiselevel=-1)
            writemsg("!!! %s\n" % (e, ), noiselevel=-1)
            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:
        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 as 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 as e:
                    writemsg(_("!!! Failed to rename %s to %s\n") % \
                     (hardlink_tmp, dest), noiselevel=-1)
                    writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                    return None
                hardlinked = True
                try:
                    _os.unlink(src_bytes)
                except OSError:
                    pass
                break

    renamefailed = 1
    if hardlinked:
        renamefailed = False
    if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
        try:
            if selinux_enabled:
                selinux.rename(src, dest)
            else:
                os.rename(src, dest)
            renamefailed = 0
        except OSError as e:
            if e.errno != errno.EXDEV:
                # Some random error.
                writemsg("!!! %s\n" % _("Failed to move %(src)s to %(dest)s") %
                         {
                             "src": src,
                             "dest": dest
                         },
                         noiselevel=-1)
                writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                return None
            # Invalid cross-device-link 'bind' mounted or actually Cross-Device
    if renamefailed:
        if stat.S_ISREG(sstat[stat.ST_MODE]):
            dest_tmp = dest + "#new"
            dest_tmp_bytes = _unicode_encode(dest_tmp,
                                             encoding=encoding,
                                             errors='strict')
            try:  # For safety copy then move it over.
                _copyfile(src_bytes, dest_tmp_bytes)
                if xattr_enabled:
                    try:
                        _copyxattr(src_bytes,
                                   dest_tmp_bytes,
                                   exclude=mysettings.get(
                                       "PORTAGE_XATTR_EXCLUDE",
                                       "security.* system.nfs4_acl"))
                    except SystemExit:
                        raise
                    except:
                        msg = _("Failed to copy extended attributes. "
                                "In order to avoid this error, set "
                                "FEATURES=\"-xattr\" in make.conf.")
                        msg = textwrap.wrap(msg, 65)
                        for line in msg:
                            writemsg("!!! %s\n" % (line, ), noiselevel=-1)
                        raise
                _apply_stat(sstat, dest_tmp_bytes)
                _rename(dest_tmp_bytes, dest_bytes)
                _os.unlink(src_bytes)
            except SystemExit as e:
                raise
            except Exception as e:
                writemsg("!!! %s\n" % _('copy %(src)s -> %(dest)s failed.') % {
                    "src": src,
                    "dest": dest
                },
                         noiselevel=-1)
                writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                return None
        else:
            #we don't yet handle special, so we need to fall back to /bin/mv
            a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
            if a != os.EX_OK:
                writemsg(_("!!! Failed to move special file:\n"),
                         noiselevel=-1)
                writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
                 {"src": _unicode_decode(src, encoding=encoding),
                 "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
                writemsg("!!! %s\n" % a, noiselevel=-1)
                return None  # failure

    # In Python <3.3 always use stat_obj[stat.ST_MTIME] for the integral timestamp
    # which is returned, since the stat_obj.st_mtime float attribute rounds *up*
    # if the nanosecond part of the timestamp is 999999881 ns or greater.
    try:
        if hardlinked:
            if sys.hexversion >= 0x3030000:
                newmtime = os.stat(dest).st_mtime_ns
            else:
                newmtime = os.stat(dest)[stat.ST_MTIME]
        else:
            # Note: It is not possible to preserve nanosecond precision
            # (supported in POSIX.1-2008 via utimensat) with the IEEE 754
            # double precision float which only has a 53 bit significand.
            if newmtime is not None:
                if sys.hexversion >= 0x3030000:
                    os.utime(dest, ns=(newmtime, newmtime))
                else:
                    os.utime(dest, (newmtime, newmtime))
            else:
                if sys.hexversion >= 0x3030000:
                    newmtime = sstat.st_mtime_ns
                else:
                    newmtime = sstat[stat.ST_MTIME]
                if renamefailed:
                    if sys.hexversion >= 0x3030000:
                        # If rename succeeded then timestamps are automatically
                        # preserved with complete precision because the source
                        # and destination inodes are the same. Otherwise, manually
                        # update timestamps with nanosecond precision.
                        os.utime(dest, ns=(newmtime, newmtime))
                    else:
                        # If rename succeeded then timestamps are automatically
                        # preserved with complete precision because the source
                        # and destination inodes are the same. Otherwise, round
                        # down to the nearest whole second since python's float
                        # st_mtime cannot be used to preserve the st_mtim.tv_nsec
                        # field with complete precision. Note that we have to use
                        # stat_obj[stat.ST_MTIME] here because the float
                        # stat_obj.st_mtime rounds *up* sometimes.
                        os.utime(dest, (newmtime, newmtime))
    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.
        try:
            if sys.hexversion >= 0x3030000:
                newmtime = os.stat(dest).st_mtime_ns
            else:
                newmtime = os.stat(dest)[stat.ST_MTIME]
        except OSError as e:
            writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
            writemsg("!!! %s\n" % dest, noiselevel=-1)
            writemsg("!!! %s\n" % str(e), noiselevel=-1)
            return None

    if bsd_chflags:
        # Restore the flags we saved before moving
        if pflags:
            bsd_chflags.chflags(os.path.dirname(dest), pflags)

    return newmtime