xref: /freebsd/tests/sys/kern/getdirentries_test.c (revision ae07a5805b1906f29e786f415d67bef334557bd3)
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