]> granicus.if.org Git - mutt/commitdiff
Convert context and buffy to use nanosecond timestamps.
authorKevin McCarthy <kevin@8t8.us>
Thu, 14 Jun 2018 08:17:56 +0000 (16:17 +0800)
committerKevin McCarthy <kevin@8t8.us>
Mon, 18 Jun 2018 05:05:15 +0000 (13:05 +0800)
The inotify interface has an unfortunate side effect of making Mutt
react too quickly to new mail.  Sometimes, the mail is only
half-delivered when the mailbox is checked.  Because Mutt is using the
stat mtime - seconds resolution - this means it won't realize there
are more messages delivered during the same second.

Nanosecond resolution fields were standardized in POSIX.1-2008, so
check for and use those if they are available.

buffy.c
buffy.h
configure.ac
mbox.c
mh.c
mutt.h
muttlib.c
mx.c
protos.h

diff --git a/buffy.c b/buffy.c
index 6db2c1fcb7821b11b517a5e47b00add8f8f90f58..eab17e6e88e81974c34d275f99d601a8f4587fd5 100644 (file)
--- a/buffy.c
+++ b/buffy.c
@@ -149,7 +149,11 @@ static int test_new_folder (const char *path)
 
 void mutt_buffy_cleanup (const char *buf, struct stat *st)
 {
+#ifdef HAVE_UTIMENSAT
+  struct timespec ts[2];
+#else
   struct utimbuf ut;
+#endif
   BUFFY *tmp;
 
   if (option(OPTCHECKMBOXSIZE))
@@ -163,12 +167,30 @@ void mutt_buffy_cleanup (const char *buf, struct stat *st)
     /* fix up the times so buffy won't get confused */
     if (st->st_mtime > st->st_atime)
     {
+#ifdef HAVE_UTIMENSAT
+      ts[0].tv_sec = 0;
+      ts[0].tv_nsec = UTIME_OMIT;
+      ts[1].tv_sec = 0;
+      ts[1].tv_nsec = UTIME_NOW;
+      utimensat (0, buf, ts, 0);
+#else
       ut.actime = st->st_atime;
       ut.modtime = time (NULL);
       utime (buf, &ut); 
+#endif
     }
     else
+    {
+#ifdef HAVE_UTIMENSAT
+      ts[0].tv_sec = 0;
+      ts[0].tv_nsec = UTIME_NOW;
+      ts[1].tv_sec = 0;
+      ts[1].tv_nsec = UTIME_NOW;
+      utimensat (0, buf, ts, 0);
+#else
       utime (buf, NULL);
+#endif
+    }
   }
 }
 
