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 /*
18 * Create a directory with a single subdirectory.
19 */
20 static void
opendir_prepare(const struct atf_tc * tc)21 opendir_prepare(const struct atf_tc *tc)
22 {
23 ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
24 ATF_REQUIRE_EQ(0, mkdir("dir/subdir", 0755));
25 }
26
27 /*
28 * Assuming dirp represents the directory created by opendir_prepare(),
29 * verify that readdir() returns what we expected to see there.
30 */
31 static void
opendir_check(const struct atf_tc * tc,DIR * dirp)32 opendir_check(const struct atf_tc *tc, DIR *dirp)
33 {
34 struct dirent *ent;
35
36 ATF_REQUIRE((ent = readdir(dirp)) != NULL);
37 ATF_CHECK_EQ(1, ent->d_namlen);
38 ATF_CHECK_STREQ(".", ent->d_name);
39 ATF_CHECK_EQ(DT_DIR, ent->d_type);
40 ATF_REQUIRE((ent = readdir(dirp)) != NULL);
41 ATF_CHECK_EQ(2, ent->d_namlen);
42 ATF_CHECK_STREQ("..", ent->d_name);
43 ATF_CHECK_EQ(DT_DIR, ent->d_type);
44 ATF_REQUIRE((ent = readdir(dirp)) != NULL);
45 ATF_CHECK_EQ(sizeof("subdir") - 1, ent->d_namlen);
46 ATF_CHECK_STREQ("subdir", ent->d_name);
47 ATF_CHECK_EQ(DT_DIR, ent->d_type);
48 ATF_CHECK(readdir(dirp) == NULL);
49 ATF_CHECK(readdir(dirp) == NULL);
50 }
51
52 ATF_TC(opendir_ok);
ATF_TC_HEAD(opendir_ok,tc)53 ATF_TC_HEAD(opendir_ok, tc)
54 {
55 atf_tc_set_md_var(tc, "descr", "Open a directory.");
56 }
ATF_TC_BODY(opendir_ok,tc)57 ATF_TC_BODY(opendir_ok, tc)
58 {
59 DIR *dirp;
60
61 opendir_prepare(tc);
62 ATF_REQUIRE((dirp = opendir("dir")) != NULL);
63 opendir_check(tc, dirp);
64 ATF_CHECK_EQ(0, closedir(dirp));
65 }
66
67 ATF_TC(opendir_fifo);
ATF_TC_HEAD(opendir_fifo,tc)68 ATF_TC_HEAD(opendir_fifo, tc)
69 {
70 atf_tc_set_md_var(tc, "descr", "Do not hang if given a named pipe.");
71 }
ATF_TC_BODY(opendir_fifo,tc)72 ATF_TC_BODY(opendir_fifo, tc)
73 {
74 DIR *dirp;
75 int fd;
76
77 ATF_REQUIRE((fd = mkfifo("fifo", 0644)) >= 0);
78 ATF_REQUIRE_EQ(0, close(fd));
79 ATF_REQUIRE((dirp = opendir("fifo")) == NULL);
80 ATF_CHECK_EQ(ENOTDIR, errno);
81 }
82
83 ATF_TC(fdopendir_ok);
ATF_TC_HEAD(fdopendir_ok,tc)84 ATF_TC_HEAD(fdopendir_ok, tc)
85 {
86 atf_tc_set_md_var(tc, "descr",
87 "Open a directory from a directory descriptor.");
88 }
ATF_TC_BODY(fdopendir_ok,tc)89 ATF_TC_BODY(fdopendir_ok, tc)
90 {
91 DIR *dirp;
92 int dd;
93
94 opendir_prepare(tc);
95 ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
96 ATF_REQUIRE((dirp = fdopendir(dd)) != NULL);
97 opendir_check(tc, dirp);
98 ATF_CHECK_EQ(dd, fdclosedir(dirp));
99 ATF_CHECK_EQ(0, close(dd));
100 }
101
102 ATF_TC(fdopendir_ebadf);
ATF_TC_HEAD(fdopendir_ebadf,tc)103 ATF_TC_HEAD(fdopendir_ebadf, tc)
104 {
105 atf_tc_set_md_var(tc, "descr",
106 "Open a directory from an invalid descriptor.");
107 }
ATF_TC_BODY(fdopendir_ebadf,tc)108 ATF_TC_BODY(fdopendir_ebadf, tc)
109 {
110 DIR *dirp;
111 int dd;
112
113 ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
114 ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
115 ATF_CHECK_EQ(0, close(dd));
116 ATF_REQUIRE((dirp = fdopendir(dd)) == NULL);
117 ATF_CHECK_EQ(EBADF, errno);
118 }
119
120 ATF_TC(fdopendir_enotdir);
ATF_TC_HEAD(fdopendir_enotdir,tc)121 ATF_TC_HEAD(fdopendir_enotdir, tc)
122 {
123 atf_tc_set_md_var(tc, "descr",
124 "Open a directory from a non-directory descriptor.");
125 }
ATF_TC_BODY(fdopendir_enotdir,tc)126 ATF_TC_BODY(fdopendir_enotdir, tc)
127 {
128 DIR *dirp;
129 int fd;
130
131 ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0);
132 ATF_REQUIRE((dirp = fdopendir(fd)) == NULL);
133 ATF_CHECK_EQ(ENOTDIR, errno);
134 ATF_CHECK_EQ(0, close(fd));
135 }
136
ATF_TP_ADD_TCS(tp)137 ATF_TP_ADD_TCS(tp)
138 {
139 ATF_TP_ADD_TC(tp, opendir_ok);
140 ATF_TP_ADD_TC(tp, fdopendir_ok);
141 ATF_TP_ADD_TC(tp, fdopendir_ebadf);
142 ATF_TP_ADD_TC(tp, fdopendir_enotdir);
143 ATF_TP_ADD_TC(tp, opendir_fifo);
144 return (atf_no_error());
145 }
146