From: Kevin McCarthy Date: Thu, 14 Jun 2018 08:17:56 +0000 (+0800) Subject: Convert context and buffy to use nanosecond timestamps. X-Git-Tag: mutt-1-11-rel~139 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=120f63a9bea469e9980cf0270a1bb54841051015;p=mutt Convert context and buffy to use nanosecond timestamps. 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. --- diff --git a/buffy.c b/buffy.c index 6db2c1fc..eab17e6e 100644 --- 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 561f5e7f..c0cfddf4 100644 --- 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; diff --git a/configure.ac b/configure.ac index d004fe9c..29252173 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ]]) + +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 ]]) + +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 34668832..171689f0 100644 --- 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 4d616e0a..a48dd832 100644 --- 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 431c38d1..09e38194 100644 --- a/mutt.h +++ b/mutt.h @@ -85,6 +85,14 @@ # 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 */ diff --git a/muttlib.c b/muttlib.c index 39f5754a..6835fe4a 100644 --- 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 a015bf44..9d21fb18 100644 --- 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, ×); +#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 diff --git a/protos.h b/protos.h index 8bcda67f..4076d861 100644 --- 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,