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

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: +NormalizePathSeparators 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::IsLink(find_info))
295 return false;
kinuko 2012/04/11 10:58:49 Should we simply delete this..?
tzik 2012/04/11 13:39:06 Looks able to erase whole line. If the link have b
296
297 if (file_util::FileEnumerator::IsDirectory(find_info)) {
298 pending_directories.push(relative_file_path);
299 continue;
300 }
301
302 // Check if the file has a database entry.
303 std::set<FilePath>::iterator itr = files_in_db_.find(relative_file_path);
304 if (itr == files_in_db_.end()) {
305 if (!file_util::Delete(absolute_file_path, false))
306 return false;
307 } else {
308 files_in_db_.erase(itr);
309 }
310 }
311 }
312
313 return files_in_db_.empty();
314 }
315
316 bool DatabaseCheckHelper::ScanHierarchy() {
317 size_t visited_directories = 0;
318 size_t visited_files = 0;
319 size_t visited_links = 0;
320
321 std::stack<FileId> directories;
322 directories.push(0);
323
324 // Check if the root directory exists as a directory.
325 FileInfo file_info;
326 if (!dir_db_->GetFileInfo(0, &file_info))
327 return false;
328 if (file_info.parent_id != 0 ||
329 !file_info.is_directory())
330 return false;
331
332 while (!directories.empty()) {
333 ++visited_directories;
334 FileId dir_id = directories.top();
335 directories.pop();
336
337 std::vector<FileId> children;
338 if (!dir_db_->ListChildren(dir_id, &children))
339 return false;
340 for (std::vector<FileId>::iterator itr = children.begin();
341 itr != children.end();
342 ++itr) {
343 // Any directory must not have root directory as child.
344 if (!*itr)
345 return false;
346
347 // Check if the child knows the parent as its parent.
348 FileInfo file_info;
349 if (!dir_db_->GetFileInfo(*itr, &file_info))
350 return false;
351 if (file_info.parent_id != dir_id)
352 return false;
353
354 // Check if the parent knows the name of its child correctly.
355 FileId file_id;
356 if (!dir_db_->GetChildWithName(dir_id, file_info.name, &file_id) ||
357 file_id != *itr)
358 return false;
359
360 if (file_info.is_directory())
361 directories.push(*itr);
362 else
363 ++visited_files;
364 ++visited_links;
365 }
366 }
367
368 // Check if we've visited all database entries.
369 return num_directories_in_db_ == visited_directories &&
370 num_files_in_db_ == visited_files &&
371 num_hierarchy_links_in_db_ == visited_links;
372 }
373
109 } // namespace 374 } // namespace
110 375
111 namespace fileapi { 376 namespace fileapi {
112 377
113 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { 378 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) {
114 } 379 }
115 380
116 FileSystemDirectoryDatabase::FileInfo::~FileInfo() { 381 FileSystemDirectoryDatabase::FileInfo::~FileInfo() {
117 } 382 }
118 383
119 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase( 384 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase(
120 const FilePath& filesystem_data_directory) 385 const FilePath& filesystem_data_directory)
121 : filesystem_data_directory_(filesystem_data_directory) { 386 : filesystem_data_directory_(filesystem_data_directory) {
122 } 387 }
123 388
124 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { 389 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() {
125 } 390 }
126 391
127 bool FileSystemDirectoryDatabase::GetChildWithName( 392 bool FileSystemDirectoryDatabase::GetChildWithName(
128 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { 393 FileId parent_id, const FilePath::StringType& name, FileId* child_id) {
129 if (!Init(FAIL_ON_CORRUPTION)) 394 if (!Init(REPAIR_ON_CORRUPTION))
130 return false; 395 return false;
131 DCHECK(child_id); 396 DCHECK(child_id);
132 std::string child_key = GetChildLookupKey(parent_id, name); 397 std::string child_key = GetChildLookupKey(parent_id, name);
133 std::string child_id_string; 398 std::string child_id_string;
134 leveldb::Status status = 399 leveldb::Status status =
135 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 400 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
136 if (status.IsNotFound()) 401 if (status.IsNotFound())
137 return false; 402 return false;
138 if (status.ok()) { 403 if (status.ok()) {
139 if (!base::StringToInt64(child_id_string, child_id)) { 404 if (!base::StringToInt64(child_id_string, child_id)) {
(...skipping 20 matching lines...) Expand all
160 if (!GetChildWithName(local_id, name, &local_id)) 425 if (!GetChildWithName(local_id, name, &local_id))
161 return false; 426 return false;
162 } 427 }
163 *file_id = local_id; 428 *file_id = local_id;
164 return true; 429 return true;
165 } 430 }
166 431
167 bool FileSystemDirectoryDatabase::ListChildren( 432 bool FileSystemDirectoryDatabase::ListChildren(
168 FileId parent_id, std::vector<FileId>* children) { 433 FileId parent_id, std::vector<FileId>* children) {
169 // Check to add later: fail if parent is a file, at least in debug builds. 434 // Check to add later: fail if parent is a file, at least in debug builds.
170 if (!Init(FAIL_ON_CORRUPTION)) 435 if (!Init(REPAIR_ON_CORRUPTION))
171 return false; 436 return false;
172 DCHECK(children); 437 DCHECK(children);
173 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); 438 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id);
174 439
175 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 440 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
176 iter->Seek(child_key_prefix); 441 iter->Seek(child_key_prefix);
177 children->clear(); 442 children->clear();
178 while (iter->Valid() && 443 while (iter->Valid() &&
179 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { 444 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) {
180 std::string child_id_string = iter->value().ToString(); 445 std::string child_id_string = iter->value().ToString();
181 FileId child_id; 446 FileId child_id;
182 if (!base::StringToInt64(child_id_string, &child_id)) { 447 if (!base::StringToInt64(child_id_string, &child_id)) {
183 LOG(ERROR) << "Hit database corruption!"; 448 LOG(ERROR) << "Hit database corruption!";
184 return false; 449 return false;
185 } 450 }
186 children->push_back(child_id); 451 children->push_back(child_id);
187 iter->Next(); 452 iter->Next();
188 } 453 }
189 return true; 454 return true;
190 } 455 }
191 456
192 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { 457 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) {
193 if (!Init(FAIL_ON_CORRUPTION)) 458 if (!Init(REPAIR_ON_CORRUPTION))
194 return false; 459 return false;
195 DCHECK(info); 460 DCHECK(info);
196 std::string file_key = GetFileLookupKey(file_id); 461 std::string file_key = GetFileLookupKey(file_id);
197 std::string file_data_string; 462 std::string file_data_string;
198 leveldb::Status status = 463 leveldb::Status status =
199 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); 464 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string);
200 if (status.ok()) { 465 if (status.ok()) {
201 return FileInfoFromPickle( 466 return FileInfoFromPickle(
202 Pickle(file_data_string.data(), file_data_string.length()), info); 467 Pickle(file_data_string.data(), file_data_string.length()), info);
203 } 468 }
204 // Special-case the root, for databases that haven't been initialized yet. 469 // 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 470 // 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. 471 // first file in the database, will fail and confuse callers.
207 if (status.IsNotFound() && !file_id) { 472 if (status.IsNotFound() && !file_id) {
208 info->name = FilePath::StringType(); 473 info->name = FilePath::StringType();
209 info->data_path = FilePath(); 474 info->data_path = FilePath();
210 info->modification_time = base::Time::Now(); 475 info->modification_time = base::Time::Now();
211 info->parent_id = 0; 476 info->parent_id = 0;
212 return true; 477 return true;
213 } 478 }
214 HandleError(FROM_HERE, status); 479 HandleError(FROM_HERE, status);
215 return false; 480 return false;
216 } 481 }
217 482
218 bool FileSystemDirectoryDatabase::AddFileInfo( 483 bool FileSystemDirectoryDatabase::AddFileInfo(
219 const FileInfo& info, FileId* file_id) { 484 const FileInfo& info, FileId* file_id) {
220 if (!Init(FAIL_ON_CORRUPTION)) 485 if (!Init(REPAIR_ON_CORRUPTION))
221 return false; 486 return false;
222 DCHECK(file_id); 487 DCHECK(file_id);
223 std::string child_key = GetChildLookupKey(info.parent_id, info.name); 488 std::string child_key = GetChildLookupKey(info.parent_id, info.name);
224 std::string child_id_string; 489 std::string child_id_string;
225 leveldb::Status status = 490 leveldb::Status status =
226 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 491 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
227 if (status.ok()) { 492 if (status.ok()) {
228 LOG(ERROR) << "File exists already!"; 493 LOG(ERROR) << "File exists already!";
229 return false; 494 return false;
230 } 495 }
(...skipping 21 matching lines...) Expand all
252 status = db_->Write(leveldb::WriteOptions(), &batch); 517 status = db_->Write(leveldb::WriteOptions(), &batch);
253 if (!status.ok()) { 518 if (!status.ok()) {
254 HandleError(FROM_HERE, status); 519 HandleError(FROM_HERE, status);
255 return false; 520 return false;
256 } 521 }
257 *file_id = temp_id; 522 *file_id = temp_id;
258 return true; 523 return true;
259 } 524 }
260 525
261 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { 526 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) {
262 if (!Init(FAIL_ON_CORRUPTION)) 527 if (!Init(REPAIR_ON_CORRUPTION))
263 return false; 528 return false;
264 leveldb::WriteBatch batch; 529 leveldb::WriteBatch batch;
265 if (!RemoveFileInfoHelper(file_id, &batch)) 530 if (!RemoveFileInfoHelper(file_id, &batch))
266 return false; 531 return false;
267 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 532 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
268 if (!status.ok()) { 533 if (!status.ok()) {
269 HandleError(FROM_HERE, status); 534 HandleError(FROM_HERE, status);
270 return false; 535 return false;
271 } 536 }
272 return true; 537 return true;
273 } 538 }
274 539
275 bool FileSystemDirectoryDatabase::UpdateFileInfo( 540 bool FileSystemDirectoryDatabase::UpdateFileInfo(
276 FileId file_id, const FileInfo& new_info) { 541 FileId file_id, const FileInfo& new_info) {
277 // TODO: We should also check to see that this doesn't create a loop, but 542 // TODO: We should also check to see that this doesn't create a loop, but
278 // perhaps only in a debug build. 543 // perhaps only in a debug build.
279 if (!Init(FAIL_ON_CORRUPTION)) 544 if (!Init(REPAIR_ON_CORRUPTION))
280 return false; 545 return false;
281 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. 546 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB.
282 FileInfo old_info; 547 FileInfo old_info;
283 if (!GetFileInfo(file_id, &old_info)) 548 if (!GetFileInfo(file_id, &old_info))
284 return false; 549 return false;
285 if (old_info.parent_id != new_info.parent_id && 550 if (old_info.parent_id != new_info.parent_id &&
286 !VerifyIsDirectory(new_info.parent_id)) 551 !VerifyIsDirectory(new_info.parent_id))
287 return false; 552 return false;
288 if (old_info.parent_id != new_info.parent_id || 553 if (old_info.parent_id != new_info.parent_id ||
289 old_info.name != new_info.name) { 554 old_info.name != new_info.name) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 pickle.size())); 618 pickle.size()));
354 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 619 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
355 if (!status.ok()) { 620 if (!status.ok()) {
356 HandleError(FROM_HERE, status); 621 HandleError(FROM_HERE, status);
357 return false; 622 return false;
358 } 623 }
359 return true; 624 return true;
360 } 625 }
361 626
362 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { 627 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) {
363 if (!Init(FAIL_ON_CORRUPTION)) 628 if (!Init(REPAIR_ON_CORRUPTION))
364 return false; 629 return false;
365 DCHECK(next); 630 DCHECK(next);
366 std::string int_string; 631 std::string int_string;
367 leveldb::Status status = 632 leveldb::Status status =
368 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); 633 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string);
369 if (status.ok()) { 634 if (status.ok()) {
370 int64 temp; 635 int64 temp;
371 if (!base::StringToInt64(int_string, &temp)) { 636 if (!base::StringToInt64(int_string, &temp)) {
372 LOG(ERROR) << "Hit database corruption!"; 637 LOG(ERROR) << "Hit database corruption!";
373 return false; 638 return false;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 options.create_if_missing = true; 680 options.create_if_missing = true;
416 leveldb::DB* db; 681 leveldb::DB* db;
417 leveldb::Status status = leveldb::DB::Open(options, path, &db); 682 leveldb::Status status = leveldb::DB::Open(options, path, &db);
418 ReportInitStatus(status); 683 ReportInitStatus(status);
419 if (status.ok()) { 684 if (status.ok()) {
420 db_.reset(db); 685 db_.reset(db);
421 return true; 686 return true;
422 } 687 }
423 HandleError(FROM_HERE, status); 688 HandleError(FROM_HERE, status);
424 689
425 if (recovery_option == FAIL_ON_CORRUPTION) 690 switch (recovery_option) {
691 case FAIL_ON_CORRUPTION:
692 return false;
693 case REPAIR_ON_CORRUPTION:
694 if (RepairDatabase(path))
695 return true;
696 // fall through
697 case DELETE_ON_CORRUPTION:
698 if (!file_util::Delete(filesystem_data_directory_, true))
699 return false;
700 if (!file_util::CreateDirectory(filesystem_data_directory_))
701 return false;
kinuko 2012/04/11 10:58:49 Maybe we should output some WARNING message for th
tzik 2012/04/11 13:39:06 Done.
702 return Init(FAIL_ON_CORRUPTION);
703 }
704
705 NOTREACHED();
706 return false;
707 }
708
709 bool FileSystemDirectoryDatabase::RepairDatabase(const std::string& db_path) {
710 DCHECK(!db_.get());
711 if (!leveldb::RepairDB(db_path, leveldb::Options()).ok())
426 return false; 712 return false;
713 if (!Init(FAIL_ON_CORRUPTION))
714 return false;
715 if (IsFileSystemConsistent())
716 return true;
717 db_.reset();
718 return false;
719 }
427 720
428 DCHECK_EQ(DELETE_ON_CORRUPTION, recovery_option); 721 bool FileSystemDirectoryDatabase::IsFileSystemConsistent() {
429 if (!file_util::Delete(filesystem_data_directory_, true)) 722 if (!Init(FAIL_ON_CORRUPTION))
430 return false; 723 return false;
431 if (!file_util::CreateDirectory(filesystem_data_directory_)) 724 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_);
432 return false; 725 return helper.IsFileSystemConsistent();
433 return Init(FAIL_ON_CORRUPTION);
434 } 726 }
435 727
436 void FileSystemDirectoryDatabase::ReportInitStatus( 728 void FileSystemDirectoryDatabase::ReportInitStatus(
437 const leveldb::Status& status) { 729 const leveldb::Status& status) {
438 base::Time now = base::Time::Now(); 730 base::Time now = base::Time::Now();
439 const base::TimeDelta minimum_interval = 731 const base::TimeDelta minimum_interval =
440 base::TimeDelta::FromHours(kMinimumReportIntervalHours); 732 base::TimeDelta::FromHours(kMinimumReportIntervalHours);
441 if (last_reported_time_ + minimum_interval >= now) 733 if (last_reported_time_ + minimum_interval >= now)
442 return; 734 return;
443 last_reported_time_ = now; 735 last_reported_time_ = now;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); 769 batch.Put(LastIntegerKey(), base::Int64ToString(-1));
478 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 770 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
479 if (!status.ok()) { 771 if (!status.ok()) {
480 HandleError(FROM_HERE, status); 772 HandleError(FROM_HERE, status);
481 return false; 773 return false;
482 } 774 }
483 return true; 775 return true;
484 } 776 }
485 777
486 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { 778 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) {
487 if (!Init(FAIL_ON_CORRUPTION)) 779 if (!Init(REPAIR_ON_CORRUPTION))
488 return false; 780 return false;
489 DCHECK(file_id); 781 DCHECK(file_id);
490 std::string id_string; 782 std::string id_string;
491 leveldb::Status status = 783 leveldb::Status status =
492 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); 784 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string);
493 if (status.ok()) { 785 if (status.ok()) {
494 if (!base::StringToInt64(id_string, file_id)) { 786 if (!base::StringToInt64(id_string, file_id)) {
495 LOG(ERROR) << "Hit database corruption!"; 787 LOG(ERROR) << "Hit database corruption!";
496 return false; 788 return false;
497 } 789 }
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 859
568 void FileSystemDirectoryDatabase::HandleError( 860 void FileSystemDirectoryDatabase::HandleError(
569 const tracked_objects::Location& from_here, 861 const tracked_objects::Location& from_here,
570 const leveldb::Status& status) { 862 const leveldb::Status& status) {
571 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " 863 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: "
572 << from_here.ToString() << " with error: " << status.ToString(); 864 << from_here.ToString() << " with error: " << status.ToString();
573 db_.reset(); 865 db_.reset();
574 } 866 }
575 867
576 } // namespace fileapi 868 } // namespace fileapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698