void DatabaseTracker::deleteDatabasesModifiedSince(std::chrono::system_clock::time_point time) { Vector<RefPtr<SecurityOrigin>> originsCopy; origins(originsCopy); for (auto& origin : originsCopy) { Vector<String> databaseNames; if (!databaseNamesForOrigin(origin.get(), databaseNames)) continue; size_t deletedDatabases = 0; for (auto& databaseName : databaseNames) { auto fullPath = fullPathForDatabase(origin.get(), databaseName, false); time_t modificationTime; if (!getFileModificationTime(fullPath, modificationTime)) continue; if (modificationTime < std::chrono::system_clock::to_time_t(time)) continue; deleteDatabase(origin.get(), databaseName); ++deletedDatabases; } if (deletedDatabases == databaseNames.size()) deleteOrigin(origin.get()); } }
unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin) { String path = fullPathForDatabase(origin, name, false); if (path.isEmpty()) return 0; return SQLiteFileSystem::getDatabaseFileSize(path); }
// 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); }
// 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); }
DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) { String originIdentifier = origin->databaseIdentifier(); String displayName; int64_t expectedUsage; { LockHolder lockDatabase(m_databaseGuard); openTrackerDatabase(DontCreateIfDoesNotExist); if (!m_database.isOpen()) return DatabaseDetails(); SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); if (statement.prepare() != SQLITE_OK) return DatabaseDetails(); statement.bindText(1, originIdentifier); statement.bindText(2, name); int result = statement.step(); if (result == SQLITE_DONE) return DatabaseDetails(); if (result != SQLITE_ROW) { LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); return DatabaseDetails(); } displayName = statement.getColumnText(0); expectedUsage = statement.getColumnInt64(1); } String path = fullPathForDatabase(origin, name, false); if (path.isEmpty()) return DatabaseDetails(name, displayName, expectedUsage, 0, 0, 0); return DatabaseDetails(name, displayName, expectedUsage, SQLiteFileSystem::getDatabaseFileSize(path), SQLiteFileSystem::databaseCreationTime(path), SQLiteFileSystem::databaseModificationTime(path)); }