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