text: /*
  ** 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 = 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 = 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 = 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 = 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 = MAX(pReal->nDatabase, iAmt+iOfst);
        }
      }
    }else{
      /* Journal file. */
      int iRem = iAmt;
      int iBuf = 0;
      int ii = 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 = 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 = MIN(pReal->nDatabase, size);
    }else{
      pReal->nJournal = 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] = (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){
    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 *p = (tmp_file *)pFile;
      memset(p, 0, sizeof(*p));
      p->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 = 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 = 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 = 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 = 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
  