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

systemd / systemd / 18958539692

30 Oct 2025 09:15PM UTC coverage: 72.046% (-0.2%) from 72.245%
18958539692

push

github

web-flow
importd: port export-tar code to use the one systemd-dissect already uses (#39405)

Split out of #38728.

(Testcase is part of that PR)

92 of 135 new or added lines in 5 files covered. (68.15%)

4530 existing lines in 57 files now uncovered.

304067 of 422048 relevant lines covered (72.05%)

1172093.27 hits per line

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

92.97
/src/basic/mkdir.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <unistd.h>
4

5
#include "alloc-util.h"
6
#include "btrfs.h"
7
#include "chase.h"
8
#include "errno-util.h"
9
#include "fd-util.h"
10
#include "format-util.h"
11
#include "fs-util.h"
12
#include "log.h"
13
#include "mkdir.h"
14
#include "path-util.h"
15
#include "stat-util.h"
16
#include "string-util.h"
17
#include "time-util.h"
18
#include "user-util.h"
19

20
int mkdirat_safe_internal(
29,984✔
21
                int dir_fd,
22
                const char *path,
23
                mode_t mode,
24
                uid_t uid,
25
                gid_t gid,
26
                MkdirFlags flags,
27
                mkdirat_func_t _mkdirat) {
28

29
        struct stat st;
29,984✔
30
        int r;
29,984✔
31

32
        assert(path);
29,984✔
33
        assert(mode != MODE_INVALID);
29,984✔
34
        assert(_mkdirat && _mkdirat != mkdirat);
29,984✔
35

36
        r = _mkdirat(dir_fd, path, mode);
29,984✔
37
        if (r >= 0)
29,984✔
38
                return chmod_and_chown_at(dir_fd, path, mode, uid, gid);
29,984✔
39
        if (r != -EEXIST)
22,700✔
40
                return r;
41

42
        if (fstatat(dir_fd, path, &st, AT_SYMLINK_NOFOLLOW) < 0)
22,699✔
43
                return -errno;
×
44

45
        if ((flags & MKDIR_FOLLOW_SYMLINK) && S_ISLNK(st.st_mode)) {
22,699✔
46
                _cleanup_free_ char *p = NULL;
3✔
47

48
                r = chaseat(dir_fd, path, CHASE_NONEXISTENT, &p, NULL);
3✔
49
                if (r < 0)
3✔
50
                        return r;
51
                if (r == 0)
3✔
52
                        return mkdirat_safe_internal(dir_fd, p, mode, uid, gid,
1✔
53
                                                     flags & ~MKDIR_FOLLOW_SYMLINK,
1✔
54
                                                     _mkdirat);
55

56
                if (fstatat(dir_fd, p, &st, AT_SYMLINK_NOFOLLOW) < 0)
2✔
57
                        return -errno;
×
58
        }
59

60
        if (flags & MKDIR_IGNORE_EXISTING)
22,698✔
61
                return 0;
62

63
        if (!S_ISDIR(st.st_mode))
6,369✔
64
                return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(ENOTDIR),
1✔
65
                                      "Path \"%s\" already exists and is not a directory, refusing.", path);
66

67
        if ((st.st_mode & ~mode & 0777) != 0)
6,368✔
68
                return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
113✔
69
                                      "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
70
                                      path, st.st_mode & 0777, mode);
71

72
        if ((uid != UID_INVALID && st.st_uid != uid) ||
6,255✔
73
            (gid != GID_INVALID && st.st_gid != gid))
5,229✔
74
                return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
3✔
75
                                      "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
76
                                      path, st.st_uid, st.st_gid, uid != UID_INVALID ? FORMAT_UID(uid) : "-",
77
                                      gid != UID_INVALID ? FORMAT_GID(gid) : "-");
78

79
        return 0;
80
}
81

82
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
418,454✔
83
        return RET_NERRNO(mkdirat(dirfd, pathname, mode));
418,454✔
84
}
85

86
int mkdirat_safe(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
21✔
87
        return mkdirat_safe_internal(dir_fd, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
21✔
88
}
89

90
int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat) {
739,795✔
91
        const char *e = NULL;
739,795✔
92
        int r;
739,795✔
93

94
        assert(path);
739,795✔
95
        assert(_mkdirat != mkdirat);
739,795✔
96

97
        if (isempty(path))
739,795✔
98
                return 0;
739,795✔
99

100
        if (!path_is_safe(path))
739,795✔
101
                return -ENOTDIR;
102

103
        /* return immediately if directory exists */
104
        r = path_find_last_component(path, /* accept_dot_dot= */ false, &e, NULL);
739,795✔
105
        if (r <= 0) /* r == 0 means path is equivalent to prefix. */
739,795✔
106
                return r;
107
        if (e == path)
739,792✔
108
                return 0;
109

110
        assert(e > path);
737,836✔
111
        assert(*e == '/');
737,836✔
112

113
        /* drop the last component */
114
        path = strndupa_safe(path, e - path);
737,836✔
115
        r = is_dir_at(dir_fd, path, /* follow = */ true);
737,836✔
116
        if (r > 0)
737,836✔
117
                return 0;
118
        if (r == 0)
5,605✔
119
                return -ENOTDIR;
120

121
        /* create every parent directory in the path, except the last component */
122
        for (const char *p = path;;) {
5,605✔
123
                char *s;
28,652✔
124
                int n;
28,652✔
125

126
                n = path_find_first_component(&p, /* accept_dot_dot= */ false, (const char **) &s);
28,652✔
127
                if (n <= 0)
28,652✔
128
                        return n;
5,605✔
129

130
                assert(p);
23,048✔
131
                assert(s >= path);
23,048✔
132
                assert(IN_SET(s[n], '/', '\0'));
23,048✔
133

134
                s[n] = '\0';
23,048✔
135

136
                r = mkdirat_safe_internal(dir_fd, path, mode, uid, gid, flags | MKDIR_IGNORE_EXISTING, _mkdirat);
23,048✔
137
                if (r < 0 && r != -EEXIST)
23,048✔
138
                        return r;
139

140
                s[n] = *p == '\0' ? '\0' : '/';
40,490✔
141
        }
142
}
143

