/* GNU dump extensions to tar.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007, 2008 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_SET_FLAG(d,f) (d)->flags |= (f)
#define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
+struct dumpdir /* Dump directory listing */
+{
+ char *contents; /* Actual contents */
+ size_t total; /* Total number of elements */
+ size_t elc; /* Number of D/N/Y elements. */
+ char **elv; /* Array of D/N/Y elements */
+};
+
/* Directory attributes. */
struct directory
{
struct timespec mtime; /* Modification time */
dev_t device_number; /* device number for directory */
ino_t inode_number; /* inode number for directory */
- char *contents; /* Directory contents */
- char *icontents; /* Initial contents if the directory was
+ struct dumpdir *dump; /* Directory contents */
+ struct dumpdir *idump; /* Initial contents if the directory was
rescanned */
enum children children; /* What to save under this directory */
unsigned flags; /* See DIRF_ macros above */
char name[1]; /* file name of directory */
};
+struct dumpdir *
+dumpdir_create0 (const char *contents, const char *cmask)
+{
+ struct dumpdir *dump;
+ size_t i, total, ctsize, len;
+ const char *p;
+
+ for (i = 0, total = 0, ctsize = 1, p = contents; *p; total++, p += len)
+ {
+ len = strlen (p) + 1;
+ ctsize += len;
+ if (!cmask || strchr (cmask, *p))
+ i++;
+ }
+ dump = xmalloc (sizeof (*dump) + ctsize);
+ dump->contents = (char*)(dump + 1);
+ memcpy (dump->contents, contents, ctsize);
+ dump->total = total;
+ dump->elc = i;
+ dump->elv = xcalloc (i + 1, sizeof (dump->elv[0]));
+
+ for (i = 0, p = dump->contents; *p; p += strlen (p) + 1)
+ {
+ if (!cmask || strchr (cmask, *p))
+ dump->elv[i++] = p + 1;
+ }
+ dump->elv[i] = NULL;
+ return dump;
+}
+
+struct dumpdir *
+dumpdir_create (const char *contents)
+{
+ return dumpdir_create0 (contents, "YND");
+}
+
+void
+dumpdir_free (struct dumpdir *dump)
+{
+ free (dump->elv);
+ free (dump);
+}
+
+static int
+compare_dirnames (const void *first, const void *second)
+{
+ char const *const *name1 = first;
+ char const *const *name2 = second;
+ return strcmp (*name1, *name2);
+}
+
+/* Locate NAME in the dumpdir array DUMP.
+ Return pointer to the slot in DUMP->contents, or NULL if not found */
+char *
+dumpdir_locate (struct dumpdir *dump, const char *name)
+{
+ char **ptr;
+ if (!dump)
+ return NULL;
+
+ ptr = bsearch (&name, dump->elv, dump->elc, sizeof (dump->elv[0]),
+ compare_dirnames);
+ return ptr ? *ptr - 1: NULL;
+}
+
+struct dumpdir_iter
+{
+ struct dumpdir *dump; /* Dumpdir being iterated */
+ int all; /* Iterate over all entries, not only D/N/Y */
+ size_t next; /* Index of the next element */
+};
+
+char *
+dumpdir_next (struct dumpdir_iter *itr)
+{
+ size_t cur = itr->next;
+ char *ret = NULL;
+
+ if (itr->all)
+ {
+ ret = itr->dump->contents + cur;
+ if (*ret == 0)
+ return NULL;
+ itr->next += strlen (ret) + 1;
+ }
+ else if (cur < itr->dump->elc)
+ {
+ ret = itr->dump->elv[cur] - 1;
+ itr->next++;
+ }
+
+ return ret;
+}
+
+char *
+dumpdir_first (struct dumpdir *dump, int all, struct dumpdir_iter **pitr)
+{
+ struct dumpdir_iter *itr = xmalloc (sizeof (*itr));
+ itr->dump = dump;
+ itr->all = all;
+ itr->next = 0;
+ *pitr = itr;
+ return dumpdir_next (itr);
+}
+
+/* Return size in bytes of the dumpdir array P */
+size_t
+dumpdir_size (const char *p)
+{
+ size_t totsize = 0;
+
+ while (*p)
+ {
+ size_t size = strlen (p) + 1;
+ totsize += size;
+ p += size;
+ }
+ return totsize + 1;
+}
+
+\f
static Hash_table *directory_table;
static Hash_table *directory_meta_table;
size_t namelen = strlen (name);
size_t size = offsetof (struct directory, name) + namelen + 1;
struct directory *directory = xmalloc (size);
- directory->contents = directory->icontents = NULL;
+ directory->dump = directory->idump = NULL;
directory->orig = NULL;
directory->flags = false;
strcpy (directory->name, name);
found that the directory exists. */
static struct directory *
note_directory (char const *name, struct timespec mtime,
- dev_t dev, ino_t ino, bool nfs, bool found, char *contents)
+ dev_t dev, ino_t ino, bool nfs, bool found,
+ const char *contents)
{
struct directory *directory = make_directory (name);
if (found)
DIR_SET_FLAG (directory, DIRF_FOUND);
if (contents)
- {
- size_t size = dumpdir_size (contents);
- directory->contents = xmalloc (size);
- memcpy (directory->contents, contents, size);
- }
+ directory->dump = dumpdir_create (contents);
else
- directory->contents = NULL;
+ directory->dump = NULL;
if (! ((directory_table
|| (directory_table = hash_initialize (0, 0,
return directory;
}
-/* Locate NAME in the dumpdir array DUMP.
- Return pointer to the slot in the array, or NULL if not found */
-const char *
-dumpdir_locate (const char *dump, const char *name)
-{
- if (dump)
- while (*dump)
- {
- /* 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 (!strchr ("RX", *dump))
- {
- int rc = strcmp (dump + 1, name);
- if (rc == 0)
- return dump;
- if (rc > 1)
- break;
- }
- dump += strlen (dump) + 1;
- }
- return NULL;
-}
-
-/* Return size in bytes of the dumpdir array P */
-size_t
-dumpdir_size (const char *p)
-{
- size_t totsize = 0;
-
- while (*p)
- {
- size_t size = strlen (p) + 1;
- totsize += size;
- p += size;
- }
- return totsize + 1;
-}
-
-static int
-compare_dirnames (const void *first, const void *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
build a new dumpdir template.
DIR must be returned by a previous call to savedir().
- File names in DIRECTORY->contents must be sorted
+ File names in DIRECTORY->dump->contents must be sorted
alphabetically.
- DIRECTORY->contents is replaced with the created template. Each entry is
+ DIRECTORY->dump is replaced with the created template. Each entry is
prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
void
const char *p;
char const **array;
char *new_dump, *new_dump_ptr;
- const char *dump;
+ struct dumpdir *dump;
if (directory->children == ALL_CHILDREN)
dump = NULL;
else if (DIR_IS_RENAMED (directory))
- dump = directory->orig->icontents ?
- directory->orig->icontents : directory->orig->contents;
+ dump = directory->orig->idump ?
+ directory->orig->idump : directory->orig->dump;
else
- dump = directory->contents;
+ dump = directory->dump;
/* Count the size of DIR and the number of elements it contains */
dirsize = 0;
{
if (directory->tagfile)
*new_dump_ptr = strcmp (directory->tagfile, array[i]) == 0 ?
- ' ' : 'I';
+ ' ' : 'I';
else
*new_dump_ptr = ' ';
new_dump_ptr++;
- dump = loc + strlen (loc) + 1;
}
else if (directory->tagfile)
*new_dump_ptr++ = strcmp (directory->tagfile, array[i]) == 0 ?
;
}
*new_dump_ptr = 0;
- directory->icontents = directory->contents;
- directory->contents = new_dump;
+ directory->idump = directory->dump;
+ directory->dump = dumpdir_create0 (new_dump, NULL);
free (array);
}
/* Recursively scan the given directory. */
-static char *
+static const char *
scan_directory (char *dir, dev_t device)
{
char *dirp = savedir (dir); /* for scanning directory */
if (dirp && directory->children != NO_CHILDREN)
{
- char *entry; /* directory entry being scanned */
+ char *entry; /* directory entry being scanned */
size_t entrylen; /* length of directory entry */
+ dumpdir_iter_t itr;
makedumpdir (directory, dirp);
- for (entry = directory->contents;
- (entrylen = strlen (entry)) != 0;
- entry += entrylen + 1)
+ for (entry = dumpdir_first (directory->dump, 1, &itr);
+ entry;
+ entry = dumpdir_next (itr))
{
+ entrylen = strlen (entry);
if (name_buffer_size <= entrylen - 1 + name_length)
{
do
*entry = 'Y';
}
}
+ free (itr);
}
free (name_buffer);
if (dirp)
free (dirp);
- return directory->contents;
+ return directory->dump ? directory->dump->contents : NULL;
}
-char *
+const char *
get_directory_contents (char *dir, dev_t device)
{
return scan_directory (dir, device);
fwrite (s, strlen (s) + 1, 1, fp);
fwrite (directory->name, strlen (directory->name) + 1, 1, fp);
- if (directory->contents)
+ if (directory->dump)
{
- char *p;
- for (p = directory->contents; *p; p += strlen (p) + 1)
- {
- if (strchr ("YND", *p))
- fwrite (p, strlen (p) + 1, 1, fp);
- }
+ const char *p;
+ dumpdir_iter_t 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);
}
char *current_dir;
char *cur, *arc, *p;
char *temp_stub = NULL;
+ struct dumpdir *dump;
if (!is_dumpdir (¤t_stat_info))
return false;
free (temp_stub);
/* Process deletes */
+ dump = dumpdir_create (current_stat_info.dumpdir);
p = NULL;
for (cur = current_dir; *cur; cur += strlen (cur) + 1)
{
continue;
}
- if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
+ if (!(entry = dumpdir_locate (dump, cur))
|| (*entry == 'D' && !S_ISDIR (st.st_mode))
|| (*entry == 'Y' && S_ISDIR (st.st_mode)))
{
}
}
free (p);
-
+ dumpdir_free (dump);
+
free (current_dir);
return true;
}