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 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 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 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