• 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 "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, &timestampNs)) {
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, &timestampNs)) {
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