Rewrite update algorithm.

* src/common.h (namebuf_t): New typedef.
(namebuf_create, namebuf_free)
(namebuf_name): New prototypes.
(remname): New prototype.
* src/misc.c (struct namebuf): New structure.
(namebuf_create, namebuf_free)
(namebuf_name): New functions.
* src/create.c (dup_dir0): Remove is_avoided_name
checks. This is taken care of in update_archive.
* src/incremen.c (scan_directory): Use namebuf
to produce full file names.
* src/names.c (nametail): Remove extra level of
indirection. All uses updated.
(avoided_name_table, add_avoided_name)
(is_avoided_name): Remove.
* src/update.c (update_archive): Change algorithm.
Instead of adding unmodified files to the avoided_name
table, create namelist so that it contains only
modified files.

* tests/Makefile.am: Add update01.at, update02.at
* tests/testsuite.at: Likewise.
* tests/update.at (AT_KEYWORDS): Add update00.
This commit is contained in:
Sergey Poznyakoff
2009-10-14 23:26:52 +03:00
parent 0c6c288b53
commit cac45fffc5
11 changed files with 290 additions and 140 deletions

View File

@@ -596,6 +596,11 @@ char *normalize_filename (const char *name);
void replace_prefix (char **pname, const char *samp, size_t slen,
const char *repl, size_t rlen);
typedef struct namebuf *namebuf_t;
namebuf_t namebuf_create (const char *dir);
void namebuf_free (namebuf_t buf);
char *namebuf_name (namebuf_t buf, const char *name);
void code_ns_fraction (int ns, char *p);
char const *code_timespec (struct timespec ts, char *sbuf);
enum { BILLION = 1000000000, LOG10_BILLION = 9 };
@@ -666,6 +671,7 @@ const char *name_next (int change_dirs);
void name_gather (void);
struct name *addname (char const *string, int change_dir,
bool cmdline, struct name *parent);
void remname (struct name *name);
bool name_match (const char *name);
void names_notfound (void);
void collect_and_sort_names (void);

View File

