String IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(const SecurityOriginData& topLevelOrigin, const SecurityOriginData& openingOrigin, const String& rootDirectory) { String mainFrameDirectory = pathByAppendingComponent(rootDirectory, topLevelOrigin.databaseIdentifier()); // If the opening origin and main frame origins are the same, there is no partitioning. if (openingOrigin == topLevelOrigin) return mainFrameDirectory; return pathByAppendingComponent(mainFrameDirectory, openingOrigin.databaseIdentifier()); }
static void removeAllDatabasesForOriginPath(const String& originPath, std::chrono::system_clock::time_point modifiedSince) { Vector<String> databasePaths = listDirectory(originPath, "*"); for (auto& databasePath : databasePaths) { String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3"); if (!fileExists(databaseFile)) continue; if (modifiedSince > std::chrono::system_clock::time_point::min()) { time_t modificationTime; if (!getFileModificationTime(databaseFile, modificationTime)) continue; if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince) continue; } SQLiteFileSystem::deleteDatabaseFile(databaseFile); deleteEmptyDirectory(databasePath); } deleteEmptyDirectory(originPath); }
std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::getOrEstablishMetadata() { ASSERT(!isMainThread()); String dbFilename = pathByAppendingComponent(m_absoluteDatabaseDirectory, "IndexedDB.sqlite3"); m_sqliteDB = openSQLiteDatabaseAtPath(dbFilename); if (!m_sqliteDB) return nullptr; RefPtr<UniqueIDBDatabaseBackingStoreSQLite> protector(this); m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) { return collate(aLength, a, bLength, b); }); std::unique_ptr<IDBDatabaseMetadata> metadata = extractExistingMetadata(); if (!metadata) metadata = createAndPopulateInitialMetadata(); if (!metadata) LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data()); // The database id is a runtime concept and doesn't need to be stored in the metadata database. metadata->id = generateDatabaseId(); return metadata; }
static PassRefPtr<IDBSQLiteDatabase> openSQLiteDatabase(SecurityOrigin* securityOrigin, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory) { String path = ":memory:"; if (!pathBase.isEmpty()) { if (!makeAllDirectories(pathBase)) { // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data()); return 0; } path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb"); } RefPtr<IDBSQLiteDatabase> sqliteDatabase = IDBSQLiteDatabase::create(fileIdentifier, factory); if (!sqliteDatabase->db().open(path)) { // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data()); return 0; } // FIXME: Error checking? sqliteDatabase->db().setMaximumSize(maximumSize); sqliteDatabase->db().turnOnIncrementalAutoVacuum(); return sqliteDatabase.release(); }
static void removeAllDatabasesForOriginPath(const String& originPath, double startDate, double endDate) { // FIXME: We should also close/invalidate any live handles to the database files we are about to delete. // Right now: // - For read-only operations, they will continue functioning as normal on the unlinked file. // - For write operations, they will start producing errors as SQLite notices the missing backing store. // This is tracked by https://bugs.webkit.org/show_bug.cgi?id=135347 Vector<String> databasePaths = listDirectory(originPath, "*"); for (auto& databasePath : databasePaths) { String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3"); if (!fileExists(databaseFile)) continue; time_t modTime; getFileModificationTime(databaseFile, modTime); if (modTime < startDate || modTime > endDate) continue; deleteFile(databaseFile); deleteEmptyDirectory(databasePath); } deleteEmptyDirectory(originPath); }
String SQLiteFileSystem::getFileNameForNewDatabase(const String& dbDir, const String&, const String&, SQLiteDatabase* db) { // try to get the next sequence number from the given database // if we can't get a number, return an empty string SQLiteStatement sequenceStatement(*db, "SELECT seq FROM sqlite_sequence WHERE name='Databases';"); if (sequenceStatement.prepare() != SQLResultOk) return String(); int result = sequenceStatement.step(); int64_t seq = 0; if (result == SQLResultRow) seq = sequenceStatement.getColumnInt64(0); else if (result != SQLResultDone) return String(); sequenceStatement.finalize(); // increment the number until we can use it to form a file name that doesn't exist String fileName; do { ++seq; fileName = pathByAppendingComponent(dbDir, String::format("%016"PRIx64".db", seq)); } while (fileExists(fileName)); return String::format("%016"PRIx64".db", seq); }
String WebResourceLoadStatisticsStore::persistentStoragePath(const String& label) const { if (m_storagePath.isEmpty()) return emptyString(); // TODO Decide what to call this file return pathByAppendingComponent(m_storagePath, label + "_resourceLog.plist"); }
// Called on a background thread. String StorageSyncManager::fullDatabaseFilename(const String& databaseIdentifier) { if (!makeAllDirectories(m_path)) { LOG_ERROR("Unabled to create LocalStorage database path %s", m_path.utf8().data()); return String(); } return pathByAppendingComponent(m_path, databaseIdentifier + ".localstorage"); }
String LocalStorageDatabaseTracker::databasePath(const String& filename) const { if (!makeAllDirectories(m_localStorageDirectory)) { LOG_ERROR("Unable to create LocalStorage database path %s", m_localStorageDirectory.utf8().data()); return String(); } return pathByAppendingComponent(m_localStorageDirectory, filename); }
CookieManager::CookieManager() : m_count(0) { m_cookieJarFileName = pathByAppendingComponent(OWB_DATA, "cookieCollection.db"); // We force the cookie backing store to be open with the cookie jar to avoid // calling cookieManager() again and recursively calling this constructor. cookieBackingStore().open(cookieJar()); getBackingStoreCookies(); }
CookieManager::CookieManager() : m_count(0) , m_privateMode(false) , m_shouldDumpAllCookies(false) , m_cookieJarFileName(pathByAppendingComponent(BlackBerry::Platform::Client::get()->getApplicationDataDirectory().c_str(), "/cookieCollection.db")) , m_policy(CookieStorageAcceptPolicyAlways) , m_cookieBackingStore(CookieDatabaseBackingStore::create()) , m_limitTimer(this, &CookieManager::cookieLimitCleanUp) { }
static String findWebKitProcess(const char* processName) { static const char* execDirectory = g_getenv("WEBKIT_EXEC_PATH"); if (execDirectory) { String processPath = pathByAppendingComponent(filenameToString(execDirectory), processName); if (fileExists(processPath)) return processPath; } static String executablePath = getExecutablePath(); if (!executablePath.isNull()) { String processPath = pathByAppendingComponent(executablePath, processName); if (fileExists(processPath)) return processPath; } // FIXME: Define and use LIBEXECDIR. return String(processName); }
void DatabaseProcess::deleteIndexedDatabaseEntriesForOrigin(const SecurityOriginData& originData) { if (m_indexedDatabaseDirectory.isEmpty()) return; RefPtr<SecurityOrigin> origin = originData.securityOrigin(); String databaseIdentifier = origin->databaseIdentifier(); String originPath = pathByAppendingComponent(m_indexedDatabaseDirectory, databaseIdentifier); removeAllDatabasesForOriginPath(originPath, std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max()); }
void IDBServer::performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, uint64_t callbackID) { if (!m_databaseDirectoryPath.isEmpty()) { for (const auto& origin : origins) { String originPath = pathByAppendingComponent(m_databaseDirectoryPath, origin.securityOrigin()->databaseIdentifier()); removeAllDatabasesForOriginPath(originPath, std::chrono::system_clock::time_point::min()); } } postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID)); }
static String findWebKitProcess(const char* processName) { #if ENABLE(DEVELOPER_MODE) static const char* execDirectory = g_getenv("WEBKIT_EXEC_PATH"); if (execDirectory) { String processPath = pathByAppendingComponent(filenameToString(execDirectory), processName); if (fileExists(processPath)) return processPath; } static String executablePath = getExecutablePath(); if (!executablePath.isNull()) { String processPath = pathByAppendingComponent(executablePath, processName); if (fileExists(processPath)) return processPath; } #endif return pathByAppendingComponent(filenameToString(PKGLIBEXECDIR), processName); }
void DatabaseProcess::deleteIndexedDatabaseEntriesForOrigins(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins) { if (m_indexedDatabaseDirectory.isEmpty()) return; for (const auto& securityOrigin : securityOrigins) { String originPath = pathByAppendingComponent(m_indexedDatabaseDirectory, securityOrigin->databaseIdentifier()); removeAllDatabasesForOriginPath(originPath, std::chrono::system_clock::time_point::min()); } }
void SQLiteIDBTransaction::moveBlobFilesIfNecessary() { String databaseDirectory = m_backingStore.fullDatabaseDirectory(); for (auto& entry : m_blobTemporaryAndStoredFilenames) { m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first); if (!hardLinkOrCopyFile(entry.first, pathByAppendingComponent(databaseDirectory, entry.second))) LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), pathByAppendingComponent(databaseDirectory, entry.second).utf8().data()); m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first); } m_blobTemporaryAndStoredFilenames.clear(); }
void SQLiteIDBTransaction::deleteBlobFilesIfNecessary() { if (m_blobRemovedFilenames.isEmpty()) return; String databaseDirectory = m_backingStore.fullDatabaseDirectory(); for (auto& entry : m_blobRemovedFilenames) { String fullPath = pathByAppendingComponent(databaseDirectory, entry); m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(fullPath); m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(fullPath); } m_blobRemovedFilenames.clear(); }
UniqueIDBDatabase::UniqueIDBDatabase(const UniqueIDBDatabaseIdentifier& identifier) : m_identifier(identifier) , m_acceptingNewRequests(true) , m_didGetMetadataFromBackingStore(false) { m_inMemory = !canShareDatabases(identifier.openingOrigin(), identifier.mainFrameOrigin()); if (m_inMemory) return; // Each unique Indexed Database exists in a directory named for the database, which exists in a directory representing its opening origin. m_databaseRelativeDirectory = pathByAppendingComponent(databaseFilenameIdentifier(identifier.openingOrigin()), filenameForDatabaseName()); DatabaseProcess::shared().ensureIndexedDatabaseRelativePathExists(m_databaseRelativeDirectory); }
static String storageDirectory(DWORD pathIdentifier) { Vector<UChar> buffer(MAX_PATH); if (FAILED(SHGetFolderPathW(0, pathIdentifier | CSIDL_FLAG_CREATE, 0, 0, buffer.data()))) return String(); buffer.resize(wcslen(buffer.data())); String directory = String::adopt(buffer); static const String companyNameDirectory = "Apple Computer\\"; directory = pathByAppendingComponent(directory, companyNameDirectory + bundleName()); if (!makeAllDirectories(directory)) return String(); return directory; }
void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) { if (m_database.isOpen()) return; // The cache directory should never be null, but if it for some weird reason is we bail out. if (m_cacheDirectory.isNull()) return; String applicationCachePath = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db"); if (!createIfDoesNotExist && !fileExists(applicationCachePath)) return; makeAllDirectories(m_cacheDirectory); m_database.open(applicationCachePath); if (!m_database.isOpen()) return; verifySchemaVersion(); // Create tables executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, " "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, " "cache INTEGER NOT NULL ON CONFLICT FAIL)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, " "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)"); // When a cache is deleted, all its entries and its whitelist should be deleted. executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheDeleted AFTER DELETE ON Caches" " FOR EACH ROW BEGIN" " DELETE FROM CacheEntries WHERE cache = OLD.id;" " DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;" " DELETE FROM FallbackURLs WHERE cache = OLD.id;" " END"); // When a cache resource is deleted, its data blob should also be deleted. executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources" " FOR EACH ROW BEGIN" " DELETE FROM CacheResourceData WHERE id = OLD.data;" " END"); }
CString openTemporaryFile(const char*, PlatformFileHandle& handle) { handle = INVALID_HANDLE_VALUE; wchar_t tempPath[MAX_PATH]; int tempPathLength = ::GetTempPath(_countof(tempPath), tempPath); if (tempPathLength <= 0 || tempPathLength > _countof(tempPath)) return CString(); HCRYPTPROV hCryptProv = 0; if (!CryptAcquireContext(&hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) return CString(); String proposedPath; while (1) { wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names) const int randomPartLength = 8; if (!CryptGenRandom(hCryptProv, randomPartLength * 2, reinterpret_cast<BYTE*>(tempFile))) break; // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation. // don't include both upper and lowercase since Windows file systems are typically not case sensitive. const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; for (int i = 0; i < randomPartLength; ++i) tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)]; ASSERT(wcslen(tempFile) * 2 == sizeof(tempFile) - 2); proposedPath = pathByAppendingComponent(String(tempPath), String(tempFile)); // use CREATE_NEW to avoid overwriting an existing file with the same name handle = CreateFile(proposedPath.charactersWithNullTermination(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); if (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS) continue; break; } CryptReleaseContext(hCryptProv, 0); if (!isHandleValid(handle)) return CString(); return proposedPath.latin1(); }
void WebContext::ensureDatabaseProcess() { if (m_databaseProcess) return; m_databaseProcess = DatabaseProcessProxy::create(this); DatabaseProcessCreationParameters parameters; // Indexed databases exist in a subdirectory of the "database directory path." // Currently, the top level of that directory contains entities related to WebSQL databases. // We should fix this, and move WebSQL into a subdirectory (https://bugs.webkit.org/show_bug.cgi?id=124807) // In the meantime, an entity name prefixed with three underscores will not conflict with any WebSQL entities. parameters.indexedDatabaseDirectory = pathByAppendingComponent(databaseDirectory(), "___IndexedDB"); SandboxExtension::createHandleForReadWriteDirectory(parameters.indexedDatabaseDirectory, parameters.indexedDatabaseDirectoryExtensionHandle); m_databaseProcess->send(Messages::DatabaseProcess::InitializeDatabaseProcess(parameters), 0); }
void PluginDatabase::updatePersistentMetadataCache() { if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty()) return; makeAllDirectories(persistentMetadataCachePath()); String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename); deleteFile(absoluteCachePath); if (m_plugins.isEmpty()) return; PlatformFileHandle file; file = openFile(absoluteCachePath, OpenForWrite); if (!isHandleValid(file)) { LOG_ERROR("Unable to open plugin metadata cache for saving"); return; } char localSchemaVersion = schemaVersion; if (writeToFile(file, &localSchemaVersion, 1) != 1) { LOG_ERROR("Unable to write plugin metadata cache schema"); closeFile(file); deleteFile(absoluteCachePath); return; } PluginSet::const_iterator end = m_plugins.end(); for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { if (!(writeUTF8String(file, (*it)->path()) && writeTime(file, (*it)->lastModified()) && writeUTF8String(file, (*it)->name()) && writeUTF8String(file, (*it)->description()) && writeUTF8String(file, (*it)->fullMIMEDescription()))) { LOG_ERROR("Unable to write plugin metadata to cache"); closeFile(file); deleteFile(absoluteCachePath); return; } } closeFile(file); }
static String storageDirectory(DWORD pathIdentifier) { #if OS(WINCE) return String(); #else Vector<UChar> buffer(MAX_PATH); if (FAILED(SHGetFolderPathW(0, pathIdentifier | CSIDL_FLAG_CREATE, 0, 0, buffer.data()))) return String(); buffer.resize(wcslen(buffer.data())); String directory = String::adopt(buffer); DEFINE_STATIC_LOCAL(String, companyNameDirectory, (ASCIILiteral("Apple Computer\\"))); directory = pathByAppendingComponent(directory, companyNameDirectory + bundleName()); if (!makeAllDirectories(directory)) return String(); return directory; #endif }
String LocalStorage::fullDatabaseFilename(SecurityOrigin* origin) { // FIXME: Once we actually track origin/quota entries to see which origins have local storage established, // we will return an empty path name if the origin isn't allowed to have LocalStorage. // We'll need to wait here until the AreaImport task to complete before making that decision. if (m_path.isEmpty()) return String(); ASSERT(origin); if (!origin) return String(); if (!makeAllDirectories(m_path)) { LOG_ERROR("Unabled to create LocalStorage database path %s", m_path.utf8().data()); return String(); } return pathByAppendingComponent(m_path, origin->databaseIdentifier() + ".localstorage"); }
std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::getOrEstablishMetadata() { ASSERT(!isMainThread()); String dbFilename = pathByAppendingComponent(m_absoluteDatabaseDirectory, "IndexedDB.sqlite3"); m_sqliteDB = openSQLiteDatabaseAtPath(dbFilename); if (!m_sqliteDB) return nullptr; std::unique_ptr<IDBDatabaseMetadata> metadata = extractExistingMetadata(); if (!metadata) metadata = createAndPopulateInitialMetadata(); if (!metadata) LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data()); // The database id is a runtime concept and doesn't need to be stored in the metadata database. metadata->id = generateDatabaseId(); return metadata; }
static PassOwnPtr<SQLiteDatabase> openSQLiteDatabase(SecurityOrigin* securityOrigin, String name, const String& pathBase, int64_t maximumSize) { String path = ":memory:"; if (!pathBase.isEmpty()) { if (!makeAllDirectories(pathBase)) { // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data()); return 0; } path = pathByAppendingComponent(pathBase, IDBFactoryBackendImpl::databaseFileName(name, securityOrigin)); } OwnPtr<SQLiteDatabase> sqliteDatabase = adoptPtr(new SQLiteDatabase()); if (!sqliteDatabase->open(path)) { // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data()); return 0; } sqliteDatabase->setMaximumSize(maximumSize); return sqliteDatabase.release(); }
String openTemporaryFile(const String&, PlatformFileHandle& handle) { handle = INVALID_HANDLE_VALUE; wchar_t tempPath[MAX_PATH]; int tempPathLength = ::GetTempPathW(WTF_ARRAY_LENGTH(tempPath), tempPath); if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath)) return String(); String proposedPath; do { wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names) const int randomPartLength = 8; cryptographicallyRandomValues(tempFile, randomPartLength * sizeof(wchar_t)); // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation. // don't include both upper and lowercase since Windows file systems are typically not case sensitive. const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; for (int i = 0; i < randomPartLength; ++i) tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)]; ASSERT(wcslen(tempFile) == WTF_ARRAY_LENGTH(tempFile) - 1); proposedPath = pathByAppendingComponent(tempPath, tempFile); if (proposedPath.isEmpty()) break; // use CREATE_NEW to avoid overwriting an existing file with the same name handle = ::CreateFileW(proposedPath.charactersWithNullTermination().data(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); } while (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS); if (!isHandleValid(handle)) return String(); return proposedPath; }
String DatabaseProcess::absoluteIndexedDatabasePathFromDatabaseRelativePath(const String& relativePath) { // FIXME: pathByAppendingComponent() was originally designed to append individual atomic components. // We don't have a function designed to append a multi-component subpath, but we should. return pathByAppendingComponent(m_indexedDatabaseDirectory, relativePath); }