Commit a82168f4 authored by satorux@chromium.org's avatar satorux@chromium.org

gdata: Fix a bug in GDataLevelDB where errors are not handled right

GDataEntry::FromProtoString() fails if incompatible proto is detected
We should return an error rather than DCHECK it.
See crbug.com/142912 for how incompatible proto is rejected.

BUG=127856
TEST=out/Release/unit_tests --gtest_filter=GDataDBTest.*

Review URL: https://chromiumcodereview.appspot.com/10701118

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145950 0039d316-1c4b-4281-b951-d872f2087c98
parent 7aca2365
...@@ -47,6 +47,11 @@ class GDataDB { ...@@ -47,6 +47,11 @@ class GDataDB {
// Will not return NULL. // Will not return NULL.
virtual scoped_ptr<GDataDBIter> CreateIterator(const FilePath& path) = 0; virtual scoped_ptr<GDataDBIter> CreateIterator(const FilePath& path) = 0;
// Puts |raw_value| keyed with |resource_id| to the database.
// Used for testing (ex. injecting incompatible proto).
virtual Status PutRawForTesting(const std::string& resource_id,
const std::string& raw_value) = 0;
protected: protected:
GDataDB() {} GDataDB() {}
}; };
......
...@@ -5,10 +5,11 @@ ...@@ -5,10 +5,11 @@
#include "chrome/browser/chromeos/gdata/gdata_db.h" #include "chrome/browser/chromeos/gdata/gdata_db.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "chrome/browser/chromeos/gdata/gdata.pb.h"
#include "chrome/browser/chromeos/gdata/gdata_db_factory.h" #include "chrome/browser/chromeos/gdata/gdata_db_factory.h"
#include "chrome/browser/chromeos/gdata/gdata_files.h" #include "chrome/browser/chromeos/gdata/gdata_files.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gdata { namespace gdata {
namespace { namespace {
...@@ -33,6 +34,10 @@ class GDataDBTest : public testing::Test { ...@@ -33,6 +34,10 @@ class GDataDBTest : public testing::Test {
// matching |source| exists. // matching |source| exists.
void TestGetFound(const GDataEntry& source); void TestGetFound(const GDataEntry& source);
// Tests GDataDB::GetPath and GDataDB::ResourceId, ensuring that an entry
// matching |source| is corrupt.
void TestGetCorrupt(const GDataEntry& source);
// Initialize the database with the following entries: // Initialize the database with the following entries:
// drive/dir1 // drive/dir1
// drive/dir2 // drive/dir2
...@@ -98,6 +103,17 @@ void GDataDBTest::TestGetFound(const GDataEntry& source) { ...@@ -98,6 +103,17 @@ void GDataDBTest::TestGetFound(const GDataEntry& source) {
EXPECT_EQ(source.content_url(), entry->content_url()); EXPECT_EQ(source.content_url(), entry->content_url());
} }
void GDataDBTest::TestGetCorrupt(const GDataEntry& source) {
scoped_ptr<GDataEntry> entry;
GDataDB::Status status = gdata_db_->GetByPath(source.GetFilePath(), &entry);
EXPECT_EQ(GDataDB::DB_CORRUPTION, status);
EXPECT_FALSE(entry.get());
status = gdata_db_->GetByResourceId(source.resource_id(), &entry);
EXPECT_EQ(GDataDB::DB_CORRUPTION, status);
EXPECT_FALSE(entry.get());
}
void GDataDBTest::InitDB() { void GDataDBTest::InitDB() {
int sequence_id = 1; int sequence_id = 1;
GDataRootDirectory root; GDataRootDirectory root;
...@@ -186,17 +202,18 @@ TEST_F(GDataDBTest, PutTest) { ...@@ -186,17 +202,18 @@ TEST_F(GDataDBTest, PutTest) {
TestGetNotFound(dir); TestGetNotFound(dir);
GDataDB::Status status = gdata_db_->Put(dir); GDataDB::Status status = gdata_db_->Put(dir);
EXPECT_EQ(GDataDB::DB_OK, status); ASSERT_EQ(GDataDB::DB_OK, status);
TestGetFound(dir); TestGetFound(dir);
scoped_ptr<GDataEntry> entry; scoped_ptr<GDataEntry> entry;
gdata_db_->GetByPath(dir.GetFilePath(), &entry); status = gdata_db_->GetByPath(dir.GetFilePath(), &entry);
ASSERT_EQ(GDataDB::DB_OK, status);
EXPECT_EQ(dir.upload_url(), entry->AsGDataDirectory()->upload_url()); EXPECT_EQ(dir.upload_url(), entry->AsGDataDirectory()->upload_url());
EXPECT_TRUE(entry->AsGDataDirectory()->file_info().is_directory); EXPECT_TRUE(entry->AsGDataDirectory()->file_info().is_directory);
status = gdata_db_->DeleteByPath(dir.GetFilePath()); status = gdata_db_->DeleteByPath(dir.GetFilePath());
EXPECT_EQ(GDataDB::DB_OK, status); ASSERT_EQ(GDataDB::DB_OK, status);
TestGetNotFound(dir); TestGetNotFound(dir);
...@@ -210,16 +227,17 @@ TEST_F(GDataDBTest, PutTest) { ...@@ -210,16 +227,17 @@ TEST_F(GDataDBTest, PutTest) {
TestGetNotFound(file); TestGetNotFound(file);
status = gdata_db_->Put(file); status = gdata_db_->Put(file);
EXPECT_EQ(GDataDB::DB_OK, status); ASSERT_EQ(GDataDB::DB_OK, status);
TestGetFound(file); TestGetFound(file);
gdata_db_->GetByPath(file.GetFilePath(), &entry); status = gdata_db_->GetByPath(file.GetFilePath(), &entry);
ASSERT_EQ(GDataDB::DB_OK, status);
EXPECT_EQ(file.file_md5(), entry->AsGDataFile()->file_md5()); EXPECT_EQ(file.file_md5(), entry->AsGDataFile()->file_md5());
EXPECT_FALSE(entry->AsGDataFile()->file_info().is_directory); EXPECT_FALSE(entry->AsGDataFile()->file_info().is_directory);
status = gdata_db_->DeleteByPath(file.GetFilePath()); status = gdata_db_->DeleteByPath(file.GetFilePath());
EXPECT_EQ(GDataDB::DB_OK, status); ASSERT_EQ(GDataDB::DB_OK, status);
TestGetNotFound(file); TestGetNotFound(file);
} }
...@@ -274,4 +292,37 @@ TEST_F(GDataDBTest, IterTest) { ...@@ -274,4 +292,37 @@ TEST_F(GDataDBTest, IterTest) {
TestIter("dir4", NULL, 0); TestIter("dir4", NULL, 0);
} }
TEST_F(GDataDBTest, IncompatibleProtoTest) {
GDataRootDirectory root;
GDataFile file(&root, &root);
file.set_title("file");
file.set_file_name("file");
file.set_resource_id("file_resource_id");
file.set_content_url(GURL("http://content/dir/file"));
file.set_file_md5("file_md5");
// Add a file and check if it's found.
GDataDB::Status status = gdata_db_->Put(file);
ASSERT_EQ(GDataDB::DB_OK, status);
TestGetFound(file);
// Check if the iterator works too.
const char* all_entries[] = {
"drive/file",
};
TestIter("", all_entries, arraysize(all_entries));
// Tweak the file proto to simulate an incompatible proto in the DB.
GDataFileProto file_proto;
file.ToProto(&file_proto);
file_proto.clear_upload_url(); // This will make FromProto() fail.
std::string serialized_proto;
file_proto.SerializeToString(&serialized_proto);
gdata_db_->PutRawForTesting("file_resource_id", serialized_proto);
// Check if the corruption is detected.
TestGetCorrupt(file);
// We should no longer be able to find the file by iteration.
TestIter("", NULL, 0);
}
} // namespace gdata } // namespace gdata
...@@ -139,8 +139,7 @@ GDataDB::Status GDataLevelDB::GetByResourceId(const std::string& resource_id, ...@@ -139,8 +139,7 @@ GDataDB::Status GDataLevelDB::GetByResourceId(const std::string& resource_id,
if (db_status.ok()) { if (db_status.ok()) {
DCHECK(!serialized_proto.empty()); DCHECK(!serialized_proto.empty());
*entry = GDataEntry::FromProtoString(serialized_proto); *entry = GDataEntry::FromProtoString(serialized_proto);
DCHECK(entry->get()); return entry->get() ? DB_OK : DB_CORRUPTION;
return DB_OK;
} }
return GetStatus(db_status); return GetStatus(db_status);
} }
...@@ -176,6 +175,18 @@ scoped_ptr<GDataDBIter> GDataLevelDB::CreateIterator(const FilePath& path) { ...@@ -176,6 +175,18 @@ scoped_ptr<GDataDBIter> GDataLevelDB::CreateIterator(const FilePath& path) {
path)); path));
} }
GDataDB::Status GDataLevelDB::PutRawForTesting(
const std::string& resource_id,
const std::string& raw_value) {
const std::string resource_id_key = ResourceIdToKey(resource_id);
leveldb::Status db_status = level_db_->Put(
leveldb::WriteOptions(),
leveldb::Slice(resource_id_key),
leveldb::Slice(raw_value));
return GetStatus(db_status);
}
GDataLevelDBIter::GDataLevelDBIter(scoped_ptr<leveldb::Iterator> level_db_iter, GDataLevelDBIter::GDataLevelDBIter(scoped_ptr<leveldb::Iterator> level_db_iter,
GDataDB* db, GDataDB* db,
const FilePath& path) const FilePath& path)
...@@ -211,7 +222,8 @@ bool GDataLevelDBIter::GetNext(std::string* path, ...@@ -211,7 +222,8 @@ bool GDataLevelDBIter::GetNext(std::string* path,
GDataDB::Status status = GDataDB::Status status =
db_->GetByResourceId(level_db_iter_->value().ToString(), entry); db_->GetByResourceId(level_db_iter_->value().ToString(), entry);
DCHECK_EQ(GDataDB::DB_OK, status); if (status != GDataDB::DB_OK)
return false;
key_slice.remove_prefix(sizeof(kPathPrefix) - 1); key_slice.remove_prefix(sizeof(kPathPrefix) - 1);
path->assign(key_slice.ToString()); path->assign(key_slice.ToString());
......
...@@ -32,6 +32,8 @@ class GDataLevelDB : public GDataDB { ...@@ -32,6 +32,8 @@ class GDataLevelDB : public GDataDB {
virtual Status GetByPath(const FilePath& path, virtual Status GetByPath(const FilePath& path,
scoped_ptr<GDataEntry>* file) OVERRIDE; scoped_ptr<GDataEntry>* file) OVERRIDE;
virtual scoped_ptr<GDataDBIter> CreateIterator(const FilePath& path) OVERRIDE; virtual scoped_ptr<GDataDBIter> CreateIterator(const FilePath& path) OVERRIDE;
virtual Status PutRawForTesting(const std::string& resource_id,
const std::string& raw_value) OVERRIDE;
// Returns |resource_id| for |path| by looking up path_db_. // Returns |resource_id| for |path| by looking up path_db_.
Status ResourceIdForPath(const FilePath& path, std::string* resource_id); Status ResourceIdForPath(const FilePath& path, std::string* resource_id);
......
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