Home | History | Annotate | Download | only in storage
      1 /*
      2  * Copyright (C) 2017 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 DEBUG false  // STOPSHIP if true
     18 #include "Log.h"
     19 
     20 #include "android-base/stringprintf.h"
     21 #include "guardrail/StatsdStats.h"
     22 #include "storage/StorageManager.h"
     23 #include "stats_log_util.h"
     24 
     25 #include <android-base/file.h>
     26 #include <dirent.h>
     27 #include <private/android_filesystem_config.h>
     28 #include <fstream>
     29 #include <iostream>
     30 
     31 namespace android {
     32 namespace os {
     33 namespace statsd {
     34 
     35 using android::util::FIELD_COUNT_REPEATED;
     36 using android::util::FIELD_TYPE_MESSAGE;
     37 using std::map;
     38 
     39 #define STATS_DATA_DIR "/data/misc/stats-data"
     40 #define STATS_SERVICE_DIR "/data/misc/stats-service"
     41 
     42 // for ConfigMetricsReportList
     43 const int FIELD_ID_REPORTS = 2;
     44 
     45 using android::base::StringPrintf;
     46 using std::unique_ptr;
     47 
     48 // Returns array of int64_t which contains timestamp in seconds, uid, and
     49 // configID.
     50 static void parseFileName(char* name, int64_t* result) {
     51     int index = 0;
     52     char* substr = strtok(name, "_");
     53     while (substr != nullptr && index < 3) {
     54         result[index] = StrToInt64(substr);
     55         index++;
     56         substr = strtok(nullptr, "_");
     57     }
     58     // When index ends before hitting 3, file name is corrupted. We
     59     // intentionally put -1 at index 0 to indicate the error to caller.
     60     // TODO: consider removing files with unexpected name format.
     61     if (index < 3) {
     62         result[0] = -1;
     63     }
     64 }
     65 
     66 static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
     67     return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
     68                         (long long)configID);
     69 }
     70 
     71 void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
     72     int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
     73     if (fd == -1) {
     74         VLOG("Attempt to access %s but failed", file);
     75         return;
     76     }
     77     trimToFit(STATS_SERVICE_DIR);
     78     trimToFit(STATS_DATA_DIR);
     79 
     80     int result = write(fd, buffer, numBytes);
     81     if (result == numBytes) {
     82         VLOG("Successfully wrote %s", file);
     83     } else {
     84         VLOG("Failed to write %s", file);
     85     }
     86 
     87     result = fchown(fd, AID_STATSD, AID_STATSD);
     88     if (result) {
     89         VLOG("Failed to chown %s to statsd", file);
     90     }
     91 
     92     close(fd);
     93 }
     94 
     95 void StorageManager::deleteFile(const char* file) {
     96     if (remove(file) != 0) {
     97         VLOG("Attempt to delete %s but is not found", file);
     98     } else {
     99         VLOG("Successfully deleted %s", file);
    100     }
    101 }
    102 
    103 void StorageManager::deleteAllFiles(const char* path) {
    104     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
    105     if (dir == NULL) {
    106         VLOG("Directory does not exist: %s", path);
    107         return;
    108     }
    109 
    110     dirent* de;
    111     while ((de = readdir(dir.get()))) {
    112         char* name = de->d_name;
    113         if (name[0] == '.') continue;
    114         deleteFile(StringPrintf("%s/%s", path, name).c_str());
    115     }
    116 }
    117 
    118 void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
    119     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
    120     if (dir == NULL) {
    121         VLOG("Directory does not exist: %s", path);
    122         return;
    123     }
    124 
    125     dirent* de;
    126     while ((de = readdir(dir.get()))) {
    127         char* name = de->d_name;
    128         if (name[0] == '.') {
    129             continue;
    130         }
    131         size_t nameLen = strlen(name);
    132         size_t suffixLen = strlen(suffix);
    133         if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
    134             deleteFile(StringPrintf("%s/%s", path, name).c_str());
    135         }
    136     }
    137 }
    138 
    139 void StorageManager::sendBroadcast(const char* path,
    140                                    const std::function<void(const ConfigKey&)>& sendBroadcast) {
    141     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
    142     if (dir == NULL) {
    143         VLOG("no stats-data directory on disk");
    144         return;
    145     }
    146 
    147     dirent* de;
    148     while ((de = readdir(dir.get()))) {
    149         char* name = de->d_name;
    150         if (name[0] == '.') continue;
    151         VLOG("file %s", name);
    152 
    153         int64_t result[3];
    154         parseFileName(name, result);
    155         if (result[0] == -1) continue;
    156         int64_t uid = result[1];
    157         int64_t configID = result[2];
    158 
    159         sendBroadcast(ConfigKey((int)uid, configID));
    160     }
    161 }
    162 
    163 bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
    164     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
    165     if (dir == NULL) {
    166         VLOG("Path %s does not exist", STATS_DATA_DIR);
    167         return false;
    168     }
    169 
    170     string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
    171 
    172     dirent* de;
    173     while ((de = readdir(dir.get()))) {
    174         char* name = de->d_name;
    175         if (name[0] == '.') continue;
    176 
    177         size_t nameLen = strlen(name);
    178         size_t suffixLen = suffix.length();
    179         if (suffixLen <= nameLen &&
    180             strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
    181             // Check again that the file name is parseable.
    182             int64_t result[3];
    183             parseFileName(name, result);
    184             if (result[0] == -1) continue;
    185             return true;
    186         }
    187     }
    188     return false;
    189 }
    190 
    191 void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
    192     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
    193     if (dir == NULL) {
    194         VLOG("Path %s does not exist", STATS_DATA_DIR);
    195         return;
    196     }
    197 
    198     string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
    199 
    200     dirent* de;
    201     while ((de = readdir(dir.get()))) {
    202         char* name = de->d_name;
    203         if (name[0] == '.') continue;
    204 
    205         size_t nameLen = strlen(name);
    206         size_t suffixLen = suffix.length();
    207         if (suffixLen <= nameLen &&
    208             strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
    209             int64_t result[3];
    210             parseFileName(name, result);
    211             if (result[0] == -1) continue;
    212             int64_t timestamp = result[0];
    213             int64_t uid = result[1];
    214             int64_t configID = result[2];
    215 
    216             string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
    217             int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
    218             if (fd != -1) {
    219                 string content;
    220                 if (android::base::ReadFdToString(fd, &content)) {
    221                     proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
    222                                 content.c_str(), content.size());
    223                 }
    224                 close(fd);
    225             }
    226 
    227             // Remove file from disk after reading.
    228             remove(file_name.c_str());
    229         }
    230     }
    231 }
    232 
    233 bool StorageManager::readFileToString(const char* file, string* content) {
    234     int fd = open(file, O_RDONLY | O_CLOEXEC);
    235     bool res = false;
    236     if (fd != -1) {
    237         if (android::base::ReadFdToString(fd, content)) {
    238             res = true;
    239         } else {
    240             VLOG("Failed to read file %s\n", file);
    241         }
    242         close(fd);
    243     }
    244     return res;
    245 }
    246 
    247 void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
    248     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
    249     if (dir == NULL) {
    250         VLOG("no default config on disk");
    251         return;
    252     }
    253     trimToFit(STATS_SERVICE_DIR);
    254 
    255     dirent* de;
    256     while ((de = readdir(dir.get()))) {
    257         char* name = de->d_name;
    258         if (name[0] == '.') continue;
    259         VLOG("file %s", name);
    260 
    261         int64_t result[3];
    262         parseFileName(name, result);
    263         if (result[0] == -1) continue;
    264         int64_t timestamp = result[0];
    265         int64_t uid = result[1];
    266         int64_t configID = result[2];
    267         string file_name = getFilePath(STATS_SERVICE_DIR, timestamp, uid, configID);
    268         int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
    269         if (fd != -1) {
    270             string content;
    271             if (android::base::ReadFdToString(fd, &content)) {
    272                 StatsdConfig config;
    273                 if (config.ParseFromString(content)) {
    274                     configsMap[ConfigKey(uid, configID)] = config;
    275                     VLOG("map key uid=%lld|configID=%lld", (long long)uid, (long long)configID);
    276                 }
    277             }
    278             close(fd);
    279         }
    280     }
    281 }
    282 
    283 bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
    284     string content;
    285     return config != nullptr &&
    286         StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
    287 }
    288 
    289 bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
    290     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
    291                                              closedir);
    292     if (dir == NULL) {
    293         VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
    294         return false;
    295     }
    296 
    297     string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
    298     dirent* de;
    299     while ((de = readdir(dir.get()))) {
    300         char* name = de->d_name;
    301         if (name[0] == '.') {
    302             continue;
    303         }
    304         size_t nameLen = strlen(name);
    305         size_t suffixLen = suffix.length();
    306         // There can be at most one file that matches this suffix (config key).
    307         if (suffixLen <= nameLen &&
    308             strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
    309             int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
    310                                   O_RDONLY | O_CLOEXEC);
    311             if (fd != -1) {
    312                 if (android::base::ReadFdToString(fd, content)) {
    313                     return true;
    314                 }
    315                 close(fd);
    316             }
    317         }
    318     }
    319     return false;
    320 }
    321 
    322 bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
    323                                         const vector<uint8_t>& config) {
    324     string content;
    325     if (StorageManager::readConfigFromDisk(key, &content)) {
    326         vector<uint8_t> vec(content.begin(), content.end());
    327         if (vec == config) {
    328             return true;
    329         }
    330     }
    331     return false;
    332 }
    333 
    334 void StorageManager::trimToFit(const char* path) {
    335     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
    336     if (dir == NULL) {
    337         VLOG("Path %s does not exist", path);
    338         return;
    339     }
    340     dirent* de;
    341     int totalFileSize = 0;
    342     vector<string> fileNames;
    343     while ((de = readdir(dir.get()))) {
    344         char* name = de->d_name;
    345         if (name[0] == '.') continue;
    346 
    347         int64_t result[3];
    348         parseFileName(name, result);
    349         if (result[0] == -1) continue;
    350         int64_t timestamp = result[0];
    351         int64_t uid = result[1];
    352         int64_t configID = result[2];
    353         string file_name = getFilePath(path, timestamp, uid, configID);
    354 
    355         // Check for timestamp and delete if it's too old.
    356         long fileAge = getWallClockSec() - timestamp;
    357         if (fileAge > StatsdStats::kMaxAgeSecond) {
    358             deleteFile(file_name.c_str());
    359         }
    360 
    361         fileNames.push_back(file_name);
    362         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
    363         if (file.is_open()) {
    364             file.seekg(0, ios::end);
    365             int fileSize = file.tellg();
    366             file.close();
    367             totalFileSize += fileSize;
    368         }
    369     }
    370 
    371     if (fileNames.size() > StatsdStats::kMaxFileNumber ||
    372         totalFileSize > StatsdStats::kMaxFileSize) {
    373         // Reverse sort to effectively remove from the back (oldest entries).
    374         // This will sort files in reverse-chronological order.
    375         sort(fileNames.begin(), fileNames.end(), std::greater<std::string>());
    376     }
    377 
    378     // Start removing files from oldest to be under the limit.
    379     while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
    380                                     totalFileSize > StatsdStats::kMaxFileSize)) {
    381         string file_name = fileNames.at(fileNames.size() - 1);
    382         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
    383         if (file.is_open()) {
    384             file.seekg(0, ios::end);
    385             int fileSize = file.tellg();
    386             file.close();
    387             totalFileSize -= fileSize;
    388         }
    389 
    390         deleteFile(file_name.c_str());
    391         fileNames.pop_back();
    392     }
    393 }
    394 
    395 void StorageManager::printStats(FILE* out) {
    396     printDirStats(out, STATS_SERVICE_DIR);
    397     printDirStats(out, STATS_DATA_DIR);
    398 }
    399 
    400 void StorageManager::printDirStats(FILE* out, const char* path) {
    401     fprintf(out, "Printing stats of %s\n", path);
    402     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
    403     if (dir == NULL) {
    404         VLOG("Path %s does not exist", path);
    405         return;
    406     }
    407     dirent* de;
    408     int fileCount = 0;
    409     int totalFileSize = 0;
    410     while ((de = readdir(dir.get()))) {
    411         char* name = de->d_name;
    412         if (name[0] == '.') {
    413             continue;
    414         }
    415         int64_t result[3];
    416         parseFileName(name, result);
    417         if (result[0] == -1) continue;
    418         int64_t timestamp = result[0];
    419         int64_t uid = result[1];
    420         int64_t configID = result[2];
    421         fprintf(out, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld",
    422                 fileCount + 1,
    423                 (long long)timestamp,
    424                 (int)uid,
    425                 (long long)configID);
    426         string file_name = getFilePath(path, timestamp, uid, configID);
    427         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
    428         if (file.is_open()) {
    429             file.seekg(0, ios::end);
    430             int fileSize = file.tellg();
    431             file.close();
    432             fprintf(out, ", File Size: %d bytes", fileSize);
    433             totalFileSize += fileSize;
    434         }
    435         fprintf(out, "\n");
    436         fileCount++;
    437     }
    438     fprintf(out, "\tTotal number of files: %d, Total size of files: %d bytes.\n",
    439             fileCount, totalFileSize);
    440 }
    441 
    442 }  // namespace statsd
    443 }  // namespace os
    444 }  // namespace android
    445