[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.
This commit is contained in:
Andrew M. Kuchling 2006-11-10 13:15:58 +00:00
parent bb876b9c69
commit 7ea928c452
2 changed files with 26 additions and 5 deletions

View File

@ -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):
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."""

View File

@ -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.)