aboutsummaryrefslogtreecommitdiffstats
path: root/archivemail.py
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 /archivemail.py
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.
Diffstat (limited to 'archivemail.py')
-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()