1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/important_file_writer.h"
#include <stdio.h>
#include <ostream>
#include <string>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/task.h"
#include "base/thread.h"
#include "base/time.h"
using base::TimeDelta;
namespace {
const int kDefaultCommitIntervalMs = 10000;
class WriteToDiskTask : public Task {
public:
WriteToDiskTask(const FilePath& path, const std::string& data)
: path_(path),
data_(data) {
}
virtual void Run() {
// Write the data to a temp file then rename to avoid data loss if we crash
// while writing the file. Ensure that the temp file is on the same volume
// as target file, so it can be moved in one step, and that the temp file
// is securely created.
FilePath tmp_file_path;
FILE* tmp_file = file_util::CreateAndOpenTemporaryFileInDir(
path_.DirName(), &tmp_file_path);
if (!tmp_file) {
LogFailure("could not create temporary file");
return;
}
size_t bytes_written = fwrite(data_.data(), 1, data_.length(), tmp_file);
if (!file_util::CloseFile(tmp_file)) {
file_util::Delete(tmp_file_path, false);
LogFailure("failed to close temporary file");
return;
}
if (bytes_written < data_.length()) {
file_util::Delete(tmp_file_path, false);
LogFailure("error writing, bytes_written=" + UintToString(bytes_written));
return;
}
if (file_util::Move(tmp_file_path, path_)) {
LogSuccess();
return;
}
file_util::Delete(tmp_file_path, false);
LogFailure("could not rename temporary file");
}
private:
void LogSuccess() {
LOG(INFO) << "successfully saved " << path_.value();
}
void LogFailure(const std::string& message) {
LOG(WARNING) << "failed to write " << path_.value()
<< ": " << message;
}
const FilePath path_;
const std::string data_;
DISALLOW_COPY_AND_ASSIGN(WriteToDiskTask);
};
} // namespace
ImportantFileWriter::ImportantFileWriter(const FilePath& path,
const base::Thread* backend_thread)
: path_(path),
backend_thread_(backend_thread),
commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)) {
DCHECK(CalledOnValidThread());
}
ImportantFileWriter::~ImportantFileWriter() {
DCHECK(CalledOnValidThread());
if (timer_.IsRunning()) {
timer_.Stop();
CommitPendingData();
}
}
void ImportantFileWriter::WriteNow(const std::string& data) {
DCHECK(CalledOnValidThread());
if (timer_.IsRunning())
timer_.Stop();
Task* task = new WriteToDiskTask(path_, data);
if (backend_thread_) {
backend_thread_->message_loop()->PostTask(FROM_HERE, task);
} else {
task->Run();
delete task;
}
}
void ImportantFileWriter::ScheduleWrite(const std::string& data) {
DCHECK(CalledOnValidThread());
data_ = data;
if (!timer_.IsRunning()) {
timer_.Start(commit_interval_, this,
&ImportantFileWriter::CommitPendingData);
}
}
void ImportantFileWriter::CommitPendingData() {
WriteNow(data_);
}
|