struct timespec dtotimespec (double sec) { if (! (TYPE_MINIMUM (time_t) < sec)) return make_timespec (TYPE_MINIMUM (time_t), 0); else if (! (sec < 1.0 + TYPE_MAXIMUM (time_t))) return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_RESOLUTION - 1); else { time_t s = sec; double frac = TIMESPEC_RESOLUTION * (sec - s); long ns = frac; ns += ns < frac; s += ns / TIMESPEC_RESOLUTION; ns %= TIMESPEC_RESOLUTION; if (ns < 0) { s--; ns += TIMESPEC_RESOLUTION; } return make_timespec (s, ns); } }
struct timespec dtotimespec (double sec) { double min_representable = TYPE_MINIMUM (time_t); double max_representable = ((TYPE_MAXIMUM (time_t) * (double) TIMESPEC_RESOLUTION + (TIMESPEC_RESOLUTION - 1)) / TIMESPEC_RESOLUTION); if (! (min_representable < sec)) return make_timespec (TYPE_MINIMUM (time_t), 0); else if (! (sec < max_representable)) return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_RESOLUTION - 1); else { time_t s = sec; double frac = TIMESPEC_RESOLUTION * (sec - s); long ns = frac; ns += ns < frac; s += ns / TIMESPEC_RESOLUTION; ns %= TIMESPEC_RESOLUTION; if (ns < 0) { s--; ns += TIMESPEC_RESOLUTION; } return make_timespec (s, ns); } }
/* Read incremental snapshot format 2 */ static void read_incr_db_2 (void) { struct obstack stk; char offbuf[INT_BUFSIZE_BOUND (off_t)]; obstack_init (&stk); read_timespec (listed_incremental_stream, &newer_mtime_option); for (;;) { intmax_t i; struct timespec mtime; dev_t dev; ino_t ino; bool nfs; char *name; char *content; size_t s; if (! read_num (listed_incremental_stream, "nfs", 0, 1, &i)) return; /* Normal return */ nfs = i; read_timespec (listed_incremental_stream, &mtime); if (! read_num (listed_incremental_stream, "dev", TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t), &i)) break; dev = i; if (! read_num (listed_incremental_stream, "ino", TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t), &i)) break; ino = i; if (read_obstack (listed_incremental_stream, &stk, &s)) break; name = obstack_finish (&stk); while (read_obstack (listed_incremental_stream, &stk, &s) == 0 && s > 1) ; if (getc (listed_incremental_stream) != 0) FATAL_ERROR ((0, 0, _("%s: byte %s: %s"), quotearg_colon (listed_incremental_option), offtostr (ftello (listed_incremental_stream), offbuf), _("Missing record terminator"))); content = obstack_finish (&stk); note_directory (name, mtime, dev, ino, nfs, false, content); obstack_free (&stk, content); } FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); }
static void read_timespec (FILE *fp, struct timespec *pval) { int c = getc (fp); intmax_t i; uintmax_t u; if (c == '-') { read_negative_num (fp, TYPE_MINIMUM (time_t), &i); c = 0; pval->tv_sec = i; } else { c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u); pval->tv_sec = u; } if (c || read_num (fp, BILLION - 1, &u)) FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); pval->tv_nsec = u; }
/* Initialize directory data. */ static inline void duinfo_init (struct duinfo *a) { a->size = 0; a->tmax.tv_sec = TYPE_MINIMUM (time_t); a->tmax.tv_nsec = -1; }
struct timespec timespec_sub (struct timespec a, struct timespec b) { struct timespec r; time_t rs = a.tv_sec; time_t bs = b.tv_sec; int ns = a.tv_nsec - b.tv_nsec; int rns = ns; if (ns < 0) { rns = ns + 1000000000; if (rs == TYPE_MINIMUM (time_t)) { if (bs <= 0) goto low_overflow; bs--; } else rs--; } if (INT_SUBTRACT_OVERFLOW (rs, bs)) { if (rs < 0) { low_overflow: rs = TYPE_MINIMUM (time_t); rns = 0; } else { rs = TYPE_MAXIMUM (time_t); rns = 999999999; } } else rs -= bs; r.tv_sec = rs; r.tv_nsec = rns; return r; }
/* Output incremental data for the directory ENTRY to the file DATA. Return nonzero if successful, preserving errno on write failure. */ static bool write_directory_file_entry (void *entry, void *data) { struct directory const *directory = entry; FILE *fp = data; if (DIR_IS_FOUND (directory)) { char buf[SYSINT_BUFSIZE]; char const *s; s = DIR_IS_NFS (directory) ? "1" : "0"; fwrite (s, 2, 1, fp); s = sysinttostr (directory->mtime.tv_sec, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), buf); fwrite (s, strlen (s) + 1, 1, fp); s = imaxtostr (directory->mtime.tv_nsec, buf); fwrite (s, strlen (s) + 1, 1, fp); s = sysinttostr (directory->device_number, TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t), buf); fwrite (s, strlen (s) + 1, 1, fp); s = sysinttostr (directory->inode_number, TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t), buf); fwrite (s, strlen (s) + 1, 1, fp); fwrite (directory->name, strlen (directory->name) + 1, 1, fp); if (directory->dump) { const char *p; struct dumpdir_iter *itr; for (p = dumpdir_first (directory->dump, 0, &itr); p; p = dumpdir_next (itr)) fwrite (p, strlen (p) + 1, 1, fp); free (itr); } fwrite ("\0\0", 2, 1, fp); } return ! ferror (fp); }
struct timespec timespec_sub (struct timespec a, struct timespec b) { time_t rs = a.tv_sec; time_t bs = b.tv_sec; int ns = a.tv_nsec - b.tv_nsec; int rns = ns; if (ns < 0) { rns = ns + TIMESPEC_RESOLUTION; if (rs == TYPE_MINIMUM (time_t)) { if (bs <= 0) goto low_overflow; bs--; } else rs--; } if (INT_SUBTRACT_OVERFLOW (rs, bs)) { if (rs < 0) { low_overflow: rs = TYPE_MINIMUM (time_t); rns = 0; } else { rs = TYPE_MAXIMUM (time_t); rns = TIMESPEC_RESOLUTION - 1; } } else rs -= bs; return make_timespec (rs, rns); }
static bool decode_time (struct timespec *ts, char const *arg, char const *keyword) { switch (_decode_time (ts, arg, keyword)) { case decode_time_success: return true; case decode_time_bad_header: ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), keyword, arg)); return false; case decode_time_range: out_of_range_header (keyword, arg, - (uintmax_t) TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t)); return false; } return true; }
static void read_timespec (FILE *fp, struct timespec *pval) { intmax_t s, ns; if (read_num (fp, "sec", TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), &s) && read_num (fp, "nsec", 0, BILLION - 1, &ns)) { pval->tv_sec = s; pval->tv_nsec = ns; } else { FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); } }
struct timespec timespec_add (struct timespec a, struct timespec b) { time_t rs = a.tv_sec; time_t bs = b.tv_sec; int ns = a.tv_nsec + b.tv_nsec; int nsd = ns - TIMESPEC_RESOLUTION; int rns = ns; time_t tmin = TYPE_MINIMUM (time_t); time_t tmax = TYPE_MAXIMUM (time_t); if (0 <= nsd) { rns = nsd; if (bs < tmax) bs++; else if (rs < 0) rs++; else goto high_overflow; } /* INT_ADD_WRAPV is not appropriate since time_t might be unsigned. In theory time_t might be narrower than int, so plain INT_ADD_OVERFLOW does not suffice. */ if (! INT_ADD_OVERFLOW (rs, bs) && tmin <= rs + bs && rs + bs <= tmax) rs += bs; else { if (rs < 0) { rs = tmin; rns = 0; } else { high_overflow: rs = tmax; rns = TIMESPEC_RESOLUTION - 1; } } return make_timespec (rs, rns); }
static bool decode_time (struct timespec *ts, char const *arg, char const *keyword) { char *arg_lim; struct timespec t = decode_timespec (arg, &arg_lim, true); if (! valid_timespec (t)) { if (arg < arg_lim && !*arg_lim) out_of_range_header (keyword, arg, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t)); else ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), keyword, arg)); return false; } *ts = t; return true; }
struct timespec timespec_add (struct timespec a, struct timespec b) { time_t rs = a.tv_sec; time_t bs = b.tv_sec; int ns = a.tv_nsec + b.tv_nsec; int nsd = ns - TIMESPEC_RESOLUTION; int rns = ns; if (0 <= nsd) { rns = nsd; if (rs == TYPE_MAXIMUM (time_t)) { if (0 <= bs) goto high_overflow; bs++; } else rs++; } if (INT_ADD_OVERFLOW (rs, bs)) { if (rs < 0) { rs = TYPE_MINIMUM (time_t); rns = 0; } else { high_overflow: rs = TYPE_MAXIMUM (time_t); rns = TIMESPEC_RESOLUTION - 1; } } else rs += bs; return make_timespec (rs, rns); }
static void print_context_label (char const *mark, struct file_data *inf, char const *name, char const *label) { if (label) fprintf (outfile, "%s %s\n", mark, label); else { char buf[MAX (INT_STRLEN_BOUND (int) + 32, INT_STRLEN_BOUND (time_t) + 11)]; struct tm const *tm = localtime (&inf->stat.st_mtime); int nsec = get_stat_mtime_ns (&inf->stat); if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec))) { verify (TYPE_IS_INTEGER (time_t)); if (LONG_MIN <= TYPE_MINIMUM (time_t) && TYPE_MAXIMUM (time_t) <= LONG_MAX) { long int sec = inf->stat.st_mtime; sprintf (buf, "%ld.%.9d", sec, nsec); } else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX) { intmax_t sec = inf->stat.st_mtime; sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec); } else { uintmax_t sec = inf->stat.st_mtime; sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec); } } fprintf (outfile, "%s %s\t%s\n", mark, name, buf); } }
with any changes made to the read_num() calls in the parsing loop inside read_incr_db_2(). (This function is invoked via the --show-snapshot-field-ranges command line option.) */ struct field_range { char const *fieldname; intmax_t min_val; uintmax_t max_val; }; static struct field_range const field_ranges[] = { { "nfs", 0, 1 }, { "timestamp_sec", TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t) }, { "timestamp_nsec", 0, BILLION - 1 }, { "dev", TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t) }, { "ino", TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t) }, { NULL, 0, 0 } }; void show_snapshot_field_ranges (void) { struct field_range const *p; char minbuf[SYSINT_BUFSIZE]; char maxbuf[SYSINT_BUFSIZE]; printf("This tar's snapshot file field ranges are\n"); printf (" (%-15s => [ %s, %s ]):\n\n", "field name", "min", "max");
#if __GNUC__ >= 2 && DO_PEDANTIC # define verify_same_types(expr1,expr2) \ extern void _verify_func(__LINE__) (__typeof__ (expr1) *); \ extern void _verify_func(__LINE__) (__typeof__ (expr2) *); # define _verify_func(line) _verify_func2(line) # define _verify_func2(line) verify_func_ ## line #else # define verify_same_types(expr1,expr2) extern void verify_func (int) #endif /* 7.18.1.1. Exact-width integer types */ /* 7.18.2.1. Limits of exact-width integer types */ int8_t a1[3] = { INT8_C (17), INT8_MIN, INT8_MAX }; verify (TYPE_MINIMUM (int8_t) == INT8_MIN); verify (TYPE_MAXIMUM (int8_t) == INT8_MAX); verify_same_types (INT8_MIN, (int8_t) 0 + 0); verify_same_types (INT8_MAX, (int8_t) 0 + 0); int16_t a2[3] = { INT16_C (17), INT16_MIN, INT16_MAX }; verify (TYPE_MINIMUM (int16_t) == INT16_MIN); verify (TYPE_MAXIMUM (int16_t) == INT16_MAX); verify_same_types (INT16_MIN, (int16_t) 0 + 0); verify_same_types (INT16_MAX, (int16_t) 0 + 0); int32_t a3[3] = { INT32_C (17), INT32_MIN, INT32_MAX }; verify (TYPE_MINIMUM (int32_t) == INT32_MIN); verify (TYPE_MAXIMUM (int32_t) == INT32_MAX); verify_same_types (INT32_MIN, (int32_t) 0 + 0); verify_same_types (INT32_MAX, (int32_t) 0 + 0);
/* Read incremental snapshot formats 0 and 1 */ static void read_incr_db_01 (int version, const char *initbuf) { int n; uintmax_t u; char *buf = NULL; size_t bufsize = 0; char *ebuf; long lineno = 1; if (version == 1) { if (getline (&buf, &bufsize, listed_incremental_stream) <= 0) { read_error (listed_incremental_option); free (buf); return; } ++lineno; } else { buf = strdup (initbuf); bufsize = strlen (buf) + 1; } newer_mtime_option = decode_timespec (buf, &ebuf, false); if (! valid_timespec (newer_mtime_option)) ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); else { if (version == 1 && *ebuf) { char const *buf_ns = ebuf + 1; errno = 0; u = strtoumax (buf_ns, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || buf_ns == ebuf) { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t); newer_mtime_option.tv_nsec = -1; } else newer_mtime_option.tv_nsec = u; } } while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream))) { dev_t dev; ino_t ino; bool nfs = buf[0] == '+'; char *strp = buf + nfs; struct timespec mtime; lineno++; if (buf[n - 1] == '\n') buf[n - 1] = '\0'; if (version == 1) { mtime = decode_timespec (strp, &ebuf, false); strp = ebuf; if (!valid_timespec (mtime) || *strp != ' ') ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time"))); errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time (nanoseconds)"))); mtime.tv_nsec = -1; } else mtime.tv_nsec = u; strp = ebuf; } else mtime.tv_sec = mtime.tv_nsec = 0; dev = strtosysint (strp, &ebuf, TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t)); strp = ebuf; if (errno || *strp != ' ') ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid device number"))); ino = strtosysint (strp, &ebuf, TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t)); strp = ebuf; if (errno || *strp != ' ') ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid inode number"))); strp++; unquote_string (strp); note_directory (strp, mtime, dev, ino, nfs, false, NULL); } free (buf); }
struct timespec decode_timespec (char const *arg, char **arg_lim, bool parse_fraction) { time_t s = TYPE_MINIMUM (time_t); int ns = -1; char const *p = arg; bool negative = *arg == '-'; struct timespec r; if (! ISDIGIT (arg[negative])) errno = EINVAL; else { errno = 0; if (negative) { intmax_t i = strtoimax (arg, arg_lim, 10); if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i) s = i; else errno = ERANGE; } else { uintmax_t i = strtoumax (arg, arg_lim, 10); if (i <= TYPE_MAXIMUM (time_t)) s = i; else errno = ERANGE; } p = *arg_lim; ns = 0; if (parse_fraction && *p == '.') { int digits = 0; bool trailing_nonzero = false; while (ISDIGIT (*++p)) if (digits < LOG10_BILLION) digits++, ns = 10 * ns + (*p - '0'); else trailing_nonzero |= *p != '0'; while (digits < LOG10_BILLION) digits++, ns *= 10; if (negative) { /* Convert "-1.10000000000001" to s == -2, ns == 89999999. I.e., truncate time stamps towards minus infinity while converting them to internal form. */ ns += trailing_nonzero; if (ns != 0) { if (s == TYPE_MINIMUM (time_t)) ns = -1; else { s--; ns = BILLION - ns; } } } } if (errno == ERANGE) ns = -1; } *arg_lim = (char *) p; r.tv_sec = s; r.tv_nsec = ns; return r; }
/* Read incremental snapshot formats 0 and 1 */ static void read_incr_db_01 (int version, const char *initbuf) { int n; uintmax_t u; time_t sec; long int nsec; char *buf = NULL; size_t bufsize = 0; char *ebuf; long lineno = 1; if (version == 1) { if (getline (&buf, &bufsize, listed_incremental_stream) <= 0) { read_error (listed_incremental_option); free (buf); return; } ++lineno; } else { buf = strdup (initbuf); bufsize = strlen (buf) + 1; } sec = TYPE_MINIMUM (time_t); nsec = -1; errno = 0; u = strtoumax (buf, &ebuf, 10); if (!errno && TYPE_MAXIMUM (time_t) < u) errno = ERANGE; if (errno || buf == ebuf) ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); else { sec = u; if (version == 1 && *ebuf) { char const *buf_ns = ebuf + 1; errno = 0; u = strtoumax (buf_ns, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || buf_ns == ebuf) { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); sec = TYPE_MINIMUM (time_t); } else nsec = u; } else { /* pre-1 incremental format does not contain nanoseconds */ nsec = 0; } } newer_mtime_option.tv_sec = sec; newer_mtime_option.tv_nsec = nsec; while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream))) { dev_t dev; ino_t ino; bool nfs = buf[0] == '+'; char *strp = buf + nfs; struct timespec mtime; lineno++; if (buf[n - 1] == '\n') buf[n - 1] = '\0'; if (version == 1) { errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && TYPE_MAXIMUM (time_t) < u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time (seconds)"))); sec = (time_t) -1; } else sec = u; strp = ebuf; errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time (nanoseconds)"))); nsec = -1; } else nsec = u; mtime.tv_sec = sec; mtime.tv_nsec = nsec; strp = ebuf; } else memset (&mtime, 0, sizeof mtime); errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && TYPE_MAXIMUM (dev_t) < u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid device number"))); dev = (dev_t) -1; } else dev = u; strp = ebuf; errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && TYPE_MAXIMUM (ino_t) < u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid inode number"))); ino = (ino_t) -1; } else ino = u; strp = ebuf; strp++; unquote_string (strp); note_directory (strp, mtime, dev, ino, nfs, false, NULL); } free (buf); }
static enum decode_time_status _decode_time (struct timespec *ts, char const *arg, char const *keyword) { time_t s; unsigned long int ns = 0; char *p; char *arg_lim; bool negative = *arg == '-'; errno = 0; if (ISDIGIT (arg[negative])) { if (negative) { intmax_t i = strtoimax (arg, &arg_lim, 10); if (TYPE_SIGNED (time_t) ? i < TYPE_MINIMUM (time_t) : i < 0) return decode_time_range; s = i; } else { uintmax_t i = strtoumax (arg, &arg_lim, 10); if (TYPE_MAXIMUM (time_t) < i) return decode_time_range; s = i; } p = arg_lim; if (errno == ERANGE) return decode_time_range; if (*p == '.') { int digits = 0; bool trailing_nonzero = false; while (ISDIGIT (*++p)) if (digits < LOG10_BILLION) { ns = 10 * ns + (*p - '0'); digits++; } else trailing_nonzero |= *p != '0'; while (digits++ < LOG10_BILLION) ns *= 10; if (negative) { /* Convert "-1.10000000000001" to s == -2, ns == 89999999. I.e., truncate time stamps towards minus infinity while converting them to internal form. */ ns += trailing_nonzero; if (ns != 0) { if (s == TYPE_MINIMUM (time_t)) return decode_time_range; s--; ns = BILLION - ns; } } } if (! *p) { ts->tv_sec = s; ts->tv_nsec = ns; return decode_time_success; } } return decode_time_bad_header; }