Fix handling of files removed during incremental dumps.

Changes to src/create.c and src/incremen.c are partially
based on patch from Alexander Peslyak <solar at openwall.com>.

The new testcases require paxutils commit f653a2b or later.

* src/common.h (struct name): New member `cmdline'.
(dump_file): Change type of the 2nd argument to bool.
(file_removed_diag, dir_removed_diag): New prototypes.
(addname): New argument `cmdline'.
(name_from_list): Change return value.
* src/create.c (dump_dir0, dump_dir): top_level is bool.
(create_archive): Update calls to name_from_list.
Take advantage of the name->cmdline to set top_level argument
during incremental backups.
(dump_file0): top_level is bool.
Do not bail out if a no-top-level file disappears during incremental
backup, use file_removed_diag instead.
(dump_filed): top_level is bool.
* src/incremen.c (update_parent_directory): Silently ignore
ENOENT.  It should have already been reported elsewhere.
(scan_directory): Use dir_removed_diag to report missing directories.
* src/misc.c (file_removed_diag, dir_removed_diag): New functions.
* src/names.c (name_gather): Set ->cmdname.
(addname): Likewise. All uses updated.
(name_from_list): Return struct name const *. All uses updated.

* tests/filerem01.at: New testcase.
* tests/filerem02.at: New testcase.
* tests/Makefile.am, tests/testsuite.at: Add filerem01.at, filerem02.at
* tests/grow.at, test/truncate.at: Use new syntax for genfile --run.

