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 #if defined _MSC_VER && _MSC_VER == 1800
6 // TODO(scottmg): Internal errors on VS2013 RC in LTCG. This should be removed
7 // after RTM. http://crbug.com/288948
8 #pragma optimize("", off)
11 #include "base/files/important_file_writer.h"
17 #include "base/bind.h"
18 #include "base/critical_closure.h"
19 #include "base/file_util.h"
20 #include "base/files/file.h"
21 #include "base/files/file_path.h"
22 #include "base/logging.h"
23 #include "base/metrics/histogram.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/task_runner.h"
26 #include "base/task_runner_util.h"
27 #include "base/threading/thread.h"
28 #include "base/time/time.h"
34 const int kDefaultCommitIntervalMs
= 10000;
36 enum TempFileFailure
{
45 void LogFailure(const FilePath
& path
, TempFileFailure failure_code
,
46 const std::string
& message
) {
47 UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code
,
48 TEMP_FILE_FAILURE_MAX
);
49 DPLOG(WARNING
) << "temp file failure: " << path
.value().c_str()
56 bool ImportantFileWriter::WriteFileAtomically(const FilePath
& path
,
57 const std::string
& data
) {
58 // Write the data to a temp file then rename to avoid data loss if we crash
59 // while writing the file. Ensure that the temp file is on the same volume
60 // as target file, so it can be moved in one step, and that the temp file
61 // is securely created.
62 FilePath tmp_file_path
;
63 if (!base::CreateTemporaryFileInDir(path
.DirName(), &tmp_file_path
)) {
64 LogFailure(path
, FAILED_CREATING
, "could not create temporary file");
68 File
tmp_file(tmp_file_path
, File::FLAG_OPEN
| File::FLAG_WRITE
);
69 if (!tmp_file
.IsValid()) {
70 LogFailure(path
, FAILED_OPENING
, "could not open temporary file");
74 // If this happens in the wild something really bad is going on.
75 CHECK_LE(data
.length(), static_cast<size_t>(kint32max
));
76 int bytes_written
= tmp_file
.Write(0, data
.data(),
77 static_cast<int>(data
.length()));
78 tmp_file
.Flush(); // Ignore return value.
81 if (bytes_written
< static_cast<int>(data
.length())) {
82 LogFailure(path
, FAILED_WRITING
, "error writing, bytes_written=" +
83 IntToString(bytes_written
));
84 base::DeleteFile(tmp_file_path
, false);
88 if (!base::ReplaceFile(tmp_file_path
, path
, NULL
)) {
89 LogFailure(path
, FAILED_RENAMING
, "could not rename temporary file");
90 base::DeleteFile(tmp_file_path
, false);
97 ImportantFileWriter::ImportantFileWriter(const FilePath
& path
,
98 base::SequencedTaskRunner
* task_runner
)
100 task_runner_(task_runner
),
102 commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs
)),
103 weak_factory_(this) {
104 DCHECK(CalledOnValidThread());
105 DCHECK(task_runner_
.get());
108 ImportantFileWriter::~ImportantFileWriter() {
109 // We're usually a member variable of some other object, which also tends
110 // to be our serializer. It may not be safe to call back to the parent object
112 DCHECK(!HasPendingWrite());
115 bool ImportantFileWriter::HasPendingWrite() const {
116 DCHECK(CalledOnValidThread());
117 return timer_
.IsRunning();
120 void ImportantFileWriter::WriteNow(const std::string
& data
) {
121 DCHECK(CalledOnValidThread());
122 if (data
.length() > static_cast<size_t>(kint32max
)) {
127 if (HasPendingWrite())
130 if (!PostWriteTask(data
)) {
131 // Posting the task to background message loop is not expected
132 // to fail, but if it does, avoid losing data and just hit the disk
133 // on the current thread.
136 WriteFileAtomically(path_
, data
);
140 void ImportantFileWriter::ScheduleWrite(DataSerializer
* serializer
) {
141 DCHECK(CalledOnValidThread());
144 serializer_
= serializer
;
146 if (!timer_
.IsRunning()) {
147 timer_
.Start(FROM_HERE
, commit_interval_
, this,
148 &ImportantFileWriter::DoScheduledWrite
);
152 void ImportantFileWriter::DoScheduledWrite() {
155 if (serializer_
->SerializeData(&data
)) {
158 DLOG(WARNING
) << "failed to serialize data to be saved in "
159 << path_
.value().c_str();
164 void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback(
165 const base::Closure
& on_next_successful_write
) {
166 DCHECK(on_next_successful_write_
.is_null());
167 on_next_successful_write_
= on_next_successful_write
;
170 bool ImportantFileWriter::PostWriteTask(const std::string
& data
) {
171 // TODO(gab): This code could always use PostTaskAndReplyWithResult and let
172 // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but
173 // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and
174 // suppressing all of those is unrealistic hence we avoid most of them by
175 // using PostTask() in the typical scenario below.
176 if (!on_next_successful_write_
.is_null()) {
177 return base::PostTaskAndReplyWithResult(
181 Bind(&ImportantFileWriter::WriteFileAtomically
, path_
, data
)),
182 Bind(&ImportantFileWriter::ForwardSuccessfulWrite
,
183 weak_factory_
.GetWeakPtr()));
185 return task_runner_
->PostTask(
188 Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically
),
192 void ImportantFileWriter::ForwardSuccessfulWrite(bool result
) {
193 DCHECK(CalledOnValidThread());
194 if (result
&& !on_next_successful_write_
.is_null()) {
195 on_next_successful_write_
.Run();
196 on_next_successful_write_
.Reset();