From 6caaea36e03b9fd74b68ca43e7f4dec91f1ff7f9 Mon Sep 17 00:00:00 2001 From: John Goerzen Date: Fri, 19 Oct 2007 01:06:18 +0100 Subject: [PATCH] Fix Maildir race fixes deb#439384 From: martin f krafft Subject: race condition in Maildir writing The offlineimap Maildir code checks for file existence and then opens a file. That's open to a race condition. It's better to open the file and fail if it already exists. The following patch does this. It catches OSError 17 (file exists) and re-raises all others. I'll leave it up to you to decide whether this is appropriate. --- offlineimap/folder/Maildir.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/offlineimap/folder/Maildir.py b/offlineimap/folder/Maildir.py index 56a63e4..c5d5092 100644 --- a/offlineimap/folder/Maildir.py +++ b/offlineimap/folder/Maildir.py @@ -158,7 +158,8 @@ class MaildirFolder(BaseFolder): # Otherwise, save the message in tmp/ and then call savemessageflags() # to give it a permanent home. tmpdir = os.path.join(self.getfullname(), 'tmp') - messagename = None + file = fd = None + messagename = tmpmessaename = None attempts = 0 while 1: if attempts > 15: @@ -171,19 +172,24 @@ class MaildirFolder(BaseFolder): socket.gethostname(), uid, md5.new(self.getvisiblename()).hexdigest()) - if os.path.exists(os.path.join(tmpdir, messagename)): - time.sleep(2) - attempts += 1 - else: - break - tmpmessagename = messagename.split(',')[0] - ui.debug('maildir', 'savemessage: using temporary name %s' % tmpmessagename) - file = open(os.path.join(tmpdir, tmpmessagename), "wt") + tmpmessagename = messagename.split(',')[0] + try: + fd = os.open(os.path.join(tmpdir, tmpmessagename), + os.O_WRONLY + os.O_CREAT + os.O_EXCL) + file = os.fdopen(fd, 'w') + ui.debug('maildir', 'savemessage: using temporary name %s' % tmpmessagename) + except OSError, e: + if e.errno == 17: + time.sleep(2) + attempts += 1 + continue + raise + file.write(content) # Make sure the data hits the disk file.flush() - os.fsync(file.fileno()) + os.fsync(fd) file.close() if rtime != None: