chiark / gitweb /
Prep v236 : Add missing SPDX-License-Identifier (2/9) src/basic
[elogind.git] / src / basic / mkdir.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <stdbool.h>
23 #include <string.h>
24 #include <sys/stat.h>
25
26 #include "alloc-util.h"
27 #include "fs-util.h"
28 #include "macro.h"
29 #include "mkdir.h"
30 #include "path-util.h"
31 #include "stat-util.h"
32 #include "user-util.h"
33
34 /// Additional includes needed by elogind
35 #include "missing.h"
36
37 int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink, mkdir_func_t _mkdir) {
38         struct stat st;
39         int r;
40
41         if (_mkdir(path, mode) >= 0) {
42                 r = chmod_and_chown(path, mode, uid, gid);
43                 if (r < 0)
44                         return r;
45         }
46
47         if (lstat(path, &st) < 0)
48                 return -errno;
49
50         if (follow_symlink && S_ISLNK(st.st_mode)) {
51                 _cleanup_free_ char *p = NULL;
52
53                 r = chase_symlinks(path, NULL, CHASE_NONEXISTENT, &p);
54                 if (r < 0)
55                         return r;
56                 if (r == 0)
57                         return mkdir_safe_internal(p, mode, uid, gid, false, _mkdir);
58
59                 if (lstat(p, &st) < 0)
60                         return -errno;
61         }
62
63         if ((st.st_mode & 0007) > (mode & 0007) ||
64             (st.st_mode & 0070) > (mode & 0070) ||
65             (st.st_mode & 0700) > (mode & 0700) ||
66             (uid != UID_INVALID && st.st_uid != uid) ||
67             (gid != GID_INVALID && st.st_gid != gid) ||
68             !S_ISDIR(st.st_mode))
69                 return -EEXIST;
70
71         return 0;
72 }
73
74 int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
75         return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir);
76 }
77
78 int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
79         const char *p, *e;
80         int r;
81
82         assert(path);
83
84         if (prefix && !path_startswith(path, prefix))
85                 return -ENOTDIR;
86
87         /* return immediately if directory exists */
88         e = strrchr(path, '/');
89         if (!e)
90                 return -EINVAL;
91
92         if (e == path)
93                 return 0;
94
95         p = strndupa(path, e - path);
96         r = is_dir(p, true);
97         if (r > 0)
98                 return 0;
99         if (r == 0)
100                 return -ENOTDIR;
101
102         /* create every parent directory in the path, except the last component */
103         p = path + strspn(path, "/");
104         for (;;) {
105                 char t[strlen(path) + 1];
106
107                 e = p + strcspn(p, "/");
108                 p = e + strspn(e, "/");
109
110                 /* Is this the last component? If so, then we're
111                  * done */
112                 if (*p == 0)
113                         return 0;
114
115                 memcpy(t, path, e - path);
116                 t[e-path] = 0;
117
118                 if (prefix && path_startswith(prefix, t))
119                         continue;
120
121                 r = _mkdir(t, mode);
122                 if (r < 0 && errno != EEXIST)
123                         return -errno;
124         }
125 }
126
127 int mkdir_parents(const char *path, mode_t mode) {
128         return mkdir_parents_internal(NULL, path, mode, mkdir);
129 }
130
131 int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
132         int r;
133
134         /* Like mkdir -p */
135
136         r = mkdir_parents_internal(prefix, path, mode, _mkdir);
137         if (r < 0)
138                 return r;
139
140         r = _mkdir(path, mode);
141         if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
142                 return -errno;
143
144         return 0;
145 }
146
147 int mkdir_p(const char *path, mode_t mode) {
148         return mkdir_p_internal(NULL, path, mode, mkdir);
149 }