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/critical_closure.h"
     15 #include "base/debug/alias.h"
     16 #include "base/files/file.h"
     17 #include "base/files/file_path.h"
     18 #include "base/files/file_util.h"
     19 #include "base/logging.h"
     20 #include "base/macros.h"
     21 #include "base/metrics/histogram_macros.h"
     22 #include "base/numerics/safe_conversions.h"
     23 #include "base/strings/string_number_conversions.h"
     24 #include "base/strings/string_util.h"
     25 #include "base/task_runner.h"
     26 #include "base/task_runner_util.h"
     27 #include "base/threading/thread.h"
     28 #include "base/time/time.h"
     29 #include "build/build_config.h"
     30 
     31 namespace base {
     32 
     33 namespace {
     34 
     35 const int kDefaultCommitIntervalMs = 10000;
     36 
     37 // This enum is used to define the buckets for an enumerated UMA histogram.
     38 // Hence,
     39 //   (a) existing enumerated constants should never be deleted or reordered, and
     40 //   (b) new constants should only be appended at the end of the enumeration.
     41 enum TempFileFailure {
     42   FAILED_CREATING,
     43   FAILED_OPENING,
     44   FAILED_CLOSING,  // Unused.
     45   FAILED_WRITING,
     46   FAILED_RENAMING,
     47   FAILED_FLUSHING,
     48   TEMP_FILE_FAILURE_MAX
     49 };
     50 
     51 void LogFailure(const FilePath& path, TempFileFailure failure_code,
     52                 StringPiece message) {
     53   UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code,
     54                             TEMP_FILE_FAILURE_MAX);
     55   DPLOG(WARNING) << "temp file failure: " << path.value() << " : " << message;
     56 }
     57 
     58 // Helper function to call WriteFileAtomically() with a
     59 // std::unique_ptr<std::string>.
     60 void WriteScopedStringToFileAtomically(
     61     const FilePath& path,
     62     std::unique_ptr<std::string> data,
     63     Closure before_write_callback,
     64     Callback<void(bool success)> after_write_callback) {
     65   if (!before_write_callback.is_null())
     66     before_write_callback.Run();
     67 
     68   bool result = ImportantFileWriter::WriteFileAtomically(path, *data);
     69 
     70   if (!after_write_callback.is_null())
     71     after_write_callback.Run(result);
     72 }
     73 
     74 }  // namespace
     75 
     76 // static
     77 bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
     78                                               StringPiece data) {
     79 #if defined(OS_CHROMEOS)
     80   // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly,
     81   // and this function seems to be one of the slowest shutdown steps.
     82   // Include some info to the report for investigation. crbug.com/418627
     83   // TODO(hashimoto): Remove this.
     84   struct {
     85     size_t data_size;
     86     char path[128];
     87   } file_info;
     88   file_info.data_size = data.size();
     89   strlcpy(file_info.path, path.value().c_str(), arraysize(file_info.path));
     90   debug::Alias(&file_info);
     91 #endif
     92 
     93   // Write the data to a temp file then rename to avoid data loss if we crash
     94   // while writing the file. Ensure that the temp file is on the same volume
     95   // as target file, so it can be moved in one step, and that the temp file
     96   // is securely created.
     97   FilePath tmp_file_path;
     98   if (!CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
     99     LogFailure(path, FAILED_CREATING, "could not create temporary file");
    100     return false;
    101   }
    102 
    103   File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
    104   if (!tmp_file.IsValid()) {
    105     LogFailure(path, FAILED_OPENING, "could not open temporary file");
    106     DeleteFile(tmp_file_path, false);
    107     return false;
    108   }
    109 
    110   // If this fails in the wild, something really bad is going on.
    111   const int data_length = checked_cast<int32_t>(data.length());
    112   int bytes_written = tmp_file.Write(0, data.data(), data_length);
    113   bool flush_success = tmp_file.Flush();
    114   tmp_file.Close();
    115 
    116   if (bytes_written < data_length) {
    117     LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
    118                IntToString(bytes_written));
    119     DeleteFile(tmp_file_path, false);
    120     return false;
    121   }
    122 
    123   if (!flush_success) {
    124     LogFailure(path, FAILED_FLUSHING, "error flushing");
    125     DeleteFile(tmp_file_path, false);
    126     return false;
    127   }
    128 
    129   if (!ReplaceFile(tmp_file_path, path, nullptr)) {
    130     LogFailure(path, FAILED_RENAMING, "could not rename temporary file");
    131     DeleteFile(tmp_file_path, false);
    132     return false;
    133   }
    134 
    135   return true;
    136 }
    137 
    138 ImportantFileWriter::ImportantFileWriter(
    139     const FilePath& path,
    140     scoped_refptr<SequencedTaskRunner> task_runner)
    141     : ImportantFileWriter(
    142           path,
    143           std::move(task_runner),
    144           TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)) {}
    145 
    146 ImportantFileWriter::ImportantFileWriter(
    147     const FilePath& path,
    148     scoped_refptr<SequencedTaskRunner> task_runner,
    149     TimeDelta interval)
    150     : path_(path),
    151       task_runner_(std::move(task_runner)),
    152       serializer_(nullptr),
    153       commit_interval_(interval),
    154       weak_factory_(this) {
    155   DCHECK(CalledOnValidThread());
    156   DCHECK(task_runner_);
    157 }
    158 
    159 ImportantFileWriter::~ImportantFileWriter() {
    160   // We're usually a member variable of some other object, which also tends
    161   // to be our serializer. It may not be safe to call back to the parent object
    162   // being destructed.
    163   DCHECK(!HasPendingWrite());
    164 }
    165 
    166 bool ImportantFileWriter::HasPendingWrite() const {
    167   DCHECK(CalledOnValidThread());
    168   return timer_.IsRunning();
    169 }
    170 
    171 void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) {
    172   DCHECK(CalledOnValidThread());
    173   if (!IsValueInRangeForNumericType<int32_t>(data->length())) {
    174     NOTREACHED();
    175     return;
    176   }
    177 
    178   if (HasPendingWrite())
    179     timer_.Stop();
    180 
    181   Closure task = Bind(&WriteScopedStringToFileAtomically, path_, Passed(&data),
    182                       Passed(&before_next_write_callback_),
    183                       Passed(&after_next_write_callback_));
    184 
    185   if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) {
    186     // Posting the task to background message loop is not expected
    187     // to fail, but if it does, avoid losing data and just hit the disk
    188     // on the current thread.
    189     NOTREACHED();
    190 
    191     task.Run();
    192   }
    193 }
    194 
    195 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
    196   DCHECK(CalledOnValidThread());
    197 
    198   DCHECK(serializer);
    199   serializer_ = serializer;
    200 
    201   if (!timer_.IsRunning()) {
    202     timer_.Start(FROM_HERE, commit_interval_, this,
    203                  &ImportantFileWriter::DoScheduledWrite);
    204   }
    205 }
    206 
    207 void ImportantFileWriter::DoScheduledWrite() {
    208   DCHECK(serializer_);
    209   std::unique_ptr<std::string> data(new std::string);
    210   if (serializer_->SerializeData(data.get())) {
    211     WriteNow(std::move(data));
    212   } else {
    213     DLOG(WARNING) << "failed to serialize data to be saved in "
    214                   << path_.value();
    215   }
    216   serializer_ = nullptr;
    217 }
    218 
    219 void ImportantFileWriter::RegisterOnNextWriteCallbacks(
    220     const Closure& before_next_write_callback,
    221     const Callback<void(bool success)>& after_next_write_callback) {
    222   before_next_write_callback_ = before_next_write_callback;
    223   after_next_write_callback_ = after_next_write_callback;
    224 }
    225 
    226 }  // namespace base
    227