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 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 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 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 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 109 bool ImportantFileWriter::HasPendingWrite() const { 110 DCHECK(CalledOnValidThread()); 111 return timer_.IsRunning(); 112 } 113 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 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 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 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 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 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