* NEWS: Update.
* doc/tar.texi: Minor fix.
This commit is contained in:
Sergey Poznyakoff
2009-08-08 19:53:54 +03:00
parent 2b1bffbad6
commit 51aee274e8
15 changed files with 250 additions and 51 deletions
+12 -5
View File
@@ -331,8 +331,10 @@ struct name
char *name; /* File name or globbing pattern */
size_t length; /* cached strlen (name) */
int matching_flags; /* wildcard flags if name is a pattern */
int matching_flags; /* wildcard flags if name is a pattern */
bool cmdline; /* true if this name was given in the
command line */
int change_dir; /* Number of the directory to change to.
Set with the -C option. */
uintmax_t found_count; /* number of times a matching file has
@@ -443,7 +445,7 @@ bool cachedir_file_p (const char *name);
bool file_dumpable_p (struct tar_stat_info *st);
void create_archive (void);
void pad_archive (off_t size_left);
void dump_file (const char *st, int top_level, dev_t parent_device);
void dump_file (const char *st, bool top_level, dev_t parent_device);
union block *start_header (struct tar_stat_info *st);
void finish_header (struct tar_stat_info *st, union block *header,
off_t block_ordinal);
@@ -629,6 +631,10 @@ void readlink_diag (char const *name);
void savedir_diag (char const *name);
void seek_diag_details (char const *name, off_t offset);
void stat_diag (char const *name);
void file_removed_diag (const char *name, bool top_level,
void (*diagfn) (char const *name));
void dir_removed_diag (char const *name, bool top_level,
void (*diagfn) (char const *name));
void write_error_details (char const *name, size_t status, size_t size);
void write_fatal (char const *name) __attribute__ ((noreturn));
void write_fatal_details (char const *name, ssize_t status, size_t size)
@@ -656,12 +662,13 @@ void name_add_dir (const char *name);
void name_term (void);
const char *name_next (int change_dirs);
void name_gather (void);
struct name *addname (char const *string, int change_dir, struct name *parent);
struct name *addname (char const *string, int change_dir,
bool cmdline, struct name *parent);
bool name_match (const char *name);
void names_notfound (void);
void collect_and_sort_names (void);
struct name *name_scan (const char *name);
char *name_from_list (void);
struct name const *name_from_list (void);
void blank_name_list (void);
char *new_name (const char *dir_name, const char *name);
size_t stripped_prefix_len (char const *file_name, size_t num);
+22 -26
View File
@@ -1092,7 +1092,7 @@ dump_regular_file (int fd, struct tar_stat_info *st)
static void
dump_dir0 (char *directory,
struct tar_stat_info *st, int top_level, dev_t parent_device)
struct tar_stat_info *st, bool top_level, dev_t parent_device)
{
dev_t our_device = st->stat.st_dev;
const char *tag_file_name;
@@ -1210,7 +1210,7 @@ dump_dir0 (char *directory,
}
strcpy (name_buf + name_len, entry);
if (!excluded_name (name_buf))
dump_file (name_buf, 0, our_device);
dump_file (name_buf, false, our_device);
}
free (name_buf);
@@ -1224,7 +1224,7 @@ dump_dir0 (char *directory,
name_buf = xmalloc (name_size);
strcpy (name_buf, st->orig_file_name);
strcat (name_buf, tag_file_name);
dump_file (name_buf, 0, our_device);
dump_file (name_buf, false, our_device);
free (name_buf);
break;
@@ -1250,7 +1250,8 @@ ensure_slash (char **pstr)
}
static bool
dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
dump_dir (int fd, struct tar_stat_info *st, bool top_level,
dev_t parent_device)
{
char *directory = fdsavedir (fd);
if (!directory)
@@ -1271,7 +1272,7 @@ dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
void
create_archive (void)
{
const char *p;
struct name const *p;
open_archive (ACCESS_WRITE);
buffer_write_global_xheader ();
@@ -1285,21 +1286,21 @@ create_archive (void)
collect_and_sort_names ();
while ((p = name_from_list ()) != NULL)
if (!excluded_name (p))
dump_file (p, -1, (dev_t) 0);
if (!excluded_name (p->name))
dump_file (p->name, p->cmdline, (dev_t) 0);
blank_name_list ();
while ((p = name_from_list ()) != NULL)
if (!excluded_name (p))
if (!excluded_name (p->name))
{
size_t plen = strlen (p);
size_t plen = strlen (p->name);
if (buffer_size <= plen)
{
while ((buffer_size *= 2) <= plen)
continue;
buffer = xrealloc (buffer, buffer_size);
}
memcpy (buffer, p, plen);
memcpy (buffer, p->name, plen);
if (! ISSLASH (buffer[plen - 1]))
buffer[plen++] = DIRECTORY_SEPARATOR;
q = directory_contents (gnu_list_name->directory);
@@ -1316,7 +1317,7 @@ create_archive (void)
buffer = xrealloc (buffer, buffer_size);
}
strcpy (buffer + plen, q + 1);
dump_file (buffer, -1, (dev_t) 0);
dump_file (buffer, false, (dev_t) 0);
}
q += qlen + 1;
}
@@ -1325,9 +1326,10 @@ create_archive (void)
}
else
{
while ((p = name_next (1)) != NULL)
if (!excluded_name (p))
dump_file (p, 1, (dev_t) 0);
const char *name;
while ((name = name_next (1)) != NULL)
if (!excluded_name (name))
dump_file (name, true, (dev_t) 0);
}
write_eot ();
@@ -1475,7 +1477,6 @@ check_links (void)
}
}
/* Dump a single file, recursing on directories. P is the file name
to dump. TOP_LEVEL tells whether this is a top-level call; zero
means no, positive means yes, and negative means the top level
@@ -1487,7 +1488,7 @@ check_links (void)
static void
dump_file0 (struct tar_stat_info *st, const char *p,
int top_level, dev_t parent_device)
bool top_level, dev_t parent_device)
{
union block *header;
char type;
@@ -1508,7 +1509,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
if (deref_stat (dereference_option, p, &st->stat) != 0)
{
stat_diag (p);
file_removed_diag (p, top_level, stat_diag);
return;
}
st->archive_file_size = original_size = st->stat.st_size;
@@ -1580,12 +1581,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
: 0)));
if (fd < 0)
{
if (!top_level && errno == ENOENT)
WARNOPT (WARN_FILE_REMOVED,
(0, 0, _("%s: File removed before we read it"),
quotearg_colon (p)));
else
open_diag (p);
file_removed_diag (p, top_level, open_diag);
return;
}
}
@@ -1655,7 +1651,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
: fstat (fd, &final_stat))
!= 0)
{
stat_diag (p);
file_removed_diag (p, top_level, stat_diag);
ok = false;
}
}
@@ -1713,7 +1709,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
size = readlink (p, buffer, linklen + 1);
if (size < 0)
{
readlink_diag (p);
file_removed_diag (p, top_level, readlink_diag);
return;
}
buffer[size] = '\0';
@@ -1795,7 +1791,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
}
void
dump_file (const char *p, int top_level, dev_t parent_device)
dump_file (const char *p, bool top_level, dev_t parent_device)
{
struct tar_stat_info st;
tar_stat_init (&st);
+12 -2
View File
@@ -413,7 +413,11 @@ update_parent_directory (const char *name)
{
struct stat st;
if (deref_stat (dereference_option, p, &st) != 0)
stat_diag (name);
{
if (errno != ENOENT)
stat_diag (directory->name);
/* else: should have been already reported */
}
else
directory->mtime = get_stat_mtime (&st);
}
@@ -549,6 +553,12 @@ procdir (const char *name_buffer, struct stat *stat_data,
if (one_file_system_option && device != stat_data->st_dev
/* ... except if it was explicitely given in the command line */
&& !is_individual_file (name_buffer))
/* FIXME:
WARNOPT (WARN_XDEV,
(0, 0,
_("%s: directory is on a different filesystem; not dumped"),
quotearg_colon (directory->name)));
*/
directory->children = NO_CHILDREN;
else if (flag & PD_FORCE_CHILDREN)
{
@@ -699,7 +709,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
if (deref_stat (dereference_option, name_buffer, &stat_data))
{
stat_diag (name_buffer);
dir_removed_diag (name_buffer, false, stat_diag);
/* FIXME: used to be
children = CHANGED_CHILDREN;
but changed to: */
+24
View File
@@ -745,6 +745,30 @@ stat_diag (char const *name)
stat_error (name);
}
void
file_removed_diag (const char *name, bool top_level,
void (*diagfn) (char const *name))
{
if (!top_level && errno == ENOENT)
WARNOPT (WARN_FILE_REMOVED,
(0, 0, _("%s: File removed before we read it"),
quotearg_colon (name)));
else
diagfn (name);
}
void
dir_removed_diag (const char *name, bool top_level,
void (*diagfn) (char const *name))
{
if (!top_level && errno == ENOENT)
WARNOPT (WARN_FILE_REMOVED,
(0, 0, _("%s: Directory removed before we read it"),
quotearg_colon (name)));
else
diagfn (name);
}
void
write_fatal_details (char const *name, ssize_t status, size_t size)
{
+12 -10
View File
@@ -420,12 +420,13 @@ name_gather (void)
buffer->matching_flags = matching_flags;
buffer->directory = NULL;
buffer->parent = NULL;
buffer->cmdline = true;
namelist = buffer;
nametail = &namelist->next;
}
else if (change_dir)
addname (0, change_dir, NULL);
addname (0, change_dir, false, NULL);
}
else
{
@@ -439,11 +440,11 @@ name_gather (void)
change_dir = chdir_arg (xstrdup (ep->v.name));
if (ep)
addname (ep->v.name, change_dir, NULL);
addname (ep->v.name, change_dir, true, NULL);
else
{
if (change_dir != change_dir0)
addname (0, change_dir, NULL);
addname (NULL, change_dir, false, NULL);
break;
}
}
@@ -452,7 +453,7 @@ name_gather (void)
/* Add a name to the namelist. */
struct name *
addname (char const *string, int change_dir, struct name *parent)
addname (char const *string, int change_dir, bool cmdline, struct name *parent)
{
struct name *name = make_name (string);
@@ -463,6 +464,7 @@ addname (char const *string, int change_dir, struct name *parent)
name->change_dir = change_dir;
name->directory = NULL;
name->parent = parent;
name->cmdline = cmdline;
*nametail = name;
nametail = &name->next;
@@ -811,7 +813,7 @@ add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
namebuf = xrealloc (namebuf, allocated_length + 1);
}
strcpy (namebuf + name_length, string + 1);
np = addname (namebuf, change_dir, name);
np = addname (namebuf, change_dir, false, name);
if (!child_head)
child_head = np;
else
@@ -886,7 +888,7 @@ collect_and_sort_names (void)
name_gather ();
if (!namelist)
addname (".", 0, NULL);
addname (".", 0, false, NULL);
if (listed_incremental_option)
{
@@ -1030,8 +1032,8 @@ name_scan (const char *file_name)
find and return all the non-found names in the namelist. */
struct name *gnu_list_name;
char *
name_from_list (void)
struct name const *
name_from_list ()
{
if (!gnu_list_name)
gnu_list_name = namelist;
@@ -1042,9 +1044,9 @@ name_from_list (void)
{
gnu_list_name->found_count++;
chdir_do (gnu_list_name->change_dir);
return gnu_list_name->name;
return gnu_list_name;
}
return 0;
return NULL;
}
void
+1 -1
View File
@@ -1388,7 +1388,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
case 'K':
starting_file_option = true;
addname (arg, 0, NULL);
addname (arg, 0, true, NULL);
break;
case ONE_FILE_SYSTEM_OPTION:
+3 -3
View File
@@ -189,10 +189,10 @@ update_archive (void)
output_start = current_block->buffer;
{
char *file_name;
while ((file_name = name_from_list ()) != NULL)
struct name const *p;
while ((p = name_from_list ()) != NULL)
{
char *file_name = p->name;
if (excluded_name (file_name))
continue;
if (interactive_option && !confirm ("add", file_name))