1/* 2 * Copyright (C) 2016 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#define LOG_TAG "incidentd" 18 19#include "report_directory.h" 20 21#include <cutils/log.h> 22#include <private/android_filesystem_config.h> 23#include <utils/String8.h> 24 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <dirent.h> 28#include <libgen.h> 29#include <unistd.h> 30 31#include <vector> 32 33using namespace android; 34using namespace std; 35 36status_t 37create_directory(const char* directory) 38{ 39 struct stat st; 40 status_t err = NO_ERROR; 41 char* dir = strdup(directory); 42 43 // Skip first slash 44 char* d = dir + 1; 45 46 // Create directories, assigning them to the system user 47 bool last = false; 48 while (!last) { 49 d = strchr(d, '/'); 50 if (d != NULL) { 51 *d = '\0'; 52 } else { 53 last = true; 54 } 55 if (stat(dir, &st) == 0) { 56 if (!S_ISDIR(st.st_mode)) { 57 err = ALREADY_EXISTS; 58 goto done; 59 } 60 } else { 61 if (mkdir(dir, 0770)) { 62 ALOGE("No incident reports today. " 63 "Unable to create incident report dir %s: %s", dir, 64 strerror(errno)); 65 err = -errno; 66 goto done; 67 } 68 if (chmod(dir, 0770)) { 69 ALOGE("No incident reports today. " 70 "Unable to set permissions for incident report dir %s: %s", dir, 71 strerror(errno)); 72 err = -errno; 73 goto done; 74 } 75 if (chown(dir, AID_SYSTEM, AID_SYSTEM)) { 76 ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n", 77 dir, strerror(errno)); 78 err = -errno; 79 goto done; 80 } 81 } 82 if (!last) { 83 *d++ = '/'; 84 } 85 } 86 87 // Ensure that the final directory is owned by the system with 0770. If it isn't 88 // we won't write into it. 89 if (stat(directory, &st) != 0) { 90 ALOGE("No incident reports today. Can't stat: %s", directory); 91 err = -errno; 92 goto done; 93 } 94 if ((st.st_mode & 0777) != 0770) { 95 ALOGE("No incident reports today. Mode is %0o on report directory %s", 96 st.st_mode, directory); 97 err = BAD_VALUE; 98 goto done; 99 } 100 if (st.st_uid != AID_SYSTEM || st.st_gid != AID_SYSTEM) { 101 ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", 102 st.st_uid, st.st_gid, directory); 103 err = BAD_VALUE; 104 goto done; 105 } 106 107done: 108 free(dir); 109 return err; 110} 111 112static bool 113stat_mtime_cmp(const pair<String8,struct stat>& a, const pair<String8,struct stat>& b) 114{ 115 return a.second.st_mtime < b.second.st_mtime; 116} 117 118void 119clean_directory(const char* directory, off_t maxSize, size_t maxCount) 120{ 121 DIR* dir; 122 struct dirent* entry; 123 struct stat st; 124 125 vector<pair<String8,struct stat>> files; 126 127 if ((dir = opendir(directory)) == NULL) { 128 ALOGE("Couldn't open incident directory: %s", directory); 129 return; 130 } 131 132 String8 dirbase(String8(directory) + "/"); 133 134 off_t totalSize = 0; 135 size_t totalCount = 0; 136 137 // Enumerate, count and add up size 138 while ((entry = readdir(dir)) != NULL) { 139 if (entry->d_name[0] == '.') { 140 continue; 141 } 142 String8 filename = dirbase + entry->d_name; 143 if (stat(filename.string(), &st) != 0) { 144 ALOGE("Unable to stat file %s", filename.string()); 145 continue; 146 } 147 if (!S_ISREG(st.st_mode)) { 148 continue; 149 } 150 files.push_back(pair<String8,struct stat>(filename, st)); 151 152 totalSize += st.st_size; 153 totalCount++; 154 } 155 156 closedir(dir); 157 158 // Count or size is less than max, then we're done. 159 if (totalSize < maxSize && totalCount < maxCount) { 160 return; 161 } 162 163 // Oldest files first. 164 sort(files.begin(), files.end(), stat_mtime_cmp); 165 166 // Remove files until we're under our limits. 167 for (vector<pair<String8,struct stat>>::iterator it = files.begin(); 168 it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) { 169 remove(it->first.string()); 170 totalSize -= it->second.st_size; 171 totalCount--; 172 } 173} 174