aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolaus Schulz <microschulz@web.de>2010-01-16 01:06:30 +0100
committerNikolaus Schulz <microschulz@web.de>2010-07-19 01:40:24 +0200
commitb9b2174c45e90bade3940411c81e15ab4bc32bbc (patch)
treeef7d3a07d578675f1a55c34fc724e360f4d1921b
parent86471d12a4959f8ab760fc0dac4da266c6b6e80b (diff)
downloadarchivemail-b9b2174c45e90bade3940411c81e15ab4bc32bbc.tar.gz
archivemail-b9b2174c45e90bade3940411c81e15ab4bc32bbc.tar.bz2
archivemail-b9b2174c45e90bade3940411c81e15ab4bc32bbc.zip
At critical points, flush mbox files and sync them to disk
This should minimize the risk of data loss. Flushing a locked mbox file before unlocking it also ensures that there's no window when another process could lock the mbox after us, but still see the old content.
-rwxr-xr-xarchivemail.py33
1 files changed, 30 insertions, 3 deletions
diff --git a/archivemail.py b/archivemail.py
index e4e8425..0633718 100755
--- a/archivemail.py
+++ b/archivemail.py
@@ -411,6 +411,11 @@ class LockableMboxMixin:
os.remove(lock_name)
_stale.dotlock_files.remove(lock_name)
+ def commit(self):
+ """Sync the mbox file to disk."""
+ self.mbox_file.flush()
+ os.fsync(self.mbox_file.fileno())
+
def close(self):
"""Close the mbox file"""
vprint("closing file '%s'" % self.mbox_file_name)
@@ -539,6 +544,11 @@ class TempMbox:
if not msg_has_mbox_format:
self.mbox_file.write(os.linesep)
+ def commit(self):
+ """Sync the mbox file to disk."""
+ self.mbox_file.flush()
+ os.fsync(self.mbox_file.fileno())
+
def close(self):
"""Close the mbox file"""
vprint("closing file '%s'" % self.mbox_file_name)
@@ -562,10 +572,23 @@ class CompressedTempMbox(TempMbox):
TempMbox.__init__(self, prefix)
self.raw_file = self.mbox_file
self.mbox_file = gzip.GzipFile(mode="a", fileobj=self.mbox_file)
+ # Workaround that GzipFile.close() isn't idempotent in Python < 2.6
+ # (python issue #2959). There is no GzipFile.closed, so we need a
+ # replacement.
+ self.gzipfile_closed = False
+
+ def commit(self):
+ """Finish gzip file and sync it to disk."""
+ # This method is currently not used
+ self.mbox_file.close() # close GzipFile, writing gzip trailer
+ self.gzipfile_closed = True
+ self.raw_file.flush()
+ os.fsync(self.raw_file.fileno())
def close(self):
- """Finish gzip file and close it."""
- self.mbox_file.close() # close GzipFile, writing gzip trailer
+ """Close the gzip file."""
+ if not self.gzipfile_closed:
+ self.mbox_file.close()
self.raw_file.close()
@@ -1152,8 +1175,9 @@ def _archive_mbox(mailbox_name, final_archive_name):
commit_archive(archive, final_archive_name)
if retain:
pending_changes = original.mbox_file.tell() != retain.mbox_file.tell()
- retain.close()
if pending_changes:
+ retain.commit()
+ retain.close()
vprint("writing back changed mailbox '%s'..." % \
original.mbox_file_name)
# Prepare for recovery on error.
@@ -1165,12 +1189,14 @@ def _archive_mbox(mailbox_name, final_archive_name):
os.getpid())
try:
original.overwrite_with(retain.mbox_file_name)
+ original.commit()
except:
retain.saveas(saved_name)
print "Error writing back changed mailbox; saved good copy to " \
"%s" % saved_name
raise
else:
+ retain.close()
vprint("no changes to mbox '%s'" % original.mbox_file_name)
retain.remove()
original.unlock()
@@ -1577,6 +1603,7 @@ def commit_archive(archive, final_archive_name):
final_archive.lock()
try:
final_archive.append(archive.mbox_file_name)
+ final_archive.commit()
finally:
final_archive.unlock()
final_archive.close()