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