diff --git a/dom/cache/DBAction.cpp b/dom/cache/DBAction.cpp index b8a2d136d95d..ec463bf5c38a 100644 --- a/dom/cache/DBAction.cpp +++ b/dom/cache/DBAction.cpp @@ -14,6 +14,8 @@ #include "nsIFile.h" #include "nsIURI.h" #include "nsNetUtil.h" +#include "DBSchema.h" +#include "FileUtils.h" namespace mozilla { namespace dom { @@ -75,6 +77,8 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, MOZ_ASSERT(aDBDir); MOZ_ASSERT(aConnOut); + nsCOMPtr conn; + bool exists; nsresult rv = aDBDir->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -123,21 +127,48 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); if (NS_WARN_IF(!ss)) { return NS_ERROR_UNEXPECTED; } - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, aConnOut); + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn)); if (rv == NS_ERROR_FILE_CORRUPTED) { NS_WARNING("Cache database corrupted. Recreating empty database."); + conn = nullptr; + // There is nothing else we can do to recover. Also, this data can // be deleted by QuotaManager at any time anyways. - rv = dbFile->Remove(false); + rv = WipeDatabase(dbFile, aDBDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // TODO: clean up any orphaned body files (bug 1110446) - - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, aConnOut); + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn)); } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - MOZ_ASSERT(*aConnOut); + + // Check the schema to make sure it is not too old. + int32_t schemaVersion = 0; + rv = conn->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + if (schemaVersion > 0 && schemaVersion < DBSchema::kMaxWipeSchemaVersion) { + conn = nullptr; + rv = WipeDatabase(dbFile, aDBDir); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn)); + } + + conn.forget(aConnOut); + + return rv; +} + +nsresult +DBAction::WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir) +{ + nsresult rv = aDBFile->Remove(false); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Delete the morgue as well. + rv = FileUtils::BodyDeleteDir(aDBDir); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + return rv; } diff --git a/dom/cache/DBAction.h b/dom/cache/DBAction.h index 7dd7ab3a505d..be40d8974be9 100644 --- a/dom/cache/DBAction.h +++ b/dom/cache/DBAction.h @@ -49,6 +49,8 @@ private: nsresult OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aQuotaDir, mozIStorageConnection** aConnOut); + nsresult WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir); + const Mode mMode; }; diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 6acf83e19dcc..1ec875383c14 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -20,6 +20,7 @@ namespace dom { namespace cache { +const int32_t DBSchema::kMaxWipeSchemaVersion = 1; const int32_t DBSchema::kLatestSchemaVersion = 1; const int32_t DBSchema::kMaxEntriesPerStatement = 255; diff --git a/dom/cache/DBSchema.h b/dom/cache/DBSchema.h index 6b0a595e0165..d59746769018 100644 --- a/dom/cache/DBSchema.h +++ b/dom/cache/DBSchema.h @@ -88,6 +88,9 @@ public: Namespace aNamespace, nsTArray& aKeysOut); + // We will wipe out databases with a schema versions less than this. + static const int32_t kMaxWipeSchemaVersion; + private: typedef int32_t EntryId; diff --git a/dom/cache/FileUtils.cpp b/dom/cache/FileUtils.cpp index 1bfda8b8f0e8..2cbfef755a90 100644 --- a/dom/cache/FileUtils.cpp +++ b/dom/cache/FileUtils.cpp @@ -47,6 +47,29 @@ FileUtils::BodyCreateDir(nsIFile* aBaseDir) return rv; } +// static +nsresult +FileUtils::BodyDeleteDir(nsIFile* aBaseDir) +{ + MOZ_ASSERT(aBaseDir); + + nsCOMPtr aBodyDir; + nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aBodyDir->Append(NS_LITERAL_STRING("morgue")); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aBodyDir->Remove(/* recursive = */ true); + if (rv == NS_ERROR_FILE_NOT_FOUND || + rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { + rv = NS_OK; + } + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + return rv; +} + // static nsresult FileUtils::BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, diff --git a/dom/cache/FileUtils.h b/dom/cache/FileUtils.h index 8f9cf16fe18b..76fd5ee90b00 100644 --- a/dom/cache/FileUtils.h +++ b/dom/cache/FileUtils.h @@ -30,6 +30,10 @@ public: }; static nsresult BodyCreateDir(nsIFile* aBaseDir); + // Note that this function can only be used during the initialization of the + // database. We're unlikely to be able to delete the DB successfully past + // that point due to the file being in use. + static nsresult BodyDeleteDir(nsIFile* aBaseDir); static nsresult BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut);