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

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: -TODO 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 <math.h> 7 #include <math.h>
8 8
9 #include "base/file_util.h" 9 #include "base/file_util.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
12 #include "base/pickle.h" 12 #include "base/pickle.h"
13 #include "base/string_number_conversions.h" 13 #include "base/string_number_conversions.h"
14 #include "base/string_util.h" 14 #include "base/string_util.h"
15 #include "third_party/leveldatabase/src/include/leveldb/db.h" 15 #include "third_party/leveldatabase/src/include/leveldb/db.h"
16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
17 #include "webkit/fileapi/file_system_usage_cache.h"
17 #include "webkit/fileapi/file_system_util.h" 18 #include "webkit/fileapi/file_system_util.h"
18 19
19 namespace { 20 namespace {
20 21
21 bool PickleFromFileInfo( 22 bool PickleFromFileInfo(
22 const fileapi::FileSystemDirectoryDatabase::FileInfo& info, 23 const fileapi::FileSystemDirectoryDatabase::FileInfo& info,
23 Pickle* pickle) { 24 Pickle* pickle) {
24 DCHECK(pickle); 25 DCHECK(pickle);
25 std::string data_path; 26 std::string data_path;
26 // Round off here to match the behavior of the filesystem on real files. 27 // Round off here to match the behavior of the filesystem on real files.
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 99
99 const char* LastIntegerKey() { 100 const char* LastIntegerKey() {
100 return kLastIntegerKey; 101 return kLastIntegerKey;
101 } 102 }
102 103
103 std::string GetFileLookupKey( 104 std::string GetFileLookupKey(
104 fileapi::FileSystemDirectoryDatabase::FileId file_id) { 105 fileapi::FileSystemDirectoryDatabase::FileId file_id) {
105 return base::Int64ToString(file_id); 106 return base::Int64ToString(file_id);
106 } 107 }
107 108
109 // Assumptions:
110 // - Any database entry is one of:
111 // - ("CHILD_OF:|parent_id|:<name>", "|file_id|"),
112 // - ("LAST_FILE_ID", "|last_file_id|"),
113 // - ("LAST_INTEGER", "|last_integer|"),
114 // - ("|file_id|", "pickled FileInfo")
115 // whire FileInfo has |parent_id|, |data_path|, |name| and
116 // |modification_time|,
117 // Constraints:
118 // - Each |file_id| has unique backing file.
ericu 2012/04/03 01:52:11 No--there are file_ids for directories, which have
tzik 2012/04/04 06:50:39 Done.
119 // - Each file in |filesystem_data_directory_| has a database entry.
120 // - Directory structure is tree, i.e. connected and acyclic.
121 class DatabaseCheckHelper {
122 public:
123 typedef fileapi::FileSystemDirectoryDatabase::FileId FileId;
124 typedef fileapi::FileSystemDirectoryDatabase::FileInfo FileInfo;
125
126 DatabaseCheckHelper(fileapi::FileSystemDirectoryDatabase* dir_db,
127 leveldb::DB* db,
128 const FilePath& path);
129
130 bool IsFileSystemConsistent() {
131 return IsDatabaseEmpty() ||
132 (ScanDatabase() && ScanDirectory() && ScanHierarchy());
133 }
134
135 private:
136 bool IsDatabaseEmpty();
137 bool ScanDatabase();
ericu 2012/04/03 01:52:11 It's probably worth a comment that these need to b
tzik 2012/04/04 06:50:39 Done.
138 bool ScanDirectory();
139 bool ScanHierarchy();
140
141 fileapi::FileSystemDirectoryDatabase* dir_db_;
142 leveldb::DB* db_;
143 FilePath path_;
144
145 std::set<FilePath> files_in_db_;
146
147 size_t num_directories_in_db_;
148 size_t num_files_in_db_;
149 size_t num_hierarchy_links_in_db_;
150
151 FileId last_file_id_;
152 FileId last_integer_;
153 };
154
155 DatabaseCheckHelper::DatabaseCheckHelper(
156 fileapi::FileSystemDirectoryDatabase* dir_db,
157 leveldb::DB* db,
158 const FilePath& path)
159 : dir_db_(dir_db), db_(db), path_(path),
160 files_in_db_(),
ericu 2012/04/03 01:52:11 Omit the default constructor for files_in_db.
tzik 2012/04/04 06:50:39 Done.
161 num_directories_in_db_(0),
162 num_files_in_db_(0),
163 num_hierarchy_links_in_db_(0),
164 last_file_id_(-1), last_integer_(-1) {
165 DCHECK(dir_db_);
166 DCHECK(db_);
167 DCHECK(!path_.empty() && file_util::DirectoryExists(path_));
168 }
169
170 bool DatabaseCheckHelper::IsDatabaseEmpty() {
171 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
172 itr->SeekToFirst();
173 return !itr->Valid();
174 }
175
176 bool DatabaseCheckHelper::ScanDatabase() {
177 // Scans all database entries sequentially to verify each of them has unique
178 // backing file.
179 int64 max_file_id = -1;
180
181 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
182 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
183 std::string key = itr->key().ToString();
184 if (StartsWithASCII(key, kChildLookupPrefix, true)) {
185 // key: "CHILD_OF:<parent_id>:<name>"
186 // value: "<child_id>"
187 ++num_hierarchy_links_in_db_;
188 } else if (key == kLastFileIdKey) {
189 // key: "LAST_FILE_ID"
190 // value: "<last_file_id>"
191 if (last_file_id_ >= 0 ||
192 !base::StringToInt64(itr->value().ToString(), &last_file_id_))
193 return false;
194
195 if (last_file_id_ < 0)
196 return false;
197 } else if (key == kLastIntegerKey) {
198 // key: "LAST_INTEGER"
199 // value: "<last_integer>"
200 if (last_integer_ >= 0 ||
201 !base::StringToInt64(itr->value().ToString(), &last_integer_))
202 return false;
203 } else {
204 // key: "<entry_id>"
205 // value: "<pickled FileInfo>"
206 FileInfo file_info;
207 if (!FileInfoFromPickle(
208 Pickle(itr->value().data(), itr->value().size()), &file_info))
209 return false;
210
211 FileId file_id = -1;
212 if (!base::StringToInt64(key, &file_id) || file_id < 0)
213 return false;
214
215 if (max_file_id < file_id)
216 max_file_id = file_id;
217
218 if (file_info.is_directory()) {
219 ++num_directories_in_db_;
ericu 2012/04/03 01:52:11 Verify that there's no data path in the pickle.
tzik 2012/04/04 06:50:39 Done. Implementation of FileInfo::is_directory() i
220 } else {
221 ++num_files_in_db_;
ericu 2012/04/03 01:52:11 Verify that the file id is unique.
tzik 2012/04/04 06:50:39 Done.
222
223 // Ensure any pair of file entry don't share their data_path.
224 if (!files_in_db_.insert(file_info.data_path).second)
225 return false;
226
227 // Ensure the backing file exists as a normal file.
228 base::PlatformFileInfo platform_file_info;
229 if (!file_util::GetFileInfo(
230 path_.Append(file_info.data_path), &platform_file_info) ||
231 platform_file_info.is_directory ||
232 platform_file_info.is_symbolic_link)
233 return false;
234 }
235 }
236 }
237
238 // TODO(tzik): Add constraint for |last_integer_| to avoid possible
239 // data path confliction on ObfuscatedFileUtil.
240 return max_file_id <= last_file_id_;
241 }
242
243 bool DatabaseCheckHelper::ScanDirectory() {
244 // Scans all local file system entries to verify each of them has a database
245 // entry.
246 const FilePath kExcludes[] = {
247 FilePath(kDirectoryDatabaseName),
248 FilePath(fileapi::FileSystemUsageCache::kUsageFileName),
249 };
250
251 std::stack<FilePath> pending_directories;
252 pending_directories.push(FilePath());
253
254 while (!pending_directories.empty()) {
255 FilePath dir_path = pending_directories.top();
256 pending_directories.pop();
257
258 FilePath absolute_path = dir_path.empty() ? path_ : path_.Append(dir_path);
259 file_util::FileEnumerator file_enum(
260 path_.Append(dir_path), false /* recursive */,
261 static_cast<file_util::FileEnumerator::FileType>(
262 file_util::FileEnumerator::DIRECTORIES |
263 file_util::FileEnumerator::FILES));
264
265 FilePath file_path;
266 while (!(file_path = file_enum.Next()).empty()) {
267 file_util::FileEnumerator::FindInfo find_info;
268 file_enum.GetFindInfo(&find_info);
269
270 if (std::find(kExcludes, kExcludes + arraysize(kExcludes),
271 dir_path) != kExcludes + arraysize(kExcludes))
ericu 2012/04/03 01:52:11 Add a counter to make sure that you find the right
tzik 2012/04/04 06:50:39 IMO, it is not needed for now. After patchset 5, t
ericu 2012/04/05 00:22:26 That works.
272 continue;
273
274 if (file_util::FileEnumerator::IsLink(find_info))
275 return false;
276
277 FilePath data_path = dir_path.Append(
278 file_util::FileEnumerator::GetFilename(find_info));
279 pending_directories.push(data_path);
ericu 2012/04/03 01:52:11 You're adding all files to pending_directories, no
tzik 2012/04/04 06:50:39 Done. This part of CL was broken..
280 if (file_util::FileEnumerator::IsDirectory(find_info))
281 continue;
282
283 // Check if the file has a database entry.
284 std::set<FilePath>::iterator itr = files_in_db_.find(data_path);
285 if (itr == files_in_db_.end())
286 return false;
287 files_in_db_.erase(itr);
288 }
289 }
290
291 return files_in_db_.empty();
292 }
293
294 bool DatabaseCheckHelper::ScanHierarchy() {
295 size_t visited_directories = 0;
296 size_t visited_files = 0;
297 size_t visited_links = 0;
298
299 std::stack<FileId> directories;
300 directories.push(0);
301
302 // Check if the root directory exists as a directory.
303 FileInfo file_info;
304 if (!dir_db_->GetFileInfo(0, &file_info))
305 return false;
306 if (file_info.parent_id != 0 ||
307 !file_info.is_directory())
308 return false;
309
310 while (!directories.empty()) {
311 ++visited_directories;
312 FileId dir_id = directories.top();
313 directories.pop();
314
315 std::vector<FileId> children;
316 if (!dir_db_->ListChildren(dir_id, &children))
317 return false;
318 for (std::vector<FileId>::iterator itr = children.begin();
319 itr != children.end();
320 ++itr) {
321 // Any directory must not have root directory as child.
322 if (!*itr)
323 return false;
324
325 // Check if the child knows the parent as its parent.
326 FileInfo file_info;
327 if (!dir_db_->GetFileInfo(*itr, &file_info))
328 return false;
329 if (file_info.parent_id != dir_id)
330 return false;
331
332 // Check if the parent knows the name of its child correctly.
333 FileId file_id;
334 if (!dir_db_->GetChildWithName(dir_id, file_info.name, &file_id) ||
335 file_id != *itr)
336 return false;
337
338 if (file_info.is_directory())
339 directories.push(*itr);
340 else
341 ++visited_files;
342 ++visited_links;
343 }
344 }
345
346 // Check if we've visited all database entries.
347 return num_directories_in_db_ == visited_directories &&
348 num_files_in_db_ == visited_files &&
349 num_hierarchy_links_in_db_ == visited_links;
350 }
351
108 } // namespace 352 } // namespace
109 353
110 namespace fileapi { 354 namespace fileapi {
111 355
112 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { 356 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) {
113 } 357 }
114 358
115 FileSystemDirectoryDatabase::FileInfo::~FileInfo() { 359 FileSystemDirectoryDatabase::FileInfo::~FileInfo() {
116 } 360 }
117 361
118 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase( 362 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase(
119 const FilePath& filesystem_data_directory) 363 const FilePath& filesystem_data_directory)
120 : filesystem_data_directory_(filesystem_data_directory) { 364 : filesystem_data_directory_(filesystem_data_directory) {
121 } 365 }
122 366
123 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { 367 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() {
124 } 368 }
125 369
126 bool FileSystemDirectoryDatabase::GetChildWithName( 370 bool FileSystemDirectoryDatabase::GetChildWithName(
127 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { 371 FileId parent_id, const FilePath::StringType& name, FileId* child_id) {
128 if (!Init(FAIL_ON_CORRUPTION)) 372 if (!Init(REPAIR_ON_CORRUPTION))
129 return false; 373 return false;
130 DCHECK(child_id); 374 DCHECK(child_id);
131 std::string child_key = GetChildLookupKey(parent_id, name); 375 std::string child_key = GetChildLookupKey(parent_id, name);
132 std::string child_id_string; 376 std::string child_id_string;
133 leveldb::Status status = 377 leveldb::Status status =
134 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 378 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
135 if (status.IsNotFound()) 379 if (status.IsNotFound())
136 return false; 380 return false;
137 if (status.ok()) { 381 if (status.ok()) {
138 if (!base::StringToInt64(child_id_string, child_id)) { 382 if (!base::StringToInt64(child_id_string, child_id)) {
(...skipping 20 matching lines...) Expand all
159 if (!GetChildWithName(local_id, name, &local_id)) 403 if (!GetChildWithName(local_id, name, &local_id))
160 return false; 404 return false;
161 } 405 }
162 *file_id = local_id; 406 *file_id = local_id;
163 return true; 407 return true;
164 } 408 }
165 409
166 bool FileSystemDirectoryDatabase::ListChildren( 410 bool FileSystemDirectoryDatabase::ListChildren(
167 FileId parent_id, std::vector<FileId>* children) { 411 FileId parent_id, std::vector<FileId>* children) {
168 // Check to add later: fail if parent is a file, at least in debug builds. 412 // Check to add later: fail if parent is a file, at least in debug builds.
169 if (!Init(FAIL_ON_CORRUPTION)) 413 if (!Init(REPAIR_ON_CORRUPTION))
170 return false; 414 return false;
171 DCHECK(children); 415 DCHECK(children);
172 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); 416 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id);
173 417
174 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 418 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
175 iter->Seek(child_key_prefix); 419 iter->Seek(child_key_prefix);
176 children->clear(); 420 children->clear();
177 while (iter->Valid() && 421 while (iter->Valid() &&
178 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { 422 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) {
179 std::string child_id_string = iter->value().ToString(); 423 std::string child_id_string = iter->value().ToString();
180 FileId child_id; 424 FileId child_id;
181 if (!base::StringToInt64(child_id_string, &child_id)) { 425 if (!base::StringToInt64(child_id_string, &child_id)) {
182 LOG(ERROR) << "Hit database corruption!"; 426 LOG(ERROR) << "Hit database corruption!";
183 return false; 427 return false;
184 } 428 }
185 children->push_back(child_id); 429 children->push_back(child_id);
186 iter->Next(); 430 iter->Next();
187 } 431 }
188 return true; 432 return true;
189 } 433 }
190 434
191 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { 435 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) {
192 if (!Init(FAIL_ON_CORRUPTION)) 436 if (!Init(REPAIR_ON_CORRUPTION))
193 return false; 437 return false;
194 DCHECK(info); 438 DCHECK(info);
195 std::string file_key = GetFileLookupKey(file_id); 439 std::string file_key = GetFileLookupKey(file_id);
196 std::string file_data_string; 440 std::string file_data_string;
197 leveldb::Status status = 441 leveldb::Status status =
198 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); 442 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string);
199 if (status.ok()) { 443 if (status.ok()) {
200 return FileInfoFromPickle( 444 return FileInfoFromPickle(
201 Pickle(file_data_string.data(), file_data_string.length()), info); 445 Pickle(file_data_string.data(), file_data_string.length()), info);
202 } 446 }
203 // Special-case the root, for databases that haven't been initialized yet. 447 // Special-case the root, for databases that haven't been initialized yet.
204 // Without this, a query for the root's file info, made before creating the 448 // Without this, a query for the root's file info, made before creating the
205 // first file in the database, will fail and confuse callers. 449 // first file in the database, will fail and confuse callers.
206 if (status.IsNotFound() && !file_id) { 450 if (status.IsNotFound() && !file_id) {
207 info->name = FilePath::StringType(); 451 info->name = FilePath::StringType();
208 info->data_path = FilePath(); 452 info->data_path = FilePath();
209 info->modification_time = base::Time::Now(); 453 info->modification_time = base::Time::Now();
210 info->parent_id = 0; 454 info->parent_id = 0;
211 return true; 455 return true;
212 } 456 }
213 HandleError(FROM_HERE, status); 457 HandleError(FROM_HERE, status);
214 return false; 458 return false;
215 } 459 }
216 460
217 bool FileSystemDirectoryDatabase::AddFileInfo( 461 bool FileSystemDirectoryDatabase::AddFileInfo(
218 const FileInfo& info, FileId* file_id) { 462 const FileInfo& info, FileId* file_id) {
219 if (!Init(FAIL_ON_CORRUPTION)) 463 if (!Init(REPAIR_ON_CORRUPTION))
220 return false; 464 return false;
221 DCHECK(file_id); 465 DCHECK(file_id);
222 std::string child_key = GetChildLookupKey(info.parent_id, info.name); 466 std::string child_key = GetChildLookupKey(info.parent_id, info.name);
223 std::string child_id_string; 467 std::string child_id_string;
224 leveldb::Status status = 468 leveldb::Status status =
225 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 469 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
226 if (status.ok()) { 470 if (status.ok()) {
227 LOG(ERROR) << "File exists already!"; 471 LOG(ERROR) << "File exists already!";
228 return false; 472 return false;
229 } 473 }
(...skipping 21 matching lines...) Expand all
251 status = db_->Write(leveldb::WriteOptions(), &batch); 495 status = db_->Write(leveldb::WriteOptions(), &batch);
252 if (!status.ok()) { 496 if (!status.ok()) {
253 HandleError(FROM_HERE, status); 497 HandleError(FROM_HERE, status);
254 return false; 498 return false;
255 } 499 }
256 *file_id = temp_id; 500 *file_id = temp_id;
257 return true; 501 return true;
258 } 502 }
259 503
260 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { 504 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) {
261 if (!Init(FAIL_ON_CORRUPTION)) 505 if (!Init(REPAIR_ON_CORRUPTION))
262 return false; 506 return false;
263 leveldb::WriteBatch batch; 507 leveldb::WriteBatch batch;
264 if (!RemoveFileInfoHelper(file_id, &batch)) 508 if (!RemoveFileInfoHelper(file_id, &batch))
265 return false; 509 return false;
266 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 510 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
267 if (!status.ok()) { 511 if (!status.ok()) {
268 HandleError(FROM_HERE, status); 512 HandleError(FROM_HERE, status);
269 return false; 513 return false;
270 } 514 }
271 return true; 515 return true;
272 } 516 }
273 517
274 bool FileSystemDirectoryDatabase::UpdateFileInfo( 518 bool FileSystemDirectoryDatabase::UpdateFileInfo(
275 FileId file_id, const FileInfo& new_info) { 519 FileId file_id, const FileInfo& new_info) {
276 // TODO: We should also check to see that this doesn't create a loop, but 520 // TODO: We should also check to see that this doesn't create a loop, but
277 // perhaps only in a debug build. 521 // perhaps only in a debug build.
278 if (!Init(FAIL_ON_CORRUPTION)) 522 if (!Init(REPAIR_ON_CORRUPTION))
279 return false; 523 return false;
280 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. 524 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB.
281 FileInfo old_info; 525 FileInfo old_info;
282 if (!GetFileInfo(file_id, &old_info)) 526 if (!GetFileInfo(file_id, &old_info))
283 return false; 527 return false;
284 if (old_info.parent_id != new_info.parent_id && 528 if (old_info.parent_id != new_info.parent_id &&
285 !VerifyIsDirectory(new_info.parent_id)) 529 !VerifyIsDirectory(new_info.parent_id))
286 return false; 530 return false;
287 if (old_info.parent_id != new_info.parent_id || 531 if (old_info.parent_id != new_info.parent_id ||
288 old_info.name != new_info.name) { 532 old_info.name != new_info.name) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
352 pickle.size())); 596 pickle.size()));
353 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 597 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
354 if (!status.ok()) { 598 if (!status.ok()) {
355 HandleError(FROM_HERE, status); 599 HandleError(FROM_HERE, status);
356 return false; 600 return false;
357 } 601 }
358 return true; 602 return true;
359 } 603 }
360 604
361 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { 605 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) {
362 if (!Init(FAIL_ON_CORRUPTION)) 606 if (!Init(REPAIR_ON_CORRUPTION))
363 return false; 607 return false;
364 DCHECK(next); 608 DCHECK(next);
365 std::string int_string; 609 std::string int_string;
366 leveldb::Status status = 610 leveldb::Status status =
367 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); 611 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string);
368 if (status.ok()) { 612 if (status.ok()) {
369 int64 temp; 613 int64 temp;
370 if (!base::StringToInt64(int_string, &temp)) { 614 if (!base::StringToInt64(int_string, &temp)) {
371 LOG(ERROR) << "Hit database corruption!"; 615 LOG(ERROR) << "Hit database corruption!";
372 return false; 616 return false;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 options.create_if_missing = true; 658 options.create_if_missing = true;
415 leveldb::DB* db; 659 leveldb::DB* db;
416 leveldb::Status status = leveldb::DB::Open(options, path, &db); 660 leveldb::Status status = leveldb::DB::Open(options, path, &db);
417 ReportInitStatus(status); 661 ReportInitStatus(status);
418 if (status.ok()) { 662 if (status.ok()) {
419 db_.reset(db); 663 db_.reset(db);
420 return true; 664 return true;
421 } 665 }
422 HandleError(FROM_HERE, status); 666 HandleError(FROM_HERE, status);
423 667
424 if (recovery_option == FAIL_ON_CORRUPTION) 668 switch (recovery_option) {
669 case FAIL_ON_CORRUPTION:
670 return false;
671 case REPAIR_ON_CORRUPTION:
672 if (RepairDatabase(path))
673 return Init(FAIL_ON_CORRUPTION);
674 // fall through
675 case DELETE_ON_CORRUPTION:
676 if (!file_util::Delete(filesystem_data_directory_, true))
677 return false;
678 if (!file_util::CreateDirectory(filesystem_data_directory_))
679 return false;
680 return Init(FAIL_ON_CORRUPTION);
681 }
682
683 NOTREACHED();
684 return false;
685 }
686
687 bool FileSystemDirectoryDatabase::RepairDatabase(const std::string& db_path) {
688 DCHECK(!db_.get());
689 if (!leveldb::RepairDB(db_path, leveldb::Options()).ok())
425 return false; 690 return false;
691 if (!Init(FAIL_ON_CORRUPTION))
ericu 2012/04/03 01:52:11 You're calling Init(FAIL_ON_CORRUPTION) both here
tzik 2012/04/04 06:50:39 Done.
692 return false;
693 if (IsFileSystemConsistent())
694 return true;
695 db_.reset();
696 return false;
697 }
426 698
427 DCHECK_EQ(DELETE_ON_CORRUPTION, recovery_option); 699 bool FileSystemDirectoryDatabase::IsFileSystemConsistent() {
428 if (!file_util::Delete(filesystem_data_directory_, true)) 700 if (!Init(FAIL_ON_CORRUPTION))
429 return false; 701 return false;
430 if (!file_util::CreateDirectory(filesystem_data_directory_)) 702 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_);
431 return false; 703 return helper.IsFileSystemConsistent();
432 return Init(FAIL_ON_CORRUPTION);
433 } 704 }
434 705
435 void FileSystemDirectoryDatabase::ReportInitStatus( 706 void FileSystemDirectoryDatabase::ReportInitStatus(
436 const leveldb::Status& status) { 707 const leveldb::Status& status) {
437 base::Time now = base::Time::Now(); 708 base::Time now = base::Time::Now();
438 const base::TimeDelta minimum_interval = 709 const base::TimeDelta minimum_interval =
439 base::TimeDelta::FromHours(kMinimumReportIntervalHours); 710 base::TimeDelta::FromHours(kMinimumReportIntervalHours);
440 if (last_reported_time_ + minimum_interval >= now) 711 if (last_reported_time_ + minimum_interval >= now)
441 return; 712 return;
442 last_reported_time_ = now; 713 last_reported_time_ = now;
(...skipping 27 matching lines...) Expand all
470 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); 741 batch.Put(LastIntegerKey(), base::Int64ToString(-1));
471 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 742 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
472 if (!status.ok()) { 743 if (!status.ok()) {
473 HandleError(FROM_HERE, status); 744 HandleError(FROM_HERE, status);
474 return false; 745 return false;
475 } 746 }
476 return true; 747 return true;
477 } 748 }
478 749
479 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { 750 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) {
480 if (!Init(FAIL_ON_CORRUPTION)) 751 if (!Init(REPAIR_ON_CORRUPTION))
481 return false; 752 return false;
482 DCHECK(file_id); 753 DCHECK(file_id);
483 std::string id_string; 754 std::string id_string;
484 leveldb::Status status = 755 leveldb::Status status =
485 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); 756 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string);
486 if (status.ok()) { 757 if (status.ok()) {
487 if (!base::StringToInt64(id_string, file_id)) { 758 if (!base::StringToInt64(id_string, file_id)) {
488 LOG(ERROR) << "Hit database corruption!"; 759 LOG(ERROR) << "Hit database corruption!";
489 return false; 760 return false;
490 } 761 }
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
560 831
561 void FileSystemDirectoryDatabase::HandleError( 832 void FileSystemDirectoryDatabase::HandleError(
562 const tracked_objects::Location& from_here, 833 const tracked_objects::Location& from_here,
563 const leveldb::Status& status) { 834 const leveldb::Status& status) {
564 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " 835 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: "
565 << from_here.ToString() << " with error: " << status.ToString(); 836 << from_here.ToString() << " with error: " << status.ToString();
566 db_.reset(); 837 db_.reset();
567 } 838 }
568 839
569 } // namespace fileapi 840 } // namespace fileapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698