]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
cp,mv,install: avoid excess stat calls on non-GNU
authorPaul Eggert <eggert@cs.ucla.edu>
Wed, 13 Apr 2022 06:56:41 +0000 (23:56 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Wed, 13 Apr 2022 06:57:15 +0000 (23:57 -0700)
* 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
gl/lib/targetdir.h
src/cp.c
src/install.c
src/mv.c

index 79d888887cefef4cda441e6f1b5c25d2e96580f5..a966e1ea1fdfe1c74b917fa245b1b58ca46c9f43 100644 (file)
@@ -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;
index be34d498136136b973295451a9e0e89a5352d8d9..cb6b06835919e189a73f1e083154d79b00d7cde2 100644 (file)
@@ -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
index 7d69db928afca67881a7a705d976864993a4ea0c..0f44b351393acb2ca379a9bdaa814e81f74202a4 100644 (file)
--- 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));
         }
     }
index 4b06f96398f8ba706652e34dc457d648d22462ba..5c4baf7d4c25f6311d3f121478324fe4ab5ad56d 100644 (file)
@@ -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;
index cb10713d71ab5228bcb59d3b630863ec9837fb07..53b9c13005cf944e6bea1189b778420ef88b41bd 100644 (file)
--- 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));
             }
         }