1940142d6SJitendra Bhati /*
2940142d6SJitendra Bhati * Copyright (c) 2026 Jitendra Bhati
3940142d6SJitendra Bhati *
4940142d6SJitendra Bhati * SPDX-License-Identifier: BSD-2-Clause
5940142d6SJitendra Bhati */
6940142d6SJitendra Bhati
7940142d6SJitendra Bhati /*
8940142d6SJitendra Bhati * Tests for fts_set(), fts_set_clientptr(), fts_get_clientptr(),
9940142d6SJitendra Bhati * and fts_get_stream().
10940142d6SJitendra Bhati */
11940142d6SJitendra Bhati
12940142d6SJitendra Bhati #include <sys/stat.h>
13940142d6SJitendra Bhati
14940142d6SJitendra Bhati #include <errno.h>
15940142d6SJitendra Bhati #include <fcntl.h>
16940142d6SJitendra Bhati #include <fts.h>
17940142d6SJitendra Bhati #include <stdbool.h>
18940142d6SJitendra Bhati #include <stdio.h>
19940142d6SJitendra Bhati #include <stdlib.h>
20940142d6SJitendra Bhati #include <string.h>
21940142d6SJitendra Bhati #include <unistd.h>
22940142d6SJitendra Bhati
23940142d6SJitendra Bhati #include <atf-c.h>
24940142d6SJitendra Bhati
25940142d6SJitendra Bhati /*
26940142d6SJitendra Bhati * fts_set with invalid options must return non-zero with EINVAL.
27940142d6SJitendra Bhati * Note: fts_set returns 1 (not -1) on error.
28940142d6SJitendra Bhati */
29940142d6SJitendra Bhati ATF_TC(invalid_options);
ATF_TC_HEAD(invalid_options,tc)30940142d6SJitendra Bhati ATF_TC_HEAD(invalid_options, tc)
31940142d6SJitendra Bhati {
32940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
33940142d6SJitendra Bhati "fts_set with invalid options returns non-zero with EINVAL");
34940142d6SJitendra Bhati }
ATF_TC_BODY(invalid_options,tc)35940142d6SJitendra Bhati ATF_TC_BODY(invalid_options, tc)
36940142d6SJitendra Bhati {
37940142d6SJitendra Bhati char *paths[] = { ".", NULL };
38940142d6SJitendra Bhati FTS *fts;
39940142d6SJitendra Bhati FTSENT *ent;
40940142d6SJitendra Bhati
41940142d6SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
42940142d6SJitendra Bhati
43940142d6SJitendra Bhati ent = fts_read(fts);
44940142d6SJitendra Bhati ATF_REQUIRE(ent != NULL);
45940142d6SJitendra Bhati ATF_REQUIRE_ERRNO(EINVAL, fts_set(fts, ent, 99) != 0);
46940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
47940142d6SJitendra Bhati }
48940142d6SJitendra Bhati
49940142d6SJitendra Bhati /*
50940142d6SJitendra Bhati * FTS_AGAIN causes the current node to be re-stat()ed and returned
51940142d6SJitendra Bhati * again on the next fts_read() call.
52940142d6SJitendra Bhati */
53940142d6SJitendra Bhati ATF_TC(again);
ATF_TC_HEAD(again,tc)54940142d6SJitendra Bhati ATF_TC_HEAD(again, tc)
55940142d6SJitendra Bhati {
56940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
57940142d6SJitendra Bhati "FTS_AGAIN causes the current node to be returned once more");
58940142d6SJitendra Bhati }
ATF_TC_BODY(again,tc)59940142d6SJitendra Bhati ATF_TC_BODY(again, tc)
60940142d6SJitendra Bhati {
61940142d6SJitendra Bhati char *paths[] = { "dir", NULL };
62940142d6SJitendra Bhati FTS *fts;
63940142d6SJitendra Bhati FTSENT *ent;
64940142d6SJitendra Bhati int revisit_count;
65940142d6SJitendra Bhati
66940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
67940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
68940142d6SJitendra Bhati
69*ee213339SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
70940142d6SJitendra Bhati
71940142d6SJitendra Bhati revisit_count = 0;
72*ee213339SJitendra Bhati for (errno = 0; (ent = fts_read(fts)) != NULL; errno = 0) {
73940142d6SJitendra Bhati if (ent->fts_info == FTS_F && revisit_count == 0) {
74940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0,
75940142d6SJitendra Bhati fts_set(fts, ent, FTS_AGAIN),
76940142d6SJitendra Bhati "fts_set(FTS_AGAIN): %m");
77940142d6SJitendra Bhati revisit_count++;
78940142d6SJitendra Bhati } else if (ent->fts_info == FTS_F && revisit_count >= 1) {
79940142d6SJitendra Bhati revisit_count++;
80940142d6SJitendra Bhati }
81940142d6SJitendra Bhati }
82940142d6SJitendra Bhati ATF_CHECK_EQ_MSG(0, errno, "traversal ended with errno %d", errno);
83940142d6SJitendra Bhati ATF_CHECK_EQ_MSG(2, revisit_count,
84940142d6SJitendra Bhati "expected file visited twice via FTS_AGAIN, saw %d",
85940142d6SJitendra Bhati revisit_count);
86940142d6SJitendra Bhati
87940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
88940142d6SJitendra Bhati }
89940142d6SJitendra Bhati
90940142d6SJitendra Bhati /*
91940142d6SJitendra Bhati * FTS_AGAIN set twice in a row causes the node to be visited three
92940142d6SJitendra Bhati * times total. Each fts_read() clears fts_options, so the caller must
93940142d6SJitendra Bhati * set FTS_AGAIN again explicitly each time.
94940142d6SJitendra Bhati */
95940142d6SJitendra Bhati ATF_TC(again_consecutive);
ATF_TC_HEAD(again_consecutive,tc)96940142d6SJitendra Bhati ATF_TC_HEAD(again_consecutive, tc)
97940142d6SJitendra Bhati {
98940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
99940142d6SJitendra Bhati "FTS_AGAIN set twice in a row visits the node three times");
100940142d6SJitendra Bhati }
ATF_TC_BODY(again_consecutive,tc)101940142d6SJitendra Bhati ATF_TC_BODY(again_consecutive, tc)
102940142d6SJitendra Bhati {
103940142d6SJitendra Bhati char *paths[] = { "file", NULL };
104940142d6SJitendra Bhati FTS *fts;
105940142d6SJitendra Bhati FTSENT *ent;
106940142d6SJitendra Bhati int visit_count;
107940142d6SJitendra Bhati
108940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("file", 0644)));
109940142d6SJitendra Bhati
110940142d6SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
111940142d6SJitendra Bhati
112940142d6SJitendra Bhati visit_count = 0;
113940142d6SJitendra Bhati while ((ent = fts_read(fts)) != NULL) {
114940142d6SJitendra Bhati if (ent->fts_info == FTS_F) {
115940142d6SJitendra Bhati visit_count++;
116940142d6SJitendra Bhati if (visit_count < 3)
117940142d6SJitendra Bhati ATF_REQUIRE_EQ(0,
118940142d6SJitendra Bhati fts_set(fts, ent, FTS_AGAIN));
119940142d6SJitendra Bhati }
120940142d6SJitendra Bhati }
121940142d6SJitendra Bhati ATF_CHECK_EQ_MSG(3, visit_count,
122940142d6SJitendra Bhati "expected 3 visits with consecutive FTS_AGAIN, got %d",
123940142d6SJitendra Bhati visit_count);
124940142d6SJitendra Bhati
125940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
126940142d6SJitendra Bhati }
127940142d6SJitendra Bhati
128940142d6SJitendra Bhati /*
129940142d6SJitendra Bhati * FTS_FOLLOW on an FTS_SL entry pointing to a regular file yields FTS_F.
130940142d6SJitendra Bhati */
131940142d6SJitendra Bhati ATF_TC(follow_symlink_to_file);
ATF_TC_HEAD(follow_symlink_to_file,tc)132940142d6SJitendra Bhati ATF_TC_HEAD(follow_symlink_to_file, tc)
133940142d6SJitendra Bhati {
134940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
135940142d6SJitendra Bhati "FTS_FOLLOW on FTS_SL to regular file yields FTS_F");
136940142d6SJitendra Bhati }
ATF_TC_BODY(follow_symlink_to_file,tc)137940142d6SJitendra Bhati ATF_TC_BODY(follow_symlink_to_file, tc)
138940142d6SJitendra Bhati {
139940142d6SJitendra Bhati char *paths[] = { "dir", NULL };
140940142d6SJitendra Bhati FTS *fts;
141940142d6SJitendra Bhati FTSENT *ent;
142*ee213339SJitendra Bhati bool followed;
143940142d6SJitendra Bhati
144940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
145940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/target", 0644)));
146940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, symlink("target", "dir/link"));
147940142d6SJitendra Bhati
148*ee213339SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
149940142d6SJitendra Bhati
150*ee213339SJitendra Bhati followed = false;
151940142d6SJitendra Bhati while ((ent = fts_read(fts)) != NULL) {
152940142d6SJitendra Bhati if (ent->fts_info == FTS_SL &&
153940142d6SJitendra Bhati strcmp(ent->fts_name, "link") == 0)
154940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, fts_set(fts, ent, FTS_FOLLOW));
155940142d6SJitendra Bhati else if (ent->fts_info == FTS_F &&
156940142d6SJitendra Bhati strcmp(ent->fts_name, "link") == 0)
157*ee213339SJitendra Bhati followed = true;
158940142d6SJitendra Bhati }
159*ee213339SJitendra Bhati ATF_CHECK_MSG(followed,
160940142d6SJitendra Bhati "FTS_FOLLOW on symlink-to-file must yield FTS_F");
161940142d6SJitendra Bhati
162940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
163940142d6SJitendra Bhati }
164940142d6SJitendra Bhati
165940142d6SJitendra Bhati /*
166940142d6SJitendra Bhati * FTS_FOLLOW on an FTS_SL entry pointing to a directory causes descent
167940142d6SJitendra Bhati * into the target directory.
168940142d6SJitendra Bhati */
169940142d6SJitendra Bhati ATF_TC(follow_symlink_to_dir);
ATF_TC_HEAD(follow_symlink_to_dir,tc)170940142d6SJitendra Bhati ATF_TC_HEAD(follow_symlink_to_dir, tc)
171940142d6SJitendra Bhati {
172940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
173940142d6SJitendra Bhati "FTS_FOLLOW on FTS_SL to directory causes descent");
174940142d6SJitendra Bhati }
ATF_TC_BODY(follow_symlink_to_dir,tc)175940142d6SJitendra Bhati ATF_TC_BODY(follow_symlink_to_dir, tc)
176940142d6SJitendra Bhati {
177940142d6SJitendra Bhati char *paths[] = { "dir", NULL };
178940142d6SJitendra Bhati FTS *fts;
179940142d6SJitendra Bhati FTSENT *ent;
180*ee213339SJitendra Bhati bool saw_inside;
181940142d6SJitendra Bhati
182940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
183940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir/real", 0755));
184940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/real/inside", 0644)));
185940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, symlink("real", "dir/link"));
186940142d6SJitendra Bhati
187*ee213339SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
188940142d6SJitendra Bhati
189*ee213339SJitendra Bhati saw_inside = false;
190940142d6SJitendra Bhati while ((ent = fts_read(fts)) != NULL) {
191940142d6SJitendra Bhati if (ent->fts_info == FTS_SL &&
192940142d6SJitendra Bhati strcmp(ent->fts_name, "link") == 0)
193940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, fts_set(fts, ent, FTS_FOLLOW));
194940142d6SJitendra Bhati if (ent->fts_info == FTS_F &&
195940142d6SJitendra Bhati strcmp(ent->fts_name, "inside") == 0 &&
196940142d6SJitendra Bhati strcmp(ent->fts_path, "dir/link/inside") == 0)
197*ee213339SJitendra Bhati saw_inside = true;
198940142d6SJitendra Bhati }
199*ee213339SJitendra Bhati ATF_CHECK_MSG(saw_inside,
200940142d6SJitendra Bhati "FTS_FOLLOW on symlink-to-dir should descend and visit 'inside'");
201940142d6SJitendra Bhati
202940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
203940142d6SJitendra Bhati }
204940142d6SJitendra Bhati
205940142d6SJitendra Bhati /*
206940142d6SJitendra Bhati * FTS_FOLLOW on a dangling symlink (FTS_SLNONE) yields FTS_SLNONE again.
207940142d6SJitendra Bhati * FTS_SLNONE requires FTS_LOGICAL — under FTS_PHYSICAL a dangling
208940142d6SJitendra Bhati * symlink is reported as FTS_SL.
209940142d6SJitendra Bhati */
210940142d6SJitendra Bhati ATF_TC(follow_dead_symlink);
ATF_TC_HEAD(follow_dead_symlink,tc)211940142d6SJitendra Bhati ATF_TC_HEAD(follow_dead_symlink, tc)
212940142d6SJitendra Bhati {
213940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
214940142d6SJitendra Bhati "FTS_FOLLOW on dead symlink yields FTS_SLNONE");
215940142d6SJitendra Bhati }
ATF_TC_BODY(follow_dead_symlink,tc)216940142d6SJitendra Bhati ATF_TC_BODY(follow_dead_symlink, tc)
217940142d6SJitendra Bhati {
218940142d6SJitendra Bhati char *paths[] = { "dead", NULL };
219940142d6SJitendra Bhati FTS *fts;
220940142d6SJitendra Bhati FTSENT *ent;
221940142d6SJitendra Bhati
222940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, symlink("no-such-target", "dead"));
223940142d6SJitendra Bhati
224940142d6SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_LOGICAL, NULL)) != NULL);
225940142d6SJitendra Bhati
226940142d6SJitendra Bhati ent = fts_read(fts);
227940142d6SJitendra Bhati ATF_REQUIRE(ent != NULL);
228940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(FTS_SLNONE, ent->fts_info,
229940142d6SJitendra Bhati "expected FTS_SLNONE for dead symlink, got %d", ent->fts_info);
230940142d6SJitendra Bhati
231940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, fts_set(fts, ent, FTS_FOLLOW));
232940142d6SJitendra Bhati ent = fts_read(fts);
233940142d6SJitendra Bhati ATF_REQUIRE(ent != NULL);
234940142d6SJitendra Bhati ATF_CHECK_EQ_MSG(FTS_SLNONE, ent->fts_info,
235940142d6SJitendra Bhati "FTS_FOLLOW on dead symlink should still be FTS_SLNONE, got %d",
236940142d6SJitendra Bhati ent->fts_info);
237940142d6SJitendra Bhati
238940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
239940142d6SJitendra Bhati }
240940142d6SJitendra Bhati
241940142d6SJitendra Bhati /*
242940142d6SJitendra Bhati * FTS_SKIP on an FTS_D node prevents descent into that directory.
243940142d6SJitendra Bhati * The next fts_read() converts the node to FTS_DP without visiting
244940142d6SJitendra Bhati * any children.
245940142d6SJitendra Bhati */
246940142d6SJitendra Bhati ATF_TC(skip);
ATF_TC_HEAD(skip,tc)247940142d6SJitendra Bhati ATF_TC_HEAD(skip, tc)
248940142d6SJitendra Bhati {
249940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
250940142d6SJitendra Bhati "FTS_SKIP prevents descent into a directory");
251940142d6SJitendra Bhati }
ATF_TC_BODY(skip,tc)252940142d6SJitendra Bhati ATF_TC_BODY(skip, tc)
253940142d6SJitendra Bhati {
254940142d6SJitendra Bhati char *paths[] = { "dir", NULL };
255940142d6SJitendra Bhati FTS *fts;
256940142d6SJitendra Bhati FTSENT *ent;
257*ee213339SJitendra Bhati bool saw_inside;
258940142d6SJitendra Bhati
259940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
260940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir/skip_me", 0755));
261940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/skip_me/inside", 0644)));
262940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/sibling", 0644)));
263940142d6SJitendra Bhati
264*ee213339SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
265940142d6SJitendra Bhati
266*ee213339SJitendra Bhati saw_inside = false;
267940142d6SJitendra Bhati while ((ent = fts_read(fts)) != NULL) {
268940142d6SJitendra Bhati if (ent->fts_info == FTS_D &&
269940142d6SJitendra Bhati strcmp(ent->fts_name, "skip_me") == 0)
270940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, fts_set(fts, ent, FTS_SKIP));
271940142d6SJitendra Bhati if (strcmp(ent->fts_name, "inside") == 0)
272*ee213339SJitendra Bhati saw_inside = true;
273940142d6SJitendra Bhati }
274*ee213339SJitendra Bhati ATF_CHECK_MSG(!saw_inside,
275940142d6SJitendra Bhati "FTS_SKIP: 'inside' must not have been visited");
276940142d6SJitendra Bhati
277940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
278940142d6SJitendra Bhati }
279940142d6SJitendra Bhati
280940142d6SJitendra Bhati /*
281940142d6SJitendra Bhati * fts_set_clientptr() and fts_get_clientptr() store and retrieve an
282940142d6SJitendra Bhati * arbitrary pointer on the FTS stream.
283940142d6SJitendra Bhati */
284940142d6SJitendra Bhati ATF_TC(clientptr_roundtrip);
ATF_TC_HEAD(clientptr_roundtrip,tc)285940142d6SJitendra Bhati ATF_TC_HEAD(clientptr_roundtrip, tc)
286940142d6SJitendra Bhati {
287940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
288940142d6SJitendra Bhati "fts_set_clientptr / fts_get_clientptr round-trip");
289940142d6SJitendra Bhati }
ATF_TC_BODY(clientptr_roundtrip,tc)290940142d6SJitendra Bhati ATF_TC_BODY(clientptr_roundtrip, tc)
291940142d6SJitendra Bhati {
292*ee213339SJitendra Bhati char *paths[] = { "dir", NULL };
293940142d6SJitendra Bhati FTS *fts;
294*ee213339SJitendra Bhati FTSENT *ent;
295940142d6SJitendra Bhati int value = 42;
296940142d6SJitendra Bhati
297*ee213339SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
298*ee213339SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
299*ee213339SJitendra Bhati
300940142d6SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
301940142d6SJitendra Bhati
302*ee213339SJitendra Bhati /* Initially NULL. */
303940142d6SJitendra Bhati ATF_CHECK_EQ(NULL, fts_get_clientptr(fts));
304940142d6SJitendra Bhati
305940142d6SJitendra Bhati fts_set_clientptr(fts, &value);
306940142d6SJitendra Bhati
307*ee213339SJitendra Bhati while ((ent = fts_read(fts)) != NULL) {
308*ee213339SJitendra Bhati /*
309*ee213339SJitendra Bhati * Verify the pointer is accessible and correct
310*ee213339SJitendra Bhati * while traversal is active.
311*ee213339SJitendra Bhati */
312*ee213339SJitendra Bhati ATF_CHECK_EQ_MSG(&value, fts_get_clientptr(fts),
313*ee213339SJitendra Bhati "fts_get_clientptr did not return the stored pointer "
314*ee213339SJitendra Bhati "for entry '%s'", ent->fts_name);
315*ee213339SJitendra Bhati }
316*ee213339SJitendra Bhati
317*ee213339SJitendra Bhati /* Overwrite with NULL, verify. */
318940142d6SJitendra Bhati fts_set_clientptr(fts, NULL);
319940142d6SJitendra Bhati ATF_CHECK_EQ(NULL, fts_get_clientptr(fts));
320940142d6SJitendra Bhati
321940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
322940142d6SJitendra Bhati }
323940142d6SJitendra Bhati
324940142d6SJitendra Bhati /*
325940142d6SJitendra Bhati * fts_get_stream() returns the parent FTS* from any FTSENT* returned
326940142d6SJitendra Bhati * by fts_read().
327940142d6SJitendra Bhati */
328940142d6SJitendra Bhati ATF_TC(get_stream_backpointer);
ATF_TC_HEAD(get_stream_backpointer,tc)329940142d6SJitendra Bhati ATF_TC_HEAD(get_stream_backpointer, tc)
330940142d6SJitendra Bhati {
331940142d6SJitendra Bhati atf_tc_set_md_var(tc, "descr",
332940142d6SJitendra Bhati "fts_get_stream returns the parent FTS* from an FTSENT*");
333940142d6SJitendra Bhati }
ATF_TC_BODY(get_stream_backpointer,tc)334940142d6SJitendra Bhati ATF_TC_BODY(get_stream_backpointer, tc)
335940142d6SJitendra Bhati {
336940142d6SJitendra Bhati char *paths[] = { "dir", NULL };
337940142d6SJitendra Bhati FTS *fts;
338940142d6SJitendra Bhati FTSENT *ent;
339940142d6SJitendra Bhati
340940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
341940142d6SJitendra Bhati ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
342940142d6SJitendra Bhati
343940142d6SJitendra Bhati ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
344940142d6SJitendra Bhati
345940142d6SJitendra Bhati while ((ent = fts_read(fts)) != NULL) {
346940142d6SJitendra Bhati ATF_CHECK_EQ_MSG(fts, fts_get_stream(ent),
347940142d6SJitendra Bhati "fts_get_stream(ent) must return the parent FTS*, "
348940142d6SJitendra Bhati "entry: %s info: %d",
349940142d6SJitendra Bhati ent->fts_name, ent->fts_info);
350940142d6SJitendra Bhati }
351940142d6SJitendra Bhati
352940142d6SJitendra Bhati ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
353940142d6SJitendra Bhati }
354940142d6SJitendra Bhati
ATF_TP_ADD_TCS(tp)355940142d6SJitendra Bhati ATF_TP_ADD_TCS(tp)
356940142d6SJitendra Bhati {
357940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, invalid_options);
358940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, again);
359940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, again_consecutive);
360940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, follow_symlink_to_file);
361940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, follow_symlink_to_dir);
362940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, follow_dead_symlink);
363940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, skip);
364940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, clientptr_roundtrip);
365940142d6SJitendra Bhati ATF_TP_ADD_TC(tp, get_stream_backpointer);
366940142d6SJitendra Bhati
367940142d6SJitendra Bhati return (atf_no_error());
368940142d6SJitendra Bhati }
369