mirror of
https://git.savannah.gnu.org/git/tar.git
synced 2026-04-28 20:27:06 +00:00
Improve performance of relative opendir
* src/misc.c (fdbase_opendir): When the new directory is a subdirectory of the old one, open relative to the old one rather than relative all the way back to chdir_fd, and if that open fails for a non-EMFILE reason, keep rather than discard the old directory.
This commit is contained in:
76
src/misc.c
76
src/misc.c
@@ -1134,7 +1134,8 @@ chdir_id (void)
|
||||
/* Caches of recent calls to fdbase and fdbase1. */
|
||||
static struct fdbase_cache
|
||||
{
|
||||
/* Length of subdirectory name. If zero, no subdir is cached here:
|
||||
/* Length of subdirectory name, which need not be null-terminated.
|
||||
If the length is zero, no subdir is cached here:
|
||||
SUBDIR (if nonnull) is merely a buffer available for use later,
|
||||
and CHDIR_CURRENT and FD are irrelevant. */
|
||||
idx_t subdirlen;
|
||||
@@ -1202,27 +1203,70 @@ fdbase_opendir (char const *file_name, bool alternate)
|
||||
return (struct fdbase) { .fd = chdir_fd, .base = name };
|
||||
|
||||
struct fdbase_cache *c = &fdbase_cache[alternate];
|
||||
int fd = c->fd;
|
||||
bool submatch = (0 < c->subdirlen && c->subdirlen <= subdirlen
|
||||
&& c->chdir_current == chdir_current
|
||||
&& !ISSLASH (name[c->subdirlen])
|
||||
&& memeq (c->subdir, name, c->subdirlen));
|
||||
|
||||
if (! (c->chdir_current == chdir_current
|
||||
&& c->subdirlen == subdirlen
|
||||
&& memeq (c->subdir, name, subdirlen)))
|
||||
if (! (submatch && c->subdirlen == subdirlen))
|
||||
{
|
||||
if (c->subdirlen && 0 <= c->fd)
|
||||
close (c->fd);
|
||||
|
||||
c->chdir_current = chdir_current;
|
||||
/* Copy the new directory's name into the cache. */
|
||||
char *subdir = c->subdir;
|
||||
if (c->subdiralloc <= subdirlen)
|
||||
c->subdir = xpalloc (c->subdir, &c->subdiralloc,
|
||||
subdirlen - c->subdiralloc + 1, -1, 1);
|
||||
char *p = mempcpy (c->subdir, name, subdirlen);
|
||||
c->subdir = subdir = xpalloc (subdir, &c->subdiralloc,
|
||||
subdirlen - c->subdiralloc + 1, -1, 1);
|
||||
char *p = mempcpy (subdir, name, subdirlen);
|
||||
*p = '\0';
|
||||
c->fd = openat (chdir_fd, c->subdir, open_searchdir_flags);
|
||||
c->subdirlen = c->fd < 0 ? 0 : subdirlen;
|
||||
if (BADFD != -1 && c->fd < 0)
|
||||
c->fd = BADFD;
|
||||
|
||||
if (submatch && c->subdirlen < subdirlen
|
||||
&& !ISSLASH (subdir[c->subdirlen]))
|
||||
{
|
||||
/* The new directory is a subdirectory of the old,
|
||||
so open relative to FD rather than to chdir_fd. */
|
||||
int subfd = openat (fd, &subdir[c->subdirlen], open_searchdir_flags);
|
||||
if (subfd < 0)
|
||||
{
|
||||
/* Keep the old directory cached and report open failure,
|
||||
unless EMFILE means it's possible that falling
|
||||
through to close the old directory would mean we
|
||||
could successfully retry from the chdir_fd level.
|
||||
When reporting failure, there is no need to
|
||||
null-terminate the old directory, since the code does
|
||||
not assume null termination. */
|
||||
if (errno != EMFILE)
|
||||
return (struct fdbase) { .fd = BADFD, .base = base };
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Replace the old directory with the new one. */
|
||||
close (fd);
|
||||
c->fd = subfd;
|
||||
c->subdirlen = subdirlen;
|
||||
return (struct fdbase) { .fd = subfd, .base = base };
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove any old directory info,
|
||||
and add new info if the new directory can be opened. */
|
||||
if (0 < c->subdirlen)
|
||||
close (fd);
|
||||
fd = openat (chdir_fd, c->subdir, open_searchdir_flags);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (BADFD != -1 && fd < 0)
|
||||
fd = BADFD;
|
||||
c->subdirlen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->chdir_current = chdir_current;
|
||||
c->fd = fd;
|
||||
c->subdirlen = subdirlen;
|
||||
}
|
||||
}
|
||||
|
||||
return (struct fdbase) { .fd = c->fd, .base = base };
|
||||
return (struct fdbase) { .fd = fd, .base = base };
|
||||
}
|
||||
|
||||
struct fdbase
|
||||
|
||||
Reference in New Issue
Block a user