Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / base / files / important_file_writer.cc
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/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"
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   FAILED_FLUSHING,
37   TEMP_FILE_FAILURE_MAX
38 };
39
40 void LogFailure(const FilePath& path, TempFileFailure failure_code,
41                 const std::string& message) {
42   UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code,
43                             TEMP_FILE_FAILURE_MAX);
44   DPLOG(WARNING) << "temp file failure: " << path.value().c_str()
45                  << " : " << message;
46 }
47
48 }  // namespace
49
50 // static
51 bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
52                                               const std::string& data) {
53   // Write the data to a temp file then rename to avoid data loss if we crash
54   // while writing the file. Ensure that the temp file is on the same volume
55   // as target file, so it can be moved in one step, and that the temp file
56   // is securely created.
57   FilePath tmp_file_path;
58   if (!base::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
59     LogFailure(path, FAILED_CREATING, "could not create temporary file");
60     return false;
61   }
62
63   File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
64   if (!tmp_file.IsValid()) {
65     LogFailure(path, FAILED_OPENING, "could not open temporary file");
66     return false;
67   }
68
69   // If this happens in the wild something really bad is going on.
70   CHECK_LE(data.length(), static_cast<size_t>(kint32max));
71   int bytes_written = tmp_file.Write(0, data.data(),
72                                      static_cast<int>(data.length()));
73   bool flush_success = tmp_file.Flush();
74   tmp_file.Close();
75
76   if (bytes_written < static_cast<int>(data.length())) {
77     LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
78                IntToString(bytes_written));
79     base::DeleteFile(tmp_file_path, false);
80     return false;
81   }
82
83   if (!flush_success) {
84     LogFailure(path, FAILED_FLUSHING, "error flushing");
85     base::DeleteFile(tmp_file_path, false);
86     return false;
87   }
88
89   if (!base::ReplaceFile(tmp_file_path, path, NULL)) {
90     LogFailure(path, FAILED_RENAMING, "could not rename temporary file");
91     base::DeleteFile(tmp_file_path, false);
92     return false;
93   }
94
95   return true;
96 }
97
98 ImportantFileWriter::ImportantFileWriter(
99     const FilePath& path,
100     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
101     : path_(path),
102       task_runner_(task_runner),
103       serializer_(NULL),
104       commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)),
105       weak_factory_(this) {
106   DCHECK(CalledOnValidThread());
107   DCHECK(task_runner_.get());
108 }
109
110 ImportantFileWriter::~ImportantFileWriter() {
111   // We're usually a member variable of some other object, which also tends
112   // to be our serializer. It may not be safe to call back to the parent object
113   // being destructed.
114   DCHECK(!HasPendingWrite());
115 }
116
117 bool ImportantFileWriter::HasPendingWrite() const {
118   DCHECK(CalledOnValidThread());
119   return timer_.IsRunning();
120 }
121
122 void ImportantFileWriter::WriteNow(const std::string& data) {
123   DCHECK(CalledOnValidThread());
124   if (data.length() > static_cast<size_t>(kint32max)) {
125     NOTREACHED();
126     return;
127   }
128
129   if (HasPendingWrite())
130     timer_.Stop();
131
132   if (!PostWriteTask(data)) {
133     // Posting the task to background message loop is not expected
134     // to fail, but if it does, avoid losing data and just hit the disk
135     // on the current thread.
136     NOTREACHED();
137
138     WriteFileAtomically(path_, data);
139   }
140 }
141
142 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
143   DCHECK(CalledOnValidThread());
144
145   DCHECK(serializer);
146   serializer_ = serializer;
147
148   if (!timer_.IsRunning()) {
149     timer_.Start(FROM_HERE, commit_interval_, this,
150                  &ImportantFileWriter::DoScheduledWrite);
151   }
152 }
153
154 void ImportantFileWriter::DoScheduledWrite() {
155   DCHECK(serializer_);
156   std::string data;
157   if (serializer_->SerializeData(&data)) {
158     WriteNow(data);
159   } else {
160     DLOG(WARNING) << "failed to serialize data to be saved in "
161                   << path_.value().c_str();
162   }
163   serializer_ = NULL;
164 }
165
166 void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback(
167     const base::Closure& on_next_successful_write) {
168   DCHECK(on_next_successful_write_.is_null());
169   on_next_successful_write_ = on_next_successful_write;
170 }
171
172 bool ImportantFileWriter::PostWriteTask(const std::string& data) {
173   // TODO(gab): This code could always use PostTaskAndReplyWithResult and let
174   // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but
175   // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and
176   // suppressing all of those is unrealistic hence we avoid most of them by
177   // using PostTask() in the typical scenario below.
178   if (!on_next_successful_write_.is_null()) {
179     return base::PostTaskAndReplyWithResult(
180         task_runner_.get(),
181         FROM_HERE,
182         MakeCriticalClosure(
183             Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)),
184         Bind(&ImportantFileWriter::ForwardSuccessfulWrite,
185              weak_factory_.GetWeakPtr()));
186   }
187   return task_runner_->PostTask(
188       FROM_HERE,
189       MakeCriticalClosure(
190           Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
191                path_, data)));
192 }
193
194 void ImportantFileWriter::ForwardSuccessfulWrite(bool result) {
195   DCHECK(CalledOnValidThread());
196   if (result && !on_next_successful_write_.is_null()) {
197     on_next_successful_write_.Run();
198     on_next_successful_write_.Reset();
199   }
200 }
201
202 }  // namespace base