mirror of https://github.com/python/cpython
[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:
parent
bb876b9c69
commit
7ea928c452
|
@ -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."""
|
||||||
|
|
|
@ -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.)
|
||||||
|
|
Loading…
Reference in New Issue