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(400 * 1024 * 1024) {  // Incident reports can take up to 400MB 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