components/sqlite_vfs/README.md
sqlite_vfs provides a custom SQLite Virtual File System (VFS) for Chromium
that allows sql::Database to operate within sandboxed
processes where direct filesystem access is restricted.
Instead of using standard file paths to access data, this VFS operates on
pre-opened base::File handles that are securely passed to other processes via
mojo.
sqlite_vfs supports:
The fundamental lifecycle for a database is:
base_name for each database that will reside in
the above directory.MakePendingFileSet() to create the files
and configure them as desired. This initial
PendingFileSet provides read/write access to the
database. It can be used as-is or passed to another process for use.SqliteVfsFileSet::Bind()
to consume a PendingFileSet and produce a SqliteVfsFileSet for use by the
process.RegisterSandboxedFiles() to
register the file set's files for use by SQLite.sql::Database, specifying the name of
sqlite_vfs's VFS, and opens the database with the virtual path of the file
set's main database file.Multiple connections to a database can be made (provided that the original call
to MakePendingFileSet() specified single_connection=false) by way of
ShareConnection(). A shared connection can be restricted to
read-only access.
SqliteVfsFileSet::Abandon() can be called
to mark a database as no longer suitable for use by any connection. This causes
most, if not all, operations on the database to fail with SQLITE_IOERR_LOCK
(a.k.a. sql::SqliteResultCode::kIoLock and sql::SqliteErrorCode::kIoLock).
Client code interacting with databases should take this error as a signal to
promptly release all resources and close the database.
If Abandon() returns LockState::kNotHeld, then it is safe to create a new
file set for the same database. Otherwise, DeleteFiles() should
be called to delete the database's files, as the files cannot be used until all
clients have closed their connections, and there is no mechanism by which this
can be coordinated.
MakePendingFileSet() without abandonment as described above will lead to
database corruption. There is no protection against this -- it is the client's
responsibility to never do this.A database using a write-ahead log that supports multiple connections requires a WAL-index file that is mapped into each process's address space. The index is populated on-demand based on the contents of the write-ahead log by the first read/write connection to a database.
To avoid leaving a stale index on-disk in case of abnormal termination,
MakePendingFileSet creates a randomly-named file for the index and ensures
that it is deleted on close. On POSIX systems, the file is unlinked immediately
after creation. On Windows, the file is opened with FILE_FLAG_DELETE_ON_CLOSE
so that the OS deletes it when the last handle is closed.
To allow sharing read-only connections on POSIX, where it is not possible to
reduce access to a file descriptor during duplication, MakePendingFileSet
opens a second descriptor to the WAL-index file with read-only access. This is
passed along to the SqliteVfsFileSet so that it can be used for an eventual
request to share the file set for read-only access.
SandboxedFile::ShmMap maintains an independent mapped
region for each 32KB page of the WAL-index. A read/write connection will grow
the file as-needed to satisfy these mappings. A read-only connection
communicates its nature to SQLite by returning SQLITE_READONLY_CANTINIT for
any request that requires growing the file, and SQLITE_READONLY on success for
any request that it can satisfy. In the former case, SQLite will automatically
maintain a private WAL-index for the connection until a subsequent request
succeeds after a read/write connection has grown the file. Since a read-only
connection cannot, itself, cause the WAL-index to grow, this fallback only
happens if the read-only connection opens the database before any read/write
connection.