summaryrefslogtreecommitdiffstats
path: root/Lib/mailbox.py
diff options
context:
space:
mode:
authorAndrew M. Kuchling <amk@amk.ca>2006-11-09 21:16:46 (GMT)
committerAndrew M. Kuchling <amk@amk.ca>2006-11-09 21:16:46 (GMT)
commit978d8286ae36ac3570d1a2c6f48187ad9c0919c7 (patch)
treec5a4821258061a9887a4c5eb702df7beec4216c8 /Lib/mailbox.py
parent6fc2382883516434c5876e768fabd75a0c633d84 (diff)
downloadcpython-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.
Diffstat (limited to 'Lib/mailbox.py')
-rwxr-xr-xLib/mailbox.py27
1 files changed, 22 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."""