Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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(true)) |
|
jsbell
2012/03/09 23:10:11
The Boolean argument isn't very readable. Use an e
tzik
2012/03/10 00:16:55
Done.
| |
| 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 Loading... | |
| 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(true)) |
| 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(true)) |
| 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(true)) |
| 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 Loading... | |
| 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(true)) |
| 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(true)) |
| 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 Loading... | |
| 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(true)) |
| 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 Loading... | |
| 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(bool cleanup_if_corrupted) { |
| 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 (cleanup_if_corrupted && CheckIfDatabaseCorrupted(status)) { | |
| 433 LOG(WARNING) << "FileSystem API directory database is corrupted." | |
| 434 << " Attempting cleanup."; | |
| 435 if (leveldb::DestroyDB(path_, leveldb::Options()).ok()) { | |
| 436 LOG(WARNING) << "FileSystem API directory database cleanup completed." | |
| 437 << " Reopening."; | |
| 438 return Init(false); | |
| 439 } | |
| 440 LOG(WARNING) << "Failed to cleanup FileSystem API directory database."; | |
| 441 } | |
| 442 | |
| 443 return false; | |
| 444 } | |
| 445 | |
| 446 bool FileSystemDirectoryDatabase::CheckIfDatabaseCorrupted( | |
|
jsbell
2012/03/09 23:10:11
What is the benefit in factoring this method out?
tzik
2012/03/10 00:16:55
I'd initially thought, we have many thing to do he
| |
| 447 const leveldb::Status& status) const { | |
| 448 // TODO(tzik): Return status.code() == leveldb::Status::kCorruption, after | |
| 449 // leveldb roll. | |
| 450 return !status.ok(); | |
| 430 } | 451 } |
| 431 | 452 |
| 432 bool FileSystemDirectoryDatabase::StoreDefaultValues() { | 453 bool FileSystemDirectoryDatabase::StoreDefaultValues() { |
| 433 // Verify that this is a totally new database, and initialize it. | 454 // Verify that this is a totally new database, and initialize it. |
| 434 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); | 455 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); |
| 435 iter->SeekToFirst(); | 456 iter->SeekToFirst(); |
| 436 if (iter->Valid()) { // DB was not empty--we shouldn't have been called. | 457 if (iter->Valid()) { // DB was not empty--we shouldn't have been called. |
| 437 LOG(ERROR) << "File system origin database is corrupt!"; | 458 LOG(ERROR) << "File system origin database is corrupt!"; |
| 438 return false; | 459 return false; |
| 439 } | 460 } |
| 440 // This is always the first write into the database. If we ever add a | 461 // This is always the first write into the database. If we ever add a |
| 441 // version number, it should go in this transaction too. | 462 // version number, it should go in this transaction too. |
| 442 FileInfo root; | 463 FileInfo root; |
| 443 root.parent_id = 0; | 464 root.parent_id = 0; |
| 444 root.modification_time = base::Time::Now(); | 465 root.modification_time = base::Time::Now(); |
| 445 leveldb::WriteBatch batch; | 466 leveldb::WriteBatch batch; |
| 446 if (!AddFileInfoHelper(root, 0, &batch)) | 467 if (!AddFileInfoHelper(root, 0, &batch)) |
| 447 return false; | 468 return false; |
| 448 batch.Put(LastFileIdKey(), base::Int64ToString(0)); | 469 batch.Put(LastFileIdKey(), base::Int64ToString(0)); |
| 449 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); | 470 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); |
| 450 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 471 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 451 if (!status.ok()) { | 472 if (!status.ok()) { |
| 452 HandleError(FROM_HERE, status); | 473 HandleError(FROM_HERE, status); |
| 453 return false; | 474 return false; |
| 454 } | 475 } |
| 455 return true; | 476 return true; |
| 456 } | 477 } |
| 457 | 478 |
| 458 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { | 479 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { |
| 459 if (!Init()) | 480 if (!Init(true)) |
| 460 return false; | 481 return false; |
| 461 DCHECK(file_id); | 482 DCHECK(file_id); |
| 462 std::string id_string; | 483 std::string id_string; |
| 463 leveldb::Status status = | 484 leveldb::Status status = |
| 464 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); | 485 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); |
| 465 if (status.ok()) { | 486 if (status.ok()) { |
| 466 if (!base::StringToInt64(id_string, file_id)) { | 487 if (!base::StringToInt64(id_string, file_id)) { |
| 467 LOG(ERROR) << "Hit database corruption!"; | 488 LOG(ERROR) << "Hit database corruption!"; |
| 468 return false; | 489 return false; |
| 469 } | 490 } |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 539 | 560 |
| 540 void FileSystemDirectoryDatabase::HandleError( | 561 void FileSystemDirectoryDatabase::HandleError( |
| 541 const tracked_objects::Location& from_here, | 562 const tracked_objects::Location& from_here, |
| 542 leveldb::Status status) { | 563 leveldb::Status status) { |
| 543 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " | 564 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " |
| 544 << from_here.ToString() << " with error: " << status.ToString(); | 565 << from_here.ToString() << " with error: " << status.ToString(); |
| 545 db_.reset(); | 566 db_.reset(); |
| 546 } | 567 } |
| 547 | 568 |
| 548 } // namespace fileapi | 569 } // namespace fileapi |
| OLD | NEW |