blob: e08e360c02b9c8e5980eeec806446b691fc4004b [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
Elliott Hughes4bbd5bf2016-04-01 18:24:39 -070017#include "DirUtil.h"
18
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080019#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
Yabin Cui4425c1d2016-02-10 13:47:32 -080029#include <string>
30
Elliott Hughes4bbd5bf2016-04-01 18:24:39 -070031#include <selinux/label.h>
32#include <selinux/selinux.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080033
34typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
35
36static DirStatus
37getPathDirStatus(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
60int
61dirCreateHierarchy(const char *path, int mode,
Stephen Smalley779701d2012-02-09 14:13:23 -050062 const struct utimbuf *timestamp, bool stripFileName,
63 struct selabel_handle *sehnd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080064{
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 }
Yabin Cui4425c1d2016-02-10 13:47:32 -080074 // Allocate a path that we can modify; stick a slash on
75 // the end to make things easier.
76 std::string cpath = path;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080077 if (stripFileName) {
Yabin Cui4425c1d2016-02-10 13:47:32 -080078 // Strip everything after the last slash.
79 size_t pos = cpath.rfind('/');
80 if (pos == std::string::npos) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080081 errno = ENOENT;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080082 return -1;
83 }
Yabin Cui4425c1d2016-02-10 13:47:32 -080084 cpath.resize(pos + 1);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080085 } else {
Yabin Cui4425c1d2016-02-10 13:47:32 -080086 // Make sure that the path ends in a slash.
87 cpath.push_back('/');
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080088 }
89
90 /* See if it already exists.
91 */
Yabin Cui4425c1d2016-02-10 13:47:32 -080092 ds = getPathDirStatus(cpath.c_str());
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080093 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 */
Yabin Cui4425c1d2016-02-10 13:47:32 -0800102 const char *path_start = &cpath[0];
103 char *p = &cpath[0];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800104 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 */
Yabin Cui4425c1d2016-02-10 13:47:32 -0800126 ds = getPathDirStatus(path_start);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800127 if (ds == DILLEGAL) {
128 /* Could happen if some other process/thread is
129 * messing with the filesystem.
130 */
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800131 return -1;
132 } else if (ds == DMISSING) {
133 int err;
134
Stephen Smalley779701d2012-02-09 14:13:23 -0500135 char *secontext = NULL;
136
137 if (sehnd) {
Yabin Cui4425c1d2016-02-10 13:47:32 -0800138 selabel_lookup(sehnd, &secontext, path_start, mode);
Stephen Smalley779701d2012-02-09 14:13:23 -0500139 setfscreatecon(secontext);
140 }
Stephen Smalley779701d2012-02-09 14:13:23 -0500141
Yabin Cui4425c1d2016-02-10 13:47:32 -0800142 err = mkdir(path_start, mode);
Stephen Smalley779701d2012-02-09 14:13:23 -0500143
Stephen Smalley779701d2012-02-09 14:13:23 -0500144 if (secontext) {
145 freecon(secontext);
146 setfscreatecon(NULL);
147 }
Stephen Smalley779701d2012-02-09 14:13:23 -0500148
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800149 if (err != 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800150 return -1;
151 }
Yabin Cui4425c1d2016-02-10 13:47:32 -0800152 if (timestamp != NULL && utime(path_start, timestamp)) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800153 return -1;
154 }
155 }
156 // else, this directory already exists.
Yabin Cui4425c1d2016-02-10 13:47:32 -0800157
158 // Repair the path and continue.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800159 *p = '/';
160 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800161 return 0;
162}
163
164int
165dirUnlinkHierarchy(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) {
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100191 //TODO: don't blow the stack
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800192 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}