diff options
author | Andrew M. Kuchling <amk@amk.ca> | 2006-11-09 21:16:46 (GMT) |
---|---|---|
committer | Andrew M. Kuchling <amk@amk.ca> | 2006-11-09 21:16:46 (GMT) |
commit | 978d8286ae36ac3570d1a2c6f48187ad9c0919c7 (patch) | |
tree | c5a4821258061a9887a4c5eb702df7beec4216c8 | |
parent | 6fc2382883516434c5876e768fabd75a0c633d84 (diff) | |
download | cpython-978d8286ae36ac3570d1a2c6f48187ad9c0919c7.zip cpython-978d8286ae36ac3570d1a2c6f48187ad9c0919c7.tar.gz cpython-978d8286ae36ac3570d1a2c6f48187ad9c0919c7.tar.bz2 |
[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.
-rwxr-xr-x | Lib/mailbox.py | 27 | ||||
-rw-r--r-- | Misc/NEWS | 4 |
2 files changed, 26 insertions, 5 deletions
diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 51c2ccc..c6b0fa0 100755 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -255,7 +255,19 @@ class Maildir(Mailbox): suffix = '' uniq = os.path.basename(tmp_file.name).split(self.colon)[0] 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): os.utime(dest, (os.path.getatime(dest), message.get_date())) return uniq @@ -431,12 +443,17 @@ class Maildir(Mailbox): except OSError, e: if e.errno == errno.ENOENT: Maildir._count += 1 - return open(path, 'wb+') + try: + return _create_carefully(path) + except OSError, e: + if e.errno != errno.EEXIST: + raise else: raise - else: - raise ExternalClashError('Name clash prevented file creation: %s' % - path) + + # Fall through to here if stat succeeded or open raised EEXIST. + raise ExternalClashError('Name clash prevented file creation: %s' % + path) def _refresh(self): """Update table of contents mapping.""" @@ -100,6 +100,10 @@ Library weren't passing the message factory on to newly created Maildir/MH 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 written to disk after calling .flush() or .close(). (Patch by David Watson.) |