Path: blob/main/lib/libc/tests/gen/scandir_test.c
105018 views
/*-1* Copyright (c) 2025 Klara, Inc.2*3* SPDX-License-Identifier: BSD-2-Clause4*/56#include <sys/stat.h>78#include <dirent.h>9#include <errno.h>10#include <fcntl.h>11#include <stdio.h>12#include <stdlib.h>1314#include <atf-c.h>1516static void17scandir_prepare(const struct atf_tc *tc)18{19ATF_REQUIRE_EQ(0, mkdir("dir", 0755));20ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));21ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));22ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));23ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));24}2526static void27scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist)28{29ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);30ATF_CHECK_STREQ("link", namelist[0]->d_name);31ATF_CHECK_STREQ("file", namelist[1]->d_name);32ATF_CHECK_STREQ("dir", namelist[2]->d_name);33ATF_CHECK_STREQ("..", namelist[3]->d_name);34ATF_CHECK_STREQ(".", namelist[4]->d_name);35}3637static int38scandir_select(const struct dirent *ent)39{40return (strcmp(ent->d_name, "skip") != 0);41}4243static int44scandir_compare(const struct dirent **a, const struct dirent **b)45{46return (strcmp((*b)->d_name, (*a)->d_name));47}4849ATF_TC(scandir_test);50ATF_TC_HEAD(scandir_test, tc)51{52atf_tc_set_md_var(tc, "descr", "Test scandir()");53}54ATF_TC_BODY(scandir_test, tc)55{56struct dirent **namelist = NULL;57int i, ret;5859scandir_prepare(tc);60ret = scandir("dir", &namelist, scandir_select, scandir_compare);61scandir_verify(tc, ret, namelist);62for (i = 0; i < ret; i++)63free(namelist[i]);64free(namelist);65}6667ATF_TC(fdscandir_test);68ATF_TC_HEAD(fdscandir_test, tc)69{70atf_tc_set_md_var(tc, "descr", "Test fdscandir()");71}72ATF_TC_BODY(fdscandir_test, tc)73{74struct dirent **namelist = NULL;75int fd, i, ret;7677scandir_prepare(tc);78ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);79ret = fdscandir(fd, &namelist, scandir_select, scandir_compare);80scandir_verify(tc, ret, namelist);81for (i = 0; i < ret; i++)82free(namelist[i]);83free(namelist);84ATF_REQUIRE_EQ(0, close(fd));85}8687ATF_TC(scandirat_test);88ATF_TC_HEAD(scandirat_test, tc)89{90atf_tc_set_md_var(tc, "descr", "Test scandirat()");91}92ATF_TC_BODY(scandirat_test, tc)93{94struct dirent **namelist = NULL;95int fd, i, ret;9697scandir_prepare(tc);98ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);99ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare);100scandir_verify(tc, ret, namelist);101for (i = 0; i < ret; i++)102free(namelist[i]);103free(namelist);104ATF_REQUIRE_EQ(0, close(fd));105}106107static int108scandir_none(const struct dirent *ent __unused)109{110return (0);111}112113ATF_TC(scandir_none);114ATF_TC_HEAD(scandir_none, tc)115{116atf_tc_set_md_var(tc, "descr",117"Test scandir() when no entries are selected");118}119ATF_TC_BODY(scandir_none, tc)120{121struct dirent **namelist = NULL;122123ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort));124ATF_REQUIRE(namelist);125free(namelist);126}127128/*129* Test that scandir() propagates errors from readdir(): we create a130* directory with enough entries that it can't be read in a single131* getdirentries() call, then abuse the selection callback to close the132* file descriptor scandir() is using after the first call, causing the133* next one to fail, and verify that readdir() returns an error instead of134* a partial result. We make two passes, one in which nothing was135* selected before the error occurred, and one in which everything was.136*/137static int scandir_error_count;138static int scandir_error_fd;139static int scandir_error_select_return;140141static int142scandir_error_select(const struct dirent *ent __unused)143{144if (scandir_error_count++ == 0)145close(scandir_error_fd);146return (scandir_error_select_return);147}148149ATF_TC(scandir_error);150ATF_TC_HEAD(scandir_error, tc)151{152atf_tc_set_md_var(tc, "descr",153"Test that scandir() propagates errors from readdir()");154}155ATF_TC_BODY(scandir_error, tc)156{157char path[16];158struct dirent **namelist = NULL;159int fd, i;160161ATF_REQUIRE_EQ(0, mkdir("dir", 0755));162for (i = 0; i < 1024; i++) {163snprintf(path, sizeof(path), "dir/%04x", i);164ATF_REQUIRE_EQ(0, symlink(path + 4, path));165}166167/* first pass, select nothing */168ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);169scandir_error_count = 0;170scandir_error_fd = fd;171scandir_error_select_return = 0;172ATF_CHECK_ERRNO(EBADF,173fdscandir(fd, &namelist, scandir_error_select, NULL) < 0);174ATF_CHECK_EQ(NULL, namelist);175176/* second pass, select everything */177ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);178scandir_error_count = 0;179scandir_error_fd = fd;180scandir_error_select_return = 1;181ATF_CHECK_ERRNO(EBADF,182fdscandir(fd, &namelist, scandir_error_select, NULL) < 0);183ATF_CHECK_EQ(NULL, namelist);184}185186ATF_TP_ADD_TCS(tp)187{188ATF_TP_ADD_TC(tp, scandir_test);189ATF_TP_ADD_TC(tp, fdscandir_test);190ATF_TP_ADD_TC(tp, scandirat_test);191ATF_TP_ADD_TC(tp, scandir_none);192ATF_TP_ADD_TC(tp, scandir_error);193return (atf_no_error());194}195196197