Branch data Line data Source code
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 : 4 : int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) {
19 : : struct stat st;
20 : : int r;
21 : :
22 [ - + ]: 4 : assert(_mkdir != mkdir);
23 : :
24 [ + - ]: 4 : if (_mkdir(path, mode) >= 0) {
25 : 4 : r = chmod_and_chown(path, mode, uid, gid);
26 [ - + ]: 4 : if (r < 0)
27 : 0 : return r;
28 : : }
29 : :
30 [ - + ]: 4 : if (lstat(path, &st) < 0)
31 : 0 : return -errno;
32 : :
33 [ - + # # ]: 4 : if ((flags & MKDIR_FOLLOW_SYMLINK) && S_ISLNK(st.st_mode)) {
34 [ # # ]: 0 : _cleanup_free_ char *p = NULL;
35 : :
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 : : _mkdir);
43 : :
44 [ # # ]: 0 : if (lstat(p, &st) < 0)
45 : 0 : return -errno;
46 : : }
47 : :
48 [ - + ]: 4 : if (!S_ISDIR(st.st_mode)) {
49 [ # # # # ]: 0 : log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
50 : : "Path \"%s\" already exists and is not a directory, refusing.", path);
51 : 0 : return -ENOTDIR;
52 : : }
53 [ + - ]: 4 : if ((st.st_mode & 0007) > (mode & 0007) ||
54 [ + - ]: 4 : (st.st_mode & 0070) > (mode & 0070) ||
55 [ - + ]: 4 : (st.st_mode & 0700) > (mode & 0700)) {
56 [ # # # # ]: 0 : log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
57 : : "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
58 : : path, st.st_mode & 0777, mode);
59 : 0 : return -EEXIST;
60 : : }
61 [ + - + - : 4 : if ((uid != UID_INVALID && st.st_uid != uid) ||
+ - ]
62 [ - + ]: 4 : (gid != GID_INVALID && st.st_gid != gid)) {
63 : 0 : char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-";
64 : :
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 : : "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
71 : : path, st.st_uid, st.st_gid, u, g);
72 : 0 : return -EEXIST;
73 : : }
74 : :
75 : 4 : return 0;
76 : : }
77 : :
78 : 1152 : int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
79 [ + + ]: 1152 : if (mkdir(pathname, mode) < 0)
80 : 892 : return -errno;
81 : 260 : return 0;
82 : : }
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 : : }
89 : :
90 : 4 : int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
91 : 4 : return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_errno_wrapper);
92 : : }
93 : :
94 : 856 : int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
95 : : const char *p, *e;
96 : : int r;
97 : :
98 [ - + ]: 856 : assert(path);
99 [ - + ]: 856 : assert(_mkdir != mkdir);
100 : :
101 [ - + # # ]: 856 : if (prefix && !path_startswith(path, prefix))
102 : 0 : return -ENOTDIR;
103 : :
104 : : /* return immediately if directory exists */
105 : 856 : e = strrchr(path, '/');
106 [ - + ]: 856 : if (!e)
107 : 0 : return -EINVAL;
108 : :
109 [ - + ]: 856 : if (e == path)
110 : 0 : return 0;
111 : :
112 : 856 : p = strndupa(path, e - path);
113 : 856 : r = is_dir(p, true);
114 [ + + ]: 856 : if (r > 0)
115 : 648 : return 0;
116 [ - + ]: 208 : if (r == 0)
117 : 0 : return -ENOTDIR;
118 : :
119 : : /* create every parent directory in the path, except the last component */
120 : 208 : p = path + strspn(path, "/");
121 : 928 : for (;;) {
122 : 1136 : char t[strlen(path) + 1];
123 : :
124 : 1136 : e = p + strcspn(p, "/");
125 : 1136 : p = e + strspn(e, "/");
126 : :
127 : : /* Is this the last component? If so, then we're done */
128 [ + + ]: 1136 : if (*p == 0)
129 : 208 : return 0;
130 : :
131 : 928 : memcpy(t, path, e - path);
132 : 928 : t[e-path] = 0;
133 : :
134 [ - + # # ]: 928 : if (prefix && path_startswith(prefix, t))
135 : 0 : continue;
136 : :
137 : 928 : r = _mkdir(t, mode);
138 [ + + - + ]: 928 : if (r < 0 && r != -EEXIST)
139 : 0 : return r;
140 : : }
141 : : }
142 : :
143 : 296 : int mkdir_parents(const char *path, mode_t mode) {
144 : 296 : return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper);
145 : : }
146 : :
147 : 28 : int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
148 : : int r;
149 : :
150 : : /* Like mkdir -p */
151 : :
152 [ - + ]: 28 : assert(_mkdir != mkdir);
153 : :
154 : 28 : r = mkdir_parents_internal(prefix, path, mode, _mkdir);
155 [ - + ]: 28 : if (r < 0)
156 : 0 : return r;
157 : :
158 : 28 : r = _mkdir(path, mode);
159 [ - + # # : 28 : if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
# # ]
160 : 0 : return r;
161 : :
162 : 28 : return 0;
163 : : }
164 : :
165 : 24 : int mkdir_p(const char *path, mode_t mode) {
166 : 24 : return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper);
167 : : }
|