144
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat) {
419,879✔
145
        _cleanup_close_ int fd = AT_FDCWD;
419,879✔
146
        const char *p;
419,879✔
147

148
        assert(path);
419,879✔
149
        assert(_mkdirat != mkdirat);
419,879✔
150

151
        if (prefix) {
419,879✔
152
                p = path_startswith_full(path, prefix, PATH_STARTSWITH_REFUSE_DOT_DOT);
1,864✔
153
                if (!p)
1,864✔
154
                        return -EINVAL;
155

156
                fd = open(prefix, O_PATH|O_DIRECTORY|O_CLOEXEC);
1,863✔
157
                if (fd < 0)
1,863✔
158
                        return -errno;
2✔
159
        } else
160
                p = path;
161

162
        return mkdirat_parents_internal(fd, p, mode, uid, gid, flags, _mkdirat);
419,876✔
163
}
164

165
int mkdirat_parents(int dir_fd, const char *path, mode_t mode) {
175,347✔
166
        return mkdirat_parents_internal(dir_fd, path, mode, UID_INVALID, UID_INVALID, 0, mkdirat_errno_wrapper);
175,347✔
167
}
168

169
int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
252✔
170
        return mkdir_parents_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
252✔
171
}
172

173
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat) {
419,507✔
174
        int r;
419,507✔
175

176
        /* Like mkdir -p */
177

178
        assert(_mkdirat != mkdirat);
419,507✔
179

180
        r = mkdir_parents_internal(prefix, path, mode, uid, gid, flags | MKDIR_FOLLOW_SYMLINK, _mkdirat);
419,507✔
181
        if (r < 0)
419,507✔
182
                return r;
183

184
        if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) {
838,291✔
185
                r = _mkdirat(AT_FDCWD, path, mode);
418,787✔
186
                if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
418,787✔
187
                        return r;
3✔
188
        } else {
189
                r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdirat);
717✔
190
                if (r < 0 && r != -EEXIST)
717✔
191
                        return r;
×
192
        }
193

194
        return 0;
195
}
196

197
int mkdir_p(const char *path, mode_t mode) {
409,887✔
198
        return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdirat_errno_wrapper);
409,887✔
199
}
200

201
int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
1,733✔
202
        return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
1,733✔
203
}
204

205
int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes) {
259✔
206
        _cleanup_free_ char *pp = NULL, *bn = NULL;
259✔
207
        _cleanup_close_ int dfd = -EBADF;
259✔
208
        int r;
259✔
209

210
        assert(p);
259✔
211

212
        r = path_extract_directory(p, &pp);
259✔
213
        if (r == -EDESTADDRREQ) {
259✔
214
                /* only fname is passed, no prefix to operate on */
215
                dfd = open(".", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
×
216
                if (dfd < 0)
×
217
                        return -errno;
×
218
        } else if (r == -EADDRNOTAVAIL)
259✔
219
                /* only root dir or "." was passed, i.e. there is no parent to extract, in that case there's nothing to do. */
220
                return 0;
221
        else if (r < 0)
140✔
222
                return r;
223
        else {
224
                /* Extracting the parent dir worked, hence we aren't top-level? Recurse up first. */
225
                r = mkdir_p_root_full(root, pp, uid, gid, m, ts, subvolumes);
140✔
226
                if (r < 0)
140✔
227
                        return r;
228

229
                dfd = chase_and_open(pp, root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_DIRECTORY, NULL);
140✔
230
                if (dfd < 0)
140✔
231
                        return dfd;
232
        }
233

234
        r = path_extract_filename(p, &bn);
138✔
235
        if (r == -EADDRNOTAVAIL) /* Already top-level */
138✔
236
                return 0;
237
        if (r < 0)
138✔
238
                return r;
239

240
        _cleanup_close_ int nfd = xopenat_full(
533✔
241
                                dfd, bn,
242
                                O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC,
243
                                path_strv_contains(subvolumes, p) ? XO_SUBVOLUME : 0,
138✔
244
                                m);
245
        if (nfd == -EEXIST)
138✔
246
                return 0;
247
        if (nfd < 0)
66✔
248
                return nfd;
249

250
        if (ts == USEC_INFINITY && !uid_is_valid(uid) && !gid_is_valid(gid))
129✔
251
                return 1;
252

253
        if (ts != USEC_INFINITY) {
3✔
254
                struct timespec tspec;
3✔
255
                timespec_store(&tspec, ts);
3✔
256

257
                if (futimens(dfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0)
3✔
UNCOV
258
                        return -errno;
×
259

260
                if (futimens(nfd, (const struct timespec[2]) { tspec, tspec }) < 0)
3✔
261
                        return -errno;
×
262
        }
263

264
        if ((uid_is_valid(uid) || gid_is_valid(gid)) && fchown(nfd, uid, gid) < 0)
6✔
UNCOV
265
                return -errno;
×
266

267
        return 1;
268
}
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