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 <algorithm> | |
| 7 #include <math.h> | 8 #include <math.h> |
| 8 | 9 |
| 9 #include "base/file_util.h" | 10 #include "base/file_util.h" |
| 10 #include "base/location.h" | 11 #include "base/location.h" |
| 11 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 12 #include "base/pickle.h" | 13 #include "base/pickle.h" |
| 13 #include "base/string_number_conversions.h" | 14 #include "base/string_number_conversions.h" |
| 14 #include "base/string_util.h" | 15 #include "base/string_util.h" |
| 15 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 16 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| 16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| 18 #include "webkit/fileapi/file_system_usage_cache.h" | |
| 17 #include "webkit/fileapi/file_system_util.h" | 19 #include "webkit/fileapi/file_system_util.h" |
| 18 | 20 |
| 19 namespace { | 21 namespace { |
| 20 | 22 |
| 21 bool PickleFromFileInfo( | 23 bool PickleFromFileInfo( |
| 22 const fileapi::FileSystemDirectoryDatabase::FileInfo& info, | 24 const fileapi::FileSystemDirectoryDatabase::FileInfo& info, |
| 23 Pickle* pickle) { | 25 Pickle* pickle) { |
| 24 DCHECK(pickle); | 26 DCHECK(pickle); |
| 25 std::string data_path; | 27 std::string data_path; |
| 26 // Round off here to match the behavior of the filesystem on real files. | 28 // Round off here to match the behavior of the filesystem on real files. |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 | 101 |
| 100 const char* LastIntegerKey() { | 102 const char* LastIntegerKey() { |
| 101 return kLastIntegerKey; | 103 return kLastIntegerKey; |
| 102 } | 104 } |
| 103 | 105 |
| 104 std::string GetFileLookupKey( | 106 std::string GetFileLookupKey( |
| 105 fileapi::FileSystemDirectoryDatabase::FileId file_id) { | 107 fileapi::FileSystemDirectoryDatabase::FileId file_id) { |
| 106 return base::Int64ToString(file_id); | 108 return base::Int64ToString(file_id); |
| 107 } | 109 } |
| 108 | 110 |
| 111 // Assumptions: | |
| 112 // - Any database entry is one of: | |
| 113 // - ("CHILD_OF:|parent_id|:<name>", "|file_id|"), | |
| 114 // - ("LAST_FILE_ID", "|last_file_id|"), | |
| 115 // - ("LAST_INTEGER", "|last_integer|"), | |
| 116 // - ("|file_id|", "pickled FileInfo") | |
| 117 // where FileInfo has |parent_id|, |data_path|, |name| and | |
| 118 // |modification_time|, | |
| 119 // Constraints: | |
| 120 // - Each file in the database has unique backing file. | |
| 121 // - Each file in |filesystem_data_directory_| has a database entry. | |
| 122 // - Directory structure is tree, i.e. connected and acyclic. | |
| 123 class DatabaseCheckHelper { | |
| 124 public: | |
| 125 typedef fileapi::FileSystemDirectoryDatabase::FileId FileId; | |
| 126 typedef fileapi::FileSystemDirectoryDatabase::FileInfo FileInfo; | |
| 127 | |
| 128 DatabaseCheckHelper(fileapi::FileSystemDirectoryDatabase* dir_db, | |
| 129 leveldb::DB* db, | |
| 130 const FilePath& path); | |
| 131 | |
| 132 bool IsFileSystemConsistent() { | |
| 133 return IsDatabaseEmpty() || | |
| 134 (ScanDatabase() && ScanDirectory() && ScanHierarchy()); | |
| 135 } | |
| 136 | |
| 137 private: | |
| 138 bool IsDatabaseEmpty(); | |
| 139 // These 3 methods need to be called in the order. Each method requires its | |
| 140 // previous method finished successfully. They also require the database is | |
| 141 // not empty. | |
| 142 bool ScanDatabase(); | |
| 143 bool ScanDirectory(); | |
| 144 bool ScanHierarchy(); | |
| 145 | |
| 146 fileapi::FileSystemDirectoryDatabase* dir_db_; | |
| 147 leveldb::DB* db_; | |
| 148 FilePath path_; | |
| 149 | |
| 150 std::set<FilePath> files_in_db_; | |
| 151 | |
| 152 size_t num_directories_in_db_; | |
| 153 size_t num_files_in_db_; | |
| 154 size_t num_hierarchy_links_in_db_; | |
| 155 | |
| 156 FileId last_file_id_; | |
| 157 FileId last_integer_; | |
| 158 }; | |
| 159 | |
| 160 DatabaseCheckHelper::DatabaseCheckHelper( | |
| 161 fileapi::FileSystemDirectoryDatabase* dir_db, | |
| 162 leveldb::DB* db, | |
| 163 const FilePath& path) | |
| 164 : dir_db_(dir_db), db_(db), path_(path), | |
| 165 num_directories_in_db_(0), | |
| 166 num_files_in_db_(0), | |
| 167 num_hierarchy_links_in_db_(0), | |
| 168 last_file_id_(-1), last_integer_(-1) { | |
| 169 DCHECK(dir_db_); | |
| 170 DCHECK(db_); | |
| 171 DCHECK(!path_.empty() && file_util::DirectoryExists(path_)); | |
| 172 } | |
| 173 | |
| 174 bool DatabaseCheckHelper::IsDatabaseEmpty() { | |
| 175 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
| 176 itr->SeekToFirst(); | |
| 177 return !itr->Valid(); | |
| 178 } | |
| 179 | |
| 180 bool DatabaseCheckHelper::ScanDatabase() { | |
| 181 // Scans all database entries sequentially to verify each of them has unique | |
| 182 // backing file. | |
| 183 int64 max_file_id = -1; | |
| 184 std::set<FileId> file_ids; | |
| 185 | |
| 186 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
| 187 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) { | |
| 188 std::string key = itr->key().ToString(); | |
| 189 if (StartsWithASCII(key, kChildLookupPrefix, true)) { | |
| 190 // key: "CHILD_OF:<parent_id>:<name>" | |
| 191 // value: "<child_id>" | |
| 192 ++num_hierarchy_links_in_db_; | |
| 193 } else if (key == kLastFileIdKey) { | |
| 194 // key: "LAST_FILE_ID" | |
| 195 // value: "<last_file_id>" | |
| 196 if (last_file_id_ >= 0 || | |
| 197 !base::StringToInt64(itr->value().ToString(), &last_file_id_)) | |
| 198 return false; | |
| 199 | |
| 200 if (last_file_id_ < 0) | |
| 201 return false; | |
| 202 } else if (key == kLastIntegerKey) { | |
| 203 // key: "LAST_INTEGER" | |
| 204 // value: "<last_integer>" | |
| 205 if (last_integer_ >= 0 || | |
| 206 !base::StringToInt64(itr->value().ToString(), &last_integer_)) | |
| 207 return false; | |
| 208 } else { | |
| 209 // key: "<entry_id>" | |
| 210 // value: "<pickled FileInfo>" | |
| 211 FileInfo file_info; | |
| 212 if (!FileInfoFromPickle( | |
| 213 Pickle(itr->value().data(), itr->value().size()), &file_info)) | |
| 214 return false; | |
| 215 | |
| 216 FileId file_id = -1; | |
| 217 if (!base::StringToInt64(key, &file_id) || file_id < 0) | |
| 218 return false; | |
| 219 | |
| 220 if (max_file_id < file_id) | |
| 221 max_file_id = file_id; | |
| 222 if (!file_ids.insert(file_id).second) | |
| 223 return false; | |
| 224 | |
| 225 if (file_info.is_directory()) { | |
| 226 ++num_directories_in_db_; | |
| 227 DCHECK(file_info.data_path.empty()); | |
| 228 } else { | |
| 229 // Ensure any pair of file entry don't share their data_path. | |
| 230 if (!files_in_db_.insert(file_info.data_path).second) | |
| 231 return false; | |
| 232 | |
| 233 // Ensure the backing file exists as a normal file. | |
| 234 base::PlatformFileInfo platform_file_info; | |
| 235 if (!file_util::GetFileInfo( | |
| 236 path_.Append(file_info.data_path), &platform_file_info) || | |
| 237 platform_file_info.is_directory || | |
| 238 platform_file_info.is_symbolic_link) { | |
| 239 // leveldb::Iterator iterates a snapshot of the database. | |
| 240 // So even after RemoveFileInfo() call, we'll visit hierarchy link | |
| 241 // from |parent_id| to |file_id|. | |
| 242 if (!dir_db_->RemoveFileInfo(file_id)) | |
| 243 return false; | |
| 244 --num_hierarchy_links_in_db_; | |
| 245 files_in_db_.erase(file_info.data_path); | |
| 246 } else { | |
| 247 ++num_files_in_db_; | |
| 248 } | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 // TODO(tzik): Add constraint for |last_integer_| to avoid possible | |
| 254 // data path confliction on ObfuscatedFileUtil. | |
| 255 return max_file_id <= last_file_id_; | |
| 256 } | |
| 257 | |
| 258 bool DatabaseCheckHelper::ScanDirectory() { | |
| 259 // Scans all local file system entries to verify each of them has a database | |
| 260 // entry. | |
| 261 const FilePath kExcludes[] = { | |
| 262 FilePath(kDirectoryDatabaseName), | |
| 263 FilePath(fileapi::FileSystemUsageCache::kUsageFileName), | |
| 264 }; | |
| 265 | |
| 266 // Any path in |pending_directories| is relative to |path_|. | |
| 267 std::stack<FilePath> pending_directories; | |
| 268 pending_directories.push(FilePath()); | |
| 269 | |
| 270 while (!pending_directories.empty()) { | |
| 271 FilePath dir_path = pending_directories.top(); | |
| 272 pending_directories.pop(); | |
| 273 | |
| 274 file_util::FileEnumerator file_enum( | |
| 275 dir_path.empty() ? path_ : path_.Append(dir_path), | |
| 276 false /* recursive */, | |
| 277 static_cast<file_util::FileEnumerator::FileType>( | |
| 278 file_util::FileEnumerator::DIRECTORIES | | |
| 279 file_util::FileEnumerator::FILES)); | |
| 280 | |
| 281 FilePath absolute_file_path; | |
| 282 while (!(absolute_file_path = file_enum.Next()).empty()) { | |
| 283 file_util::FileEnumerator::FindInfo find_info; | |
| 284 file_enum.GetFindInfo(&find_info); | |
| 285 | |
| 286 FilePath relative_file_path; | |
| 287 if (!path_.AppendRelativePath(absolute_file_path, &relative_file_path)) | |
| 288 return false; | |
| 289 | |
| 290 if (std::find(kExcludes, kExcludes + arraysize(kExcludes), | |
| 291 relative_file_path) != kExcludes + arraysize(kExcludes)) | |
| 292 continue; | |
| 293 | |
| 294 if (file_util::FileEnumerator::IsLink(find_info)) | |
| 295 return false; | |
|
kinuko
2012/04/11 10:58:49
Should we simply delete this..?
tzik
2012/04/11 13:39:06
Looks able to erase whole line.
If the link have b
| |
| 296 | |
| 297 if (file_util::FileEnumerator::IsDirectory(find_info)) { | |
| 298 pending_directories.push(relative_file_path); | |
| 299 continue; | |
| 300 } | |
| 301 | |
| 302 // Check if the file has a database entry. | |
| 303 std::set<FilePath>::iterator itr = files_in_db_.find(relative_file_path); | |
| 304 if (itr == files_in_db_.end()) { | |
| 305 if (!file_util::Delete(absolute_file_path, false)) | |
| 306 return false; | |
| 307 } else { | |
| 308 files_in_db_.erase(itr); | |
| 309 } | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 return files_in_db_.empty(); | |
| 314 } | |
| 315 | |
| 316 bool DatabaseCheckHelper::ScanHierarchy() { | |
| 317 size_t visited_directories = 0; | |
| 318 size_t visited_files = 0; | |
| 319 size_t visited_links = 0; | |
| 320 | |
| 321 std::stack<FileId> directories; | |
| 322 directories.push(0); | |
| 323 | |
| 324 // Check if the root directory exists as a directory. | |
| 325 FileInfo file_info; | |
| 326 if (!dir_db_->GetFileInfo(0, &file_info)) | |
| 327 return false; | |
| 328 if (file_info.parent_id != 0 || | |
| 329 !file_info.is_directory()) | |
| 330 return false; | |
| 331 | |
| 332 while (!directories.empty()) { | |
| 333 ++visited_directories; | |
| 334 FileId dir_id = directories.top(); | |
| 335 directories.pop(); | |
| 336 | |
| 337 std::vector<FileId> children; | |
| 338 if (!dir_db_->ListChildren(dir_id, &children)) | |
| 339 return false; | |
| 340 for (std::vector<FileId>::iterator itr = children.begin(); | |
| 341 itr != children.end(); | |
| 342 ++itr) { | |
| 343 // Any directory must not have root directory as child. | |
| 344 if (!*itr) | |
| 345 return false; | |
| 346 | |
| 347 // Check if the child knows the parent as its parent. | |
| 348 FileInfo file_info; | |
| 349 if (!dir_db_->GetFileInfo(*itr, &file_info)) | |
| 350 return false; | |
| 351 if (file_info.parent_id != dir_id) | |
| 352 return false; | |
| 353 | |
| 354 // Check if the parent knows the name of its child correctly. | |
| 355 FileId file_id; | |
| 356 if (!dir_db_->GetChildWithName(dir_id, file_info.name, &file_id) || | |
| 357 file_id != *itr) | |
| 358 return false; | |
| 359 | |
| 360 if (file_info.is_directory()) | |
| 361 directories.push(*itr); | |
| 362 else | |
| 363 ++visited_files; | |
| 364 ++visited_links; | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 // Check if we've visited all database entries. | |
| 369 return num_directories_in_db_ == visited_directories && | |
| 370 num_files_in_db_ == visited_files && | |
| 371 num_hierarchy_links_in_db_ == visited_links; | |
| 372 } | |
| 373 | |
| 109 } // namespace | 374 } // namespace |
| 110 | 375 |
| 111 namespace fileapi { | 376 namespace fileapi { |
| 112 | 377 |
| 113 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { | 378 FileSystemDirectoryDatabase::FileInfo::FileInfo() : parent_id(0) { |
| 114 } | 379 } |
| 115 | 380 |
| 116 FileSystemDirectoryDatabase::FileInfo::~FileInfo() { | 381 FileSystemDirectoryDatabase::FileInfo::~FileInfo() { |
| 117 } | 382 } |
| 118 | 383 |
| 119 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase( | 384 FileSystemDirectoryDatabase::FileSystemDirectoryDatabase( |
| 120 const FilePath& filesystem_data_directory) | 385 const FilePath& filesystem_data_directory) |
| 121 : filesystem_data_directory_(filesystem_data_directory) { | 386 : filesystem_data_directory_(filesystem_data_directory) { |
| 122 } | 387 } |
| 123 | 388 |
| 124 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { | 389 FileSystemDirectoryDatabase::~FileSystemDirectoryDatabase() { |
| 125 } | 390 } |
| 126 | 391 |
| 127 bool FileSystemDirectoryDatabase::GetChildWithName( | 392 bool FileSystemDirectoryDatabase::GetChildWithName( |
| 128 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { | 393 FileId parent_id, const FilePath::StringType& name, FileId* child_id) { |
| 129 if (!Init(FAIL_ON_CORRUPTION)) | 394 if (!Init(REPAIR_ON_CORRUPTION)) |
| 130 return false; | 395 return false; |
| 131 DCHECK(child_id); | 396 DCHECK(child_id); |
| 132 std::string child_key = GetChildLookupKey(parent_id, name); | 397 std::string child_key = GetChildLookupKey(parent_id, name); |
| 133 std::string child_id_string; | 398 std::string child_id_string; |
| 134 leveldb::Status status = | 399 leveldb::Status status = |
| 135 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); | 400 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); |
| 136 if (status.IsNotFound()) | 401 if (status.IsNotFound()) |
| 137 return false; | 402 return false; |
| 138 if (status.ok()) { | 403 if (status.ok()) { |
| 139 if (!base::StringToInt64(child_id_string, child_id)) { | 404 if (!base::StringToInt64(child_id_string, child_id)) { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 160 if (!GetChildWithName(local_id, name, &local_id)) | 425 if (!GetChildWithName(local_id, name, &local_id)) |
| 161 return false; | 426 return false; |
| 162 } | 427 } |
| 163 *file_id = local_id; | 428 *file_id = local_id; |
| 164 return true; | 429 return true; |
| 165 } | 430 } |
| 166 | 431 |
| 167 bool FileSystemDirectoryDatabase::ListChildren( | 432 bool FileSystemDirectoryDatabase::ListChildren( |
| 168 FileId parent_id, std::vector<FileId>* children) { | 433 FileId parent_id, std::vector<FileId>* children) { |
| 169 // Check to add later: fail if parent is a file, at least in debug builds. | 434 // Check to add later: fail if parent is a file, at least in debug builds. |
| 170 if (!Init(FAIL_ON_CORRUPTION)) | 435 if (!Init(REPAIR_ON_CORRUPTION)) |
| 171 return false; | 436 return false; |
| 172 DCHECK(children); | 437 DCHECK(children); |
| 173 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); | 438 std::string child_key_prefix = GetChildListingKeyPrefix(parent_id); |
| 174 | 439 |
| 175 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); | 440 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions())); |
| 176 iter->Seek(child_key_prefix); | 441 iter->Seek(child_key_prefix); |
| 177 children->clear(); | 442 children->clear(); |
| 178 while (iter->Valid() && | 443 while (iter->Valid() && |
| 179 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { | 444 StartsWithASCII(iter->key().ToString(), child_key_prefix, true)) { |
| 180 std::string child_id_string = iter->value().ToString(); | 445 std::string child_id_string = iter->value().ToString(); |
| 181 FileId child_id; | 446 FileId child_id; |
| 182 if (!base::StringToInt64(child_id_string, &child_id)) { | 447 if (!base::StringToInt64(child_id_string, &child_id)) { |
| 183 LOG(ERROR) << "Hit database corruption!"; | 448 LOG(ERROR) << "Hit database corruption!"; |
| 184 return false; | 449 return false; |
| 185 } | 450 } |
| 186 children->push_back(child_id); | 451 children->push_back(child_id); |
| 187 iter->Next(); | 452 iter->Next(); |
| 188 } | 453 } |
| 189 return true; | 454 return true; |
| 190 } | 455 } |
| 191 | 456 |
| 192 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { | 457 bool FileSystemDirectoryDatabase::GetFileInfo(FileId file_id, FileInfo* info) { |
| 193 if (!Init(FAIL_ON_CORRUPTION)) | 458 if (!Init(REPAIR_ON_CORRUPTION)) |
| 194 return false; | 459 return false; |
| 195 DCHECK(info); | 460 DCHECK(info); |
| 196 std::string file_key = GetFileLookupKey(file_id); | 461 std::string file_key = GetFileLookupKey(file_id); |
| 197 std::string file_data_string; | 462 std::string file_data_string; |
| 198 leveldb::Status status = | 463 leveldb::Status status = |
| 199 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); | 464 db_->Get(leveldb::ReadOptions(), file_key, &file_data_string); |
| 200 if (status.ok()) { | 465 if (status.ok()) { |
| 201 return FileInfoFromPickle( | 466 return FileInfoFromPickle( |
| 202 Pickle(file_data_string.data(), file_data_string.length()), info); | 467 Pickle(file_data_string.data(), file_data_string.length()), info); |
| 203 } | 468 } |
| 204 // Special-case the root, for databases that haven't been initialized yet. | 469 // Special-case the root, for databases that haven't been initialized yet. |
| 205 // Without this, a query for the root's file info, made before creating the | 470 // Without this, a query for the root's file info, made before creating the |
| 206 // first file in the database, will fail and confuse callers. | 471 // first file in the database, will fail and confuse callers. |
| 207 if (status.IsNotFound() && !file_id) { | 472 if (status.IsNotFound() && !file_id) { |
| 208 info->name = FilePath::StringType(); | 473 info->name = FilePath::StringType(); |
| 209 info->data_path = FilePath(); | 474 info->data_path = FilePath(); |
| 210 info->modification_time = base::Time::Now(); | 475 info->modification_time = base::Time::Now(); |
| 211 info->parent_id = 0; | 476 info->parent_id = 0; |
| 212 return true; | 477 return true; |
| 213 } | 478 } |
| 214 HandleError(FROM_HERE, status); | 479 HandleError(FROM_HERE, status); |
| 215 return false; | 480 return false; |
| 216 } | 481 } |
| 217 | 482 |
| 218 bool FileSystemDirectoryDatabase::AddFileInfo( | 483 bool FileSystemDirectoryDatabase::AddFileInfo( |
| 219 const FileInfo& info, FileId* file_id) { | 484 const FileInfo& info, FileId* file_id) { |
| 220 if (!Init(FAIL_ON_CORRUPTION)) | 485 if (!Init(REPAIR_ON_CORRUPTION)) |
| 221 return false; | 486 return false; |
| 222 DCHECK(file_id); | 487 DCHECK(file_id); |
| 223 std::string child_key = GetChildLookupKey(info.parent_id, info.name); | 488 std::string child_key = GetChildLookupKey(info.parent_id, info.name); |
| 224 std::string child_id_string; | 489 std::string child_id_string; |
| 225 leveldb::Status status = | 490 leveldb::Status status = |
| 226 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); | 491 db_->Get(leveldb::ReadOptions(), child_key, &child_id_string); |
| 227 if (status.ok()) { | 492 if (status.ok()) { |
| 228 LOG(ERROR) << "File exists already!"; | 493 LOG(ERROR) << "File exists already!"; |
| 229 return false; | 494 return false; |
| 230 } | 495 } |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 252 status = db_->Write(leveldb::WriteOptions(), &batch); | 517 status = db_->Write(leveldb::WriteOptions(), &batch); |
| 253 if (!status.ok()) { | 518 if (!status.ok()) { |
| 254 HandleError(FROM_HERE, status); | 519 HandleError(FROM_HERE, status); |
| 255 return false; | 520 return false; |
| 256 } | 521 } |
| 257 *file_id = temp_id; | 522 *file_id = temp_id; |
| 258 return true; | 523 return true; |
| 259 } | 524 } |
| 260 | 525 |
| 261 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { | 526 bool FileSystemDirectoryDatabase::RemoveFileInfo(FileId file_id) { |
| 262 if (!Init(FAIL_ON_CORRUPTION)) | 527 if (!Init(REPAIR_ON_CORRUPTION)) |
| 263 return false; | 528 return false; |
| 264 leveldb::WriteBatch batch; | 529 leveldb::WriteBatch batch; |
| 265 if (!RemoveFileInfoHelper(file_id, &batch)) | 530 if (!RemoveFileInfoHelper(file_id, &batch)) |
| 266 return false; | 531 return false; |
| 267 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 532 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 268 if (!status.ok()) { | 533 if (!status.ok()) { |
| 269 HandleError(FROM_HERE, status); | 534 HandleError(FROM_HERE, status); |
| 270 return false; | 535 return false; |
| 271 } | 536 } |
| 272 return true; | 537 return true; |
| 273 } | 538 } |
| 274 | 539 |
| 275 bool FileSystemDirectoryDatabase::UpdateFileInfo( | 540 bool FileSystemDirectoryDatabase::UpdateFileInfo( |
| 276 FileId file_id, const FileInfo& new_info) { | 541 FileId file_id, const FileInfo& new_info) { |
| 277 // TODO: We should also check to see that this doesn't create a loop, but | 542 // TODO: We should also check to see that this doesn't create a loop, but |
| 278 // perhaps only in a debug build. | 543 // perhaps only in a debug build. |
| 279 if (!Init(FAIL_ON_CORRUPTION)) | 544 if (!Init(REPAIR_ON_CORRUPTION)) |
| 280 return false; | 545 return false; |
| 281 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. | 546 DCHECK(file_id); // You can't remove the root, ever. Just delete the DB. |
| 282 FileInfo old_info; | 547 FileInfo old_info; |
| 283 if (!GetFileInfo(file_id, &old_info)) | 548 if (!GetFileInfo(file_id, &old_info)) |
| 284 return false; | 549 return false; |
| 285 if (old_info.parent_id != new_info.parent_id && | 550 if (old_info.parent_id != new_info.parent_id && |
| 286 !VerifyIsDirectory(new_info.parent_id)) | 551 !VerifyIsDirectory(new_info.parent_id)) |
| 287 return false; | 552 return false; |
| 288 if (old_info.parent_id != new_info.parent_id || | 553 if (old_info.parent_id != new_info.parent_id || |
| 289 old_info.name != new_info.name) { | 554 old_info.name != new_info.name) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 353 pickle.size())); | 618 pickle.size())); |
| 354 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 619 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 355 if (!status.ok()) { | 620 if (!status.ok()) { |
| 356 HandleError(FROM_HERE, status); | 621 HandleError(FROM_HERE, status); |
| 357 return false; | 622 return false; |
| 358 } | 623 } |
| 359 return true; | 624 return true; |
| 360 } | 625 } |
| 361 | 626 |
| 362 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { | 627 bool FileSystemDirectoryDatabase::GetNextInteger(int64* next) { |
| 363 if (!Init(FAIL_ON_CORRUPTION)) | 628 if (!Init(REPAIR_ON_CORRUPTION)) |
| 364 return false; | 629 return false; |
| 365 DCHECK(next); | 630 DCHECK(next); |
| 366 std::string int_string; | 631 std::string int_string; |
| 367 leveldb::Status status = | 632 leveldb::Status status = |
| 368 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); | 633 db_->Get(leveldb::ReadOptions(), LastIntegerKey(), &int_string); |
| 369 if (status.ok()) { | 634 if (status.ok()) { |
| 370 int64 temp; | 635 int64 temp; |
| 371 if (!base::StringToInt64(int_string, &temp)) { | 636 if (!base::StringToInt64(int_string, &temp)) { |
| 372 LOG(ERROR) << "Hit database corruption!"; | 637 LOG(ERROR) << "Hit database corruption!"; |
| 373 return false; | 638 return false; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 415 options.create_if_missing = true; | 680 options.create_if_missing = true; |
| 416 leveldb::DB* db; | 681 leveldb::DB* db; |
| 417 leveldb::Status status = leveldb::DB::Open(options, path, &db); | 682 leveldb::Status status = leveldb::DB::Open(options, path, &db); |
| 418 ReportInitStatus(status); | 683 ReportInitStatus(status); |
| 419 if (status.ok()) { | 684 if (status.ok()) { |
| 420 db_.reset(db); | 685 db_.reset(db); |
| 421 return true; | 686 return true; |
| 422 } | 687 } |
| 423 HandleError(FROM_HERE, status); | 688 HandleError(FROM_HERE, status); |
| 424 | 689 |
| 425 if (recovery_option == FAIL_ON_CORRUPTION) | 690 switch (recovery_option) { |
| 691 case FAIL_ON_CORRUPTION: | |
| 692 return false; | |
| 693 case REPAIR_ON_CORRUPTION: | |
| 694 if (RepairDatabase(path)) | |
| 695 return true; | |
| 696 // fall through | |
| 697 case DELETE_ON_CORRUPTION: | |
| 698 if (!file_util::Delete(filesystem_data_directory_, true)) | |
| 699 return false; | |
| 700 if (!file_util::CreateDirectory(filesystem_data_directory_)) | |
| 701 return false; | |
|
kinuko
2012/04/11 10:58:49
Maybe we should output some WARNING message for th
tzik
2012/04/11 13:39:06
Done.
| |
| 702 return Init(FAIL_ON_CORRUPTION); | |
| 703 } | |
| 704 | |
| 705 NOTREACHED(); | |
| 706 return false; | |
| 707 } | |
| 708 | |
| 709 bool FileSystemDirectoryDatabase::RepairDatabase(const std::string& db_path) { | |
| 710 DCHECK(!db_.get()); | |
| 711 if (!leveldb::RepairDB(db_path, leveldb::Options()).ok()) | |
| 426 return false; | 712 return false; |
| 713 if (!Init(FAIL_ON_CORRUPTION)) | |
| 714 return false; | |
| 715 if (IsFileSystemConsistent()) | |
| 716 return true; | |
| 717 db_.reset(); | |
| 718 return false; | |
| 719 } | |
| 427 | 720 |
| 428 DCHECK_EQ(DELETE_ON_CORRUPTION, recovery_option); | 721 bool FileSystemDirectoryDatabase::IsFileSystemConsistent() { |
| 429 if (!file_util::Delete(filesystem_data_directory_, true)) | 722 if (!Init(FAIL_ON_CORRUPTION)) |
| 430 return false; | 723 return false; |
| 431 if (!file_util::CreateDirectory(filesystem_data_directory_)) | 724 DatabaseCheckHelper helper(this, db_.get(), filesystem_data_directory_); |
| 432 return false; | 725 return helper.IsFileSystemConsistent(); |
| 433 return Init(FAIL_ON_CORRUPTION); | |
| 434 } | 726 } |
| 435 | 727 |
| 436 void FileSystemDirectoryDatabase::ReportInitStatus( | 728 void FileSystemDirectoryDatabase::ReportInitStatus( |
| 437 const leveldb::Status& status) { | 729 const leveldb::Status& status) { |
| 438 base::Time now = base::Time::Now(); | 730 base::Time now = base::Time::Now(); |
| 439 const base::TimeDelta minimum_interval = | 731 const base::TimeDelta minimum_interval = |
| 440 base::TimeDelta::FromHours(kMinimumReportIntervalHours); | 732 base::TimeDelta::FromHours(kMinimumReportIntervalHours); |
| 441 if (last_reported_time_ + minimum_interval >= now) | 733 if (last_reported_time_ + minimum_interval >= now) |
| 442 return; | 734 return; |
| 443 last_reported_time_ = now; | 735 last_reported_time_ = now; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 477 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); | 769 batch.Put(LastIntegerKey(), base::Int64ToString(-1)); |
| 478 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | 770 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| 479 if (!status.ok()) { | 771 if (!status.ok()) { |
| 480 HandleError(FROM_HERE, status); | 772 HandleError(FROM_HERE, status); |
| 481 return false; | 773 return false; |
| 482 } | 774 } |
| 483 return true; | 775 return true; |
| 484 } | 776 } |
| 485 | 777 |
| 486 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { | 778 bool FileSystemDirectoryDatabase::GetLastFileId(FileId* file_id) { |
| 487 if (!Init(FAIL_ON_CORRUPTION)) | 779 if (!Init(REPAIR_ON_CORRUPTION)) |
| 488 return false; | 780 return false; |
| 489 DCHECK(file_id); | 781 DCHECK(file_id); |
| 490 std::string id_string; | 782 std::string id_string; |
| 491 leveldb::Status status = | 783 leveldb::Status status = |
| 492 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); | 784 db_->Get(leveldb::ReadOptions(), LastFileIdKey(), &id_string); |
| 493 if (status.ok()) { | 785 if (status.ok()) { |
| 494 if (!base::StringToInt64(id_string, file_id)) { | 786 if (!base::StringToInt64(id_string, file_id)) { |
| 495 LOG(ERROR) << "Hit database corruption!"; | 787 LOG(ERROR) << "Hit database corruption!"; |
| 496 return false; | 788 return false; |
| 497 } | 789 } |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 567 | 859 |
| 568 void FileSystemDirectoryDatabase::HandleError( | 860 void FileSystemDirectoryDatabase::HandleError( |
| 569 const tracked_objects::Location& from_here, | 861 const tracked_objects::Location& from_here, |
| 570 const leveldb::Status& status) { | 862 const leveldb::Status& status) { |
| 571 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " | 863 LOG(ERROR) << "FileSystemDirectoryDatabase failed at: " |
| 572 << from_here.ToString() << " with error: " << status.ToString(); | 864 << from_here.ToString() << " with error: " << status.ToString(); |
| 573 db_.reset(); | 865 db_.reset(); |
| 574 } | 866 } |
| 575 | 867 |
| 576 } // namespace fileapi | 868 } // namespace fileapi |
| OLD | NEW |