From 70ac47ae61eb3c0cb2e33a342128ef1a7653b17b Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Wed, 3 Feb 2016 21:15:48 -0500 Subject: [PATCH] allow atomic_move to not be atomic ... just 'cause people build bad systems that insist on not allowing updates in an atomic manner and force us to do them in a very unsafe way that has race conditions and can lead to many issues. if using this option you should really be opening a bug report with the system that only allows for this type of update. and now i shower though i doubt i'll feel clean --- lib/ansible/module_utils/basic.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 27b4cc2f4da..c1d061eba36 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -1668,7 +1668,7 @@ class AnsibleModule(object): e = get_exception() sys.stderr.write("could not cleanup %s: %s" % (tmpfile, e)) - def atomic_move(self, src, dest): + def atomic_move(self, src, dest, unsafe_writes=False): '''atomically move src to dest, copying attributes from dest, returns true on success it uses os.rename to ensure this as it is an atomic operation, rest of the function is to work around limitations, corner cases and ensure selinux context is saved if possible''' @@ -1708,9 +1708,25 @@ class AnsibleModule(object): os.rename(src, dest) except (IOError, OSError): e = get_exception() - # only try workarounds for errno 18 (cross device), 1 (not permitted), 13 (permission denied) - # and 26 (text file busy) which happens on vagrant synced folders - if e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY]: + # sadly there are some situations where we cannot ensure atomicity, but only if + # the user insists and we get the appropriate error we update the file unsafely + if unsafe_writes and e.errno == errno.EBUSY: + #TODO: issue warning that this is an unsafe operation, but doing it cause user insists + try: + try: + out_dest = open(dest, 'wb') + in_src = open(src, 'rb') + shutil.copyfileobj(in_src, out_dest) + finally: # assuring closed files in 2.4 compatible way + if out_dest: + out_dest.close() + if in_src: + in_src.close() + except (shutil.Error, OSError, IOError), e: + self.fail_json(msg='Could not write data to file (%s) from (%s): %s' % (dest, src, e)) + elif e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY]: + # only try workarounds for errno 18 (cross device), 1 (not permitted), 13 (permission denied) + # and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, e)) dest_dir = os.path.dirname(dest)