@@ -1096,73 +1096,70 @@ dump_dir0 (char *directory,
{
dev_t our_device = st->stat.st_dev;
const char *tag_file_name;
if (!is_avoided_name (st->orig_file_name))
union block *blk = NULL;
off_t block_ordinal = current_block_ordinal ();
st->stat.st_size = 0; /* force 0 size on dir */
blk = start_header (st);
if (!blk)
return;
if (incremental_option && archive_format != POSIX_FORMAT)
blk->header.typeflag = GNUTYPE_DUMPDIR;
else /* if (standard_option) */
blk->header.typeflag = DIRTYPE;
/* If we're gnudumping, we aren't done yet so don't close it. */
if (!incremental_option)
finish_header (st, blk, block_ordinal);
else if (gnu_list_name->directory)
{
union block *blk = NULL;
off_t block_ordinal = current_block_ordinal ();
st->stat.st_size = 0; /* force 0 size on dir */
blk = start_header (st);
if (!blk)
return;
if (incremental_option && archive_format != POSIX_FORMAT)
blk->header.typeflag = GNUTYPE_DUMPDIR;
else /* if (standard_option) */
blk->header.typeflag = DIRTYPE;
/* If we're gnudumping, we aren't done yet so don't close it. */
if (!incremental_option)
finish_header (st, blk, block_ordinal);
else if (gnu_list_name->directory)
if (archive_format == POSIX_FORMAT)
{
if (archive_format == POSIX_FORMAT)
{
xheader_store ("GNU.dumpdir", st,
safe_directory_contents (gnu_list_name->directory));
finish_header (st, blk, block_ordinal);
}
else
{
off_t size_left;
off_t totsize;
size_t bufsize;
ssize_t count;
const char *buffer, *p_buffer;
block_ordinal = current_block_ordinal ();
buffer = safe_directory_contents (gnu_list_name->directory);
totsize = dumpdir_size (buffer);
OFF_TO_CHARS (totsize, blk->header.size);
finish_header (st, blk, block_ordinal);
p_buffer = buffer;
size_left = totsize;
mv_begin (st);
mv_total_size (totsize);
while (size_left > 0)
{
mv_size_left (size_left);
blk = find_next_block ();
bufsize = available_space_after (blk);
if (size_left < bufsize)
{
bufsize = size_left;
count = bufsize % BLOCKSIZE;
if (count)
memset (blk->buffer + size_left, 0, BLOCKSIZE - count);
}
memcpy (blk->buffer, p_buffer, bufsize);
size_left -= bufsize;
p_buffer += bufsize;
set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
}
mv_end ();
}
return;
xheader_store ("GNU.dumpdir", st,
safe_directory_contents (gnu_list_name->directory));
finish_header (st, blk, block_ordinal);
}
else
{
off_t size_left;
off_t totsize;
size_t bufsize;
ssize_t count;
const char *buffer, *p_buffer;
block_ordinal = current_block_ordinal ();
buffer = safe_directory_contents (gnu_list_name->directory);
totsize = dumpdir_size (buffer);
OFF_TO_CHARS (totsize, blk->header.size);
finish_header (st, blk, block_ordinal);
p_buffer = buffer;
size_left = totsize;
mv_begin (st);
mv_total_size (totsize);
while (size_left > 0)
{
mv_size_left (size_left);
blk = find_next_block ();
bufsize = available_space_after (blk);
if (size_left < bufsize)
{
bufsize = size_left;
count = bufsize % BLOCKSIZE;
if (count)
memset (blk->buffer + size_left, 0, BLOCKSIZE - count);
}
memcpy (blk->buffer, p_buffer, bufsize);
size_left -= bufsize;
p_buffer += bufsize;
set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
}
mv_end ();
}
return;
}
if (!recursion_option)
@@ -1557,9 +1554,6 @@ dump_file0 (struct tar_stat_info *st, const char *p,
return;
}
if (is_avoided_name (p))
return;
is_dir = S_ISDIR (st->stat.st_mode) != 0;
if (!is_dir && dump_hard_link (st))

View File

@@ -692,9 +692,8 @@ struct directory *
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_buffer_size; /* allocated size of name_buffer, minus 2 */
size_t name_length; /* used length in name_buffer */
namebuf_t nbuf;
char *tmp;
struct stat stat_data;
struct directory *directory;
char ch;
@@ -702,35 +701,28 @@ scan_directory (char *dir, dev_t device, bool cmdline)
if (! dirp)
savedir_error (dir);
name_buffer_size = strlen (dir) + NAME_FIELD_SIZE;
name_buffer = xmalloc (name_buffer_size + 2);
strcpy (name_buffer, dir);
zap_slashes (name_buffer);
tmp = xstrdup (dir);
zap_slashes (tmp);
if (deref_stat (dereference_option, name_buffer, &stat_data))
if (deref_stat (dereference_option, tmp, &stat_data))
{
dir_removed_diag (name_buffer, cmdline, stat_diag);
free (name_buffer);
dir_removed_diag (tmp, cmdline, stat_diag);
free (tmp);
free (dirp);
return NULL;
}
directory = procdir (name_buffer, &stat_data, device,
directory = procdir (tmp, &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;
}
free (tmp);
nbuf = namebuf_create (dir);
if (dirp && directory->children != NO_CHILDREN)
{
char *entry; /* directory entry being scanned */
size_t entrylen; /* length of directory entry */
dumpdir_iter_t itr;
makedumpdir (directory, dirp);
@@ -739,25 +731,17 @@ scan_directory (char *dir, dev_t device, bool cmdline)
entry;
entry = dumpdir_next (itr))
{
entrylen = strlen (entry);
if (name_buffer_size <= entrylen - 1 + name_length)
{
do
name_buffer_size += NAME_FIELD_SIZE;
while (name_buffer_size <= entrylen - 1 + name_length);
name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
}
strcpy (name_buffer + name_length, entry + 1);
char *full_name = namebuf_name (nbuf, entry + 1);
if (*entry == 'I') /* Ignored entry */
*entry = 'N';
else if (excluded_name (name_buffer))
else if (excluded_name (full_name))
*entry = 'N';
else
{
if (deref_stat (dereference_option, name_buffer, &stat_data))
if (deref_stat (dereference_option, full_name, &stat_data))
{
file_removed_diag (name_buffer, false, stat_diag);
file_removed_diag (full_name, false, stat_diag);
*entry = 'N';
continue;
}
@@ -770,7 +754,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
else if (directory->children == ALL_CHILDREN)
pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN;
*entry = 'D';
procdir (name_buffer, &stat_data, device, pd_flag, entry);
procdir (full_name, &stat_data, device, pd_flag, entry);
}
else if (one_file_system_option && device != stat_data.st_dev)
@@ -792,7 +776,8 @@ scan_directory (char *dir, dev_t device, bool cmdline)
free (itr);
}
free (name_buffer);
namebuf_free (nbuf);
if (dirp)
free (dirp);

View File

@@ -827,3 +827,44 @@ page_aligned_alloc (void **ptr, size_t size)
return ptr_align (*ptr, alignment);
}
struct namebuf
{
char *buffer; /* directory, `/', and directory member */
size_t buffer_size; /* allocated size of name_buffer */
size_t dir_length; /* length of directory part in buffer */
};
namebuf_t
namebuf_create (const char *dir)
{
namebuf_t buf = xmalloc (sizeof (*buf));
buf->buffer_size = strlen (dir) + 2;
buf->buffer = xmalloc (buf->buffer_size);
strcpy (buf->buffer, dir);
buf->dir_length = strlen (buf->buffer);
if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
buf->buffer[buf->dir_length++] = DIRECTORY_SEPARATOR;
return buf;
}
void
namebuf_free (namebuf_t buf)
{
free (buf->buffer);
free (buf);
}
char *
namebuf_name (namebuf_t buf, const char *name)
{
size_t len = strlen (name);
while (buf->dir_length + len + 1 >= buf->buffer_size)
buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
strcpy (buf->buffer + buf->dir_length, name);
return buf->buffer;
}

View File

@@ -205,7 +205,7 @@ free_name (struct name *p)
/* Names from the command call. */
static struct name *namelist; /* first name in list, if any */
static struct name **nametail = &namelist; /* end of name list */
static struct name *nametail; /* end of name list */
/* File name arguments are processed in two stages: first a
name_array (see below) is filled, then the names from it
@@ -422,8 +422,7 @@ name_gather (void)
buffer->parent = NULL;
buffer->cmdline = true;
namelist = buffer;
nametail = &namelist->next;
namelist = nametail = buffer;
}
else if (change_dir)
addname (0, change_dir, false, NULL);
@@ -457,7 +456,7 @@ addname (char const *string, int change_dir, bool cmdline, struct name *parent)
{
struct name *name = make_name (string);
name->prev = *nametail;
name->prev = nametail;
name->next = NULL;
name->found_count = 0;
name->matching_flags = matching_flags;
@@ -465,9 +464,12 @@ addname (char const *string, int change_dir, bool cmdline, struct name *parent)
name->directory = NULL;
name->parent = parent;
name->cmdline = cmdline;
*nametail = name;
nametail = &name->next;
if (nametail)
nametail->next = name;
else
namelist = name;
nametail = name;
return name;
}
@@ -501,7 +503,7 @@ remname (struct name *name)
if ((p = name->next) != NULL)
p->prev = name->prev;
else
nametail = &name->prev;
nametail = name->prev;
}
/* Return true if and only if name FILE_NAME (from an archive) matches any
@@ -521,8 +523,8 @@ name_match (const char *file_name)
if (cursor->name[0] == 0)
{
chdir_do (cursor->change_dir);
namelist = 0;
nametail = &namelist;
namelist = NULL;
nametail = NULL;
return true;
}
@@ -535,8 +537,8 @@ name_match (const char *file_name)
if (starting_file_option)
{
free (namelist);
namelist = 0;
nametail = &namelist;
namelist = NULL;
nametail = NULL;
}
chdir_do (cursor->change_dir);
@@ -627,8 +629,8 @@ names_notfound (void)
}
/* Don't bother freeing the name list; we're about to exit. */
namelist = 0;
nametail = &namelist;
namelist = NULL;
nametail = NULL;
if (same_order_option)
{
@@ -975,7 +977,7 @@ collect_and_sort_names (void)
prev_name = name;
num_names++;
}
nametail = &prev_name;
nametail = prev_name;
hash_free (nametab);
namelist = merge_sort (namelist, num_names, compare_names_found);
@@ -1074,24 +1076,6 @@ excluded_name (char const *name)
{
return excluded_file_name (excluded, name + FILE_SYSTEM_PREFIX_LEN (name));
}
/* Names to avoid dumping. */
static Hash_table *avoided_name_table;
/* Remember to not archive NAME. */
void
add_avoided_name (char const *name)
{
hash_string_insert (&avoided_name_table, name);
}
/* Should NAME be avoided when archiving? */
bool
is_avoided_name (char const *name)
{
return hash_string_lookup (avoided_name_table, name);
}
static Hash_table *individual_file_table;

View File

@@ -137,13 +137,35 @@ update_archive (void)
chdir_do (name->change_dir);
if (deref_stat (dereference_option,
current_stat_info.file_name, &s) == 0
&& (tar_timespec_cmp (get_stat_mtime (&s),
current_stat_info.mtime)
<= 0))
add_avoided_name (current_stat_info.file_name);
current_stat_info.file_name, &s) == 0)
{
if (S_ISDIR (s.st_mode))
{
char *p, *dirp;
dirp = savedir (name->name);
if (!dirp)
savedir_error (name->name);
else
{
namebuf_t nbuf = namebuf_create (name->name);
for (p = dirp; *p; p += strlen (p) + 1)
addname (namebuf_name (nbuf, p),
0, false, NULL);
namebuf_free (nbuf);
free (dirp);
remname (name);
}
}
else if (tar_timespec_cmp (get_stat_mtime (&s),
current_stat_info.mtime)
<= 0)
remname (name);
}
}
skip_member ();
break;
}

