1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// Use of this source code is governed by a BSD-style license that can be 306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// found in the LICENSE file. 406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "chrome/common/important_file_writer.h" 606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include <stdio.h> 806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include <string> 1006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 1106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/file_path.h" 1206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/file_util.h" 1306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/logging.h" 1406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/message_loop_proxy.h" 153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h" 1606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/task.h" 173f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread.h" 1806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/time.h" 1906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 2006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochusing base::TimeDelta; 2106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 2206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochnamespace { 2306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 2406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochconst int kDefaultCommitIntervalMs = 10000; 2506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 2606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochclass WriteToDiskTask : public Task { 2706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch public: 2806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch WriteToDiskTask(const FilePath& path, const std::string& data) 2906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch : path_(path), 3006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch data_(data) { 3106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 3206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 3306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch virtual void Run() { 3406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // Write the data to a temp file then rename to avoid data loss if we crash 3506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // while writing the file. Ensure that the temp file is on the same volume 3606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // as target file, so it can be moved in one step, and that the temp file 3706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // is securely created. 3806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch FilePath tmp_file_path; 3906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch FILE* tmp_file = file_util::CreateAndOpenTemporaryFileInDir( 4006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch path_.DirName(), &tmp_file_path); 4106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (!tmp_file) { 4206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch LogFailure("could not create temporary file"); 4306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch return; 4406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 4506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 4606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch size_t bytes_written = fwrite(data_.data(), 1, data_.length(), tmp_file); 4706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (!file_util::CloseFile(tmp_file)) { 4806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch LogFailure("failed to close temporary file"); 49513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch file_util::Delete(tmp_file_path, false); 5006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch return; 5106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 5206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (bytes_written < data_.length()) { 533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LogFailure("error writing, bytes_written=" + 543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick base::Uint64ToString(bytes_written)); 55513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch file_util::Delete(tmp_file_path, false); 5606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch return; 5706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 5806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!file_util::ReplaceFile(tmp_file_path, path_)) { 603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LogFailure("could not rename temporary file"); 61513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch file_util::Delete(tmp_file_path, false); 6206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch return; 6306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 6406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 6506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 6606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch private: 6706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch void LogFailure(const std::string& message) { 68513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch PLOG(WARNING) << "failed to write " << path_.value() 69513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch << ": " << message; 7006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 7106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 7206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch const FilePath path_; 7306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch const std::string data_; 7406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 7506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DISALLOW_COPY_AND_ASSIGN(WriteToDiskTask); 7606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}; 7706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 7806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} // namespace 7906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 8006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen MurdochImportantFileWriter::ImportantFileWriter( 8106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch const FilePath& path, base::MessageLoopProxy* file_message_loop_proxy) 8206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch : path_(path), 8306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch file_message_loop_proxy_(file_message_loop_proxy), 8406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch serializer_(NULL), 8506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch commit_interval_(TimeDelta::FromMilliseconds( 8606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch kDefaultCommitIntervalMs)) { 8706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(CalledOnValidThread()); 8806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(file_message_loop_proxy_.get()); 8906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} 9006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 9106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen MurdochImportantFileWriter::~ImportantFileWriter() { 9206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // We're usually a member variable of some other object, which also tends 9306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // to be our serializer. It may not be safe to call back to the parent object 9406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // being destructed. 9506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(!HasPendingWrite()); 9606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} 9706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 9806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochbool ImportantFileWriter::HasPendingWrite() const { 9906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(CalledOnValidThread()); 10006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch return timer_.IsRunning(); 10106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} 10206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 10306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochvoid ImportantFileWriter::WriteNow(const std::string& data) { 10406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(CalledOnValidThread()); 10506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 10606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (HasPendingWrite()) 10706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch timer_.Stop(); 10806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!file_message_loop_proxy_->PostTask( 110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen FROM_HERE, new WriteToDiskTask(path_, data))) { 111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Posting the task to background message loop is not expected 112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // to fail, but if it does, avoid losing data and just hit the disk 113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // on the current thread. 114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen NOTREACHED(); 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen WriteToDiskTask write_task(path_, data); 117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen write_task.Run(); 118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 11906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} 12006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 12106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochvoid ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { 12206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(CalledOnValidThread()); 12306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 12406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(serializer); 12506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch serializer_ = serializer; 12606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 12706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (!MessageLoop::current()) { 12806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch // Happens in unit tests. 12906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DoScheduledWrite(); 13006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch return; 13106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 13206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 13306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (!timer_.IsRunning()) { 13406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch timer_.Start(commit_interval_, this, 13506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch &ImportantFileWriter::DoScheduledWrite); 13606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 13706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} 13806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch 13906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochvoid ImportantFileWriter::DoScheduledWrite() { 14006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch DCHECK(serializer_); 14106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch std::string data; 14206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch if (serializer_->SerializeData(&data)) { 14306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch WriteNow(data); 14406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } else { 14506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch LOG(WARNING) << "failed to serialize data to be saved in " 14606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch << path_.value(); 14706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch } 14806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch serializer_ = NULL; 14906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch} 150