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