
As promised, here's a patch that: 1) Adds the SPLIT_DIRS option which does this: root@gandalf:/var/local/mailman/lists# l total 16 drwxrwsr-x 4 root mailman 4096 Jan 1 20:03 ./ drwxrwsr-x 19 mailman mailman 4096 Jan 1 09:55 ../ drwxrwsr-x 3 root mailman 4096 Jan 1 19:46 m/ lrwxrwxrwx 1 root mailman 43 Jan 1 20:01 mailman-owner -> /var/local/mailman/lists/m/ma/mailman-owner/ drwxrwsr-x 3 root mailman 4096 Jan 1 12:18 t/ lrwxrwxrwx 1 root mailman 34 Jan 1 20:02 test -> /var/local/mailman/lists/t/te/test/ lrwxrwxrwx 1 root mailman 35 Jan 1 20:03 test2 -> /var/local/mailman/lists/t/te/test2/ root@gandalf:/var/local/mailman/archives/private# l total 16 drwxrws--x 4 root mailman 4096 Jan 1 20:03 ./ drwxrwsr-x 4 root mailman 4096 Jan 1 09:55 ../ drwxrwsr-x 3 root mailman 4096 Jan 1 19:46 m/ lrwxrwxrwx 1 root mailman 54 Jan 1 20:01 mailman-owner -> /var/local/mailman/archives/private/m/ma/mailman-owner/ lrwxrwxrwx 1 root mailman 59 Jan 1 20:01 mailman-owner.mbox -> /var/local/mailman/archives/private/m/ma/mailman-owner.mbox/ drwxrwsr-x 3 root mailman 4096 Jan 1 12:18 t/ lrwxrwxrwx 1 root mailman 45 Jan 1 20:02 test -> /var/local/mailman/archives/private/t/te/test/ lrwxrwxrwx 1 root mailman 46 Jan 1 20:03 test2 -> /var/local/mailman/archives/private/t/te/test2/ lrwxrwxrwx 1 root mailman 51 Jan 1 20:03 test2.mbox -> /var/local/mailman/archives/private/t/te/test2.mbox/ lrwxrwxrwx 1 root mailman 50 Jan 1 20:02 test.mbox -> /var/local/mailman/archives/private/t/te/test.mbox/ This gets around the 32K link / directory limit in many filesystems (limiting mailman to 16K lists) 2) Creates the pipermail html dir at list creation time so that you don't get an http error when you view the archive of a list that doesn't have messages yet 3) rmlist now does what it advertises with -a (you couldn't erase archives after erasing a list) root@gandalf:/var/local/mailman/bin# ./rmlist test2 Not removing archives. Reinvoke with -a to remove them. Removing list info Removing list info root@gandalf:/var/local/mailman/bin# ./rmlist -a test2 List test2 does not exist or was already deleted, trying to remove archives. Removing private archives Removing private archives Removing private archives Removing private archives Removing public archives test2 public archives not found as /var/local/mailman/archives/public/t/te/test2 Removing public archives test2 public archives not found as /var/local/mailman/archives/public/t/te/test2.mbox If everyone is cool with this, I'll write a tool to convert an existing installation to the new (optional) directory layout (I need this for lists.sourceforge.net) diff -urN mailman/Mailman/Archiver/Archiver.py mailman.subdirs/Mailman/Archiver/Archiver.py --- mailman/Mailman/Archiver/Archiver.py Fri Oct 26 23:57:47 2001 +++ mailman.subdirs/Mailman/Archiver/Archiver.py Tue Jan 1 11:07:56 2002 @@ -76,6 +76,10 @@ # listname.mbox # listname/ # lots-of-pipermail-stuff + # (note that if mm_cfg.SPLIT_DIRS is set, we create subdirectories and + # use symlinks (this gets around a 32k directories limit in some + # filesystems linked to a 32k hardlink limit per inode -- Marc)) + # # public/ # listname.mbox@ -> ../private/listname.mbox # listname@ -> ../private/listname @@ -89,7 +93,25 @@ omask = os.umask(0) try: try: - os.mkdir(self.archive_dir()+'.mbox', 02775) + listname=self.internal_name(); + if mm_cfg.SPLIT_DIRS: + archprivdir=os.path.join(mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, + listname[0], listname[0:2], listname + '.mbox') + os.makedirs(archprivdir, 02775) + os.symlink(archprivdir, self.archive_dir()+'.mbox') + else: + os.mkdir(self.archive_dir()+'.mbox', 02775) + # We also create an empty pipermail archive directory (pipermail + # would create it, but in the meantime lists with no archives + # return errors when you browse the non existant archive dir) + # Besides, pipermail won't know about mm_cfg.SPLIT_DIRS -- Marc + if mm_cfg.SPLIT_DIRS: + archprivdir=os.path.join(mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, + listname[0], listname[0:2], listname) + os.makedirs(archprivdir, 02775) + os.symlink(archprivdir, self.archive_dir()) + else: + os.mkdir(self.archive_dir(), 02775) except OSError, e: if e.errno <> errno.EEXIST: raise finally: diff -urN mailman/Mailman/Archiver/pipermail.py mailman.subdirs/Mailman/Archiver/pipermail.py --- mailman/Mailman/Archiver/pipermail.py Fri Nov 30 09:07:32 2001 +++ mailman.subdirs/Mailman/Archiver/pipermail.py Tue Jan 1 11:28:15 2002 @@ -252,6 +252,9 @@ self.database = database # If the directory doesn't exist, create it + # This code shouldn't get run anymore, we create the directory in + # Archiver.py. It should only get used by legacy lists created that + # are only receiving their first message in the HTML archive now -- Marc try: os.stat(self.basedir) except os.error, errdata: diff -urN mailman/Mailman/Defaults.py.in mailman.subdirs/Mailman/Defaults.py.in --- mailman/Mailman/Defaults.py.in Tue Jan 1 08:29:01 2002 +++ mailman.subdirs/Mailman/Defaults.py.in Tue Jan 1 12:50:35 2002 @@ -62,6 +62,13 @@ HOME_PAGE = 'index.html' MAILMAN_SITE_LIST = 'mailman' +# Set to '1' to have mailman start creating lists in directories like +# ~mailman/lists/l/li/listname/ (same thing for the archives) to get around +# the 32K directory limitation in some filesystems. +# Because this sets symlinks to the expected positions, it is fully forward +# and backward compatible -- Marc +SPLIT_DIRS = 0 + ##### diff -urN mailman/Mailman/MailList.py mailman.subdirs/Mailman/MailList.py --- mailman/Mailman/MailList.py Tue Jan 1 08:29:02 2002 +++ mailman.subdirs/Mailman/MailList.py Tue Jan 1 10:40:25 2002 @@ -388,7 +388,13 @@ Utils.ValidateEmail(admin) omask = os.umask(0) try: - os.makedirs(os.path.join(mm_cfg.LIST_DATA_DIR, name), 02775) + listdir=os.path.join(mm_cfg.LIST_DATA_DIR, name) + if mm_cfg.SPLIT_DIRS: + splitdir=os.path.join(mm_cfg.LIST_DATA_DIR, name[0], name[0:2], name) + os.makedirs(splitdir, 02775) + os.symlink(splitdir, listdir) + else: + os.makedirs(listdir, 02775) finally: os.umask(omask) self._full_path = os.path.join(mm_cfg.LIST_DATA_DIR, name) diff -urN mailman/bin/rmlist mailman.subdirs/bin/rmlist --- mailman/bin/rmlist Sat Sep 8 01:18:47 2001 +++ mailman.subdirs/bin/rmlist Tue Jan 1 12:35:35 2002 @@ -1,4 +1,4 @@ -#! @PYTHON@ +#! /usr/bin/python # # Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc. # @@ -78,11 +78,6 @@ usage(1) listname = args[0].lower().strip() - if not Utils.list_exists(listname): - usage(1, _('No such list: %(listname)s')) - - mlist = MailList.MailList(listname, lock=0) - removeArchives = 0 for opt, arg in opts: if opt in ('-a', '--archives'): @@ -90,28 +85,57 @@ elif opt in ('-h', '--help'): usage(0) + if not Utils.list_exists(listname): + if not removeArchives: + usage(1, _('No such list (or list already deleted): %(listname)s')) + else: + print _('List %(listname)s does not exist or was already deleted, trying to remove archives.') + if not removeArchives: print _('Not removing archives. Reinvoke with -a to remove them.') - # Do the MTA-specific list deletion tasks - if mm_cfg.MTA: - modname = 'Mailman.MTA.' + mm_cfg.MTA - __import__(modname) - sys.modules[modname].remove(mlist) - REMOVABLES = [('lists/%s', 'list info'), - ] + listsplitsubdir = os.path.join(listname[0], listname[0:2], listname) + listmboxsplitsubdir = os.path.join(listname[0], listname[0:2], listname + ".mbox") + REMOVABLES = [ ] + if Utils.list_exists(listname): + mlist = MailList.MailList(listname, lock=0) + + # Do the MTA-specific list deletion tasks + if mm_cfg.MTA: + modname = 'Mailman.MTA.' + mm_cfg.MTA + __import__(modname) + sys.modules[modname].remove(mlist) + + REMOVABLES = [ + (os.path.join('lists', listname), _('list info')), + (os.path.join('lists', listsplitsubdir), _('list info')) + ] + if removeArchives: - REMOVABLES.extend( - [('archives/private/%s', _('private archives')), - ('archives/private/%s.mbox', _('private archives')), - ('archives/public/%s', _('public archives')), - ('archives/public/%s.mbox', _('public archives')), - ]) + REMOVABLES.extend ([ + (os.path.join('archives', 'private', listname), + _('private archives')), + (os.path.join('archives', 'private', listsplitsubdir), + _('private archives')), + (os.path.join('archives', 'private', listname + '.mbox'), + _('private archives')), + (os.path.join('archives', 'private', listmboxsplitsubdir), + _('private archives')), + + (os.path.join('archives', 'public', listname), + _('public archives')), + (os.path.join('archives', 'public', listsplitsubdir), + _('public archives')), + (os.path.join('archives', 'public', listname + '.mbox'), + _('public archives')), + (os.path.join('archives', 'public', listmboxsplitsubdir), + _('public archives')) + ]) for dirtmpl, msg in REMOVABLES: - dir = os.path.join(mm_cfg.VAR_PREFIX, dirtmpl % listname) + dir = os.path.join(mm_cfg.VAR_PREFIX, dirtmpl) remove_it(listname, dir, msg) -- Microsoft is to operating systems & security .... .... what McDonalds is to gourmet cooking Home page: http://marc.merlins.org/ | Finger marc_f@merlins.org for PGP key