• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19448983682

17 Nov 2025 11:32PM UTC coverage: 72.503% (-0.2%) from 72.719%
19448983682

push

github

web-flow
core/unit: unit_process_job() tweaks (#39753)

6 of 8 new or added lines in 1 file covered. (75.0%)

3363 existing lines in 68 files now uncovered.

308308 of 425234 relevant lines covered (72.5%)

1141671.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.0
/src/basic/glob-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <dirent.h>
4
#include <sys/stat.h>
5

6
#include "dirent-util.h"
7
#include "errno-util.h"
8
#include "glob-util.h"
9
#include "string-util.h"
10
#include "strv.h"
11

12
#ifndef __GLIBC__
13
static bool safe_glob_verify(const char *p, const char *prefix) {
14
        if (isempty(p))
15
                return false; /* should not happen, but for safey. */
16

17
        if (prefix) {
18
                /* Skip the prefix, as we allow dots in prefix.
19
                 * Note, glob() does not normalize paths, hence do not use path_startswith(). */
20
                p = startswith(p, prefix);
21
                if (!p)
22
                        return false; /* should not happen, but for safety. */
23
        }
24

25
        for (;;) {
26
                p += strspn(p, "/");
27
                if (*p == '\0')
28
                        return true;
29
                if (*p == '.') {
30
                        p++;
31
                        if (IN_SET(*p, '/', '\0'))
32
                                return false; /* refuse dot */
33
                        if (*p == '.') {
34
                                p++;
35
                                if (IN_SET(*p, '/', '\0'))
36
                                        return false; /* refuse dot dot */
37
                        }
38
                }
39

40
                p += strcspn(p, "/");
41
                if (*p == '\0')
42
                        return true;
43
        }
44
}
45

46
static int filter_glob_result(char * const *result, const char *path, char ***ret) {
47
        int r;
48

49
        assert(path);
50

51
        if (strv_isempty(result))
52
                return -ENOENT;
53

54
        _cleanup_free_ char *prefix = NULL;
55
        r = glob_non_glob_prefix(path, &prefix);
56
        if (r < 0 && r != -ENOENT)
57
                return r;
58

59
        _cleanup_strv_free_ char **filtered = NULL;
60
        size_t n_filtered = 0;
61
        STRV_FOREACH(p, result) {
62
                if (!safe_glob_verify(*p, prefix))
63
                        continue;
64

65
                if (!ret)
66
                        return 0; /* Found at least one entry, let's return earlier. */
67

68
                /* When musl is used, each entry is not a head of allocated memory. Hence, it is
69
                 * necessary to copy the string. */
70
                r = strv_extend_with_size(&filtered, &n_filtered, *p);
71
                if (r < 0)
72
                        return r;
73
        }
74

75
        if (n_filtered == 0)
76
                return -ENOENT;
77

78
        assert(ret);
79
        *ret = TAKE_PTR(filtered);
80
        return 0;
81
}
82
#endif
83

84
DEFINE_TRIVIAL_DESTRUCTOR(closedir_wrapper, void, closedir);
12,506✔
85

86
int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***ret) {
46,012✔
UNCOV
87
        _cleanup_(globfree) glob_t g = {
×
88
                .gl_closedir = closedir_wrapper,
89
                .gl_readdir = (struct dirent* (*)(void *)) readdir_no_dot,
90
                .gl_opendir = (void* (*)(const char *)) (opendir_func ?: opendir),
46,012✔
91
                .gl_lstat = lstat,
92
                .gl_stat = stat,
93
        };
94
        int r;
46,012✔
95

96
        assert(path);
46,012✔
97

98
        errno = 0;
46,012✔
99
        r = glob(path, flags | GLOB_ALTDIRFUNC, NULL, &g);
46,012✔
100
        if (r == GLOB_NOMATCH)
46,012✔
101
                return -ENOENT;
102
        if (r == GLOB_NOSPACE)
8,017✔
103
                return -ENOMEM;
104
        if (r != 0)
8,017✔
UNCOV
105
                return errno_or_else(EIO);
×
106

107
#ifndef __GLIBC__
108
        return filter_glob_result(g.gl_pathv, path, ret);
109
#endif
110

111
        if (strv_isempty(g.gl_pathv))
54,029✔
112
                return -ENOENT;
113

114
        if (ret) {
8,017✔
115
                *ret = g.gl_pathv;
8,017✔
116
                TAKE_STRUCT(g); /* To avoid the result being freed. */
8,017✔
117
        }
118

119
        return 0;
120
}
121

122
int glob_first(const char *path, char **ret) {
12✔
123
        _cleanup_strv_free_ char **v = NULL;
12✔
124
        int r;
12✔
125

126
        assert(path);
12✔
127

128
        r = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &v);
12✔
129
        if (r == -ENOENT) {
12✔
130
                if (ret)
6✔
131
                        *ret = NULL;
5✔
132
                return false;
6✔
133
        }
134
        if (r < 0)
6✔
135
                return r;
136

137
        assert(!strv_isempty(v));
6✔
138

139
        if (ret) {
6✔
140
                /* Free all results except for the first one. */
141
                STRV_FOREACH(p, strv_skip(v, 1))
3✔
UNCOV
142
                        *p = mfree(*p);
×
143

144
                /* Then, take the first result. */
145
                *ret = TAKE_PTR(*v);
3✔
146
        }
147

148
        return true;
149
}
150

151
int glob_extend(char ***strv, const char *path, int flags) {
4,134✔
152
        char **v;
4,134✔
153
        int r;
4,134✔
154

155
        assert(path);
4,134✔
156

157
        r = safe_glob(path, GLOB_NOSORT|GLOB_BRACE|flags, &v);
4,134✔
158
        if (r < 0)
4,134✔
159
                return r;
4,134✔
160

161
        return strv_extend_strv_consume(strv, v, /* filter_duplicates = */ false);
4,134✔
162
}
163

164
int glob_non_glob_prefix(const char *path, char **ret) {
3,390✔
165
        /* Return the path of the path that has no glob characters. */
166

167
        size_t n = strcspn(path, GLOB_CHARS);
3,390✔
168

169
        if (path[n] != '\0')
3,390✔
170
                while (n > 0 && path[n-1] != '/')
65,854✔
171
                        n--;
172

173
        if (n == 0)
3,390✔
174
                return -ENOENT;
175

176
        char *ans = strndup(path, n);
3,389✔
177
        if (!ans)
3,389✔
178
                return -ENOMEM;
179
        *ret = ans;
3,389✔
180
        return 0;
3,389✔
181
}
182

183
bool string_is_glob(const char *p) {
233,964✔
184
        /* Check if a string contains any glob patterns. */
185
        return !!strpbrk(p, GLOB_CHARS);
233,964✔
186
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc