diff -u -r release-20010228/conf/sample-local.cf release-20010228.patched/conf/sample-local.cf --- release-20010228/conf/sample-local.cf Thu Feb 1 20:52:56 2001 +++ release-20010228.patched/conf/sample-local.cf Mon Aug 13 11:23:41 2001 @@ -114,6 +114,26 @@ # home_mailbox = Maildir/ home_mailbox = +# The mail_spool_hash_depth parameter specifies optional +# hashed storage of mailboxes in a UNIX-style /var/spool/mail or +# or /var/mail directory. For instance, if the user is "wieste" +# and mail_spool_hash_depth = 3, then Wieste's mailbox +# would be in /var/mail/w/i/e/wieste. Use the default of zero for +# standard UNIX-style flat filesystem storage. +mail_spool_hash_depth = 0 + +# If mail_spool_hash_depth is nonzero, the mail_spool_hash_create +# variable determines if missing hash subdirectories under /var/mail +# or /var/spool/mail are created as needed for local mail delivery. +# If mail_spool_hash_create is "no", then it is assumed that all +# possible hash subdirectores have been created ahead of time. +# Missing hash subdirectories means that mail may not be delivered +# properly, so only set this to "no" if you are *sure* that you have +# all hash subdirectories created first. However, if you *are* sure +# then setting this to "no" speeds up local mail delivery slightly. +# ==> Has no effect if mail_spool_hash_depth is zero. <== +mail_spool_hash_create = yes + # The luser_relay parameter specifies an optional destination address # for unknown recipients. By default, mail for unknown local recipients # is bounced. diff -u -r release-20010228/src/global/mail_params.c release-20010228.patched/src/global/mail_params.c --- release-20010228/src/global/mail_params.c Sun Feb 25 14:51:56 2001 +++ release-20010228.patched/src/global/mail_params.c Mon Aug 13 11:23:48 2001 @@ -44,6 +44,8 @@ /* char *var_db_type; /* char *var_hash_queue_names; /* int var_hash_queue_depth; +/* int var_mail_spool_hash_depth; +/* int var_mail_spool_hash_create; /* int var_trigger_timeout; /* char *var_rcpt_delim; /* int var_fork_tries; @@ -157,6 +159,8 @@ char *var_db_type; char *var_hash_queue_names; int var_hash_queue_depth; +int var_mail_spool_hash_depth; +int var_mail_spool_hash_create; int var_trigger_timeout; char *var_rcpt_delim; int var_fork_tries; @@ -305,6 +309,7 @@ VAR_LINE_LIMIT, DEF_LINE_LIMIT, &var_line_limit, 512, 0, VAR_MESSAGE_LIMIT, DEF_MESSAGE_LIMIT, &var_message_limit, 0, 0, VAR_HASH_QUEUE_DEPTH, DEF_HASH_QUEUE_DEPTH, &var_hash_queue_depth, 1, 0, + VAR_MAIL_SPOOL_HASH_DEPTH, DEF_MAIL_SPOOL_HASH_DEPTH, &var_mail_spool_hash_depth, 0, 0, VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0, VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0, 0, @@ -324,6 +329,7 @@ VAR_DISABLE_DNS, DEF_DISABLE_DNS, &var_disable_dns, VAR_SOFT_BOUNCE, DEF_SOFT_BOUNCE, &var_soft_bounce, VAR_OWNREQ_SPECIAL, DEF_OWNREQ_SPECIAL, &var_ownreq_special, + VAR_MAIL_SPOOL_HASH_CREATE, DEF_MAIL_SPOOL_HASH_CREATE, &var_mail_spool_hash_create, 0, }; diff -u -r release-20010228/src/global/mail_params.h release-20010228.patched/src/global/mail_params.h --- release-20010228/src/global/mail_params.h Sun Feb 25 13:34:21 2001 +++ release-20010228.patched/src/global/mail_params.h Mon Aug 13 11:23:58 2001 @@ -350,6 +350,14 @@ #define DEF_MAIL_SPOOL_DIR _PATH_MAILDIR extern char *var_mail_spool_dir; +#define VAR_MAIL_SPOOL_HASH_DEPTH "mail_spool_hash_depth" +#define DEF_MAIL_SPOOL_HASH_DEPTH 0 +extern int var_mail_spool_hash_depth; + +#define VAR_MAIL_SPOOL_HASH_CREATE "mail_spool_hash_create" +#define DEF_MAIL_SPOOL_HASH_CREATE 1 +extern bool var_mail_spool_hash_create; + #define VAR_HOME_MAILBOX "home_mailbox" #define DEF_HOME_MAILBOX "" extern char *var_home_mailbox; diff -u -r release-20010228/src/local/local.h release-20010228.patched/src/local/local.h --- release-20010228/src/local/local.h Sat Nov 25 09:33:13 2000 +++ release-20010228.patched/src/local/local.h Mon Aug 13 11:24:08 2001 @@ -157,6 +157,7 @@ extern int deliver_indirect(LOCAL_STATE); extern int deliver_maildir(LOCAL_STATE, USER_ATTR, char *); extern int deliver_unknown(LOCAL_STATE, USER_ATTR); +extern int mailbox_mkdirs(const char *, const char *); /* * Restrictions on delivery to sensitive destinations. diff -u -r release-20010228/src/local/mailbox.c release-20010228.patched/src/local/mailbox.c --- release-20010228/src/local/mailbox.c Sun Dec 3 20:34:59 2000 +++ release-20010228.patched/src/local/mailbox.c Mon Aug 13 11:24:13 2001 @@ -59,8 +59,11 @@ #include #include #include +#include #include #include +#include +#include /* Global library. */ @@ -96,6 +99,11 @@ MBOX *mp; int status; int copy_flags; + int user_hash; + int user_length; + int ret_mkdirs = -1; + int ntries = 1; + int maxtries = 5; VSTRING *biff; long end; struct stat st; @@ -121,11 +129,44 @@ status = -1; why = vstring_alloc(100); if (*var_home_mailbox) { - spool_dir = 0; - mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); + spool_dir = 0; + mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); } else { - spool_dir = var_mail_spool_dir; - mailbox = concatenate(spool_dir, "/", state.msg_attr.user, (char *) 0); + /* + * standard UNIX-style flat file system in mail spool directory + */ + spool_dir = var_mail_spool_dir; + if (var_mail_spool_hash_depth == 0) { + mailbox = concatenate(spool_dir, "/", state.msg_attr.user, (char *) 0); + } else { + /* + * hashed directories under the mail spool directory + * in this case, the hash size is minimum of username length and + * the spool hash depth, we don't want underscores for short names + */ + user_length = strlen(state.msg_attr.user); + user_hash = (user_length < var_mail_spool_hash_depth) ? + user_length : var_mail_spool_hash_depth; + mailbox = concatenate(spool_dir, "/", + dir_forest(NULL, state.msg_attr.user, user_hash), + state.msg_attr.user, (char *) 0); + + /* + * create the hashed directories if mail_spool_hash_create "yes" + * try multiple times if need be to avoid race conditions with + * other "local" processes working in same directory tree + */ + if(var_mail_spool_hash_create) { + while(ntries <= maxtries) { + ret_mkdirs = mailbox_mkdirs(spool_dir,mailbox); + if(ret_mkdirs == 0) break; + ntries++; + } + if(ret_mkdirs < 0) + msg_warn("%d attempts to create directory for %s", + maxtries,mailbox); + } + } } /* @@ -295,4 +336,45 @@ mypwfree(mbox_pwd); *statusp = status; return (YES); +} + +/* mailbox_mkdirs - fill in missing directories for hashed /var/mail */ + +int mailbox_mkdirs(const char *spool_dir, const char *path) +{ + char *myname = "mailbox_mkdirs"; + char *saved_path = mystrdup(path); + struct stat st; + mode_t lastumask; + int ret; + + /* + * stat the spool_dir to determine owner, group, and permissions + * make any subdirs created here the same as the parent spool_dir + */ + if(stat(spool_dir, &st) < 0) + msg_panic("%s: %s, %m", myname, spool_dir); + + /* + * zero out the umask so that it does not influence directory perms + * set the euid, egid to that of the mail spool_dir for owner, group + */ + lastumask = umask(0); + set_eugid(st.st_uid, st.st_gid); + + /* + * Truncate a copy of the pathname (for safety sake), and create the + * missing directories with same owner, group, perms as spool_dir + */ + if (split_at_right(saved_path, '/') == 0) + msg_panic("%s: no slash in: %s", myname, saved_path); + ret = make_dirs(saved_path, st.st_mode); + + /* + * Cleanup: set the euid, egid, and umask back to what they were + */ + (void) umask(lastumask); + set_eugid(var_owner_uid, var_owner_gid); + myfree(saved_path); + return (ret); }