Commit a0d0bb47 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.
parent 26aafb74
...@@ -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)
try:
if hasattr(os, 'link'):
os.link(tmp_file.name, dest)
os.remove(tmp_file.name)
else:
os.rename(tmp_file.name, dest) 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,10 +443,15 @@ class Maildir(Mailbox): ...@@ -431,10 +443,15 @@ 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:
else: return _create_carefully(path)
except OSError, e:
if e.errno != errno.EEXIST:
raise raise
else: else:
raise
# Fall through to here if stat succeeded or open raised EEXIST.
raise ExternalClashError('Name clash prevented file creation: %s' % raise ExternalClashError('Name clash prevented file creation: %s' %
path) path)
......
...@@ -140,6 +140,10 @@ Library ...@@ -140,6 +140,10 @@ Library
- Bug #1575506: mailbox.py: Single-file mailboxes didn't re-lock - Bug #1575506: mailbox.py: Single-file mailboxes didn't re-lock
properly in their flush() method. properly in their flush() method.
- Patch #1514543: mailbox.py: In the Maildir class, report errors if there's
a filename clash instead of possibly losing a message. (Patch by David
Watson.)
- Patch #1514544: mailbox.py: Try to ensure that messages/indexes have - Patch #1514544: mailbox.py: Try to ensure that messages/indexes have
been physically written to disk after calling .flush() or been physically written to disk after calling .flush() or
.close(). (Patch by David Watson.) .close(). (Patch by David 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