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