// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsql_sqlite_vfs_p.h" #include #include // defines PATH_MAX on unix #include #include // defines FILENAME_MAX everywhere #ifndef PATH_MAX # define PATH_MAX FILENAME_MAX #endif #if SQLITE_VERSION_NUMBER < 3040000 typedef const char *sqlite3_filename; #endif namespace { struct Vfs : sqlite3_vfs { sqlite3_vfs *pVfs; sqlite3_io_methods ioMethods; }; struct File : sqlite3_file { class QtFile : public QFile { public: QtFile(const QString &name, bool removeOnClose) : QFile(name) , removeOnClose(removeOnClose) {} ~QtFile() override { if (removeOnClose) remove(); } private: bool removeOnClose; }; QtFile *pFile; }; int xClose(sqlite3_file *sfile) { auto file = static_cast(sfile); delete file->pFile; file->pFile = nullptr; return SQLITE_OK; } int xRead(sqlite3_file *sfile, void *ptr, int iAmt, sqlite3_int64 iOfst) { auto file = static_cast(sfile); if (!file->pFile->seek(iOfst)) return SQLITE_IOERR_READ; auto sz = file->pFile->read(static_cast(ptr), iAmt); if (sz < iAmt) { memset(static_cast(ptr) + sz, 0, size_t(iAmt - sz)); return SQLITE_IOERR_SHORT_READ; } return SQLITE_OK; } int xWrite(sqlite3_file *sfile, const void *data, int iAmt, sqlite3_int64 iOfst) { auto file = static_cast(sfile); if (!file->pFile->seek(iOfst)) return SQLITE_IOERR_SEEK; return file->pFile->write(reinterpret_cast(data), iAmt) == iAmt ? SQLITE_OK : SQLITE_IOERR_WRITE; } int xTruncate(sqlite3_file *sfile, sqlite3_int64 size) { auto file = static_cast(sfile); return file->pFile->resize(size) ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; } int xSync(sqlite3_file *sfile, int /*flags*/) { static_cast(sfile)->pFile->flush(); return SQLITE_OK; } int xFileSize(sqlite3_file *sfile, sqlite3_int64 *pSize) { auto file = static_cast(sfile); *pSize = file->pFile->size(); return SQLITE_OK; } // No lock/unlock for QFile, QLockFile doesn't work for me int xLock(sqlite3_file *, int) { return SQLITE_OK; } int xUnlock(sqlite3_file *, int) { return SQLITE_OK; } int xCheckReservedLock(sqlite3_file *, int *pResOut) { *pResOut = 0; return SQLITE_OK; } int xFileControl(sqlite3_file *, int, void *) { return SQLITE_NOTFOUND; } int xSectorSize(sqlite3_file *) { return 4096; } int xDeviceCharacteristics(sqlite3_file *) { return 0; // no SQLITE_IOCAP_XXX } int xOpen(sqlite3_vfs *svfs, sqlite3_filename zName, sqlite3_file *sfile, int flags, int *pOutFlags) { auto vfs = static_cast(svfs); auto file = static_cast(sfile); memset(file, 0, sizeof(File)); QIODeviceBase::OpenMode mode = QIODeviceBase::NotOpen; if (!zName || (flags & SQLITE_OPEN_MEMORY)) return SQLITE_PERM; if ((flags & SQLITE_OPEN_READONLY) && !(flags & SQLITE_OPEN_READWRITE) && !(flags & SQLITE_OPEN_CREATE) && !(flags & SQLITE_OPEN_DELETEONCLOSE)) { mode |= QIODeviceBase::OpenModeFlag::ReadOnly; } else { /* ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction ** with the [SQLITE_OPEN_CREATE] flag, which are both directly ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the ** SQLITE_OPEN_CREATE, is used to indicate that file should always ** be created, and that it is an error if it already exists. ** It is not used to indicate the file should be opened ** for exclusive access. */ if ((flags & SQLITE_OPEN_CREATE) && (flags & SQLITE_OPEN_EXCLUSIVE)) mode |= QIODeviceBase::OpenModeFlag::NewOnly; if (flags & SQLITE_OPEN_READWRITE) mode |= QIODeviceBase::OpenModeFlag::ReadWrite; } file->pMethods = &vfs->ioMethods; file->pFile = new File::QtFile(QString::fromUtf8(zName), bool(flags & SQLITE_OPEN_DELETEONCLOSE)); if (!file->pFile->open(mode)) return SQLITE_CANTOPEN; if (pOutFlags) *pOutFlags = flags; return SQLITE_OK; } int xDelete(sqlite3_vfs *, const char *zName, int) { return QFile::remove(QString::fromUtf8(zName)) ? SQLITE_OK : SQLITE_ERROR; } int xAccess(sqlite3_vfs */*svfs*/, const char *zName, int flags, int *pResOut) { *pResOut = 0; switch (flags) { case SQLITE_ACCESS_EXISTS: case SQLITE_ACCESS_READ: *pResOut = QFile::exists(QString::fromUtf8(zName)); break; default: break; } return SQLITE_OK; } int xFullPathname(sqlite3_vfs *, const char *zName, int nOut, char *zOut) { if (!zName) return SQLITE_ERROR; int i = 0; for (;zName[i] && i < nOut; ++i) zOut[i] = zName[i]; if (i >= nOut) return SQLITE_ERROR; zOut[i] = '\0'; return SQLITE_OK; } int xRandomness(sqlite3_vfs *svfs, int nByte, char *zOut) { auto vfs = static_cast(svfs)->pVfs; return vfs->xRandomness(vfs, nByte, zOut); } int xSleep(sqlite3_vfs *svfs, int microseconds) { auto vfs = static_cast(svfs)->pVfs; return vfs->xSleep(vfs, microseconds); } int xCurrentTime(sqlite3_vfs *svfs, double *zOut) { auto vfs = static_cast(svfs)->pVfs; return vfs->xCurrentTime(vfs, zOut); } int xGetLastError(sqlite3_vfs *, int, char *) { return 0; } int xCurrentTimeInt64(sqlite3_vfs *svfs, sqlite3_int64 *zOut) { auto vfs = static_cast(svfs)->pVfs; return vfs->xCurrentTimeInt64(vfs, zOut); } } // namespace { void register_qt_vfs() { static Vfs vfs; memset(&vfs, 0, sizeof(Vfs)); vfs.iVersion = 1; vfs.szOsFile = sizeof(File); vfs.mxPathname = PATH_MAX; vfs.zName = "QtVFS"; vfs.xOpen = &xOpen; vfs.xDelete = &xDelete; vfs.xAccess = &xAccess; vfs.xFullPathname = &xFullPathname; vfs.xRandomness = &xRandomness; vfs.xSleep = &xSleep; vfs.xCurrentTime = &xCurrentTime; vfs.xGetLastError = &xGetLastError; vfs.xCurrentTimeInt64 = &xCurrentTimeInt64; vfs.pVfs = sqlite3_vfs_find(nullptr); vfs.ioMethods.iVersion = 1; vfs.ioMethods.xClose = &xClose; vfs.ioMethods.xRead = &xRead; vfs.ioMethods.xWrite = &xWrite; vfs.ioMethods.xTruncate = &xTruncate; vfs.ioMethods.xSync = &xSync; vfs.ioMethods.xFileSize = &xFileSize; vfs.ioMethods.xLock = &xLock; vfs.ioMethods.xUnlock = &xUnlock; vfs.ioMethods.xCheckReservedLock = &xCheckReservedLock; vfs.ioMethods.xFileControl = &xFileControl; vfs.ioMethods.xSectorSize = &xSectorSize; vfs.ioMethods.xDeviceCharacteristics = &xDeviceCharacteristics; sqlite3_vfs_register(&vfs, 0); }