From 197a570ff0a17db5c8e003645d154e57bddc70ce Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 12 Apr 2022 23:56:41 -0700 Subject: [PATCH] cp,mv,install: avoid excess stat calls on non-GNU * gl/lib/targetdir.c (target_directory_operand): New arg ST. All callers changed. * src/cp.c (do_copy): * src/mv.c (main): Avoid unnecessary stat call if target_directory_operand already got the status. --- gl/lib/targetdir.c | 12 ++++++------ gl/lib/targetdir.h | 4 +++- src/cp.c | 7 ++++--- src/install.c | 5 +++-- src/mv.c | 10 ++++++---- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/gl/lib/targetdir.c b/gl/lib/targetdir.c index 79d888887c..a966e1ea1f 100644 --- a/gl/lib/targetdir.c +++ b/gl/lib/targetdir.c @@ -53,18 +53,18 @@ must_be_working_directory (char const *f) /* Return a file descriptor open to FILE, for use in openat. As an optimization, return AT_FDCWD if FILE must be the working directory. + As a side effect, possibly set *ST to the file's status. Fail and set errno if FILE is not a directory. On failure return -2 if AT_FDCWD is -1, -1 otherwise. */ int -target_directory_operand (char const *file) +target_directory_operand (char const *file, struct stat *st) { if (must_be_working_directory (file)) return AT_FDCWD; int fd = -1; int maybe_dir = -1; - struct stat st; /* On old systems without O_DIRECTORY, like Solaris 10, check with stat first lest we try to open a fifo for example and hang. @@ -72,9 +72,9 @@ target_directory_operand (char const *file) where open() was seen to return EACCES for non executable non dirs. */ if ((!O_DIRECTORY || (O_PATHSEARCH == O_SEARCH)) - && stat (file, &st) == 0) + && stat (file, st) == 0) { - maybe_dir = S_ISDIR (st.st_mode); + maybe_dir = S_ISDIR (st->st_mode); if (! maybe_dir) errno = ENOTDIR; } @@ -87,8 +87,8 @@ target_directory_operand (char const *file) /* On old systems like Solaris 10 double check type, to ensure we've opened a directory. */ int err; - if (fstat (fd, &st) != 0 ? (err = errno, true) - : !S_ISDIR (st.st_mode) && (err = ENOTDIR, true)) + if (fstat (fd, st) != 0 ? (err = errno, true) + : !S_ISDIR (st->st_mode) && (err = ENOTDIR, true)) { close (fd); errno = err; diff --git a/gl/lib/targetdir.h b/gl/lib/targetdir.h index be34d49813..cb6b068359 100644 --- a/gl/lib/targetdir.h +++ b/gl/lib/targetdir.h @@ -17,6 +17,7 @@ #include #include +#include #ifndef _GL_INLINE_HEADER_BEGIN #error "Please include config.h first." @@ -28,9 +29,10 @@ _GL_INLINE_HEADER_BEGIN /* Return a file descriptor open to FILE, for use in openat. As an optimization, return AT_FDCWD if FILE must be the working directory. + As a side effect, possibly set *ST to the file's status. Fail and set errno if FILE is not a directory. On failure return -2 if AT_FDCWD is -1, -1 otherwise. */ -extern int target_directory_operand (char const *file); +extern int target_directory_operand (char const *file, struct stat *st); /* Return true if FD represents success for target_directory_operand. */ TARGETDIR_INLINE _GL_ATTRIBUTE_PURE bool diff --git a/src/cp.c b/src/cp.c index 7d69db928a..0f44b35139 100644 --- a/src/cp.c +++ b/src/cp.c @@ -602,7 +602,7 @@ do_copy (int n_files, char **file, char const *target_directory, } else if (target_directory) { - target_dirfd = target_directory_operand (target_directory); + target_dirfd = target_directory_operand (target_directory, &sb); if (! target_dirfd_valid (target_dirfd)) die (EXIT_FAILURE, errno, _("target directory %s"), quoteaf (target_directory)); @@ -610,7 +610,7 @@ do_copy (int n_files, char **file, char const *target_directory, else { char const *lastfile = file[n_files - 1]; - int fd = target_directory_operand (lastfile); + int fd = target_directory_operand (lastfile, &sb); if (target_dirfd_valid (fd)) { target_dirfd = fd; @@ -634,7 +634,8 @@ do_copy (int n_files, char **file, char const *target_directory, | O_DIRECTORY) failed with EACCES not ENOTDIR. */ if (2 < n_files || (O_PATHSEARCH == O_SEARCH && err == EACCES - && stat (lastfile, &sb) == 0 && S_ISDIR (sb.st_mode))) + && (sb.st_mode || stat (lastfile, &sb) == 0) + && S_ISDIR (sb.st_mode))) die (EXIT_FAILURE, err, _("target %s"), quoteaf (lastfile)); } } diff --git a/src/install.c b/src/install.c index 4b06f96398..5c4baf7d4c 100644 --- a/src/install.c +++ b/src/install.c @@ -931,6 +931,7 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + struct stat sb; int target_dirfd = AT_FDCWD; if (no_target_directory) { @@ -946,7 +947,7 @@ main (int argc, char **argv) } else if (target_directory) { - target_dirfd = target_directory_operand (target_directory); + target_dirfd = target_directory_operand (target_directory, &sb); if (! (target_dirfd_valid (target_dirfd) || (mkdir_and_install && errno == ENOENT))) die (EXIT_FAILURE, errno, _("failed to access %s"), @@ -955,7 +956,7 @@ main (int argc, char **argv) else if (!dir_arg) { char const *lastfile = file[n_files - 1]; - int fd = target_directory_operand (lastfile); + int fd = target_directory_operand (lastfile, &sb); if (target_dirfd_valid (fd)) { target_dirfd = fd; diff --git a/src/mv.c b/src/mv.c index cb10713d71..53b9c13005 100644 --- a/src/mv.c +++ b/src/mv.c @@ -382,6 +382,8 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + struct stat sb; + sb.st_mode = 0; int target_dirfd = AT_FDCWD; if (no_target_directory) { @@ -397,7 +399,7 @@ main (int argc, char **argv) } else if (target_directory) { - target_dirfd = target_directory_operand (target_directory); + target_dirfd = target_directory_operand (target_directory, &sb); if (! target_dirfd_valid (target_dirfd)) die (EXIT_FAILURE, errno, _("target directory %s"), quoteaf (target_directory)); @@ -411,7 +413,7 @@ main (int argc, char **argv) ? errno : 0); if (x.rename_errno != 0) { - int fd = target_directory_operand (lastfile); + int fd = target_directory_operand (lastfile, &sb); if (target_dirfd_valid (fd)) { x.rename_errno = -1; @@ -431,10 +433,10 @@ main (int argc, char **argv) directory, in case opening a non-directory with (O_SEARCH | O_DIRECTORY) failed with EACCES not ENOTDIR. */ int err = errno; - struct stat st; if (2 < n_files || (O_PATHSEARCH == O_SEARCH && err == EACCES - && stat (lastfile, &st) == 0 && S_ISDIR (st.st_mode))) + && (sb.st_mode != 0 || stat (lastfile, &sb) == 0) + && S_ISDIR (sb.st_mode))) die (EXIT_FAILURE, err, _("target %s"), quoteaf (lastfile)); } } -- 2.47.2