1 /*- 2 * Copyright (c) 2025 Klara, Inc. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/stat.h> 8 #include <sys/mount.h> 9 10 #include <dirent.h> 11 #include <fcntl.h> 12 #include <errno.h> 13 #include <stdint.h> 14 15 #include <atf-c.h> 16 17 ATF_TC(getdirentries_ok); 18 ATF_TC_HEAD(getdirentries_ok, tc) 19 { 20 atf_tc_set_md_var(tc, "descr", "Successfully read a directory."); 21 } 22 ATF_TC_BODY(getdirentries_ok, tc) 23 { 24 char dbuf[4096]; 25 struct dirent *d; 26 off_t base; 27 ssize_t ret; 28 int dd, n; 29 30 ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); 31 ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 32 ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0); 33 ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base)); 34 ATF_REQUIRE_EQ(base, lseek(dd, 0, SEEK_CUR)); 35 ATF_CHECK_EQ(0, close(dd)); 36 for (n = 0, d = (struct dirent *)dbuf; 37 d < (struct dirent *)(dbuf + ret); 38 d = (struct dirent *)((char *)d + d->d_reclen), n++) 39 /* nothing */ ; 40 ATF_CHECK_EQ((struct dirent *)(dbuf + ret), d); 41 ATF_CHECK_EQ(2, n); 42 } 43 44 ATF_TC(getdirentries_ebadf); 45 ATF_TC_HEAD(getdirentries_ebadf, tc) 46 { 47 atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " 48 "from an invalid descriptor."); 49 } 50 ATF_TC_BODY(getdirentries_ebadf, tc) 51 { 52 char dbuf[4096]; 53 off_t base; 54 int fd; 55 56 ATF_REQUIRE((fd = open("file", O_CREAT | O_WRONLY, 0644)) >= 0); 57 ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); 58 ATF_CHECK_EQ(EBADF, errno); 59 ATF_REQUIRE_EQ(0, close(fd)); 60 ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); 61 ATF_CHECK_EQ(EBADF, errno); 62 } 63 64 ATF_TC(getdirentries_efault); 65 ATF_TC_HEAD(getdirentries_efault, tc) 66 { 67 atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " 68 "to an invalid buffer."); 69 } 70 ATF_TC_BODY(getdirentries_efault, tc) 71 { 72 char dbuf[4096]; 73 off_t base, *basep; 74 int dd; 75 76 ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); 77 ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 78 ATF_REQUIRE_EQ(-1, getdirentries(dd, NULL, sizeof(dbuf), &base)); 79 ATF_CHECK_EQ(EFAULT, errno); 80 basep = NULL; 81 basep++; 82 ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), basep)); 83 ATF_CHECK_EQ(EFAULT, errno); 84 ATF_CHECK_EQ(0, close(dd)); 85 } 86 87 ATF_TC(getdirentries_einval); 88 ATF_TC_HEAD(getdirentries_einval, tc) 89 { 90 atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " 91 "with various invalid parameters."); 92 } 93 ATF_TC_BODY(getdirentries_einval, tc) 94 { 95 struct statfs fsb; 96 char dbuf[4096]; 97 off_t base; 98 ssize_t ret; 99 int dd; 100 101 ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); 102 ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 103 ATF_REQUIRE_EQ(0, fstatfs(dd, &fsb)); 104 /* nbytes too small */ 105 ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, 8, &base)); 106 ATF_CHECK_EQ(EINVAL, errno); 107 /* nbytes too big */ 108 ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, SIZE_MAX, &base)); 109 ATF_CHECK_EQ(EINVAL, errno); 110 /* invalid position */ 111 ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0); 112 ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base)); 113 ATF_REQUIRE(base > 0); 114 ATF_REQUIRE_EQ(base + 3, lseek(dd, 3, SEEK_CUR)); 115 /* known to fail on ufs (FFS2) and zfs, and work on tmpfs */ 116 if (strcmp(fsb.f_fstypename, "ufs") == 0 || 117 strcmp(fsb.f_fstypename, "zfs") == 0) { 118 atf_tc_expect_fail("incorrectly returns 0 instead of EINVAL " 119 "on %s", fsb.f_fstypename); 120 } 121 ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base)); 122 ATF_CHECK_EQ(EINVAL, errno); 123 ATF_CHECK_EQ(0, close(dd)); 124 } 125 126 ATF_TC(getdirentries_enoent); 127 ATF_TC_HEAD(getdirentries_enoent, tc) 128 { 129 atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " 130 "after it is deleted."); 131 } 132 ATF_TC_BODY(getdirentries_enoent, tc) 133 { 134 char dbuf[4096]; 135 off_t base; 136 int dd; 137 138 ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); 139 ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); 140 ATF_REQUIRE_EQ(0, rmdir("dir")); 141 ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base)); 142 ATF_CHECK_EQ(ENOENT, errno); 143 } 144 145 ATF_TC(getdirentries_enotdir); 146 ATF_TC_HEAD(getdirentries_enotdir, tc) 147 { 148 atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " 149 "from a descriptor not associated with a directory."); 150 } 151 ATF_TC_BODY(getdirentries_enotdir, tc) 152 { 153 char dbuf[4096]; 154 off_t base; 155 int fd; 156 157 ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0); 158 ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); 159 ATF_CHECK_EQ(ENOTDIR, errno); 160 ATF_CHECK_EQ(0, close(fd)); 161 } 162 163 ATF_TP_ADD_TCS(tp) 164 { 165 ATF_TP_ADD_TC(tp, getdirentries_ok); 166 ATF_TP_ADD_TC(tp, getdirentries_ebadf); 167 ATF_TP_ADD_TC(tp, getdirentries_efault); 168 ATF_TP_ADD_TC(tp, getdirentries_einval); 169 ATF_TP_ADD_TC(tp, getdirentries_enoent); 170 ATF_TP_ADD_TC(tp, getdirentries_enotdir); 171 return (atf_no_error()); 172 } 173