blob: 823b6ed2f340e026233c2ecfde9917063984f8e4 [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
17#include <stdlib.h>
18#include <string.h>
19#include <stdio.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <unistd.h>
23#include <errno.h>
24#include <dirent.h>
25#include <limits.h>
26
Yabin Cui4425c1d2016-02-10 13:47:32 -080027#include <string>
28
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080029#include "DirUtil.h"
30
31typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
32
33static DirStatus
34getPathDirStatus(const char *path)
35{
36 struct stat st;
37 int err;
38
39 err = stat(path, &st);
40 if (err == 0) {
41 /* Something's there; make sure it's a directory.
42 */
43 if (S_ISDIR(st.st_mode)) {
44 return DDIR;
45 }
46 errno = ENOTDIR;
47 return DILLEGAL;
48 } else if (errno != ENOENT) {
49 /* Something went wrong, or something in the path
50 * is bad. Can't do anything in this situation.
51 */
52 return DILLEGAL;
53 }
54 return DMISSING;
55}
56
57int
58dirCreateHierarchy(const char *path, int mode,
Stephen Smalley779701d2012-02-09 14:13:23 -050059 const struct utimbuf *timestamp, bool stripFileName,
60 struct selabel_handle *sehnd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080061{
62 DirStatus ds;
63
64 /* Check for an empty string before we bother
65 * making any syscalls.
66 */
67 if (path[0] == '\0') {
68 errno = ENOENT;
69 return -1;
70 }
Yabin Cui4425c1d2016-02-10 13:47:32 -080071 // Allocate a path that we can modify; stick a slash on
72 // the end to make things easier.
73 std::string cpath = path;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080074 if (stripFileName) {
Yabin Cui4425c1d2016-02-10 13:47:32 -080075 // Strip everything after the last slash.
76 size_t pos = cpath.rfind('/');
77 if (pos == std::string::npos) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080078 errno = ENOENT;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080079 return -1;
80 }
Yabin Cui4425c1d2016-02-10 13:47:32 -080081 cpath.resize(pos + 1);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080082 } else {
Yabin Cui4425c1d2016-02-10 13:47:32 -080083 // Make sure that the path ends in a slash.
84 cpath.push_back('/');
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080085 }
86
87 /* See if it already exists.
88 */
Yabin Cui4425c1d2016-02-10 13:47:32 -080089 ds = getPathDirStatus(cpath.c_str());
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080090 if (ds == DDIR) {
91 return 0;
92 } else if (ds == DILLEGAL) {
93 return -1;
94 }
95
96 /* Walk up the path from the root and make each level.
97 * If a directory already exists, no big deal.
98 */
Yabin Cui4425c1d2016-02-10 13:47:32 -080099 const char *path_start = &cpath[0];
100 char *p = &cpath[0];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800101 while (*p != '\0') {
102 /* Skip any slashes, watching out for the end of the string.
103 */
104 while (*p != '\0' && *p == '/') {
105 p++;
106 }
107 if (*p == '\0') {
108 break;
109 }
110
111 /* Find the end of the next path component.
112 * We know that we'll see a slash before the NUL,
113 * because we added it, above.
114 */
115 while (*p != '/') {
116 p++;
117 }
118 *p = '\0';
119
120 /* Check this part of the path and make a new directory
121 * if necessary.
122 */
Yabin Cui4425c1d2016-02-10 13:47:32 -0800123 ds = getPathDirStatus(path_start);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800124 if (ds == DILLEGAL) {
125 /* Could happen if some other process/thread is
126 * messing with the filesystem.
127 */
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800128 return -1;
129 } else if (ds == DMISSING) {
130 int err;
131
Stephen Smalley779701d2012-02-09 14:13:23 -0500132 char *secontext = NULL;
133
134 if (sehnd) {
Yabin Cui4425c1d2016-02-10 13:47:32 -0800135 selabel_lookup(sehnd, &secontext, path_start, mode);
Stephen Smalley779701d2012-02-09 14:13:23 -0500136 setfscreatecon(secontext);
137 }
Stephen Smalley779701d2012-02-09 14:13:23 -0500138
Yabin Cui4425c1d2016-02-10 13:47:32 -0800139 err = mkdir(path_start, mode);
Stephen Smalley779701d2012-02-09 14:13:23 -0500140
Stephen Smalley779701d2012-02-09 14:13:23 -0500141 if (secontext) {
142 freecon(secontext);
143 setfscreatecon(NULL);
144 }
Stephen Smalley779701d2012-02-09 14:13:23 -0500145
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800146 if (err != 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800147 return -1;
148 }
Yabin Cui4425c1d2016-02-10 13:47:32 -0800149 if (timestamp != NULL && utime(path_start, timestamp)) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800150 return -1;
151 }
152 }
153 // else, this directory already exists.
Yabin Cui4425c1d2016-02-10 13:47:32 -0800154
155 // Repair the path and continue.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800156 *p = '/';
157 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800158 return 0;
159}
160
161int
162dirUnlinkHierarchy(const char *path)
163{
164 struct stat st;
165 DIR *dir;
166 struct dirent *de;
167 int fail = 0;
168
169 /* is it a file or directory? */
170 if (lstat(path, &st) < 0) {
171 return -1;
172 }
173
174 /* a file, so unlink it */
175 if (!S_ISDIR(st.st_mode)) {
176 return unlink(path);
177 }
178
179 /* a directory, so open handle */
180 dir = opendir(path);
181 if (dir == NULL) {
182 return -1;
183 }
184
185 /* recurse over components */
186 errno = 0;
187 while ((de = readdir(dir)) != NULL) {
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100188 //TODO: don't blow the stack
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800189 char dn[PATH_MAX];
190 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
191 continue;
192 }
193 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
194 if (dirUnlinkHierarchy(dn) < 0) {
195 fail = 1;
196 break;
197 }
198 errno = 0;
199 }
200 /* in case readdir or unlink_recursive failed */
201 if (fail || errno < 0) {
202 int save = errno;
203 closedir(dir);
204 errno = save;
205 return -1;
206 }
207
208 /* close directory handle */
209 if (closedir(dir) < 0) {
210 return -1;
211 }
212
213 /* delete target directory */
214 return rmdir(path);
215}