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: 28 WriteToDiskTask(const FilePath& path, const std::string& data) 29 : path_(path), 30 data_(data) { 31 } 32 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: 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 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 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 98 bool ImportantFileWriter::HasPendingWrite() const { 99 DCHECK(CalledOnValidThread()); 100 return timer_.IsRunning(); 101 } 102 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 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 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