+ }
+ else
+ memset (&mtime, 0, sizeof mtime);
+
+ errno = 0;
+ dev = u = strtoumax (strp, &ebuf, 10);
+ if (!isspace (*ebuf))
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid device number")));
+ else if (dev != u)
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Device number out of range")));
+ strp = ebuf;
+
+ errno = 0;
+ ino = u = strtoumax (strp, &ebuf, 10);
+ if (!isspace (*ebuf))
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid inode number")));
+ else if (ino != u)
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Inode number out of range")));
+ strp = ebuf;
+
+ strp++;
+ unquote_string (strp);
+ note_directory (strp, mtime, dev, ino, nfs, false, NULL);
+ }
+ free (buf);
+}
+
+/* Read a nul-terminated string from FP and store it in STK.
+ Store the number of bytes read (including nul terminator) in PCOUNT.
+
+ Return the last character read or EOF on end of file. */
+static int
+read_obstack (FILE *fp, struct obstack *stk, size_t *pcount)
+{
+ int c;
+ size_t i;
+
+ for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
+ obstack_1grow (stk, c);
+ obstack_1grow (stk, 0);
+
+ *pcount = i;
+ return c;
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+ uintmax_t. Return the resulting value in PVAL.
+
+ Throw fatal error if the string cannot be converted.
+
+ Return the last character read or EOF on end of file. */
+
+static int
+read_num (FILE *fp, uintmax_t *pval)
+{
+ int c;
+ size_t i;
+ char buf[UINTMAX_STRSIZE_BOUND], *ep;
+
+ for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
+ {
+ if (i == sizeof buf - 1)
+ FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
+ buf[i] = c;
+ }
+ buf[i] = 0;
+ *pval = strtoumax (buf, &ep, 10);
+ if (*ep)
+ FATAL_ERROR ((0, 0, _("Unexpected field value in snapshot file")));
+ return c;
+}
+
+/* Read incremental snapshot format 2 */
+static void
+read_incr_db_2 ()
+{
+ uintmax_t u;
+ struct obstack stk;
+
+ obstack_init (&stk);
+
+ if (read_num (listed_incremental_stream, &u))
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Error reading time stamp")));
+ newer_mtime_option.tv_sec = u;
+ if (newer_mtime_option.tv_sec != u)
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Time stamp out of range")));
+
+ if (read_num (listed_incremental_stream, &u))
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Error reading time stamp")));
+ newer_mtime_option.tv_nsec = u;
+ if (newer_mtime_option.tv_nsec != u)
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Time stamp out of range")));
+
+ for (;;)
+ {
+ struct timespec mtime;
+ dev_t dev;
+ ino_t ino;
+ bool nfs;
+ char *name;
+ char *content;
+ size_t s;
+
+ if (read_num (listed_incremental_stream, &u))
+ return; /* Normal return */
+
+ nfs = u;
+
+ if (read_num (listed_incremental_stream, &u))
+ break;
+ mtime.tv_sec = u;
+ if (mtime.tv_sec != u)
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Modification time (seconds) out of range")));
+
+ if (read_num (listed_incremental_stream, &u))
+ break;
+ mtime.tv_nsec = u;
+ if (mtime.tv_nsec != u)
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Modification time (nanoseconds) out of range")));
+
+ if (read_num (listed_incremental_stream, &u))
+ break;
+ dev = u;
+ if (dev != u)
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Device number out of range")));
+
+ if (read_num (listed_incremental_stream, &u))
+ break;
+ ino = u;
+ if (ino != u)
+ FATAL_ERROR ((0, 0, "%s: %s",
+ quotearg_colon (listed_incremental_option),
+ _("Inode number out of range")));
+
+ 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: %s",
+ quotearg_colon (listed_incremental_option),
+ _("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")));
+}
+
+/* Read incremental snapshot file (directory file).
+ If the file has older incremental version, make sure that it is processed
+ correctly and that tar will use the most conservative backup method among
+ possible alternatives (i.e. prefer ALL_CHILDREN over CHANGED_CHILDREN,
+ etc.) This ensures that the snapshots are updated to the recent version
+ without any loss of data. */
+void
+read_directory_file (void)
+{
+ int fd;
+ char *buf = 0;
+ size_t bufsize;
+ long lineno = 1;
+
+ /* Open the file for both read and write. That way, we can write
+ it later without having to reopen it, and don't have to worry if
+ we chdir in the meantime. */
+ fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
+ if (fd < 0)
+ {
+ open_error (listed_incremental_option);
+ return;
+ }
+
+ listed_incremental_stream = fdopen (fd, "r+");
+ if (! listed_incremental_stream)
+ {
+ open_error (listed_incremental_option);
+ close (fd);
+ return;
+ }
+
+ if (0 < getline (&buf, &bufsize, listed_incremental_stream))
+ {
+ char *ebuf;
+ int incremental_version;