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) 54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#if defined _MSC_VER && _MSC_VER == 1800 64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// TODO(scottmg): Internal errors on VS2013 RC in LTCG. This should be removed 74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// after RTM. http://crbug.com/288948 84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#pragma optimize("", off) 94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#endif 104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/important_file_writer.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h> 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/critical_closure.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h" 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h" 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/task_runner.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h" 26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kDefaultCommitIntervalMs = 10000; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum TempFileFailure { 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_CREATING, 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_OPENING, 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_CLOSING, 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_WRITING, 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAILED_RENAMING, 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TEMP_FILE_FAILURE_MAX 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LogFailure(const FilePath& path, TempFileFailure failure_code, 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& message) { 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code, 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TEMP_FILE_FAILURE_MAX); 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DPLOG(WARNING) << "temp file failure: " << path.value().c_str() 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << " : " << message; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::string& data) { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Write the data to a temp file then rename to avoid data loss if we crash 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // while writing the file. Ensure that the temp file is on the same volume 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // as target file, so it can be moved in one step, and that the temp file 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is securely created. 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FilePath tmp_file_path; 61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!base::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) { 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_CREATING, "could not create temporary file"); 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PlatformFile tmp_file = 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CreatePlatformFile(tmp_file_path, flags, NULL, NULL); 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tmp_file == kInvalidPlatformFileValue) { 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_OPENING, "could not open temporary file"); 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If this happens in the wild something really bad is going on. 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CHECK_LE(data.length(), static_cast<size_t>(kint32max)); 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int bytes_written = WritePlatformFile( 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tmp_file, 0, data.data(), static_cast<int>(data.length())); 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FlushPlatformFile(tmp_file); // Ignore return value. 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ClosePlatformFile(tmp_file)) { 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_CLOSING, "failed to close temporary file"); 827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch base::DeleteFile(tmp_file_path, false); 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (bytes_written < static_cast<int>(data.length())) { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" + 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IntToString(bytes_written)); 897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch base::DeleteFile(tmp_file_path, false); 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!base::ReplaceFile(tmp_file_path, path, NULL)) { 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogFailure(path, FAILED_RENAMING, "could not rename temporary file"); 957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch base::DeleteFile(tmp_file_path, false); 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImportantFileWriter::ImportantFileWriter( 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const FilePath& path, base::SequencedTaskRunner* task_runner) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : path_(path), 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) task_runner_(task_runner), 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) serializer_(NULL), 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) commit_interval_(TimeDelta::FromMilliseconds( 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kDefaultCommitIntervalMs)) { 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(task_runner_.get()); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImportantFileWriter::~ImportantFileWriter() { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We're usually a member variable of some other object, which also tends 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to be our serializer. It may not be safe to call back to the parent object 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // being destructed. 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!HasPendingWrite()); 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ImportantFileWriter::HasPendingWrite() const { 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return timer_.IsRunning(); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ImportantFileWriter::WriteNow(const std::string& data) { 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data.length() > static_cast<size_t>(kint32max)) { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (HasPendingWrite()) 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_.Stop(); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!task_runner_->PostTask( 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FROM_HERE, 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MakeCriticalClosure( 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically), 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) path_, data)))) { 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Posting the task to background message loop is not expected 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to fail, but if it does, avoid losing data and just hit the disk 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // on the current thread. 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) WriteFileAtomically(path_, data); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(serializer); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) serializer_ = serializer; 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!timer_.IsRunning()) { 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_.Start(FROM_HERE, commit_interval_, this, 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &ImportantFileWriter::DoScheduledWrite); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ImportantFileWriter::DoScheduledWrite() { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(serializer_); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string data; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (serializer_->SerializeData(&data)) { 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WriteNow(data); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(WARNING) << "failed to serialize data to be saved in " 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << path_.value().c_str(); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) serializer_ = NULL; 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 174