Commit 411a970b authored by Andrew M. Kuchling's avatar Andrew M. Kuchling

[Patch #1514543] mailbox (Maildir): avoid losing messages on name clash

Two changes:

Where possible, use link()/remove() to move files into a directory; this
makes it easier to avoid overwriting an existing file.

Use _create_carefully() to create files in tmp/, which uses O_EXCL.

Backport candidate.
parent c38f36a5
...@@ -255,7 +255,19 @@ class Maildir(Mailbox): ...@@ -255,7 +255,19 @@ class Maildir(Mailbox):
suffix = '' suffix = ''
uniq = os.path.basename(tmp_file.name).split(self.colon)[0] uniq = os.path.basename(tmp_file.name).split(self.colon)[0]
dest = os.path.join(self._path, subdir, uniq + suffix) dest = os.path.join(self._path, subdir, uniq + suffix)
os.rename(tmp_file.name, dest) try:
if hasattr(os, 'link'):
os.link(tmp_file.name, dest)
os.remove(tmp_file.name)
else:
os.rename(tmp_file.name, dest)
except OSError, e:
os.remove(tmp_file.name)
if e.errno == errno.EEXIST:
raise ExternalClashError('Name clash with existing message: %s'
% dest)
else:
raise
if isinstance(message, MaildirMessage): if isinstance(message, MaildirMessage):
os.utime(dest, (os.path.getatime(dest), message.get_date())) os.utime(dest, (os.path.getatime(dest), message.get_date()))
return uniq return uniq
...@@ -431,12 +443,17 @@ class Maildir(Mailbox): ...@@ -431,12 +443,17 @@ class Maildir(Mailbox):
except OSError, e: except OSError, e:
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
Maildir._count += 1 Maildir._count += 1
return open(path, 'wb+') try:
return _create_carefully(path)
except OSError, e:
if e.errno != errno.EEXIST:
raise
else: else:
raise raise
else:
raise ExternalClashError('Name clash prevented file creation: %s' % # Fall through to here if stat succeeded or open raised EEXIST.
path) raise ExternalClashError('Name clash prevented file creation: %s' %
path)
def _refresh(self): def _refresh(self):
"""Update table of contents mapping.""" """Update table of contents mapping."""
......
...@@ -100,6 +100,10 @@ Library ...@@ -100,6 +100,10 @@ Library
weren't passing the message factory on to newly created Maildir/MH weren't passing the message factory on to newly created Maildir/MH
objects. objects.
- Patch #1514543: In the Maildir clash, report errors if there's
a name clash instead of possibly losing a message. (Patch by David
Watson.)
- Patch #1514544: Try to ensure that messages/indexes have been physically - Patch #1514544: Try to ensure that messages/indexes have been physically
written to disk after calling .flush() or .close(). (Patch by David written to disk after calling .flush() or .close(). (Patch by David
Watson.) Watson.)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment