chromeos: Check validity of loaded data in DriveResourceMetadataStorageDB::Initialize

Broken data should be detected and cleared during initialization to avoid fatal bugs.

BUG=232054
TEST=unit_tests

Review URL: https://codereview.chromium.org/14167009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194550 0039d316-1c4b-4281-b951-d872f2087c98
parent d40b5e91
......@@ -201,12 +201,10 @@ bool DriveResourceMetadataStorageDB::Initialize() {
if (status.ok())
child_map_.reset(db);
// Check the version of existing DB.
// Check the validity of existing DB.
if (resource_map_ && child_map_) {
scoped_ptr<DriveResourceMetadataHeader> header = GetHeader();
if (!header ||
header->version() != kDBVersion) {
LOG(ERROR) << "Reject incompatible DB.";
if (!CheckValidity()) {
LOG(ERROR) << "Reject invalid DB.";
resource_map_.reset();
child_map_.reset();
}
......@@ -429,4 +427,72 @@ DriveResourceMetadataStorageDB::GetHeader() {
return header.Pass();
}
bool DriveResourceMetadataStorageDB::CheckValidity() {
base::ThreadRestrictions::AssertIOAllowed();
// Perform read with checksums verification enalbed.
leveldb::ReadOptions options;
options.verify_checksums = true;
scoped_ptr<leveldb::Iterator> it(resource_map_->NewIterator(options));
it->SeekToFirst();
// Check the header.
DriveResourceMetadataHeader header;
if (!it->Valid() ||
it->key() != GetHeaderDBKey() || // Header entry must come first.
!header.ParseFromArray(it->value().data(), it->value().size()) ||
header.version() != kDBVersion) {
DLOG(ERROR) << "Invalid header detected. version = " << header.version();
return false;
}
// Check all entires.
size_t num_checked_child_map_entries = 0;
DriveEntryProto entry;
std::string child_resource_id;
for (it->Next(); it->Valid(); it->Next()) {
// Check if stored data is broken.
if (!entry.ParseFromArray(it->value().data(), it->value().size()) ||
entry.resource_id() != it->key()) {
DLOG(ERROR) << "Broken entry detected";
return false;
}
// Check if parent-child relationship is stored correctly.
if (!entry.parent_resource_id().empty()) {
leveldb::Status status = child_map_->Get(
options,
leveldb::Slice(GetChildMapKey(entry.parent_resource_id(),
entry.base_name())),
&child_resource_id);
if (!status.ok() || child_resource_id != entry.resource_id()) {
DLOG(ERROR) << "Child map is broken. status = " << status.ToString();
return false;
}
++num_checked_child_map_entries;
}
}
if (!it->status().ok()) {
DLOG(ERROR) << "Error during checking resource map. status = "
<< it->status().ToString();
return false;
}
// Check all child map entries are referenced from |resource_map_|.
size_t num_child_map_entries = 0;
it.reset(child_map_->NewIterator(options));
for (it->SeekToFirst(); it->Valid(); it->Next()) {
++num_child_map_entries;
}
if (!it->status().ok() ||
num_child_map_entries != num_checked_child_map_entries) {
DLOG(ERROR) << "Error during checking child map. status = "
<< it->status().ToString();
return false;
}
return true;
}
} // namespace drive
......@@ -162,6 +162,9 @@ class DriveResourceMetadataStorageDB
// Gets header.
scoped_ptr<DriveResourceMetadataHeader> GetHeader();
// Checks validity of the data.
bool CheckValidity();
// Path to the directory where the data is stored.
base::FilePath directory_path_;
......
......@@ -44,6 +44,10 @@ class DriveResourceMetadataStorageTest : public testing::Test {
storage_->PutHeader(*header);
}
bool CheckValidity() {
return storage_->CheckValidity();
}
base::ScopedTempDir temp_dir_;
scoped_ptr<DriveResourceMetadataStorageDB> storage_;
};
......@@ -199,23 +203,32 @@ TEST_F(DriveResourceMetadataStorageTest, OpenExistingDB) {
DriveEntryProto entry1;
entry1.set_resource_id(parent_id1);
DriveEntryProto entry2;
entry2.set_resource_id(child_id1);
entry2.set_parent_resource_id(parent_id1);
entry2.set_base_name(child_name1);
// Put some data.
storage_->PutEntry(entry1);
storage_->PutEntry(entry2);
storage_->PutChild(parent_id1, child_name1, child_id1);
scoped_ptr<DriveEntryProto> result = storage_->GetEntry(parent_id1);
ASSERT_TRUE(result);
EXPECT_EQ(parent_id1, result->resource_id());
EXPECT_EQ(child_id1, storage_->GetChild(parent_id1, child_name1));
// Close DB and reopen.
storage_.reset(new DriveResourceMetadataStorageDB(temp_dir_.path()));
ASSERT_TRUE(storage_->Initialize());
// Can read data.
scoped_ptr<DriveEntryProto> result;
result = storage_->GetEntry(parent_id1);
ASSERT_TRUE(result);
EXPECT_EQ(parent_id1, result->resource_id());
result = storage_->GetEntry(child_id1);
ASSERT_TRUE(result);
EXPECT_EQ(child_id1, result->resource_id());
EXPECT_EQ(parent_id1, result->parent_resource_id());
EXPECT_EQ(child_name1, result->base_name());
EXPECT_EQ(child_id1, storage_->GetChild(parent_id1, child_name1));
}
......@@ -251,4 +264,46 @@ TEST_F(DriveResourceMetadataStorageTest, WrongPath) {
ASSERT_FALSE(storage_->Initialize());
}
TEST_F(DriveResourceMetadataStorageTest, CheckValidity) {
const std::string key1 = "foo";
const std::string name1 = "hoge";
const std::string key2 = "bar";
const std::string name2 = "fuga";
const std::string key3 = "boo";
const std::string name3 = "piyo";
// Put entry with key1.
DriveEntryProto entry;
entry.set_resource_id(key1);
entry.set_base_name(name1);
storage_->PutEntry(entry);
EXPECT_TRUE(CheckValidity());
// Put entry with key2 under key1.
entry.set_resource_id(key2);
entry.set_parent_resource_id(key1);
entry.set_base_name(name2);
storage_->PutEntry(entry);
EXPECT_FALSE(CheckValidity()); // Missing parent-child relationship.
// Add missing parent-child relationship between key1 and key2.
storage_->PutChild(key1, name2, key2);
EXPECT_TRUE(CheckValidity());
// Add parent-child relationship between key1 and key3.
storage_->PutChild(key1, name3, key3);
EXPECT_FALSE(CheckValidity()); // key3 is not stored in the storage.
// Put entry with key3 under key1.
entry.set_resource_id(key3);
entry.set_parent_resource_id(key1);
entry.set_base_name(name3);
storage_->PutEntry(entry);
EXPECT_TRUE(CheckValidity());
// Parent-child relationship with wrong name.
storage_->PutChild(key1, name2, key3);
EXPECT_FALSE(CheckValidity());
}
} // namespace drive
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment