• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "WorkDirectory.h"
20 
21 #include "proto_util.h"
22 #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 
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 /**
72  * Read a protobuf from disk into the message.
73  */
read_proto(MessageLite * msg,const string & filename)74 static 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  */
write_proto(const MessageLite & msg,const string & filename)93 static 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 
strip_extension(const string & filename)110 static string strip_extension(const string& filename) {
111     return filename.substr(0, filename.find('.'));
112 }
113 
ends_with(const string & str,const string & ending)114 static 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.
parse_timestamp_ns(const string & id,int64_t * result)123 static 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 
has_section(const ReportFileProto_Report & report,int section)129 static 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 
create_directory(const char * directory)139 status_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 
191 done:
192     free(dir);
193     return err;
194 }
195 
log_envelope(const ReportFileProto & envelope)196 void 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());
212     ALOGD("  data_file_size=%lld", envelope.data_file_size());
213     ALOGD("  completed=%d", envelope.completed());
214     ALOGD("}");
215 }
216 
217 // ================================================================================
218 struct 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 
WorkDirectoryEntry()229 WorkDirectoryEntry::WorkDirectoryEntry()
230         :envelope(),
231          data(),
232          size(0) {
233 }
234 
WorkDirectoryEntry(const WorkDirectoryEntry & that)235 WorkDirectoryEntry::WorkDirectoryEntry(const WorkDirectoryEntry& that)
236         :envelope(that.envelope),
237          data(that.data),
238          size(that.size) {
239 }
240 
~WorkDirectoryEntry()241 WorkDirectoryEntry::~WorkDirectoryEntry() {
242 }
243 
244 // ================================================================================
ReportFile(const sp<WorkDirectory> & workDirectory,int64_t timestampNs,const string & envelopeFileName,const string & dataFileName)245 ReportFile::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 
~ReportFile()258 ReportFile::~ReportFile() {
259     if (mDataFd >= 0) {
260         close(mDataFd);
261     }
262 }
263 
getTimestampNs() const264 int64_t ReportFile::getTimestampNs() const {
265     return mTimestampNs;
266 }
267 
addReport(const IncidentReportArgs & args)268 void 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 
removeReport(const string & pkg,const string & cls)305 void 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 
removeReports(const string & pkg)317 void 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 
setMetadata(const IncidentMetadata & metadata)328 void ReportFile::setMetadata(const IncidentMetadata& metadata) {
329     *mEnvelope.mutable_metadata() = metadata;
330 }
331 
markCompleted()332 void ReportFile::markCompleted() {
333     mEnvelope.set_completed(true);
334 }
335 
markApproved(const string & pkg,const string & cls)336 status_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 
setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy)348 void ReportFile::setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy) {
349     mEnvelope.set_privacy_policy(persistedPrivacyPolicy);
350 }
351 
saveEnvelope()352 status_t ReportFile::saveEnvelope() {
353     return save_envelope_impl(true);
354 }
355 
trySaveEnvelope()356 status_t ReportFile::trySaveEnvelope() {
357     return save_envelope_impl(false);
358 }
359 
loadEnvelope()360 status_t ReportFile::loadEnvelope() {
361     return load_envelope_impl(true);
362 }
363 
tryLoadEnvelope()364 status_t ReportFile::tryLoadEnvelope() {
365     return load_envelope_impl(false);
366 }
367 
getEnvelope()368 const ReportFileProto& ReportFile::getEnvelope() {
369     return mEnvelope;
370 }
371 
startWritingDataFile()372 status_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 
closeDataFile()385 void 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 
startFilteringData(int writeFd,const IncidentReportArgs & args)393 status_t ReportFile::startFilteringData(int writeFd, const IncidentReportArgs& args) {
394     // Open data file.
395     int dataFd = open(mDataFileName.c_str(), O_RDONLY | O_CLOEXEC);
396     if (dataFd < 0) {
397         ALOGW("Error opening incident report '%s' %s", getDataFileName().c_str(), strerror(-errno));
398         close(writeFd);
399         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) {
405         ALOGW("Error running fstat incident report '%s' %s", getDataFileName().c_str(),
406               strerror(-errno));
407         close(writeFd);
408         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
412               " bytes: %s",
413               (int64_t)mEnvelope.data_file_size(), st.st_size, mDataFileName.c_str());
414         ALOGW("Removing incident report");
415         mWorkDirectory->remove(this);
416         close(writeFd);
417         return BAD_VALUE;
418     }
419 
420     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         }
427     }
428 
429     if (mEnvelope.has_metadata()) {
430         write_section(writeFd, FIELD_ID_INCIDENT_METADATA, mEnvelope.metadata());
431     }
432 
433     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     }
438 
439     close(writeFd);
440     return NO_ERROR;
441 }
442 
getDataFileName() const443 string ReportFile::getDataFileName() const {
444     return mDataFileName;
445 }
446 
getEnvelopeFileName() const447 string ReportFile::getEnvelopeFileName() const {
448     return mEnvelopeFileName;
449 }
450 
getDataFileFd()451 int ReportFile::getDataFileFd() {
452     return mDataFd;
453 }
454 
setWriteError(status_t err)455 void ReportFile::setWriteError(status_t err) {
456     mError = err;
457 }
458 
getWriteError()459 status_t ReportFile::getWriteError() {
460     return mError;
461 }
462 
getId()463 string ReportFile::getId() {
464     return to_string(mTimestampNs);
465 }
466 
save_envelope_impl(bool cleanup)467 status_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 
load_envelope_impl(bool cleanup)480 status_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 // ================================================================================
496 //
497 
WorkDirectory()498 WorkDirectory::WorkDirectory()
499         :mDirectory("/data/misc/incidents"),
500          mMaxFileCount(100),
501          mMaxDiskUsageBytes(100 * 1024 * 1024) {  // Incident reports can take up to 100MB on disk.
502                                                  // TODO: Should be a flag.
503     create_directory(mDirectory.c_str());
504 }
505 
WorkDirectory(const string & dir,int maxFileCount,long maxDiskUsageBytes)506 WorkDirectory::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 
createReportFile()513 sp<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 
getReports(vector<sp<ReportFile>> * result,int64_t after)534 status_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 
getReport(const string & pkg,const string & cls,const string & id,IncidentReportArgs * args)557 sp<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 
hasMore(int64_t after)594 bool 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 
commit(const sp<ReportFile> & report,const string & pkg,const string & cls)602 void 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 
commitAll(const string & pkg)616 void 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 
remove(const sp<ReportFile> & report)641 void 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 
make_timestamp_ns_locked()650 int64_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  */
file_exists_locked(int64_t timestampNs)676 bool 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 
make_filename(int64_t timestampNs,const string & extension)682 string 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 
get_directory_contents_locked(map<string,WorkDirectoryEntry> * files,int64_t after)689 off_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 
clean_directory_locked()769 void 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 
delete_files_for_report_if_necessary(const sp<ReportFile> & report)802 void 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 // ================================================================================
get_args_from_report(IncidentReportArgs * out,const ReportFileProto_Report & report)813 void 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 
837