diff options
author | Lars Wirzenius <liw@liw.fi> | 2010-07-11 11:12:55 +1200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2010-07-11 11:12:55 +1200 |
commit | 5d4da199344780051b7e0070c6e11f0501a922e8 (patch) | |
tree | 98d14a97c6977736b5cf595b9dea004ef74f2c5e | |
parent | e11eaecf92bf123697f04ee375e3a258e4ffd7f1 (diff) | |
download | obnam-5d4da199344780051b7e0070c6e11f0501a922e8.tar.gz |
Change overwrite_file to use a temporary name while writing.
This is not quite as safe and secure as with local filesystems
but as good as I can make it.
-rw-r--r-- | obnamlib/plugins/sftp_plugin.py | 36 |
1 files changed, 35 insertions, 1 deletions
diff --git a/obnamlib/plugins/sftp_plugin.py b/obnamlib/plugins/sftp_plugin.py index 658b5046..ee1b8453 100644 --- a/obnamlib/plugins/sftp_plugin.py +++ b/obnamlib/plugins/sftp_plugin.py @@ -19,6 +19,7 @@ import errno import logging import os import pwd +import random import stat import urlparse @@ -244,9 +245,42 @@ class SftpFS(obnamlib.VirtualFileSystem): raise OSError(errno.EEXIST, 'File exists', pathname) self._write_helper(pathname, 'wx', contents) + def _tempfile(self, dirname): + '''Generate a filename that does not exist. + + This is _not_ as safe as tempfile.mkstemp. Plenty of race + conditions. But seems to be as good as SFTP will allow. + + ''' + + while True: + i = random.randint(0, 2**64-1) + basename = 'tmp.%x' % i + pathname = os.path.join(dirname, basename) + if not self.exists(pathname): + return pathname + def overwrite_file(self, pathname, contents, make_backup=True): - self._write_helper(pathname, 'w', contents) + dirname = os.path.dirname(pathname) + tempname = self._tempfile(dirname) + self._write_helper(pathname, 'wx', contents) + # Rename existing to have a .bak suffix. If _that_ file already + # exists, remove that. + bak = pathname + ".bak" + try: + self.remove(bak) + except OSError: + pass + if self.exists(pathname): + self.rename(pathname, bak) + self.rename(tempname, pathname) + if not make_backup: + try: + self.remove(bak) + except OSError: + pass + def _write_helper(self, pathname, mode, contents): dirname = os.path.dirname(pathname) if dirname: |