• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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 
157             err = mkdir(cpath, mode);
158 
159 #ifdef HAVE_SELINUX
160 
161             if (secontext) {
162                 freecon(secontext);
163                 setfscreatecon(NULL);
164             }
165 #endif
166 
167             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 
187 int
dirUnlinkHierarchy(const char * path)188 dirUnlinkHierarchy(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 
243 int
dirSetHierarchyPermissions(const char * path,int uid,int gid,int dirMode,int fileMode)244 dirSetHierarchyPermissions(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 }
300