Home | History | Annotate | Download | only in common
      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 "chrome/common/important_file_writer.h"
      6 
      7 #include <stdio.h>
      8 
      9 #include <string>
     10 
     11 #include "base/file_path.h"
     12 #include "base/file_util.h"
     13 #include "base/logging.h"
     14 #include "base/message_loop_proxy.h"
     15 #include "base/string_number_conversions.h"
     16 #include "base/task.h"
     17 #include "base/threading/thread.h"
     18 #include "base/time.h"
     19 
     20 using base::TimeDelta;
     21 
     22 namespace {
     23 
     24 const int kDefaultCommitIntervalMs = 10000;
     25 
     26 class WriteToDiskTask : public Task {
     27  public:
     28   WriteToDiskTask(const FilePath& path, const std::string& data)
     29       : path_(path),
     30         data_(data) {
     31   }
     32 
     33   virtual void Run() {
     34     // Write the data to a temp file then rename to avoid data loss if we crash
     35     // while writing the file. Ensure that the temp file is on the same volume
     36     // as target file, so it can be moved in one step, and that the temp file
     37     // is securely created.
     38     FilePath tmp_file_path;
     39     FILE* tmp_file = file_util::CreateAndOpenTemporaryFileInDir(
     40         path_.DirName(), &tmp_file_path);
     41     if (!tmp_file) {
     42       LogFailure("could not create temporary file");
     43       return;
     44     }
     45 
     46     size_t bytes_written = fwrite(data_.data(), 1, data_.length(), tmp_file);
     47     if (!file_util::CloseFile(tmp_file)) {
     48       LogFailure("failed to close temporary file");
     49       file_util::Delete(tmp_file_path, false);
     50       return;
     51     }
     52     if (bytes_written < data_.length()) {
     53       LogFailure("error writing, bytes_written=" +
     54                  base::Uint64ToString(bytes_written));
     55       file_util::Delete(tmp_file_path, false);
     56       return;
     57     }
     58 
     59     if (!file_util::ReplaceFile(tmp_file_path, path_)) {
     60       LogFailure("could not rename temporary file");
     61       file_util::Delete(tmp_file_path, false);
     62       return;
     63     }
     64   }
     65 
     66  private:
     67   void LogFailure(const std::string& message) {
     68     PLOG(WARNING) << "failed to write " << path_.value()
     69                   << ": " << message;
     70   }
     71 
     72   const FilePath path_;
     73   const std::string data_;
     74 
     75   DISALLOW_COPY_AND_ASSIGN(WriteToDiskTask);
     76 };
     77 
     78 }  // namespace
     79 
     80 ImportantFileWriter::ImportantFileWriter(
     81     const FilePath& path, base::MessageLoopProxy* file_message_loop_proxy)
     82         : path_(path),
     83           file_message_loop_proxy_(file_message_loop_proxy),
     84           serializer_(NULL),
     85           commit_interval_(TimeDelta::FromMilliseconds(
     86               kDefaultCommitIntervalMs)) {
     87   DCHECK(CalledOnValidThread());
     88   DCHECK(file_message_loop_proxy_.get());
     89 }
     90 
     91 ImportantFileWriter::~ImportantFileWriter() {
     92   // We're usually a member variable of some other object, which also tends
     93   // to be our serializer. It may not be safe to call back to the parent object
     94   // being destructed.
     95   DCHECK(!HasPendingWrite());
     96 }
     97 
     98 bool ImportantFileWriter::HasPendingWrite() const {
     99   DCHECK(CalledOnValidThread());
    100   return timer_.IsRunning();
    101 }
    102 
    103 void ImportantFileWriter::WriteNow(const std::string& data) {
    104   DCHECK(CalledOnValidThread());
    105 
    106   if (HasPendingWrite())
    107     timer_.Stop();
    108 
    109   if (!file_message_loop_proxy_->PostTask(
    110       FROM_HERE, new WriteToDiskTask(path_, data))) {
    111     // Posting the task to background message loop is not expected
    112     // to fail, but if it does, avoid losing data and just hit the disk
    113     // on the current thread.
    114     NOTREACHED();
    115 
    116     WriteToDiskTask write_task(path_, data);
    117     write_task.Run();
    118   }
    119 }
    120 
    121 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
    122   DCHECK(CalledOnValidThread());
    123 
    124   DCHECK(serializer);
    125   serializer_ = serializer;
    126 
    127   if (!MessageLoop::current()) {
    128     // Happens in unit tests.
    129     DoScheduledWrite();
    130     return;
    131   }
    132 
    133   if (!timer_.IsRunning()) {
    134     timer_.Start(commit_interval_, this,
    135                  &ImportantFileWriter::DoScheduledWrite);
    136   }
    137 }
    138 
    139 void ImportantFileWriter::DoScheduledWrite() {
    140   DCHECK(serializer_);
    141   std::string data;
    142   if (serializer_->SerializeData(&data)) {
    143     WriteNow(data);
    144   } else {
    145     LOG(WARNING) << "failed to serialize data to be saved in "
    146                  << path_.value();
    147   }
    148   serializer_ = NULL;
    149 }
    150