// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller // is responsible for making sure no new databases are opened in the file to be deleted. bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name, DeletionMode deletionMode) { String fullPath = fullPathForDatabase(origin, name, false); if (fullPath.isEmpty()) return true; #ifndef NDEBUG { LockHolder lockDatabase(m_databaseGuard); ASSERT(isDeletingDatabaseOrOriginFor(origin, name)); } #endif Vector<RefPtr<Database>> deletedDatabases; // Make sure not to hold the any locks when calling // Database::markAsDeletedAndClose(), since that can cause a deadlock // during the synchronous DatabaseThread call it triggers. { LockHolder openDatabaseMapLock(m_openDatabaseMapGuard); if (m_openDatabaseMap) { // There are some open databases, lets check if they are for this origin. DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (nameMap && nameMap->size()) { // There are some open databases for this origin, let's check // if they are this database by name. DatabaseSet* databaseSet = nameMap->get(name); if (databaseSet && databaseSet->size()) { // We have some database open with this name. Mark them as deleted. for (auto& database : *databaseSet) deletedDatabases.append(database); } } } } for (auto& database : deletedDatabases) database->markAsDeletedAndClose(); #if PLATFORM(IOS) if (deletionMode == DeletionMode::Deferred) { // On the phone, other background processes may still be accessing this database. Deleting the database directly // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background. // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after // the truncation, it should get an error since the database file is no longer valid. When Safari is launched // next time, it'll go through the database files and clean up any zero-bytes ones. SQLiteDatabase database; if (!database.open(fullPath)) return false; return SQLiteFileSystem::truncateDatabaseFile(database.sqlite3Handle()); } #else UNUSED_PARAM(deletionMode); #endif return SQLiteFileSystem::deleteDatabaseFile(fullPath); }
bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error) { error = DatabaseError::None; MutexLocker lockDatabase(m_databaseGuard); SecurityOrigin* origin = context->securityOrigin(); if (isDeletingDatabaseOrOriginFor(origin, name)) { error = DatabaseError::DatabaseIsBeingDeleted; return false; } recordCreatingDatabase(origin, name); // If a database already exists, ignore the passed-in estimated size and say it's OK. if (hasEntryForDatabase(origin, name)) return true; if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) { ASSERT(error == DatabaseError::None); return true; } // If we get here, then we do not have enough quota for one of the // following reasons as indicated by the set error: // // If the error is DatabaseSizeOverflowed, then this means the requested // estimatedSize if so unreasonably large that it can cause an overflow in // the usage budget computation. In that case, there's nothing more we can // do, and there's no need for a retry. Hence, we should indicate that // we're done with our attempt to create the database. // // If the error is DatabaseSizeExceededQuota, then we'll give the client // a chance to update the quota and call retryCanEstablishDatabase() to try // again. Hence, we don't call doneCreatingDatabase() yet in that case. if (error == DatabaseError::DatabaseSizeOverflowed) doneCreatingDatabase(origin, name); else ASSERT(error == DatabaseError::DatabaseSizeExceededQuota); return false; }
// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller // is responsible for making sure no new databases are opened in the file to be deleted. bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name) { String fullPath = fullPathForDatabase(origin, name, false); if (fullPath.isEmpty()) return true; #ifndef NDEBUG { MutexLocker lockDatabase(m_databaseGuard); ASSERT(isDeletingDatabaseOrOriginFor(origin, name)); } #endif Vector<RefPtr<DatabaseBackendBase>> deletedDatabases; // Make sure not to hold the any locks when calling // Database::markAsDeletedAndClose(), since that can cause a deadlock // during the synchronous DatabaseThread call it triggers. { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (m_openDatabaseMap) { // There are some open databases, lets check if they are for this origin. DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (nameMap && nameMap->size()) { // There are some open databases for this origin, let's check // if they are this database by name. DatabaseSet* databaseSet = nameMap->get(name); if (databaseSet && databaseSet->size()) { // We have some database open with this name. Mark them as deleted. DatabaseSet::const_iterator end = databaseSet->end(); for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) deletedDatabases.append(*it); } } } } for (unsigned i = 0; i < deletedDatabases.size(); ++i) deletedDatabases[i]->markAsDeletedAndClose(); return SQLiteFileSystem::deleteDatabaseFile(fullPath); }