This mimics behaviour of the FTW_MOUNT flag described in nftw(3) which
implements the behaviour of find(1) -mount primary standardized by
POSIX.1-2024.

* lib/fts.in.h (FTS_MOUNT): New flag.
(FTS_OPTIONMASK): Adjust.
(FTS_NAMEONLY): Adjust.
(FTS_STOP): Adjust.
* lib/fts.c (fts_read): Implement FTS_MOUNT.
(fts_build): Do not skip stat(2) when FTS_MOUNT as set.
---
 lib/fts.c    | 33 +++++++++++++++++++++------------
 lib/fts.in.h |  7 ++++---
 2 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/lib/fts.c b/lib/fts.c
index db6a0a3efa..6ddfb6f022 100644
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -1017,11 +1017,18 @@ check_for_dir:
                       fts_assert (p->fts_statp->st_size == 
FTS_NO_STAT_REQUIRED);
                   }
 
+                /* Skip files with different device numbers when FTS_MOUNT
+                   is set.  */
+                if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&
+                    p->fts_level != FTS_ROOTLEVEL &&
+                    p->fts_statp->st_dev != sp->fts_dev)
+                      goto next;
+
                 if (p->fts_info == FTS_D)
                   {
-                    /* Now that P->fts_statp is guaranteed to be valid,
-                       if this is a command-line directory, record its
-                       device number, to be used for FTS_XDEV.  */
+                    /* Now that P->fts_statp is guaranteed to be valid, if
+                       this is a command-line directory, record its device
+                       number, to be used for FTS_MOUNT and FTS_XDEV.  */
                     if (p->fts_level == FTS_ROOTLEVEL)
                       sp->fts_dev = p->fts_statp->st_dev;
                     Dprintf (("  entering: %s\n", p->fts_path));
@@ -1529,19 +1536,21 @@ mem1:                           saved_errno = errno;
                            entry. In many cases, it will simply fts_stat it,
                            but we can take advantage of any d_type information
                            to optimize away the unnecessary stat calls.  I.e.,
-                           if FTS_NOSTAT is in effect and we're not following
-                           symlinks (FTS_PHYSICAL) and d_type indicates this
-                           is *not* a directory, then we won't have to stat it
-                           at all.  If it *is* a directory, then (currently)
-                           we stat it regardless, in order to get device and
-                           inode numbers.  Some day we might optimize that
-                           away, too, for directories where d_ino is known to
-                           be valid.  */
+                           if FTS_NOSTAT is in effect, we don't need device
+                           numbers unconditionally (FTS_MOUNT) and we're not
+                           following symlinks (FTS_PHYSICAL) and d_type
+                           indicates this is *not* a directory, then we won't
+                           have to stat it  at all.  If it *is* a directory,
+                           then (currently) we stat it regardless, in order to
+                           get device and inode numbers.  Some day we might
+                           optimize that away, too, for directories where
+                           d_ino is known to be valid.  */
                         bool skip_stat = (ISSET(FTS_NOSTAT)
                                           && DT_IS_KNOWN(dp)
                                           && ! DT_MUST_BE(dp, DT_DIR)
                                           && (ISSET(FTS_PHYSICAL)
-                                              || ! DT_MUST_BE(dp, DT_LNK)));
+                                              || ! DT_MUST_BE(dp, DT_LNK))
+                                          && ! ISSET(FTS_MOUNT));
                         p->fts_info = FTS_NSOK;
                         /* Propagate dirent.d_type information back
                            to caller, when possible.  */
diff --git a/lib/fts.in.h b/lib/fts.in.h
index 9acc445039..ac36ed3f2d 100644
--- a/lib/fts.in.h
+++ b/lib/fts.in.h
@@ -186,11 +186,12 @@ typedef struct {
   /* Use this flag to disable stripping of trailing slashes
      from input path names during fts_open initialization.  */
 # define FTS_VERBATIM   0x0800
+# define FTS_MOUNT      0x1000          /* skip other devices */
 
-# define FTS_OPTIONMASK 0x0fff          /* valid user option mask */
+# define FTS_OPTIONMASK 0x1fff          /* valid user option mask */
 
-# define FTS_NAMEONLY   0x1000          /* (private) child names only */
-# define FTS_STOP       0x2000          /* (private) unrecoverable error */
+# define FTS_NAMEONLY   0x2000          /* (private) child names only */
+# define FTS_STOP       0x4000          /* (private) unrecoverable error */
         int fts_options;                /* fts_open options, global flags */
 
         /* Map a directory's device number to a boolean.  The boolean is
-- 
2.51.0


Reply via email to