View File

@@ -130,6 +130,8 @@ TESTSUITE_AT = \
spmvp10.at\
truncate.at\
update.at\
update01.at\
update02.at\
volsize.at\
volume.at\
verbose.at\

View File

@@ -207,6 +207,8 @@ m4_include([spmvp01.at])
m4_include([spmvp10.at])
m4_include([update.at])
m4_include([update01.at])
m4_include([update02.at])
m4_include([volume.at])
m4_include([volsize.at])

View File

@@ -23,9 +23,10 @@
# References: <42AB0D28.6030706@mein-horde.de>
# by Martin Lohmeier <martin@mein-horde.de>
# on Sat, 11 Jun 2005 18:11:20 +0200
# http://lists.gnu.org/archive/html/bug-tar/2005-06/msg00024.html
AT_SETUP([update unchanged directories])
AT_KEYWORDS([update])
AT_KEYWORDS([update update00])
AT_TAR_CHECK([
AT_SORT_PREREQ

58
tests/update01.at Normal file
View File

@@ -0,0 +1,58 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
# Copyright (C) 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 Free Software Foundation; either version 3, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# 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., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# Description: If dir is a directory and arc is a tar archive which
# contains that directory, and dir contains some modifications added
# after adding it to the archive, then `tar -u dir' would add dir/ to
# the archive.
# Last-Affected-Version: 1.22.90
# References: <4AD4E703.80500@teclabs.eu>
# http://lists.gnu.org/archive/html/bug-tar/2009-10/msg00017.html
AT_SETUP([update directories])
AT_KEYWORDS([update update01])
AT_TAR_CHECK([
AT_SORT_PREREQ
mkdir a
genfile --file a/b
tar cf arc a
echo "separator"
sleep 2
genfile --file a/c
tar ufv arc a
echo "separator"
tar tf arc | sort || exit 1
],
[0],
[separator
a/c
separator
a/
a/b
a/c
])
AT_CLEANUP

55
tests/update02.at Normal file
View File

@@ -0,0 +1,55 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
# Copyright (C) 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 Free Software Foundation; either version 3, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# 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., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# Description: See update01.at
# Last-Affected-Version: 1.22.90
# References: <4AD4E703.80500@teclabs.eu>
# http://lists.gnu.org/archive/html/bug-tar/2009-10/msg00017.html
AT_SETUP([update changed files])
AT_KEYWORDS([update update02])
AT_TAR_CHECK([
AT_SORT_PREREQ
mkdir a
genfile --file a/b
tar cf arc a
echo "separator"
sleep 2
touch a/b
tar ufv arc a
echo "separator"
tar tf arc | sort || exit 1
],
[0],
[separator
a/b
separator
a/
a/b
a/b
])
AT_CLEANUP