diff --git a/Changelog.draft.rst b/Changelog.draft.rst index fd445ac..2035aa8 100644 --- a/Changelog.draft.rst +++ b/Changelog.draft.rst @@ -16,9 +16,12 @@ New Features Changes ------- +* Maildirs use less memory while syncing. + Bug Fixes --------- +* Saving to Maildirs now checks for file existence without race conditions. Pending for the next major release ================================== diff --git a/offlineimap/folder/Maildir.py b/offlineimap/folder/Maildir.py index a798b36..b576507 100644 --- a/offlineimap/folder/Maildir.py +++ b/offlineimap/folder/Maildir.py @@ -28,6 +28,8 @@ try: except ImportError: from md5 import md5 +from offlineimap import OfflineImapError + uidmatchre = re.compile(',U=(\d+)') flagmatchre = re.compile(':.*2,([A-Z]+)') timestampmatchre = re.compile('(\d+)'); @@ -217,52 +219,37 @@ 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 - attempts = 0 - while 1: - if attempts > 15: - raise IOError, "Couldn't write to file %s" % messagename - timeval, timeseq = gettimeseq() - messagename = '%d_%d.%d.%s,U=%d,FMD5=%s' % \ - (timeval, - timeseq, - os.getpid(), - socket.gethostname(), - uid, - md5(self.getvisiblename()).hexdigest()) - if os.path.exists(os.path.join(tmpdir, messagename)): - time.sleep(2) - attempts += 1 + timeval, timeseq = gettimeseq() + messagename = '%d_%d.%d.%s,U=%d,FMD5=%s' % \ + (timeval, + timeseq, + os.getpid(), + socket.gethostname(), + uid, + md5(self.getvisiblename()).hexdigest()) + # open file and write it out + try: + fd = os.open(os.path.join(tmpdir, messagename), + os.O_EXCL|os.O_CREAT|os.O_WRONLY) + except OSError, e: + if e.errno == 17: + #FILE EXISTS ALREADY + severity = OfflineImapError.ERROR.MESSAGE + raise OfflineImapError("Unique filename %s already existing." %\ + messagename, severity) else: - break - tmpmessagename = messagename.split(',')[0] - self.ui.debug('maildir', 'savemessage: using temporary name %s' %\ - tmpmessagename) - file = open(os.path.join(tmpdir, tmpmessagename), "wt") - file.write(content) + raise + file = os.fdopen(fd, 'wt') + file.write(content) # Make sure the data hits the disk file.flush() if self.dofsync: - os.fsync(file.fileno()) + os.fsync(fd) file.close() if rtime != None: - os.utime(os.path.join(tmpdir, tmpmessagename), (rtime, rtime)) - self.ui.debug('maildir', 'savemessage: moving from %s to %s' % \ - (tmpmessagename, messagename)) - if tmpmessagename != messagename: # then rename it - os.rename(os.path.join(tmpdir, tmpmessagename), - os.path.join(tmpdir, messagename)) - - if self.dofsync: - try: - # fsync the directory (safer semantics in Linux) - fd = os.open(tmpdir, os.O_RDONLY) - os.fsync(fd) - os.close(fd) - except: - pass + os.utime(os.path.join(tmpdir, messagename), (rtime, rtime)) self.messagelist[uid] = {'uid': uid, 'flags': [], 'filename': os.path.join('tmp', messagename)}