• 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 "chrome/common/important_file_writer.h"
6 
7 #include <stdio.h>
8 
9 #include <string>
10 
11 #include "base/file_path.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/message_loop_proxy.h"
15 #include "base/string_number_conversions.h"
16 #include "base/task.h"
17 #include "base/threading/thread.h"
18 #include "base/time.h"
19 
20 using base::TimeDelta;
21 
22 namespace {
23 
24 const int kDefaultCommitIntervalMs = 10000;
25 
26 class WriteToDiskTask : public Task {
27  public:
WriteToDiskTask(const FilePath & path,const std::string & data)28   WriteToDiskTask(const FilePath& path, const std::string& data)
29       : path_(path),
30         data_(data) {
31   }
32 
Run()33   virtual void Run() {
34     // Write the data to a temp file then rename to avoid data loss if we crash
35     // while writing the file. Ensure that the temp file is on the same volume
36     // as target file, so it can be moved in one step, and that the temp file
37     // is securely created.
38     FilePath tmp_file_path;
39     FILE* tmp_file = file_util::CreateAndOpenTemporaryFileInDir(
40         path_.DirName(), &tmp_file_path);
41     if (!tmp_file) {
42       LogFailure("could not create temporary file");
43       return;
44     }
45 
46     size_t bytes_written = fwrite(data_.data(), 1, data_.length(), tmp_file);
47     if (!file_util::CloseFile(tmp_file)) {
48       LogFailure("failed to close temporary file");
49       file_util::Delete(tmp_file_path, false);
50       return;
51     }
52     if (bytes_written < data_.length()) {
53       LogFailure("error writing, bytes_written=" +
54                  base::Uint64ToString(bytes_written));
55       file_util::Delete(tmp_file_path, false);
56       return;
57     }
58 
59     if (!file_util::ReplaceFile(tmp_file_path, path_)) {
60       LogFailure("could not rename temporary file");
61       file_util::Delete(tmp_file_path, false);
62       return;
63     }
64   }
65 
66  private:
LogFailure(const std::string & message)67   void LogFailure(const std::string& message) {
68     PLOG(WARNING) << "failed to write " << path_.value()
69                   << ": " << message;
70   }
71 
72   const FilePath path_;
73   const std::string data_;
74 
75   DISALLOW_COPY_AND_ASSIGN(WriteToDiskTask);
76 };
77 
78 }  // namespace
79 
ImportantFileWriter(const FilePath & path,base::MessageLoopProxy * file_message_loop_proxy)80 ImportantFileWriter::ImportantFileWriter(
81     const FilePath& path, base::MessageLoopProxy* file_message_loop_proxy)
82         : path_(path),
83           file_message_loop_proxy_(file_message_loop_proxy),
84           serializer_(NULL),
85           commit_interval_(TimeDelta::FromMilliseconds(
86               kDefaultCommitIntervalMs)) {
87   DCHECK(CalledOnValidThread());
88   DCHECK(file_message_loop_proxy_.get());
89 }
90 
~ImportantFileWriter()91 ImportantFileWriter::~ImportantFileWriter() {
92   // We're usually a member variable of some other object, which also tends
93   // to be our serializer. It may not be safe to call back to the parent object
94   // being destructed.
95   DCHECK(!HasPendingWrite());
96 }
97 
HasPendingWrite() const98 bool ImportantFileWriter::HasPendingWrite() const {
99   DCHECK(CalledOnValidThread());
100   return timer_.IsRunning();
101 }
102 
WriteNow(const std::string & data)103 void ImportantFileWriter::WriteNow(const std::string& data) {
104   DCHECK(CalledOnValidThread());
105 
106   if (HasPendingWrite())
107     timer_.Stop();
108 
109   if (!file_message_loop_proxy_->PostTask(
110       FROM_HERE, new WriteToDiskTask(path_, data))) {
111     // Posting the task to background message loop is not expected
112     // to fail, but if it does, avoid losing data and just hit the disk
113     // on the current thread.
114     NOTREACHED();
115 
116     WriteToDiskTask write_task(path_, data);
117     write_task.Run();
118   }
119 }
120 
ScheduleWrite(DataSerializer * serializer)121 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
122   DCHECK(CalledOnValidThread());
123 
124   DCHECK(serializer);
125   serializer_ = serializer;
126 
127   if (!MessageLoop::current()) {
128     // Happens in unit tests.
129     DoScheduledWrite();
130     return;
131   }
132 
133   if (!timer_.IsRunning()) {
134     timer_.Start(commit_interval_, this,
135                  &ImportantFileWriter::DoScheduledWrite);
136   }
137 }
138 
DoScheduledWrite()139 void ImportantFileWriter::DoScheduledWrite() {
140   DCHECK(serializer_);
141   std::string data;
142   if (serializer_->SerializeData(&data)) {
143     WriteNow(data);
144   } else {
145     LOG(WARNING) << "failed to serialize data to be saved in "
146                  << path_.value();
147   }
148   serializer_ = NULL;
149 }
150