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