chdir_id refactoring

This prepares for future changes that need directory IDs.
* src/common.h (struct chdir_id): New struct.
* src/extract.c (extract_dir): Use chdir_id to avoid duplicate stats.
* src/misc.c (struct wd): New member ID.
(grow_wd): New function, extracted from chdir_arg and that
also initializes id.err.
(chdir_arg): Use it.  Initialize id.err.
(chdir_id): New function.
This commit is contained in:
Paul Eggert 2025-10-27 19:40:47 -07:00
parent 83cad5835f
commit 56fb4a96ca
3 changed files with 50 additions and 16 deletions

View File

@ -756,6 +756,7 @@ extern idx_t chdir_current;
extern int chdir_fd;
idx_t chdir_arg (char const *dir);
void chdir_do (idx_t dir);
struct chdir_id { int err; dev_t st_dev; ino_t st_ino; } chdir_id (void);
idx_t chdir_count (void);
void close_diag (char const *name);

View File

@ -1106,12 +1106,14 @@ extract_dir (char *file_name, char typeflag)
/* Save 'root device' to avoid purging mount points. */
if (one_file_system_option && root_device == 0)
{
struct stat st;
if (fstatat (chdir_fd, ".", &st, 0) < 0)
stat_diag (".");
struct chdir_id id = chdir_id ();
if (id.err)
{
errno = id.err;
stat_diag (".");
}
else
root_device = st.st_dev;
root_device = id.st_dev;
}
if (incremental_option)

View File

@ -913,6 +913,11 @@ struct wd
the working directory. If zero, the directory needs to be opened
to be used. */
int fd;
/* If ID.err is zero, the directory's identity;
if positive, a failure indication with errno = ID.err;
if negative, no attempt has been made yet to get the identity. */
struct chdir_id id;
};
/* A vector of chdir targets. wd[0] is the initial working directory. */
@ -943,23 +948,29 @@ chdir_count (void)
return wd_count - !!wd_count;
}
/* Grow the WD table by at least one entry. */
static void
grow_wd (void)
{
wd = xpalloc (wd, &wd_alloc, wd_alloc ? 1 : 2, -1, sizeof *wd);
if (! wd_count)
{
wd[wd_count].name = ".";
wd[wd_count].abspath = NULL;
wd[wd_count].fd = AT_FDCWD;
wd[wd_count].id.err = -1;
wd_count++;
}
}
/* DIR is the operand of a -C option; add it to vector of chdir targets,
and return the index of its location. */
idx_t
chdir_arg (char const *dir)
{
if (wd_count == wd_alloc)
{
wd = xpalloc (wd, &wd_alloc, wd_alloc ? 1 : 2, -1, sizeof *wd);
if (! wd_count)
{
wd[wd_count].name = ".";
wd[wd_count].abspath = NULL;
wd[wd_count].fd = AT_FDCWD;
wd_count++;
}
}
grow_wd ();
/* Optimize the common special case of the working directory,
or the working directory as a prefix. */
@ -975,6 +986,7 @@ chdir_arg (char const *dir)
wd[wd_count].name = dir;
wd[wd_count].abspath = NULL;
wd[wd_count].fd = 0;
wd[wd_count].id.err = -1;
return wd_count++;
}
@ -1046,6 +1058,25 @@ chdir_do (idx_t i)
chdir_fd = fd;
}
}
/* Return the identity of the current directory. */
struct chdir_id
chdir_id (void)
{
if (!wd)
grow_wd ();
struct wd *curr = &wd[chdir_current];
if (curr->id.err < 0)
{
struct stat st;
curr->id = ((chdir_fd < 0 ? stat (".", &st) : fstat (chdir_fd, &st)) < 0
? (struct chdir_id) { .err = errno }
: (struct chdir_id) { .st_dev = st.st_dev,
.st_ino = st.st_ino });
}
return curr->id;
}
const char *
tar_dirname (void)