src.lock_maildirsize
changeset 6 d2183655483b
child 7 3e755b50b6a4
equal deleted inserted replaced
5:399967a8bbf1 6:d2183655483b
       
     1 # HG changeset patch
       
     2 # Parent 5037ac63d7aece4b5e96e26bace1ab4b7e791804
       
     3 
       
     4 diff -r 5037ac63d7ae Local/Makefile
       
     5 --- a/Local/Makefile	Sat Feb 19 13:42:16 2011 +0100
       
     6 +++ b/Local/Makefile	Sun Feb 20 23:37:25 2011 +0100
       
     7 @@ -1,4 +1,5 @@
       
     8  # $Cambridge: exim/src/src/EDITME,v 1.27 2010/06/12 15:21:25 jetmore Exp $
       
     9 +EXTRALIBS_EXIM=-lrt
       
    10  
       
    11  ##################################################
       
    12  #          The Exim mail transport agent         #
       
    13 diff -r 5037ac63d7ae src/transports/appendfile.c
       
    14 --- a/src/transports/appendfile.c	Sat Feb 19 13:42:16 2011 +0100
       
    15 +++ b/src/transports/appendfile.c	Sun Feb 20 23:37:25 2011 +0100
       
    16 @@ -13,6 +13,7 @@
       
    17  
       
    18  #ifdef SUPPORT_MAILDIR
       
    19  #include "tf_maildir.h"
       
    20 +extern int lockfd;				/* from tf_maildir */	// hs12
       
    21  #endif
       
    22  
       
    23  
       
    24 @@ -278,8 +279,8 @@
       
    25  gid = gid;
       
    26  
       
    27  if (ob->expand_maildir_use_size_file)
       
    28 -	ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file, 
       
    29 -		US"`maildir_use_size_file` in transport", tblock->name);
       
    30 +  ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file, 
       
    31 +    US"`maildir_use_size_file` in transport", tblock->name);
       
    32  
       
    33  /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
       
    34  mailbox_filecount */
       
    35 @@ -841,7 +842,7 @@
       
    36                sigalrm_seen set if there has been a timeout
       
    37  */
       
    38  
       
    39 -static int
       
    40 +int
       
    41  apply_lock(int fd, int fcntltype, BOOL dofcntl, int fcntltime, BOOL doflock,
       
    42      int flocktime)
       
    43  {
       
    44 @@ -887,7 +888,6 @@
       
    45  
       
    46  
       
    47  
       
    48 -
       
    49  #ifdef SUPPORT_MBX
       
    50  /*************************************************
       
    51  *         Copy message into MBX mailbox          *
       
    52 @@ -2416,18 +2416,32 @@
       
    53        {
       
    54        off_t size;
       
    55        int filecount;
       
    56 +      uschar *excuse;
       
    57 +
       
    58 +      struct timespec t0, t1;
       
    59 +      clock_gettime(CLOCK_REALTIME, &t0);
       
    60  
       
    61        maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
       
    62 -        &size, &filecount);
       
    63 +        &size, &filecount, &excuse);
       
    64  
       
    65        if (maildirsize_fd == -1)
       
    66          {
       
    67          addr->basic_errno = errno;
       
    68 -        addr->message = string_sprintf("while opening or reading "
       
    69 -          "%s/maildirsize", check_path);
       
    70 +        //addr->message = string_sprintf("%s", excuse);
       
    71 +        addr->message = excuse;
       
    72          return FALSE;
       
    73          }
       
    74  
       
    75 +
       
    76 +      clock_gettime(CLOCK_REALTIME, &t1);
       
    77 +      t1.tv_sec -= t0.tv_sec;
       
    78 +      t1.tv_nsec -= t0.tv_nsec;
       
    79 +      if (t1.tv_nsec < 0) { t1.tv_sec--; t1.tv_nsec *= -1; }
       
    80 +      DEBUG(D_transport) {
       
    81 +         if (t1.tv_sec >= 1) debug_printf("%s/maildirsize was locked for %d.09ld seconds\n",
       
    82 +          check_path, t1.tv_sec, t1.tv_nsec);
       
    83 +      }
       
    84 +          
       
    85        if (mailbox_size < 0) mailbox_size = size;
       
    86        if (mailbox_filecount < 0) mailbox_filecount = filecount;
       
    87        }
       
    88 @@ -3125,6 +3139,8 @@
       
    89        uschar *renamename = newname;
       
    90        fd = -1;
       
    91  
       
    92 +			if (lockfd >= 0) (void)apply_lock(lockfd, F_RDLCK, 1, 120, 0, 0);  // hs12
       
    93 +
       
    94        DEBUG(D_transport) debug_printf("renaming temporary file\n");
       
    95  
       
    96        /* If there is no rename name set, we are in a non-maildir, non-mailstore
       
    97 @@ -3245,6 +3261,7 @@
       
    98            filename = dataname = NULL;   /* Prevents attempt to unlink at end */
       
    99            }
       
   100          }        /* maildir or mailstore */
       
   101 +
       
   102        }          /* successful write + close */
       
   103      }            /* isdirectory */
       
   104    }              /* write success */
       
   105 @@ -3280,6 +3297,11 @@
       
   106  detected, in order to get the file closed and the lock file tidied away. */
       
   107  
       
   108  RETURN:
       
   109 +if (lockfd >= 0) 
       
   110 +  {
       
   111 +	(void)apply_lock(lockfd, F_UNLCK, 1, 120, 0, 0); // hs12
       
   112 +	(void)close(lockfd);
       
   113 +	}
       
   114  
       
   115  #ifdef SUPPORT_MBX
       
   116  if (mbx_lockfd >= 0)
       
   117 diff -r 5037ac63d7ae src/transports/appendfile.h
       
   118 --- a/src/transports/appendfile.h	Sat Feb 19 13:42:16 2011 +0100
       
   119 +++ b/src/transports/appendfile.h	Sun Feb 20 23:37:25 2011 +0100
       
   120 @@ -95,5 +95,8 @@
       
   121  /* Function that is shared with tf_maildir.c */
       
   122  
       
   123  extern off_t  check_dir_size(uschar *, int *, const pcre *);
       
   124 +extern int apply_lock(int fd, int fcntltype, BOOL dofcntl, int fcntltime, BOOL doflock,
       
   125 +    int flocktime);
       
   126 +
       
   127  
       
   128  /* End of transports/appendfile.h */
       
   129 diff -r 5037ac63d7ae src/transports/tf_maildir.c
       
   130 --- a/src/transports/tf_maildir.c	Sat Feb 19 13:42:16 2011 +0100
       
   131 +++ b/src/transports/tf_maildir.c	Sun Feb 20 23:37:25 2011 +0100
       
   132 @@ -367,13 +367,16 @@
       
   133  
       
   134  Returns:           >=0  a file descriptor for an open maildirsize file
       
   135                     -1   there was an error opening or accessing the file
       
   136 +				        or locking
       
   137                     -2   the file was removed because of a race
       
   138  */
       
   139  
       
   140 +int lockfd = -1;	// hs12
       
   141 +
       
   142  int
       
   143  maildir_ensure_sizefile(uschar *path, appendfile_transport_options_block *ob,
       
   144    const pcre *regex, const pcre *dir_regex, off_t *returned_size,
       
   145 -  int *returned_filecount)
       
   146 +  int *returned_filecount, uschar **excuse)
       
   147  {
       
   148  int count, fd;
       
   149  off_t cached_quota = 0;
       
   150 @@ -381,25 +384,80 @@
       
   151  int filecount = 0;
       
   152  int linecount = 0;
       
   153  off_t size = 0;
       
   154 -uschar *filename;
       
   155 +uschar *filename, *lockname, *hitchname;
       
   156  uschar buffer[MAX_FILE_SIZE];
       
   157  uschar *ptr = buffer;
       
   158  uschar *endptr;
       
   159  
       
   160 +filename = string_sprintf("%s/maildirsize", path);
       
   161 +lockname = string_sprintf("%s.lock", filename);
       
   162 +hitchname = string_sprintf( "%s.%s.%08x.%08x", lockname, primary_hostname,
       
   163 +      (unsigned int)(time(NULL)), (unsigned int)getpid());
       
   164 +
       
   165 +/* Try to create a lock file. Here we should copy the loop from appendfile.c,
       
   166 +since our approach is quite simplified */
       
   167 +lockfd = Uopen(lockname, O_CREAT|O_RDWR, ob->mode ? ob->mode : 0600);
       
   168 +if (lockfd < 0)
       
   169 +  {
       
   170 +    if (errno != ENOENT)
       
   171 +	  {
       
   172 +	  *excuse = string_sprintf("open %s", lockname);
       
   173 +	  return -1;
       
   174 +	  }
       
   175 +
       
   176 +	lockfd = Uopen(hitchname, O_WRONLY|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600);
       
   177 +	if (lockfd < 0) 
       
   178 +	  {
       
   179 +		*excuse = string_sprintf("open %s", hitchname);
       
   180 +		return -1;
       
   181 +	  }
       
   182 +	if (link(hitchname, lockname) < 0)
       
   183 +	  {
       
   184 +	  int save_errno = errno;
       
   185 +	  (void)unlink(hitchname);
       
   186 +	  errno = save_errno;
       
   187 +	  if (errno != EEXIST) 
       
   188 +		{
       
   189 +		  *excuse = string_sprintf("link %s -> %s", hitchname, lockname);
       
   190 +		  return -1;
       
   191 +		}
       
   192 +	  (void)close(lockfd);
       
   193 +	  lockfd = Uopen(lockname, O_WRONLY, ob->mode ? ob->mode : 0600);
       
   194 +	  if (lockfd < 0) 
       
   195 +		{
       
   196 +		*excuse = string_sprintf("open %s", lockname);
       
   197 +		return -1;
       
   198 +		}
       
   199 +	  }
       
   200 +	(void)unlink(hitchname);
       
   201 +  }
       
   202 +	
       
   203 +
       
   204 +/* Before doing anything here we try to open and lock the maildirsize.lock
       
   205 +file. This should prevent/avoid races and parallel recalculations. We
       
   206 +need to do this on an extra lockfile, since the maildirsize file itself
       
   207 +gets renamed during the recalculation. */
       
   208 +
       
   209 +if (lockfd >= 0)
       
   210 +(void)apply_lock(lockfd, F_WRLCK, 1, 120, 0, 0);	// hs12
       
   211 +if (sigalrm_seen) 
       
   212 +  {
       
   213 +  *excuse = string_sprintf("timeout locking %s", lockname);
       
   214 +  return -1;
       
   215 +  }
       
   216 +DEBUG(D_transport) debug_printf("locked (WR) %s\n", lockname);
       
   217 +
       
   218  /* Try a few times to open or create the file, in case another process is doing
       
   219  the same thing. */
       
   220  
       
   221 -filename = string_sprintf("%s/maildirsize", path);
       
   222 -
       
   223  DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path);
       
   224  fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600);
       
   225 -if (fd < 0)
       
   226 -  {
       
   227 +if (fd < 0) {
       
   228    if (errno != ENOENT) return -1;
       
   229    DEBUG(D_transport)
       
   230      debug_printf("%s does not exist: recalculating\n", filename);
       
   231    goto RECALCULATE;
       
   232 -  }
       
   233 +}
       
   234  
       
   235  /* The file has been successfully opened. Check that the cached quota value is
       
   236  still correct, and that the size of the file is still small enough. If so,
       
   237 @@ -588,8 +646,10 @@
       
   238      }
       
   239    }
       
   240  
       
   241 +	DEBUG(D_transport) debug_printf("releasing lock\n");
       
   242 +  (void)apply_lock(lockfd, F_UNLCK, 1, 120, 0, 0);
       
   243 +
       
   244  /* Return the sizes and the file descriptor, if any */
       
   245 -
       
   246  DEBUG(D_transport) debug_printf("returning maildir size=" OFF_T_FMT
       
   247    " filecount=%d\n", size, filecount);
       
   248  *returned_size = size;
       
   249 diff -r 5037ac63d7ae src/transports/tf_maildir.h
       
   250 --- a/src/transports/tf_maildir.h	Sat Feb 19 13:42:16 2011 +0100
       
   251 +++ b/src/transports/tf_maildir.h	Sun Feb 20 23:37:25 2011 +0100
       
   252 @@ -16,7 +16,7 @@
       
   253                  uschar *);
       
   254  extern int    maildir_ensure_sizefile(uschar *,
       
   255                  appendfile_transport_options_block *, const pcre *,
       
   256 -                const pcre *, off_t *, int *);
       
   257 +                const pcre *, off_t *, int *, uschar **);
       
   258  extern void   maildir_record_length(int, int);
       
   259  
       
   260  /* End of tf_maildir.h */