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 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 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 char *secontext = NULL; 149 150 if (sehnd) { 151 selabel_lookup(sehnd, &secontext, cpath, mode); 152 setfscreatecon(secontext); 153 } 154 155 err = mkdir(cpath, mode); 156 157 if (secontext) { 158 freecon(secontext); 159 setfscreatecon(NULL); 160 } 161 162 if (err != 0) { 163 free(cpath); 164 return -1; 165 } 166 if (timestamp != NULL && utime(cpath, timestamp)) { 167 free(cpath); 168 return -1; 169 } 170 } 171 // else, this directory already exists. 172 173 /* Repair the path and continue. 174 */ 175 *p = '/'; 176 } 177 free(cpath); 178 179 return 0; 180 } 181 182 int 183 dirUnlinkHierarchy(const char *path) 184 { 185 struct stat st; 186 DIR *dir; 187 struct dirent *de; 188 int fail = 0; 189 190 /* is it a file or directory? */ 191 if (lstat(path, &st) < 0) { 192 return -1; 193 } 194 195 /* a file, so unlink it */ 196 if (!S_ISDIR(st.st_mode)) { 197 return unlink(path); 198 } 199 200 /* a directory, so open handle */ 201 dir = opendir(path); 202 if (dir == NULL) { 203 return -1; 204 } 205 206 /* recurse over components */ 207 errno = 0; 208 while ((de = readdir(dir)) != NULL) { 209 //TODO: don't blow the stack 210 char dn[PATH_MAX]; 211 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { 212 continue; 213 } 214 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); 215 if (dirUnlinkHierarchy(dn) < 0) { 216 fail = 1; 217 break; 218 } 219 errno = 0; 220 } 221 /* in case readdir or unlink_recursive failed */ 222 if (fail || errno < 0) { 223 int save = errno; 224 closedir(dir); 225 errno = save; 226 return -1; 227 } 228 229 /* close directory handle */ 230 if (closedir(dir) < 0) { 231 return -1; 232 } 233 234 /* delete target directory */ 235 return rmdir(path); 236 } 237 238 int 239 dirSetHierarchyPermissions(const char *path, 240 int uid, int gid, int dirMode, int fileMode) 241 { 242 struct stat st; 243 if (lstat(path, &st)) { 244 return -1; 245 } 246 247 /* ignore symlinks */ 248 if (S_ISLNK(st.st_mode)) { 249 return 0; 250 } 251 252 /* directories and files get different permissions */ 253 if (chown(path, uid, gid) || 254 chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) { 255 return -1; 256 } 257 258 /* recurse over directory components */ 259 if (S_ISDIR(st.st_mode)) { 260 DIR *dir = opendir(path); 261 if (dir == NULL) { 262 return -1; 263 } 264 265 errno = 0; 266 const struct dirent *de; 267 while (errno == 0 && (de = readdir(dir)) != NULL) { 268 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { 269 continue; 270 } 271 272 char dn[PATH_MAX]; 273 snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); 274 if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { 275 errno = 0; 276 } else if (errno == 0) { 277 errno = -1; 278 } 279 } 280 281 if (errno != 0) { 282 int save = errno; 283 closedir(dir); 284 errno = save; 285 return -1; 286 } 287 288 if (closedir(dir)) { 289 return -1; 290 } 291 } 292 293 return 0; 294 } 295