static int files_add(
DIR *dir,
const char *dirpath,
+ int rfd,
+ const char *root, /* for logging, can be NULL */
Hashmap **files,
Set **masked,
const char *suffix,
assert(dir);
assert(dirpath);
+ assert(rfd >= 0 || rfd == AT_FDCWD);
assert(files);
assert(masked);
- FOREACH_DIRENT(de, dir, return -errno) {
- _cleanup_free_ char *n = NULL, *p = NULL;
- struct stat st;
+ root = strempty(root);
+
+ FOREACH_DIRENT(de, dir, return log_debug_errno(errno, "Failed to read directory '%s/%s': %m",
+ root, skip_leading_slash(dirpath))) {
/* Does this match the suffix? */
if (suffix && !endswith(de->d_name, suffix))
/* Has this file already been found in an earlier directory? */
if (hashmap_contains(*files, de->d_name)) {
- log_debug("Skipping overridden file '%s/%s'.", dirpath, de->d_name);
+ log_debug("Skipping overridden file '%s/%s/%s'.",
+ root, skip_leading_slash(dirpath), de->d_name);
continue;
}
/* Has this been masked in an earlier directory? */
- if ((flags & CONF_FILES_FILTER_MASKED) && set_contains(*masked, de->d_name)) {
- log_debug("File '%s/%s' is masked by previous entry.", dirpath, de->d_name);
+ if ((flags & CONF_FILES_FILTER_MASKED) != 0 && set_contains(*masked, de->d_name)) {
+ log_debug("File '%s/%s/%s' is masked by previous entry.",
+ root, skip_leading_slash(dirpath), de->d_name);
continue;
}
- /* Read file metadata if we shall validate the check for file masks, for node types or whether the node is marked executable. */
- if (flags & (CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR|CONF_FILES_DIRECTORY|CONF_FILES_EXECUTABLE))
- if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) {
- log_debug_errno(errno, "Failed to stat '%s/%s', ignoring: %m", dirpath, de->d_name);
+ _cleanup_free_ char *p = path_join(dirpath, de->d_name);
+ if (!p)
+ return log_oom_debug();
+
+ _cleanup_free_ char *resolved_path = NULL;
+ bool need_stat = (flags & (CONF_FILES_FILTER_MASKED | CONF_FILES_REGULAR | CONF_FILES_DIRECTORY | CONF_FILES_EXECUTABLE)) != 0;
+ struct stat st;
+
+ if (!need_stat || FLAGS_SET(flags, CONF_FILES_FILTER_MASKED_BY_SYMLINK)) {
+
+ /* Even if no verification is requested, let's unconditionally call chaseat(),
+ * to drop unsafe symlinks. */
+
+ r = chaseat(rfd, p, CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &resolved_path, /* ret_fd = */ NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to chase '%s/%s', ignoring: %m",
+ root, skip_leading_slash(p));
continue;
}
+ if (r == 0 && FLAGS_SET(flags, CONF_FILES_FILTER_MASKED_BY_SYMLINK)) {
+
+ /* If the path points to /dev/null in a image or so, then the device node may not exist. */
+ if (path_equal(skip_leading_slash(resolved_path), "dev/null")) {
+ /* Mark this one as masked */
+ r = set_put_strdup(masked, de->d_name);
+ if (r < 0)
+ return log_oom_debug();
+
+ log_debug("File '%s/%s' is a mask (symlink to /dev/null).",
+ root, skip_leading_slash(p));
+ continue;
+ }
+
+ /* If the flag is set, we need to have stat, hence, skip the entry. */
+ log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to chase '%s/%s', ignoring: %m",
+ root, skip_leading_slash(p));
+ continue;
+ }
+
+ if (need_stat) {
+ r = fstatat(rfd, resolved_path, &st, AT_SYMLINK_NOFOLLOW);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to stat '%s/%s', ignoring: %m",
+ root, skip_leading_slash(p));
+ continue;
+ }
+ }
+
+ } else {
+ r = chase_and_statat(rfd, p, CHASE_AT_RESOLVE_IN_ROOT, &resolved_path, &st);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to chase and stat '%s/%s', ignoring: %m",
+ root, skip_leading_slash(p));
+ continue;
+ }
+ }
/* Is this a masking entry? */
if (FLAGS_SET(flags, CONF_FILES_FILTER_MASKED_BY_SYMLINK) && stat_may_be_dev_null(&st)) {
/* Mark this one as masked */
r = set_put_strdup(masked, de->d_name);
if (r < 0)
- return r;
+ return log_oom_debug();
- log_debug("File '%s/%s' is a mask (symlink to /dev/null).", dirpath, de->d_name);
+ log_debug("File '%s/%s' is a mask (symlink to /dev/null).", root, skip_leading_slash(p));
continue;
}
/* Mark this one as masked */
r = set_put_strdup(masked, de->d_name);
if (r < 0)
- return r;
+ return log_oom_debug();
- log_debug("File '%s/%s' is a mask (an empty file).", dirpath, de->d_name);
+ log_debug("File '%s/%s' is a mask (an empty file).", root, skip_leading_slash(p));
continue;
}
- /* Does this node have the right type? */
- if (flags & (CONF_FILES_REGULAR|CONF_FILES_DIRECTORY))
- if (!((flags & CONF_FILES_DIRECTORY) && S_ISDIR(st.st_mode)) &&
- !((flags & CONF_FILES_REGULAR) && S_ISREG(st.st_mode))) {
- log_debug("Ignoring '%s/%s', as it does not have the right type.", dirpath, de->d_name);
- continue;
- }
+ /* Is this node a regular file? */
+ if (FLAGS_SET(flags, CONF_FILES_REGULAR) && !S_ISREG(st.st_mode)) {
+ log_debug("Ignoring '%s/%s', as it is not a regular file.", root, skip_leading_slash(p));
+ continue;
+ }
- /* Does this node have the executable bit set? */
- if (flags & CONF_FILES_EXECUTABLE)
- /* As requested: check if the file is marked executable. Note that we don't check access(X_OK)
- * here, as we care about whether the file is marked executable at all, and not whether it is
- * executable for us, because if so, such errors are stuff we should log about. */
+ /* Is this node a directory? */
+ if (FLAGS_SET(flags, CONF_FILES_DIRECTORY) && !S_ISDIR(st.st_mode)) {
+ log_debug("Ignoring '%s/%s', as it is not a directory.", root, skip_leading_slash(p));
+ continue;
+ }
- if ((st.st_mode & 0111) == 0) { /* not executable */
- log_debug("Ignoring '%s/%s', as it is not marked executable.", dirpath, de->d_name);
- continue;
- }
+ /* Does this node have the executable bit set?
+ * As requested: check if the file is marked executable. Note that we don't check access(X_OK)
+ * here, as we care about whether the file is marked executable at all, and not whether it is
+ * executable for us, because if so, such errors are stuff we should log about. */
+ if (FLAGS_SET(flags, CONF_FILES_EXECUTABLE) && (st.st_mode & 0111) == 0) {
+ log_debug("Ignoring '%s/%s', as it is not marked executable.", root, skip_leading_slash(p));
+ continue;
+ }
- n = strdup(de->d_name);
+ _cleanup_free_ char *n = strdup(de->d_name);
if (!n)
- return -ENOMEM;
+ return log_oom_debug();
- if ((flags & CONF_FILES_BASENAME))
- r = hashmap_ensure_put(files, &string_hash_ops_free, n, n);
- else {
- p = path_join(dirpath, de->d_name);
- if (!p)
- return -ENOMEM;
-
- r = hashmap_ensure_put(files, &string_hash_ops_free_free, n, p);
+ r = hashmap_ensure_put(files, &string_hash_ops_free_free, n, p);
+ if (r < 0) {
+ assert(r == -ENOMEM);
+ return log_oom_debug();
}
- if (r < 0)
- return r;
assert(r > 0);
TAKE_PTR(n);
return 0;
}
-static int copy_and_sort_files_from_hashmap(Hashmap *fh, char ***ret) {
+static int copy_and_sort_files_from_hashmap(Hashmap *fh, const char *root, ConfFilesFlags flags, char ***ret) {
_cleanup_free_ char **sv = NULL;
- char **files;
+ _cleanup_strv_free_ char **files = NULL;
+ size_t n = 0;
int r;
assert(ret);
r = hashmap_dump_sorted(fh, (void***) &sv, /* ret_n = */ NULL);
if (r < 0)
- return r;
+ return log_oom_debug();
/* The entries in the array given by hashmap_dump_sorted() are still owned by the hashmap. */
- files = strv_copy(sv);
- if (!files)
- return -ENOMEM;
+ STRV_FOREACH(s, sv) {
+ _cleanup_free_ char *p = NULL;
- *ret = files;
+ if (FLAGS_SET(flags, CONF_FILES_BASENAME)) {
+ r = path_extract_filename(*s, &p);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract filename from '%s': %m", *s);
+ } else if (root) {
+ p = path_join(root, skip_leading_slash(*s));
+ if (!p)
+ return log_oom_debug();
+ }
+
+ if (p)
+ r = strv_consume_with_size(&files, &n, TAKE_PTR(p));
+ else
+ r = strv_extend_with_size(&files, &n, *s);
+ if (r < 0)
+ return log_oom_debug();
+ }
+
+ *ret = TAKE_PTR(files);
return 0;
}
-int conf_files_list_strv(
- char ***ret,
+static int conf_files_list_impl(
const char *suffix,
- const char *root,
+ int rfd,
+ const char *root, /* for logging, can be NULL */
ConfFilesFlags flags,
- const char * const *dirs) {
+ const char * const *dirs,
+ Hashmap **ret) {
_cleanup_hashmap_free_ Hashmap *fh = NULL;
_cleanup_set_free_ Set *masked = NULL;
int r;
+ assert(rfd >= 0 || rfd == AT_FDCWD);
assert(ret);
STRV_FOREACH(p, dirs) {
_cleanup_closedir_ DIR *dir = NULL;
_cleanup_free_ char *path = NULL;
- r = chase_and_opendir(*p, root, CHASE_PREFIX_ROOT, &path, &dir);
+ r = chase_and_opendirat(rfd, *p, CHASE_AT_RESOLVE_IN_ROOT, &path, &dir);
if (r < 0) {
if (r != -ENOENT)
- log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p);
+ log_debug_errno(r, "Failed to chase and open directory '%s%s', ignoring: %m", strempty(root), *p);
continue;
}
- r = files_add(dir, path, &fh, &masked, suffix, flags);
+ r = files_add(dir, path, rfd, root, &fh, &masked, suffix, flags);
if (r == -ENOMEM)
return r;
- if (r < 0)
- log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path);
}
- return copy_and_sort_files_from_hashmap(fh, ret);
+ *ret = TAKE_PTR(fh);
+ return 0;
+}
+
+int conf_files_list_strv(
+ char ***ret,
+ const char *suffix,
+ const char *root,
+ ConfFilesFlags flags,
+ const char * const *dirs) {
+
+ _cleanup_hashmap_free_ Hashmap *fh = NULL;
+ int r;
+
+ assert(ret);
+
+ _cleanup_close_ int rfd = open(empty_to_root(root), O_CLOEXEC|O_DIRECTORY|O_PATH);
+ if (rfd < 0)
+ return log_debug_errno(errno, "Failed to open '%s': %m", root);
+
+ r = conf_files_list_impl(suffix, rfd, root, flags, dirs, &fh);
+ if (r < 0)
+ return r;
+
+ return copy_and_sort_files_from_hashmap(fh, empty_to_root(root), flags, ret);
}
int conf_files_list_strv_at(
const char * const *dirs) {
_cleanup_hashmap_free_ Hashmap *fh = NULL;
- _cleanup_set_free_ Set *masked = NULL;
+ _cleanup_free_ char *root = NULL;
int r;
assert(rfd >= 0 || rfd == AT_FDCWD);
assert(ret);
- STRV_FOREACH(p, dirs) {
- _cleanup_closedir_ DIR *dir = NULL;
- _cleanup_free_ char *path = NULL;
-
- r = chase_and_opendirat(rfd, *p, CHASE_AT_RESOLVE_IN_ROOT, &path, &dir);
- if (r < 0) {
- if (r != -ENOENT)
- log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p);
- continue;
- }
+ if (rfd >= 0 && DEBUG_LOGGING)
+ (void) fd_get_path(rfd, &root); /* for logging */
- r = files_add(dir, path, &fh, &masked, suffix, flags);
- if (r == -ENOMEM)
- return r;
- if (r < 0)
- log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path);
- }
+ r = conf_files_list_impl(suffix, rfd, root, flags, dirs, &fh);
+ if (r < 0)
+ return r;
- return copy_and_sort_files_from_hashmap(fh, ret);
+ return copy_and_sort_files_from_hashmap(fh, /* root = */ NULL, flags, ret);
}
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path) {
#include "conf-files.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "tmpfile-util.h"
TEST(conf_files_list) {
- _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
- _cleanup_close_ int tfd = -EBADF;
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL, *t2 = NULL;
+ _cleanup_close_ int tfd = -EBADF, tfd2 = -EBADF;
_cleanup_strv_free_ char **result = NULL;
- const char *search1, *search2, *search1_a, *search1_b, *search1_c, *search2_aa;
+ const char *search1, *search2, *search3, *search1_a, *search1_b, *search1_c, *search2_aa, *search2_mm;
- tfd = mkdtemp_open("/tmp/test-conf-files-XXXXXX", O_PATH, &t);
- assert(tfd >= 0);
+ ASSERT_OK(tfd = mkdtemp_open("/tmp/test-conf-files-XXXXXX", O_PATH, &t));
+ ASSERT_OK(tfd2 = mkdtemp_open("/tmp/test-conf-files-XXXXXX", O_PATH, &t2));
- assert_se(mkdirat(tfd, "dir1", 0755) >= 0);
- assert_se(mkdirat(tfd, "dir2", 0755) >= 0);
+ ASSERT_OK_ERRNO(mkdirat(tfd, "dir1", 0755));
+ ASSERT_OK_ERRNO(mkdirat(tfd, "dir2", 0755));
+ ASSERT_OK_ERRNO(mkdirat(tfd, "dir3", 0755));
search1 = strjoina(t, "/dir1/");
search2 = strjoina(t, "/dir2/");
+ search3 = strjoina(t, "/dir3/");
FOREACH_STRING(p, "a.conf", "b.conf", "c.foo") {
_cleanup_free_ char *path = NULL;
assert_se(write_string_file(path, "foobar", WRITE_STRING_FILE_CREATE) >= 0);
}
- assert_se(symlinkat("/dev/null", tfd, "dir1/m.conf") >= 0);
+ ASSERT_OK_ERRNO(symlinkat("/dev/null", tfd, "dir1/m.conf"));
+ ASSERT_OK_ERRNO(symlinkat("../dev/null", tfd, "dir1/mm.conf"));
- FOREACH_STRING(p, "a.conf", "aa.conf", "m.conf") {
+ FOREACH_STRING(p, "a.conf", "aa.conf", "m.conf", "mm.conf") {
_cleanup_free_ char *path = NULL;
assert_se(path = path_join(search2, p));
assert_se(write_string_file(path, "hogehoge", WRITE_STRING_FILE_CREATE) >= 0);
}
+ ASSERT_OK(touch(strjoina(t2, "/absolute-empty.real")));
+ ASSERT_OK(symlinkat_atomic_full(strjoina(t2, "/absolute-empty.real"), AT_FDCWD, strjoina(search3, "absolute-empty.conf"), /* flags = */ 0));
+
+ ASSERT_OK(write_string_file_at(tfd2, "absolute-non-empty.real", "absolute-non-empty", WRITE_STRING_FILE_CREATE));
+ ASSERT_OK(symlinkat_atomic_full(strjoina(t2, "/absolute-non-empty.real"), AT_FDCWD, strjoina(search3, "absolute-non-empty.conf"), /* flags = */ 0));
+
+ ASSERT_OK(touch(strjoina(t2, "/relative-empty.real")));
+ ASSERT_OK(symlinkat_atomic_full(strjoina(t2, "/relative-empty.real"), AT_FDCWD, strjoina(search3, "relative-empty.conf"), SYMLINK_MAKE_RELATIVE));
+
+ ASSERT_OK(write_string_file_at(tfd2, "relative-non-empty.real", "relative-non-empty", WRITE_STRING_FILE_CREATE));
+ ASSERT_OK(symlinkat_atomic_full(strjoina(t2, "/relative-non-empty.real"), AT_FDCWD, strjoina(search3, "relative-non-empty.conf"), SYMLINK_MAKE_RELATIVE));
+
+ ASSERT_OK(touch(strjoina(t, "/absolute-empty-for-root.real")));
+ ASSERT_OK(symlinkat_atomic_full("/absolute-empty-for-root.real", AT_FDCWD, strjoina(search3, "absolute-empty-for-root.conf"), /* flags = */ 0));
+
+ ASSERT_OK(write_string_file_at(tfd, "absolute-non-empty-for-root.real", "absolute-non-empty", WRITE_STRING_FILE_CREATE));
+ ASSERT_OK(symlinkat_atomic_full("/absolute-non-empty-for-root.real", AT_FDCWD, strjoina(search3, "absolute-non-empty-for-root.conf"), /* flags = */ 0));
+
+ ASSERT_OK(touch(strjoina(t, "/relative-empty-for-root.real")));
+ ASSERT_OK(symlinkat_atomic_full("../../../../relative-empty-for-root.real", AT_FDCWD, strjoina(search3, "relative-empty-for-root.conf"), /* flags = */ 0));
+
+ ASSERT_OK(write_string_file_at(tfd, "relative-non-empty-for-root.real", "relative-non-empty", WRITE_STRING_FILE_CREATE));
+ ASSERT_OK(symlinkat_atomic_full("../../../../relative-non-empty-for-root.real", AT_FDCWD, strjoina(search3, "relative-non-empty-for-root.conf"), /* flags = */ 0));
+
search1_a = strjoina(search1, "a.conf");
search1_b = strjoina(search1, "b.conf");
search1_c = strjoina(search1, "c.foo");
search2_aa = strjoina(search2, "aa.conf");
+ search2_mm = strjoina(search2, "mm.conf");
/* search dir1 without suffix */
assert_se(conf_files_list(&result, NULL, NULL, CONF_FILES_FILTER_MASKED, search1) >= 0);
/* search two dirs */
assert_se(conf_files_list_strv(&result, ".conf", NULL, CONF_FILES_FILTER_MASKED, STRV_MAKE_CONST(search1, search2)) >= 0);
strv_print(result);
- assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b)));
+ assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b, search2_mm)));
result = strv_free(result);
assert_se(conf_files_list_strv_at(&result, ".conf", AT_FDCWD, CONF_FILES_FILTER_MASKED, STRV_MAKE_CONST(search1, search2)) >= 0);
strv_print(result);
- assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b)));
+ assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b, search2_mm)));
result = strv_free(result);
result = strv_free(result);
+ /* search dir3 */
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, /* root = */ NULL, CONF_FILES_FILTER_MASKED, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, /* root = */ NULL, CONF_FILES_FILTER_MASKED_BY_EMPTY, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, /* root = */ NULL, CONF_FILES_FILTER_MASKED_BY_SYMLINK, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-empty.conf"),
+ strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, /* root = */ NULL, CONF_FILES_REGULAR, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-empty.conf"),
+ strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, t, CONF_FILES_FILTER_MASKED, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-non-empty-for-root.conf"),
+ strjoina(search3, "relative-non-empty-for-root.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, t, CONF_FILES_FILTER_MASKED_BY_EMPTY, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-non-empty-for-root.conf"),
+ strjoina(search3, "relative-non-empty-for-root.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, t, CONF_FILES_FILTER_MASKED_BY_SYMLINK, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-empty-for-root.conf"),
+ strjoina(search3, "absolute-non-empty-for-root.conf"),
+ strjoina(search3, "relative-empty-for-root.conf"),
+ strjoina(search3, "relative-non-empty-for-root.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list(&result, /* suffix = */ NULL, t, CONF_FILES_REGULAR, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-empty-for-root.conf"),
+ strjoina(search3, "absolute-non-empty-for-root.conf"),
+ strjoina(search3, "relative-empty-for-root.conf"),
+ strjoina(search3, "relative-non-empty-for-root.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, AT_FDCWD, CONF_FILES_FILTER_MASKED, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, AT_FDCWD, CONF_FILES_FILTER_MASKED_BY_EMPTY, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, AT_FDCWD, CONF_FILES_FILTER_MASKED_BY_SYMLINK, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-empty.conf"),
+ strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, AT_FDCWD, CONF_FILES_REGULAR, search3));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE(strjoina(search3, "absolute-empty.conf"),
+ strjoina(search3, "absolute-non-empty.conf"),
+ strjoina(search3, "relative-empty.conf"),
+ strjoina(search3, "relative-non-empty.conf"))));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, tfd, CONF_FILES_FILTER_MASKED, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE("dir3/absolute-non-empty-for-root.conf",
+ "dir3/relative-non-empty-for-root.conf")));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, tfd, CONF_FILES_FILTER_MASKED_BY_EMPTY, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE("dir3/absolute-non-empty-for-root.conf",
+ "dir3/relative-non-empty-for-root.conf")));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, tfd, CONF_FILES_FILTER_MASKED_BY_SYMLINK, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE("dir3/absolute-empty-for-root.conf",
+ "dir3/absolute-non-empty-for-root.conf",
+ "dir3/relative-empty-for-root.conf",
+ "dir3/relative-non-empty-for-root.conf")));
+ result = strv_free(result);
+
+ ASSERT_OK(conf_files_list_at(&result, /* suffix = */ NULL, tfd, CONF_FILES_REGULAR, "/dir3/"));
+ strv_print(result);
+ ASSERT_TRUE(strv_equal(result, STRV_MAKE("dir3/absolute-empty-for-root.conf",
+ "dir3/absolute-non-empty-for-root.conf",
+ "dir3/relative-empty-for-root.conf",
+ "dir3/relative-non-empty-for-root.conf")));
+ result = strv_free(result);
+
/* filename only */
assert_se(conf_files_list_strv(&result, ".conf", NULL, CONF_FILES_FILTER_MASKED | CONF_FILES_BASENAME, STRV_MAKE_CONST(search1, search2)) >= 0);
strv_print(result);
- assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf")));
+ assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf", "mm.conf")));
result = strv_free(result);
assert_se(conf_files_list_strv_at(&result, ".conf", AT_FDCWD, CONF_FILES_FILTER_MASKED | CONF_FILES_BASENAME, STRV_MAKE_CONST(search1, search2)) >= 0);
strv_print(result);
- assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf")));
+ assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf", "mm.conf")));
result = strv_free(result);