bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
{
    {
        LockHolder lockDatabase(m_databaseGuard);
        openTrackerDatabase(DontCreateIfDoesNotExist);
        if (!m_database.isOpen())
            return false;

        if (!canDeleteDatabase(origin, name)) {
            ASSERT_NOT_REACHED();
            return false;
        }
        recordDeletingDatabase(origin, name);
    }

    // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
    if (!deleteDatabaseFile(origin, name, DeletionMode::Default)) {
        LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
        LockHolder lockDatabase(m_databaseGuard);
        doneDeletingDatabase(origin, name);
        return false;
    }

    LockHolder lockDatabase(m_databaseGuard);

    SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
    if (statement.prepare() != SQLITE_OK) {
        LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
        doneDeletingDatabase(origin, name);
        return false;
    }

    statement.bindText(1, origin->databaseIdentifier());
    statement.bindText(2, name);

    if (!statement.executeCommand()) {
        LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
        doneDeletingDatabase(origin, name);
        return false;
    }

    if (m_client) {
        m_client->dispatchDidModifyOrigin(origin);
        m_client->dispatchDidModifyDatabase(origin, name);
#if PLATFORM(IOS)
        m_client->dispatchDidDeleteDatabase();
#endif
    }
    doneDeletingDatabase(origin, name);
    
    return true;
}
Esempio n. 2
0
DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
{
    String originIdentifier = origin->databaseIdentifier();
    String displayName;
    int64_t expectedUsage;

    {
        MutexLocker 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() != SQLResultOk)
            return DatabaseDetails();

        statement.bindText(1, originIdentifier);
        statement.bindText(2, name);

        int result = statement.step();
        if (result == SQLResultDone)
            return DatabaseDetails();

        if (result != SQLResultRow) {
            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);
    }

    return DatabaseDetails(name, displayName, expectedUsage, usageForDatabase(name, origin));
}
void StorageTracker::setStorageDirectoryPath(const String& path)
{
    MutexLocker lockDatabase(m_databaseGuard);
    ASSERT(!m_database.isOpen());
    
    m_storageDirectoryPath = path.threadsafeCopy();
}
void StorageTracker::syncSetOriginDetails(const String& originIdentifier, const String& databaseFile)
{
    ASSERT(!isMainThread());

    MutexLocker lockDatabase(m_databaseGuard);

    openTrackerDatabase(true);
    
    if (!m_database.isOpen())
        return;

    SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
    if (statement.prepare() != SQLResultOk) {
        LOG_ERROR("Unable to establish origin '%s' in the tracker", originIdentifier.ascii().data());
        return;
    } 
    
    statement.bindText(1, originIdentifier);
    statement.bindText(2, databaseFile);
    
    if (statement.step() != SQLResultDone)
        LOG_ERROR("Unable to establish origin '%s' in the tracker", originIdentifier.ascii().data());

    {
        MutexLocker lockOrigins(m_originSetGuard);
        if (!m_originSet.contains(originIdentifier))
            m_originSet.add(originIdentifier);
    }

    {
        MutexLocker lockClient(m_clientGuard);
        if (m_client)
            m_client->dispatchDidModifyOrigin(originIdentifier);
    }
}
// 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);
}
void StorageTracker::cancelDeletingOrigin(const String& originIdentifier)
{
    if (!m_isActive)
        return;

    MutexLocker lockDatabase(m_databaseGuard);
    MutexLocker lockOrigins(m_originSetGuard);
    if (!m_originsBeingDeleted.isEmpty())
        m_originsBeingDeleted.remove(originIdentifier);
}
void StorageTracker::syncDeleteAllOrigins()
{
    ASSERT(!isMainThread());
    
    MutexLocker lockDatabase(m_databaseGuard);
    
    openTrackerDatabase(false);
    if (!m_database.isOpen())
        return;
    
    SQLiteStatement statement(m_database, "SELECT origin, path FROM Origins");
    if (statement.prepare() != SQLResultOk) {
        LOG_ERROR("Failed to prepare statement.");
        return;
    }
    
    int result;
    while ((result = statement.step()) == SQLResultRow) {
        if (!canDeleteOrigin(statement.getColumnText(0)))
            continue;

        SQLiteFileSystem::deleteDatabaseFile(statement.getColumnText(1));

        {
            MutexLocker lockClient(m_clientGuard);
            if (m_client)
                m_client->dispatchDidModifyOrigin(statement.getColumnText(0));
        }
    }
    
    if (result != SQLResultDone)
        LOG_ERROR("Failed to read in all origins from the database.");
    
    if (m_database.isOpen())
        m_database.close();
    
    if (!SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath())) {
        // In the case where it is not possible to delete the database file (e.g some other program
        // like a virus scanner is accessing it), make sure to remove all entries.
        openTrackerDatabase(false);
        if (!m_database.isOpen())
            return;
        SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins");
        if (deleteStatement.prepare() != SQLResultOk) {
            LOG_ERROR("Unable to prepare deletion of all origins");
            return;
        }
        if (!deleteStatement.executeCommand()) {
            LOG_ERROR("Unable to execute deletion of all origins");
            return;
        }
    }
    SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_storageDirectoryPath);
}
void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
{
    LockHolder lockDatabase(m_databaseGuard);

    if (quotaForOriginNoLock(origin) == quota)
        return;

    openTrackerDatabase(CreateIfDoesNotExist);
    if (!m_database.isOpen())
        return;
    
#if PLATFORM(IOS)
    bool insertedNewOrigin = false;
#endif

    bool originEntryExists = hasEntryForOriginNoLock(origin);
    if (!originEntryExists) {
        SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
        if (statement.prepare() != SQLITE_OK) {
            LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
        } else {
            statement.bindText(1, origin->databaseIdentifier());
            statement.bindInt64(2, quota);

            if (statement.step() != SQLITE_DONE)
                LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
#if PLATFORM(IOS)
            else
                insertedNewOrigin = true;
#endif
        }
    } else {
        SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
        bool error = statement.prepare() != SQLITE_OK;
        if (!error) {
            statement.bindInt64(1, quota);
            statement.bindText(2, origin->databaseIdentifier());

            error = !statement.executeCommand();
        }

        if (error)
            LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
    }

    if (m_client) {
#if PLATFORM(IOS)
        if (insertedNewOrigin)
            m_client->dispatchDidAddNewOrigin(origin);
#endif
        m_client->dispatchDidModifyOrigin(origin);
    }
}
Esempio n. 9
0
bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
{
    Vector<String> temp;
    {
        MutexLocker lockDatabase(m_databaseGuard);
        if (!databaseNamesForOriginNoLock(origin, temp))
          return false;
    }

    for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter)
        resultVector.append(iter->isolatedCopy());
    return true;
}
Esempio n. 10
0
bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
{
    Vector<String> temp;
    {
        LockHolder lockDatabase(m_databaseGuard);
        if (!databaseNamesForOriginNoLock(origin, temp))
          return false;
    }

    for (auto& databaseName : temp)
        resultVector.append(databaseName.isolatedCopy());
    return true;
}
Esempio n. 11
0
void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
{
    String originIdentifier = origin->databaseIdentifier();
    int64_t guid = 0;

    MutexLocker lockDatabase(m_databaseGuard);

    openTrackerDatabase(CreateIfDoesNotExist);
    if (!m_database.isOpen())
        return;
    SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");
    if (statement.prepare() != SQLResultOk)
        return;

    statement.bindText(1, originIdentifier);
    statement.bindText(2, name);

    int result = statement.step();
    if (result == SQLResultRow)
        guid = statement.getColumnInt64(0);
    statement.finalize();

    if (guid == 0) {
        if (result != SQLResultDone)
            LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());
        else {
            // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker
            // But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case
            // So we'll print an error instead
            LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker",
                       name.ascii().data(), originIdentifier.ascii().data());
        }
        return;
    }

    SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");
    if (updateStatement.prepare() != SQLResultOk)
        return;

    updateStatement.bindText(1, displayName);
    updateStatement.bindInt64(2, estimatedSize);
    updateStatement.bindInt64(3, guid);

    if (updateStatement.step() != SQLResultDone) {
        LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());
        return;
    }

    if (m_client)
        m_client->dispatchDidModifyDatabase(origin, name);
}
void StorageTracker::syncImportOriginIdentifiers()
{
    ASSERT(m_isActive);
    
    ASSERT(!isMainThread());

    {
        MutexLocker lockDatabase(m_databaseGuard);

        // Don't force creation of StorageTracker's db just because a tracker
        // was initialized. It will be created if local storage dbs are found
        // by syncFileSystemAndTrackerDatabse() or the next time a local storage
        // db is created by StorageAreaSync.
        openTrackerDatabase(false);

        if (m_database.isOpen()) {
            SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
            if (statement.prepare() != SQLResultOk) {
                LOG_ERROR("Failed to prepare statement.");
                return;
            }
            
            int result;
            
            {
                MutexLocker lockOrigins(m_originSetGuard);
                while ((result = statement.step()) == SQLResultRow)
                    m_originSet.add(statement.getColumnText(0).threadsafeCopy());
            }
            
            if (result != SQLResultDone) {
                LOG_ERROR("Failed to read in all origins from the database.");
                return;
            }
        }
    }
    
    syncFileSystemAndTrackerDatabase();
    
    {
        MutexLocker lockClient(m_clientGuard);
        if (m_client) {
            MutexLocker lockOrigins(m_originSetGuard);
            OriginSet::const_iterator end = m_originSet.end();
            for (OriginSet::const_iterator it = m_originSet.begin(); it != end; ++it)
                m_client->dispatchDidModifyOrigin(*it);
        }
    }
}
Esempio n. 13
0
void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
{
    MutexLocker lockDatabase(m_databaseGuard);

    if (quotaForOriginNoLock(origin) == quota)
        return;

    openTrackerDatabase(CreateIfDoesNotExist);
    if (!m_database.isOpen())
        return;

    bool originEntryExists = hasEntryForOriginNoLock(origin);
    if (!originEntryExists) {
        SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
        if (statement.prepare() != SQLResultOk) {
            LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
        } else {
            statement.bindText(1, origin->databaseIdentifier());
            statement.bindInt64(2, quota);

            if (statement.step() != SQLResultDone)
                LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
        }
    } else {
        SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
        bool error = statement.prepare() != SQLResultOk;
        if (!error) {
            statement.bindInt64(1, quota);
            statement.bindText(2, origin->databaseIdentifier());

            error = !statement.executeCommand();
        }

        if (error)
#if OS(WINDOWS)
            LOG_ERROR("Failed to set quota %I64u in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
#else
            LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
#endif
    }

    if (m_client)
        m_client->dispatchDidModifyOrigin(origin);
}
Esempio n. 14
0
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;
}
Esempio n. 15
0
// 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);
}
Esempio n. 16
0
// Note: a thought about performance: hasAdequateQuotaForOrigin() was also
// called in canEstablishDatabase(), and hence, we're repeating some work within
// hasAdequateQuotaForOrigin(). However, retryCanEstablishDatabase() should only
// be called in the rare even if canEstablishDatabase() fails. Since it is rare,
// we should not bother optimizing it. It is more beneficial to keep
// hasAdequateQuotaForOrigin() simple and correct (i.e. bug free), and just
// re-use it. Also note that the path for opening a database involves IO, and
// hence should not be a performance critical path anyway. 
bool DatabaseTracker::retryCanEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error)
{
    error = DatabaseError::None;

    MutexLocker lockDatabase(m_databaseGuard);
    SecurityOrigin* origin = context->securityOrigin();

    // We have already eliminated other types of errors in canEstablishDatabase().
    // The only reason we're in retryCanEstablishDatabase() is because we gave
    // the client a chance to update the quota and are rechecking it here.
    // If we fail this check, the only possible reason this time should be due
    // to inadequate quota.
    if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
        ASSERT(error == DatabaseError::None);
        return true;
    }

    ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
    doneCreatingDatabase(origin, name);

    return false;
}
Esempio n. 17
0
PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin)
{
    MutexLocker lockDatabase(m_databaseGuard);
    String databaseIdentifier = origin->databaseIdentifier();

    // The originLockMap is accessed from multiple DatabaseThreads since
    // different script contexts can be writing to different databases from
    // the same origin. Hence, the databaseIdentifier key needs to be an
    // isolated copy. An isolated copy gives us a value whose refCounting is
    // thread-safe, since our copy is guarded by the m_databaseGuard mutex.
    databaseIdentifier = databaseIdentifier.isolatedCopy();

    OriginLockMap::AddResult addResult =
        m_originLockMap.add(databaseIdentifier, RefPtr<OriginLock>());
    if (!addResult.isNewEntry)
        return addResult.iterator->value;

    String path = originPath(origin);
    RefPtr<OriginLock> lock = adoptRef(new OriginLock(path));
    ASSERT(lock);
    addResult.iterator->value = lock;

    return lock.release();
}
Esempio n. 18
0
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));
}
Esempio n. 19
0
void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin>>& originsResult)
{
    MutexLocker lockDatabase(m_databaseGuard);

    openTrackerDatabase(DontCreateIfDoesNotExist);
    if (!m_database.isOpen())
        return;

    SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
    if (statement.prepare() != SQLResultOk) {
        LOG_ERROR("Failed to prepare statement.");
        return;
    }

    int result;
    while ((result = statement.step()) == SQLResultRow) {
        RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
        originsResult.append(origin->isolatedCopy());
    }
    originsResult.shrinkToFit();

    if (result != SQLResultDone)
        LOG_ERROR("Failed to read in all origins from the database.");
}
Esempio n. 20
0
unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database)
{
    // The maximum size for a database is the full quota for its origin, minus the current usage within the origin,
    // plus the current usage of the given database
    MutexLocker lockDatabase(m_databaseGuard);
    SecurityOrigin* origin = database->securityOrigin();

    unsigned long long quota = quotaForOriginNoLock(origin);
    unsigned long long diskUsage = usageForOrigin(origin);
    unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database->fileName());
    ASSERT(databaseFileSize <= diskUsage);

    if (diskUsage > quota)
        return databaseFileSize;

    // A previous error may have allowed the origin to exceed its quota, or may
    // have allowed this database to exceed our cached estimate of the origin
    // disk usage. Don't multiply that error through integer underflow, or the
    // effective quota will permanently become 2^64.
    unsigned long long maxSize = quota - diskUsage + databaseFileSize;
    if (maxSize > quota)
        maxSize = databaseFileSize;
    return maxSize;
}
Esempio n. 21
0
// It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is
// taking place.
bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
{
    Vector<String> databaseNames;
    {
        MutexLocker lockDatabase(m_databaseGuard);
        openTrackerDatabase(DontCreateIfDoesNotExist);
        if (!m_database.isOpen())
            return false;

        if (!databaseNamesForOriginNoLock(origin, databaseNames)) {
            LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
            return false;
        }
        if (!canDeleteOrigin(origin)) {
            LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data());
            ASSERT_NOT_REACHED();
            return false;
        }
        recordDeletingOrigin(origin);
    }

    // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
    for (unsigned i = 0; i < databaseNames.size(); ++i) {
        if (!deleteDatabaseFile(origin, databaseNames[i])) {
            // Even if the file can't be deleted, we want to try and delete the rest, don't return early here.
            LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
        }
    }

    {
        MutexLocker lockDatabase(m_databaseGuard);
        deleteOriginLockFor(origin);
        doneDeletingOrigin(origin);

        SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
        if (statement.prepare() != SQLResultOk) {
            LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
            return false;
        }

        statement.bindText(1, origin->databaseIdentifier());

        if (!statement.executeCommand()) {
            LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
            return false;
        }

        SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
        if (originStatement.prepare() != SQLResultOk) {
            LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
            return false;
        }

        originStatement.bindText(1, origin->databaseIdentifier());

        if (!originStatement.executeCommand()) {
            LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
            return false;
        }

        SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));

        RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
        bool isEmpty = true;

        openTrackerDatabase(DontCreateIfDoesNotExist);
        if (m_database.isOpen()) {
            SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
            if (statement.prepare() != SQLResultOk)
                LOG_ERROR("Failed to prepare statement.");
            else if (statement.step() == SQLResultRow)
                isEmpty = false;
        }

        // If we removed the last origin, do some additional deletion.
        if (isEmpty) {
            if (m_database.isOpen())
                m_database.close();
           SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath());
           SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath);
        }

        if (m_client) {
            m_client->dispatchDidModifyOrigin(origin);
            for (unsigned i = 0; i < databaseNames.size(); ++i)
                m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
        }
    }
    return true;
}
Esempio n. 22
0
void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
{
    MutexLocker lockDatabase(m_databaseGuard);
    ASSERT(!m_database.isOpen());
    m_databaseDirectoryPath = path.isolatedCopy();
}
Esempio n. 23
0
unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
{
    MutexLocker lockDatabase(m_databaseGuard);
    return quotaForOriginNoLock(origin);
}
Esempio n. 24
0
void DatabaseTracker::doneCreatingDatabase(DatabaseBackendBase* database)
{
    MutexLocker lockDatabase(m_databaseGuard);
    doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier());
}
void StorageTracker::syncDeleteOrigin(const String& originIdentifier)
{
    ASSERT(!isMainThread());

    MutexLocker lockDatabase(m_databaseGuard);
    
    if (!canDeleteOrigin(originIdentifier)) {
        LOG_ERROR("Attempted to delete origin '%s' while it was being created\n", originIdentifier.ascii().data());
        return;
    }
    
    openTrackerDatabase(false);
    if (!m_database.isOpen())
        return;
    
    // Get origin's db file path, delete entry in tracker's db, then delete db file.
    SQLiteStatement pathStatement(m_database, "SELECT path FROM Origins WHERE origin=?");
    if (pathStatement.prepare() != SQLResultOk) {
        LOG_ERROR("Unable to prepare selection of path for origin '%s'", originIdentifier.ascii().data());
        return;
    }
    pathStatement.bindText(1, originIdentifier);
    int result = pathStatement.step();
    if (result != SQLResultRow) {
        LOG_ERROR("Unable to find origin '%s' in Origins table", originIdentifier.ascii().data());
        return;
    }
    
    String path = pathStatement.getColumnText(0);

    ASSERT(!path.isEmpty());
    
    SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins where origin=?");
    if (deleteStatement.prepare() != SQLResultOk) {
        LOG_ERROR("Unable to prepare deletion of origin '%s'", originIdentifier.ascii().data());
        return;
    }
    deleteStatement.bindText(1, originIdentifier);
    if (!deleteStatement.executeCommand()) {
        LOG_ERROR("Unable to execute deletion of origin '%s'", originIdentifier.ascii().data());
        return;
    }

    SQLiteFileSystem::deleteDatabaseFile(path);
    
    bool shouldDeleteTrackerFiles = false;
    {
        MutexLocker originLock(m_originSetGuard);
        m_originSet.remove(originIdentifier);
        shouldDeleteTrackerFiles = m_originSet.isEmpty();
    }

    if (shouldDeleteTrackerFiles) {
        m_database.close();
        SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath());
        SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_storageDirectoryPath);
    }

    {
        MutexLocker lockClient(m_clientGuard);
        if (m_client)
            m_client->dispatchDidModifyOrigin(originIdentifier);
    }
}
Esempio n. 26
0
void DatabaseTracker::removeDeletedOpenedDatabases()
{
    // This is called when another app has deleted a database.  Go through all opened databases in this
    // tracker and close any that's no longer being tracked in the database.
    
    {
        // Acquire the lock before calling openTrackerDatabase.
        LockHolder lockDatabase(m_databaseGuard);
        openTrackerDatabase(DontCreateIfDoesNotExist);
    }

    if (!m_database.isOpen())
        return;
    
    // Keep track of which opened databases have been deleted.
    Vector<RefPtr<Database> > deletedDatabases;
    typedef HashMap<RefPtr<SecurityOrigin>, Vector<String> > DeletedDatabaseMap;
    DeletedDatabaseMap deletedDatabaseMap;
    
    // Make sure not to hold the m_openDatabaseMapGuard mutex when calling
    // Database::markAsDeletedAndClose(), since that can cause a deadlock
    // during the synchronous DatabaseThread call it triggers.
    {
        LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
        if (m_openDatabaseMap) {
            for (auto& openDatabase : *m_openDatabaseMap) {
                auto& origin = openDatabase.key;
                DatabaseNameMap* databaseNameMap = openDatabase.value;
                Vector<String> deletedDatabaseNamesForThisOrigin;

                // Loop through all opened databases in this origin.  Get the current database file path of each database and see if
                // it still matches the path stored in the opened database object.
                for (auto& databases : *databaseNameMap) {
                    String databaseName = databases.key;
                    String databaseFileName;
                    SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
                    if (statement.prepare() == SQLITE_OK) {
                        statement.bindText(1, origin->databaseIdentifier());
                        statement.bindText(2, databaseName);
                        if (statement.step() == SQLITE_ROW)
                            databaseFileName = statement.getColumnText(0);
                        statement.finalize();
                    }
                    
                    bool foundDeletedDatabase = false;
                    for (auto& db : *databases.value) {
                        // We are done if this database has already been marked as deleted.
                        if (db->deleted())
                            continue;
                        
                        // If this database has been deleted or if its database file no longer matches the current version, this database is no longer valid and it should be marked as deleted.
                        if (databaseFileName.isNull() || databaseFileName != pathGetFileName(db->fileName())) {
                            deletedDatabases.append(db);
                            foundDeletedDatabase = true;
                        }
                    }
                    
                    // If the database no longer exists, we should remember to remove it from the OriginQuotaManager later.
                    if (foundDeletedDatabase && databaseFileName.isNull())
                        deletedDatabaseNamesForThisOrigin.append(databaseName);
                }
                
                if (!deletedDatabaseNamesForThisOrigin.isEmpty())
                    deletedDatabaseMap.set(origin, deletedDatabaseNamesForThisOrigin);
            }
        }
    }
    
    for (auto& deletedDatabase : deletedDatabases)
        deletedDatabase->markAsDeletedAndClose();

    for (auto& deletedDatabase : deletedDatabaseMap) {
        SecurityOrigin* origin = deletedDatabase.key.get();
        if (m_client)
            m_client->dispatchDidModifyOrigin(origin);
        
        const Vector<String>& databaseNames = deletedDatabase.value;
        for (auto& databaseName : databaseNames) {
            if (m_client)
                m_client->dispatchDidModifyDatabase(origin, databaseName);
        }        
    }
}
Esempio n. 27
0
String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
{
    MutexLocker lockDatabase(m_databaseGuard);
    return fullPathForDatabaseNoLock(origin, name, createIfNotExists).isolatedCopy();
}
Esempio n. 28
0
bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
{
    MutexLocker lockDatabase(m_databaseGuard);
    return hasEntryForOriginNoLock(origin);
}