|
@@ -343,11 +343,12 @@ dumpdir_locate (const char *dump, const char *name)
|
|
|
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)
|
|
@@ -556,49 +557,6 @@ get_directory_contents (char *dir_name, dev_t device)
|
|
|
}
|
|
|
|
|
|
|
|
|
-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)
|
|
|
{
|
|
@@ -639,14 +597,17 @@ rename_handler (void *data, void *proc_data)
|
|
|
|
|
|
/* 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;
|
|
@@ -1173,50 +1134,160 @@ is_dumpdir (struct tar_stat_info *stat_info)
|
|
|
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. */
|
|
|
-
|
|
|
- skip_member ();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ /* 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;
|
|
@@ -1267,8 +1338,16 @@ purge_directory (char const *directory_name)
|
|
|
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)
|
|
|
{
|
|
@@ -1281,6 +1360,7 @@ list_dumpdir (char *buffer, size_t size)
|
|
|
case 'D':
|
|
|
case 'R':
|
|
|
case 'T':
|
|
|
+ case 'X':
|
|
|
fprintf (stdlis, "%c ", *buffer);
|
|
|
buffer++;
|
|
|
size--;
|