important_file_writer.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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/thread.h" 18#include "base/time.h" 19 20using base::TimeDelta; 21 22namespace { 23 24const int kDefaultCommitIntervalMs = 10000; 25 26class 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 80ImportantFileWriter::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 91ImportantFileWriter::~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 98bool ImportantFileWriter::HasPendingWrite() const { 99 DCHECK(CalledOnValidThread()); 100 return timer_.IsRunning(); 101} 102 103void ImportantFileWriter::WriteNow(const std::string& data) { 104 DCHECK(CalledOnValidThread()); 105 106 if (HasPendingWrite()) 107 timer_.Stop(); 108 109 // TODO(sanjeevr): Add a DCHECK for the return value of PostTask. 110 // (Some tests fail if we add the DCHECK and they need to be fixed first). 111 file_message_loop_proxy_->PostTask(FROM_HERE, 112 new WriteToDiskTask(path_, data)); 113} 114 115void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { 116 DCHECK(CalledOnValidThread()); 117 118 DCHECK(serializer); 119 serializer_ = serializer; 120 121 if (!MessageLoop::current()) { 122 // Happens in unit tests. 123 DoScheduledWrite(); 124 return; 125 } 126 127 if (!timer_.IsRunning()) { 128 timer_.Start(commit_interval_, this, 129 &ImportantFileWriter::DoScheduledWrite); 130 } 131} 132 133void ImportantFileWriter::DoScheduledWrite() { 134 DCHECK(serializer_); 135 std::string data; 136 if (serializer_->SerializeData(&data)) { 137 WriteNow(data); 138 } else { 139 LOG(WARNING) << "failed to serialize data to be saved in " 140 << path_.value(); 141 } 142 serializer_ = NULL; 143} 144