diff -r 399967a8bbf1 -r d2183655483b src.lock_maildirsize --- /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 */