1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webkit/browser/fileapi/sandbox_origin_database.h"
10 #include "base/file_util.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/format_macros.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "third_party/leveldatabase/src/include/leveldb/db.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
21 #include "webkit/common/fileapi/file_system_util.h"
25 const base::FilePath::CharType kOriginDatabaseName[] =
26 FILE_PATH_LITERAL("Origins");
27 const char kOriginKeyPrefix[] = "ORIGIN:";
28 const char kLastPathKey[] = "LAST_PATH";
29 const int64 kMinimumReportIntervalHours = 1;
30 const char kInitStatusHistogramLabel[] = "FileSystem.OriginDatabaseInit";
31 const char kDatabaseRepairHistogramLabel[] = "FileSystem.OriginDatabaseRepair";
35 INIT_STATUS_CORRUPTION,
37 INIT_STATUS_UNKNOWN_ERROR,
42 DB_REPAIR_SUCCEEDED = 0,
47 std::string OriginToOriginKey(const std::string& origin) {
48 std::string key(kOriginKeyPrefix);
52 const char* LastPathKey() {
60 SandboxOriginDatabase::SandboxOriginDatabase(
61 const base::FilePath& file_system_directory)
62 : file_system_directory_(file_system_directory) {
65 SandboxOriginDatabase::~SandboxOriginDatabase() {
68 bool SandboxOriginDatabase::Init(InitOption init_option,
69 RecoveryOption recovery_option) {
73 base::FilePath db_path = GetDatabasePath();
74 if (init_option == FAIL_IF_NONEXISTENT && !base::PathExists(db_path))
77 std::string path = FilePathToString(db_path);
78 leveldb::Options options;
79 options.max_open_files = 0; // Use minimum.
80 options.create_if_missing = true;
82 leveldb::Status status = leveldb::DB::Open(options, path, &db);
83 ReportInitStatus(status);
88 HandleError(FROM_HERE, status);
90 // Corruption due to missing necessary MANIFEST-* file causes IOError instead
91 // of Corruption error.
92 // Try to repair database even when IOError case.
93 if (!status.IsCorruption() && !status.IsIOError())
96 switch (recovery_option) {
97 case FAIL_ON_CORRUPTION:
99 case REPAIR_ON_CORRUPTION:
100 LOG(WARNING) << "Attempting to repair SandboxOriginDatabase.";
102 if (RepairDatabase(path)) {
103 UMA_HISTOGRAM_ENUMERATION(kDatabaseRepairHistogramLabel,
104 DB_REPAIR_SUCCEEDED, DB_REPAIR_MAX);
105 LOG(WARNING) << "Repairing SandboxOriginDatabase completed.";
108 UMA_HISTOGRAM_ENUMERATION(kDatabaseRepairHistogramLabel,
109 DB_REPAIR_FAILED, DB_REPAIR_MAX);
111 case DELETE_ON_CORRUPTION:
112 if (!base::DeleteFile(file_system_directory_, true))
114 if (!file_util::CreateDirectory(file_system_directory_))
116 return Init(init_option, FAIL_ON_CORRUPTION);
122 bool SandboxOriginDatabase::RepairDatabase(const std::string& db_path) {
124 leveldb::Options options;
125 options.max_open_files = 0; // Use minimum.
126 if (!leveldb::RepairDB(db_path, options).ok() ||
127 !Init(FAIL_IF_NONEXISTENT, FAIL_ON_CORRUPTION)) {
128 LOG(WARNING) << "Failed to repair SandboxOriginDatabase.";
132 // See if the repaired entries match with what we have on disk.
133 std::set<base::FilePath> directories;
134 base::FileEnumerator file_enum(file_system_directory_,
135 false /* recursive */,
136 base::FileEnumerator::DIRECTORIES);
137 base::FilePath path_each;
138 while (!(path_each = file_enum.Next()).empty())
139 directories.insert(path_each.BaseName());
140 std::set<base::FilePath>::iterator db_dir_itr =
141 directories.find(base::FilePath(kOriginDatabaseName));
142 // Make sure we have the database file in its directory and therefore we are
143 // working on the correct path.
144 DCHECK(db_dir_itr != directories.end());
145 directories.erase(db_dir_itr);
147 std::vector<OriginRecord> origins;
148 if (!ListAllOrigins(&origins)) {
153 // Delete any obsolete entries from the origins database.
154 for (std::vector<OriginRecord>::iterator db_origin_itr = origins.begin();
155 db_origin_itr != origins.end();
157 std::set<base::FilePath>::iterator dir_itr =
158 directories.find(db_origin_itr->path);
159 if (dir_itr == directories.end()) {
160 if (!RemovePathForOrigin(db_origin_itr->origin)) {
165 directories.erase(dir_itr);
169 // Delete any directories not listed in the origins database.
170 for (std::set<base::FilePath>::iterator dir_itr = directories.begin();
171 dir_itr != directories.end();
173 if (!base::DeleteFile(file_system_directory_.Append(*dir_itr),
174 true /* recursive */)) {
183 void SandboxOriginDatabase::HandleError(
184 const tracked_objects::Location& from_here,
185 const leveldb::Status& status) {
187 LOG(ERROR) << "SandboxOriginDatabase failed at: "
188 << from_here.ToString() << " with error: " << status.ToString();
191 void SandboxOriginDatabase::ReportInitStatus(const leveldb::Status& status) {
192 base::Time now = base::Time::Now();
193 base::TimeDelta minimum_interval =
194 base::TimeDelta::FromHours(kMinimumReportIntervalHours);
195 if (last_reported_time_ + minimum_interval >= now)
197 last_reported_time_ = now;
200 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
201 INIT_STATUS_OK, INIT_STATUS_MAX);
202 } else if (status.IsCorruption()) {
203 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
204 INIT_STATUS_CORRUPTION, INIT_STATUS_MAX);
205 } else if (status.IsIOError()) {
206 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
207 INIT_STATUS_IO_ERROR, INIT_STATUS_MAX);
209 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
210 INIT_STATUS_UNKNOWN_ERROR, INIT_STATUS_MAX);
214 bool SandboxOriginDatabase::HasOriginPath(const std::string& origin) {
215 if (!Init(FAIL_IF_NONEXISTENT, REPAIR_ON_CORRUPTION))
220 leveldb::Status status =
221 db_->Get(leveldb::ReadOptions(), OriginToOriginKey(origin), &path);
224 if (status.IsNotFound())
226 HandleError(FROM_HERE, status);
230 bool SandboxOriginDatabase::GetPathForOrigin(
231 const std::string& origin, base::FilePath* directory) {
232 if (!Init(CREATE_IF_NONEXISTENT, REPAIR_ON_CORRUPTION))
237 std::string path_string;
238 std::string origin_key = OriginToOriginKey(origin);
239 leveldb::Status status =
240 db_->Get(leveldb::ReadOptions(), origin_key, &path_string);
241 if (status.IsNotFound()) {
242 int last_path_number;
243 if (!GetLastPathNumber(&last_path_number))
245 path_string = base::StringPrintf("%03u", last_path_number + 1);
246 // store both back as a single transaction
247 leveldb::WriteBatch batch;
248 batch.Put(LastPathKey(), path_string);
249 batch.Put(origin_key, path_string);
250 status = db_->Write(leveldb::WriteOptions(), &batch);
252 HandleError(FROM_HERE, status);
257 *directory = StringToFilePath(path_string);
260 HandleError(FROM_HERE, status);
264 bool SandboxOriginDatabase::RemovePathForOrigin(const std::string& origin) {
265 if (!Init(CREATE_IF_NONEXISTENT, REPAIR_ON_CORRUPTION))
267 leveldb::Status status =
268 db_->Delete(leveldb::WriteOptions(), OriginToOriginKey(origin));
269 if (status.ok() || status.IsNotFound())
271 HandleError(FROM_HERE, status);
275 bool SandboxOriginDatabase::ListAllOrigins(
276 std::vector<OriginRecord>* origins) {
278 if (!Init(CREATE_IF_NONEXISTENT, REPAIR_ON_CORRUPTION)) {
282 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
283 std::string origin_key_prefix = OriginToOriginKey(std::string());
284 iter->Seek(origin_key_prefix);
286 while (iter->Valid() &&
287 StartsWithASCII(iter->key().ToString(), origin_key_prefix, true)) {
289 iter->key().ToString().substr(origin_key_prefix.length());
290 base::FilePath path = StringToFilePath(iter->value().ToString());
291 origins->push_back(OriginRecord(origin, path));
297 void SandboxOriginDatabase::DropDatabase() {
301 base::FilePath SandboxOriginDatabase::GetDatabasePath() const {
302 return file_system_directory_.Append(kOriginDatabaseName);
305 void SandboxOriginDatabase::RemoveDatabase() {
307 base::DeleteFile(GetDatabasePath(), true /* recursive */);
310 bool SandboxOriginDatabase::GetLastPathNumber(int* number) {
313 std::string number_string;
314 leveldb::Status status =
315 db_->Get(leveldb::ReadOptions(), LastPathKey(), &number_string);
317 return base::StringToInt(number_string, number);
318 if (!status.IsNotFound()) {
319 HandleError(FROM_HERE, status);
322 // Verify that this is a totally new database, and initialize it.
323 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
325 if (iter->Valid()) { // DB was not empty, but had no last path number!
326 LOG(ERROR) << "File system origin database is corrupt!";
329 // This is always the first write into the database. If we ever add a
330 // version number, they should go in in a single transaction.
332 db_->Put(leveldb::WriteOptions(), LastPathKey(), std::string("-1"));
334 HandleError(FROM_HERE, status);
341 } // namespace fileapi