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, ×tampNs)) { 599 return nullptr; 600 } 601 602 // Make the ReportFile object, and then see if it's valid and for pkg and cls. 603 sp<ReportFile> result = new ReportFile(this, timestampNs, 604 make_filename(timestampNs, EXTENSION_ENVELOPE), 605 make_filename(timestampNs, EXTENSION_DATA)); 606 607 err = result->tryLoadEnvelope(); 608 if (err != NO_ERROR) { 609 ALOGW("Can't open envelope file for report %s/%s %s", pkg.c_str(), cls.c_str(), id.c_str()); 610 return nullptr; 611 } 612 613 const ReportFileProto& envelope = result->getEnvelope(); 614 const size_t reportCount = envelope.report_size(); 615 for (int i = 0; i < reportCount; i++) { 616 const ReportFileProto_Report& report = envelope.report(i); 617 if (report.pkg() == pkg && report.cls() == cls) { 618 if (args != nullptr) { 619 get_args_from_report(args, report); 620 } 621 return result; 622 } 623 624 } 625 626 return nullptr; 627 } 628 hasMore(int64_t after)629 bool WorkDirectory::hasMore(int64_t after) { 630 unique_lock<mutex> lock(mLock); 631 632 map<string,WorkDirectoryEntry> files; 633 get_directory_contents_locked(&files, after); 634 return files.size() > 0; 635 } 636 commit(const sp<ReportFile> & report,const string & pkg,const string & cls)637 void WorkDirectory::commit(const sp<ReportFile>& report, const string& pkg, const string& cls) { 638 status_t err; 639 ALOGI("Committing report %s for %s/%s", report->getId().c_str(), pkg.c_str(), cls.c_str()); 640 641 unique_lock<mutex> lock(mLock); 642 643 // Load the envelope here inside the lock. 644 err = report->loadEnvelope(); 645 646 report->removeReport(pkg, cls); 647 648 delete_files_for_report_if_necessary(report); 649 } 650 commitAll(const string & pkg)651 void WorkDirectory::commitAll(const string& pkg) { 652 status_t err; 653 ALOGI("All reports for %s", pkg.c_str()); 654 655 unique_lock<mutex> lock(mLock); 656 657 map<string,WorkDirectoryEntry> files; 658 get_directory_contents_locked(&files, 0); 659 660 for (map<string,WorkDirectoryEntry>::iterator it = files.begin(); 661 it != files.end(); it++) { 662 sp<ReportFile> reportFile = new ReportFile(this, it->second.timestampNs, 663 it->second.envelope, it->second.data); 664 665 err = reportFile->loadEnvelope(); 666 if (err != NO_ERROR) { 667 continue; 668 } 669 670 reportFile->removeReports(pkg); 671 672 delete_files_for_report_if_necessary(reportFile); 673 } 674 } 675 remove(const sp<ReportFile> & report)676 void WorkDirectory::remove(const sp<ReportFile>& report) { 677 unique_lock<mutex> lock(mLock); 678 // Set this to false to leave files around for debugging. 679 if (DO_UNLINK) { 680 unlink(report->getDataFileName().c_str()); 681 unlink(report->getEnvelopeFileName().c_str()); 682 } 683 } 684 make_timestamp_ns_locked()685 int64_t WorkDirectory::make_timestamp_ns_locked() { 686 // Guarantee that we don't have duplicate timestamps. 687 // This is a little bit lame, but since reports are created on the 688 // same thread and are kinda slow we'll seldomly actually hit the 689 // condition. The bigger risk is the clock getting reset and causing 690 // a collision. In that case, we'll just make incident reporting a 691 // little bit slower. Nobody will notice if we just loop until we 692 // have a unique file name. 693 int64_t timestampNs = 0; 694 do { 695 struct timespec spec; 696 if (timestampNs > 0) { 697 spec.tv_sec = 0; 698 spec.tv_nsec = 1; 699 nanosleep(&spec, nullptr); 700 } 701 clock_gettime(CLOCK_REALTIME, &spec); 702 timestampNs = int64_t(spec.tv_sec) * 1000 + spec.tv_nsec; 703 } while (file_exists_locked(timestampNs)); 704 return (timestampNs >= 0)? timestampNs : -timestampNs; 705 } 706 707 /** 708 * It is required to hold the lock here so in case someone else adds it 709 * our result is still correct for the caller. 710 */ file_exists_locked(int64_t timestampNs)711 bool WorkDirectory::file_exists_locked(int64_t timestampNs) { 712 const string filename = make_filename(timestampNs, EXTENSION_ENVELOPE); 713 struct stat st; 714 return stat(filename.c_str(), &st) == 0; 715 } 716 make_filename(int64_t timestampNs,const string & extension)717 string WorkDirectory::make_filename(int64_t timestampNs, const string& extension) { 718 // Zero pad the timestamp so it can also be alpha sorted. 719 stringstream result; 720 result << mDirectory << '/' << setfill('0') << setw(20) << timestampNs << extension; 721 return result.str(); 722 } 723 get_directory_contents_locked(map<string,WorkDirectoryEntry> * files,int64_t after)724 off_t WorkDirectory::get_directory_contents_locked(map<string,WorkDirectoryEntry>* files, 725 int64_t after) { 726 DIR* dir; 727 struct dirent* entry; 728 729 if ((dir = opendir(mDirectory.c_str())) == NULL) { 730 ALOGE("Couldn't open incident directory: %s", mDirectory.c_str()); 731 return -1; 732 } 733 734 string dirbase(mDirectory); 735 if (mDirectory[dirbase.size() - 1] != '/') dirbase += "/"; 736 737 off_t totalSize = 0; 738 739 // Enumerate, count and add up size 740 while ((entry = readdir(dir)) != NULL) { 741 if (entry->d_name[0] == '.') { 742 continue; 743 } 744 string entryname = entry->d_name; // local to this dir 745 string filename = dirbase + entryname; // fully qualified 746 747 bool isEnvelope = ends_with(entryname, EXTENSION_ENVELOPE); 748 bool isData = ends_with(entryname, EXTENSION_DATA); 749 750 // If the file isn't one of our files, just ignore it. Otherwise, 751 // sum up the sizes. 752 if (isEnvelope || isData) { 753 string timestamp = strip_extension(entryname); 754 755 int64_t timestampNs; 756 if (!parse_timestamp_ns(timestamp, ×tampNs)) { 757 continue; 758 } 759 760 if (after == 0 || timestampNs > after) { 761 struct stat st; 762 if (stat(filename.c_str(), &st) != 0) { 763 ALOGE("Unable to stat file %s", filename.c_str()); 764 continue; 765 } 766 if (!S_ISREG(st.st_mode)) { 767 continue; 768 } 769 770 WorkDirectoryEntry& entry = (*files)[timestamp]; 771 if (isEnvelope) { 772 entry.envelope = filename; 773 } else if (isData) { 774 entry.data = filename; 775 } 776 entry.timestampNs = timestampNs; 777 entry.size += st.st_size; 778 totalSize += st.st_size; 779 } 780 } 781 } 782 783 closedir(dir); 784 785 // Now check if there are any data files that don't have envelope files. 786 // If there are, then just go ahead and delete them now. Don't wait for 787 // a cleaning. 788 789 if (DO_UNLINK) { 790 map<string,WorkDirectoryEntry>::iterator it = files->begin(); 791 while (it != files->end()) { 792 if (it->second.envelope.length() == 0) { 793 unlink(it->second.data.c_str()); 794 it = files->erase(it); 795 } else { 796 it++; 797 } 798 } 799 } 800 801 return totalSize; 802 } 803 clean_directory_locked()804 void WorkDirectory::clean_directory_locked() { 805 DIR* dir; 806 struct dirent* entry; 807 struct stat st; 808 809 // Map of filename without extension to the entries about it. Conveniently, 810 // this also keeps the list sorted by filename, which is a timestamp. 811 map<string,WorkDirectoryEntry> files; 812 off_t totalSize = get_directory_contents_locked(&files, 0); 813 if (totalSize < 0) { 814 return; 815 } 816 int totalCount = files.size(); 817 818 // Count or size is less than max, then we're done. 819 if (totalSize < mMaxDiskUsageBytes && totalCount < mMaxFileCount) { 820 return; 821 } 822 823 // Remove files until we're under our limits. 824 if (DO_UNLINK) { 825 for (map<string, WorkDirectoryEntry>::const_iterator it = files.begin(); 826 it != files.end() && (totalSize >= mMaxDiskUsageBytes 827 || totalCount >= mMaxFileCount); 828 it++) { 829 unlink(it->second.envelope.c_str()); 830 unlink(it->second.data.c_str()); 831 totalSize -= it->second.size; 832 totalCount--; 833 } 834 } 835 } 836 delete_files_for_report_if_necessary(const sp<ReportFile> & report)837 void WorkDirectory::delete_files_for_report_if_necessary(const sp<ReportFile>& report) { 838 if (report->getEnvelope().report_size() == 0) { 839 ALOGI("Report %s is finished. Deleting from storage.", report->getId().c_str()); 840 if (DO_UNLINK) { 841 unlink(report->getDataFileName().c_str()); 842 unlink(report->getEnvelopeFileName().c_str()); 843 } 844 } 845 } 846 847 // ================================================================================ get_args_from_report(IncidentReportArgs * out,const ReportFileProto_Report & report)848 void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report& report) { 849 out->setPrivacyPolicy(report.privacy_policy()); 850 out->setAll(report.all_sections()); 851 out->setReceiverPkg(report.pkg()); 852 out->setReceiverCls(report.cls()); 853 out->setGzip(report.gzip()); 854 855 const int sectionCount = report.section_size(); 856 for (int i = 0; i < sectionCount; i++) { 857 out->addSection(report.section(i)); 858 } 859 860 const int headerCount = report.header_size(); 861 for (int i = 0; i < headerCount; i++) { 862 const string& header = report.header(i); 863 vector<uint8_t> vec(header.begin(), header.end()); 864 out->addHeader(vec); 865 } 866 } 867 868 869 } // namespace incidentd 870 } // namespace os 871 } // namespace android 872 873