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

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: '' 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 19 matching lines...) Expand all
46 fileapi::FileSystemDirectoryDatabase::FileInfo* info) { 47 fileapi::FileSystemDirectoryDatabase::FileInfo* info) {
47 PickleIterator iter(pickle); 48 PickleIterator iter(pickle);
48 std::string data_path; 49 std::string data_path;
49 std::string name; 50 std::string name;
50 int64 internal_time; 51 int64 internal_time;
51 52
52 if (pickle.ReadInt64(&iter, &info->parent_id) && 53 if (pickle.ReadInt64(&iter, &info->parent_id) &&
53 pickle.ReadString(&iter, &data_path) && 54 pickle.ReadString(&iter, &data_path) &&
54 pickle.ReadString(&iter, &name) && 55 pickle.ReadString(&iter, &name) &&
55 pickle.ReadInt64(&iter, &internal_time)) { 56 pickle.ReadInt64(&iter, &internal_time)) {
56
57 info->data_path = fileapi::StringToFilePath(data_path); 57 info->data_path = fileapi::StringToFilePath(data_path);
58 info->name = fileapi::StringToFilePath(name).value(); 58 info->name = fileapi::StringToFilePath(name).value();
59 info->modification_time = base::Time::FromInternalValue(internal_time); 59 info->modification_time = base::Time::FromInternalValue(internal_time);
60 return true; 60 return true;
61 } 61 }
62 LOG(ERROR) << "Pickle could not be digested!"; 62 LOG(ERROR) << "Pickle could not be digested!";
63 return false; 63 return false;
64 } 64 }
65 65
66 const FilePath::CharType kDirectoryDatabaseName[] = FILE_PATH_LITERAL("Paths"); 66 const FilePath::CharType kDirectoryDatabaseName[] = FILE_PATH_LITERAL("Paths");
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
100 100
101 const char* LastIntegerKey() { 101 const char* LastIntegerKey() {
102 return kLastIntegerKey; 102 return kLastIntegerKey;
103 } 103 }
104 104
105 std::string GetFileLookupKey( 105 std::string GetFileLookupKey(
106 fileapi::FileSystemDirectoryDatabase::FileId file_id) { 106 fileapi::FileSystemDirectoryDatabase::FileId file_id) {
107 return base::Int64ToString(file_id); 107 return base::Int64ToString(file_id);
108 } 108 }
109 109
110 // Assumptions:
111 // - Any database entry is one of:
112 // - ("CHILD_OF:|parent_id|:<name>", "|file_id|"),
113 // - ("LAST_FILE_ID", "|last_file_id|"),
114 // - ("LAST_INTEGER", "|last_integer|"),
115 // - ("|file_id|", "pickled FileInfo")
116 // where FileInfo has |parent_id|, |data_path|, |name| and
117 // |modification_time|,
118 // Constraints:
119 // - Each file in the database has unique backing file.
120 // - Each file in |filesystem_data_directory_| has a database entry.
121 // - Directory structure is tree, i.e. connected and acyclic.
122 class DatabaseCheckHelper {
123 public:
124 typedef fileapi::FileSystemDirectoryDatabase::FileId FileId;
125 typedef fileapi::FileSystemDirectoryDatabase::FileInfo FileInfo;
126
127 DatabaseCheckHelper(fileapi::FileSystemDirectoryDatabase* dir_db,
128 leveldb::DB* db,
129 const FilePath& path);
130
131 bool IsFileSystemConsistent() {
132 return IsDatabaseEmpty() ||
133 (ScanDatabase() && ScanDirectory() && ScanHierarchy());
134 }
135
136 private:
137 bool IsDatabaseEmpty();
138 // These 3 methods need to be called in the order. Each method requires its
139 // previous method finished successfully. They also require the database is
140 // not empty.
141 bool ScanDatabase();
142 bool ScanDirectory();
143 bool ScanHierarchy();
144
145 fileapi::FileSystemDirectoryDatabase* dir_db_;
146 leveldb::DB* db_;
147 FilePath path_;
148
149 std::set<FilePath> files_in_db_;
150
151 size_t num_directories_in_db_;
152 size_t num_files_in_db_;
153 size_t num_hierarchy_links_in_db_;
154
155 FileId last_file_id_;
156 FileId last_integer_;
157 };
158
159 DatabaseCheckHelper::DatabaseCheckHelper(
160 fileapi::FileSystemDirectoryDatabase* dir_db,
161 leveldb::DB* db,
162 const FilePath& path)
163 : dir_db_(dir_db), db_(db), path_(path),
164 num_directories_in_db_(0),
165 num_files_in_db_(0),
166 num_hierarchy_links_in_db_(0),
167 last_file_id_(-1), last_integer_(-1) {
168 DCHECK(dir_db_);
169 DCHECK(db_);
170 DCHECK(!path_.empty() && file_util::DirectoryExists(path_));
171 }
172
173 bool DatabaseCheckHelper::IsDatabaseEmpty() {
174 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
175 itr->SeekToFirst();
176 return !itr->Valid();
177 }
178
179 bool DatabaseCheckHelper::ScanDatabase() {
180 // Scans all database entries sequentially to verify each of them has unique
181 // backing file.
182 int64 max_file_id = -1;
183 std::set<FileId> file_ids;
184
185 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
186 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
187 std::string key = itr->key().ToString();
188 if (StartsWithASCII(key, kChildLookupPrefix, true)) {
189 // key: "CHILD_OF:<parent_id>:<name>"
190 // value: "<child_id>"
191 ++num_hierarchy_links_in_db_;
192 } else if (key == kLastFileIdKey) {
193 // key: "LAST_FILE_ID"
194 // value: "<last_file_id>"
195 if (last_file_id_ >= 0 ||
196 !base::StringToInt64(itr->value().ToString(), &last_file_id_))
197 return false;
198
199 if (last_file_id_ < 0)
200 return false;
201 } else if (key == kLastIntegerKey) {
202 // key: "LAST_INTEGER"
203 // value: "<last_integer>"
204 if (last_integer_ >= 0 ||
205 !base::StringToInt64(itr->value().ToString(), &last_integer_))
206 return false;
207 } else {
208 // key: "<entry_id>"
209 // value: "<pickled FileInfo>"
210 FileInfo file_info;
211 if (!FileInfoFromPickle(
212 Pickle(itr->value().data(), itr->value().size()), &file_info))
213 return false;
214
215 FileId file_id = -1;
216 if (!base::StringToInt64(key, &file_id) || file_id < 0)
217 return false;
218
219 if (max_file_id < file_id)
220 max_file_id = file_id;
221 if (!file_ids.insert(file_id).second)
222 return false;
223
224 if (file_info.is_directory()) {
225 ++num_directories_in_db_;
226 DCHECK(file_info.data_path.empty());
227 } else {
228 ++num_files_in_db_;
229
230 // Ensure any pair of file entry don't share their data_path.
231 if (!files_in_db_.insert(file_info.data_path).second)
232 return false;
233
234 // Ensure the backing file exists as a normal file.
235 base::PlatformFileInfo platform_file_info;
236 if (!file_util::GetFileInfo(
237 path_.Append(file_info.data_path), &platform_file_info) ||
238 platform_file_info.is_directory ||
239 platform_file_info.is_symbolic_link)
240 return false;
241 }
242 }
243 }
244
245 // TODO(tzik): Add constraint for |last_integer_| to avoid possible
246 // data path confliction on ObfuscatedFileUtil.
247 return max_file_id <= last_file_id_;
248 }
249
250 bool DatabaseCheckHelper::ScanDirectory() {
251 // Scans all local file system entries to verify each of them has a database
252 // entry.
253 const FilePath kExcludes[] = {
254 FilePath(kDirectoryDatabaseName),
255 FilePath(fileapi::FileSystemUsageCache::kUsageFileName),
256 };
257
258 // Any path in |pending_directories| is relative to |path_|.
259 std::stack<FilePath> pending_directories;
260 pending_directories.push(FilePath());
261
262 while (!pending_directories.empty()) {
263 FilePath dir_path = pending_directories.top();
264 pending_directories.pop();
265
266 file_util::FileEnumerator file_enum(
267 dir_path.empty() ? path_ : path_.Append(dir_path),
268 false /* recursive */,
269 static_cast<file_util::FileEnumerator::FileType>(
270 file_util::FileEnumerator::DIRECTORIES |
271 file_util::FileEnumerator::FILES));
272
273 FilePath absolute_file_path;
274 while (!(absolute_file_path = file_enum.Next()).empty()) {
275 file_util::FileEnumerator::FindInfo find_info;
276 file_enum.GetFindInfo(&find_info);
277
278 FilePath relative_file_path;
279 if (!path_.AppendRelativePath(absolute_file_path, &relative_file_path))
280 return false;
281
282 if (std::find(kExcludes, kExcludes + arraysize(kExcludes),
283 relative_file_path) != kExcludes + arraysize(kExcludes))
284 continue;
285
286 if (file_util::FileEnumerator::IsLink(find_info))
287 return false;
288
289 if (file_util::FileEnumerator::IsDirectory(find_info)) {
290 pending_directories.push(relative_file_path);
291 continue;
292 }
293
294 // Check if the file has a database entry.
295 std::set<FilePath>::iterator itr = files_in_db_.find(relative_file_path);
296 if (itr == files_in_db_.end())
297 return false;
298 files_in_db_.erase(itr);
299 }
300 }
301
302 return files_in_db_.empty();
303 }
304
305 bool DatabaseCheckHelper::ScanHierarchy() {
306 size_t visited_directories = 0;
307 size_t visited_files = 0;
308 size_t visited_links = 0;
309
310 std::stack<FileId> directories;
311 directories.push(0);
312
313 // Check if the root directory exists as a directory.
314 FileInfo file_info;
315 if (!dir_db_->GetFileInfo(0, &file_info))
316 return false;
317 if (file_info.parent_id != 0 ||
318 !file_info.is_directory())
319 return false;
320
321 while (!directories.empty()) {
322 ++visited_directories;
323 FileId dir_id = directories.top();
324 directories.pop();
325
326 std::vector<FileId> children;
327 if (!dir_db_->ListChildren(dir_id, &children))
328 return false;
329 for (std::vector<FileId>::iterator itr = children.begin();
330 itr != children.end();
331 ++itr) {
332 // Any directory must not have root directory as child.
333 if (!*itr)
334 return false;
335
336 // Check if the child knows the parent as its parent.
337 FileInfo file_info;
338 if (!dir_db_->GetFileInfo(*itr, &file_info))
339 return false;
340 if (file_info.parent_id != dir_id)
341 return false;
342
343 // Check if the parent knows the name of its child correctly.
344 FileId file_id;
345 if (!dir_db_->GetChildWithName(dir_id, file_info.name, &file_id) ||
346 file_id != *itr)
347 return false;
348
349 if (file_info.is_directory())
350 directories.push(*itr);
351 else
352 ++visited_files;
353 ++visited_links;
354 }
355 }
356
357 // Check if we've visited all database entries.
358 return num_directories_in_db_ == visited_directories &&
359 num_files_in_db_ == visited_files &&
360 num_hierarchy_links_in_db_ == visited_links;
361 }
362
110 } // namespace 363 } // namespace
111 364
112 namespace fileapi { 365 namespace fileapi {
113 366
114 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { 367 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) {
115 } 368 }
116 369
117 FileSystemDirectoryDatabase::FileInfo::~FileInfo() { 370 FileSystemDirectoryDatabase::FileInfo::~FileInfo() {
118 } 371 }
119 372
120 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase( 373 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase(
121 const FilePath& filesystem_data_directory) 374 const FilePath& filesystem_data_directory)
122 : filesystem_data_directory_(filesystem_data_directory) { 375 : filesystem_data_directory_(filesystem_data_directory) {
123 } 376 }
124 377
125 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { 378 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() {
126 } 379 }
127 380
128 bool FileSystemDirectoryDatabase::GetChildWithName( 381 bool FileSystemDirectoryDatabase::GetChildWithName(
129 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { 382 FileId parent_id, const FilePath::StringType& name, FileId* child_id) {
130 if (!Init(FAIL_ON_CORRUPTION)) 383 if (!Init(REPAIR_ON_CORRUPTION))
131 return false; 384 return false;
132 DCHECK(child_id); 385 DCHECK(child_id);
133 std::string child_key = GetChildLookupKey(parent_id, name); 386 std::string child_key = GetChildLookupKey(parent_id, name);
134 std::string child_id_string; 387 std::string child_id_string;
135 leveldb::Status status = 388 leveldb::Status status =
136 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 389 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
137 if (status.IsNotFound()) 390 if (status.IsNotFound())
138 return false; 391 return false;
139 if (status.ok()) { 392 if (status.ok()) {
140 if (!base::StringToInt64(child_id_string, child_id)) { 393 if (!base::StringToInt64(child_id_string, child_id)) {
(...skipping 20 matching lines...) Expand all
161 if (!GetChildWithName(local_id, name, &local_id)) 414 if (!GetChildWithName(local_id, name, &local_id))
162 return false; 415 return false;
163 } 416 }
164 *file_id = local_id; 417 *file_id = local_id;
165 return true; 418 return true;
166 } 419 }
167 420
168 bool FileSystemDirectoryDatabase::ListChildren( 421 bool FileSystemDirectoryDatabase::ListChildren(
169 FileId parent_id, std::vector<FileId>* children) { 422 FileId parent_id, std::vector<FileId>* children) {
170 // Check to add later: fail if parent is a file, at least in debug builds. 423 // Check to add later: fail if parent is a file, at least in debug builds.
171 if (!Init(FAIL_ON_CORRUPTION)) 424 if (!Init(REPAIR_ON_CORRUPTION))
172 return false; 425 return false;
173 DCHECK(children); 426 DCHECK(children);
174 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); 427 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id);
175 428
176 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 429 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
177 iter->Seek(child_key_prefix); 430 iter->Seek(child_key_prefix);
178 children->clear(); 431 children->clear();
179 while (iter->Valid() && 432 while (iter->Valid() &&
180 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { 433 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) {
181 std::string child_id_string = iter->value().ToString(); 434 std::string child_id_string = iter->value().ToString();
182 FileId child_id; 435 FileId child_id;
183 if (!base::StringToInt64(child_id_string, &child_id)) { 436 if (!base::StringToInt64(child_id_string, &child_id)) {
184 LOG(ERROR) << "Hit database corruption!"; 437 LOG(ERROR) << "Hit database corruption!";
185 return false; 438 return false;
186 } 439 }
187 children->push_back(child_id); 440 children->push_back(child_id);
188 iter->Next(); 441 iter->Next();
189 } 442 }
190 return true; 443 return true;
191 } 444 }
192 445
193 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { 446 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) {
194 if (!Init(FAIL_ON_CORRUPTION)) 447 if (!Init(REPAIR_ON_CORRUPTION))
195 return false; 448 return false;
196 DCHECK(info); 449 DCHECK(info);
197 std::string file_key = GetFileLookupKey(file_id); 450 std::string file_key = GetFileLookupKey(file_id);
198 std::string file_data_string; 451 std::string file_data_string;
199 leveldb::Status status = 452 leveldb::Status status =
200 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); 453 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string);
201 if (status.ok()) { 454 if (status.ok()) {
202 return FileInfoFromPickle( 455 return FileInfoFromPickle(
203 Pickle(file_data_string.data(), file_data_string.length()), info); 456 Pickle(file_data_string.data(), file_data_string.length()), info);
204 } 457 }
205 // Special-case the root, for databases that haven't been initialized yet. 458 // Special-case the root, for databases that haven't been initialized yet.
206 // Without this, a query for the root's file info, made before creating the 459 // Without this, a query for the root's file info, made before creating the
207 // first file in the database, will fail and confuse callers. 460 // first file in the database, will fail and confuse callers.
208 if (status.IsNotFound() && !file_id) { 461 if (status.IsNotFound() && !file_id) {
209 info->name = FilePath::StringType(); 462 info->name = FilePath::StringType();
210 info->data_path = FilePath(); 463 info->data_path = FilePath();
211 info->modification_time = base::Time::Now(); 464 info->modification_time = base::Time::Now();
212 info->parent_id = 0; 465 info->parent_id = 0;
213 return true; 466 return true;
214 } 467 }
215 HandleError(FROM_HERE, status); 468 HandleError(FROM_HERE, status);
216 return false; 469 return false;
217 } 470 }
218 471
219 bool FileSystemDirectoryDatabase::AddFileInfo( 472 bool FileSystemDirectoryDatabase::AddFileInfo(
220 const FileInfo& info, FileId* file_id) { 473 const FileInfo& info, FileId* file_id) {
221 if (!Init(FAIL_ON_CORRUPTION)) 474 if (!Init(REPAIR_ON_CORRUPTION))
222 return false; 475 return false;
223 DCHECK(file_id); 476 DCHECK(file_id);
224 std::string child_key = GetChildLookupKey(info.parent_id, info.name); 477 std::string child_key = GetChildLookupKey(info.parent_id, info.name);
225 std::string child_id_string; 478 std::string child_id_string;
226 leveldb::Status status = 479 leveldb::Status status =
227 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 480 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
228 if (status.ok()) { 481 if (status.ok()) {
229 LOG(ERROR) << "File exists already!"; 482 LOG(ERROR) << "File exists already!";
230 return false; 483 return false;
231 } 484 }
(...skipping 21 matching lines...) Expand all
253 status = db_->Write(leveldb::WriteOptions(), &batch); 506 status = db_->Write(leveldb::WriteOptions(), &batch);
254 if (!status.ok()) { 507 if (!status.ok()) {
255 HandleError(FROM_HERE, status); 508 HandleError(FROM_HERE, status);
256 return false; 509 return false;
257 } 510 }
258 *file_id = temp_id; 511 *file_id = temp_id;
259 return true; 512 return true;
260 } 513 }
261 514
262 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { 515 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) {
263 if (!Init(FAIL_ON_CORRUPTION)) 516 if (!Init(REPAIR_ON_CORRUPTION))
264 return false; 517 return false;
265 leveldb::WriteBatch batch; 518 leveldb::WriteBatch batch;
266 if (!RemoveFileInfoHelper(file_id, &batch)) 519 if (!RemoveFileInfoHelper(file_id, &batch))
267 return false; 520 return false;
268 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 521 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
269 if (!status.ok()) { 522 if (!status.ok()) {
270 HandleError(FROM_HERE, status); 523 HandleError(FROM_HERE, status);
271 return false; 524 return false;
272 } 525 }
273 return true; 526 return true;
274 } 527 }
275 528
276 bool FileSystemDirectoryDatabase::UpdateFileInfo( 529 bool FileSystemDirectoryDatabase::UpdateFileInfo(
277 FileId file_id, const FileInfo& new_info) { 530 FileId file_id, const FileInfo& new_info) {
278 // TODO: We should also check to see that this doesn't create a loop, but 531 // TODO: We should also check to see that this doesn't create a loop, but
279 // perhaps only in a debug build. 532 // perhaps only in a debug build.
280 if (!Init(FAIL_ON_CORRUPTION)) 533 if (!Init(REPAIR_ON_CORRUPTION))
281 return false; 534 return false;
282 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. 535 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB.
283 FileInfo old_info; 536 FileInfo old_info;
284 if (!GetFileInfo(file_id, &old_info)) 537 if (!GetFileInfo(file_id, &old_info))
285 return false; 538 return false;
286 if (old_info.parent_id != new_info.parent_id && 539 if (old_info.parent_id != new_info.parent_id &&
287 !VerifyIsDirectory(new_info.parent_id)) 540 !VerifyIsDirectory(new_info.parent_id))
288 return false; 541 return false;
289 if (old_info.parent_id != new_info.parent_id || 542 if (old_info.parent_id != new_info.parent_id ||
290 old_info.name != new_info.name) { 543 old_info.name != new_info.name) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 pickle.size())); 607 pickle.size()));
355 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 608 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
356 if (!status.ok()) { 609 if (!status.ok()) {
357 HandleError(FROM_HERE, status); 610 HandleError(FROM_HERE, status);
358 return false; 611 return false;
359 } 612 }
360 return true; 613 return true;
361 } 614 }
362 615
363 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { 616 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) {
364 if (!Init(FAIL_ON_CORRUPTION)) 617 if (!Init(REPAIR_ON_CORRUPTION))
365 return false; 618 return false;
366 DCHECK(next); 619 DCHECK(next);
367 std::string int_string; 620 std::string int_string;
368 leveldb::Status status = 621 leveldb::Status status =
369 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); 622 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string);
370 if (status.ok()) { 623 if (status.ok()) {
371 int64 temp; 624 int64 temp;
372 if (!base::StringToInt64(int_string, &temp)) { 625 if (!base::StringToInt64(int_string, &temp)) {
373 LOG(ERROR) << "Hit database corruption!"; 626 LOG(ERROR) << "Hit database corruption!";
374 return false; 627 return false;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 options.create_if_missing = true; 669 options.create_if_missing = true;
417 leveldb::DB* db; 670 leveldb::DB* db;
418 leveldb::Status status = leveldb::DB::Open(options, path, &db); 671 leveldb::Status status = leveldb::DB::Open(options, path, &db);
419 ReportInitStatus(status); 672 ReportInitStatus(status);
420 if (status.ok()) { 673 if (status.ok()) {
421 db_.reset(db); 674 db_.reset(db);
422 return true; 675 return true;
423 } 676 }
424 HandleError(FROM_HERE, status); 677 HandleError(FROM_HERE, status);
425 678
426 if (recovery_option == FAIL_ON_CORRUPTION) 679 switch (recovery_option) {
680 case FAIL_ON_CORRUPTION:
681 return false;
682 case REPAIR_ON_CORRUPTION:
683 if (RepairDatabase(path))
684 return true;
685 // fall through
686 case DELETE_ON_CORRUPTION:
687 if (!file_util::Delete(filesystem_data_directory_, true))
688 return false;
689 if (!file_util::CreateDirectory(filesystem_data_directory_))
690 return false;
691 return Init(FAIL_ON_CORRUPTION);
692 }
693
694 NOTREACHED();
695 return false;
696 }
697
698 bool FileSystemDirectoryDatabase::RepairDatabase(const std::string& db_path) {
699 DCHECK(!db_.get());
700 if (!leveldb::RepairDB(db_path, leveldb::Options()).ok())
427 return false; 701 return false;
702 if (!Init(FAIL_ON_CORRUPTION))
703 return false;
704 if (IsFileSystemConsistent())
705 return true;
706 db_.reset();
707 return false;
708 }
428 709
429 DCHECK_EQ(DELETE_ON_CORRUPTION, recovery_option); 710 bool FileSystemDirectoryDatabase::IsFileSystemConsistent() {
430 if (!file_util::Delete(filesystem_data_directory_, true)) 711 if (!Init(FAIL_ON_CORRUPTION))
431 return false; 712 return false;
432 if (!file_util::CreateDirectory(filesystem_data_directory_)) 713 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_);
433 return false; 714 return helper.IsFileSystemConsistent();
434 return Init(FAIL_ON_CORRUPTION);
435 } 715 }
436 716
437 void FileSystemDirectoryDatabase::ReportInitStatus( 717 void FileSystemDirectoryDatabase::ReportInitStatus(
438 const leveldb::Status& status) { 718 const leveldb::Status& status) {
439 base::Time now = base::Time::Now(); 719 base::Time now = base::Time::Now();
440 const base::TimeDelta minimum_interval = 720 const base::TimeDelta minimum_interval =
441 base::TimeDelta::FromHours(kMinimumReportIntervalHours); 721 base::TimeDelta::FromHours(kMinimumReportIntervalHours);
442 if (last_reported_time_ + minimum_interval >= now) 722 if (last_reported_time_ + minimum_interval >= now)
443 return; 723 return;
444 last_reported_time_ = now; 724 last_reported_time_ = now;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); 758 batch.Put(LastIntegerKey(), base::Int64ToString(-1));
479 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 759 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
480 if (!status.ok()) { 760 if (!status.ok()) {
481 HandleError(FROM_HERE, status); 761 HandleError(FROM_HERE, status);
482 return false; 762 return false;
483 } 763 }
484 return true; 764 return true;
485 } 765 }
486 766
487 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { 767 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) {
488 if (!Init(FAIL_ON_CORRUPTION)) 768 if (!Init(REPAIR_ON_CORRUPTION))
489 return false; 769 return false;
490 DCHECK(file_id); 770 DCHECK(file_id);
491 std::string id_string; 771 std::string id_string;
492 leveldb::Status status = 772 leveldb::Status status =
493 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); 773 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string);
494 if (status.ok()) { 774 if (status.ok()) {
495 if (!base::StringToInt64(id_string, file_id)) { 775 if (!base::StringToInt64(id_string, file_id)) {
496 LOG(ERROR) << "Hit database corruption!"; 776 LOG(ERROR) << "Hit database corruption!";
497 return false; 777 return false;
498 } 778 }
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
568 848
569 void FileSystemDirectoryDatabase::HandleError( 849 void FileSystemDirectoryDatabase::HandleError(
570 const tracked_objects::Location& from_here, 850 const tracked_objects::Location& from_here,
571 const leveldb::Status& status) { 851 const leveldb::Status& status) {
572 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " 852 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: "
573 << from_here.ToString() << " with error: " << status.ToString(); 853 << from_here.ToString() << " with error: " << status.ToString();
574 db_.reset(); 854 db_.reset();
575 } 855 }
576 856
577 } // namespace fileapi 857 } // namespace fileapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698