2017-10-21 12:59:27 +00:00
/*
* * 2017 - 10 - 20
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * This file implements a VFS shim that allows an SQLite database to be
* * appended onto the end of some other file , such as an executable .
* *
* * A special record must appear at the end of the file that identifies the
2021-03-05 20:46:53 +00:00
* * file as an appended database and provides the offset to the first page
* * of the exposed content . ( Or , it is the length of the content prefix . )
* * For best performance page 1 should be located at a disk page boundary ,
* * though that is not required .
2017-10-21 12:59:27 +00:00
* *
2017-12-14 16:28:27 +00:00
* * When opening a database using this VFS , the connection might treat
2021-03-05 20:46:53 +00:00
* * the file as an ordinary SQLite database , or it might treat it as a
2021-03-08 12:59:16 +00:00
* * database appended onto some other file . The decision is made by
* * applying the following rules in order :
2017-12-14 16:28:27 +00:00
* *
2021-03-08 12:59:16 +00:00
* * ( 1 ) An empty file is an ordinary database .
2017-12-14 16:28:27 +00:00
* *
2021-03-08 12:59:16 +00:00
* * ( 2 ) If the file ends with the appendvfs trailer string
* * " Start-Of-SQLite3-NNNNNNNN " that file is an appended database .
2017-12-14 16:28:27 +00:00
* *
2021-03-08 13:30:29 +00:00
* * ( 3 ) If the file begins with the standard SQLite prefix string
* * " SQLite format 3 " , that file is an ordinary database .
* *
2017-12-14 16:28:27 +00:00
* * ( 4 ) If none of the above apply and the SQLITE_OPEN_CREATE flag is
* * set , then a new database is appended to the already existing file .
* *
* * ( 5 ) Otherwise , SQLITE_CANTOPEN is returned .
* *
* * To avoid unnecessary complications with the PENDING_BYTE , the size of
2021-03-16 14:12:26 +00:00
* * the file containing the database is limited to 1 GiB . ( 1073741824 bytes )
* * This VFS will not read or write past the 1 GiB mark . This restriction
2021-03-05 20:46:53 +00:00
* * might be lifted in future versions . For now , if you need a larger
* * database , then keep it in a separate file .
2017-10-21 12:59:27 +00:00
* *
2021-03-05 20:46:53 +00:00
* * If the file being opened is a plain database ( not an appended one ) , then
2021-03-08 13:30:29 +00:00
* * this shim is a pass - through into the default underlying VFS . ( rule 3 )
2017-10-21 12:59:27 +00:00
* */
2018-01-07 20:38:10 +00:00
# include "sqlite3ext.h"
2017-10-21 12:59:27 +00:00
SQLITE_EXTENSION_INIT1
# include <string.h>
# include <assert.h>
/* The append mark at the end of the database is:
* *
* * Start - Of - SQLite3 - NNNNNNNN
* * 123456789 123456789 12345
* *
* * The NNNNNNNN represents a 64 - bit big - endian unsigned integer which is
2021-03-05 20:46:53 +00:00
* * the offset to page 1 , and also the length of the prefix content .
2017-10-21 12:59:27 +00:00
*/
# define APND_MARK_PREFIX "Start-Of-SQLite3-"
# define APND_MARK_PREFIX_SZ 17
2021-03-05 20:46:53 +00:00
# define APND_MARK_FOS_SZ 8
# define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
2017-10-21 12:59:27 +00:00
2017-12-14 16:28:27 +00:00
/*
* * Maximum size of the combined prefix + database + append - mark . This
* * must be less than 0x40000000 to avoid locking issues on Windows .
*/
2021-03-16 14:12:26 +00:00
# define APND_MAX_SIZE (0x40000000)
2017-12-14 16:28:27 +00:00
2021-03-05 20:46:53 +00:00
/*
2021-03-16 14:12:26 +00:00
* * Try to align the database to an even multiple of APND_ROUNDUP bytes .
2021-03-05 20:46:53 +00:00
*/
2021-03-16 14:12:26 +00:00
# ifndef APND_ROUNDUP
# define APND_ROUNDUP 4096
2021-03-07 18:55:25 +00:00
# endif
2021-03-16 14:12:26 +00:00
# define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1))
# define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
2021-03-05 20:46:53 +00:00
2017-10-21 12:59:27 +00:00
/*
* * Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs ApndVfs ;
typedef struct ApndFile ApndFile ;
/* Access to a lower-level VFS that (might) implement dynamic loading,
* * access to randomness , etc .
*/
# define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
# define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
2021-03-16 14:12:26 +00:00
/* An open appendvfs file
* *
* * An instance of this structure describes the appended database file .
* * A separate sqlite3_file object is always appended . The appended
* * sqlite3_file object ( which can be accessed using ORIGFILE ( ) ) describes
* * the entire file , including the prefix , the database , and the
* * append - mark .
* *
* * The structure of an AppendVFS database is like this :
* *
* * + - - - - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - +
* * | prefix - file | padding | database | append - mark |
* * + - - - - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - +
* * ^ ^
* * | |
* * iPgOne iMark
* *
* *
* * " prefix file " - file onto which the database has been appended .
* * " padding " - zero or more bytes inserted so that " database "
* * starts on an APND_ROUNDUP boundary
* * " database " - The SQLite database file
* * " append-mark " - The 25 - byte " Start-Of-SQLite3-NNNNNNNN " that indicates
* * the offset from the start of prefix - file to the start
* * of " database " .
* *
* * The size of the database is iMark - iPgOne .
* *
* * The NNNNNNNN in the " Start-Of-SQLite3-NNNNNNNN " suffix is the value
* * of iPgOne stored as a big - ending 64 - bit integer .
* *
* * iMark will be the size of the underlying file minus 25 ( APND_MARKSIZE ) .
* * Or , iMark is - 1 to indicate that it has not yet been written .
*/
2017-10-21 12:59:27 +00:00
struct ApndFile {
2021-03-16 14:12:26 +00:00
sqlite3_file base ; /* Subclass. MUST BE FIRST! */
sqlite3_int64 iPgOne ; /* Offset to the start of the database */
sqlite3_int64 iMark ; /* Offset of the append mark. -1 if unwritten */
/* Always followed by another sqlite3_file that describes the whole file */
2017-10-21 12:59:27 +00:00
} ;
/*
* * Methods for ApndFile
*/
static int apndClose ( sqlite3_file * ) ;
static int apndRead ( sqlite3_file * , void * , int iAmt , sqlite3_int64 iOfst ) ;
static int apndWrite ( sqlite3_file * , const void * , int iAmt , sqlite3_int64 iOfst ) ;
static int apndTruncate ( sqlite3_file * , sqlite3_int64 size ) ;
static int apndSync ( sqlite3_file * , int flags ) ;
static int apndFileSize ( sqlite3_file * , sqlite3_int64 * pSize ) ;
static int apndLock ( sqlite3_file * , int ) ;
static int apndUnlock ( sqlite3_file * , int ) ;
static int apndCheckReservedLock ( sqlite3_file * , int * pResOut ) ;
static int apndFileControl ( sqlite3_file * , int op , void * pArg ) ;
static int apndSectorSize ( sqlite3_file * ) ;
static int apndDeviceCharacteristics ( sqlite3_file * ) ;
static int apndShmMap ( sqlite3_file * , int iPg , int pgsz , int , void volatile * * ) ;
static int apndShmLock ( sqlite3_file * , int offset , int n , int flags ) ;
static void apndShmBarrier ( sqlite3_file * ) ;
static int apndShmUnmap ( sqlite3_file * , int deleteFlag ) ;
static int apndFetch ( sqlite3_file * , sqlite3_int64 iOfst , int iAmt , void * * pp ) ;
static int apndUnfetch ( sqlite3_file * , sqlite3_int64 iOfst , void * p ) ;
/*
* * Methods for ApndVfs
*/
static int apndOpen ( sqlite3_vfs * , const char * , sqlite3_file * , int , int * ) ;
static int apndDelete ( sqlite3_vfs * , const char * zName , int syncDir ) ;
static int apndAccess ( sqlite3_vfs * , const char * zName , int flags , int * ) ;
static int apndFullPathname ( sqlite3_vfs * , const char * zName , int , char * zOut ) ;
static void * apndDlOpen ( sqlite3_vfs * , const char * zFilename ) ;
static void apndDlError ( sqlite3_vfs * , int nByte , char * zErrMsg ) ;
static void ( * apndDlSym ( sqlite3_vfs * pVfs , void * p , const char * zSym ) ) ( void ) ;
static void apndDlClose ( sqlite3_vfs * , void * ) ;
static int apndRandomness ( sqlite3_vfs * , int nByte , char * zOut ) ;
static int apndSleep ( sqlite3_vfs * , int microseconds ) ;
static int apndCurrentTime ( sqlite3_vfs * , double * ) ;
static int apndGetLastError ( sqlite3_vfs * , int , char * ) ;
static int apndCurrentTimeInt64 ( sqlite3_vfs * , sqlite3_int64 * ) ;
static int apndSetSystemCall ( sqlite3_vfs * , const char * , sqlite3_syscall_ptr ) ;
static sqlite3_syscall_ptr apndGetSystemCall ( sqlite3_vfs * , const char * z ) ;
static const char * apndNextSystemCall ( sqlite3_vfs * , const char * zName ) ;
static sqlite3_vfs apnd_vfs = {
3 , /* iVersion (set when registered) */
0 , /* szOsFile (set when registered) */
1024 , /* mxPathname */
0 , /* pNext */
" apndvfs " , /* zName */
0 , /* pAppData (set when registered) */
apndOpen , /* xOpen */
apndDelete , /* xDelete */
apndAccess , /* xAccess */
apndFullPathname , /* xFullPathname */
apndDlOpen , /* xDlOpen */
apndDlError , /* xDlError */
apndDlSym , /* xDlSym */
apndDlClose , /* xDlClose */
apndRandomness , /* xRandomness */
apndSleep , /* xSleep */
apndCurrentTime , /* xCurrentTime */
apndGetLastError , /* xGetLastError */
apndCurrentTimeInt64 , /* xCurrentTimeInt64 */
apndSetSystemCall , /* xSetSystemCall */
apndGetSystemCall , /* xGetSystemCall */
apndNextSystemCall /* xNextSystemCall */
} ;
static const sqlite3_io_methods apnd_io_methods = {
3 , /* iVersion */
apndClose , /* xClose */
apndRead , /* xRead */
apndWrite , /* xWrite */
apndTruncate , /* xTruncate */
apndSync , /* xSync */
apndFileSize , /* xFileSize */
apndLock , /* xLock */
apndUnlock , /* xUnlock */
apndCheckReservedLock , /* xCheckReservedLock */
apndFileControl , /* xFileControl */
apndSectorSize , /* xSectorSize */
apndDeviceCharacteristics , /* xDeviceCharacteristics */
apndShmMap , /* xShmMap */
apndShmLock , /* xShmLock */
apndShmBarrier , /* xShmBarrier */
apndShmUnmap , /* xShmUnmap */
apndFetch , /* xFetch */
apndUnfetch /* xUnfetch */
} ;
/*
* * Close an apnd - file .
*/
static int apndClose ( sqlite3_file * pFile ) {
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xClose ( pFile ) ;
}
/*
* * Read data from an apnd - file .
*/
static int apndRead (
sqlite3_file * pFile ,
void * zBuf ,
int iAmt ,
sqlite_int64 iOfst
) {
2021-03-07 18:55:25 +00:00
ApndFile * paf = ( ApndFile * ) pFile ;
2017-10-21 12:59:27 +00:00
pFile = ORIGFILE ( pFile ) ;
2021-03-07 18:55:25 +00:00
return pFile - > pMethods - > xRead ( pFile , zBuf , iAmt , paf - > iPgOne + iOfst ) ;
2017-10-21 12:59:27 +00:00
}
2017-12-14 16:28:27 +00:00
/*
2021-03-05 20:46:53 +00:00
* * Add the append - mark onto what should become the end of the file .
2021-03-07 18:55:25 +00:00
* If and only if this succeeds , internal ApndFile . iMark is updated .
* Parameter iWriteEnd is the appendvfs - relative offset of the new mark .
2017-12-14 16:28:27 +00:00
*/
2021-03-05 20:46:53 +00:00
static int apndWriteMark (
2021-03-07 18:55:25 +00:00
ApndFile * paf ,
2021-03-05 20:46:53 +00:00
sqlite3_file * pFile ,
2021-03-07 18:55:25 +00:00
sqlite_int64 iWriteEnd
2021-03-05 20:46:53 +00:00
) {
2021-03-07 18:55:25 +00:00
sqlite_int64 iPgOne = paf - > iPgOne ;
2017-12-14 16:28:27 +00:00
unsigned char a [ APND_MARK_SIZE ] ;
2021-03-07 18:55:25 +00:00
int i = APND_MARK_FOS_SZ ;
int rc ;
assert ( pFile = = ORIGFILE ( paf ) ) ;
2017-12-14 16:28:27 +00:00
memcpy ( a , APND_MARK_PREFIX , APND_MARK_PREFIX_SZ ) ;
2021-03-16 14:12:26 +00:00
while ( - - i > = 0 ) {
2021-03-07 18:55:25 +00:00
a [ APND_MARK_PREFIX_SZ + i ] = ( unsigned char ) ( iPgOne & 0xff ) ;
iPgOne > > = 8 ;
2021-03-05 20:46:53 +00:00
}
2021-03-07 18:55:25 +00:00
iWriteEnd + = paf - > iPgOne ;
2021-03-05 20:46:53 +00:00
if ( SQLITE_OK = = ( rc = pFile - > pMethods - > xWrite
2021-03-07 18:55:25 +00:00
( pFile , a , APND_MARK_SIZE , iWriteEnd ) ) ) {
paf - > iMark = iWriteEnd ;
2017-12-14 16:28:27 +00:00
}
2021-03-05 20:46:53 +00:00
return rc ;
2017-12-14 16:28:27 +00:00
}
2017-10-21 12:59:27 +00:00
/*
* * Write data to an apnd - file .
*/
static int apndWrite (
sqlite3_file * pFile ,
2017-12-14 16:28:27 +00:00
const void * zBuf ,
2017-10-21 12:59:27 +00:00
int iAmt ,
sqlite_int64 iOfst
) {
2021-03-07 18:55:25 +00:00
ApndFile * paf = ( ApndFile * ) pFile ;
sqlite_int64 iWriteEnd = iOfst + iAmt ;
if ( iWriteEnd > = APND_MAX_SIZE ) return SQLITE_FULL ;
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
2021-03-07 18:55:25 +00:00
/* If append-mark is absent or will be overwritten, write it. */
if ( paf - > iMark < 0 | | paf - > iPgOne + iWriteEnd > paf - > iMark ) {
int rc = apndWriteMark ( paf , pFile , iWriteEnd ) ;
2021-03-16 14:12:26 +00:00
if ( SQLITE_OK ! = rc ) return rc ;
2017-12-14 16:28:27 +00:00
}
2021-03-07 18:55:25 +00:00
return pFile - > pMethods - > xWrite ( pFile , zBuf , iAmt , paf - > iPgOne + iOfst ) ;
2017-10-21 12:59:27 +00:00
}
/*
* * Truncate an apnd - file .
*/
static int apndTruncate ( sqlite3_file * pFile , sqlite_int64 size ) {
2021-03-07 18:55:25 +00:00
ApndFile * paf = ( ApndFile * ) pFile ;
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
2021-03-07 18:55:25 +00:00
/* The append mark goes out first so truncate failure does not lose it. */
2021-03-16 14:12:26 +00:00
if ( SQLITE_OK ! = apndWriteMark ( paf , pFile , size ) ) return SQLITE_IOERR ;
2021-03-07 18:55:25 +00:00
/* Truncate underlying file just past append mark */
return pFile - > pMethods - > xTruncate ( pFile , paf - > iMark + APND_MARK_SIZE ) ;
2017-10-21 12:59:27 +00:00
}
/*
* * Sync an apnd - file .
*/
static int apndSync ( sqlite3_file * pFile , int flags ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xSync ( pFile , flags ) ;
2017-10-21 12:59:27 +00:00
}
/*
* * Return the current file - size of an apnd - file .
2021-03-05 20:46:53 +00:00
* * If the append mark is not yet there , the file - size is 0.
2017-10-21 12:59:27 +00:00
*/
static int apndFileSize ( sqlite3_file * pFile , sqlite_int64 * pSize ) {
2021-03-05 20:46:53 +00:00
ApndFile * paf = ( ApndFile * ) pFile ;
* pSize = ( paf - > iMark > = 0 ) ? ( paf - > iMark - paf - > iPgOne ) : 0 ;
return SQLITE_OK ;
2017-10-21 12:59:27 +00:00
}
/*
* * Lock an apnd - file .
*/
static int apndLock ( sqlite3_file * pFile , int eLock ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xLock ( pFile , eLock ) ;
2017-10-21 12:59:27 +00:00
}
/*
* * Unlock an apnd - file .
*/
static int apndUnlock ( sqlite3_file * pFile , int eLock ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xUnlock ( pFile , eLock ) ;
2017-10-21 12:59:27 +00:00
}
/*
* * Check if another file - handle holds a RESERVED lock on an apnd - file .
*/
static int apndCheckReservedLock ( sqlite3_file * pFile , int * pResOut ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xCheckReservedLock ( pFile , pResOut ) ;
2017-10-21 12:59:27 +00:00
}
/*
* * File control method . For custom operations on an apnd - file .
*/
static int apndFileControl ( sqlite3_file * pFile , int op , void * pArg ) {
2021-03-07 18:55:25 +00:00
ApndFile * paf = ( ApndFile * ) pFile ;
2017-10-21 12:59:27 +00:00
int rc ;
pFile = ORIGFILE ( pFile ) ;
2021-03-11 13:43:29 +00:00
if ( op = = SQLITE_FCNTL_SIZE_HINT ) * ( sqlite3_int64 * ) pArg + = paf - > iPgOne ;
2017-10-21 12:59:27 +00:00
rc = pFile - > pMethods - > xFileControl ( pFile , op , pArg ) ;
if ( rc = = SQLITE_OK & & op = = SQLITE_FCNTL_VFSNAME ) {
2021-03-08 12:59:16 +00:00
* ( char * * ) pArg = sqlite3_mprintf ( " apnd(%lld)/%z " , paf - > iPgOne , * ( char * * ) pArg ) ;
2017-10-21 12:59:27 +00:00
}
return rc ;
}
/*
* * Return the sector - size in bytes for an apnd - file .
*/
static int apndSectorSize ( sqlite3_file * pFile ) {
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xSectorSize ( pFile ) ;
}
/*
* * Return the device characteristic flags supported by an apnd - file .
*/
static int apndDeviceCharacteristics ( sqlite3_file * pFile ) {
pFile = ORIGFILE ( pFile ) ;
2017-12-14 16:57:11 +00:00
return pFile - > pMethods - > xDeviceCharacteristics ( pFile ) ;
2017-10-21 12:59:27 +00:00
}
/* Create a shared memory file mapping */
static int apndShmMap (
sqlite3_file * pFile ,
int iPg ,
int pgsz ,
int bExtend ,
void volatile * * pp
) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xShmMap ( pFile , iPg , pgsz , bExtend , pp ) ;
2017-10-21 12:59:27 +00:00
}
/* Perform locking on a shared-memory segment */
static int apndShmLock ( sqlite3_file * pFile , int offset , int n , int flags ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xShmLock ( pFile , offset , n , flags ) ;
2017-10-21 12:59:27 +00:00
}
/* Memory barrier operation on shared memory */
static void apndShmBarrier ( sqlite3_file * pFile ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
pFile - > pMethods - > xShmBarrier ( pFile ) ;
2017-10-21 12:59:27 +00:00
}
/* Unmap a shared memory segment */
static int apndShmUnmap ( sqlite3_file * pFile , int deleteFlag ) {
2017-12-14 16:28:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xShmUnmap ( pFile , deleteFlag ) ;
2017-10-21 12:59:27 +00:00
}
/* Fetch a page of a memory-mapped file */
static int apndFetch (
sqlite3_file * pFile ,
sqlite3_int64 iOfst ,
int iAmt ,
void * * pp
) {
ApndFile * p = ( ApndFile * ) pFile ;
2021-03-16 14:12:26 +00:00
if ( p - > iMark < 0 | | iOfst + iAmt > p - > iMark ) {
2021-03-05 20:46:53 +00:00
return SQLITE_IOERR ; /* Cannot read what is not yet there. */
2021-03-16 14:12:26 +00:00
}
2017-10-21 12:59:27 +00:00
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xFetch ( pFile , iOfst + p - > iPgOne , iAmt , pp ) ;
}
/* Release a memory-mapped page */
static int apndUnfetch ( sqlite3_file * pFile , sqlite3_int64 iOfst , void * pPage ) {
ApndFile * p = ( ApndFile * ) pFile ;
pFile = ORIGFILE ( pFile ) ;
return pFile - > pMethods - > xUnfetch ( pFile , iOfst + p - > iPgOne , pPage ) ;
}
2017-12-14 16:28:27 +00:00
/*
* * Try to read the append - mark off the end of a file . Return the
2021-03-05 20:46:53 +00:00
* * start of the appended database if the append - mark is present .
* * If there is no valid append - mark , return - 1 ;
2021-03-08 13:45:08 +00:00
* *
* * An append - mark is only valid if the NNNNNNNN start - of - database offset
* * indicates that the appended database contains at least one page . The
* * start - of - database value must be a multiple of 512.
2017-12-14 16:28:27 +00:00
*/
static sqlite3_int64 apndReadMark ( sqlite3_int64 sz , sqlite3_file * pFile ) {
int rc , i ;
sqlite3_int64 iMark ;
2021-03-05 20:46:53 +00:00
int msbs = 8 * ( APND_MARK_FOS_SZ - 1 ) ;
2017-12-14 16:28:27 +00:00
unsigned char a [ APND_MARK_SIZE ] ;
2021-03-05 20:46:53 +00:00
if ( APND_MARK_SIZE ! = ( sz & 0x1ff ) ) return - 1 ;
2017-12-14 16:28:27 +00:00
rc = pFile - > pMethods - > xRead ( pFile , a , APND_MARK_SIZE , sz - APND_MARK_SIZE ) ;
if ( rc ) return - 1 ;
if ( memcmp ( a , APND_MARK_PREFIX , APND_MARK_PREFIX_SZ ) ! = 0 ) return - 1 ;
2021-03-05 20:46:53 +00:00
iMark = ( ( sqlite3_int64 ) ( a [ APND_MARK_PREFIX_SZ ] & 0x7f ) ) < < msbs ;
for ( i = 1 ; i < 8 ; i + + ) {
msbs - = 8 ;
iMark | = ( sqlite3_int64 ) a [ APND_MARK_PREFIX_SZ + i ] < < msbs ;
2017-12-14 16:28:27 +00:00
}
2021-03-08 13:45:08 +00:00
if ( iMark > ( sz - APND_MARK_SIZE - 512 ) ) return - 1 ;
if ( iMark & 0x1ff ) return - 1 ;
2017-12-14 16:28:27 +00:00
return iMark ;
}
2021-03-05 20:46:53 +00:00
static const char apvfsSqliteHdr [ ] = " SQLite format 3 " ;
/*
* * Check to see if the file is an appendvfs SQLite database file .
* * Return true iff it is such . Parameter sz is the file ' s size .
*/
static int apndIsAppendvfsDatabase ( sqlite3_int64 sz , sqlite3_file * pFile ) {
int rc ;
char zHdr [ 16 ] ;
sqlite3_int64 iMark = apndReadMark ( sz , pFile ) ;
if ( iMark > = 0 ) {
2021-03-16 14:12:26 +00:00
/* If file has the correct end-marker, the expected odd size, and the
* * SQLite DB type marker where the end - marker puts it , then it
* * is an appendvfs database .
*/
2021-03-05 20:46:53 +00:00
rc = pFile - > pMethods - > xRead ( pFile , zHdr , sizeof ( zHdr ) , iMark ) ;
2021-03-16 14:12:26 +00:00
if ( SQLITE_OK = = rc
& & memcmp ( zHdr , apvfsSqliteHdr , sizeof ( zHdr ) ) = = 0
& & ( sz & 0x1ff ) = = APND_MARK_SIZE
& & sz > = 512 + APND_MARK_SIZE
) {
2021-03-05 20:46:53 +00:00
return 1 ; /* It's an appendvfs database */
2021-03-16 14:12:26 +00:00
}
2021-03-05 20:46:53 +00:00
}
return 0 ;
}
/*
* * Check to see if the file is an ordinary SQLite database file .
* * Return true iff so . Parameter sz is the file ' s size .
*/
static int apndIsOrdinaryDatabaseFile ( sqlite3_int64 sz , sqlite3_file * pFile ) {
char zHdr [ 16 ] ;
2021-03-08 13:30:29 +00:00
if ( apndIsAppendvfsDatabase ( sz , pFile ) /* rule 2 */
2021-03-08 12:59:16 +00:00
| | ( sz & 0x1ff ) ! = 0
| | SQLITE_OK ! = pFile - > pMethods - > xRead ( pFile , zHdr , sizeof ( zHdr ) , 0 )
| | memcmp ( zHdr , apvfsSqliteHdr , sizeof ( zHdr ) ) ! = 0
) {
2021-03-05 20:46:53 +00:00
return 0 ;
2021-03-08 12:59:16 +00:00
} else {
return 1 ;
}
2021-03-05 20:46:53 +00:00
}
2017-10-21 12:59:27 +00:00
/*
* * Open an apnd file handle .
*/
static int apndOpen (
2021-03-16 06:41:51 +00:00
sqlite3_vfs * pApndVfs ,
2017-10-21 12:59:27 +00:00
const char * zName ,
sqlite3_file * pFile ,
int flags ,
int * pOutFlags
) {
2021-03-16 06:41:51 +00:00
ApndFile * pApndFile = ( ApndFile * ) pFile ;
sqlite3_file * pBaseFile = ORIGFILE ( pFile ) ;
sqlite3_vfs * pBaseVfs = ORIGVFS ( pApndVfs ) ;
2017-10-21 12:59:27 +00:00
int rc ;
2021-03-17 19:05:59 +00:00
sqlite3_int64 sz = 0 ;
2017-10-21 12:59:27 +00:00
if ( ( flags & SQLITE_OPEN_MAIN_DB ) = = 0 ) {
2021-03-16 06:41:51 +00:00
/* The appendvfs is not to be used for transient or temporary databases.
2021-03-16 14:12:26 +00:00
* * Just use the base VFS open to initialize the given file object and
* * open the underlying file . ( Appendvfs is then unused for this file . )
*/
2021-03-16 06:41:51 +00:00
return pBaseVfs - > xOpen ( pBaseVfs , zName , pFile , flags , pOutFlags ) ;
2017-10-21 12:59:27 +00:00
}
2021-03-16 06:41:51 +00:00
memset ( pApndFile , 0 , sizeof ( ApndFile ) ) ;
2020-07-24 09:17:42 +00:00
pFile - > pMethods = & apnd_io_methods ;
2021-03-16 14:12:26 +00:00
pApndFile - > iMark = - 1 ; /* Append mark not yet written */
2021-03-16 06:41:51 +00:00
rc = pBaseVfs - > xOpen ( pBaseVfs , zName , pBaseFile , flags , pOutFlags ) ;
2021-03-16 14:12:26 +00:00
if ( rc = = SQLITE_OK ) {
rc = pBaseFile - > pMethods - > xFileSize ( pBaseFile , & sz ) ;
2021-04-07 09:31:32 +00:00
if ( rc ) {
pBaseFile - > pMethods - > xClose ( pBaseFile ) ;
}
2021-03-16 14:12:26 +00:00
}
2017-12-14 16:28:27 +00:00
if ( rc ) {
2021-03-16 14:12:26 +00:00
pFile - > pMethods = 0 ;
return rc ;
2017-12-14 16:28:27 +00:00
}
2021-03-16 06:41:51 +00:00
if ( apndIsOrdinaryDatabaseFile ( sz , pBaseFile ) ) {
/* The file being opened appears to be just an ordinary DB. Copy
2021-03-16 14:12:26 +00:00
* * the base dispatch - table so this instance mimics the base VFS .
*/
2021-03-16 06:41:51 +00:00
memmove ( pApndFile , pBaseFile , pBaseVfs - > szOsFile ) ;
2017-12-14 16:28:27 +00:00
return SQLITE_OK ;
}
2021-03-16 06:41:51 +00:00
pApndFile - > iPgOne = apndReadMark ( sz , pFile ) ;
if ( pApndFile - > iPgOne > = 0 ) {
2021-03-16 14:12:26 +00:00
pApndFile - > iMark = sz - APND_MARK_SIZE ; /* Append mark found */
2017-12-14 16:28:27 +00:00
return SQLITE_OK ;
}
if ( ( flags & SQLITE_OPEN_CREATE ) = = 0 ) {
2021-03-16 06:41:51 +00:00
pBaseFile - > pMethods - > xClose ( pBaseFile ) ;
2018-01-06 13:33:21 +00:00
rc = SQLITE_CANTOPEN ;
2021-03-16 14:12:26 +00:00
pFile - > pMethods = 0 ;
} else {
/* Round newly added appendvfs location to #define'd page boundary.
* * Note that nothing has yet been written to the underlying file .
* * The append mark will be written along with first content write .
* * Until then , paf - > iMark value indicates it is not yet written .
*/
pApndFile - > iPgOne = APND_START_ROUNDUP ( sz ) ;
2017-10-21 12:59:27 +00:00
}
2018-01-06 13:33:21 +00:00
return rc ;
2017-10-21 12:59:27 +00:00
}
/*
2021-03-05 20:46:53 +00:00
* * Delete an apnd file .
* * For an appendvfs , this could mean delete the appendvfs portion ,
* * leaving the appendee as it was before it gained an appendvfs .
* * For now , this code deletes the underlying file too .
2017-10-21 12:59:27 +00:00
*/
static int apndDelete ( sqlite3_vfs * pVfs , const char * zPath , int dirSync ) {
return ORIGVFS ( pVfs ) - > xDelete ( ORIGVFS ( pVfs ) , zPath , dirSync ) ;
}
2021-03-05 20:46:53 +00:00
/*
* * All other VFS methods are pass - thrus .
*/
2017-10-21 12:59:27 +00:00
static int apndAccess (
sqlite3_vfs * pVfs ,
const char * zPath ,
int flags ,
int * pResOut
) {
return ORIGVFS ( pVfs ) - > xAccess ( ORIGVFS ( pVfs ) , zPath , flags , pResOut ) ;
}
static int apndFullPathname (
sqlite3_vfs * pVfs ,
const char * zPath ,
int nOut ,
char * zOut
) {
return ORIGVFS ( pVfs ) - > xFullPathname ( ORIGVFS ( pVfs ) , zPath , nOut , zOut ) ;
}
static void * apndDlOpen ( sqlite3_vfs * pVfs , const char * zPath ) {
return ORIGVFS ( pVfs ) - > xDlOpen ( ORIGVFS ( pVfs ) , zPath ) ;
}
static void apndDlError ( sqlite3_vfs * pVfs , int nByte , char * zErrMsg ) {
ORIGVFS ( pVfs ) - > xDlError ( ORIGVFS ( pVfs ) , nByte , zErrMsg ) ;
}
static void ( * apndDlSym ( sqlite3_vfs * pVfs , void * p , const char * zSym ) ) ( void ) {
return ORIGVFS ( pVfs ) - > xDlSym ( ORIGVFS ( pVfs ) , p , zSym ) ;
}
static void apndDlClose ( sqlite3_vfs * pVfs , void * pHandle ) {
ORIGVFS ( pVfs ) - > xDlClose ( ORIGVFS ( pVfs ) , pHandle ) ;
}
static int apndRandomness ( sqlite3_vfs * pVfs , int nByte , char * zBufOut ) {
return ORIGVFS ( pVfs ) - > xRandomness ( ORIGVFS ( pVfs ) , nByte , zBufOut ) ;
}
static int apndSleep ( sqlite3_vfs * pVfs , int nMicro ) {
return ORIGVFS ( pVfs ) - > xSleep ( ORIGVFS ( pVfs ) , nMicro ) ;
}
static int apndCurrentTime ( sqlite3_vfs * pVfs , double * pTimeOut ) {
return ORIGVFS ( pVfs ) - > xCurrentTime ( ORIGVFS ( pVfs ) , pTimeOut ) ;
}
static int apndGetLastError ( sqlite3_vfs * pVfs , int a , char * b ) {
return ORIGVFS ( pVfs ) - > xGetLastError ( ORIGVFS ( pVfs ) , a , b ) ;
}
static int apndCurrentTimeInt64 ( sqlite3_vfs * pVfs , sqlite3_int64 * p ) {
return ORIGVFS ( pVfs ) - > xCurrentTimeInt64 ( ORIGVFS ( pVfs ) , p ) ;
}
static int apndSetSystemCall (
sqlite3_vfs * pVfs ,
const char * zName ,
sqlite3_syscall_ptr pCall
) {
return ORIGVFS ( pVfs ) - > xSetSystemCall ( ORIGVFS ( pVfs ) , zName , pCall ) ;
}
static sqlite3_syscall_ptr apndGetSystemCall (
sqlite3_vfs * pVfs ,
const char * zName
) {
return ORIGVFS ( pVfs ) - > xGetSystemCall ( ORIGVFS ( pVfs ) , zName ) ;
}
static const char * apndNextSystemCall ( sqlite3_vfs * pVfs , const char * zName ) {
return ORIGVFS ( pVfs ) - > xNextSystemCall ( ORIGVFS ( pVfs ) , zName ) ;
}
# ifdef _WIN32
__declspec ( dllexport )
# endif
/*
* * This routine is called when the extension is loaded .
* * Register the new VFS .
*/
int sqlite3_appendvfs_init (
sqlite3 * db ,
char * * pzErrMsg ,
const sqlite3_api_routines * pApi
) {
int rc = SQLITE_OK ;
sqlite3_vfs * pOrig ;
SQLITE_EXTENSION_INIT2 ( pApi ) ;
2018-01-17 13:15:23 +00:00
( void ) pzErrMsg ;
( void ) db ;
2017-10-21 12:59:27 +00:00
pOrig = sqlite3_vfs_find ( 0 ) ;
2021-06-15 15:15:40 +00:00
if ( pOrig = = 0 ) return SQLITE_ERROR ;
2017-10-21 12:59:27 +00:00
apnd_vfs . iVersion = pOrig - > iVersion ;
apnd_vfs . pAppData = pOrig ;
2017-12-14 16:57:11 +00:00
apnd_vfs . szOsFile = pOrig - > szOsFile + sizeof ( ApndFile ) ;
2017-12-14 16:28:27 +00:00
rc = sqlite3_vfs_register ( & apnd_vfs , 0 ) ;
2017-10-21 12:59:27 +00:00
# ifdef APPENDVFS_TEST
if ( rc = = SQLITE_OK ) {
rc = sqlite3_auto_extension ( ( void ( * ) ( void ) ) apndvfsRegister ) ;
}
# endif
if ( rc = = SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY ;
return rc ;
}