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