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 <stdio.h>
      8 
      9 #include <string>
     10 
     11 #include "base/bind.h"
     12 #include "base/critical_closure.h"
     13 #include "base/file_util.h"
     14 #include "base/files/file.h"
     15 #include "base/files/file_path.h"
     16 #include "base/logging.h"
     17 #include "base/metrics/histogram.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/task_runner.h"
     20 #include "base/task_runner_util.h"
     21 #include "base/threading/thread.h"
     22 #include "base/time/time.h"
     23 
     24 namespace base {
     25 
     26 namespace {
     27 
     28 const int kDefaultCommitIntervalMs = 10000;
     29 
     30 enum TempFileFailure {
     31   FAILED_CREATING,
     32   FAILED_OPENING,
     33   FAILED_CLOSING,
     34   FAILED_WRITING,
     35   FAILED_RENAMING,
     36   TEMP_FILE_FAILURE_MAX
     37 };
     38 
     39 void LogFailure(const FilePath& path, TempFileFailure failure_code,
     40                 const std::string& message) {
     41   UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code,
     42                             TEMP_FILE_FAILURE_MAX);
     43   DPLOG(WARNING) << "temp file failure: " << path.value().c_str()
     44                  << " : " << message;
     45 }
     46 
     47 }  // namespace
     48 
     49 // static
     50 bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
     51                                               const std::string& data) {
     52   // Write the data to a temp file then rename to avoid data loss if we crash
     53   // while writing the file. Ensure that the temp file is on the same volume
     54   // as target file, so it can be moved in one step, and that the temp file
     55   // is securely created.
     56   FilePath tmp_file_path;
     57   if (!base::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
     58     LogFailure(path, FAILED_CREATING, "could not create temporary file");
     59     return false;
     60   }
     61 
     62   File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
     63   if (!tmp_file.IsValid()) {
     64     LogFailure(path, FAILED_OPENING, "could not open temporary file");
     65     return false;
     66   }
     67 
     68   // If this happens in the wild something really bad is going on.
     69   CHECK_LE(data.length(), static_cast<size_t>(kint32max));
     70   int bytes_written = tmp_file.Write(0, data.data(),
     71                                      static_cast<int>(data.length()));
     72   tmp_file.Flush();  // Ignore return value.
     73   tmp_file.Close();
     74 
     75   if (bytes_written < static_cast<int>(data.length())) {
     76     LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
     77                IntToString(bytes_written));
     78     base::DeleteFile(tmp_file_path, false);
     79     return false;
     80   }
     81 
     82   if (!base::ReplaceFile(tmp_file_path, path, NULL)) {
     83     LogFailure(path, FAILED_RENAMING, "could not rename temporary file");
     84     base::DeleteFile(tmp_file_path, false);
     85     return false;
     86   }
     87 
     88   return true;
     89 }
     90 
     91 ImportantFileWriter::ImportantFileWriter(const FilePath& path,
     92                                          base::SequencedTaskRunner* task_runner)
     93     : path_(path),
     94       task_runner_(task_runner),
     95       serializer_(NULL),
     96       commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)),
     97       weak_factory_(this) {
     98   DCHECK(CalledOnValidThread());
     99   DCHECK(task_runner_.get());
    100 }
    101 
    102 ImportantFileWriter::~ImportantFileWriter() {
    103   // We're usually a member variable of some other object, which also tends
    104   // to be our serializer. It may not be safe to call back to the parent object
    105   // being destructed.
    106   DCHECK(!HasPendingWrite());
    107 }
    108 
    109 bool ImportantFileWriter::HasPendingWrite() const {
    110   DCHECK(CalledOnValidThread());
    111   return timer_.IsRunning();
    112 }
    113 
    114 void ImportantFileWriter::WriteNow(const std::string& data) {
    115   DCHECK(CalledOnValidThread());
    116   if (data.length() > static_cast<size_t>(kint32max)) {
    117     NOTREACHED();
    118     return;
    119   }
    120 
    121   if (HasPendingWrite())
    122     timer_.Stop();
    123 
    124   if (!PostWriteTask(data)) {
    125     // Posting the task to background message loop is not expected
    126     // to fail, but if it does, avoid losing data and just hit the disk
    127     // on the current thread.
    128     NOTREACHED();
    129 
    130     WriteFileAtomically(path_, data);
    131   }
    132 }
    133 
    134 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
    135   DCHECK(CalledOnValidThread());
    136 
    137   DCHECK(serializer);
    138   serializer_ = serializer;
    139 
    140   if (!timer_.IsRunning()) {
    141     timer_.Start(FROM_HERE, commit_interval_, this,
    142                  &ImportantFileWriter::DoScheduledWrite);
    143   }
    144 }
    145 
    146 void ImportantFileWriter::DoScheduledWrite() {
    147   DCHECK(serializer_);
    148   std::string data;
    149   if (serializer_->SerializeData(&data)) {
    150     WriteNow(data);
    151   } else {
    152     DLOG(WARNING) << "failed to serialize data to be saved in "
    153                   << path_.value().c_str();
    154   }
    155   serializer_ = NULL;
    156 }
    157 
    158 void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback(
    159     const base::Closure& on_next_successful_write) {
    160   DCHECK(on_next_successful_write_.is_null());
    161   on_next_successful_write_ = on_next_successful_write;
    162 }
    163 
    164 bool ImportantFileWriter::PostWriteTask(const std::string& data) {
    165   // TODO(gab): This code could always use PostTaskAndReplyWithResult and let
    166   // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but
    167   // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and
    168   // suppressing all of those is unrealistic hence we avoid most of them by
    169   // using PostTask() in the typical scenario below.
    170   if (!on_next_successful_write_.is_null()) {
    171     return base::PostTaskAndReplyWithResult(
    172         task_runner_,
    173         FROM_HERE,
    174         MakeCriticalClosure(
    175             Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)),
    176         Bind(&ImportantFileWriter::ForwardSuccessfulWrite,
    177              weak_factory_.GetWeakPtr()));
    178   }
    179   return task_runner_->PostTask(
    180       FROM_HERE,
    181       MakeCriticalClosure(
    182           Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
    183                path_, data)));
    184 }
    185 
    186 void ImportantFileWriter::ForwardSuccessfulWrite(bool result) {
    187   DCHECK(CalledOnValidThread());
    188   if (result && !on_next_successful_write_.is_null()) {
    189     on_next_successful_write_.Run();
    190     on_next_successful_write_.Reset();
    191   }
    192 }
    193 
    194 }  // namespace base
    195