1 /*- 2 * Copyright (c) 2025 Klara, Inc. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/stat.h> 8 9 #include <dirent.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 15 #include <atf-c.h> 16 17 static void 18 scandir_prepare(const struct atf_tc *tc) 19 { 20 ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); 21 ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755)); 22 ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644))); 23 ATF_REQUIRE_EQ(0, symlink("file", "dir/link")); 24 ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755)); 25 } 26 27 static void 28 scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist) 29 { 30 ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n); 31 ATF_CHECK_STREQ("link", namelist[0]->d_name); 32 ATF_CHECK_STREQ("file", namelist[1]->d_name); 33 ATF_CHECK_STREQ("dir", namelist[2]->d_name); 34 ATF_CHECK_STREQ("..", namelist[3]->d_name); 35 ATF_CHECK_STREQ(".", namelist[4]->d_name); 36 } 37 38 static int 39 scandir_select(const struct dirent *ent) 40 { 41 return (strcmp(ent->d_name, "skip") != 0); 42 } 43 44 static int 45 scandir_compare(const struct dirent **a, const struct dirent **b) 46 { 47 return (strcmp((*b)->d_name, (*a)->d_name)); 48 } 49 50 ATF_TC(scandir_test); 51 ATF_TC_HEAD(scandir_test, tc) 52 { 53 atf_tc_set_md_var(tc, "descr", "Test scandir()"); 54 } 55 ATF_TC_BODY(scandir_test, tc) 56 { 57 struct dirent **namelist = NULL; 58 int i, ret; 59 60 scandir_prepare(tc); 61 ret = scandir("dir", &namelist, scandir_select, scandir_compare); 62 scandir_verify(tc, ret, namelist); 63 for (i = 0; i < ret; i++) 64 free(namelist[i]); 65 free(namelist); 66 } 67 68 ATF_TC(fdscandir_test); 69 ATF_TC_HEAD(fdscandir_test, tc) 70 { 71 atf_tc_set_md_var(tc, "descr", "Test fdscandir()"); 72 } 73 ATF_TC_BODY(fdscandir_test, tc) 74 { 75 struct dirent **namelist = NULL; 76 int fd, i, ret; 77 78 scandir_prepare(tc); 79 ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 80 ret = fdscandir(fd, &namelist, scandir_select, scandir_compare); 81 scandir_verify(tc, ret, namelist); 82 for (i = 0; i < ret; i++) 83 free(namelist[i]); 84 free(namelist); 85 ATF_REQUIRE_EQ(0, close(fd)); 86 } 87 88 ATF_TC(scandirat_test); 89 ATF_TC_HEAD(scandirat_test, tc) 90 { 91 atf_tc_set_md_var(tc, "descr", "Test scandirat()"); 92 } 93 ATF_TC_BODY(scandirat_test, tc) 94 { 95 struct dirent **namelist = NULL; 96 int fd, i, ret; 97 98 scandir_prepare(tc); 99 ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0); 100 ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare); 101 scandir_verify(tc, ret, namelist); 102 for (i = 0; i < ret; i++) 103 free(namelist[i]); 104 free(namelist); 105 ATF_REQUIRE_EQ(0, close(fd)); 106 } 107 108 static int 109 scandir_none(const struct dirent *ent __unused) 110 { 111 return (0); 112 } 113 114 ATF_TC(scandir_none); 115 ATF_TC_HEAD(scandir_none, tc) 116 { 117 atf_tc_set_md_var(tc, "descr", 118 "Test scandir() when no entries are selected"); 119 } 120 ATF_TC_BODY(scandir_none, tc) 121 { 122 struct dirent **namelist = NULL; 123 124 ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort)); 125 ATF_REQUIRE(namelist); 126 free(namelist); 127 } 128 129 /* 130 * Test that scandir() propagates errors from readdir(): we create a 131 * directory with enough entries that it can't be read in a single 132 * getdirentries() call, then abuse the selection callback to close the 133 * file descriptor scandir() is using after the first call, causing the 134 * next one to fail, and verify that readdir() returns an error instead of 135 * a partial result. We make two passes, one in which nothing was 136 * selected before the error occurred, and one in which everything was. 137 */ 138 static int scandir_error_count; 139 static int scandir_error_fd; 140 static int scandir_error_select_return; 141 142 static int 143 scandir_error_select(const struct dirent *ent __unused) 144 { 145 if (scandir_error_count++ == 0) 146 close(scandir_error_fd); 147 return (scandir_error_select_return); 148 } 149 150 ATF_TC(scandir_error); 151 ATF_TC_HEAD(scandir_error, tc) 152 { 153 atf_tc_set_md_var(tc, "descr", 154 "Test that scandir() propagates errors from readdir()"); 155 } 156 ATF_TC_BODY(scandir_error, tc) 157 { 158 char path[16]; 159 struct dirent **namelist = NULL; 160 int fd, i; 161 162 ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); 163 for (i = 0; i < 1024; i++) { 164 snprintf(path, sizeof(path), "dir/%04x", i); 165 ATF_REQUIRE_EQ(0, symlink(path + 4, path)); 166 } 167 168 /* first pass, select nothing */ 169 ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 170 scandir_error_count = 0; 171 scandir_error_fd = fd; 172 scandir_error_select_return = 0; 173 ATF_CHECK_ERRNO(EBADF, 174 fdscandir(fd, &namelist, scandir_error_select, NULL) < 0); 175 ATF_CHECK_EQ(NULL, namelist); 176 177 /* second pass, select everything */ 178 ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 179 scandir_error_count = 0; 180 scandir_error_fd = fd; 181 scandir_error_select_return = 1; 182 ATF_CHECK_ERRNO(EBADF, 183 fdscandir(fd, &namelist, scandir_error_select, NULL) < 0); 184 ATF_CHECK_EQ(NULL, namelist); 185 } 186 187 ATF_TP_ADD_TCS(tp) 188 { 189 ATF_TP_ADD_TC(tp, scandir_test); 190 ATF_TP_ADD_TC(tp, fdscandir_test); 191 ATF_TP_ADD_TC(tp, scandirat_test); 192 ATF_TP_ADD_TC(tp, scandir_none); 193 ATF_TP_ADD_TC(tp, scandir_error); 194 return (atf_no_error()); 195 } 196