important_file_writer.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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_path.h"
14#include "base/file_util.h"
15#include "base/logging.h"
16#include "base/task_runner.h"
17#include "base/metrics/histogram.h"
18#include "base/string_number_conversions.h"
19#include "base/threading/thread.h"
20#include "base/time.h"
21
22namespace base {
23
24namespace {
25
26const int kDefaultCommitIntervalMs = 10000;
27
28enum TempFileFailure {
29  FAILED_CREATING,
30  FAILED_OPENING,
31  FAILED_CLOSING,
32  FAILED_WRITING,
33  FAILED_RENAMING,
34  TEMP_FILE_FAILURE_MAX
35};
36
37void LogFailure(const FilePath& path, TempFileFailure failure_code,
38                const std::string& message) {
39  UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code,
40                            TEMP_FILE_FAILURE_MAX);
41  DPLOG(WARNING) << "temp file failure: " << path.value().c_str()
42                 << " : " << message;
43}
44
45void WriteToDiskTask(const FilePath& path, const std::string& data) {
46  // Write the data to a temp file then rename to avoid data loss if we crash
47  // while writing the file. Ensure that the temp file is on the same volume
48  // as target file, so it can be moved in one step, and that the temp file
49  // is securely created.
50  FilePath tmp_file_path;
51  if (!file_util::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
52    LogFailure(path, FAILED_CREATING, "could not create temporary file");
53    return;
54  }
55
56  int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE;
57  PlatformFile tmp_file =
58      CreatePlatformFile(tmp_file_path, flags, NULL, NULL);
59  if (tmp_file == kInvalidPlatformFileValue) {
60    LogFailure(path, FAILED_OPENING, "could not open temporary file");
61    return;
62  }
63
64  // If this happens in the wild something really bad is going on.
65  CHECK_LE(data.length(), static_cast<size_t>(kint32max));
66  int bytes_written = WritePlatformFile(
67      tmp_file, 0, data.data(), static_cast<int>(data.length()));
68  FlushPlatformFile(tmp_file);  // Ignore return value.
69
70  if (!ClosePlatformFile(tmp_file)) {
71    LogFailure(path, FAILED_CLOSING, "failed to close temporary file");
72    file_util::Delete(tmp_file_path, false);
73    return;
74  }
75
76  if (bytes_written < static_cast<int>(data.length())) {
77    LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
78               IntToString(bytes_written));
79    file_util::Delete(tmp_file_path, false);
80    return;
81  }
82
83  if (!file_util::ReplaceFile(tmp_file_path, path)) {
84    LogFailure(path, FAILED_RENAMING, "could not rename temporary file");
85    file_util::Delete(tmp_file_path, false);
86    return;
87  }
88}
89
90}  // namespace
91
92ImportantFileWriter::ImportantFileWriter(
93    const FilePath& path, base::SequencedTaskRunner* task_runner)
94        : path_(path),
95          task_runner_(task_runner),
96          serializer_(NULL),
97          commit_interval_(TimeDelta::FromMilliseconds(
98              kDefaultCommitIntervalMs)) {
99  DCHECK(CalledOnValidThread());
100  DCHECK(task_runner_.get());
101}
102
103ImportantFileWriter::~ImportantFileWriter() {
104  // We're usually a member variable of some other object, which also tends
105  // to be our serializer. It may not be safe to call back to the parent object
106  // being destructed.
107  DCHECK(!HasPendingWrite());
108}
109
110bool ImportantFileWriter::HasPendingWrite() const {
111  DCHECK(CalledOnValidThread());
112  return timer_.IsRunning();
113}
114
115void ImportantFileWriter::WriteNow(const std::string& data) {
116  DCHECK(CalledOnValidThread());
117  if (data.length() > static_cast<size_t>(kint32max)) {
118    NOTREACHED();
119    return;
120  }
121
122  if (HasPendingWrite())
123    timer_.Stop();
124
125  if (!task_runner_->PostTask(FROM_HERE,
126          MakeCriticalClosure(Bind(&WriteToDiskTask, path_, data)))) {
127    // Posting the task to background message loop is not expected
128    // to fail, but if it does, avoid losing data and just hit the disk
129    // on the current thread.
130    NOTREACHED();
131
132    WriteToDiskTask(path_, data);
133  }
134}
135
136void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
137  DCHECK(CalledOnValidThread());
138
139  DCHECK(serializer);
140  serializer_ = serializer;
141
142  if (!timer_.IsRunning()) {
143    timer_.Start(FROM_HERE, commit_interval_, this,
144                 &ImportantFileWriter::DoScheduledWrite);
145  }
146}
147
148void ImportantFileWriter::DoScheduledWrite() {
149  DCHECK(serializer_);
150  std::string data;
151  if (serializer_->SerializeData(&data)) {
152    WriteNow(data);
153  } else {
154    DLOG(WARNING) << "failed to serialize data to be saved in "
155                  << path_.value().c_str();
156  }
157  serializer_ = NULL;
158}
159
160}  // namespace base
161