utimens: cache whether utimensat syscall works
[pspp] / lib / fts.c
index fdbe2586c6dd1d2f91ed3fa507af088ad925a1ae..40a837ed87211053b881c278c6d379cf625bd9d9 100644 (file)
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -69,8 +69,12 @@ static char sccsid[] = "@(#)fts.c    8.6 (Berkeley) 8/14/94";
 
 #if ! _LIBC
 # include "fcntl--.h"
-# include "openat.h"
+# include "dirent--.h"
 # include "unistd--.h"
+/* FIXME - use fcntl(F_DUPFD_CLOEXEC)/openat(O_CLOEXEC) once they are
+   supported.  */
+# include "cloexec.h"
+# include "openat.h"
 # include "same-inode.h"
 #endif
 
@@ -169,16 +173,6 @@ enum Fts_stat
 # define __set_errno(Val) errno = (Val)
 #endif
 
-#ifndef __attribute__
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
-#  define __attribute__(x) /* empty */
-# endif
-#endif
-
-#ifndef ATTRIBUTE_UNUSED
-# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
-#endif
-
 /* If this host provides the openat function, then we can avoid
    attempting to open "." in some initialization code below.  */
 #ifdef HAVE_OPENAT
@@ -228,10 +222,6 @@ static void free_dir (FTS *fts) {}
 # define SIZE_MAX ((size_t) -1)
 #endif
 
-#ifndef O_DIRECTORY
-# define O_DIRECTORY 0
-#endif
-
 #define ISDOT(a)       (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
 #define STREQ(a, b)    (strcmp ((a), (b)) == 0)
 
@@ -309,11 +299,13 @@ static inline DIR *
 internal_function
 opendirat (int fd, char const *dir)
 {
-  int new_fd = openat (fd, dir, O_RDONLY);
+  int new_fd = openat (fd, dir,
+                      O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
   DIR *dirp;
 
   if (new_fd < 0)
     return NULL;
+  set_cloexec_flag (new_fd, true);
   dirp = fdopendir (new_fd);
   if (dirp == NULL)
     {
@@ -365,9 +357,12 @@ diropen (FTS const *sp, char const *dir)
   int open_flags = (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
                    | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
 
-  return (ISSET (FTS_CWDFD)
-         ? openat (sp->fts_cwd_fd, dir, open_flags)
-         : open (dir, open_flags));
+  int fd = (ISSET (FTS_CWDFD)
+            ? openat (sp->fts_cwd_fd, dir, open_flags)
+            : open (dir, open_flags));
+  if (0 <= fd)
+    set_cloexec_flag (fd, true);
+  return fd;
 }
 
 FTS *
@@ -606,20 +601,28 @@ fts_close (FTS *sp)
        if (ISSET(FTS_CWDFD))
          {
            if (0 <= sp->fts_cwd_fd)
-             close (sp->fts_cwd_fd);
+             if (close (sp->fts_cwd_fd))
+               saved_errno = errno;
          }
        else if (!ISSET(FTS_NOCHDIR))
          {
            /* Return to original directory, save errno if necessary. */
            if (fchdir(sp->fts_rfd))
              saved_errno = errno;
-           close(sp->fts_rfd);
+
+           /* If close fails, record errno only if saved_errno is zero,
+              so that we report the probably-more-meaningful fchdir errno.  */
+           if (close (sp->fts_rfd))
+             if (saved_errno == 0)
+               saved_errno = errno;
          }
 
        fd_ring_clear (&sp->fts_fd_ring);
 
+#if GNULIB_FTS
        if (sp->fts_leaf_optimization_works_ht)
          hash_free (sp->fts_leaf_optimization_works_ht);
+#endif
 
        free_dir (sp);
 
@@ -713,10 +716,13 @@ leaf_optimization_applies (int dir_fd)
 }
 
 #else
-static bool dirent_inode_sort_may_be_useful (int dir_fd) { return true; }
-static bool leaf_optimization_applies (int dir_fd) { return false; }
+static bool
+dirent_inode_sort_may_be_useful (int dir_fd _UNUSED_PARAMETER_) { return true; }
+static bool
+leaf_optimization_applies (int dir_fd _UNUSED_PARAMETER_) { return false; }
 #endif
 
+#if GNULIB_FTS
 /* link-count-optimization entry:
    map an stat.st_dev number to a boolean: leaf_optimization_works */
 struct LCO_ent
@@ -798,6 +804,7 @@ link_count_optimize_ok (FTSENT const *p)
 
   return opt_ok;
 }
+#endif
 
 /*
  * Special case of "/" at the end of the file name so that slashes aren't
@@ -928,7 +935,9 @@ next:       tmp = p;
                                SET(FTS_STOP);
                                return (NULL);
                        }
+                       free_dir(sp);
                        fts_load(sp, p);
+                       setup_dir(sp);
                        goto check_for_dir;
                }
 
@@ -961,7 +970,10 @@ check_for_dir:
                    if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
                      {
                        FTSENT *parent = p->fts_parent;
-                       if (parent->fts_n_dirs_remaining == 0
+                       if (FTS_ROOTLEVEL < p->fts_level
+                           /* ->fts_n_dirs_remaining is not valid
+                              for command-line-specified names.  */
+                           && parent->fts_n_dirs_remaining == 0
                            && ISSET(FTS_NOSTAT)
                            && ISSET(FTS_PHYSICAL)
                            && link_count_optimize_ok (parent))
@@ -971,11 +983,10 @@ check_for_dir:
                        else
                          {
                            p->fts_info = fts_stat(sp, p, false);
-                           if (S_ISDIR(p->fts_statp->st_mode))
-                             {
-                               if (parent->fts_n_dirs_remaining)
+                           if (S_ISDIR(p->fts_statp->st_mode)
+                               && p->fts_level != FTS_ROOTLEVEL
+                               && parent->fts_n_dirs_remaining)
                                  parent->fts_n_dirs_remaining--;
-                             }
                          }
                      }
                    else
@@ -1058,7 +1069,7 @@ check_for_dir:
  */
 /* ARGSUSED */
 int
-fts_set(FTS *sp ATTRIBUTE_UNUSED, FTSENT *p, int instr)
+fts_set(FTS *sp _UNUSED_PARAMETER_, FTSENT *p, int instr)
 {
        if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
            instr != FTS_NOINSTR && instr != FTS_SKIP) {
@@ -1294,7 +1305,10 @@ fts_build (register FTS *sp, int type)
        if (nlinks || type == BREAD) {
                int dir_fd = dirfd(dirp);
                if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
-                 dir_fd = dup (dir_fd);
+                 {
+                   dir_fd = dup (dir_fd);
+                   set_cloexec_flag (dir_fd, true);
+                 }
                if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
                        if (nlinks && type == BREAD)
                                cur->fts_errno = errno;