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 "DirUtil.h"
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <dirent.h>
27 #include <limits.h>
28 
29 #include <string>
30 
31 #include <selinux/label.h>
32 #include <selinux/selinux.h>
33 
34 typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
35 
36 static DirStatus
getPathDirStatus(const char * path)37 getPathDirStatus(const char *path)
38 {
39     struct stat st;
40     int err;
41 
42     err = stat(path, &st);
43     if (err == 0) {
44         /* Something's there; make sure it's a directory.
45          */
46         if (S_ISDIR(st.st_mode)) {
47             return DDIR;
48         }
49         errno = ENOTDIR;
50         return DILLEGAL;
51     } else if (errno != ENOENT) {
52         /* Something went wrong, or something in the path
53          * is bad.  Can't do anything in this situation.
54          */
55         return DILLEGAL;
56     }
57     return DMISSING;
58 }
59 
60 int
dirCreateHierarchy(const char * path,int mode,const struct utimbuf * timestamp,bool stripFileName,struct selabel_handle * sehnd)61 dirCreateHierarchy(const char *path, int mode,
62         const struct utimbuf *timestamp, bool stripFileName,
63         struct selabel_handle *sehnd)
64 {
65     DirStatus ds;
66 
67     /* Check for an empty string before we bother
68      * making any syscalls.
69      */
70     if (path[0] == '\0') {
71         errno = ENOENT;
72         return -1;
73     }
74     // Allocate a path that we can modify; stick a slash on
75     // the end to make things easier.
76     std::string cpath = path;
77     if (stripFileName) {
78         // Strip everything after the last slash.
79         size_t pos = cpath.rfind('/');
80         if (pos == std::string::npos) {
81             errno = ENOENT;
82             return -1;
83         }
84         cpath.resize(pos + 1);
85     } else {
86         // Make sure that the path ends in a slash.
87         cpath.push_back('/');
88     }
89 
90     /* See if it already exists.
91      */
92     ds = getPathDirStatus(cpath.c_str());
93     if (ds == DDIR) {
94         return 0;
95     } else if (ds == DILLEGAL) {
96         return -1;
97     }
98 
99     /* Walk up the path from the root and make each level.
100      * If a directory already exists, no big deal.
101      */
102     const char *path_start = &cpath[0];
103     char *p = &cpath[0];
104     while (*p != '\0') {
105         /* Skip any slashes, watching out for the end of the string.
106          */
107         while (*p != '\0' && *p == '/') {
108             p++;
109         }
110         if (*p == '\0') {
111             break;
112         }
113 
114         /* Find the end of the next path component.
115          * We know that we'll see a slash before the NUL,
116          * because we added it, above.
117          */
118         while (*p != '/') {
119             p++;
120         }
121         *p = '\0';
122 
123         /* Check this part of the path and make a new directory
124          * if necessary.
125          */
126         ds = getPathDirStatus(path_start);
127         if (ds == DILLEGAL) {
128             /* Could happen if some other process/thread is
129              * messing with the filesystem.
130              */
131             return -1;
132         } else if (ds == DMISSING) {
133             int err;
134 
135             char *secontext = NULL;
136 
137             if (sehnd) {
138                 selabel_lookup(sehnd, &secontext, path_start, mode);
139                 setfscreatecon(secontext);
140             }
141 
142             err = mkdir(path_start, mode);
143 
144             if (secontext) {
145                 freecon(secontext);
146                 setfscreatecon(NULL);
147             }
148 
149             if (err != 0) {
150                 return -1;
151             }
152             if (timestamp != NULL && utime(path_start, timestamp)) {
153                 return -1;
154             }
155         }
156         // else, this directory already exists.
157 
158         // Repair the path and continue.
159         *p = '/';
160     }
161     return 0;
162 }
163 
164 int
dirUnlinkHierarchy(const char * path)165 dirUnlinkHierarchy(const char *path)
166 {
167     struct stat st;
168     DIR *dir;
169     struct dirent *de;
170     int fail = 0;
171 
172     /* is it a file or directory? */
173     if (lstat(path, &st) < 0) {
174         return -1;
175     }
176 
177     /* a file, so unlink it */
178     if (!S_ISDIR(st.st_mode)) {
179         return unlink(path);
180     }
181 
182     /* a directory, so open handle */
183     dir = opendir(path);
184     if (dir == NULL) {
185         return -1;
186     }
187 
188     /* recurse over components */
189     errno = 0;
190     while ((de = readdir(dir)) != NULL) {
191         //TODO: don't blow the stack
192         char dn[PATH_MAX];
193         if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
194             continue;
195         }
196         snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
197         if (dirUnlinkHierarchy(dn) < 0) {
198             fail = 1;
199             break;
200         }
201         errno = 0;
202     }
203     /* in case readdir or unlink_recursive failed */
204     if (fail || errno < 0) {
205         int save = errno;
206         closedir(dir);
207         errno = save;
208         return -1;
209     }
210 
211     /* close directory handle */
212     if (closedir(dir) < 0) {
213         return -1;
214     }
215 
216     /* delete target directory */
217     return rmdir(path);
218 }
219