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

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

Issue 9663021: Add database recovery for FileSystemOriginDatabase (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: bool -> enum Created 8 years, 9 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/location.h" 9 #include "base/location.h"
10 #include "base/pickle.h" 10 #include "base/pickle.h"
11 #include "base/string_number_conversions.h" 11 #include "base/string_number_conversions.h"
12 #include "base/string_util.h" 12 #include "base/string_util.h"
13 #include "base/sys_string_conversions.h" 13 #include "base/sys_string_conversions.h"
14 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" 14 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
15 #include "third_party/leveldatabase/src/include/leveldb/status.h"
15 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
16 #include "webkit/fileapi/file_system_util.h" 17 #include "webkit/fileapi/file_system_util.h"
17 18
18 namespace { 19 namespace {
19 20
20 bool PickleFromFileInfo( 21 bool PickleFromFileInfo(
21 const fileapi::FileSystemDirectoryDatabase::FileInfo& info, 22 const fileapi::FileSystemDirectoryDatabase::FileInfo& info,
22 Pickle* pickle) { 23 Pickle* pickle) {
23 DCHECK(pickle); 24 DCHECK(pickle);
24 std::string data_path; 25 std::string data_path;
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 #elif defined(OS_WIN) 124 #elif defined(OS_WIN)
124 path_ = base::SysWideToUTF8(path.value()); 125 path_ = base::SysWideToUTF8(path.value());
125 #endif 126 #endif
126 } 127 }
127 128
128 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { 129 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() {
129 } 130 }
130 131
131 bool FileSystemDirectoryDatabase::GetChildWithName( 132 bool FileSystemDirectoryDatabase::GetChildWithName(
132 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { 133 FileId parent_id, const FilePath::StringType& name, FileId* child_id) {
133 if (!Init()) 134 if (!Init(REBUILD_ON_CORRUPTION))
134 return false; 135 return false;
135 DCHECK(child_id); 136 DCHECK(child_id);
136 std::string child_key = GetChildLookupKey(parent_id, name); 137 std::string child_key = GetChildLookupKey(parent_id, name);
137 std::string child_id_string; 138 std::string child_id_string;
138 leveldb::Status status = 139 leveldb::Status status =
139 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 140 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
140 if (status.IsNotFound()) 141 if (status.IsNotFound())
141 return false; 142 return false;
142 if (status.ok()) { 143 if (status.ok()) {
143 if (!base::StringToInt64(child_id_string, child_id)) { 144 if (!base::StringToInt64(child_id_string, child_id)) {
(...skipping 20 matching lines...) Expand all
164 if (!GetChildWithName(local_id, name, &local_id)) 165 if (!GetChildWithName(local_id, name, &local_id))
165 return false; 166 return false;
166 } 167 }
167 *file_id = local_id; 168 *file_id = local_id;
168 return true; 169 return true;
169 } 170 }
170 171
171 bool FileSystemDirectoryDatabase::ListChildren( 172 bool FileSystemDirectoryDatabase::ListChildren(
172 FileId parent_id, std::vector<FileId>* children) { 173 FileId parent_id, std::vector<FileId>* children) {
173 // Check to add later: fail if parent is a file, at least in debug builds. 174 // Check to add later: fail if parent is a file, at least in debug builds.
174 if (!Init()) 175 if (!Init(REBUILD_ON_CORRUPTION))
175 return false; 176 return false;
176 DCHECK(children); 177 DCHECK(children);
177 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); 178 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id);
178 179
179 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 180 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
180 iter->Seek(child_key_prefix); 181 iter->Seek(child_key_prefix);
181 children->clear(); 182 children->clear();
182 while (iter->Valid() && 183 while (iter->Valid() &&
183 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { 184 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) {
184 std::string child_id_string = iter->value().ToString(); 185 std::string child_id_string = iter->value().ToString();
185 FileId child_id; 186 FileId child_id;
186 if (!base::StringToInt64(child_id_string, &child_id)) { 187 if (!base::StringToInt64(child_id_string, &child_id)) {
187 LOG(ERROR) << "Hit database corruption!"; 188 LOG(ERROR) << "Hit database corruption!";
188 return false; 189 return false;
189 } 190 }
190 children->push_back(child_id); 191 children->push_back(child_id);
191 iter->Next(); 192 iter->Next();
192 } 193 }
193 return true; 194 return true;
194 } 195 }
195 196
196 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { 197 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) {
197 if (!Init()) 198 if (!Init(REBUILD_ON_CORRUPTION))
198 return false; 199 return false;
199 DCHECK(info); 200 DCHECK(info);
200 std::string file_key = GetFileLookupKey(file_id); 201 std::string file_key = GetFileLookupKey(file_id);
201 std::string file_data_string; 202 std::string file_data_string;
202 leveldb::Status status = 203 leveldb::Status status =
203 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); 204 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string);
204 if (status.ok()) { 205 if (status.ok()) {
205 return FileInfoFromPickle( 206 return FileInfoFromPickle(
206 Pickle(file_data_string.data(), file_data_string.length()), info); 207 Pickle(file_data_string.data(), file_data_string.length()), info);
207 } 208 }
208 // Special-case the root, for databases that haven't been initialized yet. 209 // Special-case the root, for databases that haven't been initialized yet.
209 // Without this, a query for the root's file info, made before creating the 210 // Without this, a query for the root's file info, made before creating the
210 // first file in the database, will fail and confuse callers. 211 // first file in the database, will fail and confuse callers.
211 if (status.IsNotFound() && !file_id) { 212 if (status.IsNotFound() && !file_id) {
212 info->name = FilePath::StringType(); 213 info->name = FilePath::StringType();
213 info->data_path = FilePath(); 214 info->data_path = FilePath();
214 info->modification_time = base::Time::Now(); 215 info->modification_time = base::Time::Now();
215 info->parent_id = 0; 216 info->parent_id = 0;
216 return true; 217 return true;
217 } 218 }
218 HandleError(FROM_HERE, status); 219 HandleError(FROM_HERE, status);
219 return false; 220 return false;
220 } 221 }
221 222
222 bool FileSystemDirectoryDatabase::AddFileInfo( 223 bool FileSystemDirectoryDatabase::AddFileInfo(
223 const FileInfo& info, FileId* file_id) { 224 const FileInfo& info, FileId* file_id) {
224 if (!Init()) 225 if (!Init(REBUILD_ON_CORRUPTION))
225 return false; 226 return false;
226 DCHECK(file_id); 227 DCHECK(file_id);
227 std::string child_key = GetChildLookupKey(info.parent_id, info.name); 228 std::string child_key = GetChildLookupKey(info.parent_id, info.name);
228 std::string child_id_string; 229 std::string child_id_string;
229 leveldb::Status status = 230 leveldb::Status status =
230 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); 231 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string);
231 if (status.ok()) { 232 if (status.ok()) {
232 LOG(ERROR) << "File exists already!"; 233 LOG(ERROR) << "File exists already!";
233 return false; 234 return false;
234 } 235 }
(...skipping 21 matching lines...) Expand all
256 status = db_->Write(leveldb::WriteOptions(), &batch); 257 status = db_->Write(leveldb::WriteOptions(), &batch);
257 if (!status.ok()) { 258 if (!status.ok()) {
258 HandleError(FROM_HERE, status); 259 HandleError(FROM_HERE, status);
259 return false; 260 return false;
260 } 261 }
261 *file_id = temp_id; 262 *file_id = temp_id;
262 return true; 263 return true;
263 } 264 }
264 265
265 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { 266 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) {
266 if (!Init()) 267 if (!Init(REBUILD_ON_CORRUPTION))
267 return false; 268 return false;
268 leveldb::WriteBatch batch; 269 leveldb::WriteBatch batch;
269 if (!RemoveFileInfoHelper(file_id, &batch)) 270 if (!RemoveFileInfoHelper(file_id, &batch))
270 return false; 271 return false;
271 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 272 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
272 if (!status.ok()) { 273 if (!status.ok()) {
273 HandleError(FROM_HERE, status); 274 HandleError(FROM_HERE, status);
274 return false; 275 return false;
275 } 276 }
276 return true; 277 return true;
277 } 278 }
278 279
279 bool FileSystemDirectoryDatabase::UpdateFileInfo( 280 bool FileSystemDirectoryDatabase::UpdateFileInfo(
280 FileId file_id, const FileInfo& new_info) { 281 FileId file_id, const FileInfo& new_info) {
281 // TODO: We should also check to see that this doesn't create a loop, but 282 // TODO: We should also check to see that this doesn't create a loop, but
282 // perhaps only in a debug build. 283 // perhaps only in a debug build.
283 if (!Init()) 284 if (!Init(REBUILD_ON_CORRUPTION))
284 return false; 285 return false;
285 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. 286 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB.
286 FileInfo old_info; 287 FileInfo old_info;
287 if (!GetFileInfo(file_id, &old_info)) 288 if (!GetFileInfo(file_id, &old_info))
288 return false; 289 return false;
289 if (old_info.parent_id != new_info.parent_id && 290 if (old_info.parent_id != new_info.parent_id &&
290 !VerifyIsDirectory(new_info.parent_id)) 291 !VerifyIsDirectory(new_info.parent_id))
291 return false; 292 return false;
292 if (old_info.parent_id != new_info.parent_id || 293 if (old_info.parent_id != new_info.parent_id ||
293 old_info.name != new_info.name) { 294 old_info.name != new_info.name) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 pickle.size())); 358 pickle.size()));
358 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 359 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
359 if (!status.ok()) { 360 if (!status.ok()) {
360 HandleError(FROM_HERE, status); 361 HandleError(FROM_HERE, status);
361 return false; 362 return false;
362 } 363 }
363 return true; 364 return true;
364 } 365 }
365 366
366 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { 367 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) {
367 if (!Init()) 368 if (!Init(REBUILD_ON_CORRUPTION))
368 return false; 369 return false;
369 DCHECK(next); 370 DCHECK(next);
370 std::string int_string; 371 std::string int_string;
371 leveldb::Status status = 372 leveldb::Status status =
372 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); 373 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string);
373 if (status.ok()) { 374 if (status.ok()) {
374 int64 temp; 375 int64 temp;
375 if (!base::StringToInt64(int_string, &temp)) { 376 if (!base::StringToInt64(int_string, &temp)) {
376 LOG(ERROR) << "Hit database corruption!"; 377 LOG(ERROR) << "Hit database corruption!";
377 return false; 378 return false;
(...skipping 28 matching lines...) Expand all
406 name = base::SysWideToUTF8(path.value()); 407 name = base::SysWideToUTF8(path.value());
407 #endif 408 #endif
408 leveldb::Status status = leveldb::DestroyDB(name, leveldb::Options()); 409 leveldb::Status status = leveldb::DestroyDB(name, leveldb::Options());
409 if (status.ok()) 410 if (status.ok())
410 return true; 411 return true;
411 LOG(WARNING) << "Failed to destroy a database with status " << 412 LOG(WARNING) << "Failed to destroy a database with status " <<
412 status.ToString(); 413 status.ToString();
413 return false; 414 return false;
414 } 415 }
415 416
416 bool FileSystemDirectoryDatabase::Init() { 417 bool FileSystemDirectoryDatabase::Init(RecoveringOption recovering_option) {
417 if (db_.get()) 418 if (db_.get())
418 return true; 419 return true;
419 420
420 leveldb::Options options; 421 leveldb::Options options;
421 options.create_if_missing = true; 422 options.create_if_missing = true;
422 leveldb::DB* db; 423 leveldb::DB* db;
423 leveldb::Status status = leveldb::DB::Open(options, path_, &db); 424 leveldb::Status status = leveldb::DB::Open(options, path_, &db);
424 if (status.ok()) { 425 // TODO(tzik): Collect status metrics here.
425 db_.reset(db); 426 if (status.ok()) {
426 return true; 427 db_.reset(db);
427 } 428 return true;
428 HandleError(FROM_HERE, status); 429 }
429 return false; 430 HandleError(FROM_HERE, status);
431
432 if (recovering_option == LEAVE_ON_CORRUPTION)
433 return false;
434
435 DCHECK_EQ(REBUILD_ON_CORRUPTION, recovering_option);
436 LOG(WARNING) << "FileSystem API directory database is corrupted."
437 << " Attempting cleanup.";
438 if (leveldb::DestroyDB(path_, leveldb::Options()).ok()) {
michaeln 2012/03/11 17:40:10 once we've lost the directory structure, have we o
ericu 2012/03/12 19:46:01 Yes. We've lost their paths, and there is no way
439 LOG(WARNING) << "FileSystem API directory database cleanup completed."
440 << " Reopening.";
441 return Init(LEAVE_ON_CORRUPTION);
jsbell 2012/03/10 00:54:50 What will status metrics (when added) look like if
tzik 2012/03/13 06:40:29 Maybe, we will collect separate metrics with recov
442 }
443 LOG(WARNING) << "Failed to cleanup FileSystem API directory database.";
444 return false;
430 } 445 }
431 446
432 bool FileSystemDirectoryDatabase::StoreDefaultValues() { 447 bool FileSystemDirectoryDatabase::StoreDefaultValues() {
433 // Verify that this is a totally new database, and initialize it. 448 // Verify that this is a totally new database, and initialize it.
434 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); 449 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
435 iter->SeekToFirst(); 450 iter->SeekToFirst();
436 if (iter->Valid()) { // DB was not empty--we shouldn't have been called. 451 if (iter->Valid()) { // DB was not empty--we shouldn't have been called.
437 LOG(ERROR) << "File system origin database is corrupt!"; 452 LOG(ERROR) << "File system origin database is corrupt!";
438 return false; 453 return false;
439 } 454 }
440 // This is always the first write into the database. If we ever add a 455 // This is always the first write into the database. If we ever add a
441 // version number, it should go in this transaction too. 456 // version number, it should go in this transaction too.
442 FileInfo root; 457 FileInfo root;
443 root.parent_id = 0; 458 root.parent_id = 0;
444 root.modification_time = base::Time::Now(); 459 root.modification_time = base::Time::Now();
445 leveldb::WriteBatch batch; 460 leveldb::WriteBatch batch;
446 if (!AddFileInfoHelper(root, 0, &batch)) 461 if (!AddFileInfoHelper(root, 0, &batch))
447 return false; 462 return false;
448 batch.Put(LastFileIdKey(), base::Int64ToString(0)); 463 batch.Put(LastFileIdKey(), base::Int64ToString(0));
449 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); 464 batch.Put(LastIntegerKey(), base::Int64ToString(-1));
450 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 465 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
451 if (!status.ok()) { 466 if (!status.ok()) {
452 HandleError(FROM_HERE, status); 467 HandleError(FROM_HERE, status);
453 return false; 468 return false;
454 } 469 }
455 return true; 470 return true;
456 } 471 }
457 472
458 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { 473 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) {
459 if (!Init()) 474 if (!Init(REBUILD_ON_CORRUPTION))
460 return false; 475 return false;
461 DCHECK(file_id); 476 DCHECK(file_id);
462 std::string id_string; 477 std::string id_string;
463 leveldb::Status status = 478 leveldb::Status status =
464 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); 479 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string);
465 if (status.ok()) { 480 if (status.ok()) {
466 if (!base::StringToInt64(id_string, file_id)) { 481 if (!base::StringToInt64(id_string, file_id)) {
467 LOG(ERROR) << "Hit database corruption!"; 482 LOG(ERROR) << "Hit database corruption!";
468 return false; 483 return false;
469 } 484 }
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
539 554
540 void FileSystemDirectoryDatabase::HandleError( 555 void FileSystemDirectoryDatabase::HandleError(
541 const tracked_objects::Location& from_here, 556 const tracked_objects::Location& from_here,
542 leveldb::Status status) { 557 leveldb::Status status) {
543 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " 558 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: "
544 << from_here.ToString() << " with error: " << status.ToString(); 559 << from_here.ToString() << " with error: " << status.ToString();
545 db_.reset(); 560 db_.reset();
546 } 561 }
547 562
548 } // namespace fileapi 563 } // namespace fileapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698