xref: /sqlite-3.40.0/ext/misc/appendvfs.c (revision a959bf53)
13be8b1a4Sdrh /*
23be8b1a4Sdrh ** 2017-10-20
33be8b1a4Sdrh **
43be8b1a4Sdrh ** The author disclaims copyright to this source code.  In place of
53be8b1a4Sdrh ** a legal notice, here is a blessing:
63be8b1a4Sdrh **
73be8b1a4Sdrh **    May you do good and not evil.
83be8b1a4Sdrh **    May you find forgiveness for yourself and forgive others.
93be8b1a4Sdrh **    May you share freely, never taking more than you give.
103be8b1a4Sdrh **
113be8b1a4Sdrh ******************************************************************************
123be8b1a4Sdrh **
133be8b1a4Sdrh ** This file implements a VFS shim that allows an SQLite database to be
143be8b1a4Sdrh ** appended onto the end of some other file, such as an executable.
153be8b1a4Sdrh **
163be8b1a4Sdrh ** A special record must appear at the end of the file that identifies the
17feecc9f5Slarrybr ** file as an appended database and provides the offset to the first page
18feecc9f5Slarrybr ** of the exposed content. (Or, it is the length of the content prefix.)
19feecc9f5Slarrybr ** For best performance page 1 should be located at a disk page boundary,
20feecc9f5Slarrybr ** though that is not required.
213be8b1a4Sdrh **
22e483d349Sdrh ** When opening a database using this VFS, the connection might treat
23feecc9f5Slarrybr ** the file as an ordinary SQLite database, or it might treat it as a
2440942f22Sdrh ** database appended onto some other file.  The decision is made by
2540942f22Sdrh ** applying the following rules in order:
26e483d349Sdrh **
2740942f22Sdrh **  (1)  An empty file is an ordinary database.
28e483d349Sdrh **
2940942f22Sdrh **  (2)  If the file ends with the appendvfs trailer string
3040942f22Sdrh **       "Start-Of-SQLite3-NNNNNNNN" that file is an appended database.
31e483d349Sdrh **
321e6f3346Sdrh **  (3)  If the file begins with the standard SQLite prefix string
331e6f3346Sdrh **       "SQLite format 3", that file is an ordinary database.
341e6f3346Sdrh **
35e483d349Sdrh **  (4)  If none of the above apply and the SQLITE_OPEN_CREATE flag is
36e483d349Sdrh **       set, then a new database is appended to the already existing file.
37e483d349Sdrh **
38e483d349Sdrh **  (5)  Otherwise, SQLITE_CANTOPEN is returned.
39e483d349Sdrh **
40e483d349Sdrh ** To avoid unnecessary complications with the PENDING_BYTE, the size of
413fee6753Sdrh ** the file containing the database is limited to 1GiB. (1073741824 bytes)
423fee6753Sdrh ** This VFS will not read or write past the 1GiB mark.  This restriction
43feecc9f5Slarrybr ** might be lifted in future versions.  For now, if you need a larger
44feecc9f5Slarrybr ** database, then keep it in a separate file.
453be8b1a4Sdrh **
46feecc9f5Slarrybr ** If the file being opened is a plain database (not an appended one), then
471e6f3346Sdrh ** this shim is a pass-through into the default underlying VFS. (rule 3)
483be8b1a4Sdrh **/
498682e121Sdrh #include "sqlite3ext.h"
503be8b1a4Sdrh SQLITE_EXTENSION_INIT1
513be8b1a4Sdrh #include <string.h>
523be8b1a4Sdrh #include <assert.h>
533be8b1a4Sdrh 
543be8b1a4Sdrh /* The append mark at the end of the database is:
553be8b1a4Sdrh **
563be8b1a4Sdrh **     Start-Of-SQLite3-NNNNNNNN
573be8b1a4Sdrh **     123456789 123456789 12345
583be8b1a4Sdrh **
593be8b1a4Sdrh ** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
60feecc9f5Slarrybr ** the offset to page 1, and also the length of the prefix content.
613be8b1a4Sdrh */
623be8b1a4Sdrh #define APND_MARK_PREFIX     "Start-Of-SQLite3-"
633be8b1a4Sdrh #define APND_MARK_PREFIX_SZ  17
64feecc9f5Slarrybr #define APND_MARK_FOS_SZ      8
65feecc9f5Slarrybr #define APND_MARK_SIZE       (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
663be8b1a4Sdrh 
673be8b1a4Sdrh /*
68e483d349Sdrh ** Maximum size of the combined prefix + database + append-mark.  This
69e483d349Sdrh ** must be less than 0x40000000 to avoid locking issues on Windows.
70e483d349Sdrh */
713fee6753Sdrh #define APND_MAX_SIZE  (0x40000000)
72e483d349Sdrh 
73e483d349Sdrh /*
743fee6753Sdrh ** Try to align the database to an even multiple of APND_ROUNDUP bytes.
75feecc9f5Slarrybr */
763fee6753Sdrh #ifndef APND_ROUNDUP
773fee6753Sdrh #define APND_ROUNDUP 4096
787e398c04Slarrybr #endif
793fee6753Sdrh #define APND_ALIGN_MASK         ((sqlite3_int64)(APND_ROUNDUP-1))
803fee6753Sdrh #define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
81feecc9f5Slarrybr 
82feecc9f5Slarrybr /*
833be8b1a4Sdrh ** Forward declaration of objects used by this utility
843be8b1a4Sdrh */
853be8b1a4Sdrh typedef struct sqlite3_vfs ApndVfs;
863be8b1a4Sdrh typedef struct ApndFile ApndFile;
873be8b1a4Sdrh 
883be8b1a4Sdrh /* Access to a lower-level VFS that (might) implement dynamic loading,
893be8b1a4Sdrh ** access to randomness, etc.
903be8b1a4Sdrh */
913be8b1a4Sdrh #define ORIGVFS(p)  ((sqlite3_vfs*)((p)->pAppData))
923be8b1a4Sdrh #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
933be8b1a4Sdrh 
943fee6753Sdrh /* An open appendvfs file
953fee6753Sdrh **
963fee6753Sdrh ** An instance of this structure describes the appended database file.
973fee6753Sdrh ** A separate sqlite3_file object is always appended. The appended
983fee6753Sdrh ** sqlite3_file object (which can be accessed using ORIGFILE()) describes
993fee6753Sdrh ** the entire file, including the prefix, the database, and the
1003fee6753Sdrh ** append-mark.
1013fee6753Sdrh **
1023fee6753Sdrh ** The structure of an AppendVFS database is like this:
1033fee6753Sdrh **
1043fee6753Sdrh **   +-------------+---------+----------+-------------+
1053fee6753Sdrh **   | prefix-file | padding | database | append-mark |
1063fee6753Sdrh **   +-------------+---------+----------+-------------+
1073fee6753Sdrh **                           ^          ^
1083fee6753Sdrh **                           |          |
1093fee6753Sdrh **                         iPgOne      iMark
1103fee6753Sdrh **
1113fee6753Sdrh **
1123fee6753Sdrh ** "prefix file" -  file onto which the database has been appended.
1133fee6753Sdrh ** "padding"     -  zero or more bytes inserted so that "database"
1143fee6753Sdrh **                  starts on an APND_ROUNDUP boundary
1153fee6753Sdrh ** "database"    -  The SQLite database file
1163fee6753Sdrh ** "append-mark" -  The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates
1173fee6753Sdrh **                  the offset from the start of prefix-file to the start
1183fee6753Sdrh **                  of "database".
1193fee6753Sdrh **
1203fee6753Sdrh ** The size of the database is iMark - iPgOne.
1213fee6753Sdrh **
1223fee6753Sdrh ** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value
1233fee6753Sdrh ** of iPgOne stored as a big-ending 64-bit integer.
1243fee6753Sdrh **
1253fee6753Sdrh ** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE).
1263fee6753Sdrh ** Or, iMark is -1 to indicate that it has not yet been written.
127feecc9f5Slarrybr */
1283be8b1a4Sdrh struct ApndFile {
1293fee6753Sdrh   sqlite3_file base;        /* Subclass.  MUST BE FIRST! */
1303fee6753Sdrh   sqlite3_int64 iPgOne;     /* Offset to the start of the database */
1313fee6753Sdrh   sqlite3_int64 iMark;      /* Offset of the append mark.  -1 if unwritten */
1323fee6753Sdrh   /* Always followed by another sqlite3_file that describes the whole file */
1333be8b1a4Sdrh };
1343be8b1a4Sdrh 
1353be8b1a4Sdrh /*
1363be8b1a4Sdrh ** Methods for ApndFile
1373be8b1a4Sdrh */
1383be8b1a4Sdrh static int apndClose(sqlite3_file*);
1393be8b1a4Sdrh static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
1403be8b1a4Sdrh static int apndWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
1413be8b1a4Sdrh static int apndTruncate(sqlite3_file*, sqlite3_int64 size);
1423be8b1a4Sdrh static int apndSync(sqlite3_file*, int flags);
1433be8b1a4Sdrh static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize);
1443be8b1a4Sdrh static int apndLock(sqlite3_file*, int);
1453be8b1a4Sdrh static int apndUnlock(sqlite3_file*, int);
1463be8b1a4Sdrh static int apndCheckReservedLock(sqlite3_file*, int *pResOut);
1473be8b1a4Sdrh static int apndFileControl(sqlite3_file*, int op, void *pArg);
1483be8b1a4Sdrh static int apndSectorSize(sqlite3_file*);
1493be8b1a4Sdrh static int apndDeviceCharacteristics(sqlite3_file*);
1503be8b1a4Sdrh static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
1513be8b1a4Sdrh static int apndShmLock(sqlite3_file*, int offset, int n, int flags);
1523be8b1a4Sdrh static void apndShmBarrier(sqlite3_file*);
1533be8b1a4Sdrh static int apndShmUnmap(sqlite3_file*, int deleteFlag);
1543be8b1a4Sdrh static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
1553be8b1a4Sdrh static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
1563be8b1a4Sdrh 
1573be8b1a4Sdrh /*
1583be8b1a4Sdrh ** Methods for ApndVfs
1593be8b1a4Sdrh */
1603be8b1a4Sdrh static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
1613be8b1a4Sdrh static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir);
1623be8b1a4Sdrh static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *);
1633be8b1a4Sdrh static int apndFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
1643be8b1a4Sdrh static void *apndDlOpen(sqlite3_vfs*, const char *zFilename);
1653be8b1a4Sdrh static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
1663be8b1a4Sdrh static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
1673be8b1a4Sdrh static void apndDlClose(sqlite3_vfs*, void*);
1683be8b1a4Sdrh static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut);
1693be8b1a4Sdrh static int apndSleep(sqlite3_vfs*, int microseconds);
1703be8b1a4Sdrh static int apndCurrentTime(sqlite3_vfs*, double*);
1713be8b1a4Sdrh static int apndGetLastError(sqlite3_vfs*, int, char *);
1723be8b1a4Sdrh static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
1733be8b1a4Sdrh static int apndSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
1743be8b1a4Sdrh static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z);
1753be8b1a4Sdrh static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName);
1763be8b1a4Sdrh 
1773be8b1a4Sdrh static sqlite3_vfs apnd_vfs = {
1783be8b1a4Sdrh   3,                            /* iVersion (set when registered) */
1793be8b1a4Sdrh   0,                            /* szOsFile (set when registered) */
1803be8b1a4Sdrh   1024,                         /* mxPathname */
1813be8b1a4Sdrh   0,                            /* pNext */
1823be8b1a4Sdrh   "apndvfs",                    /* zName */
1833be8b1a4Sdrh   0,                            /* pAppData (set when registered) */
1843be8b1a4Sdrh   apndOpen,                     /* xOpen */
1853be8b1a4Sdrh   apndDelete,                   /* xDelete */
1863be8b1a4Sdrh   apndAccess,                   /* xAccess */
1873be8b1a4Sdrh   apndFullPathname,             /* xFullPathname */
1883be8b1a4Sdrh   apndDlOpen,                   /* xDlOpen */
1893be8b1a4Sdrh   apndDlError,                  /* xDlError */
1903be8b1a4Sdrh   apndDlSym,                    /* xDlSym */
1913be8b1a4Sdrh   apndDlClose,                  /* xDlClose */
1923be8b1a4Sdrh   apndRandomness,               /* xRandomness */
1933be8b1a4Sdrh   apndSleep,                    /* xSleep */
1943be8b1a4Sdrh   apndCurrentTime,              /* xCurrentTime */
1953be8b1a4Sdrh   apndGetLastError,             /* xGetLastError */
1963be8b1a4Sdrh   apndCurrentTimeInt64,         /* xCurrentTimeInt64 */
1973be8b1a4Sdrh   apndSetSystemCall,            /* xSetSystemCall */
1983be8b1a4Sdrh   apndGetSystemCall,            /* xGetSystemCall */
1993be8b1a4Sdrh   apndNextSystemCall            /* xNextSystemCall */
2003be8b1a4Sdrh };
2013be8b1a4Sdrh 
2023be8b1a4Sdrh static const sqlite3_io_methods apnd_io_methods = {
2033be8b1a4Sdrh   3,                              /* iVersion */
2043be8b1a4Sdrh   apndClose,                      /* xClose */
2053be8b1a4Sdrh   apndRead,                       /* xRead */
2063be8b1a4Sdrh   apndWrite,                      /* xWrite */
2073be8b1a4Sdrh   apndTruncate,                   /* xTruncate */
2083be8b1a4Sdrh   apndSync,                       /* xSync */
2093be8b1a4Sdrh   apndFileSize,                   /* xFileSize */
2103be8b1a4Sdrh   apndLock,                       /* xLock */
2113be8b1a4Sdrh   apndUnlock,                     /* xUnlock */
2123be8b1a4Sdrh   apndCheckReservedLock,          /* xCheckReservedLock */
2133be8b1a4Sdrh   apndFileControl,                /* xFileControl */
2143be8b1a4Sdrh   apndSectorSize,                 /* xSectorSize */
2153be8b1a4Sdrh   apndDeviceCharacteristics,      /* xDeviceCharacteristics */
2163be8b1a4Sdrh   apndShmMap,                     /* xShmMap */
2173be8b1a4Sdrh   apndShmLock,                    /* xShmLock */
2183be8b1a4Sdrh   apndShmBarrier,                 /* xShmBarrier */
2193be8b1a4Sdrh   apndShmUnmap,                   /* xShmUnmap */
2203be8b1a4Sdrh   apndFetch,                      /* xFetch */
2213be8b1a4Sdrh   apndUnfetch                     /* xUnfetch */
2223be8b1a4Sdrh };
2233be8b1a4Sdrh 
2243be8b1a4Sdrh /*
2253be8b1a4Sdrh ** Close an apnd-file.
2263be8b1a4Sdrh */
apndClose(sqlite3_file * pFile)2273be8b1a4Sdrh static int apndClose(sqlite3_file *pFile){
2283be8b1a4Sdrh   pFile = ORIGFILE(pFile);
2293be8b1a4Sdrh   return pFile->pMethods->xClose(pFile);
2303be8b1a4Sdrh }
2313be8b1a4Sdrh 
2323be8b1a4Sdrh /*
2333be8b1a4Sdrh ** Read data from an apnd-file.
2343be8b1a4Sdrh */
apndRead(sqlite3_file * pFile,void * zBuf,int iAmt,sqlite_int64 iOfst)2353be8b1a4Sdrh static int apndRead(
2363be8b1a4Sdrh   sqlite3_file *pFile,
2373be8b1a4Sdrh   void *zBuf,
2383be8b1a4Sdrh   int iAmt,
2393be8b1a4Sdrh   sqlite_int64 iOfst
2403be8b1a4Sdrh ){
2417e398c04Slarrybr   ApndFile *paf = (ApndFile *)pFile;
2423be8b1a4Sdrh   pFile = ORIGFILE(pFile);
2437e398c04Slarrybr   return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
2443be8b1a4Sdrh }
2453be8b1a4Sdrh 
2463be8b1a4Sdrh /*
247feecc9f5Slarrybr ** Add the append-mark onto what should become the end of the file.
2487e398c04Slarrybr *  If and only if this succeeds, internal ApndFile.iMark is updated.
2497e398c04Slarrybr *  Parameter iWriteEnd is the appendvfs-relative offset of the new mark.
250e483d349Sdrh */
apndWriteMark(ApndFile * paf,sqlite3_file * pFile,sqlite_int64 iWriteEnd)251feecc9f5Slarrybr static int apndWriteMark(
2527e398c04Slarrybr   ApndFile *paf,
253feecc9f5Slarrybr   sqlite3_file *pFile,
2547e398c04Slarrybr   sqlite_int64 iWriteEnd
255feecc9f5Slarrybr ){
2567e398c04Slarrybr   sqlite_int64 iPgOne = paf->iPgOne;
257e483d349Sdrh   unsigned char a[APND_MARK_SIZE];
2587e398c04Slarrybr   int i = APND_MARK_FOS_SZ;
2597e398c04Slarrybr   int rc;
2607e398c04Slarrybr   assert(pFile == ORIGFILE(paf));
261e483d349Sdrh   memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
2627e398c04Slarrybr   while( --i >= 0 ){
2637e398c04Slarrybr     a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
2647e398c04Slarrybr     iPgOne >>= 8;
265e483d349Sdrh   }
2667e398c04Slarrybr   iWriteEnd += paf->iPgOne;
267feecc9f5Slarrybr   if( SQLITE_OK==(rc = pFile->pMethods->xWrite
2687e398c04Slarrybr                   (pFile, a, APND_MARK_SIZE, iWriteEnd)) ){
2697e398c04Slarrybr     paf->iMark = iWriteEnd;
270feecc9f5Slarrybr   }
271feecc9f5Slarrybr   return rc;
272e483d349Sdrh }
273e483d349Sdrh 
274e483d349Sdrh /*
2753be8b1a4Sdrh ** Write data to an apnd-file.
2763be8b1a4Sdrh */
apndWrite(sqlite3_file * pFile,const void * zBuf,int iAmt,sqlite_int64 iOfst)2773be8b1a4Sdrh static int apndWrite(
2783be8b1a4Sdrh   sqlite3_file *pFile,
279e483d349Sdrh   const void *zBuf,
2803be8b1a4Sdrh   int iAmt,
2813be8b1a4Sdrh   sqlite_int64 iOfst
2823be8b1a4Sdrh ){
2837e398c04Slarrybr   ApndFile *paf = (ApndFile *)pFile;
2847e398c04Slarrybr   sqlite_int64 iWriteEnd = iOfst + iAmt;
2857e398c04Slarrybr   if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
286e483d349Sdrh   pFile = ORIGFILE(pFile);
2877e398c04Slarrybr   /* If append-mark is absent or will be overwritten, write it. */
2887e398c04Slarrybr   if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
2897e398c04Slarrybr     int rc = apndWriteMark(paf, pFile, iWriteEnd);
2903fee6753Sdrh     if( SQLITE_OK!=rc ) return rc;
2913be8b1a4Sdrh   }
2927e398c04Slarrybr   return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
293feecc9f5Slarrybr }
2943be8b1a4Sdrh 
2953be8b1a4Sdrh /*
2963be8b1a4Sdrh ** Truncate an apnd-file.
2973be8b1a4Sdrh */
apndTruncate(sqlite3_file * pFile,sqlite_int64 size)2983be8b1a4Sdrh static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
2997e398c04Slarrybr   ApndFile *paf = (ApndFile *)pFile;
300e483d349Sdrh   pFile = ORIGFILE(pFile);
3017e398c04Slarrybr   /* The append mark goes out first so truncate failure does not lose it. */
3023fee6753Sdrh   if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR;
3037e398c04Slarrybr   /* Truncate underlying file just past append mark */
3047e398c04Slarrybr   return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
3053be8b1a4Sdrh }
3063be8b1a4Sdrh 
3073be8b1a4Sdrh /*
3083be8b1a4Sdrh ** Sync an apnd-file.
3093be8b1a4Sdrh */
apndSync(sqlite3_file * pFile,int flags)3103be8b1a4Sdrh static int apndSync(sqlite3_file *pFile, int flags){
311e483d349Sdrh   pFile = ORIGFILE(pFile);
312e483d349Sdrh   return pFile->pMethods->xSync(pFile, flags);
3133be8b1a4Sdrh }
3143be8b1a4Sdrh 
3153be8b1a4Sdrh /*
3163be8b1a4Sdrh ** Return the current file-size of an apnd-file.
317feecc9f5Slarrybr ** If the append mark is not yet there, the file-size is 0.
3183be8b1a4Sdrh */
apndFileSize(sqlite3_file * pFile,sqlite_int64 * pSize)3193be8b1a4Sdrh static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
320feecc9f5Slarrybr   ApndFile *paf = (ApndFile *)pFile;
321feecc9f5Slarrybr   *pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0;
322feecc9f5Slarrybr   return SQLITE_OK;
3233be8b1a4Sdrh }
3243be8b1a4Sdrh 
3253be8b1a4Sdrh /*
3263be8b1a4Sdrh ** Lock an apnd-file.
3273be8b1a4Sdrh */
apndLock(sqlite3_file * pFile,int eLock)3283be8b1a4Sdrh static int apndLock(sqlite3_file *pFile, int eLock){
329e483d349Sdrh   pFile = ORIGFILE(pFile);
330e483d349Sdrh   return pFile->pMethods->xLock(pFile, eLock);
3313be8b1a4Sdrh }
3323be8b1a4Sdrh 
3333be8b1a4Sdrh /*
3343be8b1a4Sdrh ** Unlock an apnd-file.
3353be8b1a4Sdrh */
apndUnlock(sqlite3_file * pFile,int eLock)3363be8b1a4Sdrh static int apndUnlock(sqlite3_file *pFile, int eLock){
337e483d349Sdrh   pFile = ORIGFILE(pFile);
338e483d349Sdrh   return pFile->pMethods->xUnlock(pFile, eLock);
3393be8b1a4Sdrh }
3403be8b1a4Sdrh 
3413be8b1a4Sdrh /*
3423be8b1a4Sdrh ** Check if another file-handle holds a RESERVED lock on an apnd-file.
3433be8b1a4Sdrh */
apndCheckReservedLock(sqlite3_file * pFile,int * pResOut)3443be8b1a4Sdrh static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){
345e483d349Sdrh   pFile = ORIGFILE(pFile);
346e483d349Sdrh   return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
3473be8b1a4Sdrh }
3483be8b1a4Sdrh 
3493be8b1a4Sdrh /*
3503be8b1a4Sdrh ** File control method. For custom operations on an apnd-file.
3513be8b1a4Sdrh */
apndFileControl(sqlite3_file * pFile,int op,void * pArg)3523be8b1a4Sdrh static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
3537e398c04Slarrybr   ApndFile *paf = (ApndFile *)pFile;
3543be8b1a4Sdrh   int rc;
3553be8b1a4Sdrh   pFile = ORIGFILE(pFile);
356407cfe99Sdrh   if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne;
3573be8b1a4Sdrh   rc = pFile->pMethods->xFileControl(pFile, op, pArg);
3583be8b1a4Sdrh   if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
3597e398c04Slarrybr     *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", paf->iPgOne,*(char**)pArg);
3603be8b1a4Sdrh   }
3613be8b1a4Sdrh   return rc;
3623be8b1a4Sdrh }
3633be8b1a4Sdrh 
3643be8b1a4Sdrh /*
3653be8b1a4Sdrh ** Return the sector-size in bytes for an apnd-file.
3663be8b1a4Sdrh */
apndSectorSize(sqlite3_file * pFile)3673be8b1a4Sdrh static int apndSectorSize(sqlite3_file *pFile){
3683be8b1a4Sdrh   pFile = ORIGFILE(pFile);
3693be8b1a4Sdrh   return pFile->pMethods->xSectorSize(pFile);
3703be8b1a4Sdrh }
3713be8b1a4Sdrh 
3723be8b1a4Sdrh /*
3733be8b1a4Sdrh ** Return the device characteristic flags supported by an apnd-file.
3743be8b1a4Sdrh */
apndDeviceCharacteristics(sqlite3_file * pFile)3753be8b1a4Sdrh static int apndDeviceCharacteristics(sqlite3_file *pFile){
3763be8b1a4Sdrh   pFile = ORIGFILE(pFile);
377233ff96eSdrh   return pFile->pMethods->xDeviceCharacteristics(pFile);
3783be8b1a4Sdrh }
3793be8b1a4Sdrh 
3803be8b1a4Sdrh /* Create a shared memory file mapping */
apndShmMap(sqlite3_file * pFile,int iPg,int pgsz,int bExtend,void volatile ** pp)3813be8b1a4Sdrh static int apndShmMap(
3823be8b1a4Sdrh   sqlite3_file *pFile,
3833be8b1a4Sdrh   int iPg,
3843be8b1a4Sdrh   int pgsz,
3853be8b1a4Sdrh   int bExtend,
3863be8b1a4Sdrh   void volatile **pp
3873be8b1a4Sdrh ){
388e483d349Sdrh   pFile = ORIGFILE(pFile);
389e483d349Sdrh   return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
3903be8b1a4Sdrh }
3913be8b1a4Sdrh 
3923be8b1a4Sdrh /* Perform locking on a shared-memory segment */
apndShmLock(sqlite3_file * pFile,int offset,int n,int flags)3933be8b1a4Sdrh static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){
394e483d349Sdrh   pFile = ORIGFILE(pFile);
395e483d349Sdrh   return pFile->pMethods->xShmLock(pFile,offset,n,flags);
3963be8b1a4Sdrh }
3973be8b1a4Sdrh 
3983be8b1a4Sdrh /* Memory barrier operation on shared memory */
apndShmBarrier(sqlite3_file * pFile)3993be8b1a4Sdrh static void apndShmBarrier(sqlite3_file *pFile){
400e483d349Sdrh   pFile = ORIGFILE(pFile);
401e483d349Sdrh   pFile->pMethods->xShmBarrier(pFile);
4023be8b1a4Sdrh }
4033be8b1a4Sdrh 
4043be8b1a4Sdrh /* Unmap a shared memory segment */
apndShmUnmap(sqlite3_file * pFile,int deleteFlag)4053be8b1a4Sdrh static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){
406e483d349Sdrh   pFile = ORIGFILE(pFile);
407e483d349Sdrh   return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
4083be8b1a4Sdrh }
4093be8b1a4Sdrh 
4103be8b1a4Sdrh /* Fetch a page of a memory-mapped file */
apndFetch(sqlite3_file * pFile,sqlite3_int64 iOfst,int iAmt,void ** pp)4113be8b1a4Sdrh static int apndFetch(
4123be8b1a4Sdrh   sqlite3_file *pFile,
4133be8b1a4Sdrh   sqlite3_int64 iOfst,
4143be8b1a4Sdrh   int iAmt,
4153be8b1a4Sdrh   void **pp
4163be8b1a4Sdrh ){
4173be8b1a4Sdrh   ApndFile *p = (ApndFile *)pFile;
4183fee6753Sdrh   if( p->iMark < 0 || iOfst+iAmt > p->iMark ){
419feecc9f5Slarrybr     return SQLITE_IOERR; /* Cannot read what is not yet there. */
4203fee6753Sdrh   }
4213be8b1a4Sdrh   pFile = ORIGFILE(pFile);
4223be8b1a4Sdrh   return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
4233be8b1a4Sdrh }
4243be8b1a4Sdrh 
4253be8b1a4Sdrh /* Release a memory-mapped page */
apndUnfetch(sqlite3_file * pFile,sqlite3_int64 iOfst,void * pPage)4263be8b1a4Sdrh static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
4273be8b1a4Sdrh   ApndFile *p = (ApndFile *)pFile;
4283be8b1a4Sdrh   pFile = ORIGFILE(pFile);
4293be8b1a4Sdrh   return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
4303be8b1a4Sdrh }
4313be8b1a4Sdrh 
4323be8b1a4Sdrh /*
433e483d349Sdrh ** Try to read the append-mark off the end of a file.  Return the
434feecc9f5Slarrybr ** start of the appended database if the append-mark is present.
435feecc9f5Slarrybr ** If there is no valid append-mark, return -1;
436e0218909Sdrh **
437e0218909Sdrh ** An append-mark is only valid if the NNNNNNNN start-of-database offset
438e0218909Sdrh ** indicates that the appended database contains at least one page.  The
439e0218909Sdrh ** start-of-database value must be a multiple of 512.
440e483d349Sdrh */
apndReadMark(sqlite3_int64 sz,sqlite3_file * pFile)441e483d349Sdrh static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
442e483d349Sdrh   int rc, i;
443e483d349Sdrh   sqlite3_int64 iMark;
444feecc9f5Slarrybr   int msbs = 8 * (APND_MARK_FOS_SZ-1);
445e483d349Sdrh   unsigned char a[APND_MARK_SIZE];
446e483d349Sdrh 
447feecc9f5Slarrybr   if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1;
448e483d349Sdrh   rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
449e483d349Sdrh   if( rc ) return -1;
450e483d349Sdrh   if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
451feecc9f5Slarrybr   iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0x7f)) << msbs;
452e483d349Sdrh   for(i=1; i<8; i++){
453feecc9f5Slarrybr     msbs -= 8;
454feecc9f5Slarrybr     iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs;
455e483d349Sdrh   }
456e0218909Sdrh   if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1;
457e0218909Sdrh   if( iMark & 0x1ff ) return -1;
458e483d349Sdrh   return iMark;
459e483d349Sdrh }
460e483d349Sdrh 
461feecc9f5Slarrybr static const char apvfsSqliteHdr[] = "SQLite format 3";
462feecc9f5Slarrybr /*
463feecc9f5Slarrybr ** Check to see if the file is an appendvfs SQLite database file.
464feecc9f5Slarrybr ** Return true iff it is such. Parameter sz is the file's size.
465feecc9f5Slarrybr */
apndIsAppendvfsDatabase(sqlite3_int64 sz,sqlite3_file * pFile)466feecc9f5Slarrybr static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
467feecc9f5Slarrybr   int rc;
468feecc9f5Slarrybr   char zHdr[16];
469feecc9f5Slarrybr   sqlite3_int64 iMark = apndReadMark(sz, pFile);
470feecc9f5Slarrybr   if( iMark>=0 ){
4713fee6753Sdrh     /* If file has the correct end-marker, the expected odd size, and the
4723fee6753Sdrh     ** SQLite DB type marker where the end-marker puts it, then it
4733fee6753Sdrh     ** is an appendvfs database.
474feecc9f5Slarrybr     */
475feecc9f5Slarrybr     rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
4763fee6753Sdrh     if( SQLITE_OK==rc
4773fee6753Sdrh      && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4783fee6753Sdrh      && (sz & 0x1ff) == APND_MARK_SIZE
4793fee6753Sdrh      && sz>=512+APND_MARK_SIZE
4803fee6753Sdrh     ){
481feecc9f5Slarrybr       return 1; /* It's an appendvfs database */
482feecc9f5Slarrybr     }
4833fee6753Sdrh   }
484feecc9f5Slarrybr   return 0;
485feecc9f5Slarrybr }
486feecc9f5Slarrybr 
487feecc9f5Slarrybr /*
488feecc9f5Slarrybr ** Check to see if the file is an ordinary SQLite database file.
489feecc9f5Slarrybr ** Return true iff so. Parameter sz is the file's size.
490feecc9f5Slarrybr */
apndIsOrdinaryDatabaseFile(sqlite3_int64 sz,sqlite3_file * pFile)491feecc9f5Slarrybr static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
492feecc9f5Slarrybr   char zHdr[16];
4931e6f3346Sdrh   if( apndIsAppendvfsDatabase(sz, pFile) /* rule 2 */
494feecc9f5Slarrybr    || (sz & 0x1ff) != 0
495feecc9f5Slarrybr    || SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0)
49640942f22Sdrh    || memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))!=0
49740942f22Sdrh   ){
498feecc9f5Slarrybr     return 0;
49940942f22Sdrh   }else{
500feecc9f5Slarrybr     return 1;
501feecc9f5Slarrybr   }
50240942f22Sdrh }
503feecc9f5Slarrybr 
504e483d349Sdrh /*
5053be8b1a4Sdrh ** Open an apnd file handle.
5063be8b1a4Sdrh */
apndOpen(sqlite3_vfs * pApndVfs,const char * zName,sqlite3_file * pFile,int flags,int * pOutFlags)5073be8b1a4Sdrh static int apndOpen(
5085cad178bSlarrybr   sqlite3_vfs *pApndVfs,
5093be8b1a4Sdrh   const char *zName,
5103be8b1a4Sdrh   sqlite3_file *pFile,
5113be8b1a4Sdrh   int flags,
5123be8b1a4Sdrh   int *pOutFlags
5133be8b1a4Sdrh ){
5145cad178bSlarrybr   ApndFile *pApndFile = (ApndFile*)pFile;
5155cad178bSlarrybr   sqlite3_file *pBaseFile = ORIGFILE(pFile);
5165cad178bSlarrybr   sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
5173be8b1a4Sdrh   int rc;
5188b04094cSdrh   sqlite3_int64 sz = 0;
5193be8b1a4Sdrh   if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
5205cad178bSlarrybr     /* The appendvfs is not to be used for transient or temporary databases.
5213fee6753Sdrh     ** Just use the base VFS open to initialize the given file object and
5223fee6753Sdrh     ** open the underlying file. (Appendvfs is then unused for this file.)
5235cad178bSlarrybr     */
5245cad178bSlarrybr     return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
5253be8b1a4Sdrh   }
5265cad178bSlarrybr   memset(pApndFile, 0, sizeof(ApndFile));
5270c52f5a2Sdrh   pFile->pMethods = &apnd_io_methods;
5283fee6753Sdrh   pApndFile->iMark = -1;    /* Append mark not yet written */
5295cad178bSlarrybr 
5305cad178bSlarrybr   rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
5313fee6753Sdrh   if( rc==SQLITE_OK ){
5325cad178bSlarrybr     rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
533e483d349Sdrh     if( rc ){
5345cad178bSlarrybr       pBaseFile->pMethods->xClose(pBaseFile);
5356536c4f1Slarrybr     }
5366536c4f1Slarrybr   }
5376536c4f1Slarrybr   if( rc ){
5383fee6753Sdrh     pFile->pMethods = 0;
5393fee6753Sdrh     return rc;
5403be8b1a4Sdrh   }
5415cad178bSlarrybr   if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
5425cad178bSlarrybr     /* The file being opened appears to be just an ordinary DB. Copy
5433fee6753Sdrh     ** the base dispatch-table so this instance mimics the base VFS.
5445cad178bSlarrybr     */
5455cad178bSlarrybr     memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
546e483d349Sdrh     return SQLITE_OK;
5473be8b1a4Sdrh   }
5485cad178bSlarrybr   pApndFile->iPgOne = apndReadMark(sz, pFile);
5495cad178bSlarrybr   if( pApndFile->iPgOne>=0 ){
5503fee6753Sdrh     pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */
551e483d349Sdrh     return SQLITE_OK;
552e483d349Sdrh   }
553e483d349Sdrh   if( (flags & SQLITE_OPEN_CREATE)==0 ){
5545cad178bSlarrybr     pBaseFile->pMethods->xClose(pBaseFile);
5556b9986e8Sdrh     rc = SQLITE_CANTOPEN;
5563fee6753Sdrh     pFile->pMethods = 0;
5573fee6753Sdrh   }else{
558feecc9f5Slarrybr     /* Round newly added appendvfs location to #define'd page boundary.
5593fee6753Sdrh     ** Note that nothing has yet been written to the underlying file.
5603fee6753Sdrh     ** The append mark will be written along with first content write.
5613fee6753Sdrh     ** Until then, paf->iMark value indicates it is not yet written.
562feecc9f5Slarrybr     */
5633fee6753Sdrh     pApndFile->iPgOne = APND_START_ROUNDUP(sz);
5643fee6753Sdrh   }
5656b9986e8Sdrh   return rc;
5663be8b1a4Sdrh }
5673be8b1a4Sdrh 
5683be8b1a4Sdrh /*
569feecc9f5Slarrybr ** Delete an apnd file.
570feecc9f5Slarrybr ** For an appendvfs, this could mean delete the appendvfs portion,
571feecc9f5Slarrybr ** leaving the appendee as it was before it gained an appendvfs.
572feecc9f5Slarrybr ** For now, this code deletes the underlying file too.
5733be8b1a4Sdrh */
apndDelete(sqlite3_vfs * pVfs,const char * zPath,int dirSync)5743be8b1a4Sdrh static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
5753be8b1a4Sdrh   return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
5763be8b1a4Sdrh }
577feecc9f5Slarrybr 
578feecc9f5Slarrybr /*
579feecc9f5Slarrybr ** All other VFS methods are pass-thrus.
580feecc9f5Slarrybr */
apndAccess(sqlite3_vfs * pVfs,const char * zPath,int flags,int * pResOut)5813be8b1a4Sdrh static int apndAccess(
5823be8b1a4Sdrh   sqlite3_vfs *pVfs,
5833be8b1a4Sdrh   const char *zPath,
5843be8b1a4Sdrh   int flags,
5853be8b1a4Sdrh   int *pResOut
5863be8b1a4Sdrh ){
5873be8b1a4Sdrh   return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
5883be8b1a4Sdrh }
apndFullPathname(sqlite3_vfs * pVfs,const char * zPath,int nOut,char * zOut)5893be8b1a4Sdrh static int apndFullPathname(
5903be8b1a4Sdrh   sqlite3_vfs *pVfs,
5913be8b1a4Sdrh   const char *zPath,
5923be8b1a4Sdrh   int nOut,
5933be8b1a4Sdrh   char *zOut
5943be8b1a4Sdrh ){
5953be8b1a4Sdrh   return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
5963be8b1a4Sdrh }
apndDlOpen(sqlite3_vfs * pVfs,const char * zPath)5973be8b1a4Sdrh static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){
5983be8b1a4Sdrh   return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
5993be8b1a4Sdrh }
apndDlError(sqlite3_vfs * pVfs,int nByte,char * zErrMsg)6003be8b1a4Sdrh static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
6013be8b1a4Sdrh   ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
6023be8b1a4Sdrh }
apndDlSym(sqlite3_vfs * pVfs,void * p,const char * zSym)6033be8b1a4Sdrh static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
6043be8b1a4Sdrh   return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
6053be8b1a4Sdrh }
apndDlClose(sqlite3_vfs * pVfs,void * pHandle)6063be8b1a4Sdrh static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){
6073be8b1a4Sdrh   ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
6083be8b1a4Sdrh }
apndRandomness(sqlite3_vfs * pVfs,int nByte,char * zBufOut)6093be8b1a4Sdrh static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
6103be8b1a4Sdrh   return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
6113be8b1a4Sdrh }
apndSleep(sqlite3_vfs * pVfs,int nMicro)6123be8b1a4Sdrh static int apndSleep(sqlite3_vfs *pVfs, int nMicro){
6133be8b1a4Sdrh   return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
6143be8b1a4Sdrh }
apndCurrentTime(sqlite3_vfs * pVfs,double * pTimeOut)6153be8b1a4Sdrh static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
6163be8b1a4Sdrh   return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
6173be8b1a4Sdrh }
apndGetLastError(sqlite3_vfs * pVfs,int a,char * b)6183be8b1a4Sdrh static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){
6193be8b1a4Sdrh   return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
6203be8b1a4Sdrh }
apndCurrentTimeInt64(sqlite3_vfs * pVfs,sqlite3_int64 * p)6213be8b1a4Sdrh static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
6223be8b1a4Sdrh   return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
6233be8b1a4Sdrh }
apndSetSystemCall(sqlite3_vfs * pVfs,const char * zName,sqlite3_syscall_ptr pCall)6243be8b1a4Sdrh static int apndSetSystemCall(
6253be8b1a4Sdrh   sqlite3_vfs *pVfs,
6263be8b1a4Sdrh   const char *zName,
6273be8b1a4Sdrh   sqlite3_syscall_ptr pCall
6283be8b1a4Sdrh ){
6293be8b1a4Sdrh   return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
6303be8b1a4Sdrh }
apndGetSystemCall(sqlite3_vfs * pVfs,const char * zName)6313be8b1a4Sdrh static sqlite3_syscall_ptr apndGetSystemCall(
6323be8b1a4Sdrh   sqlite3_vfs *pVfs,
6333be8b1a4Sdrh   const char *zName
6343be8b1a4Sdrh ){
6353be8b1a4Sdrh   return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
6363be8b1a4Sdrh }
apndNextSystemCall(sqlite3_vfs * pVfs,const char * zName)6373be8b1a4Sdrh static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
6383be8b1a4Sdrh   return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
6393be8b1a4Sdrh }
6403be8b1a4Sdrh 
6413be8b1a4Sdrh 
6423be8b1a4Sdrh #ifdef _WIN32
6433be8b1a4Sdrh __declspec(dllexport)
6443be8b1a4Sdrh #endif
6453be8b1a4Sdrh /*
6463be8b1a4Sdrh ** This routine is called when the extension is loaded.
6473be8b1a4Sdrh ** Register the new VFS.
6483be8b1a4Sdrh */
sqlite3_appendvfs_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)6493be8b1a4Sdrh int sqlite3_appendvfs_init(
6503be8b1a4Sdrh   sqlite3 *db,
6513be8b1a4Sdrh   char **pzErrMsg,
6523be8b1a4Sdrh   const sqlite3_api_routines *pApi
6533be8b1a4Sdrh ){
6543be8b1a4Sdrh   int rc = SQLITE_OK;
6553be8b1a4Sdrh   sqlite3_vfs *pOrig;
6563be8b1a4Sdrh   SQLITE_EXTENSION_INIT2(pApi);
657b9685185Sdrh   (void)pzErrMsg;
658b9685185Sdrh   (void)db;
6593be8b1a4Sdrh   pOrig = sqlite3_vfs_find(0);
660*a959bf53Sdrh   if( pOrig==0 ) return SQLITE_ERROR;
6613be8b1a4Sdrh   apnd_vfs.iVersion = pOrig->iVersion;
6623be8b1a4Sdrh   apnd_vfs.pAppData = pOrig;
663233ff96eSdrh   apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile);
664e483d349Sdrh   rc = sqlite3_vfs_register(&apnd_vfs, 0);
6653be8b1a4Sdrh #ifdef APPENDVFS_TEST
6663be8b1a4Sdrh   if( rc==SQLITE_OK ){
6673be8b1a4Sdrh     rc = sqlite3_auto_extension((void(*)(void))apndvfsRegister);
6683be8b1a4Sdrh   }
6693be8b1a4Sdrh #endif
6703be8b1a4Sdrh   if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
6713be8b1a4Sdrh   return rc;
6723be8b1a4Sdrh }
673