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