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