glob-util: rework safe_glob()

Currently, callers of safe_glob() set an empty glob_t or glob_t with
opendir func, and all other components are always zero.
So, let's introduce safe_glob_full() which optionally takes opendir
function, rather than glob_t, and returns result strv, rather than
storing results in glob_t.
Also, introduce safe_glob() which is a trivial wrapper of
safe_glob_full() without opendir func.

No functional change, just refactoring.
This commit is contained in:
Yu Watanabe
2025-06-23 23:11:44 +09:00
parent 1b0c39458b
commit 2a5f950e56
6 changed files with 78 additions and 81 deletions
+47 -45
View File
@@ -6,81 +6,83 @@
#include "dirent-util.h"
#include "errno-util.h"
#include "glob-util.h"
#include "log.h"
#include "string-util.h"
#include "strv.h"
static void closedir_wrapper(void* v) {
(void) closedir(v);
}
DEFINE_TRIVIAL_DESTRUCTOR(closedir_wrapper, void, closedir);
int safe_glob(const char *path, int flags, glob_t *pglob) {
int k;
int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***ret) {
_cleanup_(globfree) glob_t g = {
.gl_closedir = closedir_wrapper,
.gl_readdir = (struct dirent* (*)(void *)) readdir_no_dot,
.gl_opendir = (void* (*)(const char *)) (opendir_func ?: opendir),
.gl_lstat = lstat,
.gl_stat = stat,
};
int r;
/* We want to set GLOB_ALTDIRFUNC ourselves, don't allow it to be set. */
assert(!(flags & GLOB_ALTDIRFUNC));
if (!pglob->gl_closedir)
pglob->gl_closedir = closedir_wrapper;
if (!pglob->gl_readdir)
pglob->gl_readdir = (struct dirent *(*)(void *)) readdir_no_dot;
if (!pglob->gl_opendir)
pglob->gl_opendir = (void *(*)(const char *)) opendir;
if (!pglob->gl_lstat)
pglob->gl_lstat = lstat;
if (!pglob->gl_stat)
pglob->gl_stat = stat;
assert(path);
errno = 0;
k = glob(path, flags | GLOB_ALTDIRFUNC, NULL, pglob);
if (k == GLOB_NOMATCH)
r = glob(path, flags | GLOB_ALTDIRFUNC, NULL, &g);
if (r == GLOB_NOMATCH)
return -ENOENT;
if (k == GLOB_NOSPACE)
if (r == GLOB_NOSPACE)
return -ENOMEM;
if (k != 0)
if (r != 0)
return errno_or_else(EIO);
if (strv_isempty(pglob->gl_pathv))
if (strv_isempty(g.gl_pathv))
return -ENOENT;
if (ret) {
*ret = g.gl_pathv;
TAKE_STRUCT(g); /* To avoid the result being freed. */
}
return 0;
}
int glob_first(const char *path, char **ret_first) {
_cleanup_globfree_ glob_t g = {};
int k;
int glob_first(const char *path, char **ret) {
_cleanup_strv_free_ char **v = NULL;
int r;
assert(path);
k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &g);
if (k == -ENOENT) {
if (ret_first)
*ret_first = NULL;
r = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &v);
if (r == -ENOENT) {
if (ret)
*ret = NULL;
return false;
}
if (k < 0)
return k;
if (r < 0)
return r;
if (ret_first) {
assert(g.gl_pathv && g.gl_pathv[0]);
assert(!strv_isempty(v));
char *first = strdup(g.gl_pathv[0]);
if (!first)
return log_oom_debug();
*ret_first = first;
if (ret) {
/* Free all results except for the first one. */
STRV_FOREACH(p, strv_skip(v, 1))
*p = mfree(*p);
/* Then, take the first result. */
*ret = TAKE_PTR(*v);
}
return true;
}
int glob_extend(char ***strv, const char *path, int flags) {
_cleanup_globfree_ glob_t g = {};
int k;
char **v;
int r;
k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE|flags, &g);
if (k < 0)
return k;
assert(path);
return strv_extend_strv(strv, g.gl_pathv, false);
r = safe_glob(path, GLOB_NOSORT|GLOB_BRACE|flags, &v);
if (r < 0)
return r;
return strv_extend_strv_consume(strv, v, /* filter_duplicates = */ false);
}
int glob_non_glob_prefix(const char *path, char **ret) {
+7 -3
View File
@@ -5,11 +5,15 @@
#include "forward.h"
/* Note: this function modifies pglob to set various functions. */
int safe_glob(const char *path, int flags, glob_t *pglob);
typedef DIR* (*opendir_t)(const char *);
int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***ret);
static inline int safe_glob(const char *path, int flags, char ***ret) {
return safe_glob_full(path, flags, NULL, ret);
}
/* Note: which match is returned depends on the implementation/system and not guaranteed to be stable */
int glob_first(const char *path, char **ret_first);
int glob_first(const char *path, char **ret);
#define glob_exists(path) glob_first(path, NULL)
int glob_extend(char ***strv, const char *path, int flags);