15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/important_file_writer.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/critical_closure.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/files/file.h" 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/files/file_util.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h" 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/task_runner.h" 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/task_runner_util.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h" 22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kDefaultCommitIntervalMs = 10000; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum TempFileFailure { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_CREATING, 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_OPENING, 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_CLOSING, 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_WRITING, 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_RENAMING, 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TEMP_FILE_FAILURE_MAX 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LogFailure(const FilePath& path, TempFileFailure failure_code, 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& message) { 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code, 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TEMP_FILE_FAILURE_MAX); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DPLOG(WARNING) << "temp file failure: " << path.value().c_str() 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << " : " << message; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::string& data) { 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Write the data to a temp file then rename to avoid data loss if we crash 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // while writing the file. Ensure that the temp file is on the same volume 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // as target file, so it can be moved in one step, and that the temp file 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is securely created. 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FilePath tmp_file_path; 57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!base::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) { 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_CREATING, "could not create temporary file"); 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE); 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!tmp_file.IsValid()) { 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_OPENING, "could not open temporary file"); 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If this happens in the wild something really bad is going on. 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CHECK_LE(data.length(), static_cast<size_t>(kint32max)); 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int bytes_written = tmp_file.Write(0, data.data(), 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<int>(data.length())); 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) tmp_file.Flush(); // Ignore return value. 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) tmp_file.Close(); 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (bytes_written < static_cast<int>(data.length())) { 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" + 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IntToString(bytes_written)); 787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch base::DeleteFile(tmp_file_path, false); 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!base::ReplaceFile(tmp_file_path, path, NULL)) { 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_RENAMING, "could not rename temporary file"); 847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch base::DeleteFile(tmp_file_path, false); 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciImportantFileWriter::ImportantFileWriter( 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const FilePath& path, 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const scoped_refptr<base::SequencedTaskRunner>& task_runner) 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : path_(path), 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) task_runner_(task_runner), 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) serializer_(NULL), 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)), 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_(this) { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(task_runner_.get()); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImportantFileWriter::~ImportantFileWriter() { 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We're usually a member variable of some other object, which also tends 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to be our serializer. It may not be safe to call back to the parent object 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // being destructed. 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!HasPendingWrite()); 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ImportantFileWriter::HasPendingWrite() const { 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return timer_.IsRunning(); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ImportantFileWriter::WriteNow(const std::string& data) { 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data.length() > static_cast<size_t>(kint32max)) { 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (HasPendingWrite()) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_.Stop(); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!PostWriteTask(data)) { 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Posting the task to background message loop is not expected 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to fail, but if it does, avoid losing data and just hit the disk 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // on the current thread. 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) WriteFileAtomically(path_, data); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(serializer); 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) serializer_ = serializer; 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!timer_.IsRunning()) { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_.Start(FROM_HERE, commit_interval_, this, 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &ImportantFileWriter::DoScheduledWrite); 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ImportantFileWriter::DoScheduledWrite() { 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(serializer_); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string data; 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (serializer_->SerializeData(&data)) { 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WriteNow(data); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(WARNING) << "failed to serialize data to be saved in " 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << path_.value().c_str(); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) serializer_ = NULL; 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback( 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const base::Closure& on_next_successful_write) { 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(on_next_successful_write_.is_null()); 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) on_next_successful_write_ = on_next_successful_write; 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool ImportantFileWriter::PostWriteTask(const std::string& data) { 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(gab): This code could always use PostTaskAndReplyWithResult and let 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // suppressing all of those is unrealistic hence we avoid most of them by 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // using PostTask() in the typical scenario below. 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!on_next_successful_write_.is_null()) { 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return base::PostTaskAndReplyWithResult( 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci task_runner_.get(), 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) FROM_HERE, 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MakeCriticalClosure( 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)), 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Bind(&ImportantFileWriter::ForwardSuccessfulWrite, 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_.GetWeakPtr())); 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return task_runner_->PostTask( 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) FROM_HERE, 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MakeCriticalClosure( 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically), 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) path_, data))); 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ImportantFileWriter::ForwardSuccessfulWrite(bool result) { 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(CalledOnValidThread()); 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (result && !on_next_successful_write_.is_null()) { 190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) on_next_successful_write_.Run(); 191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) on_next_successful_write_.Reset(); 192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 196