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 STATSD_DEBUG false  // STOPSHIP if true
18 #include "Log.h"
19 
20 #include "storage/StorageManager.h"
21 
22 #include <android-base/file.h>
23 #include <android-modules-utils/sdk_level.h>
24 #include <private/android_filesystem_config.h>
25 #include <sys/stat.h>
26 
27 #include <fstream>
28 
29 #include "android-base/stringprintf.h"
30 #include "guardrail/StatsdStats.h"
31 #include "stats_log_util.h"
32 #include "utils/DbUtils.h"
33 
34 namespace android {
35 namespace os {
36 namespace statsd {
37 
38 using android::modules::sdklevel::IsAtLeastU;
39 using android::util::FIELD_COUNT_REPEATED;
40 using android::util::FIELD_TYPE_MESSAGE;
41 using std::map;
42 
43 /**
44  * NOTE: these directories are protected by SELinux, any changes here must also update
45  * the SELinux policies.
46  */
47 #define STATS_DATA_DIR "/data/misc/stats-data"
48 #define STATS_SERVICE_DIR "/data/misc/stats-service"
49 
50 // for ConfigMetricsReportList
51 const int FIELD_ID_REPORTS = 2;
52 
53 std::mutex StorageManager::sTrainInfoMutex;
54 
55 using android::base::StringPrintf;
56 using std::unique_ptr;
57 
58 struct FileName {
59     int64_t mTimestampSec;
60     int mUid;
61     int64_t mConfigId;
62     bool mIsHistory;
getFullFileNameandroid::os::statsd::FileName63     string getFullFileName(const char* path) {
64         return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid,
65                             (long long)mConfigId, (mIsHistory ? "_history" : ""));
66     };
67 };
68 
getDataFileName(long wallClockSec,int uid,int64_t id)69 string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) {
70     return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid,
71                         (long long)id);
72 }
73 
getDataHistoryFileName(long wallClockSec,int uid,int64_t id)74 string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) {
75     return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid,
76                         (long long)id);
77 }
78 
findTrainInfoFileNameLocked(const string & trainName)79 static string findTrainInfoFileNameLocked(const string& trainName) {
80     unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
81     if (dir == NULL) {
82         VLOG("Path %s does not exist", TRAIN_INFO_DIR);
83         return "";
84     }
85     dirent* de;
86     while ((de = readdir(dir.get()))) {
87         char* fileName = de->d_name;
88         if (fileName[0] == '.' || de->d_type == DT_DIR) continue;
89 
90         size_t fileNameLength = strlen(fileName);
91         if (fileNameLength >= trainName.length()) {
92             if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
93                              trainName.length())) {
94               return string(fileName);
95             }
96         }
97     }
98 
99     return "";
100 }
101 
102 // Returns array of int64_t which contains timestamp in seconds, uid,
103 // configID and whether the file is a local history file.
parseFileName(char * name,FileName * output)104 static void parseFileName(char* name, FileName* output) {
105     int64_t result[3];
106     int index = 0;
107     char* substr = strtok(name, "_");
108     while (substr != nullptr && index < 3) {
109         result[index] = StrToInt64(substr);
110         index++;
111         substr = strtok(nullptr, "_");
112     }
113     // When index ends before hitting 3, file name is corrupted. We
114     // intentionally put -1 at index 0 to indicate the error to caller.
115     // TODO(b/110563137): consider removing files with unexpected name format.
116     if (index < 3) {
117         result[0] = -1;
118     }
119 
120     output->mTimestampSec = result[0];
121     output->mUid = result[1];
122     output->mConfigId = result[2];
123     // check if the file is a local history.
124     output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0);
125 }
126 
127 // Returns array of int64_t which contains a sqlite db's uid and configId
parseDbName(char * name)128 static ConfigKey parseDbName(char* name) {
129     char* uid = strtok(name, "_");
130     char* configId = strtok(nullptr, ".");
131     if (uid == nullptr || configId == nullptr) {
132         return ConfigKey(-1, -1);
133     }
134     return ConfigKey(StrToInt64(uid), StrToInt64(configId));
135 }
136 
writeFile(const char * file,const void * buffer,int numBytes)137 void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
138     int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
139     if (fd == -1) {
140         VLOG("Attempt to access %s but failed", file);
141         return;
142     }
143     trimToFit(STATS_SERVICE_DIR);
144     trimToFit(STATS_DATA_DIR);
145 
146     if (android::base::WriteFully(fd, buffer, numBytes)) {
147         VLOG("Successfully wrote %s", file);
148     } else {
149         ALOGE("Failed to write %s", file);
150     }
151 
152     int result = fchown(fd, AID_STATSD, AID_STATSD);
153     if (result) {
154         VLOG("Failed to chown %s to statsd", file);
155     }
156 
157     close(fd);
158 }
159 
writeTrainInfo(const InstallTrainInfo & trainInfo)160 bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
161     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
162 
163     if (trainInfo.trainName.empty()) {
164       return false;
165     }
166     deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
167 
168     std::string fileName =
169             StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
170                          trainInfo.trainName.c_str());
171 
172     int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
173     if (fd == -1) {
174         VLOG("Attempt to access %s but failed", fileName.c_str());
175         return false;
176     }
177 
178     size_t result;
179     // Write the magic word
180     result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
181     if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
182         VLOG("Failed to wrtie train info magic");
183         close(fd);
184         return false;
185     }
186 
187     // Write the train version
188     const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
189     result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
190     if (result != trainVersionCodeByteCount) {
191         VLOG("Failed to wrtie train version code");
192         close(fd);
193         return false;
194     }
195 
196     // Write # of bytes in trainName to file
197     const size_t trainNameSize = trainInfo.trainName.size();
198     const size_t trainNameSizeByteCount = sizeof(trainNameSize);
199     result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
200     if (result != trainNameSizeByteCount) {
201         VLOG("Failed to write train name size");
202         close(fd);
203         return false;
204     }
205 
206     // Write trainName to file
207     result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
208     if (result != trainNameSize) {
209         VLOG("Failed to write train name");
210         close(fd);
211         return false;
212     }
213 
214     // Write status to file
215     const size_t statusByteCount = sizeof(trainInfo.status);
216     result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
217     if (result != statusByteCount) {
218         VLOG("Failed to write status");
219         close(fd);
220         return false;
221     }
222 
223     // Write experiment id count to file.
224     const size_t experimentIdsCount = trainInfo.experimentIds.size();
225     const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
226     result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
227     if (result != experimentIdsCountByteCount) {
228         VLOG("Failed to write experiment id count");
229         close(fd);
230         return false;
231     }
232 
233     // Write experimentIds to file
234     for (size_t i = 0; i < experimentIdsCount; i++) {
235         const int64_t experimentId = trainInfo.experimentIds[i];
236         const size_t experimentIdByteCount = sizeof(experimentId);
237         result = write(fd, &experimentId, experimentIdByteCount);
238         if (result == experimentIdByteCount) {
239             VLOG("Successfully wrote experiment IDs");
240         } else {
241             VLOG("Failed to write experiment ids");
242             close(fd);
243             return false;
244         }
245     }
246 
247     // Write bools to file
248     const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
249     result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
250     if (result != boolByteCount) {
251       VLOG("Failed to write requires staging");
252       close(fd);
253       return false;
254     }
255 
256     result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
257     if (result != boolByteCount) {
258       VLOG("Failed to write rollback enabled");
259       close(fd);
260       return false;
261     }
262 
263     result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
264     if (result != boolByteCount) {
265       VLOG("Failed to write requires log latency monitor");
266       close(fd);
267       return false;
268     }
269 
270     close(fd);
271     return true;
272 }
273 
readTrainInfo(const std::string & trainName,InstallTrainInfo & trainInfo)274 bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
275     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
276     return readTrainInfoLocked(trainName, trainInfo);
277 }
278 
readTrainInfoLocked(const std::string & trainName,InstallTrainInfo & trainInfo)279 bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
280     trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
281     string fileName = findTrainInfoFileNameLocked(trainName);
282     if (fileName.empty()) {
283         return false;
284     }
285     int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC);
286     if (fd == -1) {
287         VLOG("Failed to open %s", fileName.c_str());
288         return false;
289     }
290 
291     // Read the magic word
292     uint32_t magic;
293     size_t result = read(fd, &magic, sizeof(magic));
294     if (result != sizeof(magic)) {
295         VLOG("Failed to read train info magic");
296         close(fd);
297         return false;
298     }
299 
300     if (magic != TRAIN_INFO_FILE_MAGIC) {
301         VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC);
302         close(fd);
303         return false;
304     }
305 
306     // Read the train version code
307     const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode));
308     result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
309     if (result != trainVersionCodeByteCount) {
310         VLOG("Failed to read train version code from train info file");
311         close(fd);
312         return false;
313     }
314 
315     // Read # of bytes taken by trainName in the file.
316     size_t trainNameSize;
317     result = read(fd, &trainNameSize, sizeof(size_t));
318     if (result != sizeof(size_t)) {
319         VLOG("Failed to read train name size from train info file");
320         close(fd);
321         return false;
322     }
323 
324     // On devices that are upgrading from 32 to 64 bit, reading size_t bytes will result in reading
325     // the wrong amount of data. We try to detect that issue here and read the file correctly.
326     // In the normal case on 64-bit devices, the trainNameSize's upper 4 bytes should be 0, but if
327     // a 32-bit device wrote the file, these would be the first 4 bytes of the train name.
328     bool is32To64BitUpgrade = false;
329     if ((sizeof(size_t) == sizeof(int64_t)) && (trainNameSize & 0xFFFFFFFF00000000) != 0) {
330         is32To64BitUpgrade = true;
331         // Reset the file offset to the magic + version code, and reread from the train name size.
332         off_t seekResult = lseek(fd, sizeof(magic) + sizeof(trainInfo.trainVersionCode), SEEK_SET);
333 
334         if (seekResult != sizeof(magic) + sizeof(trainInfo.trainVersionCode)) {
335             VLOG("Failed to reset file offset when reading 32 to 64 bit train info");
336             close(fd);
337             return false;
338         }
339 
340         int32_t trainNameSize32Bit;
341         result = read(fd, &trainNameSize32Bit, sizeof(int32_t));
342         if (result != sizeof(int32_t)) {
343             VLOG("Failed to read train name size 32 bit from train info file");
344             close(fd);
345             return false;
346         }
347         trainNameSize = trainNameSize32Bit;
348     }
349 
350     // Read trainName
351     trainInfo.trainName.resize(trainNameSize);
352     result = read(fd, trainInfo.trainName.data(), trainNameSize);
353     if (result != trainNameSize) {
354         VLOG("Failed to read train name from train info file");
355         close(fd);
356         return false;
357     }
358 
359     // Read status
360     const size_t statusByteCount = sizeof(trainInfo.status);
361     result = read(fd, &trainInfo.status, statusByteCount);
362     if (result != statusByteCount) {
363         VLOG("Failed to read train status from train info file");
364         close(fd);
365         return false;
366     }
367 
368     // Read experiment ids count.
369     size_t experimentIdsCount;
370     int32_t experimentIdsCount32Bit;
371     if (is32To64BitUpgrade) {
372         result = read(fd, &experimentIdsCount32Bit, sizeof(int32_t));
373         if (result != sizeof(int32_t)) {
374             VLOG("Failed to read train experiment id count 32 bit from train info file");
375             close(fd);
376             return false;
377         }
378         experimentIdsCount = experimentIdsCount32Bit;
379     } else {
380         result = read(fd, &experimentIdsCount, sizeof(size_t));
381         if (result != sizeof(size_t)) {
382             VLOG("Failed to read train experiment id count from train info file");
383             close(fd);
384             return false;
385         }
386     }
387 
388     // Read experimentIds
389     for (size_t i = 0; i < experimentIdsCount; i++) {
390         int64_t experimentId;
391         result = read(fd, &experimentId, sizeof(experimentId));
392         if (result != sizeof(experimentId)) {
393             VLOG("Failed to read train experiment id from train info file");
394             close(fd);
395             return false;
396         }
397         trainInfo.experimentIds.push_back(experimentId);
398     }
399 
400     // Read bools
401     const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
402     result = read(fd, &trainInfo.requiresStaging, boolByteCount);
403     if (result != boolByteCount) {
404         VLOG("Failed to read requires requires staging from train info file");
405         close(fd);
406         return false;
407     }
408 
409     result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
410     if (result != boolByteCount) {
411         VLOG("Failed to read requires rollback enabled from train info file");
412         close(fd);
413         return false;
414     }
415 
416     result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
417     if (result != boolByteCount) {
418         VLOG("Failed to read requires requires low latency monitor from train info file");
419         close(fd);
420         return false;
421     }
422 
423     // Expect to be at EOF.
424     char c;
425     result = read(fd, &c, 1);
426     if (result != 0) {
427         VLOG("Failed to read train info from file. Did not get expected EOF.");
428         close(fd);
429         return false;
430     }
431 
432     VLOG("Read train info file successful");
433     close(fd);
434     return true;
435 }
436 
readAllTrainInfo()437 vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
438     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
439     vector<InstallTrainInfo> trainInfoList;
440     unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
441     if (dir == NULL) {
442         VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
443         return trainInfoList;
444     }
445 
446     dirent* de;
447     while ((de = readdir(dir.get()))) {
448         char* name = de->d_name;
449         if (name[0] == '.' || de->d_type == DT_DIR) {
450             continue;
451         }
452 
453         InstallTrainInfo trainInfo;
454         bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
455         if (!readSuccess) {
456             continue;
457         }
458         trainInfoList.push_back(trainInfo);
459     }
460     return trainInfoList;
461 }
462 
deleteFile(const char * file)463 void StorageManager::deleteFile(const char* file) {
464     if (remove(file) != 0) {
465         VLOG("Attempt to delete %s but is not found", file);
466     } else {
467         VLOG("Successfully deleted %s", file);
468     }
469 }
470 
deleteAllFiles(const char * path)471 void StorageManager::deleteAllFiles(const char* path) {
472     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
473     if (dir == NULL) {
474         VLOG("Directory does not exist: %s", path);
475         return;
476     }
477 
478     dirent* de;
479     while ((de = readdir(dir.get()))) {
480         char* name = de->d_name;
481         if (name[0] == '.' || de->d_type == DT_DIR) continue;
482         deleteFile(StringPrintf("%s/%s", path, name).c_str());
483     }
484 }
485 
deleteSuffixedFiles(const char * path,const char * suffix)486 void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
487     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
488     if (dir == NULL) {
489         VLOG("Directory does not exist: %s", path);
490         return;
491     }
492 
493     dirent* de;
494     while ((de = readdir(dir.get()))) {
495         char* name = de->d_name;
496         if (name[0] == '.' || de->d_type == DT_DIR) {
497             continue;
498         }
499         size_t nameLen = strlen(name);
500         size_t suffixLen = strlen(suffix);
501         if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
502             deleteFile(StringPrintf("%s/%s", path, name).c_str());
503         }
504     }
505 }
506 
sendBroadcast(const char * path,const std::function<void (const ConfigKey &)> & sendBroadcast)507 void StorageManager::sendBroadcast(const char* path,
508                                    const std::function<void(const ConfigKey&)>& sendBroadcast) {
509     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
510     if (dir == NULL) {
511         VLOG("no stats-data directory on disk");
512         return;
513     }
514 
515     dirent* de;
516     while ((de = readdir(dir.get()))) {
517         char* name = de->d_name;
518         if (name[0] == '.' || de->d_type == DT_DIR) continue;
519         VLOG("file %s", name);
520 
521         FileName output;
522         parseFileName(name, &output);
523         if (output.mTimestampSec == -1 || output.mIsHistory) continue;
524         sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId));
525     }
526 }
527 
hasConfigMetricsReport(const ConfigKey & key)528 bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
529     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
530     if (dir == NULL) {
531         VLOG("Path %s does not exist", STATS_DATA_DIR);
532         return false;
533     }
534 
535     string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
536 
537     dirent* de;
538     while ((de = readdir(dir.get()))) {
539         char* name = de->d_name;
540         if (name[0] == '.' || de->d_type == DT_DIR) continue;
541 
542         size_t nameLen = strlen(name);
543         size_t suffixLen = suffix.length();
544         if (suffixLen <= nameLen &&
545             strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
546             // Check again that the file name is parseable.
547             FileName output;
548             parseFileName(name, &output);
549             if (output.mTimestampSec == -1 || output.mIsHistory) continue;
550             return true;
551         }
552     }
553     return false;
554 }
555 
appendConfigMetricsReport(const ConfigKey & key,ProtoOutputStream * proto,bool erase_data,bool isAdb)556 void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
557                                                bool erase_data, bool isAdb) {
558     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
559     if (dir == NULL) {
560         VLOG("Path %s does not exist", STATS_DATA_DIR);
561         return;
562     }
563 
564     dirent* de;
565     while ((de = readdir(dir.get()))) {
566         char* name = de->d_name;
567         string fileName(name);
568         if (name[0] == '.' || de->d_type == DT_DIR) continue;
569         FileName output;
570         parseFileName(name, &output);
571 
572         if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) ||
573             output.mUid != key.GetUid() || output.mConfigId != key.GetId()) {
574             continue;
575         }
576 
577         auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str());
578         int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC);
579         if (fd != -1) {
580             string content;
581             if (android::base::ReadFdToString(fd, &content)) {
582                 proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
583                              content.c_str(), content.size());
584             }
585             close(fd);
586         } else {
587             ALOGE("file cannot be opened");
588         }
589 
590         if (erase_data) {
591             remove(fullPathName.c_str());
592         } else if (!output.mIsHistory && !isAdb) {
593             // This means a real data owner has called to get this data. But the config says it
594             // wants to keep a local history. So now this file must be renamed as a history file.
595             // So that next time, when owner calls getData() again, this data won't be uploaded
596             // again. rename returns 0 on success
597             if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) {
598                 ALOGE("Failed to rename file %s", fullPathName.c_str());
599             }
600         }
601     }
602 }
603 
readFileToString(const char * file,string * content)604 bool StorageManager::readFileToString(const char* file, string* content) {
605     int fd = open(file, O_RDONLY | O_CLOEXEC);
606     bool res = false;
607     if (fd != -1) {
608         if (android::base::ReadFdToString(fd, content)) {
609             res = true;
610         } else {
611             VLOG("Failed to read file %s\n", file);
612         }
613         close(fd);
614     }
615     return res;
616 }
617 
readConfigFromDisk(map<ConfigKey,StatsdConfig> & configsMap)618 void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
619     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
620     if (dir == NULL) {
621         VLOG("no default config on disk");
622         return;
623     }
624     trimToFit(STATS_SERVICE_DIR);
625 
626     dirent* de;
627     while ((de = readdir(dir.get()))) {
628         char* name = de->d_name;
629         if (name[0] == '.' || de->d_type == DT_DIR) continue;
630 
631         FileName output;
632         parseFileName(name, &output);
633         if (output.mTimestampSec == -1) continue;
634         string file_name = output.getFullFileName(STATS_SERVICE_DIR);
635         int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
636         if (fd != -1) {
637             string content;
638             if (android::base::ReadFdToString(fd, &content)) {
639                 StatsdConfig config;
640                 if (config.ParseFromString(content)) {
641                     configsMap[ConfigKey(output.mUid, output.mConfigId)] = config;
642                     VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid,
643                          (long long)output.mConfigId);
644                 }
645             }
646             close(fd);
647         }
648     }
649 }
650 
readConfigFromDisk(const ConfigKey & key,StatsdConfig * config)651 bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
652     string content;
653     return config != nullptr &&
654         StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
655 }
656 
readConfigFromDisk(const ConfigKey & key,string * content)657 bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
658     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
659                                              closedir);
660     if (dir == NULL) {
661         VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
662         return false;
663     }
664 
665     string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
666     dirent* de;
667     while ((de = readdir(dir.get()))) {
668         char* name = de->d_name;
669         if (name[0] == '.' || de->d_type == DT_DIR) {
670             continue;
671         }
672         size_t nameLen = strlen(name);
673         size_t suffixLen = suffix.length();
674         // There can be at most one file that matches this suffix (config key).
675         if (suffixLen <= nameLen &&
676             strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
677             int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
678                                   O_RDONLY | O_CLOEXEC);
679             if (fd != -1) {
680                 bool success = android::base::ReadFdToString(fd, content);
681                 close(fd);
682                 return success;
683             }
684         }
685     }
686     return false;
687 }
688 
hasIdenticalConfig(const ConfigKey & key,const vector<uint8_t> & config)689 bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
690                                         const vector<uint8_t>& config) {
691     string content;
692     if (StorageManager::readConfigFromDisk(key, &content)) {
693         vector<uint8_t> vec(content.begin(), content.end());
694         if (vec == config) {
695             return true;
696         }
697     }
698     return false;
699 }
700 
sortFiles(vector<FileInfo> * fileNames)701 void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
702     // Reverse sort to effectively remove from the back (oldest entries).
703     // This will sort files in reverse-chronological order. Local history files have lower
704     // priority than regular data files.
705     sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) {
706         // first consider if the file is a local history
707         if (lhs.mIsHistory && !rhs.mIsHistory) {
708             return false;
709         } else if (rhs.mIsHistory && !lhs.mIsHistory) {
710             return true;
711         }
712 
713         // then consider the age.
714         if (lhs.mFileAgeSec < rhs.mFileAgeSec) {
715             return true;
716         } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) {
717             return false;
718         }
719 
720         // then good luck.... use string::compare
721         return lhs.mFileName.compare(rhs.mFileName) > 0;
722     });
723 }
724 
trimToFit(const char * path,bool parseTimestampOnly)725 void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
726     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
727     if (dir == NULL) {
728         VLOG("Path %s does not exist", path);
729         return;
730     }
731     dirent* de;
732     int totalFileSize = 0;
733     vector<FileInfo> fileNames;
734     auto nowSec = getWallClockSec();
735     while ((de = readdir(dir.get()))) {
736         char* name = de->d_name;
737         if (name[0] == '.' || de->d_type == DT_DIR) continue;
738 
739         FileName output;
740         string file_name;
741         if (parseTimestampOnly) {
742             file_name = StringPrintf("%s/%s", path, name);
743             output.mTimestampSec = StrToInt64(strtok(name, "_"));
744             output.mIsHistory = false;
745         } else {
746             parseFileName(name, &output);
747             file_name = output.getFullFileName(path);
748         }
749         if (output.mTimestampSec == -1) continue;
750 
751         // Check for timestamp and delete if it's too old.
752         long fileAge = nowSec - output.mTimestampSec;
753         if (fileAge > StatsdStats::kMaxAgeSecond ||
754             (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) {
755             deleteFile(file_name.c_str());
756             continue;
757         }
758 
759         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
760         int fileSize = 0;
761         if (file.is_open()) {
762             file.seekg(0, ios::end);
763             fileSize = file.tellg();
764             file.close();
765             totalFileSize += fileSize;
766         }
767         fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge);
768     }
769 
770     if (fileNames.size() > StatsdStats::kMaxFileNumber ||
771         totalFileSize > StatsdStats::kMaxFileSize) {
772         sortFiles(&fileNames);
773     }
774 
775     // Start removing files from oldest to be under the limit.
776     while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
777                                     totalFileSize > StatsdStats::kMaxFileSize)) {
778         totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes;
779         deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str());
780         fileNames.pop_back();
781     }
782 }
783 
printStats(int outFd)784 void StorageManager::printStats(int outFd) {
785     printDirStats(outFd, STATS_SERVICE_DIR);
786     printDirStats(outFd, STATS_DATA_DIR);
787 }
788 
printDirStats(int outFd,const char * path)789 void StorageManager::printDirStats(int outFd, const char* path) {
790     dprintf(outFd, "Printing stats of %s\n", path);
791     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
792     if (dir == NULL) {
793         VLOG("Path %s does not exist", path);
794         return;
795     }
796     dirent* de;
797     int fileCount = 0;
798     int totalFileSize = 0;
799     while ((de = readdir(dir.get()))) {
800         char* name = de->d_name;
801         if (name[0] == '.' || de->d_type == DT_DIR) {
802             continue;
803         }
804         FileName output;
805         parseFileName(name, &output);
806         if (output.mTimestampSec == -1) continue;
807         dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1,
808                 (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId,
809                 (output.mIsHistory ? "local history" : ""));
810         string file_name = output.getFullFileName(path);
811         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
812         if (file.is_open()) {
813             file.seekg(0, ios::end);
814             int fileSize = file.tellg();
815             file.close();
816             dprintf(outFd, ", File Size: %d bytes", fileSize);
817             totalFileSize += fileSize;
818         }
819         dprintf(outFd, "\n");
820         fileCount++;
821     }
822     dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
823             totalFileSize);
824 }
825 
enforceDbGuardrails(const char * path,const int64_t currWallClockSec,const int64_t maxBytes)826 void StorageManager::enforceDbGuardrails(const char* path, const int64_t currWallClockSec,
827                                          const int64_t maxBytes) {
828     if (!IsAtLeastU()) {
829         return;
830     }
831     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
832     if (dir == NULL) {
833         VLOG("Path %s does not exist", path);
834         return;
835     }
836 
837     dirent* de;
838     int64_t deleteThresholdSec = currWallClockSec - StatsdStats::kMaxAgeSecond;
839     while ((de = readdir(dir.get()))) {
840         char* name = de->d_name;
841         if (name[0] == '.' || de->d_type == DT_DIR) continue;
842         string fullPathName = StringPrintf("%s/%s", path, name);
843         struct stat fileInfo;
844         const ConfigKey key = parseDbName(name);
845         if (stat(fullPathName.c_str(), &fileInfo) != 0) {
846             // Remove file if stat fails.
847             remove(fullPathName.c_str());
848             continue;
849         }
850         StatsdStats::getInstance().noteRestrictedConfigDbSize(key, currWallClockSec,
851                                                               fileInfo.st_size);
852         if (fileInfo.st_mtime <= deleteThresholdSec || fileInfo.st_size >= maxBytes) {
853             remove(fullPathName.c_str());
854         }
855         if (hasFile(dbutils::getDbName(key).c_str())) {
856             dbutils::verifyIntegrityAndDeleteIfNecessary(key);
857         } else {
858             // Remove file if the file name fails to parse.
859             remove(fullPathName.c_str());
860         }
861     }
862 }
863 
hasFile(const char * file)864 bool StorageManager::hasFile(const char* file) {
865     struct stat fileInfo;
866     return stat(file, &fileInfo) == 0;
867 }
868 
869 }  // namespace statsd
870 }  // namespace os
871 }  // namespace android
872