Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(277)

Side by Side Diff: webkit/fileapi/file_system_directory_database.cc

Issue 9910005: Add database recovery for FileSystemDirectoryDatabase. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "webkit/fileapi/file_system_directory_database.h" 5 #include "webkit/fileapi/file_system_directory_database.h"
6 6
7 #include <algorithm>
7 #include <math.h> 8 #include <math.h>
8 9
9 #include "base/file_util.h" 10 #include "base/file_util.h"
10 #include "base/location.h" 11 #include "base/location.h"
11 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
12 #include "base/pickle.h" 13 #include "base/pickle.h"
13 #include "base/string_number_conversions.h" 14 #include "base/string_number_conversions.h"
14 #include "base/string_util.h" 15 #include "base/string_util.h"
15 #include "third_party/leveldatabase/src/include/leveldb/db.h" 16 #include "third_party/leveldatabase/src/include/leveldb/db.h"
16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
18 #include "webkit/fileapi/file_system_usage_cache.h"
17 #include "webkit/fileapi/file_system_util.h" 19 #include "webkit/fileapi/file_system_util.h"
18 20
19 namespace { 21 namespace {
20 22
21 bool PickleFromFileInfo( 23 bool PickleFromFileInfo(
22 const fileapi::FileSystemDirectoryDatabase::FileInfo& info, 24 const fileapi::FileSystemDirectoryDatabase::FileInfo& info,
23 Pickle* pickle) { 25 Pickle* pickle) {
24 DCHECK(pickle); 26 DCHECK(pickle);
25 std::string data_path; 27 std::string data_path;
26 // Round off here to match the behavior of the filesystem on real files. 28 // Round off here to match the behavior of the filesystem on real files.
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 101
100 const char* LastIntegerKey() { 102 const char* LastIntegerKey() {
101 return kLastIntegerKey; 103 return kLastIntegerKey;
102 } 104 }
103 105
104 std::string GetFileLookupKey( 106 std::string GetFileLookupKey(
105 fileapi::FileSystemDirectoryDatabase::FileId file_id) { 107 fileapi::FileSystemDirectoryDatabase::FileId file_id) {
106 return base::Int64ToString(file_id); 108 return base::Int64ToString(file_id);
107 } 109 }
108 110
111 // Assumptions:
112 // - Any database entry is one of:
113 // - ("CHILD_OF:|parent_id|:<name>", "|file_id|"),
114 // - ("LAST_FILE_ID", "|last_file_id|"),
115 // - ("LAST_INTEGER", "|last_integer|"),
116 // - ("|file_id|", "pickled FileInfo")
117 // where FileInfo has |parent_id|, |data_path|, |name| and
118 // |modification_time|,
119 // Constraints:
120 // - Each file in the database has unique backing file.
121 // - Each file in |filesystem_data_directory_| has a database entry.
122 // - Directory structure is tree, i.e. connected and acyclic.
123 class DatabaseCheckHelper {
124 public:
125 typedef fileapi::FileSystemDirectoryDatabase::FileId FileId;
126 typedef fileapi::FileSystemDirectoryDatabase::FileInfo FileInfo;
127
128 DatabaseCheckHelper(fileapi::FileSystemDirectoryDatabase* dir_db,
129 leveldb::DB* db,
130 const FilePath& path);
131
132 bool IsFileSystemConsistent() {
133 return IsDatabaseEmpty() ||
134 (ScanDatabase() && ScanDirectory() && ScanHierarchy());
135 }
136
137 private:
138 bool IsDatabaseEmpty();
139 // These 3 methods need to be called in the order. Each method requires its
140 // previous method finished successfully. They also require the database is
141 // not empty.
142 bool ScanDatabase();
143 bool ScanDirectory();
144 bool ScanHierarchy();
145
146 fileapi::FileSystemDirectoryDatabase* dir_db_;
147 leveldb::DB* db_;
148 FilePath path_;
149
150 std::set<FilePath> files_in_db_;
151
152 size_t num_directories_in_db_;
153 size_t num_files_in_db_;
154 size_t num_hierarchy_links_in_db_;
155
156 FileId last_file_id_;
157 FileId last_integer_;
158 };
159
160 DatabaseCheckHelper::DatabaseCheckHelper(
161 fileapi::FileSystemDirectoryDatabase* dir_db,
162 leveldb::DB* db,
163 const FilePath& path)
164 : dir_db_(dir_db), db_(db), path_(path),
165 num_directories_in_db_(0),
166 num_files_in_db_(0),
167 num_hierarchy_links_in_db_(0),
168 last_file_id_(-1), last_integer_(-1) {
169 DCHECK(dir_db_);
170 DCHECK(db_);
171 DCHECK(!path_.empty() && file_util::DirectoryExists(path_));
172 }
173
174 bool DatabaseCheckHelper::IsDatabaseEmpty() {
175 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
176 itr->SeekToFirst();
177 return !itr->Valid();
178 }
179
180 bool DatabaseCheckHelper::ScanDatabase() {
181 // Scans all database entries sequentially to verify each of them has unique
182 // backing file.
183 int64 max_file_id = -1;
184 std::set<FileId> file_ids;
185
186 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
187 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
188 std::string key = itr->key().ToString();
189 if (StartsWithASCII(key, kChildLookupPrefix, true)) {
190 // key: "CHILD_OF:<parent_id>:<name>"
191 // value: "<child_id>"
192 ++num_hierarchy_links_in_db_;
193 } else if (key == kLastFileIdKey) {
194 // key: "LAST_FILE_ID"
195 // value: "<last_file_id>"
196 if (last_file_id_ >= 0 ||
197 !base::StringToInt64(itr->value().ToString(), &last_file_id_))
198 return false;
199
200 if (last_file_id_ < 0)
201 return false;
202 } else if (key == kLastIntegerKey) {
203 // key: "LAST_INTEGER"
204 // value: "<last_integer>"
205 if (last_integer_ >= 0 ||
206 !base::StringToInt64(itr->value().ToString(), &last_integer_))
207 return false;
208 } else {
209 // key: "<entry_id>"
210 // value: "<pickled FileInfo>"
211 FileInfo file_info;
212 if (!FileInfoFromPickle(
213 Pickle(itr->value().data(), itr->value().size()), &file_info))
214 return false;
215
216 FileId file_id = -1;
217 if (!base::StringToInt64(key, &file_id) || file_id < 0)
218 return false;
219
220 if (max_file_id < file_id)
221 max_file_id = file_id;
222 if (!file_ids.insert(file_id).second)
223 return false;
224
225 if (file_info.is_directory()) {
226 ++num_directories_in_db_;
227 DCHECK(file_info.data_path.empty());
228 } else {
229 // Ensure any pair of file entry don't share their data_path.
230 if (!files_in_db_.insert(file_info.data_path).second)
231 return false;
232
233 // Ensure the backing file exists as a normal file.
234 base::PlatformFileInfo platform_file_info;
235 if (!file_util::GetFileInfo(
236 path_.Append(file_info.data_path), &platform_file_info) ||
237 platform_file_info.is_directory ||
238 platform_file_info.is_symbolic_link) {
239 // leveldb::Iterator iterates a snapshot of the database.
240 // So even after RemoveFileInfo() call, we'll visit hierarchy link
241 // from |parent_id| to |file_id|.
242 if (!dir_db_->RemoveFileInfo(file_id))
243 return false;
244 --num_hierarchy_links_in_db_;
245 files_in_db_.erase(file_info.data_path);
246 } else {
247 ++num_files_in_db_;
248 }
249 }
250 }
251 }
252
253 // TODO(tzik): Add constraint for |last_integer_| to avoid possible
254 // data path confliction on ObfuscatedFileUtil.
255 return max_file_id <= last_file_id_;
256 }
257
258 bool DatabaseCheckHelper::ScanDirectory() {
259 // Scans all local file system entries to verify each of them has a database
260 // entry.
261 const FilePath kExcludes[] = {
262 FilePath(kDirectoryDatabaseName),
263 FilePath(fileapi::FileSystemUsageCache::kUsageFileName),
264 };
265
266 // Any path in |pending_directories| is relative to |path_|.
267 std::stack<FilePath> pending_directories;
268 pending_directories.push(FilePath());
269
270 while (!pending_directories.empty()) {
271 FilePath dir_path = pending_directories.top();
272 pending_directories.pop();
273
274 file_util::FileEnumerator file_enum(
275 dir_path.empty() ? path_ : path_.Append(dir_path),
276 false /* recursive */,
277 static_cast<file_util::FileEnumerator::FileType>(
278 file_util::FileEnumerator::DIRECTORIES |
279 file_util::FileEnumerator::FILES));
280
281 FilePath absolute_file_path;
282 while (!(absolute_file_path = file_enum.Next()).empty()) {
283 file_util::FileEnumerator::FindInfo find_info;
284 file_enum.GetFindInfo(&find_info);
285
286 FilePath relative_file_path;
287 if (!path_.AppendRelativePath(absolute_file_path, &relative_file_path))
288 return false;
289
290 if (std::find(kExcludes, kExcludes + arraysize(kExcludes),
291 relative_file_path) != kExcludes + arraysize(kExcludes))
292 continue;
293
294 if (file_util::FileEnumerator::IsDirectory(find_info)) {
295 pending_directories.push(relative_file_path);
296 continue;
297 }
298
299 // Check if the file has a database entry.
300 std::set<FilePath>::iterator itr = files_in_db_.find(relative_file_path);
301 if (itr == files_in_db_.end()) {
302 if (!file_util::Delete(absolute_file_path, false))
303 return false;
304 } else {
305 files_in_db_.erase(itr);
306 }
307 }
308 }
309
310 return files_in_db_.empty();
311 }
312
313 bool DatabaseCheckHelper::ScanHierarchy() {
314 size_t visited_directories = 0;
315 size_t visited_files = 0;
316 size_t visited_links = 0;
317
318 std::stack<FileId> directories;
319 directories.push(0);
320
321 // Check if the root directory exists as a directory.
322 FileInfo file_info;
323 if (!dir_db_->GetFileInfo(0, &file_info))
324 return false;
325 if (file_info.parent_id != 0 ||
326 !file_info.is_directory())
327 return false;
328
329 while (!directories.empty()) {
330 ++visited_directories;
331 FileId dir_id = directories.top();
332 directories.pop();
333
334 std::vector<FileId> children;
335 if (!dir_db_->ListChildren(dir_id, &children))
336 return false;
337 for (std::vector<FileId>::iterator itr = children.begin();
338 itr != children.end();
339 ++itr) {
340 // Any directory must not have root directory as child.
341 if (!*itr)
342 return false;
343
344 // Check if the child knows the parent as its parent.
345 FileInfo file_info;
346 if (!dir_db_->GetFileInfo(*itr, &file_info))
347 return false;
348 if (file_info.parent_id != dir_id)
349 return false;
350
351 // Check if the parent knows the name of its child correctly.
352 FileId file_id;
353 if (!dir_db_->GetChildWithName(dir_id, file_info.name, &file_id) ||
354 file_id != *itr)
355 return false;
356
357 if (file_info.is_directory())
358 directories.push(*itr);
359 else
360 ++visited_files;
361 ++visited_links;
362 }
363 }
364
365 // Check if we've visited all database entries.
366 return num_directories_in_db_ == visited_directories &&
367 num_files_in_db_ == visited_files &&
368 num_hierarchy_links_in_db_ == visited_links;
369 }
370
109 } // namespace 371 } // namespace
110 372
111 namespace fileapi { 373 namespace fileapi {
112 374
113 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { 375 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) {
114 } 376 }
115 377
116 FileSystemDirectoryDatabase::FileInfo::~FileInfo() { 378 FileSystemDirectoryDatabase::FileInfo::~FileInfo() {
117 } 379 }
118 380
119 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase( 381 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase(
120 const FilePath& filesystem_data_directory) 382 const FilePath& filesystem_data_directory)
121 : filesystem_data_directory_(filesystem_data_directory) { 383 : filesystem_data_directory_(filesystem_data_directory) {
122 } 384 }
123 385
124 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { 386 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() {
125 } 387 }
126 388
127 bool FileSystemDirectoryDatabase::GetChildWithName( 389 bool FileSystemDirectoryDatabase::GetChildWithName(
128 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { 390 FileId parent_id, const FilePath::StringType& name, FileId* child_id) {
129 if (!Init(FAIL_ON_CORRUPTION)) 391 if (!Init(REPAIR_ON_CORRUPTION))
130 return false; 392 return false;
131 DCHECK(child_id); 393 DCHECK(child_id);
132 std::string child_key = GetChildLookupKey(parent_id, name); 394 std::string child_key = GetChildLookupKey(parent_id, name);
133 std::string child_id_string; 395 std::string child_id_string;
134 leveldb::Status status = 396 leveldb::Status status =
135 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 397 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
136 if (status.IsNotFound()) 398 if (status.IsNotFound())
137 return false; 399 return false;
138 if (status.ok()) { 400 if (status.ok()) {
139 if (!base::StringToInt64(child_id_string, child_id)) { 401 if (!base::StringToInt64(child_id_string, child_id)) {
(...skipping 20 matching lines...) Expand all
160 if (!GetChildWithName(local_id, name, &local_id)) 422 if (!GetChildWithName(local_id, name, &local_id))
161 return false; 423 return false;
162 } 424 }
163 *file_id = local_id; 425 *file_id = local_id;
164 return true; 426 return true;
165 } 427 }
166 428
167 bool FileSystemDirectoryDatabase::ListChildren( 429 bool FileSystemDirectoryDatabase::ListChildren(
168 FileId parent_id, std::vector<FileId>* children) { 430 FileId parent_id, std::vector<FileId>* children) {
169 // Check to add later: fail if parent is a file, at least in debug builds. 431 // Check to add later: fail if parent is a file, at least in debug builds.
170 if (!Init(FAIL_ON_CORRUPTION)) 432 if (!Init(REPAIR_ON_CORRUPTION))
171 return false; 433 return false;
172 DCHECK(children); 434 DCHECK(children);
173 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); 435 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id);
174 436
175 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 437 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
176 iter->Seek(child_key_prefix); 438 iter->Seek(child_key_prefix);
177 children->clear(); 439 children->clear();
178 while (iter->Valid() && 440 while (iter->Valid() &&
179 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { 441 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) {
180 std::string child_id_string = iter->value().ToString(); 442 std::string child_id_string = iter->value().ToString();
181 FileId child_id; 443 FileId child_id;
182 if (!base::StringToInt64(child_id_string, &child_id)) { 444 if (!base::StringToInt64(child_id_string, &child_id)) {
183 LOG(ERROR) << "Hit database corruption!"; 445 LOG(ERROR) << "Hit database corruption!";
184 return false; 446 return false;
185 } 447 }
186 children->push_back(child_id); 448 children->push_back(child_id);
187 iter->Next(); 449 iter->Next();
188 } 450 }
189 return true; 451 return true;
190 } 452 }
191 453
192 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { 454 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) {
193 if (!Init(FAIL_ON_CORRUPTION)) 455 if (!Init(REPAIR_ON_CORRUPTION))
194 return false; 456 return false;
195 DCHECK(info); 457 DCHECK(info);
196 std::string file_key = GetFileLookupKey(file_id); 458 std::string file_key = GetFileLookupKey(file_id);
197 std::string file_data_string; 459 std::string file_data_string;
198 leveldb::Status status = 460 leveldb::Status status =
199 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); 461 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string);
200 if (status.ok()) { 462 if (status.ok()) {
201 return FileInfoFromPickle( 463 return FileInfoFromPickle(
202 Pickle(file_data_string.data(), file_data_string.length()), info); 464 Pickle(file_data_string.data(), file_data_string.length()), info);
203 } 465 }
204 // Special-case the root, for databases that haven't been initialized yet. 466 // Special-case the root, for databases that haven't been initialized yet.
205 // Without this, a query for the root's file info, made before creating the 467 // Without this, a query for the root's file info, made before creating the
206 // first file in the database, will fail and confuse callers. 468 // first file in the database, will fail and confuse callers.
207 if (status.IsNotFound() && !file_id) { 469 if (status.IsNotFound() && !file_id) {
208 info->name = FilePath::StringType(); 470 info->name = FilePath::StringType();
209 info->data_path = FilePath(); 471 info->data_path = FilePath();
210 info->modification_time = base::Time::Now(); 472 info->modification_time = base::Time::Now();
211 info->parent_id = 0; 473 info->parent_id = 0;
212 return true; 474 return true;
213 } 475 }
214 HandleError(FROM_HERE, status); 476 HandleError(FROM_HERE, status);
215 return false; 477 return false;
216 } 478 }
217 479
218 bool FileSystemDirectoryDatabase::AddFileInfo( 480 bool FileSystemDirectoryDatabase::AddFileInfo(
219 const FileInfo& info, FileId* file_id) { 481 const FileInfo& info, FileId* file_id) {
220 if (!Init(FAIL_ON_CORRUPTION)) 482 if (!Init(REPAIR_ON_CORRUPTION))
221 return false; 483 return false;
222 DCHECK(file_id); 484 DCHECK(file_id);
223 std::string child_key = GetChildLookupKey(info.parent_id, info.name); 485 std::string child_key = GetChildLookupKey(info.parent_id, info.name);
224 std::string child_id_string; 486 std::string child_id_string;
225 leveldb::Status status = 487 leveldb::Status status =
226 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 488 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
227 if (status.ok()) { 489 if (status.ok()) {
228 LOG(ERROR) << "File exists already!"; 490 LOG(ERROR) << "File exists already!";
229 return false; 491 return false;
230 } 492 }
(...skipping 21 matching lines...) Expand all
252 status = db_->Write(leveldb::WriteOptions(), &batch); 514 status = db_->Write(leveldb::WriteOptions(), &batch);
253 if (!status.ok()) { 515 if (!status.ok()) {
254 HandleError(FROM_HERE, status); 516 HandleError(FROM_HERE, status);
255 return false; 517 return false;
256 } 518 }
257 *file_id = temp_id; 519 *file_id = temp_id;
258 return true; 520 return true;
259 } 521 }
260 522
261 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { 523 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) {
262 if (!Init(FAIL_ON_CORRUPTION)) 524 if (!Init(REPAIR_ON_CORRUPTION))
263 return false; 525 return false;
264 leveldb::WriteBatch batch; 526 leveldb::WriteBatch batch;
265 if (!RemoveFileInfoHelper(file_id, &batch)) 527 if (!RemoveFileInfoHelper(file_id, &batch))
266 return false; 528 return false;
267 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 529 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
268 if (!status.ok()) { 530 if (!status.ok()) {
269 HandleError(FROM_HERE, status); 531 HandleError(FROM_HERE, status);
270 return false; 532 return false;
271 } 533 }
272 return true; 534 return true;
273 } 535 }
274 536
275 bool FileSystemDirectoryDatabase::UpdateFileInfo( 537 bool FileSystemDirectoryDatabase::UpdateFileInfo(
276 FileId file_id, const FileInfo& new_info) { 538 FileId file_id, const FileInfo& new_info) {
277 // TODO: We should also check to see that this doesn't create a loop, but 539 // TODO: We should also check to see that this doesn't create a loop, but
278 // perhaps only in a debug build. 540 // perhaps only in a debug build.
279 if (!Init(FAIL_ON_CORRUPTION)) 541 if (!Init(REPAIR_ON_CORRUPTION))
280 return false; 542 return false;
281 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. 543 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB.
282 FileInfo old_info; 544 FileInfo old_info;
283 if (!GetFileInfo(file_id, &old_info)) 545 if (!GetFileInfo(file_id, &old_info))
284 return false; 546 return false;
285 if (old_info.parent_id != new_info.parent_id && 547 if (old_info.parent_id != new_info.parent_id &&
286 !VerifyIsDirectory(new_info.parent_id)) 548 !VerifyIsDirectory(new_info.parent_id))
287 return false; 549 return false;
288 if (old_info.parent_id != new_info.parent_id || 550 if (old_info.parent_id != new_info.parent_id ||
289 old_info.name != new_info.name) { 551 old_info.name != new_info.name) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 pickle.size())); 615 pickle.size()));
354 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 616 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
355 if (!status.ok()) { 617 if (!status.ok()) {
356 HandleError(FROM_HERE, status); 618 HandleError(FROM_HERE, status);
357 return false; 619 return false;
358 } 620 }
359 return true; 621 return true;
360 } 622 }
361 623
362 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { 624 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) {
363 if (!Init(FAIL_ON_CORRUPTION)) 625 if (!Init(REPAIR_ON_CORRUPTION))
364 return false; 626 return false;
365 DCHECK(next); 627 DCHECK(next);
366 std::string int_string; 628 std::string int_string;
367 leveldb::Status status = 629 leveldb::Status status =
368 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); 630 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string);
369 if (status.ok()) { 631 if (status.ok()) {
370 int64 temp; 632 int64 temp;
371 if (!base::StringToInt64(int_string, &temp)) { 633 if (!base::StringToInt64(int_string, &temp)) {
372 LOG(ERROR) << "Hit database corruption!"; 634 LOG(ERROR) << "Hit database corruption!";
373 return false; 635 return false;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 options.create_if_missing = true; 677 options.create_if_missing = true;
416 leveldb::DB* db; 678 leveldb::DB* db;
417 leveldb::Status status = leveldb::DB::Open(options, path, &db); 679 leveldb::Status status = leveldb::DB::Open(options, path, &db);
418 ReportInitStatus(status); 680 ReportInitStatus(status);
419 if (status.ok()) { 681 if (status.ok()) {
420 db_.reset(db); 682 db_.reset(db);
421 return true; 683 return true;
422 } 684 }
423 HandleError(FROM_HERE, status); 685 HandleError(FROM_HERE, status);
424 686
425 if (recovery_option == FAIL_ON_CORRUPTION) 687 switch (recovery_option) {
688 case FAIL_ON_CORRUPTION:
689 return false;
690 case REPAIR_ON_CORRUPTION:
691 LOG(WARNING) << "Corrupted FileSystemDirectoryDatabase detected."
692 << " Attempting to repair.";
693 if (RepairDatabase(path))
694 return true;
695 LOG(WARNING) << "Failed to repair FileSystemDirectoryDatabase.";
696 // fall through
697 case DELETE_ON_CORRUPTION:
698 LOG(WARNING) << "Clearing FileSystemDirectoryDatabase.";
699 if (!file_util::Delete(filesystem_data_directory_, true))
700 return false;
701 if (!file_util::CreateDirectory(filesystem_data_directory_))
702 return false;
703 return Init(FAIL_ON_CORRUPTION);
704 }
705
706 NOTREACHED();
707 return false;
708 }
709
710 bool FileSystemDirectoryDatabase::RepairDatabase(const std::string& db_path) {
711 DCHECK(!db_.get());
712 if (!leveldb::RepairDB(db_path, leveldb::Options()).ok())
426 return false; 713 return false;
714 if (!Init(FAIL_ON_CORRUPTION))
715 return false;
716 if (IsFileSystemConsistent())
717 return true;
718 db_.reset();
719 return false;
720 }
427 721
428 DCHECK_EQ(DELETE_ON_CORRUPTION, recovery_option); 722 bool FileSystemDirectoryDatabase::IsFileSystemConsistent() {
429 if (!file_util::Delete(filesystem_data_directory_, true)) 723 if (!Init(FAIL_ON_CORRUPTION))
430 return false; 724 return false;
431 if (!file_util::CreateDirectory(filesystem_data_directory_)) 725 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_);
432 return false; 726 return helper.IsFileSystemConsistent();
433 return Init(FAIL_ON_CORRUPTION);
434 } 727 }
435 728
436 void FileSystemDirectoryDatabase::ReportInitStatus( 729 void FileSystemDirectoryDatabase::ReportInitStatus(
437 const leveldb::Status& status) { 730 const leveldb::Status& status) {
438 base::Time now = base::Time::Now(); 731 base::Time now = base::Time::Now();
439 const base::TimeDelta minimum_interval = 732 const base::TimeDelta minimum_interval =
440 base::TimeDelta::FromHours(kMinimumReportIntervalHours); 733 base::TimeDelta::FromHours(kMinimumReportIntervalHours);
441 if (last_reported_time_ + minimum_interval >= now) 734 if (last_reported_time_ + minimum_interval >= now)
442 return; 735 return;
443 last_reported_time_ = now; 736 last_reported_time_ = now;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); 770 batch.Put(LastIntegerKey(), base::Int64ToString(-1));
478 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 771 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
479 if (!status.ok()) { 772 if (!status.ok()) {
480 HandleError(FROM_HERE, status); 773 HandleError(FROM_HERE, status);
481 return false; 774 return false;
482 } 775 }
483 return true; 776 return true;
484 } 777 }
485 778
486 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { 779 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) {
487 if (!Init(FAIL_ON_CORRUPTION)) 780 if (!Init(REPAIR_ON_CORRUPTION))
488 return false; 781 return false;
489 DCHECK(file_id); 782 DCHECK(file_id);
490 std::string id_string; 783 std::string id_string;
491 leveldb::Status status = 784 leveldb::Status status =
492 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); 785 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string);
493 if (status.ok()) { 786 if (status.ok()) {
494 if (!base::StringToInt64(id_string, file_id)) { 787 if (!base::StringToInt64(id_string, file_id)) {
495 LOG(ERROR) << "Hit database corruption!"; 788 LOG(ERROR) << "Hit database corruption!";
496 return false; 789 return false;
497 } 790 }
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 860
568 void FileSystemDirectoryDatabase::HandleError( 861 void FileSystemDirectoryDatabase::HandleError(
569 const tracked_objects::Location& from_here, 862 const tracked_objects::Location& from_here,
570 const leveldb::Status& status) { 863 const leveldb::Status& status) {
571 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " 864 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: "
572 << from_here.ToString() << " with error: " << status.ToString(); 865 << from_here.ToString() << " with error: " << status.ToString();
573 db_.reset(); 866 db_.reset();
574 } 867 }
575 868
576 } // namespace fileapi 869 } // namespace fileapi
OLDNEW
« no previous file with comments | « webkit/fileapi/file_system_directory_database.h ('k') | webkit/fileapi/file_system_directory_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698