Home | History | Annotate | Download | only in files
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/files/important_file_writer.h"
      6 
      7 #include <stddef.h>
      8 #include <stdint.h>
      9 #include <stdio.h>
     10 #include <string>
     11 #include <utility>
     12 
     13 #include "base/bind.h"
     14 #include "base/callback_helpers.h"
     15 #include "base/critical_closure.h"
     16 #include "base/debug/alias.h"
     17 #include "base/files/file.h"
     18 #include "base/files/file_path.h"
     19 #include "base/files/file_util.h"
     20 #include "base/logging.h"
     21 #include "base/macros.h"
     22 #include "base/metrics/histogram_functions.h"
     23 #include "base/metrics/histogram_macros.h"
     24 #include "base/numerics/safe_conversions.h"
     25 #include "base/strings/string_number_conversions.h"
     26 #include "base/strings/string_util.h"
     27 #include "base/task_runner.h"
     28 #include "base/task_runner_util.h"
     29 #include "base/threading/thread.h"
     30 #include "base/time/time.h"
     31 #include "build/build_config.h"
     32 
     33 namespace base {
     34 
     35 namespace {
     36 
     37 constexpr auto kDefaultCommitInterval = TimeDelta::FromSeconds(10);
     38 
     39 // This enum is used to define the buckets for an enumerated UMA histogram.
     40 // Hence,
     41 //   (a) existing enumerated constants should never be deleted or reordered, and
     42 //   (b) new constants should only be appended at the end of the enumeration.
     43 enum TempFileFailure {
     44   FAILED_CREATING,
     45   FAILED_OPENING,
     46   FAILED_CLOSING,  // Unused.
     47   FAILED_WRITING,
     48   FAILED_RENAMING,
     49   FAILED_FLUSHING,
     50   TEMP_FILE_FAILURE_MAX
     51 };
     52 
     53 // Helper function to write samples to a histogram with a dynamically assigned
     54 // histogram name.  Works with different error code types convertible to int
     55 // which is the actual argument type of UmaHistogramExactLinear.
     56 template <typename SampleType>
     57 void UmaHistogramExactLinearWithSuffix(const char* histogram_name,
     58                                        StringPiece histogram_suffix,
     59                                        SampleType add_sample,
     60                                        SampleType max_sample) {
     61   static_assert(std::is_convertible<SampleType, int>::value,
     62                 "SampleType should be convertible to int");
     63   DCHECK(histogram_name);
     64   std::string histogram_full_name(histogram_name);
     65   if (!histogram_suffix.empty()) {
     66     histogram_full_name.append(".");
     67     histogram_full_name.append(histogram_suffix.data(),
     68                                histogram_suffix.length());
     69   }
     70   UmaHistogramExactLinear(histogram_full_name, static_cast<int>(add_sample),
     71                           static_cast<int>(max_sample));
     72 }
     73 
     74 // Helper function to write samples to a histogram with a dynamically assigned
     75 // histogram name.  Works with short timings from 1 ms up to 10 seconds (50
     76 // buckets) which is the actual argument type of UmaHistogramTimes.
     77 void UmaHistogramTimesWithSuffix(const char* histogram_name,
     78                                  StringPiece histogram_suffix,
     79                                  TimeDelta sample) {
     80   DCHECK(histogram_name);
     81   std::string histogram_full_name(histogram_name);
     82   if (!histogram_suffix.empty()) {
     83     histogram_full_name.append(".");
     84     histogram_full_name.append(histogram_suffix.data(),
     85                                histogram_suffix.length());
     86   }
     87   UmaHistogramTimes(histogram_full_name, sample);
     88 }
     89 
     90 void LogFailure(const FilePath& path,
     91                 StringPiece histogram_suffix,
     92                 TempFileFailure failure_code,
     93                 StringPiece message) {
     94   UmaHistogramExactLinearWithSuffix("ImportantFile.TempFileFailures",
     95                                     histogram_suffix, failure_code,
     96                                     TEMP_FILE_FAILURE_MAX);
     97   DPLOG(WARNING) << "temp file failure: " << path.value() << " : " << message;
     98 }
     99 
    100 // Helper function to call WriteFileAtomically() with a
    101 // std::unique_ptr<std::string>.
    102 void WriteScopedStringToFileAtomically(
    103     const FilePath& path,
    104     std::unique_ptr<std::string> data,
    105     Closure before_write_callback,
    106     Callback<void(bool success)> after_write_callback,
    107     const std::string& histogram_suffix) {
    108   if (!before_write_callback.is_null())
    109     before_write_callback.Run();
    110 
    111   TimeTicks start_time = TimeTicks::Now();
    112   bool result =
    113       ImportantFileWriter::WriteFileAtomically(path, *data, histogram_suffix);
    114   if (result) {
    115     UmaHistogramTimesWithSuffix("ImportantFile.TimeToWrite", histogram_suffix,
    116                                 TimeTicks::Now() - start_time);
    117   }
    118 
    119   if (!after_write_callback.is_null())
    120     after_write_callback.Run(result);
    121 }
    122 
    123 void DeleteTmpFile(const FilePath& tmp_file_path,
    124                    StringPiece histogram_suffix) {
    125   if (!DeleteFile(tmp_file_path, false)) {
    126     UmaHistogramExactLinearWithSuffix(
    127         "ImportantFile.FileDeleteError", histogram_suffix,
    128         -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
    129   }
    130 }
    131 
    132 }  // namespace
    133 
    134 // static
    135 bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
    136                                               StringPiece data,
    137                                               StringPiece histogram_suffix) {
    138 #if defined(OS_CHROMEOS)
    139   // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly,
    140   // and this function seems to be one of the slowest shutdown steps.
    141   // Include some info to the report for investigation. crbug.com/418627
    142   // TODO(hashimoto): Remove this.
    143   struct {
    144     size_t data_size;
    145     char path[128];
    146   } file_info;
    147   file_info.data_size = data.size();
    148   strlcpy(file_info.path, path.value().c_str(), arraysize(file_info.path));
    149   debug::Alias(&file_info);
    150 #endif
    151 
    152   // Write the data to a temp file then rename to avoid data loss if we crash
    153   // while writing the file. Ensure that the temp file is on the same volume
    154   // as target file, so it can be moved in one step, and that the temp file
    155   // is securely created.
    156   FilePath tmp_file_path;
    157   if (!CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
    158     UmaHistogramExactLinearWithSuffix(
    159         "ImportantFile.FileCreateError", histogram_suffix,
    160         -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
    161     LogFailure(path, histogram_suffix, FAILED_CREATING,
    162                "could not create temporary file");
    163     return false;
    164   }
    165 
    166   File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
    167   if (!tmp_file.IsValid()) {
    168     UmaHistogramExactLinearWithSuffix(
    169         "ImportantFile.FileOpenError", histogram_suffix,
    170         -tmp_file.error_details(), -base::File::FILE_ERROR_MAX);
    171     LogFailure(path, histogram_suffix, FAILED_OPENING,
    172                "could not open temporary file");
    173     DeleteFile(tmp_file_path, false);
    174     return false;
    175   }
    176 
    177   // If this fails in the wild, something really bad is going on.
    178   const int data_length = checked_cast<int32_t>(data.length());
    179   int bytes_written = tmp_file.Write(0, data.data(), data_length);
    180   if (bytes_written < data_length) {
    181     UmaHistogramExactLinearWithSuffix(
    182         "ImportantFile.FileWriteError", histogram_suffix,
    183         -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
    184   }
    185   bool flush_success = tmp_file.Flush();
    186   tmp_file.Close();
    187 
    188   if (bytes_written < data_length) {
    189     LogFailure(path, histogram_suffix, FAILED_WRITING,
    190                "error writing, bytes_written=" + IntToString(bytes_written));
    191     DeleteTmpFile(tmp_file_path, histogram_suffix);
    192     return false;
    193   }
    194 
    195   if (!flush_success) {
    196     LogFailure(path, histogram_suffix, FAILED_FLUSHING, "error flushing");
    197     DeleteTmpFile(tmp_file_path, histogram_suffix);
    198     return false;
    199   }
    200 
    201   base::File::Error replace_file_error = base::File::FILE_OK;
    202   if (!ReplaceFile(tmp_file_path, path, &replace_file_error)) {
    203     UmaHistogramExactLinearWithSuffix("ImportantFile.FileRenameError",
    204                                       histogram_suffix, -replace_file_error,
    205                                       -base::File::FILE_ERROR_MAX);
    206     LogFailure(path, histogram_suffix, FAILED_RENAMING,
    207                "could not rename temporary file");
    208     DeleteTmpFile(tmp_file_path, histogram_suffix);
    209     return false;
    210   }
    211 
    212   return true;
    213 }
    214 
    215 ImportantFileWriter::ImportantFileWriter(
    216     const FilePath& path,
    217     scoped_refptr<SequencedTaskRunner> task_runner,
    218     const char* histogram_suffix)
    219     : ImportantFileWriter(path,
    220                           std::move(task_runner),
    221                           kDefaultCommitInterval,
    222                           histogram_suffix) {}
    223 
    224 ImportantFileWriter::ImportantFileWriter(
    225     const FilePath& path,
    226     scoped_refptr<SequencedTaskRunner> task_runner,
    227     TimeDelta interval,
    228     const char* histogram_suffix)
    229     : path_(path),
    230       task_runner_(std::move(task_runner)),
    231       serializer_(nullptr),
    232       commit_interval_(interval),
    233       histogram_suffix_(histogram_suffix ? histogram_suffix : ""),
    234       weak_factory_(this) {
    235   DCHECK(task_runner_);
    236 }
    237 
    238 ImportantFileWriter::~ImportantFileWriter() {
    239   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    240   // We're usually a member variable of some other object, which also tends
    241   // to be our serializer. It may not be safe to call back to the parent object
    242   // being destructed.
    243   DCHECK(!HasPendingWrite());
    244 }
    245 
    246 bool ImportantFileWriter::HasPendingWrite() const {
    247   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    248   return timer().IsRunning();
    249 }
    250 
    251 void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) {
    252   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    253   if (!IsValueInRangeForNumericType<int32_t>(data->length())) {
    254     NOTREACHED();
    255     return;
    256   }
    257 
    258   Closure task = AdaptCallbackForRepeating(
    259       BindOnce(&WriteScopedStringToFileAtomically, path_, std::move(data),
    260                std::move(before_next_write_callback_),
    261                std::move(after_next_write_callback_), histogram_suffix_));
    262 
    263   if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) {
    264     // Posting the task to background message loop is not expected
    265     // to fail, but if it does, avoid losing data and just hit the disk
    266     // on the current thread.
    267     NOTREACHED();
    268 
    269     task.Run();
    270   }
    271   ClearPendingWrite();
    272 }
    273 
    274 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
    275   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    276 
    277   DCHECK(serializer);
    278   serializer_ = serializer;
    279 
    280   if (!timer().IsRunning()) {
    281     timer().Start(
    282         FROM_HERE, commit_interval_,
    283         Bind(&ImportantFileWriter::DoScheduledWrite, Unretained(this)));
    284   }
    285 }
    286 
    287 void ImportantFileWriter::DoScheduledWrite() {
    288   DCHECK(serializer_);
    289   std::unique_ptr<std::string> data(new std::string);
    290   if (serializer_->SerializeData(data.get())) {
    291     WriteNow(std::move(data));
    292   } else {
    293     DLOG(WARNING) << "failed to serialize data to be saved in "
    294                   << path_.value();
    295   }
    296   ClearPendingWrite();
    297 }
    298 
    299 void ImportantFileWriter::RegisterOnNextWriteCallbacks(
    300     const Closure& before_next_write_callback,
    301     const Callback<void(bool success)>& after_next_write_callback) {
    302   before_next_write_callback_ = before_next_write_callback;
    303   after_next_write_callback_ = after_next_write_callback;
    304 }
    305 
    306 void ImportantFileWriter::ClearPendingWrite() {
    307   timer().Stop();
    308   serializer_ = nullptr;
    309 }
    310 
    311 void ImportantFileWriter::SetTimerForTesting(Timer* timer_override) {
    312   timer_override_ = timer_override;
    313 }
    314 
    315 }  // namespace base
    316