IndexedDB: Handle LevelDB database corruption
authorjsbell@chromium.org <jsbell@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Mar 2012 19:57:00 +0000 (19:57 +0000)
committerjsbell@chromium.org <jsbell@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Mar 2012 19:57:00 +0000 (19:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=79413

Source/WebCore:

Add LevelDBDatabase::destroy() method so that clients can retry if open() fails.

Reviewed by Tony Chang.

Test: webkit_unit_tests --gtest_filter='LevelDBDatabaseTest.CorruptionTest'

* Modules/indexeddb/IDBLevelDBBackingStore.cpp: Implement open/destroy/open strategy.
(WebCore::IDBLevelDBBackingStore::open):
* platform/leveldb/LevelDBDatabase.cpp:
(WebCore::LevelDBDatabase::destroy):
(WebCore):
* platform/leveldb/LevelDBDatabase.h:
(LevelDBDatabase):

Source/WebKit/chromium:

Reviewed by Tony Chang.

* WebKit.gyp: Skip LevelDBTest.cpp in shared component builds due to webkit_support dependency
* WebKit.gypi: Add LevelDBTest.cpp
* tests/LevelDBTest.cpp: Added.
(WebCore):
(SimpleComparator):
(WebCore::SimpleComparator::compare):
(WebCore::SimpleComparator::name):
(WebCore::encodeString):
(WebCore::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@110463 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp
Source/WebCore/platform/leveldb/LevelDBDatabase.cpp
Source/WebCore/platform/leveldb/LevelDBDatabase.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/WebKit.gyp
Source/WebKit/chromium/WebKit.gypi
Source/WebKit/chromium/tests/LevelDBTest.cpp [new file with mode: 0644]

index db48fab..7394abf 100644 (file)
@@ -1,3 +1,22 @@
+2012-03-12  Joshua Bell  <jsbell@chromium.org>
+
+        IndexedDB: Handle LevelDB database corruption
+        https://bugs.webkit.org/show_bug.cgi?id=79413
+
+        Add LevelDBDatabase::destroy() method so that clients can retry if open() fails.
+
+        Reviewed by Tony Chang.
+
+        Test: webkit_unit_tests --gtest_filter='LevelDBDatabaseTest.CorruptionTest'
+
+        * Modules/indexeddb/IDBLevelDBBackingStore.cpp: Implement open/destroy/open strategy.
+        (WebCore::IDBLevelDBBackingStore::open):
+        * platform/leveldb/LevelDBDatabase.cpp:
+        (WebCore::LevelDBDatabase::destroy):
+        (WebCore):
+        * platform/leveldb/LevelDBDatabase.h:
+        (LevelDBDatabase):
+
 2012-03-11  Shawn Singh  <shawnsingh@chromium.org>
 
         Fix regression on fast/table/table-row-compositing-repaint-crash.html
index f423bb0..4cccbe0 100644 (file)
@@ -150,6 +150,22 @@ PassRefPtr<IDBBackingStore> IDBLevelDBBackingStore::open(SecurityOrigin* securit
         String path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb.leveldb");
 
         db = LevelDBDatabase::open(path, comparator.get());
+
+        if (!db) {
+            LOG_ERROR("IndexedDB backing store open failed, attempting cleanup");
+            bool success = LevelDBDatabase::destroy(path);
+            if (!success) {
+                LOG_ERROR("IndexedDB backing store cleanup failed");
+                return PassRefPtr<IDBBackingStore>();
+            }
+
+            LOG_ERROR("IndexedDB backing store cleanup succeeded, reopening");
+            db = LevelDBDatabase::open(path, comparator.get());
+            if (!db) {
+                LOG_ERROR("IndexedDB backing store reopen after recovery failed");
+                return PassRefPtr<IDBBackingStore>();
+            }
+        }
     }
 
     if (!db)
index 41a1aef..273afd4 100644 (file)
@@ -112,6 +112,13 @@ static leveldb::Status openDB(leveldb::Comparator* comparator, leveldb::Env* env
     return leveldb::DB::Open(options, path.utf8().data(), db);
 }
 
+bool LevelDBDatabase::destroy(const String& fileName)
+{
+    leveldb::Options options;
+    const leveldb::Status s = leveldb::DestroyDB(fileName.utf8().data(), options);
+    return s.ok();
+}
+
 PassOwnPtr<LevelDBDatabase> LevelDBDatabase::open(const String& fileName, const LevelDBComparator* comparator)
 {
     OwnPtr<ComparatorAdapter> comparatorAdapter = adoptPtr(new ComparatorAdapter(comparator));
index c6ce21c..e80b49a 100644 (file)
@@ -50,6 +50,7 @@ class LevelDBDatabase {
 public:
     static PassOwnPtr<LevelDBDatabase> open(const String& fileName, const LevelDBComparator*);
     static PassOwnPtr<LevelDBDatabase> openInMemory(const LevelDBComparator*);
+    static bool destroy(const String& fileName);
     ~LevelDBDatabase();
 
     bool put(const LevelDBSlice& key, const Vector<char>& value);
index 770215c..b0b996a 100644 (file)
@@ -1,3 +1,20 @@
+2012-03-12  Joshua Bell  <jsbell@chromium.org>
+
+        IndexedDB: Handle LevelDB database corruption
+        https://bugs.webkit.org/show_bug.cgi?id=79413
+
+        Reviewed by Tony Chang.
+
+        * WebKit.gyp: Skip LevelDBTest.cpp in shared component builds due to webkit_support dependency
+        * WebKit.gypi: Add LevelDBTest.cpp
+        * tests/LevelDBTest.cpp: Added.
+        (WebCore):
+        (SimpleComparator):
+        (WebCore::SimpleComparator::compare):
+        (WebCore::SimpleComparator::name):
+        (WebCore::encodeString):
+        (WebCore::TEST):
+
 2012-03-12  Dana Jansens  <danakj@chromium.org>
 
         [chromium] Clean up culling tests and templatize to test impl constructs
index 1e1a416..ecb87a7 100644 (file)
                                 # functions defined only in !WEBKIT_IMPLEMENTATION.
                                 'tests/AssociatedURLLoaderTest.cpp',
                                 'tests/FrameTestHelpers.cpp',
+                                'tests/LevelDBTest.cpp',
                                 'tests/PopupMenuTest.cpp',
                                 'tests/RenderTableCellTest.cpp',
                                 'tests/WebFrameTest.cpp',
index 87d41bf..14760a4 100644 (file)
             'tests/KURLTest.cpp',
             'tests/LayerChromiumTest.cpp',
             'tests/LayerTextureUpdaterTest.cpp',
+            'tests/LevelDBTest.cpp',
             'tests/LocalizedNumberICUTest.cpp',
             'tests/PaintAggregatorTest.cpp',
             'tests/PlatformGestureCurveTest.cpp',
diff --git a/Source/WebKit/chromium/tests/LevelDBTest.cpp b/Source/WebKit/chromium/tests/LevelDBTest.cpp
new file mode 100644 (file)
index 0000000..2a00ae6
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if USE(LEVELDB)
+
+#include "FileSystem.h"
+#include "LevelDBComparator.h"
+#include "LevelDBDatabase.h"
+#include "LevelDBSlice.h"
+#include <gtest/gtest.h>
+#include <webkit/support/webkit_support.h>
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+
+namespace {
+
+class SimpleComparator : public LevelDBComparator {
+public:
+    virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const OVERRIDE
+    {
+        size_t len = std::min(a.end() - a.begin(), b.end() - b.begin());
+        return memcmp(a.begin(), b.begin(), len);
+    }
+    virtual const char* name() const OVERRIDE { return "temp_comparator"; }
+};
+
+Vector<char> encodeString(const std::string& s)
+{
+    Vector<char> ret(s.size());
+    for (size_t i = 0; i < s.size(); ++i)
+        ret.append(s[i]);
+    return ret;
+}
+
+TEST(LevelDBDatabaseTest, CorruptionTest)
+{
+    OwnPtr<webkit_support::ScopedTempDirectory> tempDirectory = adoptPtr(webkit_support::CreateScopedTempDirectory());
+    tempDirectory->CreateUniqueTempDir();
+    const String path = String::fromUTF8(tempDirectory->path().c_str());
+
+    const Vector<char> key = encodeString("key");
+    const Vector<char> putValue = encodeString("value");
+    Vector<char> gotValue;
+    SimpleComparator comparator;
+
+    OwnPtr<LevelDBDatabase> leveldb = LevelDBDatabase::open(path, &comparator);
+    EXPECT_TRUE(leveldb);
+    bool success = leveldb->put(key, putValue);
+    EXPECT_TRUE(success);
+    leveldb.release();
+    EXPECT_FALSE(leveldb);
+
+    leveldb = LevelDBDatabase::open(path, &comparator);
+    EXPECT_TRUE(leveldb);
+    success = leveldb->get(key, gotValue);
+    EXPECT_TRUE(success);
+    EXPECT_EQ(putValue, gotValue);
+    leveldb.release();
+    EXPECT_FALSE(leveldb);
+
+    const String filepath = pathByAppendingComponent(path, "CURRENT");
+    PlatformFileHandle handle = openFile(filepath, OpenForWrite);
+    truncateFile(handle, 0);
+    closeFile(handle);
+
+    leveldb = LevelDBDatabase::open(path, &comparator);
+    EXPECT_FALSE(leveldb);
+
+    bool destroyed = LevelDBDatabase::destroy(path);
+    EXPECT_TRUE(destroyed);
+
+    leveldb = LevelDBDatabase::open(path, &comparator);
+    EXPECT_TRUE(leveldb);
+    success = leveldb->get(key, gotValue);
+    EXPECT_FALSE(success);
+}
+
+} // namespace
+
+#endif // USE(LEVELDB)