fts: Add test cases for unreadable directories.
Sponsored by: Klara, Inc. Reviewed by: kevans Differential Revision: https://reviews.freebsd.org/D51098
This commit is contained in:
@@ -10,6 +10,7 @@ ATF_TESTS_C+= fpclassify2_test
|
||||
.if ${COMPILER_FEATURES:Mblocks}
|
||||
ATF_TESTS_C+= fts_blocks_test
|
||||
.endif
|
||||
ATF_TESTS_C+= fts_misc_test
|
||||
ATF_TESTS_C+= fts_options_test
|
||||
ATF_TESTS_C+= ftw_test
|
||||
ATF_TESTS_C+= getentropy_test
|
||||
@@ -104,6 +105,14 @@ TEST_METADATA.setdomainname_test+= is_exclusive=true
|
||||
TESTS_SUBDIRS= execve
|
||||
TESTS_SUBDIRS+= posix_spawn
|
||||
|
||||
# Tests that require address sanitizer
|
||||
.if ${COMPILER_FEATURES:Masan}
|
||||
.for t in scandir_test realpath2_test
|
||||
CFLAGS.${t}.c+= -fsanitize=address
|
||||
LDFLAGS.${t}+= -fsanitize=address
|
||||
.endfor
|
||||
.endif
|
||||
|
||||
# Tests that require blocks support
|
||||
.for t in fts_blocks_test glob_blocks_test scandir_blocks_test
|
||||
CFLAGS.${t}.c+= -fblocks
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*-
|
||||
* Copyright (c) 2025 Klara, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <fts.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
#include "fts_test.h"
|
||||
|
||||
ATF_TC(fts_unrdir);
|
||||
ATF_TC_HEAD(fts_unrdir, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "unreadable directories");
|
||||
atf_tc_set_md_var(tc, "require.user", "unprivileged");
|
||||
}
|
||||
ATF_TC_BODY(fts_unrdir, tc)
|
||||
{
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100));
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400));
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
(char *[]){ "dir", NULL },
|
||||
FTS_PHYSICAL,
|
||||
(struct fts_expect[]){
|
||||
{ FTS_D, "dir", "dir" },
|
||||
{ FTS_D, "unr", "unr" },
|
||||
{ FTS_DNR, "unr", "unr" },
|
||||
{ FTS_D, "unx", "unx" },
|
||||
{ FTS_DP, "unx", "unx" },
|
||||
{ FTS_DP, "dir", "dir" },
|
||||
{ 0 }
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
ATF_TC(fts_unrdir_nochdir);
|
||||
ATF_TC_HEAD(fts_unrdir_nochdir, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "unreadable directories (nochdir)");
|
||||
atf_tc_set_md_var(tc, "require.user", "unprivileged");
|
||||
}
|
||||
ATF_TC_BODY(fts_unrdir_nochdir, tc)
|
||||
{
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100));
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400));
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
(char *[]){ "dir", NULL },
|
||||
FTS_PHYSICAL | FTS_NOCHDIR,
|
||||
(struct fts_expect[]){
|
||||
{ FTS_D, "dir", "dir" },
|
||||
{ FTS_D, "unr", "dir/unr" },
|
||||
{ FTS_DNR, "unr", "dir/unr" },
|
||||
{ FTS_D, "unx", "dir/unx" },
|
||||
{ FTS_DP, "unx", "dir/unx" },
|
||||
{ FTS_DP, "dir", "dir" },
|
||||
{ 0 }
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
fts_check_debug();
|
||||
ATF_TP_ADD_TC(tp, fts_unrdir);
|
||||
ATF_TP_ADD_TC(tp, fts_unrdir_nochdir);
|
||||
return (atf_no_error());
|
||||
}
|
||||
@@ -15,17 +15,7 @@
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
struct fts_expect {
|
||||
int fts_info;
|
||||
const char *fts_name;
|
||||
const char *fts_accpath;
|
||||
};
|
||||
|
||||
struct fts_testcase {
|
||||
char **paths;
|
||||
int fts_options;
|
||||
struct fts_expect *fts_expect;
|
||||
};
|
||||
#include "fts_test.h"
|
||||
|
||||
static char *all_paths[] = {
|
||||
"dir",
|
||||
@@ -37,20 +27,12 @@ static char *all_paths[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/* shorter name for dead links */
|
||||
#define FTS_DL FTS_SLNONE
|
||||
|
||||
/* are we being debugged? */
|
||||
static bool debug;
|
||||
|
||||
/*
|
||||
* Prepare the files and directories we will be inspecting.
|
||||
*/
|
||||
static void
|
||||
fts_options_prepare(const struct atf_tc *tc)
|
||||
{
|
||||
debug = !getenv("__RUNNING_INSIDE_ATF_RUN") &&
|
||||
isatty(STDERR_FILENO);
|
||||
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
|
||||
ATF_REQUIRE_EQ(0, close(creat("file", 0644)));
|
||||
ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
|
||||
@@ -60,49 +42,6 @@ fts_options_prepare(const struct atf_tc *tc)
|
||||
ATF_REQUIRE_EQ(0, symlink("noent", "dead"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexical order for reproducability.
|
||||
*/
|
||||
static int
|
||||
fts_options_compar(const FTSENT * const *a, const FTSENT * const *b)
|
||||
{
|
||||
return (strcmp((*a)->fts_name, (*b)->fts_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Run FTS with the specified paths and options and verify that it
|
||||
* produces the expected result in the correct order.
|
||||
*/
|
||||
static void
|
||||
fts_options_test(const struct atf_tc *tc, const struct fts_testcase *fts_tc)
|
||||
{
|
||||
FTS *fts;
|
||||
FTSENT *ftse;
|
||||
const struct fts_expect *expect = fts_tc->fts_expect;
|
||||
long level = 0;
|
||||
|
||||
fts = fts_open(fts_tc->paths, fts_tc->fts_options, fts_options_compar);
|
||||
ATF_REQUIRE_MSG(fts != NULL, "fts_open(): %m");
|
||||
while ((ftse = fts_read(fts)) != NULL && expect->fts_name != NULL) {
|
||||
if (expect->fts_info == FTS_DP)
|
||||
level--;
|
||||
if (debug) {
|
||||
fprintf(stderr, "%2ld %2d %s\n", level,
|
||||
ftse->fts_info, ftse->fts_name);
|
||||
}
|
||||
ATF_CHECK_STREQ(expect->fts_name, ftse->fts_name);
|
||||
ATF_CHECK_STREQ(expect->fts_accpath, ftse->fts_accpath);
|
||||
ATF_CHECK_INTEQ(expect->fts_info, ftse->fts_info);
|
||||
ATF_CHECK_INTEQ(level, ftse->fts_level);
|
||||
if (expect->fts_info == FTS_D)
|
||||
level++;
|
||||
expect++;
|
||||
}
|
||||
ATF_CHECK_EQ(NULL, ftse);
|
||||
ATF_CHECK_EQ(NULL, expect->fts_name);
|
||||
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
|
||||
}
|
||||
|
||||
ATF_TC(fts_options_logical);
|
||||
ATF_TC_HEAD(fts_options_logical, tc)
|
||||
{
|
||||
@@ -111,7 +50,7 @@ ATF_TC_HEAD(fts_options_logical, tc)
|
||||
ATF_TC_BODY(fts_options_logical, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_LOGICAL,
|
||||
(struct fts_expect[]){
|
||||
@@ -162,7 +101,7 @@ ATF_TC_BODY(fts_options_logical_nostat, tc)
|
||||
*/
|
||||
atf_tc_expect_fail("FTS_LOGICAL nullifies FTS_NOSTAT");
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_LOGICAL | FTS_NOSTAT,
|
||||
(struct fts_expect[]){
|
||||
@@ -203,7 +142,7 @@ ATF_TC_HEAD(fts_options_logical_seedot, tc)
|
||||
ATF_TC_BODY(fts_options_logical_seedot, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_LOGICAL | FTS_SEEDOT,
|
||||
(struct fts_expect[]){
|
||||
@@ -252,7 +191,7 @@ ATF_TC_HEAD(fts_options_physical, tc)
|
||||
ATF_TC_BODY(fts_options_physical, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL,
|
||||
(struct fts_expect[]){
|
||||
@@ -278,7 +217,7 @@ ATF_TC_HEAD(fts_options_physical_nochdir, tc)
|
||||
ATF_TC_BODY(fts_options_physical_nochdir, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL | FTS_NOCHDIR,
|
||||
(struct fts_expect[]){
|
||||
@@ -304,7 +243,7 @@ ATF_TC_HEAD(fts_options_physical_comfollow, tc)
|
||||
ATF_TC_BODY(fts_options_physical_comfollow, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL | FTS_COMFOLLOW,
|
||||
(struct fts_expect[]){
|
||||
@@ -333,7 +272,7 @@ ATF_TC_HEAD(fts_options_physical_comfollowdir, tc)
|
||||
ATF_TC_BODY(fts_options_physical_comfollowdir, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL | FTS_COMFOLLOWDIR,
|
||||
(struct fts_expect[]){
|
||||
@@ -362,7 +301,7 @@ ATF_TC_HEAD(fts_options_physical_nostat, tc)
|
||||
ATF_TC_BODY(fts_options_physical_nostat, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL | FTS_NOSTAT,
|
||||
(struct fts_expect[]){
|
||||
@@ -388,7 +327,7 @@ ATF_TC_HEAD(fts_options_physical_nostat_type, tc)
|
||||
ATF_TC_BODY(fts_options_physical_nostat_type, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL | FTS_NOSTAT_TYPE,
|
||||
(struct fts_expect[]){
|
||||
@@ -414,7 +353,7 @@ ATF_TC_HEAD(fts_options_physical_seedot, tc)
|
||||
ATF_TC_BODY(fts_options_physical_seedot, tc)
|
||||
{
|
||||
fts_options_prepare(tc);
|
||||
fts_options_test(tc, &(struct fts_testcase){
|
||||
fts_test(tc, &(struct fts_testcase){
|
||||
all_paths,
|
||||
FTS_PHYSICAL | FTS_SEEDOT,
|
||||
(struct fts_expect[]){
|
||||
@@ -440,6 +379,7 @@ ATF_TC_BODY(fts_options_physical_seedot, tc)
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
fts_check_debug();
|
||||
ATF_TP_ADD_TC(tp, fts_options_logical);
|
||||
ATF_TP_ADD_TC(tp, fts_options_logical_nostat);
|
||||
ATF_TP_ADD_TC(tp, fts_options_logical_seedot);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*-
|
||||
* Copyright (c) 2025 Klara, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#ifndef FTS_TEST_H_INCLUDED
|
||||
#define FTS_TEST_H_INCLUDED
|
||||
|
||||
struct fts_expect {
|
||||
int fts_info;
|
||||
const char *fts_name;
|
||||
const char *fts_accpath;
|
||||
};
|
||||
|
||||
struct fts_testcase {
|
||||
char **paths;
|
||||
int fts_options;
|
||||
struct fts_expect *fts_expect;
|
||||
};
|
||||
|
||||
/* shorter name for dead links */
|
||||
#define FTS_DL FTS_SLNONE
|
||||
|
||||
/* are we being debugged? */
|
||||
static bool fts_test_debug;
|
||||
|
||||
/*
|
||||
* Set debug flag if appropriate.
|
||||
*/
|
||||
static void
|
||||
fts_check_debug(void)
|
||||
{
|
||||
fts_test_debug = !getenv("__RUNNING_INSIDE_ATF_RUN") &&
|
||||
isatty(STDERR_FILENO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexical order for reproducability.
|
||||
*/
|
||||
static int
|
||||
fts_lexical_compar(const FTSENT * const *a, const FTSENT * const *b)
|
||||
{
|
||||
return (strcmp((*a)->fts_name, (*b)->fts_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Run FTS with the specified paths and options and verify that it
|
||||
* produces the expected result in the correct order.
|
||||
*/
|
||||
static void
|
||||
fts_test(const struct atf_tc *tc, const struct fts_testcase *fts_tc)
|
||||
{
|
||||
FTS *fts;
|
||||
FTSENT *ftse;
|
||||
const struct fts_expect *expect = fts_tc->fts_expect;
|
||||
long level = 0;
|
||||
|
||||
fts = fts_open(fts_tc->paths, fts_tc->fts_options, fts_lexical_compar);
|
||||
ATF_REQUIRE_MSG(fts != NULL, "fts_open(): %m");
|
||||
while ((ftse = fts_read(fts)) != NULL && expect->fts_name != NULL) {
|
||||
if (expect->fts_info == FTS_DP || expect->fts_info == FTS_DNR)
|
||||
level--;
|
||||
if (fts_test_debug) {
|
||||
fprintf(stderr, "%2ld %2d %s\n", level,
|
||||
ftse->fts_info, ftse->fts_name);
|
||||
}
|
||||
ATF_CHECK_STREQ(expect->fts_name, ftse->fts_name);
|
||||
ATF_CHECK_STREQ(expect->fts_accpath, ftse->fts_accpath);
|
||||
ATF_CHECK_INTEQ(expect->fts_info, ftse->fts_info);
|
||||
ATF_CHECK_INTEQ(level, ftse->fts_level);
|
||||
if (expect->fts_info == FTS_D)
|
||||
level++;
|
||||
expect++;
|
||||
}
|
||||
ATF_CHECK_EQ(NULL, ftse);
|
||||
ATF_CHECK_EQ(NULL, expect->fts_name);
|
||||
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
|
||||
}
|
||||
|
||||
#endif /* FTS_TEST_H_INCLUDED */
|
||||
Reference in New Issue
Block a user