summaryrefslogtreecommitdiffstats
path: root/src/test_onefile.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
commit63847496f14c813a5d80efd5b7de0f1294ffe1e3 (patch)
tree01c7571c7c762ceee70638549a99834fdd7c411b /src/test_onefile.c
parentInitial commit. (diff)
downloadsqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.tar.xz
sqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.zip
Adding upstream version 3.45.1.upstream/3.45.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test_onefile.c')
-rw-r--r--src/test_onefile.c831
1 files changed, 831 insertions, 0 deletions
diff --git a/src/test_onefile.c b/src/test_onefile.c
new file mode 100644
index 0000000..037f4fb
--- /dev/null
+++ b/src/test_onefile.c
@@ -0,0 +1,831 @@
+/*
+** 2007 September 14
+**
+** 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.
+**
+*************************************************************************
+**
+** OVERVIEW:
+**
+** This file contains some example code demonstrating how the SQLite
+** vfs feature can be used to have SQLite operate directly on an
+** embedded media, without using an intermediate file system.
+**
+** Because this is only a demo designed to run on a workstation, the
+** underlying media is simulated using a regular file-system file. The
+** size of the file is fixed when it is first created (default size 10 MB).
+** From SQLite's point of view, this space is used to store a single
+** database file and the journal file.
+**
+** Any statement journal created is stored in volatile memory obtained
+** from sqlite3_malloc(). Any attempt to create a temporary database file
+** will fail (SQLITE_IOERR). To prevent SQLite from attempting this,
+** it should be configured to store all temporary database files in
+** main memory (see pragma "temp_store" or the SQLITE_TEMP_STORE compile
+** time option).
+**
+** ASSUMPTIONS:
+**
+** After it has been created, the blob file is accessed using the
+** following three functions only:
+**
+** mediaRead(); - Read a 512 byte block from the file.
+** mediaWrite(); - Write a 512 byte block to the file.
+** mediaSync(); - Tell the media hardware to sync.
+**
+** It is assumed that these can be easily implemented by any "real"
+** media vfs driver adapting this code.
+**
+** FILE FORMAT:
+**
+** The basic principle is that the "database file" is stored at the
+** beginning of the 10 MB blob and grows in a forward direction. The
+** "journal file" is stored at the end of the 10MB blob and grows
+** in the reverse direction. If, during a transaction, insufficient
+** space is available to expand either the journal or database file,
+** an SQLITE_FULL error is returned. The database file is never allowed
+** to consume more than 90% of the blob space. If SQLite tries to
+** create a file larger than this, SQLITE_FULL is returned.
+**
+** No allowance is made for "wear-leveling", as is required by.
+** embedded devices in the absence of equivalent hardware features.
+**
+** The first 512 block byte of the file is reserved for storing the
+** size of the "database file". It is updated as part of the sync()
+** operation. On startup, it can only be trusted if no journal file
+** exists. If a journal-file does exist, then it stores the real size
+** of the database region. The second and subsequent blocks store the
+** actual database content.
+**
+** The size of the "journal file" is not stored persistently in the
+** file. When the system is running, the size of the journal file is
+** stored in volatile memory. When recovering from a crash, this vfs
+** reports a very large size for the journal file. The normal journal
+** header and checksum mechanisms serve to prevent SQLite from
+** processing any data that lies past the logical end of the journal.
+**
+** When SQLite calls OsDelete() to delete the journal file, the final
+** 512 bytes of the blob (the area containing the first journal header)
+** are zeroed.
+**
+** LOCKING:
+**
+** File locking is a no-op. Only one connection may be open at any one
+** time using this demo vfs.
+*/
+
+#include "sqlite3.h"
+#include <assert.h>
+#include <string.h>
+
+/*
+** Maximum pathname length supported by the fs backend.
+*/
+#define BLOCKSIZE 512
+#define BLOBSIZE 10485760
+
+/*
+** Name used to identify this VFS.
+*/
+#define FS_VFS_NAME "fs"
+
+typedef struct fs_real_file fs_real_file;
+struct fs_real_file {
+ sqlite3_file *pFile;
+ const char *zName;
+ int nDatabase; /* Current size of database region */
+ int nJournal; /* Current size of journal region */
+ int nBlob; /* Total size of allocated blob */
+ int nRef; /* Number of pointers to this structure */
+ fs_real_file *pNext;
+ fs_real_file **ppThis;
+};
+
+typedef struct fs_file fs_file;
+struct fs_file {
+ sqlite3_file base;
+ int eType;
+ fs_real_file *pReal;
+};
+
+typedef struct tmp_file tmp_file;
+struct tmp_file {
+ sqlite3_file base;
+ int nSize;
+ int nAlloc;
+ char *zAlloc;
+};
+
+/* Values for fs_file.eType. */
+#define DATABASE_FILE 1
+#define JOURNAL_FILE 2
+
+/*
+** Method declarations for fs_file.
+*/
+static int fsClose(sqlite3_file*);
+static int fsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int fsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+static int fsTruncate(sqlite3_file*, sqlite3_int64 size);
+static int fsSync(sqlite3_file*, int flags);
+static int fsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int fsLock(sqlite3_file*, int);
+static int fsUnlock(sqlite3_file*, int);
+static int fsCheckReservedLock(sqlite3_file*, int *pResOut);
+static int fsFileControl(sqlite3_file*, int op, void *pArg);
+static int fsSectorSize(sqlite3_file*);
+static int fsDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for tmp_file.
+*/
+static int tmpClose(sqlite3_file*);
+static int tmpRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int tmpWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+static int tmpTruncate(sqlite3_file*, sqlite3_int64 size);
+static int tmpSync(sqlite3_file*, int flags);
+static int tmpFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int tmpLock(sqlite3_file*, int);
+static int tmpUnlock(sqlite3_file*, int);
+static int tmpCheckReservedLock(sqlite3_file*, int *pResOut);
+static int tmpFileControl(sqlite3_file*, int op, void *pArg);
+static int tmpSectorSize(sqlite3_file*);
+static int tmpDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for fs_vfs.
+*/
+static int fsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int fsDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int fsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int fsFullPathname(sqlite3_vfs*, const char *zName, int nOut,char *zOut);
+static void *fsDlOpen(sqlite3_vfs*, const char *zFilename);
+static void fsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*fsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
+static void fsDlClose(sqlite3_vfs*, void*);
+static int fsRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int fsSleep(sqlite3_vfs*, int microseconds);
+static int fsCurrentTime(sqlite3_vfs*, double*);
+
+
+typedef struct fs_vfs_t fs_vfs_t;
+struct fs_vfs_t {
+ sqlite3_vfs base;
+ fs_real_file *pFileList;
+ sqlite3_vfs *pParent;
+};
+
+static fs_vfs_t fs_vfs = {
+ {
+ 1, /* iVersion */
+ 0, /* szOsFile */
+ 0, /* mxPathname */
+ 0, /* pNext */
+ FS_VFS_NAME, /* zName */
+ 0, /* pAppData */
+ fsOpen, /* xOpen */
+ fsDelete, /* xDelete */
+ fsAccess, /* xAccess */
+ fsFullPathname, /* xFullPathname */
+ fsDlOpen, /* xDlOpen */
+ fsDlError, /* xDlError */
+ fsDlSym, /* xDlSym */
+ fsDlClose, /* xDlClose */
+ fsRandomness, /* xRandomness */
+ fsSleep, /* xSleep */
+ fsCurrentTime, /* xCurrentTime */
+ 0 /* xCurrentTimeInt64 */
+ },
+ 0, /* pFileList */
+ 0 /* pParent */
+};
+
+static sqlite3_io_methods fs_io_methods = {
+ 1, /* iVersion */
+ fsClose, /* xClose */
+ fsRead, /* xRead */
+ fsWrite, /* xWrite */
+ fsTruncate, /* xTruncate */
+ fsSync, /* xSync */
+ fsFileSize, /* xFileSize */
+ fsLock, /* xLock */
+ fsUnlock, /* xUnlock */
+ fsCheckReservedLock, /* xCheckReservedLock */
+ fsFileControl, /* xFileControl */
+ fsSectorSize, /* xSectorSize */
+ fsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0 /* xShmUnmap */
+};
+
+
+static sqlite3_io_methods tmp_io_methods = {
+ 1, /* iVersion */
+ tmpClose, /* xClose */
+ tmpRead, /* xRead */
+ tmpWrite, /* xWrite */
+ tmpTruncate, /* xTruncate */
+ tmpSync, /* xSync */
+ tmpFileSize, /* xFileSize */
+ tmpLock, /* xLock */
+ tmpUnlock, /* xUnlock */
+ tmpCheckReservedLock, /* xCheckReservedLock */
+ tmpFileControl, /* xFileControl */
+ tmpSectorSize, /* xSectorSize */
+ tmpDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0 /* xShmUnmap */
+};
+
+/* Useful macros used in several places */
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+
+/*
+** Close a tmp-file.
+*/
+static int tmpClose(sqlite3_file *pFile){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ sqlite3_free(pTmp->zAlloc);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from a tmp-file.
+*/
+static int tmpRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ if( (iAmt+iOfst)>pTmp->nSize ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ memcpy(zBuf, &pTmp->zAlloc[iOfst], iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Write data to a tmp-file.
+*/
+static int tmpWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ if( (iAmt+iOfst)>pTmp->nAlloc ){
+ int nNew = (int)(2*(iAmt+iOfst+pTmp->nAlloc));
+ char *zNew = sqlite3_realloc(pTmp->zAlloc, nNew);
+ if( !zNew ){
+ return SQLITE_NOMEM;
+ }
+ pTmp->zAlloc = zNew;
+ pTmp->nAlloc = nNew;
+ }
+ memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt);
+ pTmp->nSize = (int)MAX(pTmp->nSize, iOfst+iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Truncate a tmp-file.
+*/
+static int tmpTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ pTmp->nSize = (int)MIN(pTmp->nSize, size);
+ return SQLITE_OK;
+}
+
+/*
+** Sync a tmp-file.
+*/
+static int tmpSync(sqlite3_file *pFile, int flags){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current file-size of a tmp-file.
+*/
+static int tmpFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ *pSize = pTmp->nSize;
+ return SQLITE_OK;
+}
+
+/*
+** Lock a tmp-file.
+*/
+static int tmpLock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Unlock a tmp-file.
+*/
+static int tmpUnlock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on a tmp-file.
+*/
+static int tmpCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on a tmp-file.
+*/
+static int tmpFileControl(sqlite3_file *pFile, int op, void *pArg){
+ return SQLITE_OK;
+}
+
+/*
+** Return the sector-size in bytes for a tmp-file.
+*/
+static int tmpSectorSize(sqlite3_file *pFile){
+ return 0;
+}
+
+/*
+** Return the device characteristic flags supported by a tmp-file.
+*/
+static int tmpDeviceCharacteristics(sqlite3_file *pFile){
+ return 0;
+}
+
+/*
+** Close an fs-file.
+*/
+static int fsClose(sqlite3_file *pFile){
+ int rc = SQLITE_OK;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+
+ /* Decrement the real_file ref-count. */
+ pReal->nRef--;
+ assert(pReal->nRef>=0);
+
+ /* When the ref-count reaches 0, destroy the structure */
+ if( pReal->nRef==0 ){
+ *pReal->ppThis = pReal->pNext;
+ if( pReal->pNext ){
+ pReal->pNext->ppThis = pReal->ppThis;
+ }
+ rc = pReal->pFile->pMethods->xClose(pReal->pFile);
+ sqlite3_free(pReal);
+ }
+
+ return rc;
+}
+
+/*
+** Read data from an fs-file.
+*/
+static int fsRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc = SQLITE_OK;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ sqlite3_file *pF = pReal->pFile;
+
+ if( (p->eType==DATABASE_FILE && (iAmt+iOfst)>pReal->nDatabase)
+ || (p->eType==JOURNAL_FILE && (iAmt+iOfst)>pReal->nJournal)
+ ){
+ rc = SQLITE_IOERR_SHORT_READ;
+ }else if( p->eType==DATABASE_FILE ){
+ rc = pF->pMethods->xRead(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
+ }else{
+ /* Journal file. */
+ int iRem = iAmt;
+ int iBuf = 0;
+ int ii = (int)iOfst;
+ while( iRem>0 && rc==SQLITE_OK ){
+ int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
+ int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
+
+ rc = pF->pMethods->xRead(pF, &((char *)zBuf)[iBuf], iRealAmt, iRealOff);
+ ii += iRealAmt;
+ iBuf += iRealAmt;
+ iRem -= iRealAmt;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Write data to an fs-file.
+*/
+static int fsWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc = SQLITE_OK;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ sqlite3_file *pF = pReal->pFile;
+
+ if( p->eType==DATABASE_FILE ){
+ if( (iAmt+iOfst+BLOCKSIZE)>(pReal->nBlob-pReal->nJournal) ){
+ rc = SQLITE_FULL;
+ }else{
+ rc = pF->pMethods->xWrite(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
+ if( rc==SQLITE_OK ){
+ pReal->nDatabase = (int)MAX(pReal->nDatabase, iAmt+iOfst);
+ }
+ }
+ }else{
+ /* Journal file. */
+ int iRem = iAmt;
+ int iBuf = 0;
+ int ii = (int)iOfst;
+ while( iRem>0 && rc==SQLITE_OK ){
+ int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
+ int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
+
+ if( iRealOff<(pReal->nDatabase+BLOCKSIZE) ){
+ rc = SQLITE_FULL;
+ }else{
+ rc = pF->pMethods->xWrite(pF, &((char *)zBuf)[iBuf], iRealAmt,iRealOff);
+ ii += iRealAmt;
+ iBuf += iRealAmt;
+ iRem -= iRealAmt;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pReal->nJournal = (int)MAX(pReal->nJournal, iAmt+iOfst);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Truncate an fs-file.
+*/
+static int fsTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ if( p->eType==DATABASE_FILE ){
+ pReal->nDatabase = (int)MIN(pReal->nDatabase, size);
+ }else{
+ pReal->nJournal = (int)MIN(pReal->nJournal, size);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Sync an fs-file.
+*/
+static int fsSync(sqlite3_file *pFile, int flags){
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ sqlite3_file *pRealFile = pReal->pFile;
+ int rc = SQLITE_OK;
+
+ if( p->eType==DATABASE_FILE ){
+ unsigned char zSize[4];
+ zSize[0] = (pReal->nDatabase&0xFF000000)>>24;
+ zSize[1] = (unsigned char)((pReal->nDatabase&0x00FF0000)>>16);
+ zSize[2] = (pReal->nDatabase&0x0000FF00)>>8;
+ zSize[3] = (pReal->nDatabase&0x000000FF);
+ rc = pRealFile->pMethods->xWrite(pRealFile, zSize, 4, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = pRealFile->pMethods->xSync(pRealFile, flags&(~SQLITE_SYNC_DATAONLY));
+ }
+
+ return rc;
+}
+
+/*
+** Return the current file-size of an fs-file.
+*/
+static int fsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ if( p->eType==DATABASE_FILE ){
+ *pSize = pReal->nDatabase;
+ }else{
+ *pSize = pReal->nJournal;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Lock an fs-file.
+*/
+static int fsLock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Unlock an fs-file.
+*/
+static int fsUnlock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an fs-file.
+*/
+static int fsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on an fs-file.
+*/
+static int fsFileControl(sqlite3_file *pFile, int op, void *pArg){
+ if( op==SQLITE_FCNTL_PRAGMA ) return SQLITE_NOTFOUND;
+ return SQLITE_OK;
+}
+
+/*
+** Return the sector-size in bytes for an fs-file.
+*/
+static int fsSectorSize(sqlite3_file *pFile){
+ return BLOCKSIZE;
+}
+
+/*
+** Return the device characteristic flags supported by an fs-file.
+*/
+static int fsDeviceCharacteristics(sqlite3_file *pFile){
+ return 0;
+}
+
+/*
+** Open an fs file handle.
+*/
+static int fsOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = 0;
+ int eType;
+ int nName;
+ int rc = SQLITE_OK;
+
+ if( 0==(flags&(SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL)) ){
+ tmp_file *p2 = (tmp_file *)pFile;
+ memset(p2, 0, sizeof(*p2));
+ p2->base.pMethods = &tmp_io_methods;
+ return SQLITE_OK;
+ }
+
+ eType = ((flags&(SQLITE_OPEN_MAIN_DB))?DATABASE_FILE:JOURNAL_FILE);
+ p->base.pMethods = &fs_io_methods;
+ p->eType = eType;
+
+ assert(strlen("-journal")==8);
+ nName = (int)strlen(zName)-((eType==JOURNAL_FILE)?8:0);
+ pReal=pFsVfs->pFileList;
+ for(; pReal && strncmp(pReal->zName, zName, nName); pReal=pReal->pNext);
+
+ if( !pReal ){
+ int real_flags = (flags&~(SQLITE_OPEN_MAIN_DB))|SQLITE_OPEN_TEMP_DB;
+ sqlite3_int64 size;
+ sqlite3_file *pRealFile;
+ sqlite3_vfs *pParent = pFsVfs->pParent;
+ assert(eType==DATABASE_FILE);
+
+ pReal = (fs_real_file *)sqlite3_malloc(sizeof(*pReal)+pParent->szOsFile);
+ if( !pReal ){
+ rc = SQLITE_NOMEM;
+ goto open_out;
+ }
+ memset(pReal, 0, sizeof(*pReal)+pParent->szOsFile);
+ pReal->zName = zName;
+ pReal->pFile = (sqlite3_file *)(&pReal[1]);
+
+ rc = pParent->xOpen(pParent, zName, pReal->pFile, real_flags, pOutFlags);
+ if( rc!=SQLITE_OK ){
+ goto open_out;
+ }
+ pRealFile = pReal->pFile;
+
+ rc = pRealFile->pMethods->xFileSize(pRealFile, &size);
+ if( rc!=SQLITE_OK ){
+ goto open_out;
+ }
+ if( size==0 ){
+ rc = pRealFile->pMethods->xWrite(pRealFile, "\0", 1, BLOBSIZE-1);
+ pReal->nBlob = BLOBSIZE;
+ }else{
+ unsigned char zS[4];
+ pReal->nBlob = (int)size;
+ rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, 0);
+ pReal->nDatabase = (zS[0]<<24)+(zS[1]<<16)+(zS[2]<<8)+zS[3];
+ if( rc==SQLITE_OK ){
+ rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, pReal->nBlob-4);
+ if( zS[0] || zS[1] || zS[2] || zS[3] ){
+ pReal->nJournal = pReal->nBlob;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pReal->pNext = pFsVfs->pFileList;
+ if( pReal->pNext ){
+ pReal->pNext->ppThis = &pReal->pNext;
+ }
+ pReal->ppThis = &pFsVfs->pFileList;
+ pFsVfs->pFileList = pReal;
+ }
+ }
+
+open_out:
+ if( pReal ){
+ if( rc==SQLITE_OK ){
+ p->pReal = pReal;
+ pReal->nRef++;
+ }else{
+ if( pReal->pFile->pMethods ){
+ pReal->pFile->pMethods->xClose(pReal->pFile);
+ }
+ sqlite3_free(pReal);
+ }
+ }
+ return rc;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int fsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ int rc = SQLITE_OK;
+ fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
+ fs_real_file *pReal;
+ sqlite3_file *pF;
+ int nName = (int)strlen(zPath) - 8;
+
+ assert(strlen("-journal")==8);
+ assert(strcmp("-journal", &zPath[nName])==0);
+
+ pReal = pFsVfs->pFileList;
+ for(; pReal && strncmp(pReal->zName, zPath, nName); pReal=pReal->pNext);
+ if( pReal ){
+ pF = pReal->pFile;
+ rc = pF->pMethods->xWrite(pF, "\0\0\0\0", 4, pReal->nBlob-BLOCKSIZE);
+ if( rc==SQLITE_OK ){
+ pReal->nJournal = 0;
+ }
+ }
+ return rc;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int fsAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
+ fs_real_file *pReal;
+ int isJournal = 0;
+ int nName = (int)strlen(zPath);
+
+ if( flags!=SQLITE_ACCESS_EXISTS ){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xAccess(pParent, zPath, flags, pResOut);
+ }
+
+ assert(strlen("-journal")==8);
+ if( nName>8 && strcmp("-journal", &zPath[nName-8])==0 ){
+ nName -= 8;
+ isJournal = 1;
+ }
+
+ pReal = pFsVfs->pFileList;
+ for(; pReal && strncmp(pReal->zName, zPath, nName); pReal=pReal->pNext);
+
+ *pResOut = (pReal && (!isJournal || pReal->nJournal>0));
+ return SQLITE_OK;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (FS_MAX_PATHNAME+1) bytes.
+*/
+static int fsFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zPath, /* Possibly relative input path */
+ int nOut, /* Size of output buffer in bytes */
+ char *zOut /* Output buffer */
+){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xFullPathname(pParent, zPath, nOut, zOut);
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *fsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xDlOpen(pParent, zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void fsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ pParent->xDlError(pParent, nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*fsDlSym(sqlite3_vfs *pVfs, void *pH, const char *zSym))(void){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xDlSym(pParent, pH, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void fsDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ pParent->xDlClose(pParent, pHandle);
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int fsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xRandomness(pParent, nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int fsSleep(sqlite3_vfs *pVfs, int nMicro){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xSleep(pParent, nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int fsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xCurrentTime(pParent, pTimeOut);
+}
+
+/*
+** This procedure registers the fs vfs with SQLite. If the argument is
+** true, the fs vfs becomes the new default vfs. It is the only publicly
+** available function in this file.
+*/
+int fs_register(void){
+ if( fs_vfs.pParent ) return SQLITE_OK;
+ fs_vfs.pParent = sqlite3_vfs_find(0);
+ fs_vfs.base.mxPathname = fs_vfs.pParent->mxPathname;
+ fs_vfs.base.szOsFile = MAX(sizeof(tmp_file), sizeof(fs_file));
+ return sqlite3_vfs_register(&fs_vfs.base, 0);
+}
+
+#ifdef SQLITE_TEST
+ int SqlitetestOnefile_Init() {return fs_register();}
+#endif