diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index fe42c19688..f0d8c826a4 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -594,6 +594,20 @@ class AnsibleModule(object): self.fail_json(msg='Could not make backup of %s to %s: %s' % (fn, backupdest, e)) return backupdest + def atomic_replace(self, src, dest): + '''atomically replace dest with src, copying attributes from dest''' + st = os.stat(dest) + os.chmod(src, st.st_mode & 07777) + try: + os.chown(src, st.st_uid, st.st_gid) + except OSError, e: + if e.errno != errno.EPERM: + raise + if self.selinux_enabled(): + context = self.selinux_context(dest) + self.set_context_if_different(src, context, False) + os.rename(src, dest) + # == END DYNAMICALLY INSERTED CODE === diff --git a/library/authorized_key b/library/authorized_key index 5ba06fc2b2..0664727bbd 100755 --- a/library/authorized_key +++ b/library/authorized_key @@ -125,13 +125,7 @@ def writekeys(module, filename, keys): except IOError, e: module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, str(e))) f.close() - try: - shutil.copyfile(tmp_path, filename) - os.unlink(tmp_path) - except IOError, e: - module.fail_json(msg="Failed to copy temp file to %s: %s" % (filename, str(e))) - except OSError, e: - module.fail_json(msg="Failed to remove temp file: %s" % str(e)) + module.atomic_replace(tmp_path, filename) def enforce_state(module, params): """ diff --git a/library/copy b/library/copy index 7f7f501a3a..3ba1de781e 100755 --- a/library/copy +++ b/library/copy @@ -114,7 +114,7 @@ def main(): # might be an issue with exceeding path length dest_tmp = "%s.%s.%s.tmp" % (dest,os.getpid(),time.time()) shutil.copyfile(src, dest_tmp) - shutil.move(dest_tmp, dest) + module.atomic_replace(dest_tmp, dest) except shutil.Error: module.fail_json(msg="failed to copy: %s and %s are the same" % (src, dest)) except IOError: