Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/basic/mkdir.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
3
#include <errno.h>
4
#include <stdbool.h>
5
#include <string.h>
6
#include <sys/stat.h>
7
8
#include "alloc-util.h"
9
#include "format-util.h"
10
#include "fs-util.h"
11
#include "macro.h"
12
#include "mkdir.h"
13
#include "path-util.h"
14
#include "stat-util.h"
15
#include "stdio-util.h"
16
#include "user-util.h"
17
18
0
int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) {
19
0
        struct stat st;
20
0
        int r;
21
0
22
0
        assert(_mkdir != mkdir);
23
0
24
0
        if (_mkdir(path, mode) >= 0) {
25
0
                r = chmod_and_chown(path, mode, uid, gid);
26
0
                if (r < 0)
27
0
                        return r;
28
0
        }
29
0
30
0
        if (lstat(path, &st) < 0)
31
0
                return -errno;
32
0
33
0
        if ((flags & MKDIR_FOLLOW_SYMLINK) && S_ISLNK(st.st_mode)) {
34
0
                _cleanup_free_ char *p = NULL;
35
0
36
0
                r = chase_symlinks(path, NULL, CHASE_NONEXISTENT, &p);
37
0
                if (r < 0)
38
0
                        return r;
39
0
                if (r == 0)
40
0
                        return mkdir_safe_internal(p, mode, uid, gid,
41
0
                                                   flags & ~MKDIR_FOLLOW_SYMLINK,
42
0
                                                   _mkdir);
43
0
44
0
                if (lstat(p, &st) < 0)
45
0
                        return -errno;
46
0
        }
47
0
48
0
        if (!S_ISDIR(st.st_mode)) {
49
0
                log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
50
0
                         "Path \"%s\" already exists and is not a directory, refusing.", path);
51
0
                return -ENOTDIR;
52
0
        }
53
0
        if ((st.st_mode & 0007) > (mode & 0007) ||
54
0
            (st.st_mode & 0070) > (mode & 0070) ||
55
0
            (st.st_mode & 0700) > (mode & 0700)) {
56
0
                log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
57
0
                         "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
58
0
                         path, st.st_mode & 0777, mode);
59
0
                return -EEXIST;
60
0
        }
61
0
        if ((uid != UID_INVALID && st.st_uid != uid) ||
62
0
            (gid != GID_INVALID && st.st_gid != gid)) {
63
0
                char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-";
64
0
65
0
                if (uid != UID_INVALID)
66
0
                        xsprintf(u, UID_FMT, uid);
67
0
                if (gid != UID_INVALID)
68
0
                        xsprintf(g, GID_FMT, gid);
69
0
                log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
70
0
                         "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
71
0
                         path, st.st_uid, st.st_gid, u, g);
72
0
                return -EEXIST;
73
0
        }
74
0
75
0
        return 0;
76
0
}
77
78
16.6k
int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
79
16.6k
        if (mkdir(pathname, mode) < 0)
80
5.92k
                return -errno;
81
10.7k
        return 0;
82
10.7k
}
83
84
0
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
85
0
        if (mkdirat(dirfd, pathname, mode) < 0)
86
0
                return -errno;
87
0
        return 0;
88
0
}
89
90
0
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
91
0
        return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_errno_wrapper);
92
0
}
93
94
5.92k
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
95
5.92k
        const char *p, *e;
96
5.92k
        int r;
97
5.92k
98
5.92k
        assert(path);
99
5.92k
        assert(_mkdir != mkdir);
100
5.92k
101
5.92k
        if (prefix && !path_startswith(path, prefix))
102
0
                return -ENOTDIR;
103
5.92k
104
5.92k
        /* return immediately if directory exists */
105
5.92k
        e = strrchr(path, '/');
106
5.92k
        if (!e)
107
0
                return -EINVAL;
108
5.92k
109
5.92k
        if (e == path)
110
0
                return 0;
111
5.92k
112
5.92k
        p = strndupa(path, e - path);
113
5.92k
        r = is_dir(p, true);
114
5.92k
        if (r > 0)
115
546
                return 0;
116
5.37k
        if (r == 0)
117
0
                return -ENOTDIR;
118
5.37k
119
5.37k
        /* create every parent directory in the path, except the last component */
120
5.37k
        p = path + strspn(path, "/");
121
16.1k
        for (;;) {
122
16.1k
                char t[strlen(path) + 1];
123
16.1k
124
16.1k
                e = p + strcspn(p, "/");
125
16.1k
                p = e + strspn(e, "/");
126
16.1k
127
16.1k
                /* Is this the last component? If so, then we're done */
128
16.1k
                if (*p == 0)
129
5.37k
                        return 0;
130
10.7k
131
10.7k
                memcpy(t, path, e - path);
132
10.7k
                t[e-path] = 0;
133
10.7k
134
10.7k
                if (prefix && path_startswith(prefix, t))
135
0
                        continue;
136
10.7k
137
10.7k
                r = _mkdir(t, mode);
138
10.7k
                if (r < 0 && r != -EEXIST)
139
10.7k
                        return r;
140
10.7k
        }
141
5.37k
}
142
143
0
int mkdir_parents(const char *path, mode_t mode) {
144
0
        return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper);
145
0
}
146
147
5.92k
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
148
5.92k
        int r;
149
5.92k
150
5.92k
        /* Like mkdir -p */
151
5.92k
152
5.92k
        assert(_mkdir != mkdir);
153
5.92k
154
5.92k
        r = mkdir_parents_internal(prefix, path, mode, _mkdir);
155
5.92k
        if (r < 0)
156
0
                return r;
157
5.92k
158
5.92k
        r = _mkdir(path, mode);
159
5.92k
        if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
160
0
                return r;
161
5.92k
162
5.92k
        return 0;
163
5.92k
}
164
165
5.92k
int mkdir_p(const char *path, mode_t mode) {
166
5.92k
        return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper);
167
5.92k
}