From: Paul Eggert Date: Thu, 1 Aug 2024 17:02:06 +0000 (-0700) Subject: Fix unlikely problems with time overflow X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=6c91bd82e1001a72ffd2e7dd3a2ce1668a513247;p=thirdparty%2Ftar.git Fix unlikely problems with time overflow Also, fix some rounding errors while we’re in the neighborhood. * src/buffer.c (duration_ns, compute_duration_ns): Rename from ‘duration’ and ‘compute_duration’, and count ns rather than s, to lessen rounding error. All uses changed. (compute_duration_ns): Work even if the clock moves backward and time_t is unsigned. (print_stats): Don’t worry about null or empty TEXT, as that cannot happen. Compare double to UINTMAX_MAX + 1.0, not to UINTMAX_MAX, so that the comparison is exact. Handle the unlikely case that numbytes >= UINTMAX_MAX. * src/tar.c (parse_opt): Treat -L hugenumber as effectively infinity rather than erroring out. Prefer ckd_add to checking overflow by hand. --- diff --git a/src/buffer.c b/src/buffer.c index 267b43eb..b55a132c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -249,7 +249,8 @@ clear_read_error_count (void) /* Time-related functions */ -static double duration; +/* Time consumed during run. It is counted in ns to lessen rounding error. */ +static double duration_ns; void set_start_time (void) @@ -267,14 +268,18 @@ set_volume_start_time (void) } double -compute_duration (void) +compute_duration_ns (void) { - struct timespec now; - gettime (&now); - duration += ((now.tv_sec - last_stat_time.tv_sec) - + (now.tv_nsec - last_stat_time.tv_nsec) / 1e9); - gettime (&last_stat_time); - return duration; + struct timespec now = current_timespec (); + + /* If the clock moves back, treat it as duration 0. + This works even if time_t is unsigned. */ + if (timespec_cmp (last_stat_time, now) < 0) + duration_ns += (1e9 * (now.tv_sec - last_stat_time.tv_sec) + + (now.tv_nsec - last_stat_time.tv_nsec)); + + last_stat_time = current_timespec (); + return duration_ns; } @@ -491,19 +496,29 @@ static int print_stats (FILE *fp, const char *text, tarlong numbytes) { char abbr[LONGEST_HUMAN_READABLE + 1]; - char rate[LONGEST_HUMAN_READABLE + 1]; - int n = 0; - int human_opts = human_autoscale | human_base_1024 | human_SI | human_B; + double ulim = UINTMAX_MAX + 1.0; + + int n = fprintf (fp, "%s: "TARLONG_FORMAT" (", gettext (text), numbytes); + + if (numbytes < ulim) + n += fprintf (fp, "%s", human_readable (numbytes, abbr, human_opts, 1, 1)); + else + n += fprintf (fp, "%g", numbytes); - if (text && text[0]) - n += fprintf (fp, "%s: ", gettext (text)); - return n + fprintf (fp, TARLONG_FORMAT " (%s, %s/s)", - numbytes, - human_readable (numbytes, abbr, human_opts, 1, 1), - (0 < duration && numbytes / duration < (uintmax_t) -1 - ? human_readable (numbytes / duration, rate, human_opts, 1, 1) - : "?")); + if (!duration_ns) + n += fprintf (fp, ")"); + else + { + double rate = 1e9 * numbytes / duration_ns; + if (rate < ulim) + n += fprintf (fp, ", %s/s)", + human_readable (rate, abbr, human_opts, 1, 1)); + else + n += fprintf (fp, ", %g/s)", rate); + } + + return n; } /* Format totals to file FP. FORMATS is an array of strings to output @@ -1121,7 +1136,7 @@ close_archive (void) while (current_block > record_start); } - compute_duration (); + compute_duration_ns (); if (verify_option) verify_volume (); diff --git a/src/checkpoint.c b/src/checkpoint.c index f1710bb5..8194463e 100644 --- a/src/checkpoint.c +++ b/src/checkpoint.c @@ -291,14 +291,14 @@ format_checkpoint_string (FILE *fp, size_t len, break; case 'd': - len += fprintf (fp, "%.0f", compute_duration ()); + len += fprintf (fp, "%.0f", compute_duration_ns () / BILLION); break; case 'T': { const char **fmt = checkpoint_total_format, *fmtbuf[3]; struct wordsplit ws; - compute_duration (); + compute_duration_ns (); if (arg) { @@ -420,7 +420,7 @@ run_checkpoint_actions (bool do_write) break; case cop_totals: - compute_duration (); + compute_duration_ns (); print_total_stats (); break; diff --git a/src/common.h b/src/common.h index ba4a4f7d..c65e64d6 100644 --- a/src/common.h +++ b/src/common.h @@ -455,7 +455,7 @@ size_t available_space_after (union block *pointer); off_t current_block_ordinal (void); void close_archive (void); void closeout_volume_number (void); -double compute_duration (void); +double compute_duration_ns (void); union block *find_next_block (void); void flush_read (void); void flush_write (void); diff --git a/src/tar.c b/src/tar.c index 8efa3794..d47cdac3 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1083,7 +1083,7 @@ set_use_compress_program_option (const char *string, struct option_locus *loc) static void sigstat (int signo) { - compute_duration (); + compute_duration_ns (); print_total_stats (); #ifndef HAVE_SIGACTION signal (signo, sigstat); @@ -1688,13 +1688,24 @@ parse_opt (int key, char *arg, struct argp_state *state) uintmax_t u; char *p; - if (xstrtoumax (arg, &p, 10, &u, TAR_SIZE_SUFFIXES) != LONGINT_OK) - USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), - _("Invalid tape length"))); - if (p > arg && !strchr (TAR_SIZE_SUFFIXES, p[-1])) - tape_length_option = 1024 * (tarlong) u; - else - tape_length_option = (tarlong) u; + switch (xstrtoumax (arg, &p, 10, &u, TAR_SIZE_SUFFIXES)) + { + case LONGINT_OK: + tape_length_option = u; + if (arg < p && !strchr (TAR_SIZE_SUFFIXES, p[-1])) + tape_length_option *= 1024; + break; + + case LONGINT_OVERFLOW: + /* Treat enormous values as effectively infinity. */ + tape_length_option = 0; + break; + + default: + USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), + _("Invalid tape length"))); + } + multi_volume_option = true; } break; @@ -2102,10 +2113,9 @@ parse_opt (int key, char *arg, struct argp_state *state) uintmax_t u; if (! (xstrtoumax (arg, NULL, 10, &u, TAR_SIZE_SUFFIXES) == LONGINT_OK - && u == (size_t) u)) + && !ckd_add (&record_size, u, 0))) USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), _("Invalid record size"))); - record_size = u; if (record_size % BLOCKSIZE != 0) USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."), BLOCKSIZE)); @@ -2151,10 +2161,9 @@ parse_opt (int key, char *arg, struct argp_state *state) { uintmax_t u; if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK - && u == (size_t) u)) + && !ckd_add (&strip_name_components, u, 0))) USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), _("Invalid number of elements"))); - strip_name_components = u; } break;