/* GNU dump extensions to tar.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
#define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
#define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
#define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
-
+
#define DIR_SET_FLAG(d,f) (d)->flags |= (f)
#define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
directory->name[namelen-1] = 0;
return directory;
}
-
+
/* Create and link a new directory entry for directory NAME, having a
device number DEV and an inode number INO, with NFS indicating
whether it is an NFS device and FOUND indicating whether we have
}
else
directory->contents = NULL;
-
+
if (! ((directory_table
|| (directory_table = hash_initialize (0, 0,
hash_directory_name,
update_parent_directory (const char *name)
{
struct directory *directory;
- char *p, *name_buffer;
+ char *p;
p = dir_name (name);
directory = find_directory (p);
{
struct directory *directory;
bool nfs = NFS_FILE_STAT (*stat_data);
- struct name *np;
if ((directory = find_directory (name_buffer)) != NULL)
{
if (DIR_IS_INITED (directory))
return directory;
-
+
/* With NFS, the same file can have two different devices
if an NFS directory is mounted in multiple locations,
which is relatively common when automounting.
}
else
directory->children = CHANGED_CHILDREN;
-
+
DIR_SET_FLAG (directory, DIRF_FOUND);
}
else
if (verbose)
WARN ((0, 0, _("%s: Directory is new"),
quotearg_colon (name_buffer)));
- directory->children =
+ directory->children =
(listed_incremental_option
|| (OLDER_STAT_TIME (*stat_data, m)
|| (after_date_option
omit it... */
if (one_file_system_option && device != stat_data->st_dev
/* ... except if it was explicitely given in the command line */
- && !((np = name_scan (name_buffer, true)) && np->explicit))
+ && !is_individual_file (name_buffer))
directory->children = NO_CHILDREN;
- else if (children == ALL_CHILDREN)
+ else if (children == ALL_CHILDREN)
directory->children = ALL_CHILDREN;
DIR_SET_FLAG (directory, DIRF_INIT);
-
+
return directory;
}
if (dump)
while (*dump)
{
- /* Ignore 'R' (rename) entries, since they break alphabetical ordering.
+ /* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
+ alphabetical ordering.
They normally do not occur in dumpdirs from the snapshot files,
but this function is also used by purge_directory, which operates
on a dumpdir from the archive, hence the need for this test. */
- if (*dump != 'R')
+ if (!strchr ("RX", *dump))
{
int rc = strcmp (dump + 1, name);
if (rc == 0)
static int
compare_dirnames (const void *first, const void *second)
{
- return strcmp (*(const char**)first, *(const char**)second);
+ char const *const *name1 = first;
+ char const *const *name2 = second;
+ return strcmp (*name1, *name2);
}
/* Compare dumpdir array from DIRECTORY with directory listing DIR and
DIR must be returned by a previous call to savedir().
File names in DIRECTORY->contents must be sorted
- alphabetically.
+ alphabetically.
DIRECTORY->contents is replaced with the created template. Each entry is
prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
size_t name_length; /* used length in name_buffer */
struct stat stat_data;
struct directory *directory;
-
+
if (! dirp)
savedir_error (dir_name);
if (deref_stat (dereference_option, name_buffer, &stat_data))
{
stat_diag (name_buffer);
- /* FIXME: used to be
+ /* FIXME: used to be
children = CHANGED_CHILDREN;
but changed to: */
free (name_buffer);
}
directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
-
+
if (dirp && directory->children != NO_CHILDREN)
{
char *entry; /* directory entry being scanned */
name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
}
strcpy (name_buffer + name_length, entry + 1);
-
+
if (excluded_name (name_buffer))
*entry = 'N';
else
*entry = 'N';
continue;
}
-
+
if (S_ISDIR (stat_data.st_mode))
{
procdir (name_buffer, &stat_data, device,
verbose_option);
*entry = 'D';
}
-
+
else if (one_file_system_option && device != stat_data.st_dev)
*entry = 'N';
else if (*entry == 'Y')
/* New entry, skip further checks */;
-
+
/* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
-
+
else if (OLDER_STAT_TIME (stat_data, m)
&& (!after_date_option
|| OLDER_STAT_TIME (stat_data, c)))
}
}
}
-
+
free (name_buffer);
if (dirp)
free (dirp);
}
\f
-static bool
-try_pos (char *name, int pos, const char *dumpdir)
-{
- int i;
- static char namechars[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
- if (pos > 0)
- for (i = 0; i < sizeof namechars; i++)
- {
- name[pos] = namechars[i];
- if (!dumpdir_locate (dumpdir, name)
- || try_pos (name, pos-1, dumpdir))
- return true;
- }
-
- return false;
-}
-
-static bool
-create_temp_name (char *name, const char *dumpdir)
-{
- size_t pos = strlen (name) - 6;
- return try_pos (name + pos, 5, dumpdir);
-}
-
-char *
-make_tmp_dir_name (const char *name)
-{
- char *dirname = dir_name (name);
- char *tmp_name = NULL;
- struct directory *dir = find_directory (dirname);
-
- tmp_name = new_name (dirname, "000000");
- if (!create_temp_name (tmp_name, dir ? dir->contents : NULL))
- {
- free (tmp_name);
- tmp_name = NULL;
- }
- free (dirname);
- return tmp_name;
-}
-
static void
obstack_code_rename (struct obstack *stk, char *from, char *to)
{
{
struct directory *dir = data;
struct obstack *stk = proc_data;
-
+
if (DIR_IS_RENAMED (dir))
{
struct directory *prev, *p;
- /* Detect eventual cycles and clear DIRF_RENAMED flag, so this entries
+ /* Detect eventual cycles and clear DIRF_RENAMED flag, so these entries
are ignored when hit by this function next time.
If the chain forms a cycle, prev points to the entry DIR is renamed
from. In this case it still retains DIRF_RENAMED flag, which will be
/* Break the cycle by using a temporary name for one of its
elements.
- FIXME: Leave the choice of the name to the extractor. */
- temp_name = make_tmp_dir_name (dir->name);
- obstack_code_rename (stk, dir->name, temp_name);
+ First, create a temp name stub entry. */
+ temp_name = dir_name (dir->name);
+ obstack_1grow (stk, 'X');
+ obstack_grow (stk, temp_name, strlen (temp_name) + 1);
+
+ obstack_code_rename (stk, dir->name, "");
for (p = dir; p != prev; p = p->orig)
obstack_code_rename (stk, p->orig->name, p->name);
- obstack_code_rename (stk, temp_name, prev->name);
+ obstack_code_rename (stk, "", prev->name);
}
}
return true;
if (directory_table == NULL)
return dump;
-
+
obstack_init (&stk);
if (dump)
{
}
else
size = 0;
-
+
hash_do_for_each (directory_table, rename_handler, &stk);
if (obstack_object_size (&stk) != size)
{
{
int n;
uintmax_t u;
- time_t t = u;
+ time_t sec;
+ long int nsec;
char *buf = 0;
size_t bufsize;
char *ebuf;
buf = strdup (initbuf);
bufsize = strlen (buf) + 1;
}
-
- t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
- if (buf == ebuf || (u == 0 && errno == EINVAL))
- ERROR ((0, 0, "%s:%ld: %s",
+
+ 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 if (t != u)
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option),
- lineno,
- _("Time stamp out of range")));
- else if (version == 1)
- {
- newer_mtime_option.tv_sec = t;
-
- t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
- if (buf == ebuf || (u == 0 && errno == EINVAL))
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option),
- lineno,
- _("Invalid time stamp")));
- else if (t != u)
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option),
- lineno,
- _("Time stamp out of range")));
- newer_mtime_option.tv_nsec = t;
- }
else
{
- /* pre-1 incremental format does not contain nanoseconds */
- newer_mtime_option.tv_sec = t;
- newer_mtime_option.tv_nsec = 0;
+ 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)))
{
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;
- mtime.tv_sec = u = strtoumax (strp, &ebuf, 10);
- if (!isspace (*ebuf))
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Invalid modification time (seconds)")));
- else if (mtime.tv_sec != u)
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Modification time (seconds) out of range")));
+ 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;
- mtime.tv_nsec = u = strtoumax (strp, &ebuf, 10);
- if (!isspace (*ebuf))
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Invalid modification time (nanoseconds)")));
- else if (mtime.tv_nsec != u)
- ERROR ((0, 0, "%s:%ld: %s",
- quotearg_colon (listed_incremental_option), lineno,
- _("Modification time (nanoseconds) out of range")));
+ 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;
- 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")));
+ 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;
- 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")));
+ 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);
}
/* Read from file FP a nul-terminated string and convert it to
- uintmax_t. Return the resulting value in PVAL.
+ intmax_t. Return the resulting value in PVAL. Assume '-' has
+ already been read.
+
+ Throw a fatal error if the string cannot be converted or if the
+ converted value is less than MIN_VAL. */
+
+static void
+read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
+{
+ int c;
+ size_t i;
+ char buf[INT_BUFSIZE_BOUND (intmax_t)];
+ char *ep;
+ buf[0] = '-';
+
+ for (i = 1; ISDIGIT (c = getc (fp)); i++)
+ {
+ if (i == sizeof buf - 1)
+ FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
+ buf[i] = c;
+ }
+
+ if (c < 0)
+ {
+ if (ferror (fp))
+ FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+ else
+ FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+ }
+
+ buf[i] = 0;
+ errno = 0;
+ *pval = strtoimax (buf, &ep, 10);
+ if (c || errno || *pval < min_val)
+ FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+ uintmax_t. Return the resulting value in PVAL. Assume C has
+ already been read.
+
+ Throw a fatal error if the string cannot be converted or if the
+ converted value exceeds MAX_VAL.
- 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)
+read_unsigned_num (int c, FILE *fp, uintmax_t max_val, 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++)
+ for (i = 0; ISDIGIT (c); i++)
{
if (i == sizeof buf - 1)
FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
buf[i] = c;
+ c = getc (fp);
+ }
+
+ if (c < 0)
+ {
+ if (ferror (fp))
+ FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+ else if (i == 0)
+ return c;
+ else
+ FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
}
+
buf[i] = 0;
+ errno = 0;
*pval = strtoumax (buf, &ep, 10);
- if (*ep)
- FATAL_ERROR ((0, 0, _("Unexpected field value in snapshot file")));
+ if (c || errno || max_val < *pval)
+ FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
return c;
-}
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+ uintmax_t. Return the resulting value in PVAL.
+
+ Throw a fatal error if the string cannot be converted or if the
+ converted value exceeds MAX_VAL.
+
+ Return the last character read or EOF on end of file. */
+
+static int
+read_num (FILE *fp, uintmax_t max_val, uintmax_t *pval)
+{
+ return read_unsigned_num (getc (fp), fp, max_val, pval);
+}
+
+/* Read from FP two NUL-terminated strings representing a struct
+ timespec. Return the resulting value in PVAL.
+
+ Throw a fatal error if the string cannot be converted. */
+
+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;
+}
/* Read incremental snapshot format 2 */
static void
{
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")));
+ 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_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")));
+ read_timespec (listed_incremental_stream, &newer_mtime_option);
for (;;)
{
char *name;
char *content;
size_t s;
-
- if (read_num (listed_incremental_stream, &u))
+
+ if (read_num (listed_incremental_stream, 1, &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))
+ read_timespec (listed_incremental_stream, &mtime);
+
+ if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &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))
+ if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &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;
;
if (getc (listed_incremental_stream) != 0)
FATAL_ERROR ((0, 0, "%s: %s",
- quotearg_colon (listed_incremental_option),
+ 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")));
+ quotearg_colon (listed_incremental_option),
+ _("Unexpected EOF in snapshot file")));
}
/* Read incremental snapshot file (directory file).
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
if (0 < getline (&buf, &bufsize, listed_incremental_stream))
{
char *ebuf;
- int incremental_version;
+ uintmax_t incremental_version;
if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
{
if (!*ebuf)
ERROR((1, 0, _("Bad incremental file format")));
- incremental_version = (errno = 0, strtoumax (ebuf+1, &ebuf, 10));
+ incremental_version = strtoumax (ebuf + 1, NULL, 10);
}
else
incremental_version = 0;
case TAR_INCREMENTAL_VERSION:
read_incr_db_2 ();
break;
-
+
default:
- ERROR ((1, 0, _("Unsupported incremental format version: %d"),
+ ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
incremental_version));
}
-
+
}
if (ferror (listed_incremental_stream))
s = DIR_IS_NFS (directory) ? "1" : "0";
fwrite (s, 2, 1, fp);
- s = umaxtostr (directory->mtime.tv_sec, buf);
+ s = (TYPE_SIGNED (time_t)
+ ? imaxtostr (directory->mtime.tv_sec, buf)
+ : umaxtostr (directory->mtime.tv_sec, buf));
fwrite (s, strlen (s) + 1, 1, fp);
s = umaxtostr (directory->mtime.tv_nsec, buf);
fwrite (s, strlen (s) + 1, 1, fp);
fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
TAR_INCREMENTAL_VERSION);
- s = umaxtostr (start_time.tv_sec, buf);
+ s = (TYPE_SIGNED (time_t)
+ ? imaxtostr (start_time.tv_sec, buf)
+ : umaxtostr (start_time.tv_sec, buf));
fwrite (s, strlen (s) + 1, 1, fp);
s = umaxtostr (start_time.tv_nsec, buf);
fwrite (s, strlen (s) + 1, 1, fp);
return stat_info->is_dumpdir;
}
+static bool
+dumpdir_ok (char *dumpdir)
+{
+ char *p;
+ int has_tempdir = 0;
+ int expect = 0;
+
+ for (p = dumpdir; *p; p += strlen (p) + 1)
+ {
+ if (expect && *p != expect)
+ {
+ ERROR ((0, 0,
+ _("Malformed dumpdir: expected '%c' but found %#3o"),
+ expect, *p));
+ return false;
+ }
+ switch (*p)
+ {
+ case 'X':
+ if (has_tempdir)
+ {
+ ERROR ((0, 0,
+ _("Malformed dumpdir: 'X' duplicated")));
+ return false;
+ }
+ else
+ has_tempdir = 1;
+ break;
+
+ case 'R':
+ if (p[1] == 0)
+ {
+ if (!has_tempdir)
+ {
+ ERROR ((0, 0,
+ _("Malformed dumpdir: empty name in 'R'")));
+ return false;
+ }
+ else
+ has_tempdir = 0;
+ }
+ expect = 'T';
+ break;
+
+ case 'T':
+ if (expect != 'T')
+ {
+ ERROR ((0, 0,
+ _("Malformed dumpdir: 'T' not preceeded by 'R'")));
+ return false;
+ }
+ if (p[1] == 0 && !has_tempdir)
+ {
+ ERROR ((0, 0,
+ _("Malformed dumpdir: empty name in 'T'")));
+ return false;
+ }
+ expect = 0;
+ break;
+
+ case 'N':
+ case 'Y':
+ case 'D':
+ break;
+
+ default:
+ /* FIXME: bail out? */
+ break;
+ }
+ }
+
+ if (expect)
+ {
+ ERROR ((0, 0,
+ _("Malformed dumpdir: expected '%c' but found end of data"),
+ expect));
+ return false;
+ }
+
+ if (has_tempdir)
+ WARN ((0, 0, _("Malformed dumpdir: 'X' never used")));
+
+ return true;
+}
+
/* Examine the directories under directory_name and delete any
files that were not there at the time of the back-up. */
-void
-purge_directory (char const *directory_name)
+static bool
+try_purge_directory (char const *directory_name)
{
char *current_dir;
char *cur, *arc, *p;
+ char *temp_stub = NULL;
if (!is_dumpdir (¤t_stat_info))
- {
- skip_member ();
- return;
- }
+ return false;
current_dir = savedir (directory_name);
if (!current_dir)
- {
- /* The directory doesn't exist now. It'll be created. In any
- case, we don't have to delete any files out of it. */
+ /* The directory doesn't exist now. It'll be created. In any
+ case, we don't have to delete any files out of it. */
+ return false;
- skip_member ();
- return;
- }
+ /* Verify if dump directory is sane */
+ if (!dumpdir_ok (current_stat_info.dumpdir))
+ return false;
/* Process renames */
for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
{
- if (*arc == 'R')
+ if (*arc == 'X')
+ {
+#define TEMP_DIR_TEMPLATE "tar.XXXXXX"
+ size_t len = strlen (arc + 1);
+ temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE);
+ memcpy (temp_stub, arc + 1, len);
+ temp_stub[len] = '/';
+ memcpy (temp_stub + len + 1, TEMP_DIR_TEMPLATE,
+ sizeof TEMP_DIR_TEMPLATE);
+ if (!mkdtemp (temp_stub))
+ {
+ ERROR ((0, errno,
+ _("Cannot create temporary directory using template %s"),
+ quote (temp_stub)));
+ free (temp_stub);
+ free (current_dir);
+ return false;
+ }
+ }
+ else if (*arc == 'R')
{
char *src, *dst;
src = arc + 1;
arc += strlen (arc) + 1;
dst = arc + 1;
+ if (*src == 0)
+ src = temp_stub;
+ else if (*dst == 0)
+ dst = temp_stub;
+
if (!rename_directory (src, dst))
{
+ free (temp_stub);
free (current_dir);
/* FIXME: Make sure purge_directory(dst) will return
immediately */
- return;
+ return false;
}
}
}
-
+
+ free (temp_stub);
+
/* Process deletes */
p = NULL;
for (cur = current_dir; *cur; cur += strlen (cur) + 1)
free (p);
p = new_name (directory_name, cur);
- if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
- || (*entry == 'D' && S_ISDIR (st.st_mode))
- || (*entry == 'Y' && !S_ISDIR (st.st_mode)))
+ if (deref_stat (false, p, &st))
{
- if (deref_stat (false, p, &st))
+ if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
+ dirs and check it here? */
{
- if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
- dirs and check it here? */
- {
- stat_diag (p);
- WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
- quotearg_colon (p)));
- }
- continue;
+ stat_diag (p);
+ WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
+ quotearg_colon (p)));
}
- else if (one_file_system_option && st.st_dev != root_device)
+ continue;
+ }
+
+ if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
+ || (*entry == 'D' && !S_ISDIR (st.st_mode))
+ || (*entry == 'Y' && S_ISDIR (st.st_mode)))
+ {
+ if (one_file_system_option && st.st_dev != root_device)
{
WARN ((0, 0,
_("%s: directory is on a different device: not purging"),
}
}
free (p);
-
+
free (current_dir);
+ return true;
+}
+
+void
+purge_directory (char const *directory_name)
+{
+ if (!try_purge_directory (directory_name))
+ skip_member ();
}
void
list_dumpdir (char *buffer, size_t size)
{
+ int state = 0;
while (size)
{
switch (*buffer)
case 'D':
case 'R':
case 'T':
- fprintf (stdlis, "%c ", *buffer);
+ case 'X':
+ fprintf (stdlis, "%c", *buffer);
+ if (state == 0)
+ {
+ fprintf (stdlis, " ");
+ state = 1;
+ }
buffer++;
size--;
break;
fputc ('\n', stdlis);
buffer++;
size--;
+ state = 0;
break;
default: