summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2022-04-12 23:56:41 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2022-04-12 23:57:15 -0700
commit197a570ff0a17db5c8e003645d154e57bddc70ce (patch)
treed24b9e3a13402b4bfbbf4b89721813d2ca1b6754
parent30c932a3098146128acfd839589f308ec1bb116d (diff)
downloadcoreutils-197a570ff0a17db5c8e003645d154e57bddc70ce.tar.gz
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.
-rw-r--r--gl/lib/targetdir.c12
-rw-r--r--gl/lib/targetdir.h4
-rw-r--r--src/cp.c7
-rw-r--r--src/install.c5
-rw-r--r--src/mv.c10
5 files changed, 22 insertions, 16 deletions
diff --git a/gl/lib/targetdir.c b/gl/lib/targetdir.c
index 79d888887..a966e1ea1 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 be34d4981..cb6b06835 100644
--- a/gl/lib/targetdir.h
+++ b/gl/lib/targetdir.h
@@ -17,6 +17,7 @@
#include <fcntl.h>
#include <stdbool.h>
+#include <sys/stat.h>
#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 7d69db928..0f44b3513 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 4b06f9639..5c4baf7d4 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 cb10713d7..53b9c1300 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));
}
}