@@ -342,7 +364,8 @@ static int buffy_maildir_check_dir (BUFFY* mailbox, const char *dir_name, int ch
    */
   if (check_new && option(OPTMAILCHECKRECENT))
   {
-    if (stat(path, &sb) == 0 && sb.st_mtime < mailbox->last_visited)
+    if (stat(path, &sb) == 0 &&
+        mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &mailbox->last_visited) < 0)
     {
       rc = 0;
       check_new = 0;
@@ -383,7 +406,8 @@ static int buffy_maildir_check_dir (BUFFY* mailbox, const char *dir_name, int ch
         {
           snprintf(msgpath, sizeof(msgpath), "%s/%s", path, de->d_name);
           /* ensure this message was received since leaving this mailbox */
-          if (stat(msgpath, &sb) == 0 && (sb.st_ctime <= mailbox->last_visited))
+          if (stat(msgpath, &sb) == 0 &&
+              (mutt_stat_timespec_compare (&sb, MUTT_STAT_CTIME, &mailbox->last_visited) <= 0))
             continue;
         }
         mailbox->new = 1;
@@ -438,12 +462,15 @@ static int buffy_mbox_check (BUFFY* mailbox, struct stat *sb, int check_stats)
   if (option (OPTCHECKMBOXSIZE))
     new_or_changed = sb->st_size > mailbox->size;
   else
-    new_or_changed = sb->st_mtime > sb->st_atime
-      || (mailbox->newly_created && sb->st_ctime == sb->st_mtime && sb->st_ctime == sb->st_atime);
+    new_or_changed = (mutt_stat_compare (sb, MUTT_STAT_MTIME, sb, MUTT_STAT_ATIME) > 0)
+      || (mailbox->newly_created &&
+          (mutt_stat_compare (sb, MUTT_STAT_CTIME, sb, MUTT_STAT_MTIME) == 0) &&
+          (mutt_stat_compare (sb, MUTT_STAT_CTIME, sb, MUTT_STAT_ATIME) == 0));
 
   if (new_or_changed)
   {
-    if (!option(OPTMAILCHECKRECENT) || sb->st_mtime > mailbox->last_visited)
+    if (!option(OPTMAILCHECKRECENT) ||
+        (mutt_stat_timespec_compare (sb, MUTT_STAT_MTIME, &mailbox->last_visited) > 0))
     {
       rc = 1;
       mailbox->new = 1;
@@ -460,7 +487,7 @@ static int buffy_mbox_check (BUFFY* mailbox, struct stat *sb, int check_stats)
     mailbox->newly_created = 0;
 
   if (check_stats &&
-      (mailbox->stats_last_checked < sb->st_mtime))
+      (mutt_stat_timespec_compare (sb, MUTT_STAT_MTIME, &mailbox->stats_last_checked) > 0))
   {
     if (mx_open_mailbox (mailbox->path,
                          MUTT_READONLY | MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK,
@@ -671,7 +698,12 @@ void mutt_buffy_setnotified (const char *path)
     return;
 
   buffy->notified = 1;
-  time(&buffy->last_visited);
+#if HAVE_CLOCK_GETTIME
+  clock_gettime (CLOCK_REALTIME, &buffy->last_visited);
+#else
+  buffy->last_visited.tv_nsec = 0;
+  time(&buffy->last_visited.tv_sec);
+#endif
 }
 
 int mutt_buffy_notify (void)
diff --git a/buffy.h b/buffy.h
index 561f5e7f6e90354450a4526c898472f30cd51be1..c0cfddf4dea77fea97172a43a6a4bcf595c7b7e6 100644 (file)
--- a/buffy.h
+++ b/buffy.h
@@ -40,8 +40,8 @@ typedef struct buffy_t
   short notified;              /* user has been notified */
   short magic;                 /* mailbox type */
   short newly_created;         /* mbox or mmdf just popped into existence */
-  time_t last_visited;         /* time of last exit from this mailbox */
-  time_t stats_last_checked;   /* mtime of mailbox the last time stats where checked. */
+  struct timespec last_visited;                /* time of last exit from this mailbox */
+  struct timespec stats_last_checked;  /* mtime of mailbox the last time stats where checked. */
 }
 BUFFY;
 
index d004fe9c4c494604960450a003f314fa00321712..29252173de9f030b46b540eb6b86150692fafcfa 100644 (file)
@@ -454,6 +454,20 @@ AC_CHECK_FUNCS(strftime, , [AC_CHECK_LIB(intl, strftime)])
 dnl Set the atime of files
 AC_CHECK_FUNCS(futimens)
 
+dnl Check for struct timespec
+AC_CHECK_TYPES([struct timespec],,,[[#include <time.h>]])
+
+dnl Check for stat nanosecond resolutions
+AC_CHECK_MEMBERS([struct stat.st_atim.tv_nsec,
+                  struct stat.st_mtim.tv_nsec,
+                  struct stat.st_ctim.tv_nsec],,,[[#include <sys/stat.h>]])
+
+dnl Check for utimesnsat
+AC_CHECK_FUNCS(utimensat)
+
+dnl Check for clock_gettime
+AC_CHECK_FUNCS(clock_gettime)
+
 dnl AIX may not have fchdir()
 AC_CHECK_FUNCS(fchdir, , [mutt_cv_fchdir=no])
 
diff --git a/mbox.c b/mbox.c
index 346688323d79436a3efa225c15424936b6d17a77..171689f04c83f9e16c96ff3f06417e6db4c0c36d 100644 (file)
--- a/mbox.c
+++ b/mbox.c
@@ -90,6 +90,9 @@ int mmdf_parse_mailbox (CONTEXT *ctx)
   HEADER *hdr;
   struct stat sb;
 #ifdef NFS_ATTRIBUTE_HACK
+#ifdef HAVE_UTIMENSAT
+  struct timespec ts[2];
+#endif /* HAVE_UTIMENSAT */
   struct utimbuf newtime;
 #endif
   progress_t progress;
@@ -100,16 +103,24 @@ int mmdf_parse_mailbox (CONTEXT *ctx)
     mutt_perror (ctx->path);
     return (-1);
   }
-  ctx->atime = sb.st_atime;
-  ctx->mtime = sb.st_mtime;
+  mutt_get_stat_timespec (&ctx->atime, &sb, MUTT_STAT_ATIME);
+  mutt_get_stat_timespec (&ctx->mtime, &sb, MUTT_STAT_MTIME);
   ctx->size = sb.st_size;
 
 #ifdef NFS_ATTRIBUTE_HACK
   if (sb.st_mtime > sb.st_atime)
   {
-    newtime.modtime = sb.st_mtime;
+#ifdef HAVE_UTIMENSAT
+    ts[0].tv_sec = 0;
+    ts[0].tv_nsec = UTIME_NOW;
+    ts[1].tv_sec = 0;
+    ts[1].tv_nsec = UTIME_OMIT;
+    utimensat (0, ctx->path, ts, 0);
+#else
     newtime.actime = time (NULL);
+    newtime.modtime = sb.st_mtime;
     utime (ctx->path, &newtime);
+#endif /* HAVE_UTIMENSAT */
   }
 #endif
 
@@ -238,6 +249,9 @@ int mbox_parse_mailbox (CONTEXT *ctx)
   int count = 0, lines = 0;
   LOFF_T loc;
 #ifdef NFS_ATTRIBUTE_HACK
+#ifdef HAVE_UTIMENSAT
+  struct timespec ts[2];
+#endif /* HAVE_UTIMENSAT */
   struct utimbuf newtime;
 #endif
   progress_t progress;
@@ -251,15 +265,23 @@ int mbox_parse_mailbox (CONTEXT *ctx)
   }
 
   ctx->size = sb.st_size;
-  ctx->mtime = sb.st_mtime;
-  ctx->atime = sb.st_atime;
+  mutt_get_stat_timespec (&ctx->mtime, &sb, MUTT_STAT_MTIME);
+  mutt_get_stat_timespec (&ctx->atime, &sb, MUTT_STAT_ATIME);
 
 #ifdef NFS_ATTRIBUTE_HACK
   if (sb.st_mtime > sb.st_atime)
   {
-    newtime.modtime = sb.st_mtime;
+#ifdef HAVE_UTIMENSAT
+    ts[0].tv_sec = 0;
+    ts[0].tv_nsec = UTIME_NOW;
+    ts[1].tv_sec = 0;
+    ts[1].tv_nsec = UTIME_OMIT;
+    utimensat (0, ctx->path, ts, 0);
+#else
     newtime.actime = time (NULL);
+    newtime.modtime = sb.st_mtime;
     utime (ctx->path, &newtime);
+#endif /* HAVE_UTIMENSAT */
   }
 #endif
 
@@ -666,13 +688,14 @@ static int mbox_check_mailbox (CONTEXT *ctx, int *index_hint)
 
   if (stat (ctx->path, &st) == 0)
   {
-    if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
+    if ((mutt_stat_timespec_compare (&st, MUTT_STAT_MTIME, &ctx->mtime) == 0) &&
+        st.st_size == ctx->size)
       return (0);
 
     if (st.st_size == ctx->size)
     {
       /* the file was touched, but it is still the same length, so just exit */
-      ctx->mtime = st.st_mtime;
+      mutt_get_stat_timespec (&ctx->mtime, &st, MUTT_STAT_MTIME);
       return (0);
     }
 
diff --git a/mh.c b/mh.c
index 4d616e0a9551e39bdbd0d487c5b362fd6ab9ac5a..a48dd832d5449ff1fbe0912aeaa0afbe17f15721 100644 (file)
--- a/mh.c
+++ b/mh.c
@@ -80,7 +80,7 @@ struct mh_sequences
 
 struct mh_data
 {
-  time_t mtime_cur;
+  struct timespec mtime_cur;
   mode_t mh_umask;
 };
 
@@ -244,7 +244,7 @@ static int mh_sequences_changed(BUFFY *b)
 
   if ((snprintf(path, sizeof(path), "%s/.mh_sequences", b->path) < sizeof(path)) &&
       (stat(path, &sb) == 0))
-    return (sb.st_mtime > b->last_visited);
+    return (mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &b->last_visited) > 0);
   return -1;
 }
 
@@ -260,7 +260,7 @@ static int mh_already_notified(BUFFY *b, int msgno)
 
   if ((snprintf(path, sizeof(path), "%s/%d", b->path, msgno) < sizeof(path)) &&
       (stat(path, &sb) == 0))
-    return (sb.st_mtime <= b->last_visited);
+    return (mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &b->last_visited) <= 0);
   return -1;
 }
 
@@ -727,20 +727,20 @@ static void maildir_update_mtime (CONTEXT * ctx)
   {
     snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "cur");
     if (stat (buf, &st) == 0)
-      data->mtime_cur = st.st_mtime;
+      mutt_get_stat_timespec (&data->mtime_cur, &st, MUTT_STAT_MTIME);
     snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "new");
   }
   else
   {
     snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
     if (stat (buf, &st) == 0)
-      data->mtime_cur = st.st_mtime;
+      mutt_get_stat_timespec (&data->mtime_cur, &st, MUTT_STAT_MTIME);
 
     strfcpy (buf, ctx->path, sizeof (buf));
   }
 
   if (stat (buf, &st) == 0)
-    ctx->mtime = st.st_mtime;
+    mutt_get_stat_timespec (&ctx->mtime, &st, MUTT_STAT_MTIME);
 }
 
 /* 
@@ -2115,17 +2115,17 @@ static int maildir_check_mailbox (CONTEXT * ctx, int *index_hint)
     return -1;
 
   /* determine which subdirectories need to be scanned */
-  if (st_new.st_mtime > ctx->mtime)
+  if (mutt_stat_timespec_compare (&st_new, MUTT_STAT_MTIME, &ctx->mtime) > 0)
     changed = 1;
-  if (st_cur.st_mtime > data->mtime_cur)
+  if (mutt_stat_timespec_compare (&st_cur, MUTT_STAT_MTIME, &data->mtime_cur) > 0)
     changed |= 2;
 
   if (!changed)
     return 0;                  /* nothing to do */
 
   /* update the modification times on the mailbox */
-  data->mtime_cur = st_cur.st_mtime;
-  ctx->mtime = st_new.st_mtime;
+  mutt_get_stat_timespec (&data->mtime_cur, &st_cur, MUTT_STAT_MTIME);
+  mutt_get_stat_timespec (&ctx->mtime, &st_new, MUTT_STAT_MTIME);
 
   /* do a fast scan of just the filenames in
    * the subdirectories that have changed.
@@ -2280,14 +2280,15 @@ static int mh_check_mailbox (CONTEXT * ctx, int *index_hint)
   if (i == -1 && stat (buf, &st_cur) == -1)
     modified = 1;
 
-  if (st.st_mtime > ctx->mtime || st_cur.st_mtime > data->mtime_cur)
+  if ((mutt_stat_timespec_compare (&st, MUTT_STAT_MTIME, &ctx->mtime) > 0) ||
+      (mutt_stat_timespec_compare (&st_cur, MUTT_STAT_MTIME, &data->mtime_cur) > 0))
     modified = 1;
 
   if (!modified)
     return 0;
 
-  data->mtime_cur = st_cur.st_mtime;
-  ctx->mtime = st.st_mtime;
+  mutt_get_stat_timespec (&data->mtime_cur, &st_cur, MUTT_STAT_MTIME);
+  mutt_get_stat_timespec (&ctx->mtime, &st, MUTT_STAT_MTIME);
 
   memset (&mhs, 0, sizeof (mhs));
 
diff --git a/mutt.h b/mutt.h
index 431c38d107825d9ed9a34c1c973a6a2b75e0df10..09e381944eddd02367b143df8e2af0756f322bd1 100644 (file)
--- a/mutt.h
+++ b/mutt.h
 # define fgetc fgetc_unlocked
 #endif
 
+#ifndef HAVE_STRUCT_TIMESPEC
+struct timespec
+{
+  time_t tv_sec;
+  long tv_nsec;
+};
+#endif
+
 /* nifty trick I stole from ELM 2.5alpha. */
 #ifdef MAIN_C
 #define WHERE 
@@ -135,6 +143,14 @@ typedef struct
 /* flags for _mutt_system() */
 #define MUTT_DETACH_PROCESS    1       /* detach subprocess from group */
 
+/* flags for mutt_get_stat_timespec */
+typedef enum
+{
+  MUTT_STAT_ATIME,
+  MUTT_STAT_MTIME,
+  MUTT_STAT_CTIME
+} mutt_stat_type;
+
 /* flags for mutt_FormatString() */
 typedef enum
 {
@@ -970,8 +986,8 @@ typedef struct _context
   char *path;
   char *realpath;               /* used for buffy comparison and the sidebar */
   FILE *fp;
-  time_t atime;
-  time_t mtime;
+  struct timespec atime;
+  struct timespec mtime;
   off_t size;
   off_t vsize;
   char *pattern;                /* limit pattern string */
index 39f5754aea281d28e6a585ef020933e0bf6591e4..6835fe4a5f8ffeeaeaa3369d7ae2b88fc2097aea 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -1956,6 +1956,65 @@ void mutt_touch_atime (int f)
 #endif
 }
 
+int mutt_timespec_compare (struct timespec *a, struct timespec *b)
+{
+  if (a->tv_sec < b->tv_sec)
+    return -1;
+  if (a->tv_sec > b->tv_sec)
+    return 1;
+
+  if (a->tv_nsec < b->tv_nsec)
+    return -1;
+  if (a->tv_nsec > b->tv_nsec)
+    return 1;
+  return 0;
+}
+
+void mutt_get_stat_timespec (struct timespec *dest, struct stat *sb, mutt_stat_type type)
+{
+  dest->tv_nsec = 0;
+
+  switch (type)
+  {
+    case MUTT_STAT_ATIME:
+      dest->tv_sec = sb->st_atime;
+#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+      dest->tv_nsec = sb->st_atim.tv_nsec;
+#endif
+      break;
+    case MUTT_STAT_MTIME:
+      dest->tv_sec = sb->st_mtime;
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+      dest->tv_nsec = sb->st_mtim.tv_nsec;
+#endif
+      break;
+    case MUTT_STAT_CTIME:
+      dest->tv_sec = sb->st_ctime;
+#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
+      dest->tv_nsec = sb->st_ctim.tv_nsec;
+#endif
+      break;
+  }
+}
+
+int mutt_stat_timespec_compare (struct stat *sba, mutt_stat_type type, struct timespec *b)
+{
+  struct timespec a;
+
+  mutt_get_stat_timespec (&a, sba, type);
+  return mutt_timespec_compare (&a, b);
+}
+
+int mutt_stat_compare (struct stat *sba, mutt_stat_type sba_type,
+                       struct stat *sbb, mutt_stat_type sbb_type)
+{
+  struct timespec a, b;
+
+  mutt_get_stat_timespec (&a, sba, sba_type);
+  mutt_get_stat_timespec (&b, sbb, sbb_type);
+  return mutt_timespec_compare (&a, &b);
+}
+
 const char *mutt_make_version (void)
 {
   static char vstring[STRING];
diff --git a/mx.c b/mx.c
index a015bf44775540c6fd1cd7bc449dfda5fa31dc10..9d21fb184575e29b519ecc6b6a101f3fcd14fed9 100644 (file)
--- a/mx.c
+++ b/mx.c
@@ -422,7 +422,11 @@ int mx_get_magic (const char *path)
   }
   else if ((f = fopen (path, "r")) != NULL)
   {
+#ifdef HAVE_UTIMENSAT
+    struct timespec ts[2];
+#else
     struct utimbuf times;
+#endif /* HAVE_UTIMENSAT */
     int ch;
 
     /* Some mailbox creation tools erroneously append a blank line to
@@ -452,9 +456,15 @@ int mx_get_magic (const char *path)
        * only the type was accessed.  This is important, because detection
        * of "new mail" depends on those times set correctly.
        */
+#ifdef HAVE_UTIMENSAT
+      mutt_get_stat_timespec (&ts[0], &st, MUTT_STAT_ATIME);
+      mutt_get_stat_timespec (&ts[1], &st, MUTT_STAT_MTIME);
+      utimensat (0, path, ts, 0);
+#else
       times.actime = st.st_atime;
       times.modtime = st.st_mtime;
       utime (path, &times);
+#endif
     }
   }
   else
@@ -661,16 +671,28 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx)
 void mx_fastclose_mailbox (CONTEXT *ctx)
 {
   int i;
-  struct utimbuf ut;
+#ifdef HAVE_UTIMENSAT
+    struct timespec ts[2];
+#else
+    struct utimbuf ut;
+#endif /* HAVE_UTIMENSAT */
 
   if(!ctx) 
     return;
 
   /* fix up the times so buffy won't get confused */
-  if (ctx->peekonly && ctx->path && (ctx->mtime > ctx->atime)) {
-    ut.actime  = ctx->atime;
-    ut.modtime = ctx->mtime;
+  if (ctx->peekonly && ctx->path &&
+      (mutt_timespec_compare (&ctx->mtime, &ctx->atime) > 0))
+  {
+#ifdef HAVE_UTIMENSAT
+    ts[0] = ctx->atime;
+    ts[1] = ctx->mtime;
+    utimensat (0, ctx->path, ts, 0);
+#else
+    ut.actime  = ctx->atime.tv_sec;
+    ut.modtime = ctx->mtime.tv_sec;
     utime (ctx->path, &ut);
+#endif /* HAVE_UTIMENSAT */
   }
 
   /* never announce that a mailbox we've just left has new mail. #3290
index 8bcda67fb0b64999d601ce4de8d9c82fcf77b631..4076d861b6e6da5a7f59940c0494cd68127c9c51 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -123,6 +123,11 @@ time_t mutt_mktime (struct tm *, int);
 time_t mutt_parse_date (const char *, HEADER *);
 int is_from (const char *, char *, size_t, time_t *);
 void mutt_touch_atime (int);
+int mutt_timespec_compare (struct timespec *a, struct timespec *b);
+void mutt_get_stat_timespec (struct timespec *dest, struct stat *sb, mutt_stat_type type);
+int mutt_stat_timespec_compare (struct stat *sba, mutt_stat_type type, struct timespec *b);
+int mutt_stat_compare (struct stat *sba, mutt_stat_type sba_type, struct stat *sbb, mutt_stat_type sbb_type);
+
 
 const char *mutt_attach_fmt (
        char *dest,