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
pull/14313/head
Brian Coca 9 years ago
parent f30d5b1d94
commit 70ac47ae61

@ -1668,7 +1668,7 @@ class AnsibleModule(object):
e = get_exception() e = get_exception()
sys.stderr.write("could not cleanup %s: %s" % (tmpfile, e)) 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 '''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 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''' 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) os.rename(src, dest)
except (IOError, OSError): except (IOError, OSError):
e = get_exception() e = get_exception()
# 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) # 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 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
if e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY]:
self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, e)) self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, e))
dest_dir = os.path.dirname(dest) dest_dir = os.path.dirname(dest)

Loading…
Cancel
Save