[Mailman-Developers] patch candidate for MailList.Save()

Barry A. Warsaw bwarsaw@cnri.reston.va.us (Barry A. Warsaw)
Mon, 19 Jul 1999 18:30:17 -0400 (EDT)


>>>>> "HM" == Harald Meland <Harald.Meland@usit.uio.no> writes:

    HM> You missed

    HM>  2.5) config.db is removed

    HM> which constitutes a race condition -- I think the below patch
    HM> fixes that (but as you say, this really is crucial code to get
    HM> right, so I'm posting it here for others to comment before
    HM> committing):

Arg!  You're right of course.  Here's a slightly reworked version of
your patch.  Also, not committed yet, but seems to work for me.

-Barry

-------------------- snip snip --------------------
Index: MailList.py
===================================================================
RCS file: /projects/cvsroot/mailman/Mailman/MailList.py,v
retrieving revision 1.130
diff -c -r1.130 MailList.py
*** MailList.py	1999/07/18 17:26:55	1.130
--- MailList.py	1999/07/19 22:22:56
***************
*** 26,31 ****
--- 26,32 ----
  			 'for the site?')
  
  import sys, os, marshal, string, posixfile, time
+ import errno
  import re
  import Utils
  import Errors
***************
*** 759,764 ****
--- 760,767 ----
          fname = os.path.join(self._full_path, 'config.db')
          fname_tmp = fname + '.tmp.' + `os.getpid()`
          fname_last = fname + ".last"
+         # Make config.db unreadable by `other', as it contains all the
+         # list members' passwords (in clear text).
          omask = os.umask(007)
          try:
              try:
***************
*** 771,780 ****
                              'Failed config.db file write, retaining old state'
                              '\n %s' % `status.args`)
                  Utils.reraise()
!             # now move config.db -> config.db.last
!             # then move config.db.tmp.xxx -> config.db
!             aside_new(fname, fname_last)
!             aside_new(fname_tmp, fname)
          finally:
              os.umask(omask)
          self.CheckHTMLArchiveDir()
--- 774,789 ----
                              'Failed config.db file write, retaining old state'
                              '\n %s' % `status.args`)
                  Utils.reraise()
!             # Now do config.db.tmp.xxx -> config.db -> config.db.last
!             # rotation -- safely.
!             try:
!                 # might not exist yet
!                 os.unlink(fname_last)
!             except os.error, (code, msg):
!                 if code <> errno.ENOENT:
!                     Utils.reraise()
!             os.link(fname, fname_last)
!             os.rename(fname_tmp, fname)
          finally:
              os.umask(omask)
          self.CheckHTMLArchiveDir()
***************
*** 1376,1397 ****
  	return ("<%s.%s %s%s at %s>"
  		% (self.__module__, self.__class__.__name__,
  		   `self._internal_name`, status, hex(id(self))[2:]))
- 
- def aside_new(old_name, new_name, reopen=0):
-     """Utility to move aside a file, optionally returning a fresh open version.
- 
-     We ensure maintanance of proper umask in the process."""
-     # Make config.db unreadable by `other', as it contains all the list
-     # members' passwords (in clear text).
-     ou = os.umask(007)
-     try:
-         if os.path.exists(new_name):
-             os.unlink(new_name)
-         if os.path.exists(old_name):
-             os.link(old_name, new_name)
-             os.unlink(old_name)
-         if reopen:
-             file = open(old_name, 'w')
-             return file
-     finally:
-         os.umask(ou)
--- 1385,1387 ----