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