/* GNU dump extensions to tar.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009 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
/* Directory attributes. */
struct directory
{
+ struct directory *next;
struct timespec mtime; /* Modification time */
dev_t device_number; /* device number for directory */
ino_t inode_number; /* inode number for directory */
the original directory structure */
const char *tagfile; /* Tag file, if the directory falls under
exclusion_tag_under */
- char name[1]; /* file name of directory */
+ char *caname; /* canonical name */
+ char *name; /* file name of directory */
};
struct dumpdir *
{
struct dumpdir *dump;
size_t i, total, ctsize, len;
- const char *p;
+ char *p;
+ const char *q;
- for (i = 0, total = 0, ctsize = 1, p = contents; *p; total++, p += len)
+ for (i = 0, total = 0, ctsize = 1, q = contents; *q; total++, q += len)
{
- len = strlen (p) + 1;
+ len = strlen (q) + 1;
ctsize += len;
- if (!cmask || strchr (cmask, *p))
+ if (!cmask || strchr (cmask, *q))
i++;
}
dump = xmalloc (sizeof (*dump) + ctsize);
}
\f
+static struct directory *dirhead, *dirtail;
static Hash_table *directory_table;
static Hash_table *directory_meta_table;
/* Calculate the hash of a directory. */
static size_t
-hash_directory_name (void const *entry, size_t n_buckets)
+hash_directory_canonical_name (void const *entry, size_t n_buckets)
{
struct directory const *directory = entry;
- return hash_string (directory->name, n_buckets);
+ return hash_string (directory->caname, n_buckets);
}
/* Compare two directories for equality of their names. */
static bool
-compare_directory_names (void const *entry1, void const *entry2)
+compare_directory_canonical_names (void const *entry1, void const *entry2)
{
struct directory const *directory1 = entry1;
struct directory const *directory2 = entry2;
- return strcmp (directory1->name, directory2->name) == 0;
+ return strcmp (directory1->caname, directory2->caname) == 0;
}
static size_t
&& directory1->inode_number == directory2->inode_number;
}
-/* Make a directory entry for given NAME */
+/* Make a directory entry for given relative NAME and canonical name CANAME.
+ The latter is "stolen", i.e. the returned directory contains pointer to
+ it. */
static struct directory *
-make_directory (const char *name)
+make_directory (const char *name, char *caname)
{
size_t namelen = strlen (name);
- size_t size = offsetof (struct directory, name) + namelen + 1;
- struct directory *directory = xmalloc (size);
+ struct directory *directory = xmalloc (sizeof (*directory));
+ directory->next = NULL;
directory->dump = directory->idump = NULL;
directory->orig = NULL;
directory->flags = false;
- strcpy (directory->name, name);
- if (namelen && ISSLASH (directory->name[namelen - 1]))
- directory->name[namelen - 1] = 0;
+ if (namelen && ISSLASH (name[namelen - 1]))
+ namelen--;
+ directory->name = xmalloc (namelen + 1);
+ memcpy (directory->name, name, namelen);
+ directory->name[namelen] = 0;
+ directory->caname = caname;
directory->tagfile = NULL;
return directory;
}
+static void
+free_directory (struct directory *dir)
+{
+ free (dir->caname);
+ free (dir->name);
+ free (dir);
+}
+
+static struct directory *
+attach_directory (const char *name)
+{
+ char *cname = normalize_filename (name);
+ struct directory *dir = make_directory (name, cname);
+ if (dirtail)
+ dirtail->next = dir;
+ else
+ dirhead = dir;
+ dirtail = dir;
+ return dir;
+}
+
+\f
+static void
+replace_prefix (char **pname, const char *samp, size_t slen,
+ const char *repl, size_t rlen)
+{
+ char *name = *pname;
+ size_t nlen = strlen (name);
+ if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
+ {
+ if (rlen > slen)
+ {
+ name = xrealloc (name, nlen - slen + rlen + 1);
+ *pname = name;
+ }
+ memmove (name + rlen, name + slen, nlen - slen + 1);
+ memcpy (name, repl, rlen);
+ }
+}
+
+void
+dirlist_replace_prefix (const char *pref, const char *repl)
+{
+ struct directory *dp;
+ size_t pref_len = strlen (pref);
+ size_t repl_len = strlen (repl);
+ for (dp = dirhead; dp; dp = dp->next)
+ replace_prefix (&dp->name, pref, pref_len, repl, repl_len);
+}
+
/* 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
dev_t dev, ino_t ino, bool nfs, bool found,
const char *contents)
{
- struct directory *directory = make_directory (name);
+ struct directory *directory = attach_directory (name);
directory->mtime = mtime;
directory->device_number = dev;
if (! ((directory_table
|| (directory_table = hash_initialize (0, 0,
- hash_directory_name,
- compare_directory_names, 0)))
+ hash_directory_canonical_name,
+ compare_directory_canonical_names, 0)))
&& hash_insert (directory_table, directory)))
xalloc_die ();
return 0;
else
{
- struct directory *dir = make_directory (name);
+ char *caname = normalize_filename (name);
+ struct directory *dir = make_directory (name, caname);
struct directory *ret = hash_lookup (directory_table, dir);
- free (dir);
+ free_directory (dir);
return ret;
}
}
+#if 0
+/* Remove directory entry for the given CANAME */
+void
+remove_directory (const char *caname)
+{
+ struct directory *dir = make_directory (caname, xstrdup (caname));
+ struct directory *ret = hash_delete (directory_table, dir);
+ if (ret)
+ free_directory (ret);
+ free_directory (dir);
+}
+#endif
+
+/* Find a directory entry for NAME. If first OLD_PREFIX_LEN
+ bytes of its name match OLD_PREFIX, replace them with
+ NEW_PREFIX. */
+void
+rebase_directory (const char *name, size_t old_prefix_len,
+ const char *old_prefix,
+ const char *new_prefix)
+{
+ struct directory *dir = find_directory (name);
+ if (dir)
+ replace_prefix (&dir->name, old_prefix, old_prefix_len,
+ new_prefix, strlen (new_prefix));
+}
+
/* Return a directory entry for a given combination of device and inode
numbers, or zero if none found. */
static struct directory *
return 0;
else
{
- struct directory *dir = make_directory ("");
+ struct directory *dir = make_directory ("", NULL);
struct directory *ret;
dir->device_number = dev;
dir->inode_number = ino;
ret = hash_lookup (directory_meta_table, dir);
- free (dir);
+ free_directory (dir);
return ret;
}
}
free (p);
}
+#define PD_FORCE_CHILDREN 0x10
+#define PD_FORCE_INIT 0x20
+#define PD_CHILDREN(f) ((f) & 3)
+
static struct directory *
-procdir (char *name_buffer, struct stat *stat_data,
+procdir (const char *name_buffer, struct stat *stat_data,
dev_t device,
- enum children children,
- bool verbose,
+ int flag,
char *entry)
{
struct directory *directory;
if ((directory = find_directory (name_buffer)) != NULL)
{
if (DIR_IS_INITED (directory))
- return directory;
+ {
+ if (flag & PD_FORCE_INIT)
+ {
+ assign_string (&directory->name, name_buffer);
+ }
+ else
+ {
+ *entry = 'N'; /* Avoid duplicating this directory */
+ return directory;
+ }
+ }
+ if (strcmp (directory->name, name_buffer))
+ {
+ *entry = 'N';
+ 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.
To avoid spurious incremental redumping of
directories, consider all NFS devices as equal,
relying on the i-node to establish differences. */
-
+
if (! ((!check_device_option
|| (DIR_IS_NFS (directory) && nfs)
|| directory->device_number == stat_data->st_dev)
stat_data->st_ino);
if (d)
{
- if (verbose_option)
- WARN ((0, 0, _("%s: Directory has been renamed from %s"),
- quotearg_colon (name_buffer),
- quote_n (1, d->name)));
- directory->orig = d;
- DIR_SET_FLAG (directory, DIRF_RENAMED);
+ if (strcmp (d->name, name_buffer))
+ {
+ WARNOPT (WARN_RENAME_DIRECTORY,
+ (0, 0,
+ _("%s: Directory has been renamed from %s"),
+ quotearg_colon (name_buffer),
+ quote_n (1, d->name)));
+ directory->orig = d;
+ DIR_SET_FLAG (directory, DIRF_RENAMED);
+ dirlist_replace_prefix (d->name, name_buffer);
+ }
directory->children = CHANGED_CHILDREN;
}
else
{
- if (verbose_option)
- WARN ((0, 0, _("%s: Directory has been renamed"),
- quotearg_colon (name_buffer)));
+ WARNOPT (WARN_RENAME_DIRECTORY,
+ (0, 0, _("%s: Directory has been renamed"),
+ quotearg_colon (name_buffer)));
directory->children = ALL_CHILDREN;
directory->device_number = stat_data->st_dev;
directory->inode_number = stat_data->st_ino;
}
else
directory->children = CHANGED_CHILDREN;
-
+
DIR_SET_FLAG (directory, DIRF_FOUND);
}
else
{
struct directory *d = find_directory_meta (stat_data->st_dev,
stat_data->st_ino);
-
+
directory = note_directory (name_buffer,
get_stat_mtime(stat_data),
stat_data->st_dev,
if (d)
{
- if (verbose)
- WARN ((0, 0, _("%s: Directory has been renamed from %s"),
- quotearg_colon (name_buffer),
- quote_n (1, d->name)));
- directory->orig = d;
- DIR_SET_FLAG (directory, DIRF_RENAMED);
+ if (strcmp (d->name, name_buffer))
+ {
+ WARNOPT (WARN_RENAME_DIRECTORY,
+ (0, 0, _("%s: Directory has been renamed from %s"),
+ quotearg_colon (name_buffer),
+ quote_n (1, d->name)));
+ directory->orig = d;
+ DIR_SET_FLAG (directory, DIRF_RENAMED);
+ dirlist_replace_prefix (d->name, name_buffer);
+ }
directory->children = CHANGED_CHILDREN;
}
else
{
DIR_SET_FLAG (directory, DIRF_NEW);
- if (verbose)
- WARN ((0, 0, _("%s: Directory is new"),
- quotearg_colon (name_buffer)));
+ WARNOPT (WARN_NEW_DIRECTORY,
+ (0, 0, _("%s: Directory is new"),
+ quotearg_colon (name_buffer)));
directory->children =
(listed_incremental_option
|| (OLDER_STAT_TIME (*stat_data, m)
/* ... except if it was explicitely given in the command line */
&& !is_individual_file (name_buffer))
directory->children = NO_CHILDREN;
- else if (children == ALL_CHILDREN)
- directory->children = ALL_CHILDREN;
-
+ else if (flag & PD_FORCE_CHILDREN)
+ {
+ directory->children = PD_CHILDREN(flag);
+ if (directory->children == NO_CHILDREN)
+ *entry = 'N';
+ }
+
DIR_SET_FLAG (directory, DIRF_INIT);
- {
- const char *tag_file_name;
-
- switch (check_exclusion_tags (name_buffer, &tag_file_name))
- {
- case exclusion_tag_all:
- /* This warning can be duplicated by code in dump_file0, but only
- in case when the topmost directory being archived contains
- an exclusion tag. */
- exclusion_tag_warning (name_buffer, tag_file_name,
- _("directory not dumped"));
- if (entry)
- *entry = 'N';
- directory->children = NO_CHILDREN;
- break;
-
- case exclusion_tag_contents:
- exclusion_tag_warning (name_buffer, tag_file_name,
- _("contents not dumped"));
- directory->children = NO_CHILDREN;
- break;
+ if (directory->children != NO_CHILDREN)
+ {
+ const char *tag_file_name;
- case exclusion_tag_under:
- exclusion_tag_warning (name_buffer, tag_file_name,
- _("contents not dumped"));
- directory->tagfile = tag_file_name;
- break;
+ switch (check_exclusion_tags (name_buffer, &tag_file_name))
+ {
+ case exclusion_tag_all:
+ /* This warning can be duplicated by code in dump_file0, but only
+ in case when the topmost directory being archived contains
+ an exclusion tag. */
+ exclusion_tag_warning (name_buffer, tag_file_name,
+ _("directory not dumped"));
+ *entry = 'N';
+ directory->children = NO_CHILDREN;
+ break;
- case exclusion_tag_none:
- break;
- }
- }
+ case exclusion_tag_contents:
+ exclusion_tag_warning (name_buffer, tag_file_name,
+ _("contents not dumped"));
+ directory->children = NO_CHILDREN;
+ break;
+
+ case exclusion_tag_under:
+ exclusion_tag_warning (name_buffer, tag_file_name,
+ _("contents not dumped"));
+ directory->tagfile = tag_file_name;
+ break;
+
+ case exclusion_tag_none:
+ break;
+ }
+ }
return directory;
}
free (array);
}
-/* Recursively scan the given directory. */
-static const char *
-scan_directory (char *dir, dev_t device)
+/* Recursively scan the given directory DIR.
+ DEVICE is the device number where DIR resides (for --one-file-system).
+ If CMDLINE is true, the directory name was explicitly listed in the
+ command line. */
+const char *
+scan_directory (char *dir, dev_t device, bool cmdline)
{
char *dirp = savedir (dir); /* for scanning directory */
char *name_buffer; /* directory, `/', and directory member */
size_t name_length; /* used length in name_buffer */
struct stat stat_data;
struct directory *directory;
-
+ char ch;
+
if (! dirp)
savedir_error (dir);
name_buffer_size = strlen (dir) + NAME_FIELD_SIZE;
name_buffer = xmalloc (name_buffer_size + 2);
strcpy (name_buffer, dir);
- if (! ISSLASH (dir[strlen (dir) - 1]))
- strcat (name_buffer, "/");
- name_length = strlen (name_buffer);
-
+ zap_slashes (name_buffer);
+
if (deref_stat (dereference_option, name_buffer, &stat_data))
{
stat_diag (name_buffer);
return NULL;
}
- directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false,
- NULL);
+ directory = procdir (name_buffer, &stat_data, device,
+ (cmdline ? PD_FORCE_INIT : 0),
+ &ch);
+
+ name_length = strlen (name_buffer);
+ if (! ISSLASH (name_buffer[name_length - 1]))
+ {
+ name_buffer[name_length] = DIRECTORY_SEPARATOR;
+ /* name_buffer has been allocated an extra slot */
+ name_buffer[++name_length] = 0;
+ }
if (dirp && directory->children != NO_CHILDREN)
{
if (S_ISDIR (stat_data.st_mode))
{
+ int pd_flag = 0;
+ if (!recursion_option)
+ pd_flag |= PD_FORCE_CHILDREN | NO_CHILDREN;
+ else if (directory->children == ALL_CHILDREN)
+ pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN;
*entry = 'D';
- procdir (name_buffer, &stat_data, device,
- directory->children,
- verbose_option, entry);
+ procdir (name_buffer, &stat_data, device, pd_flag, entry);
}
else if (one_file_system_option && device != stat_data.st_dev)
}
const char *
-get_directory_contents (char *dir, dev_t device)
+get_directory_contents (char *dir, dev_t device, bool force)
{
- return scan_directory (dir, device);
+ return scan_directory (dir, device, force);
}
\f
obstack_grow (stk, s, strlen (s) + 1);
}
-static bool
-rename_handler (void *data, void *proc_data)
+static void
+store_rename (struct directory *dir, struct obstack *stk)
{
- struct directory *dir = data;
- struct obstack *stk = proc_data;
-
if (DIR_IS_RENAMED (dir))
{
struct directory *prev, *p;
obstack_code_rename (stk, "", prev->name);
}
}
- return true;
}
const char *
{
struct obstack stk;
size_t size;
-
- if (directory_table == NULL)
+ struct directory *dp;
+
+ if (dirhead == NULL)
return dump;
obstack_init (&stk);
else
size = 0;
- hash_do_for_each (directory_table, rename_handler, &stk);
+ for (dp = dirhead; dp; dp = dp->next)
+ store_rename (dp, &stk);
+
if (obstack_object_size (&stk) != size)
{
obstack_1grow (&stk, 0);
int fd;
char *buf = 0;
size_t bufsize;
+ int flags = O_RDWR | O_CREAT;
+ if (incremental_level == 0)
+ flags |= O_TRUNC;
/* 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);
+ fd = open (listed_incremental_option, flags, MODE_RW);
if (fd < 0)
{
open_error (listed_incremental_option);
}
if (has_tempdir)
- WARN ((0, 0, _("Malformed dumpdir: 'X' never used")));
+ WARNOPT (WARN_BAD_DUMPDIR,
+ (0, 0, _("Malformed dumpdir: 'X' never used")));
return true;
}