mirror of
https://git.savannah.gnu.org/git/tar.git
synced 2026-06-01 04:36:26 +00:00
Optimize searches for directory structures by keeping a pointer to struct directory in struct name.
* src/common.h (struct name): New member `directory' replaces dir_contents. Rearrange members. (rebase_directory): Change signature. (scan_directory): Change signature. (name_fill_directory) (directory_contents, safe_directory_contents): New prototypes. (append_incremental_renames): Change signature. (replace_prefix): New proto. * src/compare.c (diff_dumpdir): Use directory_contents + scan_directory. * src/create.c * src/incremen.c (replace_prefix): Move to misc.c (rebase_directory): Rewrite. (scan_directory): Return pointer to struct directory. (directory_contents, safe_directory_contents): New functions. (get_directory_contents): Remove. (name_fill_directory): New function. (append_incremental_renames): Rewrite. This also fixes a memory leak. * src/names.c (name_gather, addname): Reflect changes in struct name. (add_hierarchy_to_namelist): Rewrite using name_fill_directory and directory_contents. (rebase_child_list): Update call to rebase_directory. (collect_and_sort_names): Optimize * src/misc.c (replace_prefix): New function. * src/names.c (add_hierarchy_to_namelist): Use new get_directory_contents. * tests/incr05.at: New test case. * tests/incr06.at: New test case. * tests/Makefile.am, test/testsuite.at: Add incr05.at and incr06.at. * doc/Makefile.am (check-options): Improve rule. * doc/tar.texi, NEWS: Update.
This commit is contained in:
39
src/common.h
39
src/common.h
@@ -320,26 +320,32 @@ GLOBAL const char **archive_name_cursor;
|
||||
/* Output index file name. */
|
||||
GLOBAL char const *index_file_name;
|
||||
|
||||
/* Opaque structure for keeping directory meta-data */
|
||||
struct directory;
|
||||
|
||||
/* Structure for keeping track of filenames and lists thereof. */
|
||||
struct name
|
||||
{
|
||||
struct name *next; /* Link to the next element */
|
||||
struct name *prev; /* Link to the previous element */
|
||||
|
||||
char *name; /* File name or globbing pattern */
|
||||
size_t length; /* cached strlen (name) */
|
||||
int matching_flags; /* wildcard flags if name is a pattern */
|
||||
|
||||
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
|
||||
been found */
|
||||
int matching_flags; /* this name is a regexp, not literal */
|
||||
|
||||
size_t length; /* cached strlen(name) */
|
||||
char *name;
|
||||
|
||||
/* The following members are used for incremental dumps only */
|
||||
char const *dir_contents; /* directory contents */
|
||||
/* The following members are used for incremental dumps only,
|
||||
if this struct name represents a directory;
|
||||
see incremen.c */
|
||||
struct directory *directory;/* directory meta-data and contents */
|
||||
struct name *parent; /* pointer to the parent hierarchy */
|
||||
struct name *child; /* pointer to the first child */
|
||||
struct name *sibling; /* pointer to the next sibling */
|
||||
char *caname; /* canonical name */
|
||||
char *caname; /* canonical name */
|
||||
};
|
||||
|
||||
/* Obnoxious test to see if dimwit is trying to dump the archive. */
|
||||
@@ -505,16 +511,21 @@ char *dumpdir_locate (dumpdir_t dump, const char *name);
|
||||
char *dumpdir_next (dumpdir_iter_t itr);
|
||||
char *dumpdir_first (dumpdir_t dump, int all, dumpdir_iter_t *pitr);
|
||||
|
||||
const char *scan_directory (char *dir_name, dev_t device, bool cmdline);
|
||||
const char *append_incremental_renames (const char *dump);
|
||||
struct directory *scan_directory (char *dir, dev_t device, bool cmdline);
|
||||
void name_fill_directory (struct name *name, dev_t device, bool cmdline);
|
||||
const char *directory_contents (struct directory *dir);
|
||||
const char *safe_directory_contents (struct directory *dir);
|
||||
|
||||
void rebase_directory (struct directory *dir,
|
||||
const char *samp, size_t slen,
|
||||
const char *repl, size_t rlen);
|
||||
|
||||
void append_incremental_renames (struct directory *dir);
|
||||
void read_directory_file (void);
|
||||
void write_directory_file (void);
|
||||
void purge_directory (char const *directory_name);
|
||||
void list_dumpdir (char *buffer, size_t size);
|
||||
void update_parent_directory (const char *name);
|
||||
void rebase_directory (const char *name, size_t old_prefix_len,
|
||||
const char *old_prefix,
|
||||
const char *new_prefix);
|
||||
|
||||
size_t dumpdir_size (const char *p);
|
||||
bool is_dumpdir (struct tar_stat_info *stat_info);
|
||||
@@ -558,7 +569,7 @@ off_t off_from_header (const char *buf, size_t size);
|
||||
size_t size_from_header (const char *buf, size_t size);
|
||||
time_t time_from_header (const char *buf, size_t size);
|
||||
uid_t uid_from_header (const char *buf, size_t size);
|
||||
uintmax_t uintmax_from_header (const char * buf, size_t size);
|
||||
uintmax_t uintmax_from_header (const char *buf, size_t size);
|
||||
|
||||
void list_archive (void);
|
||||
void print_for_mkdir (char *dirname, int length, mode_t mode);
|
||||
@@ -578,6 +589,8 @@ char *quote_copy_string (const char *str);
|
||||
int unquote_string (char *str);
|
||||
char *zap_slashes (char *name);
|
||||
char *normalize_filename (const char *name);
|
||||
void replace_prefix (char **pname, const char *samp, size_t slen,
|
||||
const char *repl, size_t rlen);
|
||||
|
||||
void code_ns_fraction (int ns, char *p);
|
||||
char const *code_timespec (struct timespec ts, char *sbuf);
|
||||
|
||||
@@ -380,7 +380,8 @@ diff_dumpdir (void)
|
||||
else
|
||||
dev = stat_data.st_dev;
|
||||
|
||||
dumpdir_buffer = scan_directory (current_stat_info.file_name, dev, false);
|
||||
dumpdir_buffer = directory_contents
|
||||
(scan_directory (current_stat_info.file_name, dev, false));
|
||||
|
||||
if (dumpdir_buffer)
|
||||
{
|
||||
|
||||
14
src/create.c
14
src/create.c
@@ -1116,11 +1116,12 @@ dump_dir0 (char *directory,
|
||||
|
||||
if (!incremental_option)
|
||||
finish_header (st, blk, block_ordinal);
|
||||
else if (gnu_list_name->dir_contents)
|
||||
else if (gnu_list_name->directory)
|
||||
{
|
||||
if (archive_format == POSIX_FORMAT)
|
||||
{
|
||||
xheader_store ("GNU.dumpdir", st, gnu_list_name->dir_contents);
|
||||
xheader_store ("GNU.dumpdir", st,
|
||||
safe_directory_contents (gnu_list_name->directory));
|
||||
finish_header (st, blk, block_ordinal);
|
||||
}
|
||||
else
|
||||
@@ -1132,11 +1133,8 @@ dump_dir0 (char *directory,
|
||||
const char *buffer, *p_buffer;
|
||||
|
||||
block_ordinal = current_block_ordinal ();
|
||||
buffer = gnu_list_name->dir_contents;
|
||||
if (buffer)
|
||||
totsize = dumpdir_size (buffer);
|
||||
else
|
||||
totsize = 0;
|
||||
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;
|
||||
@@ -1304,7 +1302,7 @@ create_archive (void)
|
||||
memcpy (buffer, p, plen);
|
||||
if (! ISSLASH (buffer[plen - 1]))
|
||||
buffer[plen++] = DIRECTORY_SEPARATOR;
|
||||
q = gnu_list_name->dir_contents;
|
||||
q = directory_contents (gnu_list_name->directory);
|
||||
if (q)
|
||||
while (*q)
|
||||
{
|
||||
|
||||
@@ -290,24 +290,6 @@ attach_directory (const char *name)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -389,18 +371,15 @@ remove_directory (const char *caname)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Find a directory entry for NAME. If first OLD_PREFIX_LEN
|
||||
bytes of its name match OLD_PREFIX, replace them with
|
||||
NEW_PREFIX. */
|
||||
/* If first OLD_PREFIX_LEN bytes of DIR->NAME 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)
|
||||
rebase_directory (struct directory *dir,
|
||||
const char *old_prefix, size_t old_prefix_len,
|
||||
const char *new_prefix, size_t new_prefix_len)
|
||||
{
|
||||
struct directory *dir = find_directory (name);
|
||||
if (dir)
|
||||
replace_prefix (&dir->name, old_prefix, old_prefix_len,
|
||||
new_prefix, strlen (new_prefix));
|
||||
replace_prefix (&dir->name, old_prefix, old_prefix_len,
|
||||
new_prefix, new_prefix_len);
|
||||
}
|
||||
|
||||
/* Return a directory entry for a given combination of device and inode
|
||||
@@ -696,8 +675,10 @@ makedumpdir (struct directory *directory, const char *dir)
|
||||
/* 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 *
|
||||
command line.
|
||||
Unless *PDIR is NULL, store there a pointer to the struct directory
|
||||
describing DIR. */
|
||||
struct directory *
|
||||
scan_directory (char *dir, dev_t device, bool cmdline)
|
||||
{
|
||||
char *dirp = savedir (dir); /* for scanning directory */
|
||||
@@ -730,7 +711,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
|
||||
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]))
|
||||
{
|
||||
@@ -808,13 +789,30 @@ scan_directory (char *dir, dev_t device, bool cmdline)
|
||||
if (dirp)
|
||||
free (dirp);
|
||||
|
||||
return directory->dump ? directory->dump->contents : NULL;
|
||||
return directory;
|
||||
}
|
||||
|
||||
/* Return pointer to the contents of the directory DIR */
|
||||
const char *
|
||||
get_directory_contents (char *dir, dev_t device, bool force)
|
||||
directory_contents (struct directory *dir)
|
||||
{
|
||||
return scan_directory (dir, device, force);
|
||||
if (!dir)
|
||||
return NULL;
|
||||
return dir->dump ? dir->dump->contents : NULL;
|
||||
}
|
||||
|
||||
/* A "safe" version of directory_contents, which never returns NULL. */
|
||||
const char *
|
||||
safe_directory_contents (struct directory *dir)
|
||||
{
|
||||
const char *ret = directory_contents (dir);
|
||||
return ret ? ret : "\0\0\0\0";
|
||||
}
|
||||
|
||||
void
|
||||
name_fill_directory (struct name *name, dev_t device, bool cmdline)
|
||||
{
|
||||
name->directory = scan_directory (name->name, device, cmdline);
|
||||
}
|
||||
|
||||
|
||||
@@ -877,17 +875,19 @@ store_rename (struct directory *dir, struct obstack *stk)
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
append_incremental_renames (const char *dump)
|
||||
void
|
||||
append_incremental_renames (struct directory *dir)
|
||||
{
|
||||
struct obstack stk;
|
||||
size_t size;
|
||||
struct directory *dp;
|
||||
const char *dump;
|
||||
|
||||
if (dirhead == NULL)
|
||||
return dump;
|
||||
return;
|
||||
|
||||
obstack_init (&stk);
|
||||
dump = directory_contents (dir);
|
||||
if (dump)
|
||||
{
|
||||
size = dumpdir_size (dump) - 1;
|
||||
@@ -902,11 +902,10 @@ append_incremental_renames (const char *dump)
|
||||
if (obstack_object_size (&stk) != size)
|
||||
{
|
||||
obstack_1grow (&stk, 0);
|
||||
dump = obstack_finish (&stk);
|
||||
dumpdir_free (dir->dump);
|
||||
dir->dump = dumpdir_create (obstack_finish (&stk));
|
||||
}
|
||||
else
|
||||
obstack_free (&stk, NULL);
|
||||
return dump;
|
||||
obstack_free (&stk, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
20
src/misc.c
20
src/misc.c
@@ -236,6 +236,25 @@ normalize_filename (const char *name)
|
||||
return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING));
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handling numbers. */
|
||||
|
||||
@@ -777,3 +796,4 @@ page_aligned_alloc (void **ptr, size_t size)
|
||||
*ptr = xmalloc (size1);
|
||||
return ptr_align (*ptr, alignment);
|
||||
}
|
||||
|
||||
|
||||
25
src/names.c
25
src/names.c
@@ -418,7 +418,7 @@ name_gather (void)
|
||||
buffer->next = 0;
|
||||
buffer->found_count = 0;
|
||||
buffer->matching_flags = matching_flags;
|
||||
buffer->dir_contents = NULL;
|
||||
buffer->directory = NULL;
|
||||
buffer->parent = NULL;
|
||||
|
||||
namelist = buffer;
|
||||
@@ -461,7 +461,7 @@ addname (char const *string, int change_dir, struct name *parent)
|
||||
name->found_count = 0;
|
||||
name->matching_flags = matching_flags;
|
||||
name->change_dir = change_dir;
|
||||
name->dir_contents = NULL;
|
||||
name->directory = NULL;
|
||||
name->parent = parent;
|
||||
|
||||
*nametail = name;
|
||||
@@ -767,12 +767,11 @@ compare_names (struct name const *n1, struct name const *n2)
|
||||
static void
|
||||
add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
|
||||
{
|
||||
char *file_name = name->name;
|
||||
const char *buffer = scan_directory (file_name, device, cmdline);
|
||||
const char *buffer;
|
||||
|
||||
if (! buffer)
|
||||
name->dir_contents = "\0\0\0\0";
|
||||
else
|
||||
name_fill_directory (name, device, cmdline);
|
||||
buffer = directory_contents (name->directory);
|
||||
if (buffer)
|
||||
{
|
||||
struct name *child_head = NULL, *child_tail = NULL;
|
||||
size_t name_length = name->length;
|
||||
@@ -785,8 +784,7 @@ add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
|
||||
size_t string_length;
|
||||
int change_dir = name->change_dir;
|
||||
|
||||
name->dir_contents = buffer;
|
||||
strcpy (namebuf, file_name);
|
||||
strcpy (namebuf, name->name);
|
||||
if (! ISSLASH (namebuf[name_length - 1]))
|
||||
{
|
||||
namebuf[name_length++] = '/';
|
||||
@@ -866,8 +864,9 @@ rebase_child_list (struct name *child, struct name *parent)
|
||||
child->name = newp;
|
||||
child->length = size;
|
||||
|
||||
rebase_directory (child->name, old_prefix_len, child->parent->name,
|
||||
new_prefix);
|
||||
rebase_directory (child->directory,
|
||||
child->parent->name, old_prefix_len,
|
||||
new_prefix, new_prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,7 +914,7 @@ collect_and_sort_names (void)
|
||||
num_names = 0;
|
||||
for (name = namelist; name; name = name->next, num_names++)
|
||||
{
|
||||
if (name->found_count || name->dir_contents)
|
||||
if (name->found_count || name->directory)
|
||||
continue;
|
||||
if (name->matching_flags & EXCLUDE_WILDCARDS)
|
||||
/* NOTE: EXCLUDE_ANCHORED is not relevant here */
|
||||
@@ -990,7 +989,7 @@ collect_and_sort_names (void)
|
||||
for (name = namelist; name && name->name[0] == 0; name++)
|
||||
;
|
||||
if (name)
|
||||
name->dir_contents = append_incremental_renames (name->dir_contents);
|
||||
append_incremental_renames (name->directory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user