important_file_writer.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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/thread.h"
18#include "base/time.h"
19
20using base::TimeDelta;
21
22namespace {
23
24const int kDefaultCommitIntervalMs = 10000;
25
26class 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
80ImportantFileWriter::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
91ImportantFileWriter::~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
98bool ImportantFileWriter::HasPendingWrite() const {
99  DCHECK(CalledOnValidThread());
100  return timer_.IsRunning();
101}
102
103void ImportantFileWriter::WriteNow(const std::string& data) {
104  DCHECK(CalledOnValidThread());
105
106  if (HasPendingWrite())
107    timer_.Stop();
108
109  // TODO(sanjeevr): Add a DCHECK for the return value of PostTask.
110  // (Some tests fail if we add the DCHECK and they need to be fixed first).
111  file_message_loop_proxy_->PostTask(FROM_HERE,
112                                     new WriteToDiskTask(path_, data));
113}
114
115void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
116  DCHECK(CalledOnValidThread());
117
118  DCHECK(serializer);
119  serializer_ = serializer;
120
121  if (!MessageLoop::current()) {
122    // Happens in unit tests.
123    DoScheduledWrite();
124    return;
125  }
126
127  if (!timer_.IsRunning()) {
128    timer_.Start(commit_interval_, this,
129                 &ImportantFileWriter::DoScheduledWrite);
130  }
131}
132
133void ImportantFileWriter::DoScheduledWrite() {
134  DCHECK(serializer_);
135  std::string data;
136  if (serializer_->SerializeData(&data)) {
137    WriteNow(data);
138  } else {
139    LOG(WARNING) << "failed to serialize data to be saved in "
140                 << path_.value();
141  }
142  serializer_ = NULL;
143}
144