Hi Lukáš,

Lukáš Zaoral <[email protected]> writes:

> 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(-)

In Gnulib, we don't auto-generate the ChangeLog from commit messages. No
need to send a V2 for that, just for future reference.

> 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

The patch looks reasonable from a quick look. Do you have a patch ready
for 'find -mount'? That is probably the easiest way to test it.

I guess for a simple test case we could check that /tmp is a seperate
mountpoint and then call fts_open ("/", FTS_MOUNT, ...), and process all
FTS_D entries returned from the root (skipping children so the test
doesn't take forever). Then fail if we see /tmp returned.

Collin

Reply via email to