blob: e826061a6a4b2418b1c166516f9181004c91f608 [file] [log] [blame]
Joe Onorato99598ee2019-02-11 15:55:13 +00001/*
2 * Copyright (C) 2019 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#include "Log.h"
18
19#include "WorkDirectory.h"
20
Yao Chencbafce92019-04-01 15:56:44 -070021#include "proto_util.h"
Joe Onorato99598ee2019-02-11 15:55:13 +000022#include "PrivacyFilter.h"
23
24#include <google/protobuf/io/zero_copy_stream_impl.h>
25#include <private/android_filesystem_config.h>
26
27#include <iomanip>
28#include <map>
29#include <sstream>
30#include <thread>
31#include <vector>
32
33#include <sys/stat.h>
34#include <time.h>
35#include <unistd.h>
36#include <inttypes.h>
37
38namespace android {
39namespace os {
40namespace incidentd {
41
42using std::thread;
43using google::protobuf::MessageLite;
44using google::protobuf::RepeatedPtrField;
45using google::protobuf::io::FileInputStream;
46using google::protobuf::io::FileOutputStream;
47
48/**
49 * Turn off to skip removing files for debugging.
50 */
51static const bool DO_UNLINK = true;
52
53/**
54 * File extension for envelope files.
55 */
56static const string EXTENSION_ENVELOPE(".envelope");
57
58/**
59 * File extension for data files.
60 */
61static const string EXTENSION_DATA(".data");
62
63/**
64 * Send these reports to dropbox.
65 */
66const ComponentName DROPBOX_SENTINEL("android", "DROPBOX");
67
Yao Chencbafce92019-04-01 15:56:44 -070068/** metadata field id in IncidentProto */
69const int FIELD_ID_INCIDENT_METADATA = 2;
70
Joe Onorato99598ee2019-02-11 15:55:13 +000071/**
72 * Read a protobuf from disk into the message.
73 */
74static status_t read_proto(MessageLite* msg, const string& filename) {
75 int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
76 if (fd < 0) {
77 return -errno;
78 }
79
80 FileInputStream stream(fd);
81 stream.SetCloseOnDelete(fd);
82
83 if (!msg->ParseFromZeroCopyStream(&stream)) {
84 return BAD_VALUE;
85 }
86
87 return stream.GetErrno();
88}
89
90/**
91 * Write a protobuf to disk.
92 */
93static status_t write_proto(const MessageLite& msg, const string& filename) {
94 int fd = open(filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
95 if (fd < 0) {
96 return -errno;
97 }
98
99 FileOutputStream stream(fd);
100 stream.SetCloseOnDelete(fd);
101
102 if (!msg.SerializeToZeroCopyStream(&stream)) {
103 ALOGW("write_proto: error writing to %s", filename.c_str());
104 return BAD_VALUE;
105 }
106
107 return stream.GetErrno();
108}
109
110static string strip_extension(const string& filename) {
111 return filename.substr(0, filename.find('.'));
112}
113
114static bool ends_with(const string& str, const string& ending) {
115 if (str.length() >= ending.length()) {
116 return str.compare(str.length()-ending.length(), ending.length(), ending) == 0;
117 } else {
118 return false;
119 }
120}
121
122// Returns true if it was a valid timestamp.
123static bool parse_timestamp_ns(const string& id, int64_t* result) {
124 char* endptr;
125 *result = strtoll(id.c_str(), &endptr, 10);
126 return id.length() != 0 && *endptr == '\0';
127}
128
129static bool has_section(const ReportFileProto_Report& report, int section) {
130 const size_t sectionCount = report.section_size();
131 for (int i = 0; i < sectionCount; i++) {
132 if (report.section(i) == section) {
133 return true;
134 }
135 }
136 return false;
137}
138
139status_t create_directory(const char* directory) {
140 struct stat st;
141 status_t err = NO_ERROR;
142 char* dir = strdup(directory);
143
144 // Skip first slash
145 char* d = dir + 1;
146
147 // Create directories, assigning them to the system user
148 bool last = false;
149 while (!last) {
150 d = strchr(d, '/');
151 if (d != NULL) {
152 *d = '\0';
153 } else {
154 last = true;
155 }
156 if (stat(dir, &st) == 0) {
157 if (!S_ISDIR(st.st_mode)) {
158 err = ALREADY_EXISTS;
159 goto done;
160 }
161 } else {
162 ALOGE("No such directory %s, something wrong.", dir);
163 err = -1;
164 goto done;
165 }
166 if (!last) {
167 *d++ = '/';
168 }
169 }
170
171 // Ensure that the final directory is owned by the system with 0770. If it isn't
172 // we won't write into it.
173 if (stat(directory, &st) != 0) {
174 ALOGE("No incident reports today. Can't stat: %s", directory);
175 err = -errno;
176 goto done;
177 }
178 if ((st.st_mode & 0777) != 0770) {
179 ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode,
180 directory);
181 err = BAD_VALUE;
182 goto done;
183 }
184 if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) {
185 ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
186 st.st_uid, st.st_gid, directory);
187 err = BAD_VALUE;
188 goto done;
189 }
190
191done:
192 free(dir);
193 return err;
194}
195
196void log_envelope(const ReportFileProto& envelope) {
197 ALOGD("Envelope: {");
198 for (int i=0; i<envelope.report_size(); i++) {
199 ALOGD(" report {");
200 ALOGD(" pkg=%s", envelope.report(i).pkg().c_str());
201 ALOGD(" cls=%s", envelope.report(i).cls().c_str());
202 ALOGD(" share_approved=%d", envelope.report(i).share_approved());
203 ALOGD(" privacy_policy=%d", envelope.report(i).privacy_policy());
204 ALOGD(" all_sections=%d", envelope.report(i).all_sections());
205 for (int j=0; j<envelope.report(i).section_size(); j++) {
206 ALOGD(" section[%d]=%d", j, envelope.report(i).section(j));
207 }
208 ALOGD(" }");
209 }
210 ALOGD(" data_file=%s", envelope.data_file().c_str());
211 ALOGD(" privacy_policy=%d", envelope.privacy_policy());
Colin Crossdfa50902019-08-14 09:37:07 -0700212 ALOGD(" data_file_size=%" PRIi64, (int64_t)envelope.data_file_size());
Joe Onorato99598ee2019-02-11 15:55:13 +0000213 ALOGD(" completed=%d", envelope.completed());
214 ALOGD("}");
215}
216
217// ================================================================================
218struct WorkDirectoryEntry {
219 WorkDirectoryEntry();
220 explicit WorkDirectoryEntry(const WorkDirectoryEntry& that);
221 ~WorkDirectoryEntry();
222
223 string envelope;
224 string data;
225 int64_t timestampNs;
226 off_t size;
227};
228
229WorkDirectoryEntry::WorkDirectoryEntry()
230 :envelope(),
231 data(),
232 size(0) {
233}
234
235WorkDirectoryEntry::WorkDirectoryEntry(const WorkDirectoryEntry& that)
236 :envelope(that.envelope),
237 data(that.data),
238 size(that.size) {
239}
240
241WorkDirectoryEntry::~WorkDirectoryEntry() {
242}
243
244// ================================================================================
245ReportFile::ReportFile(const sp<WorkDirectory>& workDirectory, int64_t timestampNs,
246 const string& envelopeFileName, const string& dataFileName)
247 :mWorkDirectory(workDirectory),
248 mTimestampNs(timestampNs),
249 mEnvelopeFileName(envelopeFileName),
250 mDataFileName(dataFileName),
251 mEnvelope(),
252 mDataFd(-1),
253 mError(NO_ERROR) {
254 // might get overwritten when we read but that's ok
255 mEnvelope.set_data_file(mDataFileName);
256}
257
258ReportFile::~ReportFile() {
259 if (mDataFd >= 0) {
260 close(mDataFd);
261 }
262}
263
264int64_t ReportFile::getTimestampNs() const {
265 return mTimestampNs;
266}
267
268void ReportFile::addReport(const IncidentReportArgs& args) {
269 // There is only one report per component. Merge into an existing one if necessary.
270 ReportFileProto_Report* report;
271 const int reportCount = mEnvelope.report_size();
272 int i = 0;
273 for (; i < reportCount; i++) {
274 report = mEnvelope.mutable_report(i);
275 if (report->pkg() == args.receiverPkg() && report->cls() == args.receiverCls()) {
276 if (args.getPrivacyPolicy() < report->privacy_policy()) {
277 // Lower privacy policy (less restrictive) wins.
278 report->set_privacy_policy(args.getPrivacyPolicy());
279 }
280 report->set_all_sections(report->all_sections() | args.all());
281 for (int section: args.sections()) {
282 if (!has_section(*report, section)) {
283 report->add_section(section);
284 }
285 }
286 break;
287 }
288 }
289 if (i >= reportCount) {
290 report = mEnvelope.add_report();
291 report->set_pkg(args.receiverPkg());
292 report->set_cls(args.receiverCls());
293 report->set_privacy_policy(args.getPrivacyPolicy());
294 report->set_all_sections(args.all());
295 for (int section: args.sections()) {
296 report->add_section(section);
297 }
298 }
299
300 for (const vector<uint8_t>& header: args.headers()) {
301 report->add_header(header.data(), header.size());
302 }
303}
304
305void ReportFile::removeReport(const string& pkg, const string& cls) {
306 RepeatedPtrField<ReportFileProto_Report>* reports = mEnvelope.mutable_report();
307 const int reportCount = reports->size();
308 for (int i = 0; i < reportCount; i++) {
309 const ReportFileProto_Report& r = reports->Get(i);
310 if (r.pkg() == pkg && r.cls() == cls) {
311 reports->DeleteSubrange(i, 1);
312 return;
313 }
314 }
315}
316
317void ReportFile::removeReports(const string& pkg) {
318 RepeatedPtrField<ReportFileProto_Report>* reports = mEnvelope.mutable_report();
319 const int reportCount = reports->size();
320 for (int i = reportCount-1; i >= 0; i--) {
321 const ReportFileProto_Report& r = reports->Get(i);
322 if (r.pkg() == pkg) {
323 reports->DeleteSubrange(i, 1);
324 }
325 }
326}
327
328void ReportFile::setMetadata(const IncidentMetadata& metadata) {
329 *mEnvelope.mutable_metadata() = metadata;
330}
331
332void ReportFile::markCompleted() {
333 mEnvelope.set_completed(true);
334}
335
336status_t ReportFile::markApproved(const string& pkg, const string& cls) {
337 size_t const reportCount = mEnvelope.report_size();
338 for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) {
339 ReportFileProto_Report* report = mEnvelope.mutable_report(reportIndex);
340 if (report->pkg() == pkg && report->cls() == cls) {
341 report->set_share_approved(true);
342 return NO_ERROR;
343 }
344 }
345 return NAME_NOT_FOUND;
346}
347
348void ReportFile::setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy) {
349 mEnvelope.set_privacy_policy(persistedPrivacyPolicy);
350}
351
352status_t ReportFile::saveEnvelope() {
353 return save_envelope_impl(true);
354}
355
356status_t ReportFile::trySaveEnvelope() {
357 return save_envelope_impl(false);
358}
359
360status_t ReportFile::loadEnvelope() {
361 return load_envelope_impl(true);
362}
363
364status_t ReportFile::tryLoadEnvelope() {
365 return load_envelope_impl(false);
366}
367
368const ReportFileProto& ReportFile::getEnvelope() {
369 return mEnvelope;
370}
371
372status_t ReportFile::startWritingDataFile() {
373 if (mDataFd >= 0) {
374 ALOGW("ReportFile::startWritingDataFile called with the file already open: %s",
375 mDataFileName.c_str());
376 return ALREADY_EXISTS;
377 }
378 mDataFd = open(mDataFileName.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
379 if (mDataFd < 0) {
380 return -errno;
381 }
382 return NO_ERROR;
383}
384
385void ReportFile::closeDataFile() {
386 if (mDataFd >= 0) {
387 mEnvelope.set_data_file_size(lseek(mDataFd, 0, SEEK_END));
388 close(mDataFd);
389 mDataFd = -1;
390 }
391}
392
Yao Chencbafce92019-04-01 15:56:44 -0700393status_t ReportFile::startFilteringData(int writeFd, const IncidentReportArgs& args) {
Joe Onorato99598ee2019-02-11 15:55:13 +0000394 // Open data file.
395 int dataFd = open(mDataFileName.c_str(), O_RDONLY | O_CLOEXEC);
396 if (dataFd < 0) {
Yao Chencbafce92019-04-01 15:56:44 -0700397 ALOGW("Error opening incident report '%s' %s", getDataFileName().c_str(), strerror(-errno));
398 close(writeFd);
Joe Onorato99598ee2019-02-11 15:55:13 +0000399 return -errno;
400 }
401
402 // Check that the size on disk is what we thought we wrote.
403 struct stat st;
404 if (fstat(dataFd, &st) != 0) {
Yao Chencbafce92019-04-01 15:56:44 -0700405 ALOGW("Error running fstat incident report '%s' %s", getDataFileName().c_str(),
406 strerror(-errno));
407 close(writeFd);
Joe Onorato99598ee2019-02-11 15:55:13 +0000408 return -errno;
409 }
410 if (st.st_size != mEnvelope.data_file_size()) {
411 ALOGW("File size mismatch. Envelope says %" PRIi64 " bytes but data file is %" PRIi64
Yao Chencbafce92019-04-01 15:56:44 -0700412 " bytes: %s",
413 (int64_t)mEnvelope.data_file_size(), st.st_size, mDataFileName.c_str());
Joe Onorato99598ee2019-02-11 15:55:13 +0000414 ALOGW("Removing incident report");
415 mWorkDirectory->remove(this);
Yao Chencbafce92019-04-01 15:56:44 -0700416 close(writeFd);
Joe Onorato99598ee2019-02-11 15:55:13 +0000417 return BAD_VALUE;
418 }
419
Yao Chencbafce92019-04-01 15:56:44 -0700420 status_t err;
421
422 for (const auto& report : mEnvelope.report()) {
423 for (const auto& header : report.header()) {
424 write_header_section(writeFd,
425 reinterpret_cast<const uint8_t*>(header.c_str()), header.size());
426 }
Joe Onorato99598ee2019-02-11 15:55:13 +0000427 }
428
Yao Chencbafce92019-04-01 15:56:44 -0700429 if (mEnvelope.has_metadata()) {
430 write_section(writeFd, FIELD_ID_INCIDENT_METADATA, mEnvelope.metadata());
431 }
Joe Onorato99598ee2019-02-11 15:55:13 +0000432
Yao Chencbafce92019-04-01 15:56:44 -0700433 err = filter_and_write_report(writeFd, dataFd, mEnvelope.privacy_policy(), args);
434 if (err != NO_ERROR) {
435 ALOGW("Error writing incident report '%s' to dropbox: %s", getDataFileName().c_str(),
436 strerror(-err));
437 }
Joe Onorato99598ee2019-02-11 15:55:13 +0000438
Yao Chencbafce92019-04-01 15:56:44 -0700439 close(writeFd);
Joe Onorato99598ee2019-02-11 15:55:13 +0000440 return NO_ERROR;
441}
442
443string ReportFile::getDataFileName() const {
444 return mDataFileName;
445}
446
447string ReportFile::getEnvelopeFileName() const {
448 return mEnvelopeFileName;
449}
450
451int ReportFile::getDataFileFd() {
452 return mDataFd;
453}
454
455void ReportFile::setWriteError(status_t err) {
456 mError = err;
457}
458
459status_t ReportFile::getWriteError() {
460 return mError;
461}
462
463string ReportFile::getId() {
464 return to_string(mTimestampNs);
465}
466
467status_t ReportFile::save_envelope_impl(bool cleanup) {
468 status_t err;
469 err = write_proto(mEnvelope, mEnvelopeFileName);
470 if (err != NO_ERROR) {
471 // If there was an error writing the envelope, then delete the whole thing.
472 if (cleanup) {
473 mWorkDirectory->remove(this);
474 }
475 return err;
476 }
477 return NO_ERROR;
478}
479
480status_t ReportFile::load_envelope_impl(bool cleanup) {
481 status_t err;
482 err = read_proto(&mEnvelope, mEnvelopeFileName);
483 if (err != NO_ERROR) {
484 // If there was an error reading the envelope, then delete the whole thing.
485 if (cleanup) {
486 mWorkDirectory->remove(this);
487 }
488 return err;
489 }
490 return NO_ERROR;
491}
492
493
494
495// ================================================================================
Yao Chencbafce92019-04-01 15:56:44 -0700496//
Joe Onorato99598ee2019-02-11 15:55:13 +0000497
498WorkDirectory::WorkDirectory()
499 :mDirectory("/data/misc/incidents"),
500 mMaxFileCount(100),
Yao Chen31829672019-05-23 10:43:47 -0700501 mMaxDiskUsageBytes(100 * 1024 * 1024) { // Incident reports can take up to 100MB on disk.
Joe Onorato99598ee2019-02-11 15:55:13 +0000502 // TODO: Should be a flag.
503 create_directory(mDirectory.c_str());
504}
505
506WorkDirectory::WorkDirectory(const string& dir, int maxFileCount, long maxDiskUsageBytes)
507 :mDirectory(dir),
508 mMaxFileCount(maxFileCount),
509 mMaxDiskUsageBytes(maxDiskUsageBytes) {
510 create_directory(mDirectory.c_str());
511}
512
513sp<ReportFile> WorkDirectory::createReportFile() {
514 unique_lock<mutex> lock(mLock);
515 status_t err;
516
517 clean_directory_locked();
518
519 int64_t timestampNs = make_timestamp_ns_locked();
520 string envelopeFileName = make_filename(timestampNs, EXTENSION_ENVELOPE);
521 string dataFileName = make_filename(timestampNs, EXTENSION_DATA);
522
523 sp<ReportFile> result = new ReportFile(this, timestampNs, envelopeFileName, dataFileName);
524
525 err = result->trySaveEnvelope();
526 if (err != NO_ERROR) {
527 ALOGW("Can't save envelope file %s: %s", strerror(-errno), envelopeFileName.c_str());
528 return nullptr;
529 }
530
531 return result;
532}
533
534status_t WorkDirectory::getReports(vector<sp<ReportFile>>* result, int64_t after) {
535 unique_lock<mutex> lock(mLock);
536
537 const bool DBG = true;
538
539 if (DBG) {
540 ALOGD("WorkDirectory::getReports");
541 }
542
543 map<string,WorkDirectoryEntry> files;
544 get_directory_contents_locked(&files, after);
545 for (map<string,WorkDirectoryEntry>::iterator it = files.begin();
546 it != files.end(); it++) {
547 sp<ReportFile> reportFile = new ReportFile(this, it->second.timestampNs,
548 it->second.envelope, it->second.data);
549 if (DBG) {
550 ALOGD(" %s", reportFile->getId().c_str());
551 }
552 result->push_back(reportFile);
553 }
554 return NO_ERROR;
555}
556
557sp<ReportFile> WorkDirectory::getReport(const string& pkg, const string& cls, const string& id,
558 IncidentReportArgs* args) {
559 unique_lock<mutex> lock(mLock);
560
561 status_t err;
562 int64_t timestampNs;
563 if (!parse_timestamp_ns(id, &timestampNs)) {
564 return nullptr;
565 }
566
567 // Make the ReportFile object, and then see if it's valid and for pkg and cls.
568 sp<ReportFile> result = new ReportFile(this, timestampNs,
569 make_filename(timestampNs, EXTENSION_ENVELOPE),
570 make_filename(timestampNs, EXTENSION_DATA));
571
572 err = result->tryLoadEnvelope();
573 if (err != NO_ERROR) {
574 ALOGW("Can't open envelope file for report %s/%s %s", pkg.c_str(), cls.c_str(), id.c_str());
575 return nullptr;
576 }
577
578 const ReportFileProto& envelope = result->getEnvelope();
579 const size_t reportCount = envelope.report_size();
580 for (int i = 0; i < reportCount; i++) {
581 const ReportFileProto_Report& report = envelope.report(i);
582 if (report.pkg() == pkg && report.cls() == cls) {
583 if (args != nullptr) {
584 get_args_from_report(args, report);
585 }
586 return result;
587 }
588
589 }
590
591 return nullptr;
592}
593
594bool WorkDirectory::hasMore(int64_t after) {
595 unique_lock<mutex> lock(mLock);
596
597 map<string,WorkDirectoryEntry> files;
598 get_directory_contents_locked(&files, after);
599 return files.size() > 0;
600}
601
602void WorkDirectory::commit(const sp<ReportFile>& report, const string& pkg, const string& cls) {
603 status_t err;
604 ALOGI("Committing report %s for %s/%s", report->getId().c_str(), pkg.c_str(), cls.c_str());
605
606 unique_lock<mutex> lock(mLock);
607
608 // Load the envelope here inside the lock.
609 err = report->loadEnvelope();
610
611 report->removeReport(pkg, cls);
612
613 delete_files_for_report_if_necessary(report);
614}
615
616void WorkDirectory::commitAll(const string& pkg) {
617 status_t err;
618 ALOGI("All reports for %s", pkg.c_str());
619
620 unique_lock<mutex> lock(mLock);
621
622 map<string,WorkDirectoryEntry> files;
623 get_directory_contents_locked(&files, 0);
624
625 for (map<string,WorkDirectoryEntry>::iterator it = files.begin();
626 it != files.end(); it++) {
627 sp<ReportFile> reportFile = new ReportFile(this, it->second.timestampNs,
628 it->second.envelope, it->second.data);
629
630 err = reportFile->loadEnvelope();
631 if (err != NO_ERROR) {
632 continue;
633 }
634
635 reportFile->removeReports(pkg);
636
637 delete_files_for_report_if_necessary(reportFile);
638 }
639}
640
641void WorkDirectory::remove(const sp<ReportFile>& report) {
642 unique_lock<mutex> lock(mLock);
643 // Set this to false to leave files around for debugging.
644 if (DO_UNLINK) {
645 unlink(report->getDataFileName().c_str());
646 unlink(report->getEnvelopeFileName().c_str());
647 }
648}
649
650int64_t WorkDirectory::make_timestamp_ns_locked() {
651 // Guarantee that we don't have duplicate timestamps.
652 // This is a little bit lame, but since reports are created on the
653 // same thread and are kinda slow we'll seldomly actually hit the
654 // condition. The bigger risk is the clock getting reset and causing
655 // a collision. In that case, we'll just make incident reporting a
656 // little bit slower. Nobody will notice if we just loop until we
657 // have a unique file name.
658 int64_t timestampNs = 0;
659 do {
660 struct timespec spec;
661 if (timestampNs > 0) {
662 spec.tv_sec = 0;
663 spec.tv_nsec = 1;
664 nanosleep(&spec, nullptr);
665 }
666 clock_gettime(CLOCK_REALTIME, &spec);
667 timestampNs = (spec.tv_sec) * 1000 + spec.tv_nsec;
668 } while (file_exists_locked(timestampNs));
669 return timestampNs;
670}
671
672/**
673 * It is required to hold the lock here so in case someone else adds it
674 * our result is still correct for the caller.
675 */
676bool WorkDirectory::file_exists_locked(int64_t timestampNs) {
677 const string filename = make_filename(timestampNs, EXTENSION_ENVELOPE);
678 struct stat st;
679 return stat(filename.c_str(), &st) == 0;
680}
681
682string WorkDirectory::make_filename(int64_t timestampNs, const string& extension) {
683 // Zero pad the timestamp so it can also be alpha sorted.
684 stringstream result;
685 result << mDirectory << '/' << setfill('0') << setw(20) << timestampNs << extension;
686 return result.str();
687}
688
689off_t WorkDirectory::get_directory_contents_locked(map<string,WorkDirectoryEntry>* files,
690 int64_t after) {
691 DIR* dir;
692 struct dirent* entry;
693
694 if ((dir = opendir(mDirectory.c_str())) == NULL) {
695 ALOGE("Couldn't open incident directory: %s", mDirectory.c_str());
696 return -1;
697 }
698
699 string dirbase(mDirectory);
700 if (mDirectory[dirbase.size() - 1] != '/') dirbase += "/";
701
702 off_t totalSize = 0;
703
704 // Enumerate, count and add up size
705 while ((entry = readdir(dir)) != NULL) {
706 if (entry->d_name[0] == '.') {
707 continue;
708 }
709 string entryname = entry->d_name; // local to this dir
710 string filename = dirbase + entryname; // fully qualified
711
712 bool isEnvelope = ends_with(entryname, EXTENSION_ENVELOPE);
713 bool isData = ends_with(entryname, EXTENSION_DATA);
714
715 // If the file isn't one of our files, just ignore it. Otherwise,
716 // sum up the sizes.
717 if (isEnvelope || isData) {
718 string timestamp = strip_extension(entryname);
719
720 int64_t timestampNs;
721 if (!parse_timestamp_ns(timestamp, &timestampNs)) {
722 continue;
723 }
724
725 if (after == 0 || timestampNs > after) {
726 struct stat st;
727 if (stat(filename.c_str(), &st) != 0) {
728 ALOGE("Unable to stat file %s", filename.c_str());
729 continue;
730 }
731 if (!S_ISREG(st.st_mode)) {
732 continue;
733 }
734
735 WorkDirectoryEntry& entry = (*files)[timestamp];
736 if (isEnvelope) {
737 entry.envelope = filename;
738 } else if (isData) {
739 entry.data = filename;
740 }
741 entry.timestampNs = timestampNs;
742 entry.size += st.st_size;
743 totalSize += st.st_size;
744 }
745 }
746 }
747
748 closedir(dir);
749
750 // Now check if there are any data files that don't have envelope files.
751 // If there are, then just go ahead and delete them now. Don't wait for
752 // a cleaning.
753
754 if (DO_UNLINK) {
755 map<string,WorkDirectoryEntry>::iterator it = files->begin();
756 while (it != files->end()) {
757 if (it->second.envelope.length() == 0) {
758 unlink(it->second.data.c_str());
759 it = files->erase(it);
760 } else {
761 it++;
762 }
763 }
764 }
765
766 return totalSize;
767}
768
769void WorkDirectory::clean_directory_locked() {
770 DIR* dir;
771 struct dirent* entry;
772 struct stat st;
773
774 // Map of filename without extension to the entries about it. Conveniently,
775 // this also keeps the list sorted by filename, which is a timestamp.
776 map<string,WorkDirectoryEntry> files;
777 off_t totalSize = get_directory_contents_locked(&files, 0);
778 if (totalSize < 0) {
779 return;
780 }
781 int totalCount = files.size();
782
783 // Count or size is less than max, then we're done.
784 if (totalSize < mMaxDiskUsageBytes && totalCount < mMaxFileCount) {
785 return;
786 }
787
788 // Remove files until we're under our limits.
789 if (DO_UNLINK) {
790 for (map<string, WorkDirectoryEntry>::const_iterator it = files.begin();
791 it != files.end() && (totalSize >= mMaxDiskUsageBytes
792 || totalCount >= mMaxFileCount);
793 it++) {
794 unlink(it->second.envelope.c_str());
795 unlink(it->second.data.c_str());
796 totalSize -= it->second.size;
797 totalCount--;
798 }
799 }
800}
801
802void WorkDirectory::delete_files_for_report_if_necessary(const sp<ReportFile>& report) {
803 if (report->getEnvelope().report_size() == 0) {
804 ALOGI("Report %s is finished. Deleting from storage.", report->getId().c_str());
805 if (DO_UNLINK) {
806 unlink(report->getDataFileName().c_str());
807 unlink(report->getEnvelopeFileName().c_str());
808 }
809 }
810}
811
812// ================================================================================
813void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report& report) {
814 out->setPrivacyPolicy(report.privacy_policy());
815 out->setAll(report.all_sections());
816 out->setReceiverPkg(report.pkg());
817 out->setReceiverCls(report.cls());
818
819 const int sectionCount = report.section_size();
820 for (int i = 0; i < sectionCount; i++) {
821 out->addSection(report.section(i));
822 }
823
824 const int headerCount = report.header_size();
825 for (int i = 0; i < headerCount; i++) {
826 const string& header = report.header(i);
827 vector<uint8_t> vec(header.begin(), header.end());
828 out->addHeader(vec);
829 }
830}
831
832
833} // namespace incidentd
834} // namespace os
835} // namespace android
836