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

systemd / systemd / 21192089104

20 Jan 2026 11:35PM UTC coverage: 72.524% (-0.3%) from 72.818%
21192089104

push

github

yuwata
mkdir: reset mtime *after* fchown()

Follow-up for 34c3d5747

Also, drop pointless shortcut.

1 of 2 new or added lines in 1 file covered. (50.0%)

2960 existing lines in 48 files now uncovered.

309808 of 427181 relevant lines covered (72.52%)

1236537.64 hits per line

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

92.37
/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 "hashmap.h"
13
#include "log.h"
14
#include "mkdir.h"
15
#include "path-util.h"
16
#include "stat-util.h"
17
#include "string-util.h"
18
#include "time-util.h"
19
#include "user-util.h"
20

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

30
        struct stat st;
31,182✔
31
        int r;
31,182✔
32

33
        assert(path);
31,182✔
34
        assert(mode != MODE_INVALID);
31,182✔
35
        assert(_mkdirat && _mkdirat != mkdirat);
31,182✔
36

37
        r = _mkdirat(dir_fd, path, mode);
31,182✔
38
        if (r >= 0)
31,182✔
39
                return chmod_and_chown_at(dir_fd, path, mode, uid, gid);
31,182✔
40
        if (r != -EEXIST)
23,603✔
41
                return r;
42

43
        if (fstatat(dir_fd, path, &st, AT_SYMLINK_NOFOLLOW) < 0)
23,602✔
44
                return -errno;
×
45

46
        if ((flags & MKDIR_FOLLOW_SYMLINK) && S_ISLNK(st.st_mode)) {
23,602✔
47
                _cleanup_free_ char *p = NULL;
3✔
48

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

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

61
        if (flags & MKDIR_IGNORE_EXISTING)
23,601✔
62
                return 0;
63

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

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

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

80
        return 0;
81
}
82

83
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
439,968✔
84
        return RET_NERRNO(mkdirat(dirfd, pathname, mode));
439,968✔
85
}
86

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

91
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) {
787,621✔
92
        const char *e = NULL;
787,621✔
93
        int r;
787,621✔
94

95
        assert(path);
787,621✔
96
        assert(_mkdirat != mkdirat);
787,621✔
97

98
        if (isempty(path))
787,621✔
99
                return 0;
787,621✔
100

101
        if (!path_is_safe(path))
787,621✔
102
                return -ENOTDIR;
103

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

111
        assert(e > path);
785,626✔
112
        assert(*e == '/');
785,626✔
113

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

122
        /* create every parent directory in the path, except the last component */
123
        for (const char *p = path;;) {
5,877✔
124
                char *s;
29,776✔
125
                int n;
29,776✔
126

127
                n = path_find_first_component(&p, /* accept_dot_dot= */ false, (const char **) &s);
29,776✔
128
                if (n <= 0)
29,776✔
129
                        return n;
5,877✔
130

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

135
                s[n] = '\0';
23,900✔
136

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

141
                s[n] = *p == '\0' ? '\0' : '/';
41,922✔
142
        }
143
}
144

145
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) {
441,574✔
146
        _cleanup_close_ int fd = AT_FDCWD;
441,574✔
147
        const char *p;
441,574✔
148

149
        assert(path);
441,574✔
150
        assert(_mkdirat != mkdirat);
441,574✔
151

152
        if (prefix) {
441,574✔
153
                p = path_startswith_full(path, prefix, PATH_STARTSWITH_REFUSE_DOT_DOT);
1,850✔
154
                if (!p)
1,850✔
155
                        return -EINVAL;
156

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

163
        return mkdirat_parents_internal(fd, p, mode, uid, gid, flags, _mkdirat);
441,571✔
164
}
165

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

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

174
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) {
441,207✔
175
        int r;
441,207✔
176

177
        /* Like mkdir -p */
178

179
        assert(_mkdirat != mkdirat);
441,207✔
180

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

185
        if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) {
881,712✔
186
                r = _mkdirat(AT_FDCWD, path, mode);
440,508✔
187
                if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
440,508✔
188
                        return r;
4✔
189
        } else {
190
                r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdirat);
696✔
191
                if (r < 0 && r != -EEXIST)
696✔
192
                        return r;
×
193
        }
194

195
        return 0;
196
}
197

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

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

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

211
        assert(p);
274✔
212

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

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

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

241
        XOpenFlags flags = 0;
142✔
242
        if (hashmap_contains(subvolumes, p)) {
142✔
243
                flags = XO_SUBVOLUME;
2✔
244
                if ((PTR_TO_INT(hashmap_get(subvolumes, p)) & BTRFS_SUBVOL_NODATACOW))
2✔
245
                        flags |= XO_NOCOW;
×
246
        }
247

248
        _cleanup_close_ int nfd = xopenat_full(
416✔
249
                                dfd, bn,
250
                                O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC,
251
                                flags,
252
                                m);
253
        if (nfd == -EEXIST)
142✔
254
                return 0;
255
        if (nfd < 0)
70✔
256
                return nfd;
257

258
        if ((uid_is_valid(uid) || gid_is_valid(gid)) && fchown(nfd, uid, gid) < 0)
140✔
NEW
259
                return -errno;
×
260

261
        if (ts != USEC_INFINITY) {
70✔
262
                struct timespec tspec;
3✔
263
                timespec_store(&tspec, ts);
3✔
264

265
                if (futimens(dfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0)
3✔
266
                        return -errno;
×
267

268
                if (futimens(nfd, (const struct timespec[2]) { tspec, tspec }) < 0)
3✔
269
                        return -errno;
×
270
        }
271

272
        return 1;
273
}
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