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.
5 #include "base/files/important_file_writer.h"
11 #include "base/bind.h"
12 #include "base/critical_closure.h"
13 #include "base/files/file.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.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"
28 const int kDefaultCommitIntervalMs
= 10000;
30 enum TempFileFailure
{
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()
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");
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");
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.
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);
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);
91 ImportantFileWriter::ImportantFileWriter(const FilePath
& path
,
92 base::SequencedTaskRunner
* task_runner
)
94 task_runner_(task_runner
),
96 commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs
)),
98 DCHECK(CalledOnValidThread());
99 DCHECK(task_runner_
.get());
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
106 DCHECK(!HasPendingWrite());
109 bool ImportantFileWriter::HasPendingWrite() const {
110 DCHECK(CalledOnValidThread());
111 return timer_
.IsRunning();
114 void ImportantFileWriter::WriteNow(const std::string
& data
) {
115 DCHECK(CalledOnValidThread());
116 if (data
.length() > static_cast<size_t>(kint32max
)) {
121 if (HasPendingWrite())
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.
130 WriteFileAtomically(path_
, data
);
134 void ImportantFileWriter::ScheduleWrite(DataSerializer
* serializer
) {
135 DCHECK(CalledOnValidThread());
138 serializer_
= serializer
;
140 if (!timer_
.IsRunning()) {
141 timer_
.Start(FROM_HERE
, commit_interval_
, this,
142 &ImportantFileWriter::DoScheduledWrite
);
146 void ImportantFileWriter::DoScheduledWrite() {
149 if (serializer_
->SerializeData(&data
)) {
152 DLOG(WARNING
) << "failed to serialize data to be saved in "
153 << path_
.value().c_str();
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
;
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(
175 Bind(&ImportantFileWriter::WriteFileAtomically
, path_
, data
)),
176 Bind(&ImportantFileWriter::ForwardSuccessfulWrite
,
177 weak_factory_
.GetWeakPtr()));
179 return task_runner_
->PostTask(
182 Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically
),
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();