| /* |
| * Copyright (C) 2026 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/trace_processor/sqlite/file_system_vfs.h" |
| |
| #include <sqlite3.h> |
| |
| #include <algorithm> |
| #include <atomic> |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| #include <limits> |
| #include <memory> |
| #include <new> |
| #include <string> |
| #include <utility> |
| |
| #include "perfetto/base/compiler.h" |
| #include "perfetto/base/status.h" |
| #include "perfetto/ext/base/status_macros.h" |
| #include "perfetto/trace_processor/io.h" |
| |
| namespace perfetto::trace_processor { |
| namespace { |
| |
| std::atomic<uint64_t> g_next_vfs_id{0}; |
| |
| struct OpenFile { |
| sqlite3_file sqlite_file{}; |
| std::unique_ptr<io::File> file; |
| io::FileSystem* file_system = nullptr; |
| std::string path; |
| bool delete_on_close = false; |
| }; |
| |
| OpenFile* ToOpenFile(sqlite3_file* file) { |
| return reinterpret_cast<OpenFile*>(file); |
| } |
| |
| int Close(sqlite3_file* sqlite_file) { |
| OpenFile* file = ToOpenFile(sqlite_file); |
| file->file.reset(); |
| if (file->delete_on_close) { |
| base::ignore_result(file->file_system->DeleteFile(file->path)); |
| } |
| file->~OpenFile(); |
| return SQLITE_OK; |
| } |
| |
| int Read(sqlite3_file* sqlite_file, |
| void* data, |
| int size, |
| sqlite3_int64 offset) { |
| if (size < 0 || offset < 0) { |
| return SQLITE_IOERR_READ; |
| } |
| OpenFile* file = ToOpenFile(sqlite_file); |
| size_t bytes_read = 0; |
| if (!file->file |
| ->ReadAt(static_cast<uint64_t>(offset), data, |
| static_cast<size_t>(size), &bytes_read) |
| .ok()) { |
| return SQLITE_IOERR_READ; |
| } |
| if (bytes_read != static_cast<size_t>(size)) { |
| auto* bytes = static_cast<uint8_t*>(data); |
| std::fill(bytes + bytes_read, bytes + size, 0); |
| return SQLITE_IOERR_SHORT_READ; |
| } |
| return SQLITE_OK; |
| } |
| |
| int Write(sqlite3_file* sqlite_file, |
| const void* data, |
| int size, |
| sqlite3_int64 offset) { |
| if (size < 0 || offset < 0) { |
| return SQLITE_IOERR_WRITE; |
| } |
| base::Status status = ToOpenFile(sqlite_file) |
| ->file->WriteAt(static_cast<uint64_t>(offset), data, |
| static_cast<size_t>(size)); |
| return status.ok() ? SQLITE_OK : SQLITE_IOERR_WRITE; |
| } |
| |
| int Truncate(sqlite3_file* sqlite_file, sqlite3_int64 size) { |
| if (size < 0) { |
| return SQLITE_IOERR_TRUNCATE; |
| } |
| base::Status status = |
| ToOpenFile(sqlite_file)->file->Truncate(static_cast<uint64_t>(size)); |
| return status.ok() ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; |
| } |
| |
| int Sync(sqlite3_file* sqlite_file, int) { |
| return ToOpenFile(sqlite_file)->file->Flush().ok() ? SQLITE_OK |
| : SQLITE_IOERR_FSYNC; |
| } |
| |
| int FileSize(sqlite3_file* sqlite_file, sqlite3_int64* size) { |
| uint64_t result = 0; |
| if (!ToOpenFile(sqlite_file)->file->GetSize(&result).ok() || |
| result > |
| static_cast<uint64_t>(std::numeric_limits<sqlite3_int64>::max())) { |
| return SQLITE_IOERR_FSTAT; |
| } |
| *size = static_cast<sqlite3_int64>(result); |
| return SQLITE_OK; |
| } |
| |
| int Lock(sqlite3_file*, int) { |
| return SQLITE_OK; |
| } |
| |
| int Unlock(sqlite3_file*, int) { |
| return SQLITE_OK; |
| } |
| |
| int CheckReservedLock(sqlite3_file*, int* result) { |
| *result = 0; |
| return SQLITE_OK; |
| } |
| |
| int FileControl(sqlite3_file*, int, void*) { |
| return SQLITE_NOTFOUND; |
| } |
| |
| int SectorSize(sqlite3_file*) { |
| return 4096; |
| } |
| |
| int DeviceCharacteristics(sqlite3_file*) { |
| return 0; |
| } |
| |
| const sqlite3_io_methods kIoMethods = { |
| 1, |
| &Close, |
| &Read, |
| &Write, |
| &Truncate, |
| &Sync, |
| &FileSize, |
| &Lock, |
| &Unlock, |
| &CheckReservedLock, |
| &FileControl, |
| &SectorSize, |
| &DeviceCharacteristics, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| }; |
| |
| } // namespace |
| |
| class SqliteFileSystemVfs::Impl { |
| public: |
| // |file_system| is borrowed and must outlive this VFS. |
| explicit Impl(io::FileSystem* file_system) |
| : file_system_(file_system), |
| name_("perfetto_file_system_" + |
| std::to_string(g_next_vfs_id.fetch_add(1))) { |
| parent_ = sqlite3_vfs_find(nullptr); |
| vfs_.iVersion = 1; |
| vfs_.szOsFile = static_cast<int>(sizeof(OpenFile)); |
| vfs_.mxPathname = parent_ ? parent_->mxPathname : 1024; |
| vfs_.zName = name_.c_str(); |
| vfs_.pAppData = this; |
| vfs_.xOpen = &Open; |
| vfs_.xDelete = &Delete; |
| vfs_.xAccess = &Access; |
| vfs_.xFullPathname = &FullPathname; |
| vfs_.xDlOpen = &DlOpen; |
| vfs_.xDlError = &DlError; |
| vfs_.xDlSym = &DlSym; |
| vfs_.xDlClose = &DlClose; |
| vfs_.xRandomness = &Randomness; |
| vfs_.xSleep = &Sleep; |
| vfs_.xCurrentTime = &CurrentTime; |
| vfs_.xGetLastError = &GetLastError; |
| } |
| |
| base::Status Register() { |
| if (sqlite3_vfs_register(&vfs_, 0) != SQLITE_OK) { |
| return base::ErrStatus("Failed to register SQLite file system VFS"); |
| } |
| registered_ = true; |
| return base::OkStatus(); |
| } |
| |
| ~Impl() { |
| if (registered_) { |
| sqlite3_vfs_unregister(&vfs_); |
| } |
| } |
| |
| const char* name() const { return name_.c_str(); } |
| |
| private: |
| static Impl* From(sqlite3_vfs* vfs) { |
| return static_cast<Impl*>(vfs->pAppData); |
| } |
| |
| static int Open(sqlite3_vfs* vfs, |
| const char* path, |
| sqlite3_file* sqlite_file, |
| int flags, |
| int* out_flags) { |
| Impl* self = From(vfs); |
| std::string actual_path = |
| path ? path |
| : self->name_ + "_temp_" + std::to_string(self->next_temp_id_++); |
| |
| if ((flags & SQLITE_OPEN_EXCLUSIVE) && (flags & SQLITE_OPEN_CREATE)) { |
| bool exists = false; |
| if (!self->file_system_->FileExists(actual_path, &exists).ok() || |
| exists) { |
| return SQLITE_CANTOPEN; |
| } |
| } |
| |
| io::FileOpenOptions options; |
| if (flags & SQLITE_OPEN_READONLY) { |
| options.access = io::FileAccess::kReadOnly; |
| } else { |
| options.access = io::FileAccess::kReadWrite; |
| } |
| options.create = (flags & SQLITE_OPEN_CREATE) != 0; |
| |
| std::unique_ptr<io::File> opened; |
| if (!self->file_system_->OpenFile(actual_path, options, &opened).ok()) { |
| return SQLITE_CANTOPEN; |
| } |
| |
| auto* file = new (sqlite_file) OpenFile(); |
| file->file = std::move(opened); |
| file->file_system = self->file_system_; |
| file->path = std::move(actual_path); |
| file->delete_on_close = (flags & SQLITE_OPEN_DELETEONCLOSE) != 0; |
| file->sqlite_file.pMethods = &kIoMethods; |
| if (out_flags) { |
| *out_flags = flags; |
| } |
| return SQLITE_OK; |
| } |
| |
| static int Delete(sqlite3_vfs* vfs, const char* path, int) { |
| if (!path) { |
| return SQLITE_IOERR_DELETE; |
| } |
| return From(vfs)->file_system_->DeleteFile(path).ok() ? SQLITE_OK |
| : SQLITE_IOERR_DELETE; |
| } |
| |
| static int Access(sqlite3_vfs* vfs, const char* path, int, int* result) { |
| if (!path) { |
| return SQLITE_IOERR_ACCESS; |
| } |
| bool exists = false; |
| if (!From(vfs)->file_system_->FileExists(path, &exists).ok()) { |
| return SQLITE_IOERR_ACCESS; |
| } |
| *result = exists ? 1 : 0; |
| return SQLITE_OK; |
| } |
| |
| static int FullPathname(sqlite3_vfs*, |
| const char* path, |
| int output_size, |
| char* output) { |
| if (!path || output_size <= 0 || |
| strlen(path) >= static_cast<size_t>(output_size)) { |
| return SQLITE_CANTOPEN; |
| } |
| memcpy(output, path, strlen(path) + 1); |
| return SQLITE_OK; |
| } |
| |
| static void* DlOpen(sqlite3_vfs* vfs, const char* path) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| return parent && parent->xDlOpen ? parent->xDlOpen(parent, path) : nullptr; |
| } |
| |
| static void DlError(sqlite3_vfs* vfs, int size, char* error) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| if (parent && parent->xDlError) { |
| parent->xDlError(parent, size, error); |
| } else if (size > 0) { |
| error[0] = '\0'; |
| } |
| } |
| |
| static void (*DlSym(sqlite3_vfs* vfs, void* handle, const char* symbol))() { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| return parent && parent->xDlSym ? parent->xDlSym(parent, handle, symbol) |
| : nullptr; |
| } |
| |
| static void DlClose(sqlite3_vfs* vfs, void* handle) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| if (parent && parent->xDlClose) { |
| parent->xDlClose(parent, handle); |
| } |
| } |
| |
| static int Randomness(sqlite3_vfs* vfs, int size, char* output) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| if (parent && parent->xRandomness) { |
| return parent->xRandomness(parent, size, output); |
| } |
| memset(output, 0, static_cast<size_t>(size)); |
| return size; |
| } |
| |
| static int Sleep(sqlite3_vfs* vfs, int microseconds) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| return parent && parent->xSleep ? parent->xSleep(parent, microseconds) |
| : microseconds; |
| } |
| |
| static int CurrentTime(sqlite3_vfs* vfs, double* time) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| return parent && parent->xCurrentTime ? parent->xCurrentTime(parent, time) |
| : SQLITE_ERROR; |
| } |
| |
| static int GetLastError(sqlite3_vfs* vfs, int size, char* error) { |
| sqlite3_vfs* parent = From(vfs)->parent_; |
| return parent && parent->xGetLastError |
| ? parent->xGetLastError(parent, size, error) |
| : 0; |
| } |
| |
| // Borrowed; must outlive the VFS (see io.h platform contract). |
| io::FileSystem* file_system_ = nullptr; |
| std::string name_; |
| sqlite3_vfs* parent_ = nullptr; |
| sqlite3_vfs vfs_{}; |
| uint64_t next_temp_id_ = 0; |
| bool registered_ = false; |
| }; |
| |
| base::StatusOr<std::unique_ptr<SqliteFileSystemVfs>> |
| SqliteFileSystemVfs::Create(io::FileSystem* file_system) { |
| if (!file_system) { |
| return base::ErrStatus("SQLite file system is null"); |
| } |
| auto impl = std::make_unique<Impl>(file_system); |
| RETURN_IF_ERROR(impl->Register()); |
| return std::unique_ptr<SqliteFileSystemVfs>( |
| new SqliteFileSystemVfs(std::move(impl))); |
| } |
| |
| SqliteFileSystemVfs::SqliteFileSystemVfs(std::unique_ptr<Impl> impl) |
| : impl_(std::move(impl)) {} |
| SqliteFileSystemVfs::~SqliteFileSystemVfs() = default; |
| |
| const char* SqliteFileSystemVfs::name() const { |
| return impl_->name(); |
| } |
| |
| } // namespace perfetto::trace_processor |