--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src.lock_maildirsize Sun Feb 20 23:59:48 2011 +0100
@@ -0,0 +1,260 @@
+# HG changeset patch
+# Parent 5037ac63d7aece4b5e96e26bace1ab4b7e791804
+
+diff -r 5037ac63d7ae Local/Makefile
+--- a/Local/Makefile Sat Feb 19 13:42:16 2011 +0100
++++ b/Local/Makefile Sun Feb 20 23:37:25 2011 +0100
+@@ -1,4 +1,5 @@
+ # $Cambridge: exim/src/src/EDITME,v 1.27 2010/06/12 15:21:25 jetmore Exp $
++EXTRALIBS_EXIM=-lrt
+
+ ##################################################
+ # The Exim mail transport agent #
+diff -r 5037ac63d7ae src/transports/appendfile.c
+--- a/src/transports/appendfile.c Sat Feb 19 13:42:16 2011 +0100
++++ b/src/transports/appendfile.c Sun Feb 20 23:37:25 2011 +0100
+@@ -13,6 +13,7 @@
+
+ #ifdef SUPPORT_MAILDIR
+ #include "tf_maildir.h"
++extern int lockfd; /* from tf_maildir */ // hs12
+ #endif
+
+
+@@ -278,8 +279,8 @@
+ gid = gid;
+
+ if (ob->expand_maildir_use_size_file)
+- ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file,
+- US"`maildir_use_size_file` in transport", tblock->name);
++ ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file,
++ US"`maildir_use_size_file` in transport", tblock->name);
+
+ /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
+ mailbox_filecount */
+@@ -841,7 +842,7 @@
+ sigalrm_seen set if there has been a timeout
+ */
+
+-static int
++int
+ apply_lock(int fd, int fcntltype, BOOL dofcntl, int fcntltime, BOOL doflock,
+ int flocktime)
+ {
+@@ -887,7 +888,6 @@
+
+
+
+-
+ #ifdef SUPPORT_MBX
+ /*************************************************
+ * Copy message into MBX mailbox *
+@@ -2416,18 +2416,32 @@
+ {
+ off_t size;
+ int filecount;
++ uschar *excuse;
++
++ struct timespec t0, t1;
++ clock_gettime(CLOCK_REALTIME, &t0);
+
+ maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
+- &size, &filecount);
++ &size, &filecount, &excuse);
+
+ if (maildirsize_fd == -1)
+ {
+ addr->basic_errno = errno;
+- addr->message = string_sprintf("while opening or reading "
+- "%s/maildirsize", check_path);
++ //addr->message = string_sprintf("%s", excuse);
++ addr->message = excuse;
+ return FALSE;
+ }
+
++
++ clock_gettime(CLOCK_REALTIME, &t1);
++ t1.tv_sec -= t0.tv_sec;
++ t1.tv_nsec -= t0.tv_nsec;
++ if (t1.tv_nsec < 0) { t1.tv_sec--; t1.tv_nsec *= -1; }
++ DEBUG(D_transport) {
++ if (t1.tv_sec >= 1) debug_printf("%s/maildirsize was locked for %d.09ld seconds\n",
++ check_path, t1.tv_sec, t1.tv_nsec);
++ }
++
+ if (mailbox_size < 0) mailbox_size = size;
+ if (mailbox_filecount < 0) mailbox_filecount = filecount;
+ }
+@@ -3125,6 +3139,8 @@
+ uschar *renamename = newname;
+ fd = -1;
+
++ if (lockfd >= 0) (void)apply_lock(lockfd, F_RDLCK, 1, 120, 0, 0); // hs12
++
+ DEBUG(D_transport) debug_printf("renaming temporary file\n");
+
+ /* If there is no rename name set, we are in a non-maildir, non-mailstore
+@@ -3245,6 +3261,7 @@
+ filename = dataname = NULL; /* Prevents attempt to unlink at end */
+ }
+ } /* maildir or mailstore */
++
+ } /* successful write + close */
+ } /* isdirectory */
+ } /* write success */
+@@ -3280,6 +3297,11 @@
+ detected, in order to get the file closed and the lock file tidied away. */
+
+ RETURN:
++if (lockfd >= 0)
++ {
++ (void)apply_lock(lockfd, F_UNLCK, 1, 120, 0, 0); // hs12
++ (void)close(lockfd);
++ }
+
+ #ifdef SUPPORT_MBX
+ if (mbx_lockfd >= 0)
+diff -r 5037ac63d7ae src/transports/appendfile.h
+--- a/src/transports/appendfile.h Sat Feb 19 13:42:16 2011 +0100
++++ b/src/transports/appendfile.h Sun Feb 20 23:37:25 2011 +0100
+@@ -95,5 +95,8 @@
+ /* Function that is shared with tf_maildir.c */
+
+ extern off_t check_dir_size(uschar *, int *, const pcre *);
++extern int apply_lock(int fd, int fcntltype, BOOL dofcntl, int fcntltime, BOOL doflock,
++ int flocktime);
++
+
+ /* End of transports/appendfile.h */
+diff -r 5037ac63d7ae src/transports/tf_maildir.c
+--- a/src/transports/tf_maildir.c Sat Feb 19 13:42:16 2011 +0100
++++ b/src/transports/tf_maildir.c Sun Feb 20 23:37:25 2011 +0100
+@@ -367,13 +367,16 @@
+
+ Returns: >=0 a file descriptor for an open maildirsize file
+ -1 there was an error opening or accessing the file
++ or locking
+ -2 the file was removed because of a race
+ */
+
++int lockfd = -1; // hs12
++
+ int
+ maildir_ensure_sizefile(uschar *path, appendfile_transport_options_block *ob,
+ const pcre *regex, const pcre *dir_regex, off_t *returned_size,
+- int *returned_filecount)
++ int *returned_filecount, uschar **excuse)
+ {
+ int count, fd;
+ off_t cached_quota = 0;
+@@ -381,25 +384,80 @@
+ int filecount = 0;
+ int linecount = 0;
+ off_t size = 0;
+-uschar *filename;
++uschar *filename, *lockname, *hitchname;
+ uschar buffer[MAX_FILE_SIZE];
+ uschar *ptr = buffer;
+ uschar *endptr;
+
++filename = string_sprintf("%s/maildirsize", path);
++lockname = string_sprintf("%s.lock", filename);
++hitchname = string_sprintf( "%s.%s.%08x.%08x", lockname, primary_hostname,
++ (unsigned int)(time(NULL)), (unsigned int)getpid());
++
++/* Try to create a lock file. Here we should copy the loop from appendfile.c,
++since our approach is quite simplified */
++lockfd = Uopen(lockname, O_CREAT|O_RDWR, ob->mode ? ob->mode : 0600);
++if (lockfd < 0)
++ {
++ if (errno != ENOENT)
++ {
++ *excuse = string_sprintf("open %s", lockname);
++ return -1;
++ }
++
++ lockfd = Uopen(hitchname, O_WRONLY|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600);
++ if (lockfd < 0)
++ {
++ *excuse = string_sprintf("open %s", hitchname);
++ return -1;
++ }
++ if (link(hitchname, lockname) < 0)
++ {
++ int save_errno = errno;
++ (void)unlink(hitchname);
++ errno = save_errno;
++ if (errno != EEXIST)
++ {
++ *excuse = string_sprintf("link %s -> %s", hitchname, lockname);
++ return -1;
++ }
++ (void)close(lockfd);
++ lockfd = Uopen(lockname, O_WRONLY, ob->mode ? ob->mode : 0600);
++ if (lockfd < 0)
++ {
++ *excuse = string_sprintf("open %s", lockname);
++ return -1;
++ }
++ }
++ (void)unlink(hitchname);
++ }
++
++
++/* Before doing anything here we try to open and lock the maildirsize.lock
++file. This should prevent/avoid races and parallel recalculations. We
++need to do this on an extra lockfile, since the maildirsize file itself
++gets renamed during the recalculation. */
++
++if (lockfd >= 0)
++(void)apply_lock(lockfd, F_WRLCK, 1, 120, 0, 0); // hs12
++if (sigalrm_seen)
++ {
++ *excuse = string_sprintf("timeout locking %s", lockname);
++ return -1;
++ }
++DEBUG(D_transport) debug_printf("locked (WR) %s\n", lockname);
++
+ /* Try a few times to open or create the file, in case another process is doing
+ the same thing. */
+
+-filename = string_sprintf("%s/maildirsize", path);
+-
+ DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path);
+ fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600);
+-if (fd < 0)
+- {
++if (fd < 0) {
+ if (errno != ENOENT) return -1;
+ DEBUG(D_transport)
+ debug_printf("%s does not exist: recalculating\n", filename);
+ goto RECALCULATE;
+- }
++}
+
+ /* The file has been successfully opened. Check that the cached quota value is
+ still correct, and that the size of the file is still small enough. If so,
+@@ -588,8 +646,10 @@
+ }
+ }
+
++ DEBUG(D_transport) debug_printf("releasing lock\n");
++ (void)apply_lock(lockfd, F_UNLCK, 1, 120, 0, 0);
++
+ /* Return the sizes and the file descriptor, if any */
+-
+ DEBUG(D_transport) debug_printf("returning maildir size=" OFF_T_FMT
+ " filecount=%d\n", size, filecount);
+ *returned_size = size;
+diff -r 5037ac63d7ae src/transports/tf_maildir.h
+--- a/src/transports/tf_maildir.h Sat Feb 19 13:42:16 2011 +0100
++++ b/src/transports/tf_maildir.h Sun Feb 20 23:37:25 2011 +0100
+@@ -16,7 +16,7 @@
+ uschar *);
+ extern int maildir_ensure_sizefile(uschar *,
+ appendfile_transport_options_block *, const pcre *,
+- const pcre *, off_t *, int *);
++ const pcre *, off_t *, int *, uschar **);
+ extern void maildir_record_length(int, int);
+
+ /* End of tf_maildir.h */