Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/util/mkdir_parents.c
3848 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2009-2022 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <sys/stat.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#ifdef HAVE_STDBOOL_H
25
# include <stdbool.h>
26
#else
27
# include <compat/stdbool.h>
28
#endif /* HAVE_STDBOOL_H */
29
#include <string.h>
30
#include <unistd.h>
31
#include <dirent.h>
32
#include <errno.h>
33
#include <fcntl.h>
34
#include <limits.h>
35
36
#include <sudo_compat.h>
37
#include <sudo_fatal.h>
38
#include <sudo_gettext.h>
39
#include <sudo_debug.h>
40
#include <sudo_util.h>
41
42
/*
43
* Returns true if fd is a directory, else false.
44
* Warns on failure if not quiet.
45
*/
46
static bool
47
is_dir(int dfd, const char *name, int namelen, bool quiet)
48
{
49
struct stat sb;
50
debug_decl(is_dir, SUDO_DEBUG_UTIL);
51
52
if (fstat(dfd, &sb) != 0) {
53
if (!quiet) {
54
sudo_warn(U_("unable to stat %.*s"), namelen, name);
55
}
56
debug_return_bool(false);
57
}
58
if (!S_ISDIR(sb.st_mode)) {
59
if (!quiet) {
60
sudo_warnx(U_("%.*s exists but is not a directory (0%o)"),
61
namelen, name, (unsigned int) sb.st_mode);
62
}
63
debug_return_bool(false);
64
}
65
66
debug_return_bool(true);
67
}
68
69
/*
70
* Create any parent directories needed by path (but not path itself)
71
* and return an open fd for the parent directory or -1 on error.
72
*/
73
int
74
sudo_open_parent_dir_v1(const char *path, uid_t uid, gid_t gid, mode_t mode,
75
bool quiet)
76
{
77
const char *cp, *ep, *pathend;
78
char name[PATH_MAX];
79
int parentfd;
80
debug_decl(sudo_open_parent_dir, SUDO_DEBUG_UTIL);
81
82
/* Starting parent dir is either root or cwd. */
83
cp = path;
84
if (*cp == '/') {
85
do {
86
cp++;
87
} while (*cp == '/');
88
parentfd = open("/", O_RDONLY|O_NONBLOCK|O_DIRECTORY);
89
} else {
90
parentfd = open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY);
91
}
92
if (parentfd == -1) {
93
if (!quiet)
94
sudo_warn(U_("unable to open %s"), *path == '/' ? "/" : ".");
95
debug_return_int(-1);
96
}
97
98
/* Iterate over path components, skipping the last one. */
99
pathend = cp + strlen(cp);
100
for (cp = sudo_strsplit(cp, pathend, "/", &ep); cp != NULL && ep < pathend;
101
cp = sudo_strsplit(NULL, pathend, "/", &ep)) {
102
size_t len = (size_t)(ep - cp);
103
int dfd;
104
105
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
106
"mkdir %.*s, mode 0%o, uid %d, gid %d", (int)(ep - path), path,
107
(unsigned int)mode, (int)uid, (int)gid);
108
if (len >= sizeof(name)) {
109
errno = ENAMETOOLONG;
110
if (!quiet)
111
sudo_warn(U_("unable to mkdir %.*s"), (int)(ep - path), path);
112
goto bad;
113
}
114
memcpy(name, cp, len);
115
name[len] = '\0';
116
reopen:
117
dfd = openat(parentfd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY, 0);
118
if (dfd == -1) {
119
if (errno != ENOENT) {
120
if (!quiet) {
121
sudo_warn(U_("unable to open %.*s"),
122
(int)(ep - path), path);
123
}
124
goto bad;
125
}
126
if (mkdirat(parentfd, name, mode) == 0) {
127
dfd = openat(parentfd, name,
128
O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW, 0);
129
if (dfd == -1) {
130
if (!quiet) {
131
sudo_warn(U_("unable to open %.*s"),
132
(int)(ep - path), path);
133
}
134
goto bad;
135
}
136
/* Make sure the path we created is still a directory. */
137
if (!is_dir(dfd, path, (int)(ep - path), quiet)) {
138
close(dfd);
139
goto bad;
140
}
141
if (uid != (uid_t)-1 && gid != (gid_t)-1) {
142
if (fchown(dfd, uid, gid) != 0) {
143
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
144
"%s: unable to chown %d:%d %.*s", __func__,
145
(int)uid, (int)gid, (int)(ep - path), path);
146
}
147
}
148
} else {
149
if (errno == EEXIST)
150
goto reopen;
151
if (!quiet) {
152
sudo_warn(U_("unable to mkdir %.*s"),
153
(int)(ep - path), path);
154
}
155
goto bad;
156
}
157
} else {
158
/* Already exists, make sure it is a directory. */
159
if (!is_dir(dfd, path, (int)(ep - path), quiet)) {
160
close(dfd);
161
goto bad;
162
}
163
}
164
close(parentfd);
165
parentfd = dfd;
166
}
167
168
debug_return_int(parentfd);
169
bad:
170
if (parentfd != -1)
171
close(parentfd);
172
debug_return_int(-1);
173
}
174
175
/*
176
* Create any parent directories needed by path (but not path itself).
177
* Not currently used.
178
*/
179
bool
180
sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode,
181
bool quiet)
182
{
183
int fd;
184
debug_decl(sudo_mkdir_parents, SUDO_DEBUG_UTIL);
185
186
fd = sudo_open_parent_dir(path, uid, gid, mode, quiet);
187
if (fd == -1)
188
debug_return_bool(false);
189
close(fd);
190
debug_return_bool(true);
191
}
192
193