aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/gen/fts.39
-rw-r--r--lib/libc/gen/fts.c44
-rw-r--r--lib/libc/tests/gen/fts_options_test.c74
-rw-r--r--lib/libutil/login_class.c4
4 files changed, 123 insertions, 8 deletions
diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3
index 199603b5f3c7..b6dbfffe8079 100644
--- a/lib/libc/gen/fts.3
+++ b/lib/libc/gen/fts.3
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 21, 2026
+.Dd May 29, 2026
.Dt FTS 3
.Os
.Sh NAME
@@ -497,6 +497,13 @@ field to
and leave the contents of the
.Fa statp
field undefined.
+The roots and any directories encountered during traversal
+.Po
+.Dv FTS_D ,
+.Dv FTS_DC ,
+.Dv FTS_DP
+.Pc
+are still fully populated.
.It Dv FTS_NOSTAT_TYPE
This option is similar to
.Dv FTS_NOSTAT ,
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index 4aa386d777cd..e8063ecb646e 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -41,6 +41,7 @@
#include <fcntl.h>
#include <fts.h>
#include <stdalign.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -114,6 +115,19 @@ static const char *ufslike_filesystems[] = {
0
};
+/*
+ * POSIX provides nlink_t but unfortunately not NLINK_MAX.
+ */
+#define NLINK_MAX \
+ _Generic((nlink_t)0, \
+ int16_t: INT16_MAX, \
+ uint16_t: UINT16_MAX, \
+ int32_t: INT32_MAX, \
+ uint32_t: UINT32_MAX, \
+ int64_t: INT64_MAX, \
+ uint64_t: UINT64_MAX, \
+ default: 0)
+
static FTS *
__fts_open(FTS *sp, char * const *argv)
{
@@ -736,7 +750,7 @@ fts_build(FTS *sp, int type)
int cderrno, descend, oflag, saved_errno, nostat, doadjust,
readdir_errno;
long level;
- long nlinks; /* has to be signed because -1 is a magic value */
+ int64_t nlinks; /* has to be signed because -1 is a magic value */
size_t dnamlen, len, maxlen, nitems;
/* Set current node pointer. */
@@ -759,16 +773,36 @@ fts_build(FTS *sp, int type)
}
/*
- * Nlinks is the number of possible entries of type directory in the
- * directory if we're cheating on stat calls, 0 if we're not doing
- * any stat calls at all, -1 if we're doing stats on everything.
+ * In the FTS_PHYSICAL | FTS_NOSTAT case, we want to avoid calling
+ * fstat() unnecessarily, but we still need to call it for
+ * subdirectories. The current directory's link count provides an
+ * upper bound on the number of subdirectories we may encounter
+ * (including . and .. in the FTS_SEEDOT case). We initialize
+ * nlinks to the current directory's link count, then decrement it
+ * every time we encounter a directory, so when we hit zero we can
+ * save some time by not calling fstat() on subsequent entries.
+ *
+ * If FTS_NOSTAT is not set, or the link count is less than two
+ * (which should not be possible) or equal to NLINK_MAX (which
+ * suggests that the actual value could be higher), or the current
+ * filesystem is not known to provide reliable link counts, we
+ * initialize nlinks to -1 and fstat() everything.
+ *
+ * In the rare case where we don't need to stat anything, even
+ * subdirectories, we initialize nlinks to 0 regardless of the
+ * actual link count.
+ *
+ * Note that we ignore the FTS_NOSTAT flag in the FTS_LOGICAL
+ * case, although we could choose to only stat symbolic links.
+ * Implementing this is left as an exercise for the reader.
*/
if (type == BNAMES) {
nlinks = 0;
/* Be quiet about nostat, GCC. */
nostat = 0;
} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
- if (fts_ufslinks(sp, cur))
+ if (cur->fts_nlink >= 2 && cur->fts_nlink < NLINK_MAX &&
+ cur->fts_nlink <= INT64_MAX && fts_ufslinks(sp, cur))
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
else
nlinks = -1;
diff --git a/lib/libc/tests/gen/fts_options_test.c b/lib/libc/tests/gen/fts_options_test.c
index fc3015138a49..863c0809d16e 100644
--- a/lib/libc/tests/gen/fts_options_test.c
+++ b/lib/libc/tests/gen/fts_options_test.c
@@ -36,6 +36,8 @@ fts_options_prepare(const struct atf_tc *tc)
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
ATF_REQUIRE_EQ(0, close(creat("file", 0644)));
ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, mkdir("dir/sd", 0750));
+ ATF_REQUIRE_EQ(0, mkdir("dir/sd/sd", 0700));
ATF_REQUIRE_EQ(0, symlink("..", "dir/up"));
ATF_REQUIRE_EQ(0, symlink("dir", "dirl"));
ATF_REQUIRE_EQ(0, symlink("file", "filel"));
@@ -57,6 +59,10 @@ ATF_TC_BODY(fts_options_logical, tc)
{ FTS_DL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_F, "file", "dir/file" },
+ { FTS_D, "sd", "dir/sd" },
+ { FTS_D, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd" },
{ FTS_D, "up", "dir/up" },
{ FTS_DL, "dead", "dir/up/dead" },
{ FTS_DC, "dir", "dir/up/dir" },
@@ -67,6 +73,10 @@ ATF_TC_BODY(fts_options_logical, tc)
{ FTS_DP, "dir", "dir" },
{ FTS_D, "dirl", "dirl" },
{ FTS_F, "file", "dirl/file" },
+ { FTS_D, "sd", "dirl/sd" },
+ { FTS_D, "sd", "dirl/sd/sd" },
+ { FTS_DP, "sd", "dirl/sd/sd" },
+ { FTS_DP, "sd", "dirl/sd" },
{ FTS_D, "up", "dirl/up" },
{ FTS_DL, "dead", "dirl/up/dead" },
{ FTS_DC, "dir", "dirl/up/dir" },
@@ -108,6 +118,10 @@ ATF_TC_BODY(fts_options_logical_nostat, tc)
{ FTS_DL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_NSOK, "file", "dir/file" },
+ { FTS_D, "sd", "dir/sd" },
+ { FTS_D, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd" },
{ FTS_D, "up", "dir/up" },
{ FTS_DL, "dead", "dir/up/dead" },
{ FTS_DC, "dir", "dir/up/dir" },
@@ -118,6 +132,10 @@ ATF_TC_BODY(fts_options_logical_nostat, tc)
{ FTS_DP, "dir", "dir" },
{ FTS_D, "dirl", "dirl" },
{ FTS_NSOK, "file", "dirl/file" },
+ { FTS_D, "sd", "dirl/sd" },
+ { FTS_D, "sd", "dirl/sd/sd" },
+ { FTS_DP, "sd", "dirl/sd/sd" },
+ { FTS_DP, "sd", "dirl/sd" },
{ FTS_D, "up", "dirl/up" },
{ FTS_DL, "dead", "dirl/up/dead" },
{ FTS_DC, "dir", "dirl/up/dir" },
@@ -151,6 +169,14 @@ ATF_TC_BODY(fts_options_logical_seedot, tc)
{ FTS_DOT, ".", "dir/." },
{ FTS_DOT, "..", "dir/.." },
{ FTS_F, "file", "dir/file" },
+ { FTS_D, "sd", "dir/sd" },
+ { FTS_DOT, ".", "dir/sd/." },
+ { FTS_DOT, "..", "dir/sd/.." },
+ { FTS_D, "sd", "dir/sd/sd" },
+ { FTS_DOT, ".", "dir/sd/sd/." },
+ { FTS_DOT, "..", "dir/sd/sd/.." },
+ { FTS_DP, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd" },
{ FTS_D, "up", "dir/up" },
{ FTS_DOT, ".", "dir/up/." },
{ FTS_DOT, "..", "dir/up/.." },
@@ -165,6 +191,14 @@ ATF_TC_BODY(fts_options_logical_seedot, tc)
{ FTS_DOT, ".", "dirl/." },
{ FTS_DOT, "..", "dirl/.." },
{ FTS_F, "file", "dirl/file" },
+ { FTS_D, "sd", "dirl/sd" },
+ { FTS_DOT, ".", "dirl/sd/." },
+ { FTS_DOT, "..", "dirl/sd/.." },
+ { FTS_D, "sd", "dirl/sd/sd" },
+ { FTS_DOT, ".", "dirl/sd/sd/." },
+ { FTS_DOT, "..", "dirl/sd/sd/.." },
+ { FTS_DP, "sd", "dirl/sd/sd" },
+ { FTS_DP, "sd", "dirl/sd" },
{ FTS_D, "up", "dirl/up" },
{ FTS_DOT, ".", "dirl/up/." },
{ FTS_DOT, "..", "dirl/up/.." },
@@ -198,6 +232,10 @@ ATF_TC_BODY(fts_options_physical, tc)
{ FTS_SL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dir", "dir" },
{ FTS_SL, "dirl", "dirl" },
@@ -224,6 +262,10 @@ ATF_TC_BODY(fts_options_physical_nochdir, tc)
{ FTS_SL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_F, "file", "dir/file" },
+ { FTS_D, "sd", "dir/sd" },
+ { FTS_D, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd/sd" },
+ { FTS_DP, "sd", "dir/sd" },
{ FTS_SL, "up", "dir/up" },
{ FTS_DP, "dir", "dir" },
{ FTS_SL, "dirl", "dirl" },
@@ -250,10 +292,18 @@ ATF_TC_BODY(fts_options_physical_comfollow, tc)
{ FTS_DL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dir", "dir" },
{ FTS_D, "dirl", "dirl" },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dirl", "dirl" },
{ FTS_F, "file", "file" },
@@ -279,10 +329,18 @@ ATF_TC_BODY(fts_options_physical_comfollowdir, tc)
{ FTS_DL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dir", "dir" },
{ FTS_D, "dirl", "dirl" },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dirl", "dirl" },
{ FTS_F, "file", "file" },
@@ -308,6 +366,10 @@ ATF_TC_BODY(fts_options_physical_nostat, tc)
{ FTS_SL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_NSOK, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_NSOK, "up", "up" },
{ FTS_DP, "dir", "dir" },
{ FTS_SL, "dirl", "dirl" },
@@ -334,6 +396,10 @@ ATF_TC_BODY(fts_options_physical_nostat_type, tc)
{ FTS_SL, "dead", "dead" },
{ FTS_D, "dir", "dir" },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dir", "dir" },
{ FTS_SL, "dirl", "dirl" },
@@ -362,6 +428,14 @@ ATF_TC_BODY(fts_options_physical_seedot, tc)
{ FTS_DOT, ".", "." },
{ FTS_DOT, "..", ".." },
{ FTS_F, "file", "file" },
+ { FTS_D, "sd", "sd" },
+ { FTS_DOT, ".", "." },
+ { FTS_DOT, "..", ".." },
+ { FTS_D, "sd", "sd" },
+ { FTS_DOT, ".", "." },
+ { FTS_DOT, "..", ".." },
+ { FTS_DP, "sd", "sd" },
+ { FTS_DP, "sd", "sd" },
{ FTS_SL, "up", "up" },
{ FTS_DP, "dir", "dir" },
{ FTS_SL, "dirl", "dirl" },
diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c
index 90e4e01f7c3b..8a465e2eb24e 100644
--- a/lib/libutil/login_class.c
+++ b/lib/libutil/login_class.c
@@ -63,9 +63,9 @@ static struct login_res {
{ "vmemoryuse", login_getcapsize, RLIMIT_VMEM },
{ "pseudoterminals", login_getcapnum, RLIMIT_NPTS },
{ "swapuse", login_getcapsize, RLIMIT_SWAP },
- { "kqueues", login_getcapsize, RLIMIT_KQUEUES },
+ { "kqueues", login_getcapnum, RLIMIT_KQUEUES },
{ "umtxp", login_getcapnum, RLIMIT_UMTXP },
- { "pipebuf", login_getcapnum, RLIMIT_PIPEBUF },
+ { "pipebuf", login_getcapsize, RLIMIT_PIPEBUF },
{ "vms", login_getcapnum, RLIMIT_VMM },
{ NULL, 0, 0 }
};