diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/libc/gen/fts.3 | 9 | ||||
| -rw-r--r-- | lib/libc/gen/fts.c | 44 | ||||
| -rw-r--r-- | lib/libc/tests/gen/fts_options_test.c | 74 | ||||
| -rw-r--r-- | lib/libutil/login_class.c | 4 |
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 } }; |
