• 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)56 dirCreateHierarchy(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 
168 int
dirUnlinkHierarchy(const char * path)169 dirUnlinkHierarchy(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 
224 int
dirSetHierarchyPermissions(const char * path,int uid,int gid,int dirMode,int fileMode)225 dirSetHierarchyPermissions(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 }
281