src.lock_maildirsize
changeset 6 d2183655483b
child 7 3e755b50b6a4
--- /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 */