• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_util.h"
14 #include "base/files/file.h"
15 #include "base/files/file_path.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"
23 
24 namespace base {
25 
26 namespace {
27 
28 const int kDefaultCommitIntervalMs = 10000;
29 
30 enum TempFileFailure {
31   FAILED_CREATING,
32   FAILED_OPENING,
33   FAILED_CLOSING,
34   FAILED_WRITING,
35   FAILED_RENAMING,
36   TEMP_FILE_FAILURE_MAX
37 };
38 
LogFailure(const FilePath & path,TempFileFailure failure_code,const std::string & message)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()
44                  << " : " << message;
45 }
46 
47 }  // namespace
48 
49 // static
WriteFileAtomically(const FilePath & path,const std::string & data)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");
59     return false;
60   }
61 
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");
65     return false;
66   }
67 
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.
73   tmp_file.Close();
74 
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);
79     return false;
80   }
81 
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);
85     return false;
86   }
87 
88   return true;
89 }
90 
ImportantFileWriter(const FilePath & path,base::SequencedTaskRunner * task_runner)91 ImportantFileWriter::ImportantFileWriter(const FilePath& path,
92                                          base::SequencedTaskRunner* task_runner)
93     : path_(path),
94       task_runner_(task_runner),
95       serializer_(NULL),
96       commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)),
97       weak_factory_(this) {
98   DCHECK(CalledOnValidThread());
99   DCHECK(task_runner_.get());
100 }
101 
~ImportantFileWriter()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
105   // being destructed.
106   DCHECK(!HasPendingWrite());
107 }
108 
HasPendingWrite() const109 bool ImportantFileWriter::HasPendingWrite() const {
110   DCHECK(CalledOnValidThread());
111   return timer_.IsRunning();
112 }
113 
WriteNow(const std::string & data)114 void ImportantFileWriter::WriteNow(const std::string& data) {
115   DCHECK(CalledOnValidThread());
116   if (data.length() > static_cast<size_t>(kint32max)) {
117     NOTREACHED();
118     return;
119   }
120 
121   if (HasPendingWrite())
122     timer_.Stop();
123 
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.
128     NOTREACHED();
129 
130     WriteFileAtomically(path_, data);
131   }
132 }
133 
ScheduleWrite(DataSerializer * serializer)134 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
135   DCHECK(CalledOnValidThread());
136 
137   DCHECK(serializer);
138   serializer_ = serializer;
139 
140   if (!timer_.IsRunning()) {
141     timer_.Start(FROM_HERE, commit_interval_, this,
142                  &ImportantFileWriter::DoScheduledWrite);
143   }
144 }
145 
DoScheduledWrite()146 void ImportantFileWriter::DoScheduledWrite() {
147   DCHECK(serializer_);
148   std::string data;
149   if (serializer_->SerializeData(&data)) {
150     WriteNow(data);
151   } else {
152     DLOG(WARNING) << "failed to serialize data to be saved in "
153                   << path_.value().c_str();
154   }
155   serializer_ = NULL;
156 }
157 
RegisterOnNextSuccessfulWriteCallback(const base::Closure & on_next_successful_write)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;
162 }
163 
PostWriteTask(const std::string & data)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(
172         task_runner_,
173         FROM_HERE,
174         MakeCriticalClosure(
175             Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)),
176         Bind(&ImportantFileWriter::ForwardSuccessfulWrite,
177              weak_factory_.GetWeakPtr()));
178   }
179   return task_runner_->PostTask(
180       FROM_HERE,
181       MakeCriticalClosure(
182           Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
183                path_, data)));
184 }
185 
ForwardSuccessfulWrite(bool result)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();
191   }
192 }
193 
194 }  // namespace base
195