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 |