1 /*
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
29 typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
30
31 static DirStatus
getPathDirStatus(const char * path)32 getPathDirStatus(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
55 int
dirCreateHierarchy(const char * path,int mode,const struct utimbuf * timestamp,bool stripFileName,struct selabel_handle * sehnd)56 dirCreateHierarchy(const char *path, int mode,
57 const struct utimbuf *timestamp, bool stripFileName,
58 struct selabel_handle *sehnd)
59 {
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
148 char *secontext = NULL;
149
150 if (sehnd) {
151 selabel_lookup(sehnd, &secontext, cpath, mode);
152 setfscreatecon(secontext);
153 }
154
155 err = mkdir(cpath, mode);
156
157 if (secontext) {
158 freecon(secontext);
159 setfscreatecon(NULL);
160 }
161
162 if (err != 0) {
163 free(cpath);
164 return -1;
165 }
166 if (timestamp != NULL && utime(cpath, timestamp)) {
167 free(cpath);
168 return -1;
169 }
170 }
171 // else, this directory already exists.
172
173 /* Repair the path and continue.
174 */
175 *p = '/';
176 }
177 free(cpath);
178
179 return 0;
180 }
181
182 int
dirUnlinkHierarchy(const char * path)183 dirUnlinkHierarchy(const char *path)
184 {
185 struct stat st;
186 DIR *dir;
187 struct dirent *de;
188 int fail = 0;
189
190 /* is it a file or directory? */
191 if (lstat(path, &st) < 0) {
192 return -1;
193 }
194
195 /* a file, so unlink it */
196 if (!S_ISDIR(st.st_mode)) {
197 return unlink(path);
198 }
199
200 /* a directory, so open handle */
201 dir = opendir(path);
202 if (dir == NULL) {
203 return -1;
204 }
205
206 /* recurse over components */
207 errno = 0;
208 while ((de = readdir(dir)) != NULL) {
209 //TODO: don't blow the stack
210 char dn[PATH_MAX];
211 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
212 continue;
213 }
214 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
215 if (dirUnlinkHierarchy(dn) < 0) {
216 fail = 1;
217 break;
218 }
219 errno = 0;
220 }
221 /* in case readdir or unlink_recursive failed */
222 if (fail || errno < 0) {
223 int save = errno;
224 closedir(dir);
225 errno = save;
226 return -1;
227 }
228
229 /* close directory handle */
230 if (closedir(dir) < 0) {
231 return -1;
232 }
233
234 /* delete target directory */
235 return rmdir(path);
236 }
237