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