blob: 0d49b578065f4022be388106b6e17013fb9c5efd [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
27#include "DirUtil.h"
28
29typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
30
31static DirStatus
32getPathDirStatus(const char *path)
33{
34 struct stat st;
35 int err;
36
37 err = stat(path, &st);
38 if (err == 0) {
39 /* Something's there; make sure it's a directory.
40 */
41 if (S_ISDIR(st.st_mode)) {
42 return DDIR;
43 }
44 errno = ENOTDIR;
45 return DILLEGAL;
46 } else if (errno != ENOENT) {
47 /* Something went wrong, or something in the path
48 * is bad. Can't do anything in this situation.
49 */
50 return DILLEGAL;
51 }
52 return DMISSING;
53}
54
55int
56dirCreateHierarchy(const char *path, int mode,
Stephen Smalley779701d2012-02-09 14:13:23 -050057 const struct utimbuf *timestamp, bool stripFileName,
58 struct selabel_handle *sehnd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080059{
60 DirStatus ds;
61
62 /* Check for an empty string before we bother
63 * making any syscalls.
64 */
65 if (path[0] == '\0') {
66 errno = ENOENT;
67 return -1;
68 }
69
70 /* Allocate a path that we can modify; stick a slash on
71 * the end to make things easier.
72 */
73 size_t pathLen = strlen(path);
74 char *cpath = (char *)malloc(pathLen + 2);
75 if (cpath == NULL) {
76 errno = ENOMEM;
77 return -1;
78 }
79 memcpy(cpath, path, pathLen);
80 if (stripFileName) {
81 /* Strip everything after the last slash.
82 */
83 char *c = cpath + pathLen - 1;
84 while (c != cpath && *c != '/') {
85 c--;
86 }
87 if (c == cpath) {
88//xxx test this path
89 /* No directory component. Act like the path was empty.
90 */
91 errno = ENOENT;
92 free(cpath);
93 return -1;
94 }
95 c[1] = '\0'; // Terminate after the slash we found.
96 } else {
97 /* Make sure that the path ends in a slash.
98 */
99 cpath[pathLen] = '/';
100 cpath[pathLen + 1] = '\0';
101 }
102
103 /* See if it already exists.
104 */
105 ds = getPathDirStatus(cpath);
106 if (ds == DDIR) {
107 return 0;
108 } else if (ds == DILLEGAL) {
109 return -1;
110 }
111
112 /* Walk up the path from the root and make each level.
113 * If a directory already exists, no big deal.
114 */
115 char *p = cpath;
116 while (*p != '\0') {
117 /* Skip any slashes, watching out for the end of the string.
118 */
119 while (*p != '\0' && *p == '/') {
120 p++;
121 }
122 if (*p == '\0') {
123 break;
124 }
125
126 /* Find the end of the next path component.
127 * We know that we'll see a slash before the NUL,
128 * because we added it, above.
129 */
130 while (*p != '/') {
131 p++;
132 }
133 *p = '\0';
134
135 /* Check this part of the path and make a new directory
136 * if necessary.
137 */
138 ds = getPathDirStatus(cpath);
139 if (ds == DILLEGAL) {
140 /* Could happen if some other process/thread is
141 * messing with the filesystem.
142 */
143 free(cpath);
144 return -1;
145 } else if (ds == DMISSING) {
146 int err;
147
Stephen Smalley779701d2012-02-09 14:13:23 -0500148#ifdef HAVE_SELINUX
149 char *secontext = NULL;
150
151 if (sehnd) {
152 selabel_lookup(sehnd, &secontext, cpath, mode);
153 setfscreatecon(secontext);
154 }
155#endif
156
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800157 err = mkdir(cpath, mode);
Stephen Smalley779701d2012-02-09 14:13:23 -0500158
159#ifdef HAVE_SELINUX
160
161 if (secontext) {
162 freecon(secontext);
163 setfscreatecon(NULL);
164 }
165#endif
166
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800167 if (err != 0) {
168 free(cpath);
169 return -1;
170 }
171 if (timestamp != NULL && utime(cpath, timestamp)) {
172 free(cpath);
173 return -1;
174 }
175 }
176 // else, this directory already exists.
177
178 /* Repair the path and continue.
179 */
180 *p = '/';
181 }
182 free(cpath);
183
184 return 0;
185}
186
187int
188dirUnlinkHierarchy(const char *path)
189{
190 struct stat st;
191 DIR *dir;
192 struct dirent *de;
193 int fail = 0;
194
195 /* is it a file or directory? */
196 if (lstat(path, &st) < 0) {
197 return -1;
198 }
199
200 /* a file, so unlink it */
201 if (!S_ISDIR(st.st_mode)) {
202 return unlink(path);
203 }
204
205 /* a directory, so open handle */
206 dir = opendir(path);
207 if (dir == NULL) {
208 return -1;
209 }
210
211 /* recurse over components */
212 errno = 0;
213 while ((de = readdir(dir)) != NULL) {
214//TODO: don't blow the stack
215 char dn[PATH_MAX];
216 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
217 continue;
218 }
219 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
220 if (dirUnlinkHierarchy(dn) < 0) {
221 fail = 1;
222 break;
223 }
224 errno = 0;
225 }
226 /* in case readdir or unlink_recursive failed */
227 if (fail || errno < 0) {
228 int save = errno;
229 closedir(dir);
230 errno = save;
231 return -1;
232 }
233
234 /* close directory handle */
235 if (closedir(dir) < 0) {
236 return -1;
237 }
238
239 /* delete target directory */
240 return rmdir(path);
241}
242
243int
244dirSetHierarchyPermissions(const char *path,
245 int uid, int gid, int dirMode, int fileMode)
246{
247 struct stat st;
248 if (lstat(path, &st)) {
249 return -1;
250 }
251
252 /* ignore symlinks */
253 if (S_ISLNK(st.st_mode)) {
254 return 0;
255 }
256
257 /* directories and files get different permissions */
258 if (chown(path, uid, gid) ||
259 chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) {
260 return -1;
261 }
262
263 /* recurse over directory components */
264 if (S_ISDIR(st.st_mode)) {
265 DIR *dir = opendir(path);
266 if (dir == NULL) {
267 return -1;
268 }
269
270 errno = 0;
271 const struct dirent *de;
272 while (errno == 0 && (de = readdir(dir)) != NULL) {
273 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
274 continue;
275 }
276
277 char dn[PATH_MAX];
278 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
279 if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) {
280 errno = 0;
281 } else if (errno == 0) {
282 errno = -1;
283 }
284 }
285
286 if (errno != 0) {
287 int save = errno;
288 closedir(dir);
289 errno = save;
290 return -1;
291 }
292
293 if (closedir(dir)) {
294 return -1;
295 }
296 }
297
298 return 0;
299}