Forgot to attach the patches, sorry.
On Mon, Jul 13, 2026 at 11:17 AM James Youngman <[email protected]> wrote:
>
> These patches fix some test coverage issues for find -ls:
>
> 1. Improve detection of bugs on systems where the name of the user's
> primary group is the same as their username.
> 2. Detect accidental changes to the spacing between fields.
> 3. Check the block count field of the -ls output.
>
From bb8e0b7805bcbf80d858bc8a02e27c10fdb413c2 Mon Sep 17 00:00:00 2001
From: James Youngman <[email protected]>
Date: Mon, 13 Jul 2026 09:27:09 +0100
Subject: [PATCH 1/2] maint: use a distinct group name in ls-format-file.sh if
possible.
Also, check the block count.
* tests/find/ls-format-file.sh: where possible, set the test file up
such that its group owner is a group whose name is not the same as the
username of the current process. Check the block count also.
---
tests/find/ls-format-file.sh | 104 ++++++++++++++++++++++++++++-------
1 file changed, 84 insertions(+), 20 deletions(-)
diff --git a/tests/find/ls-format-file.sh b/tests/find/ls-format-file.sh
index 9a9989d0..ec9f2331 100755
--- a/tests/find/ls-format-file.sh
+++ b/tests/find/ls-format-file.sh
@@ -28,11 +28,28 @@ LC_ALL=C
export LC_ALL
unset LANG
-# Create a test file with some known properties.
+user_owner="$( id -u -n )"
+if [ -z "${user_owner}" ]
+then
+ # This could mean that there is no entry in the password database.
+ skip_ "unable to determine username of the current user"
+fi
+
+# Create a test file with some known properties. Because of
+# differences we cannot set a specific expectation for the number of
+# blocks occupied by a file, but we should be able to expect it to be
+# consistent with "ls -s".
umask 077
+# If you change the value of testfile here to include any characters
+# special in either glob patterns or regular expressions, you will
+# need to re-check the tests in this file to ensure that they still do
+# what you want.
testfile='lsme'
filesize=765
rm -f -- "${testfile}"
+# We get the data for the body from "yes" rather than /dev/zero just in case
+# the file system has some kind of optimization for files containing only
+# zero bytes (e.g. converting it to a hole).
if ! ( yes non-empty | dd bs=1 count="${filesize}" of="${testfile}" ) 2>/dev/null
then
framework_failure_ "failed to create test file ${testfile}"
@@ -46,27 +63,62 @@ then
framework_failure_ "failed to set mtime of ${testfile}"
fi
-# Check the actual properties of the file (to verify that the setup
-# was successful).
-check_prop() {
- printf_fmt="${1}"
- expected="${2}"
- got="$( find "${testfile}" -printf "${printf_fmt}" )"
-
- if [ "${expected}" != "${got}" ]
+# The script may be running with an effective group id which has the
+# same name as the user's username. This is inconvenient as we would
+# not detect cases where the user and group name are swapped, either
+# by find or by this test, or where the test checks against the wrong
+# value (as in 9d50e9964449fb5e9f3da84a7c19c29dbf001bb5).
+#
+# So, find a group of which the current user is a member and which, if
+# possible is not the same as the user's username.
+choose_distinct_group() {
+ chosen=N
+ unset group_name
+ for group_name in $(groups)
+ do
+ if [ "${group_name}" != "${user_owner}" ]
+ then
+ printf '%s\n' "${group_name}"
+ chosen=Y
+ break
+ fi
+ done
+ if [ "${chosen}" = N ]
then
- framework_failure_ "expected test file ${testfile} to have -printf '${printf_fmt}\n' result '${expected}' but we got '${got}'"
+ # The user's only group is the same as their username.
+ # Default to the last (and only unless two groups have the
+ # same name) group name in the output of groups.
+ echo "${group_name:-}"
fi
}
+group_owner="$(choose_distinct_group)"
+if [ -z "${group_owner}" ]
+then
+ # This is very unusual, because even if /etc/group has been
+ # deleted, id should have printed the numeric value of the user's
+ # primary group. However, there are situations in which this
+ # could happen. For example, getgid() can fail on GNU Hurd.
+ skip_ "unable to find a group of which the current user is a member"
+fi
-check_prop '%M' "-rw-------" # file mode
-check_prop '%n' 1 # link count
-check_prop '%s' "${filesize}" # size in bytes
-check_prop '%TF' "1992-03-14" # modification date
-check_prop '%TH:%TM' "19:00" # modification time (without seconds)
+if ! chgrp "${group_owner}" "${testfile}"
+then
+ # There are some circumstances in which we cannot use a group in
+ # the filesystem even though getgroups(2) returns it. An example
+ # is NFS version 2, on which the protocol limits the total number
+ # of group IDs that a process can use. If I recall correctly the
+ # limit is 16. Anyway, in this situation we will not claim that
+ # the test framework failed, because it is generally not going to
+ # be useful for the findutils maintainers to investigate these
+ # cases. We also will not continue with a fallback value as these
+ # situations would be difficult to reproduce in the event of a bug
+ # report.
+ skip_ "failed to change group of ${testfile} to ${group_owner}"
+fi
# Remember the file's inode number and block count.
-inum="$( find "${testfile}" -printf '%i\n')"
+inum="$( find "${testfile}" -printf '%i\n' )"
+blocks_expected="$( ls -s "${testfile}" | sed -e "s/${testfile}//g" )"
# The system's value of BLOCKSIZE is implementation-dependent
# ("ls -k" forces ls to use 1KiB blocks, but -ls is intended
# to look like the output of ls -dils, without -k)
@@ -89,7 +141,13 @@ then
fail_ "find -ls output does not end in newline"
fi
-grep -e ' lsme$' < output.txt >/dev/null || fail_ "find -ls output should end with the file name"
+###############################################################################
+# Checks on the output fields generated by -ls, ignoring differences in the
+# number of spaces between fields. If these tests break, this likely indicates
+# a bug in find.
+###############################################################################
+
+grep -e " ${testfile}"'$' < output.txt >/dev/null || fail_ "find -ls output should end with the file name"
# The output is supposed to look like this:
@@ -108,9 +166,10 @@ awk < output.txt \
-v ME="${ME_}" \
-v filename_expected="${testfile}" \
-v inodenum="${inum}" \
+ -v blocks_expected="${blocks_expected}" \
-v filesize="${filesize}" \
-v uowner="$(id -un)" \
- -v gowner="$(id -gn)" '
+ -v gowner="${group_owner}" '
BEGIN {
rv=2
}
@@ -146,8 +205,13 @@ NR == 1 {
printf("%s: failed test: expected inode number %d, got %s\n", ME, inodenum, $1);
}
- # field 2: size in blocks is ignored as we probably cannot assume
- # any particular value for BLOCKSIZE.
+ # field 2: size in blocks
+ if (st_blocks != blocks_expected) {
+ rv=1
+ printf("%s: failed test: expected block count %d got %d\n", ME, blocks_expected, st_blocks);
+ } else {
+ printf("%s: block count %d looks OK\n", ME, st_blocks);
+ }
# field 3 is the symbolic mode; we allow a final +
# in case there is somehow a non-default ACL on the file.
--
2.47.3
From 67e926036e8f6857440af51ccce0f54081e426f1 Mon Sep 17 00:00:00 2001
From: James Youngman <[email protected]>
Date: Mon, 13 Jul 2026 09:49:06 +0100
Subject: [PATCH 2/2] maint: check spacing of fields in find -ls, and the block
count.
* tests/find/ls-format-file.sh: check the output of -ls against the
output of a -printf format which should generate the same
output (though only for files which aren't symlinks or device nodes).
This serves as a guard against accidental changes to field widths. If
an output field is incorrect, carry out the remaining test steps
anyway (but the test still fails). Remove redundant sanity-checks on
the properties of the test file. These were useful during development
of the test but for someone investigating a test failure, they are
distracting noise. Add a check for the block count field.
* tests/find/ls-format-symlink.sh: add a similar check. If an output
field is incorrect, carry out the remaining test steps anyway (but the
test still fails). Eliminate redundant test of the "a -> b" part of
the output. Remove redundant sanity-checks on the properties of the
test file. Skip the test if we cannot create a symbolic link.
---
tests/find/ls-format-file.sh | 47 ++++++++++++++++--
tests/find/ls-format-symlink.sh | 86 +++++++++++++++++++++++----------
2 files changed, 103 insertions(+), 30 deletions(-)
diff --git a/tests/find/ls-format-file.sh b/tests/find/ls-format-file.sh
index ec9f2331..cee3737e 100755
--- a/tests/find/ls-format-file.sh
+++ b/tests/find/ls-format-file.sh
@@ -28,6 +28,11 @@ LC_ALL=C
export LC_ALL
unset LANG
+# We set fail to a nonzero value to indicate that one of the test
+# steps showed a difference between the expected and actual behaviour
+# (that is, the test failed).
+fail=0
+
user_owner="$( id -u -n )"
if [ -z "${user_owner}" ]
then
@@ -149,18 +154,19 @@ fi
grep -e " ${testfile}"'$' < output.txt >/dev/null || fail_ "find -ls output should end with the file name"
-# The output is supposed to look like this:
+# The output is supposed to look like this (though we've removed
+# consecutive spaces here):
# st_ino st_blocks
# | | symbolic st_mode
# | | | st_nlink
# | | | | Username corresponding to st_uid
-# | | | | | Group corresponding to st_gid
+# | | | | | Group name corresponding to st_gid
# | | | | | | Size in bytes
# | | | | | | | st_mtime
# | | | | | | | | file name
# | | | | | | | | |
-# 4485432 1 -rw------- 1 james james 765 Mar 14 1992 lsme
+# 4485432 1 -rw------- 1 james group 765 Mar 14 1992 lsme
awk < output.txt \
-v ME="${ME_}" \
@@ -283,4 +289,37 @@ END {
'
rv=$?
echo "Awk exit status was ${rv}"
-Exit "${rv}"
+if [ "${rv}" -ne 0 ]
+then
+ warn_ "${ME_}: one of the field values was incorrect (see above) so the test will eventually fail, but we will carry out the remaining test steps anyway"
+ fail=1
+fi
+
+
+###############################################################################
+# Checks on the output generated by -ls, including the spaces between
+# fields. If these tests break, this may indicate a bug in find, or
+# alternatively a deliberate change to the spacing of the output
+# (e.g. a deliberate change to lib/listfile.c). If you are
+# deliberately changing the output spacing, it is OK to update the
+# tests below to reflect this. If not, this is likely an
+# unintentional change which you should investigate.
+###############################################################################
+
+# Generate "by hand" in the file printf-output.txt some output which
+# should match the output of -ls.
+rm -f -- printf-output.txt
+if ! find "${testfile}" -printf '%9i %6b %M %3n %-8u %-8g %8s %3Tb %2Td %5TY %p\n' > printf-output.txt
+then
+ framework_failure_ "unable to create printf-output.txt"
+fi
+
+if ! compare printf-output.txt output.txt
+then
+ # The detailed nature of the problem should already be visible in
+ # the diff output we just emitted into the log.
+ fail=1
+ warn_ "${ME_}: the output of the -ls action seems to be incorrect"
+fi
+
+Exit "${fail}"
diff --git a/tests/find/ls-format-symlink.sh b/tests/find/ls-format-symlink.sh
index 6bf17599..c240ac47 100755
--- a/tests/find/ls-format-symlink.sh
+++ b/tests/find/ls-format-symlink.sh
@@ -28,17 +28,29 @@ LC_ALL=C
export LC_ALL
unset LANG
+# We set fail to a nonzero value to indicate that one of the test
+# steps showed a difference between the expected and actual behaviour
+# (that is, the test failed).
+fail=0
+
# Create a test file with some known properties.
umask 077
+# Note that symlink_target and symlink should not contain characters
+# which are special to find -printf. It's best also to avoid
+# characters which are special to the shell, too.
symlink_target='target'
symlink='symlink'
+
filesize=765
rm -f -- "${symlink_target}" "${symlink}"
if ! ( yes non-empty | dd bs=1 count="${filesize}" of="${symlink_target}" ) 2>/dev/null
then
framework_failure_ "failed to create test file ${symlink_target}"
fi
-ln -s "${symlink_target}" "${symlink}"
+if ! ln -s "${symlink_target}" "${symlink}"
+then
+ skip_ "unable to create a symbolic link"
+fi
# Any specific time for the test will do as long as it is far enough
# away from now for ls to display the year. This happens to be the
@@ -53,32 +65,12 @@ then
framework_failure_ "failed to set mtime of ${symlink}"
fi
-# Check the actual properties of the file (to verify that the setup
-# was successful).
-check_prop() {
- printf_fmt="${1}"
- expected="${2}"
- got="$( find "${symlink}" -printf "${printf_fmt}" )"
-
- if [ "${expected}" != "${got}" ]
- then
- framework_failure_ "expected test file ${symlink} to have -printf '${printf_fmt}\n' result '${expected}' but we got '${got}'"
- fi
-}
-
-check_prop '%M' "lrwxrwxrwx" # file mode
-check_prop '%n' 1 # link count
-check_prop '%s' 6 # size in bytes (length of "target")
-check_prop '%TF' "1999-12-31" # modification date
-check_prop '%TH:%TM' "23:59" # modification time (without seconds)
-
# Remember the file's inode number and block count.
inum="$( find "${symlink}" -printf '%i\n')"
# The system's value of BLOCKSIZE is implementation-dependent
# ("ls -k" forces ls to use 1KiB blocks, but -ls is intended
# to look like the output of ls -dils, without -k)
-
# Generate the ls output.
find "${symlink}" -ls > output.txt
@@ -88,15 +80,20 @@ cat output.txt
echo "same thing in od -c format:"
od -c output.txt
-# Determine whether there is a trailing newline. If not, output.txt
-# is not actually a text file and some other checks may not work the
-# way we expect.
+# Determine whether there is a trailing newline.
if [ $(sed -n -e '$ s/.*//p' < output.txt | wc -c) -eq 0 ]
then
+ # If we get to here, output.txt is not actually a text file and
+ # some other checks may not work the way we expect, so we stop
+ # now.
fail_ "find -ls output does not end in newline"
fi
-grep -e ' symlink -> target$' < output.txt >/dev/null || fail_ "find -ls output should end with symlink -> target"
+###############################################################################
+# Checks on the output fields generated by -ls, ignoring differences in the
+# number of spaces between fields. If these tests break, this likely indicates
+# a bug in find.
+###############################################################################
# The output is supposed to look like this:
@@ -230,4 +227,41 @@ END {
'
rv=$?
echo "Awk exit status was ${rv}"
-Exit "${rv}"
+if [ "${rv}" -ne 0 ]
+then
+ warn_ "${ME_}: one of the field values was incorrect (see above) so the test will eventually fail, but we will carry out the remaining test steps anyway"
+ fail=1
+fi
+
+
+###############################################################################
+# Checks on the output generated by -ls, including the spaces between
+# fields. If these tests break, this may indicate a bug in find, or
+# alternatively a deliberate change to the spacing of the output
+# (e.g. a deliberate change to lib/listfile.c). If you are
+# deliberately changing the output spacing, it is OK to update the
+# tests below to reflect this. If not, this is likely an
+# unintentional change which you should investigate.
+###############################################################################
+
+# Generate "by hand" in the file printf-output.txt some output which
+# should match the output of -ls. Take care when updating the format
+# string, because it is interpreted both by the shell (as it uses
+# double-quotes) then also by find -printf.
+rm -f -- printf-output.txt
+if ! find "${symlink}" \
+ -printf "%9i %6b %M %3n %-8u %-8g %8s %3Tb %2Td %5TY ${symlink} -> ${symlink_target}\n" \
+ > printf-output.txt
+then
+ framework_failure_ "unable to create printf-output.txt"
+fi
+
+if ! compare printf-output.txt output.txt
+then
+ # The detailed nature of the problem should already be visible in
+ # the diff output we just emitted into the log.
+ fail=1
+ warn_ "${ME_}: the output of the -ls action seems to be incorrect"
+fi
+
+Exit "${fail}"
--
2.47.3