blob: 8d364b74f3a7fdb516a363694f5b7fa65393e1cf [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tao Baocfe53c22017-10-03 14:37:21 -070017#include "otautil/DirUtil.h"
Elliott Hughes4bbd5bf2016-04-01 18:24:39 -070018
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080019#include <dirent.h>
Tao Baoac3d1ed2017-07-23 00:01:02 -070020#include <errno.h>
21#include <stdlib.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <unistd.h>
Ethan Yonker58f21322018-08-24 11:17:36 -050025#include <utime.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080026
Yabin Cui4425c1d2016-02-10 13:47:32 -080027#include <string>
28
Elliott Hughes4bbd5bf2016-04-01 18:24:39 -070029#include <selinux/label.h>
30#include <selinux/selinux.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080031
Tao Baoac3d1ed2017-07-23 00:01:02 -070032enum class DirStatus { DMISSING, DDIR, DILLEGAL };
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080033
Tao Baoac3d1ed2017-07-23 00:01:02 -070034static DirStatus dir_status(const std::string& path) {
35 struct stat sb;
36 if (stat(path.c_str(), &sb) == 0) {
37 // Something's there; make sure it's a directory.
38 if (S_ISDIR(sb.st_mode)) {
39 return DirStatus::DDIR;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080040 }
Tao Baoac3d1ed2017-07-23 00:01:02 -070041 errno = ENOTDIR;
42 return DirStatus::DILLEGAL;
43 } else if (errno != ENOENT) {
44 // Something went wrong, or something in the path is bad. Can't do anything in this situation.
45 return DirStatus::DILLEGAL;
46 }
47 return DirStatus::DMISSING;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080048}
49
Tao Baoac3d1ed2017-07-23 00:01:02 -070050int mkdir_recursively(const std::string& input_path, mode_t mode, bool strip_filename,
51 const selabel_handle* sehnd) {
52 // Check for an empty string before we bother making any syscalls.
53 if (input_path.empty()) {
54 errno = ENOENT;
55 return -1;
56 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080057
Tao Baoac3d1ed2017-07-23 00:01:02 -070058 // Allocate a path that we can modify; stick a slash on the end to make things easier.
59 std::string path = input_path;
60 if (strip_filename) {
61 // Strip everything after the last slash.
62 size_t pos = path.rfind('/');
63 if (pos == std::string::npos) {
64 errno = ENOENT;
65 return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080066 }
Tao Baoac3d1ed2017-07-23 00:01:02 -070067 path.resize(pos + 1);
68 } else {
69 // Make sure that the path ends in a slash.
70 path.push_back('/');
71 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080072
Tao Baoac3d1ed2017-07-23 00:01:02 -070073 // See if it already exists.
74 DirStatus ds = dir_status(path);
75 if (ds == DirStatus::DDIR) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080076 return 0;
Tao Baoac3d1ed2017-07-23 00:01:02 -070077 } else if (ds == DirStatus::DILLEGAL) {
78 return -1;
79 }
80
81 // Walk up the path from the root and make each level.
82 size_t prev_end = 0;
83 while (prev_end < path.size()) {
84 size_t next_end = path.find('/', prev_end + 1);
85 if (next_end == std::string::npos) {
86 break;
87 }
88 std::string dir_path = path.substr(0, next_end);
89 // Check this part of the path and make a new directory if necessary.
90 switch (dir_status(dir_path)) {
91 case DirStatus::DILLEGAL:
92 // Could happen if some other process/thread is messing with the filesystem.
93 return -1;
94 case DirStatus::DMISSING: {
95 char* secontext = nullptr;
96 if (sehnd) {
97 selabel_lookup(const_cast<selabel_handle*>(sehnd), &secontext, dir_path.c_str(), mode);
98 setfscreatecon(secontext);
99 }
100 int err = mkdir(dir_path.c_str(), mode);
101 if (secontext) {
102 freecon(secontext);
103 setfscreatecon(nullptr);
104 }
105 if (err != 0) {
106 return -1;
107 }
108 break;
109 }
110 default:
111 // Already exists.
112 break;
113 }
114 prev_end = next_end;
115 }
116 return 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800117}
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800118
119static DirStatus
120getPathDirStatus(const char *path)
121{
122 struct stat st;
123 int err;
124
125 err = stat(path, &st);
126 if (err == 0) {
127 /* Something's there; make sure it's a directory.
128 */
129 if (S_ISDIR(st.st_mode)) {
Ethan Yonker58f21322018-08-24 11:17:36 -0500130 return DirStatus::DDIR;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800131 }
132 errno = ENOTDIR;
Ethan Yonker58f21322018-08-24 11:17:36 -0500133 return DirStatus::DILLEGAL;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800134 } else if (errno != ENOENT) {
135 /* Something went wrong, or something in the path
136 * is bad. Can't do anything in this situation.
137 */
Ethan Yonker58f21322018-08-24 11:17:36 -0500138 return DirStatus::DILLEGAL;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800139 }
Ethan Yonker58f21322018-08-24 11:17:36 -0500140 return DirStatus::DMISSING;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800141}
142
143int
144dirCreateHierarchy(const char *path, int mode,
145 const struct utimbuf *timestamp, bool stripFileName,
146 struct selabel_handle *sehnd)
147{
148 DirStatus ds;
149
150 /* Check for an empty string before we bother
151 * making any syscalls.
152 */
153 if (path[0] == '\0') {
154 errno = ENOENT;
155 return -1;
156 }
157 // Allocate a path that we can modify; stick a slash on
158 // the end to make things easier.
159 std::string cpath = path;
160 if (stripFileName) {
161 // Strip everything after the last slash.
162 size_t pos = cpath.rfind('/');
163 if (pos == std::string::npos) {
164 errno = ENOENT;
165 return -1;
166 }
167 cpath.resize(pos + 1);
168 } else {
169 // Make sure that the path ends in a slash.
170 cpath.push_back('/');
171 }
172
173 /* See if it already exists.
174 */
175 ds = getPathDirStatus(cpath.c_str());
Ethan Yonker58f21322018-08-24 11:17:36 -0500176 if (ds == DirStatus::DDIR) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800177 return 0;
Ethan Yonker58f21322018-08-24 11:17:36 -0500178 } else if (ds == DirStatus::DILLEGAL) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800179 return -1;
180 }
181
182 /* Walk up the path from the root and make each level.
183 * If a directory already exists, no big deal.
184 */
185 const char *path_start = &cpath[0];
186 char *p = &cpath[0];
187 while (*p != '\0') {
188 /* Skip any slashes, watching out for the end of the string.
189 */
190 while (*p != '\0' && *p == '/') {
191 p++;
192 }
193 if (*p == '\0') {
194 break;
195 }
196
197 /* Find the end of the next path component.
198 * We know that we'll see a slash before the NUL,
199 * because we added it, above.
200 */
201 while (*p != '/') {
202 p++;
203 }
204 *p = '\0';
205
206 /* Check this part of the path and make a new directory
207 * if necessary.
208 */
209 ds = getPathDirStatus(path_start);
Ethan Yonker58f21322018-08-24 11:17:36 -0500210 if (ds == DirStatus::DILLEGAL) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800211 /* Could happen if some other process/thread is
212 * messing with the filesystem.
213 */
214 return -1;
Ethan Yonker58f21322018-08-24 11:17:36 -0500215 } else if (ds == DirStatus::DMISSING) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800216 int err;
217
218 char *secontext = NULL;
219
220 if (sehnd) {
221 selabel_lookup(sehnd, &secontext, path_start, mode);
222 setfscreatecon(secontext);
223 }
224
225 err = mkdir(path_start, mode);
226
227 if (secontext) {
228 freecon(secontext);
229 setfscreatecon(NULL);
230 }
231
232 if (err != 0) {
233 return -1;
234 }
235 if (timestamp != NULL && utime(path_start, timestamp)) {
236 return -1;
237 }
238 }
239 // else, this directory already exists.
240
241 // Repair the path and continue.
242 *p = '/';
243 }
244 return 0;
245}
246
247int
248dirUnlinkHierarchy(const char *path)
249{
250 struct stat st;
251 DIR *dir;
252 struct dirent *de;
253 int fail = 0;
254
255 /* is it a file or directory? */
256 if (lstat(path, &st) < 0) {
257 return -1;
258 }
259
260 /* a file, so unlink it */
261 if (!S_ISDIR(st.st_mode)) {
262 return unlink(path);
263 }
264
265 /* a directory, so open handle */
266 dir = opendir(path);
267 if (dir == NULL) {
268 return -1;
269 }
270
271 /* recurse over components */
272 errno = 0;
273 while ((de = readdir(dir)) != NULL) {
274 //TODO: don't blow the stack
275 char dn[PATH_MAX];
276 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
277 continue;
278 }
279 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
280 if (dirUnlinkHierarchy(dn) < 0) {
281 fail = 1;
282 break;
283 }
284 errno = 0;
285 }
286 /* in case readdir or unlink_recursive failed */
287 if (fail || errno < 0) {
288 int save = errno;
289 closedir(dir);
290 errno = save;
291 return -1;
292 }
293
294 /* close directory handle */
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100295 if (closedir(dir) < 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800296 return -1;
297 }
298
299 /* delete target directory */
300 return rmdir(path);
301}