From e936592b39f27b0835863ddfdb6cf9a68df85f39 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 3 Oct 2017 10:02:49 -0400 Subject: [PATCH] Bug 1404344 - Part 2: DOM Cache API schema 57-56 downgrade support. r=bkelly Alter 57 and 58 DOM Cache API to use an on-disk schema version of 25 again, as was used prior to the landing of bug 1290481 that bumped the disk schema version to 26. Patched versions will recognize this internally as schema 27 based on the presence of the column introduced by the 26 upgrade. --HG-- extra : rebase_source : 03d6e361ef71de03e99bd27bc27a95ed65ecb846 extra : source : 523debab77daab9f69ca9ea19412c655e6d1e02f --- dom/cache/DBSchema.cpp | 102 +++++++++++++++++++++++++++++++++++--- dom/cache/QuotaClient.cpp | 2 +- 2 files changed, 96 insertions(+), 8 deletions(-) diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index e5fee86cfb25..5120f902040f 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -35,8 +35,44 @@ namespace cache { namespace db { const int32_t kFirstShippedSchemaVersion = 15; namespace { +// ## Firefox 57 Cache API v25/v26/v27 Schema Hack Info +// ### Overview +// In Firefox 57 we introduced Cache API schema version 26 and Quota Manager +// schema v3 to support tracking padding for opaque responses. Unfortunately, +// Firefox 57 is a big release that may potentially result in users downgrading +// to Firefox 56 due to 57 retiring add-ons. These schema changes have the +// unfortunate side-effect of causing QuotaManager and all its clients to break +// if the user downgrades to 56. In order to avoid making a bad situation +// worse, we're now retrofitting 57 so that Firefox 56 won't freak out. +// +// ### Implementation +// We're introducing a new schema version 27 that uses an on-disk schema version +// of v25. We differentiate v25 from v27 by the presence of the column added +// by v26. This translates to: +// - v25: on-disk schema=25, no "response_padding_size" column in table +// "entries". +// - v26: on-disk schema=26, yes "response_padding_size" column in table +// "entries". +// - v27: on-disk schema=25, yes "response_padding_size" column in table +// "entries". +// +// ### Fallout +// Firefox 57 is happy because it sees schema 27 and everything is as it +// expects. +// +// Firefox 56 non-DEBUG build is fine/happy, but DEBUG builds will not be. +// - Our QuotaClient will invoke `NS_WARNING("Unknown Cache file found!");` +// at QuotaManager init time. This is harmless but annoying and potentially +// misleading. +// - The DEBUG-only Validate() call will error out whenever an attempt is made +// to open a DOM Cache database because it will notice the schema is broken +// and there is no attempt at recovery. +// +const int32_t kHackyDowngradeSchemaVersion = 25; +const int32_t kHackyPaddingSizePresentVersion = 27; +// // Update this whenever the DB schema is changed. -const int32_t kLatestSchemaVersion = 26; +const int32_t kLatestSchemaVersion = 27; // --------- // The following constants define the SQL schema. These are defined in the // same order the SQL should be executed in CreateOrMigrateSchema(). They are @@ -356,6 +392,8 @@ static nsresult CreateAndBindKeyStatement(mozIStorageConnection* aConn, mozIStorageStatement** aStateOut); static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut); +nsresult GetEffectiveSchemaVersion(mozIStorageConnection* aConn, + int32_t& schemaVersion); nsresult Validate(mozIStorageConnection* aConn); nsresult Migrate(mozIStorageConnection* aConn); } // namespace @@ -412,7 +450,7 @@ CreateOrMigrateSchema(mozIStorageConnection* aConn) MOZ_DIAGNOSTIC_ASSERT(aConn); int32_t schemaVersion; - nsresult rv = aConn->GetSchemaVersion(&schemaVersion); + nsresult rv = GetEffectiveSchemaVersion(aConn, schemaVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (schemaVersion == kLatestSchemaVersion) { @@ -473,10 +511,10 @@ CreateOrMigrateSchema(mozIStorageConnection* aConn) rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableStorage)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->SetSchemaVersion(kLatestSchemaVersion); + rv = aConn->SetSchemaVersion(kHackyDowngradeSchemaVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->GetSchemaVersion(&schemaVersion); + rv = GetEffectiveSchemaVersion(aConn, schemaVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } @@ -2463,6 +2501,44 @@ IncrementalVacuum(mozIStorageConnection* aConn) namespace { +// Wrapper around mozIStorageConnection::GetSchemaVersion() that compensates +// for hacky downgrade schema version tricks. See the block comments for +// kHackyDowngradeSchemaVersion and kHackyPaddingSizePresentVersion. +nsresult +GetEffectiveSchemaVersion(mozIStorageConnection* aConn, + int32_t& schemaVersion) +{ + nsresult rv = aConn->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + if (schemaVersion == kHackyDowngradeSchemaVersion) { + // This is the special case. Check for the existence of the + // "response_padding_size" colum in table "entries". + // + // (pragma_table_info is a table-valued function format variant of + // "PRAGMA table_info" supported since SQLite 3.16.0. Firefox 53 shipped + // was the first release with this functionality, shipping 3.16.2.) + nsCOMPtr stmt; + nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name FROM pragma_table_info('entries') WHERE " + "name = 'response_padding_size'" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // If there are any result rows, then the column is present. + bool hasColumn = false; + rv = stmt->ExecuteStep(&hasColumn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + if (hasColumn) { + schemaVersion = kHackyPaddingSizePresentVersion; + } + } + + return NS_OK; +} + + #ifdef DEBUG struct Expect { @@ -2492,7 +2568,7 @@ nsresult Validate(mozIStorageConnection* aConn) { int32_t schemaVersion; - nsresult rv = aConn->GetSchemaVersion(&schemaVersion); + nsresult rv = GetEffectiveSchemaVersion(aConn, schemaVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (NS_WARN_IF(schemaVersion != kLatestSchemaVersion)) { @@ -2598,6 +2674,7 @@ nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom24To25(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom25To26(mozIStorageConnection* aConn, bool& aRewriteSchema); +nsresult MigrateFrom26To27(mozIStorageConnection* aConn, bool& aRewriteSchema); // Configure migration functions to run for the given starting version. Migration sMigrationList[] = { Migration(15, MigrateFrom15To16), @@ -2611,6 +2688,7 @@ Migration sMigrationList[] = { Migration(23, MigrateFrom23To24), Migration(24, MigrateFrom24To25), Migration(25, MigrateFrom25To26), + Migration(26, MigrateFrom26To27), }; uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration); nsresult @@ -2649,7 +2727,7 @@ Migrate(mozIStorageConnection* aConn) MOZ_DIAGNOSTIC_ASSERT(aConn); int32_t currentVersion = 0; - nsresult rv = aConn->GetSchemaVersion(¤tVersion); + nsresult rv = GetEffectiveSchemaVersion(aConn, currentVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } bool rewriteSchema = false; @@ -2675,7 +2753,7 @@ Migrate(mozIStorageConnection* aConn) #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED int32_t lastVersion = currentVersion; #endif - rv = aConn->GetSchemaVersion(¤tVersion); + rv = GetEffectiveSchemaVersion(aConn, currentVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } MOZ_DIAGNOSTIC_ASSERT(currentVersion > lastVersion); } @@ -3174,6 +3252,16 @@ nsresult MigrateFrom25To26(mozIStorageConnection* aConn, bool& aRewriteSchema) return rv; } +nsresult MigrateFrom26To27(mozIStorageConnection* aConn, bool& aRewriteSchema) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_DIAGNOSTIC_ASSERT(aConn); + + nsresult rv = aConn->SetSchemaVersion(kHackyDowngradeSchemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + return rv; +} + } // anonymous namespace } // namespace db } // namespace cache diff --git a/dom/cache/QuotaClient.cpp b/dom/cache/QuotaClient.cpp index ee592be5a1b1..426176060315 100644 --- a/dom/cache/QuotaClient.cpp +++ b/dom/cache/QuotaClient.cpp @@ -301,7 +301,7 @@ public: } nsresult - UpgradeStorageFrom2_0To3_0(nsIFile* aDirectory) override + UpgradeStorageFrom2_0To2_1(nsIFile* aDirectory) override { AssertIsOnIOThread(); MOZ_DIAGNOSTIC_ASSERT(aDirectory);