mirror of
https://git.savannah.gnu.org/git/tar.git
synced 2026-08-16 22:36:05 +00:00
tar: reuse FD when extracting hard links
* src/extract.c (rename_directory): Redo to accommodate new semantics of fdbase1, and to use fds more intelligently. * src/misc.c (fdbase_opendir): If ALTERNATE, it is now OK to use, though not update, the main cache. This makes it possible for the caller to do linkat (N, "A", N, "B") instead of the linkat (N, "A", N + 1, "B") it previously did, and this saves some open and close calls on directories.
This commit is contained in:
+8
-5
@@ -2147,14 +2147,14 @@ extract_finish (void)
|
||||
bool
|
||||
rename_directory (char *src, char *dst)
|
||||
{
|
||||
struct fdbase f1 = fdbase1 (src);
|
||||
struct fdbase f = f1.fd == BADFD ? f1 : fdbase (dst);
|
||||
if (f.fd != BADFD && renameat (f1.fd, f1.base, f.fd, f.base) == 0)
|
||||
struct fdbase f = fdbase (src);
|
||||
struct fdbase f1 = f.fd == BADFD ? f : fdbase1 (dst);
|
||||
if (f1.fd != BADFD && renameat (f.fd, f.base, f1.fd, f1.base) == 0)
|
||||
{
|
||||
fdbase_clear ();
|
||||
fixup_delayed_set_stat (src, dst);
|
||||
}
|
||||
else if (f1.fd != BADFD)
|
||||
else if (f.fd != BADFD)
|
||||
{
|
||||
int e = errno;
|
||||
|
||||
@@ -2163,8 +2163,11 @@ rename_directory (char *src, char *dst)
|
||||
case ENOENT:
|
||||
if (0 <= make_directories (dst, MAKEDIR_PARENT))
|
||||
{
|
||||
/* make_directories likely cached DST and evicted SRC,
|
||||
so try to reuse the cached DST and then reget SRC. */
|
||||
f = fdbase (dst);
|
||||
if (f.fd != BADFD
|
||||
f1 = f.fd == BADFD ? f : fdbase1 (src);
|
||||
if (f1.fd != BADFD
|
||||
&& renameat (f1.fd, f1.base, f.fd, f.base) == 0)
|
||||
{
|
||||
fdbase_clear ();
|
||||
|
||||
+60
-47
@@ -1278,7 +1278,9 @@ open_subdir (int fd, char const *subdir, int oflags)
|
||||
|
||||
/* Return an fd open to a directory related to FILE_NAME
|
||||
along with the corresponding base name.
|
||||
Use the alternate cache if ALTERNATE, the main cache otherwise.
|
||||
If ALTERNATE, use either the main or the alternate cache but update
|
||||
only the alternate cache; otherwise, use and update only the main cache;
|
||||
this means a call with ALTERNATE cannot invalidate a call without.
|
||||
If FILE_NAME is relative, it is relative to chdir_fd.
|
||||
If CHILD_OFLAGS, open FILE_NAME itself, posssibly letting it escape from
|
||||
chdir_fd; otherwise, open FILE_NAME's parent but do not let it escape.
|
||||
@@ -1321,63 +1323,74 @@ fdbase_opendir (char const *file_name, bool alternate, int child_oflags)
|
||||
return (struct fdbase) { .fd = BADFD, .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));
|
||||
int fd;
|
||||
|
||||
if (! (submatch && c->subdirlen == subdirlen))
|
||||
/* Try to reuse fdbase_cache[0] or (if ALTERNATE) fdbase_cache[1]. */
|
||||
for (struct fdbase_cache *c = fdbase_cache; ; c++)
|
||||
{
|
||||
/* Copy the new directory's name into the cache. */
|
||||
char *subdir = c->subdir;
|
||||
if (c->subdiralloc <= subdirlen)
|
||||
c->subdir = subdir = xpalloc (subdir, &c->subdiralloc,
|
||||
subdirlen - c->subdiralloc + 1, -1, 1);
|
||||
char *p = mempcpy (subdir, name, subdirlen);
|
||||
*p = '\0';
|
||||
|
||||
if (submatch && c->subdirlen < subdirlen
|
||||
&& !ISSLASH (subdir[c->subdirlen]))
|
||||
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 (submatch && c->subdirlen == subdirlen)
|
||||
break;
|
||||
if (c == fdbase_cache + alternate)
|
||||
{
|
||||
/* The new directory is a subdirectory of the old,
|
||||
so open relative to FD rather than to chdir_fd. */
|
||||
int subfd = open_subdir (fd, &subdir[c->subdirlen], child_oflags);
|
||||
if (subfd < 0)
|
||||
/* Cannot reuse, so evict and reget fdbase_cache[ALTERNATE].
|
||||
Start by copying the new directory's name into the cache. */
|
||||
char *subdir = c->subdir;
|
||||
if (c->subdiralloc <= subdirlen)
|
||||
c->subdir = subdir = xpalloc (subdir, &c->subdiralloc,
|
||||
subdirlen - c->subdiralloc + 1,
|
||||
-1, 1);
|
||||
char *p = mempcpy (subdir, name, subdirlen);
|
||||
*p = '\0';
|
||||
|
||||
if (submatch && c->subdirlen < subdirlen
|
||||
&& !ISSLASH (subdir[c->subdirlen]))
|
||||
{
|
||||
/* Keep the old directory cached and report open failure,
|
||||
unless EMFILE/ENFILE 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 && errno != ENFILE)
|
||||
return (struct fdbase) { .fd = BADFD, .base = base };
|
||||
/* The new directory is a subdirectory of the old,
|
||||
so open relative to FD rather than to chdir_fd. */
|
||||
int subfd = open_subdir (fd, &subdir[c->subdirlen],
|
||||
child_oflags);
|
||||
if (subfd < 0)
|
||||
{
|
||||
/* Keep the old directory cached and report open failure,
|
||||
unless EMFILE/ENFILE 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 && errno != ENFILE)
|
||||
return (struct fdbase) { .fd = BADFD, .base = base };
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Replace the old directory with the new one. */
|
||||
if (!chdirable (fd))
|
||||
close (fd);
|
||||
c->fd = subfd;
|
||||
c->subdirlen = subdirlen;
|
||||
return (struct fdbase) { .fd = subfd, .base = base };
|
||||
}
|
||||
}
|
||||
|
||||
int newfd = open_subdir (chdir_fd, c->subdir, child_oflags);
|
||||
if (newfd < 0)
|
||||
fd = BADFD == -1 ? newfd : BADFD;
|
||||
else
|
||||
{
|
||||
/* Replace the old directory with the new one. */
|
||||
if (!chdirable (fd))
|
||||
/* Remove any old directory info, and add new info. */
|
||||
if (0 < c->subdirlen && !chdirable (fd))
|
||||
close (fd);
|
||||
c->fd = subfd;
|
||||
c->chdir_current = chdir_current;
|
||||
c->fd = fd = newfd;
|
||||
c->subdirlen = subdirlen;
|
||||
return (struct fdbase) { .fd = subfd, .base = base };
|
||||
}
|
||||
}
|
||||
|
||||
int newfd = open_subdir (chdir_fd, c->subdir, child_oflags);
|
||||
if (newfd < 0)
|
||||
fd = BADFD == -1 ? newfd : BADFD;
|
||||
else
|
||||
{
|
||||
/* Remove any old directory info, and add new info. */
|
||||
if (0 < c->subdirlen && !chdirable (fd))
|
||||
close (fd);
|
||||
c->chdir_current = chdir_current;
|
||||
c->fd = fd = newfd;
|
||||
c->subdirlen = subdirlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user