/* GNU dump extensions to tar.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003 Free Software Foundation, Inc.
+ 2003, 2004, 2005 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
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-#include "system.h"
+#include <system.h>
#include <getline.h>
#include <hash.h>
#include <quotearg.h>
#include "common.h"
-\f
-/* Variable sized generic character buffers. */
-
-struct accumulator
-{
- size_t allocated;
- size_t length;
- char *pointer;
-};
-
-/* Amount of space guaranteed just after a reallocation. */
-#define ACCUMULATOR_SLACK 50
-
-/* Return the accumulated data from an ACCUMULATOR buffer. */
-static char *
-get_accumulator (struct accumulator *accumulator)
-{
- return accumulator->pointer;
-}
-
-/* Allocate and return a new accumulator buffer. */
-static struct accumulator *
-new_accumulator (void)
-{
- struct accumulator *accumulator
- = xmalloc (sizeof (struct accumulator));
- accumulator->allocated = ACCUMULATOR_SLACK;
- accumulator->pointer = xmalloc (ACCUMULATOR_SLACK);
- accumulator->length = 0;
- return accumulator;
-}
-
-/* Deallocate an ACCUMULATOR buffer. */
-static void
-delete_accumulator (struct accumulator *accumulator)
-{
- free (accumulator->pointer);
- free (accumulator);
-}
-
-/* At the end of an ACCUMULATOR buffer, add a DATA block of SIZE bytes. */
-static void
-add_to_accumulator (struct accumulator *accumulator,
- const char *data, size_t size)
-{
- if (accumulator->length + size > accumulator->allocated)
- {
- accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
- accumulator->pointer =
- xrealloc (accumulator->pointer, accumulator->allocated);
- }
- memcpy (accumulator->pointer + accumulator->length, data, size);
- accumulator->length += size;
-}
-\f
/* Incremental dump specialities. */
/* Which child files to save under a directory. */
enum children children;
bool nfs;
bool found;
- char name[1]; /* path name of directory */
+ char name[1]; /* file name of directory */
};
static Hash_table *directory_table;
#endif
/* Calculate the hash of a directory. */
-static unsigned
-hash_directory (void const *entry, unsigned n_buckets)
+static size_t
+hash_directory (void const *entry, size_t n_buckets)
{
struct directory const *directory = entry;
return hash_string (directory->name, n_buckets);
return directory;
}
-/* Return a directory entry for a given path NAME, or zero if none found. */
+/* Return a directory entry for a given file NAME, or zero if none found. */
static struct directory *
find_directory (char *name)
{
(*(char *const *) second) + 1);
}
-char *
-get_directory_contents (char *path, dev_t device)
+/* Recursively scan the given directory. */
+static void
+scan_directory (struct obstack *stk, char *dir_name, dev_t device)
{
- struct accumulator *accumulator;
-
- /* Recursively scan the given PATH. */
-
- {
- char *dirp = savedir (path); /* for scanning directory */
- char const *entry; /* directory entry being scanned */
- size_t entrylen; /* length of directory entry */
- char *name_buffer; /* directory, `/', and directory member */
- size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */
- size_t name_length; /* used length in name_buffer */
- struct directory *directory; /* for checking if already already seen */
- enum children children;
-
- if (! dirp)
+ char *dirp = savedir (dir_name); /* for scanning directory */
+ char const *entry; /* directory entry being scanned */
+ size_t entrylen; /* length of directory entry */
+ char *name_buffer; /* directory, `/', and directory member */
+ size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */
+ size_t name_length; /* used length in name_buffer */
+ struct directory *directory; /* for checking if already seen */
+ enum children children;
+
+ if (! dirp)
+ {
+ savedir_error (dir_name);
+ }
+ errno = 0;
+
+ name_buffer_size = strlen (dir_name) + NAME_FIELD_SIZE;
+ name_buffer = xmalloc (name_buffer_size + 2);
+ strcpy (name_buffer, dir_name);
+ if (! ISSLASH (dir_name[strlen (dir_name) - 1]))
+ strcat (name_buffer, "/");
+ name_length = strlen (name_buffer);
+
+ directory = find_directory (dir_name);
+ children = directory ? directory->children : CHANGED_CHILDREN;
+
+ if (dirp && children != NO_CHILDREN)
+ for (entry = dirp;
+ (entrylen = strlen (entry)) != 0;
+ entry += entrylen + 1)
{
- savedir_error (path);
- }
- errno = 0;
-
- name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
- name_buffer = xmalloc (name_buffer_size + 2);
- strcpy (name_buffer, path);
- if (! ISSLASH (path[strlen (path) - 1]))
- strcat (name_buffer, "/");
- name_length = strlen (name_buffer);
-
- directory = find_directory (path);
- children = directory ? directory->children : CHANGED_CHILDREN;
-
- accumulator = new_accumulator ();
-
- if (dirp && children != NO_CHILDREN)
- for (entry = dirp;
- (entrylen = strlen (entry)) != 0;
- entry += entrylen + 1)
- {
- if (name_buffer_size <= entrylen + name_length)
- {
- do
- name_buffer_size += NAME_FIELD_SIZE;
- while (name_buffer_size <= entrylen + name_length);
- name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
- }
- strcpy (name_buffer + name_length, entry);
-
- if (excluded_name (name_buffer))
- add_to_accumulator (accumulator, "N", 1);
- else
- {
- struct stat stat_data;
-
- if (deref_stat (dereference_option, name_buffer, &stat_data))
- {
- stat_diag (name_buffer);
- continue;
- }
-
- if (S_ISDIR (stat_data.st_mode))
- {
- bool nfs = NFS_FILE_STAT (stat_data);
-
- if (directory = find_directory (name_buffer), 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.
- To avoid spurious incremental redumping of
- directories, consider all NFS devices as equal,
- relying on the i-node to establish differences. */
-
- if (! (((directory->nfs & nfs)
- || directory->device_number == stat_data.st_dev)
- && directory->inode_number == stat_data.st_ino))
+ if (name_buffer_size <= entrylen + name_length)
+ {
+ do
+ name_buffer_size += NAME_FIELD_SIZE;
+ while (name_buffer_size <= entrylen + name_length);
+ name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
+ }
+ strcpy (name_buffer + name_length, entry);
+
+ if (excluded_name (name_buffer))
+ obstack_1grow (stk, 'N');
+ else
+ {
+ struct stat stat_data;
+
+ if (deref_stat (dereference_option, name_buffer, &stat_data))
+ {
+ stat_diag (name_buffer);
+ continue;
+ }
+
+ if (S_ISDIR (stat_data.st_mode))
+ {
+ bool nfs = NFS_FILE_STAT (stat_data);
+
+ if ((directory = find_directory (name_buffer)) != NULL)
+ {
+ /* 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.
+ To avoid spurious incremental redumping of
+ directories, consider all NFS devices as equal,
+ relying on the i-node to establish differences. */
+
+ if (! (((directory->nfs & nfs)
+ || directory->device_number == stat_data.st_dev)
+ && directory->inode_number == stat_data.st_ino))
{
if (verbose_option)
WARN ((0, 0, _("%s: Directory has been renamed"),
directory->device_number = stat_data.st_dev;
directory->inode_number = stat_data.st_ino;
}
- directory->found = 1;
- }
- else
- {
- if (verbose_option)
- WARN ((0, 0, _("%s: Directory is new"),
- quotearg_colon (name_buffer)));
- directory = note_directory (name_buffer,
- stat_data.st_dev,
- stat_data.st_ino, nfs, 1);
- directory->children =
- ((listed_incremental_option
- || newer_mtime_option <= stat_data.st_mtime
- || (after_date_option &&
- newer_ctime_option <= stat_data.st_ctime))
- ? ALL_CHILDREN
- : CHANGED_CHILDREN);
- }
-
- if (one_file_system_option && device != stat_data.st_dev)
- directory->children = NO_CHILDREN;
- else if (children == ALL_CHILDREN)
- directory->children = ALL_CHILDREN;
-
- add_to_accumulator (accumulator, "D", 1);
- }
-
- else if (one_file_system_option && device != stat_data.st_dev)
- add_to_accumulator (accumulator, "N", 1);
+ directory->found = 1;
+ }
+ else
+ {
+ if (verbose_option)
+ WARN ((0, 0, _("%s: Directory is new"),
+ quotearg_colon (name_buffer)));
+ directory = note_directory (name_buffer,
+ stat_data.st_dev,
+ stat_data.st_ino, nfs, 1);
+ directory->children =
+ ((listed_incremental_option
+ || OLDER_STAT_TIME (stat_data, m)
+ || (after_date_option
+ && OLDER_STAT_TIME (stat_data, c)))
+ ? ALL_CHILDREN
+ : CHANGED_CHILDREN);
+ }
+
+ if (one_file_system_option && device != stat_data.st_dev)
+ directory->children = NO_CHILDREN;
+ else if (children == ALL_CHILDREN)
+ directory->children = ALL_CHILDREN;
+
+ obstack_1grow (stk, 'D');
+ }
+
+ else if (one_file_system_option && device != stat_data.st_dev)
+ obstack_1grow (stk, 'N');
#ifdef S_ISHIDDEN
- else if (S_ISHIDDEN (stat_data.st_mode))
- {
- add_to_accumulator (accumulator, "D", 1);
- add_to_accumulator (accumulator, entry, entrylen);
- add_to_accumulator (accumulator, "A", 2);
- continue;
- }
+ else if (S_ISHIDDEN (stat_data.st_mode))
+ {
+ obstack_1grow (stk, 'D');
+ obstack_grow (stk, entry, entrylen);
+ obstack_grow (stk, "A", 2);
+ continue;
+ }
#endif
+ else
+ if (children == CHANGED_CHILDREN
+ && OLDER_STAT_TIME (stat_data, m)
+ && (!after_date_option || OLDER_STAT_TIME (stat_data, c)))
+ obstack_1grow (stk, 'N');
else
- if (children == CHANGED_CHILDREN
- && stat_data.st_mtime < newer_mtime_option
- && (!after_date_option
- || stat_data.st_ctime < newer_ctime_option))
- add_to_accumulator (accumulator, "N", 1);
- else
- add_to_accumulator (accumulator, "Y", 1);
- }
+ obstack_1grow (stk, 'Y');
+ }
- add_to_accumulator (accumulator, entry, entrylen + 1);
- }
+ obstack_grow (stk, entry, entrylen + 1);
+ }
- add_to_accumulator (accumulator, "\000\000", 2);
+ obstack_grow (stk, "\000\000", 2);
- free (name_buffer);
- if (dirp)
- free (dirp);
- }
+ free (name_buffer);
+ if (dirp)
+ free (dirp);
+}
- /* Sort the contents of the directory, now that we have it all. */
+/* Sort the contents of the obstack, and convert it to the char * */
+static char *
+sort_obstack (struct obstack *stk)
+{
+ char *pointer = obstack_finish (stk);
+ size_t counter;
+ char *cursor;
+ char *buffer;
+ char **array;
+ char **array_cursor;
- {
- char *pointer = get_accumulator (accumulator);
- size_t counter;
- char *cursor;
- char *buffer;
- char **array;
- char **array_cursor;
-
- counter = 0;
- for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
- counter++;
-
- if (! counter)
- {
- delete_accumulator (accumulator);
- return 0;
- }
+ counter = 0;
+ for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
+ counter++;
- array = xmalloc (sizeof (char *) * (counter + 1));
+ if (!counter)
+ return NULL;
- array_cursor = array;
- for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
- *array_cursor++ = cursor;
- *array_cursor = 0;
+ array = obstack_alloc (stk, sizeof (char *) * (counter + 1));
- qsort (array, counter, sizeof (char *), compare_dirents);
+ array_cursor = array;
+ for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
+ *array_cursor++ = cursor;
+ *array_cursor = 0;
- buffer = xmalloc (cursor - pointer + 2);
+ qsort (array, counter, sizeof (char *), compare_dirents);
- cursor = buffer;
- for (array_cursor = array; *array_cursor; array_cursor++)
- {
- char *string = *array_cursor;
+ buffer = xmalloc (cursor - pointer + 2);
- while ((*cursor++ = *string++))
- continue;
- }
- *cursor = '\0';
+ cursor = buffer;
+ for (array_cursor = array; *array_cursor; array_cursor++)
+ {
+ char *string = *array_cursor;
+
+ while ((*cursor++ = *string++))
+ continue;
+ }
+ *cursor = '\0';
+ return buffer;
+}
- delete_accumulator (accumulator);
- free (array);
- return buffer;
- }
+char *
+get_directory_contents (char *dir_name, dev_t device)
+{
+ struct obstack stk;
+ char *buffer;
+
+ obstack_init (&stk);
+ scan_directory (&stk, dir_name, device);
+ buffer = sort_obstack (&stk);
+ obstack_free (&stk, NULL);
+ return buffer;
}
+
\f
+
static FILE *listed_incremental_stream;
void
ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
_("Time stamp out of range")));
else
- newer_mtime_option = t;
+ {
+ newer_mtime_option.tv_sec = t;
+ newer_mtime_option.tv_nsec = 0;
+ }
while (0 < (n = getline (&buf, &bufsize, fp)))
{
if (fclose (fp) != 0)
close_error (listed_incremental_option);
}
+
\f
/* Restoration of incremental dumps. */
+/* Examine the directories under directory_name and delete any
+ files that were not there at the time of the back-up. */
void
-gnu_restore (char const *directory_name)
+purge_directory (char const *directory_name)
{
char *archive_dir;
char *current_dir;
}
if (*arc == '\0')
{
+ struct stat st;
char *p = new_name (directory_name, cur);
+
+ if (deref_stat (true, p, &st))
+ {
+ stat_diag (p);
+ WARN((0, 0, _("%s: Not purging directory: unable to stat"),
+ quotearg_colon (p)));
+ continue;
+ }
+ else if (one_file_system_option && st.st_dev != root_device)
+ {
+ WARN((0, 0,
+ _("%s: directory is on a different device: not purging"),
+ quotearg_colon (p)));
+ continue;
+ }
+
if (! interactive_option || confirm ("delete", p))
{
if (verbose_option)
fprintf (stdlis, _("%s: Deleting %s\n"),
program_name, quote (p));
- if (! remove_any_file (p, 1))
+ if (! remove_any_file (p, RECURSIVE_REMOVE_OPTION))
{
int e = errno;
ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));