]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/mkdir.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
7 #include "alloc-util.h"
9 #include "format-util.h"
13 #include "path-util.h"
14 #include "stat-util.h"
15 #include "stdio-util.h"
16 #include "user-util.h"
18 int mkdir_safe_internal(
23 mkdir_func_t _mkdir
) {
29 assert(_mkdir
&& _mkdir
!= mkdir
);
31 if (_mkdir(path
, mode
) >= 0) {
32 r
= chmod_and_chown(path
, mode
, uid
, gid
);
37 if (lstat(path
, &st
) < 0)
40 if ((flags
& MKDIR_FOLLOW_SYMLINK
) && S_ISLNK(st
.st_mode
)) {
41 _cleanup_free_
char *p
= NULL
;
43 r
= chase_symlinks(path
, NULL
, CHASE_NONEXISTENT
, &p
, NULL
);
47 return mkdir_safe_internal(p
, mode
, uid
, gid
,
48 flags
& ~MKDIR_FOLLOW_SYMLINK
,
51 if (lstat(p
, &st
) < 0)
55 if (!S_ISDIR(st
.st_mode
))
56 return log_full_errno(flags
& MKDIR_WARN_MODE
? LOG_WARNING
: LOG_DEBUG
, SYNTHETIC_ERRNO(ENOTDIR
),
57 "Path \"%s\" already exists and is not a directory, refusing.", path
);
58 if ((st
.st_mode
& 0007) > (mode
& 0007) ||
59 (st
.st_mode
& 0070) > (mode
& 0070) ||
60 (st
.st_mode
& 0700) > (mode
& 0700))
61 return log_full_errno(flags
& MKDIR_WARN_MODE
? LOG_WARNING
: LOG_DEBUG
, SYNTHETIC_ERRNO(EEXIST
),
62 "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
63 path
, st
.st_mode
& 0777, mode
);
65 if ((uid
!= UID_INVALID
&& st
.st_uid
!= uid
) ||
66 (gid
!= GID_INVALID
&& st
.st_gid
!= gid
)) {
67 char u
[DECIMAL_STR_MAX(uid_t
)] = "-", g
[DECIMAL_STR_MAX(gid_t
)] = "-";
69 if (uid
!= UID_INVALID
)
70 xsprintf(u
, UID_FMT
, uid
);
71 if (gid
!= UID_INVALID
)
72 xsprintf(g
, GID_FMT
, gid
);
73 return log_full_errno(flags
& MKDIR_WARN_MODE
? LOG_WARNING
: LOG_DEBUG
, SYNTHETIC_ERRNO(EEXIST
),
74 "Directory \"%s\" already exists, but is owned by "UID_FMT
":"GID_FMT
" (%s:%s was requested), refusing.",
75 path
, st
.st_uid
, st
.st_gid
, u
, g
);
81 int mkdir_errno_wrapper(const char *pathname
, mode_t mode
) {
82 if (mkdir(pathname
, mode
) < 0)
87 int mkdirat_errno_wrapper(int dirfd
, const char *pathname
, mode_t mode
) {
88 if (mkdirat(dirfd
, pathname
, mode
) < 0)
93 int mkdir_safe(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, MkdirFlags flags
) {
94 return mkdir_safe_internal(path
, mode
, uid
, gid
, flags
, mkdir_errno_wrapper
);
97 int mkdir_parents_internal(const char *prefix
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, MkdirFlags flags
, mkdir_func_t _mkdir
) {
102 assert(_mkdir
!= mkdir
);
104 if (prefix
&& !path_startswith(path
, prefix
))
107 /* return immediately if directory exists */
108 e
= strrchr(path
, '/');
115 p
= strndupa(path
, e
- path
);
122 /* create every parent directory in the path, except the last component */
123 p
= path
+ strspn(path
, "/");
125 char t
[strlen(path
) + 1];
127 e
= p
+ strcspn(p
, "/");
128 p
= e
+ strspn(e
, "/");
130 /* Is this the last component? If so, then we're done */
134 memcpy(t
, path
, e
- path
);
137 if (prefix
&& path_startswith(prefix
, t
))
140 if (!uid_is_valid(uid
) && !gid_is_valid(gid
) && flags
== 0) {
142 if (r
< 0 && r
!= -EEXIST
)
145 r
= mkdir_safe_internal(t
, mode
, uid
, gid
, flags
, _mkdir
);
146 if (r
< 0 && r
!= -EEXIST
)
152 int mkdir_parents(const char *path
, mode_t mode
) {
153 return mkdir_parents_internal(NULL
, path
, mode
, UID_INVALID
, UID_INVALID
, 0, mkdir_errno_wrapper
);
156 int mkdir_parents_safe(const char *prefix
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, MkdirFlags flags
) {
157 return mkdir_parents_internal(prefix
, path
, mode
, uid
, gid
, flags
, mkdir_errno_wrapper
);
160 int mkdir_p_internal(const char *prefix
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, MkdirFlags flags
, mkdir_func_t _mkdir
) {
165 assert(_mkdir
!= mkdir
);
167 r
= mkdir_parents_internal(prefix
, path
, mode
, uid
, gid
, flags
, _mkdir
);
171 if (!uid_is_valid(uid
) && !gid_is_valid(gid
) && flags
== 0) {
172 r
= _mkdir(path
, mode
);
173 if (r
< 0 && (r
!= -EEXIST
|| is_dir(path
, true) <= 0))
176 r
= mkdir_safe_internal(path
, mode
, uid
, gid
, flags
, _mkdir
);
177 if (r
< 0 && r
!= -EEXIST
)
184 int mkdir_p(const char *path
, mode_t mode
) {
185 return mkdir_p_internal(NULL
, path
, mode
, UID_INVALID
, UID_INVALID
, 0, mkdir_errno_wrapper
);
188 int mkdir_p_safe(const char *prefix
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, MkdirFlags flags
) {
189 return mkdir_p_internal(prefix
, path
, mode
, uid
, gid
, flags
, mkdir_errno_wrapper
);
192 int mkdir_p_root(const char *root
, const char *p
, uid_t uid
, gid_t gid
, mode_t m
) {
193 _cleanup_free_
char *pp
= NULL
;
194 _cleanup_close_
int dfd
= -1;
198 pp
= dirname_malloc(p
);
203 if (!(path_equal(pp
, "/") || isempty(pp
) || path_equal(pp
, "."))) {
206 r
= mkdir_p_root(root
, pp
, uid
, gid
, m
);
212 if (path_equal(bn
, "/") || isempty(bn
) || path_equal(bn
, "."))
215 if (!filename_is_valid(bn
))
218 dfd
= chase_symlinks_and_open(pp
, root
, CHASE_PREFIX_ROOT
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
, NULL
);
222 if (mkdirat(dfd
, bn
, m
) < 0) {
229 if (uid_is_valid(uid
) || gid_is_valid(gid
)) {
230 _cleanup_close_
int nfd
= -1;
232 nfd
= openat(dfd
, bn
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
236 if (fchown(nfd
, uid
, gid
) < 0)