1/*
2** 2007 September 14
3**
4** The author disclaims copyright to this source code.  In place of
5** a legal notice, here is a blessing:
6**
7**    May you do good and not evil.
8**    May you find forgiveness for yourself and forgive others.
9**    May you share freely, never taking more than you give.
10**
11*************************************************************************
12**
13** OVERVIEW:
14**
15**   This file contains some example code demonstrating how the SQLite
16**   vfs feature can be used to have SQLite operate directly on an
17**   embedded media, without using an intermediate file system.
18**
19**   Because this is only a demo designed to run on a workstation, the
20**   underlying media is simulated using a regular file-system file. The
21**   size of the file is fixed when it is first created (default size 10 MB).
22**   From SQLite's point of view, this space is used to store a single
23**   database file and the journal file.
24**
25**   Any statement journal created is stored in volatile memory obtained
26**   from sqlite3_malloc(). Any attempt to create a temporary database file
27**   will fail (SQLITE_IOERR). To prevent SQLite from attempting this,
28**   it should be configured to store all temporary database files in
29**   main memory (see pragma "temp_store" or the SQLITE_TEMP_STORE compile
30**   time option).
31**
32** ASSUMPTIONS:
33**
34**   After it has been created, the blob file is accessed using the
35**   following three functions only:
36**
37**       mediaRead();            - Read a 512 byte block from the file.
38**       mediaWrite();           - Write a 512 byte block to the file.
39**       mediaSync();            - Tell the media hardware to sync.
40**
41**   It is assumed that these can be easily implemented by any "real"
42**   media vfs driver adapting this code.
43**
44** FILE FORMAT:
45**
46**   The basic principle is that the "database file" is stored at the
47**   beginning of the 10 MB blob and grows in a forward direction. The
48**   "journal file" is stored at the end of the 10MB blob and grows
49**   in the reverse direction. If, during a transaction, insufficient
50**   space is available to expand either the journal or database file,
51**   an SQLITE_FULL error is returned. The database file is never allowed
52**   to consume more than 90% of the blob space. If SQLite tries to
53**   create a file larger than this, SQLITE_FULL is returned.
54**
55**   No allowance is made for "wear-leveling", as is required by.
56**   embedded devices in the absence of equivalent hardware features.
57**
58**   The first 512 block byte of the file is reserved for storing the
59**   size of the "database file". It is updated as part of the sync()
60**   operation. On startup, it can only be trusted if no journal file
61**   exists. If a journal-file does exist, then it stores the real size
62**   of the database region. The second and subsequent blocks store the
63**   actual database content.
64**
65**   The size of the "journal file" is not stored persistently in the
66**   file. When the system is running, the size of the journal file is
67**   stored in volatile memory. When recovering from a crash, this vfs
68**   reports a very large size for the journal file. The normal journal
69**   header and checksum mechanisms serve to prevent SQLite from
70**   processing any data that lies past the logical end of the journal.
71**
72**   When SQLite calls OsDelete() to delete the journal file, the final
73**   512 bytes of the blob (the area containing the first journal header)
74**   are zeroed.
75**
76** LOCKING:
77**
78**   File locking is a no-op. Only one connection may be open at any one
79**   time using this demo vfs.
80*/
81
82#include "sqlite3.h"
83#include <assert.h>
84#include <string.h>
85
86/*
87** Maximum pathname length supported by the fs backend.
88*/
89#define BLOCKSIZE 512
90#define BLOBSIZE 10485760
91
92/*
93** Name used to identify this VFS.
94*/
95#define FS_VFS_NAME "fs"
96
97typedef struct fs_real_file fs_real_file;
98struct fs_real_file {
99  sqlite3_file *pFile;
100  const char *zName;
101  int nDatabase;              /* Current size of database region */
102  int nJournal;               /* Current size of journal region */
103  int nBlob;                  /* Total size of allocated blob */
104  int nRef;                   /* Number of pointers to this structure */
105  fs_real_file *pNext;
106  fs_real_file **ppThis;
107};
108
109typedef struct fs_file fs_file;
110struct fs_file {
111  sqlite3_file base;
112  int eType;
113  fs_real_file *pReal;
114};
115
116typedef struct tmp_file tmp_file;
117struct tmp_file {
118  sqlite3_file base;
119  int nSize;
120  int nAlloc;
121  char *zAlloc;
122};
123
124/* Values for fs_file.eType. */
125#define DATABASE_FILE   1
126#define JOURNAL_FILE    2
127
128/*
129** Method declarations for fs_file.
130*/
131static int fsClose(sqlite3_file*);
132static int fsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
133static int fsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
134static int fsTruncate(sqlite3_file*, sqlite3_int64 size);
135static int fsSync(sqlite3_file*, int flags);
136static int fsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
137static int fsLock(sqlite3_file*, int);
138static int fsUnlock(sqlite3_file*, int);
139static int fsCheckReservedLock(sqlite3_file*, int *pResOut);
140static int fsFileControl(sqlite3_file*, int op, void *pArg);
141static int fsSectorSize(sqlite3_file*);
142static int fsDeviceCharacteristics(sqlite3_file*);
143
144/*
145** Method declarations for tmp_file.
146*/
147static int tmpClose(sqlite3_file*);
148static int tmpRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
149static int tmpWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
150static int tmpTruncate(sqlite3_file*, sqlite3_int64 size);
151static int tmpSync(sqlite3_file*, int flags);
152static int tmpFileSize(sqlite3_file*, sqlite3_int64 *pSize);
153static int tmpLock(sqlite3_file*, int);
154static int tmpUnlock(sqlite3_file*, int);
155static int tmpCheckReservedLock(sqlite3_file*, int *pResOut);
156static int tmpFileControl(sqlite3_file*, int op, void *pArg);
157static int tmpSectorSize(sqlite3_file*);
158static int tmpDeviceCharacteristics(sqlite3_file*);
159
160/*
161** Method declarations for fs_vfs.
162*/
163static int fsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
164static int fsDelete(sqlite3_vfs*, const char *zName, int syncDir);
165static int fsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
166static int fsFullPathname(sqlite3_vfs*, const char *zName, int nOut,char *zOut);
167static void *fsDlOpen(sqlite3_vfs*, const char *zFilename);
168static void fsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
169static void (*fsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
170static void fsDlClose(sqlite3_vfs*, void*);
171static int fsRandomness(sqlite3_vfs*, int nByte, char *zOut);
172static int fsSleep(sqlite3_vfs*, int microseconds);
173static int fsCurrentTime(sqlite3_vfs*, double*);
174
175
176typedef struct fs_vfs_t fs_vfs_t;
177struct fs_vfs_t {
178  sqlite3_vfs base;
179  fs_real_file *pFileList;
180  sqlite3_vfs *pParent;
181};
182
183static fs_vfs_t fs_vfs = {
184  {
185    1,                                          /* iVersion */
186    0,                                          /* szOsFile */
187    0,                                          /* mxPathname */
188    0,                                          /* pNext */
189    FS_VFS_NAME,                                /* zName */
190    0,                                          /* pAppData */
191    fsOpen,                                     /* xOpen */
192    fsDelete,                                   /* xDelete */
193    fsAccess,                                   /* xAccess */
194    fsFullPathname,                             /* xFullPathname */
195    fsDlOpen,                                   /* xDlOpen */
196    fsDlError,                                  /* xDlError */
197    fsDlSym,                                    /* xDlSym */
198    fsDlClose,                                  /* xDlClose */
199    fsRandomness,                               /* xRandomness */
200    fsSleep,                                    /* xSleep */
201    fsCurrentTime,                              /* xCurrentTime */
202    0                                           /* xCurrentTimeInt64 */
203  },
204  0,                                            /* pFileList */
205  0                                             /* pParent */
206};
207
208static sqlite3_io_methods fs_io_methods = {
209  1,                            /* iVersion */
210  fsClose,                      /* xClose */
211  fsRead,                       /* xRead */
212  fsWrite,                      /* xWrite */
213  fsTruncate,                   /* xTruncate */
214  fsSync,                       /* xSync */
215  fsFileSize,                   /* xFileSize */
216  fsLock,                       /* xLock */
217  fsUnlock,                     /* xUnlock */
218  fsCheckReservedLock,          /* xCheckReservedLock */
219  fsFileControl,                /* xFileControl */
220  fsSectorSize,                 /* xSectorSize */
221  fsDeviceCharacteristics,      /* xDeviceCharacteristics */
222  0,                            /* xShmMap */
223  0,                            /* xShmLock */
224  0,                            /* xShmBarrier */
225  0                             /* xShmUnmap */
226};
227
228
229static sqlite3_io_methods tmp_io_methods = {
230  1,                            /* iVersion */
231  tmpClose,                     /* xClose */
232  tmpRead,                      /* xRead */
233  tmpWrite,                     /* xWrite */
234  tmpTruncate,                  /* xTruncate */
235  tmpSync,                      /* xSync */
236  tmpFileSize,                  /* xFileSize */
237  tmpLock,                      /* xLock */
238  tmpUnlock,                    /* xUnlock */
239  tmpCheckReservedLock,         /* xCheckReservedLock */
240  tmpFileControl,               /* xFileControl */
241  tmpSectorSize,                /* xSectorSize */
242  tmpDeviceCharacteristics,     /* xDeviceCharacteristics */
243  0,                            /* xShmMap */
244  0,                            /* xShmLock */
245  0,                            /* xShmBarrier */
246  0                             /* xShmUnmap */
247};
248
249/* Useful macros used in several places */
250#define MIN(x,y) ((x)<(y)?(x):(y))
251#define MAX(x,y) ((x)>(y)?(x):(y))
252
253
254/*
255** Close a tmp-file.
256*/
257static int tmpClose(sqlite3_file *pFile){
258  tmp_file *pTmp = (tmp_file *)pFile;
259  sqlite3_free(pTmp->zAlloc);
260  return SQLITE_OK;
261}
262
263/*
264** Read data from a tmp-file.
265*/
266static int tmpRead(
267  sqlite3_file *pFile,
268  void *zBuf,
269  int iAmt,
270  sqlite_int64 iOfst
271){
272  tmp_file *pTmp = (tmp_file *)pFile;
273  if( (iAmt+iOfst)>pTmp->nSize ){
274    return SQLITE_IOERR_SHORT_READ;
275  }
276  memcpy(zBuf, &pTmp->zAlloc[iOfst], iAmt);
277  return SQLITE_OK;
278}
279
280/*
281** Write data to a tmp-file.
282*/
283static int tmpWrite(
284  sqlite3_file *pFile,
285  const void *zBuf,
286  int iAmt,
287  sqlite_int64 iOfst
288){
289  tmp_file *pTmp = (tmp_file *)pFile;
290  if( (iAmt+iOfst)>pTmp->nAlloc ){
291    int nNew = 2*(iAmt+iOfst+pTmp->nAlloc);
292    char *zNew = sqlite3_realloc(pTmp->zAlloc, nNew);
293    if( !zNew ){
294      return SQLITE_NOMEM;
295    }
296    pTmp->zAlloc = zNew;
297    pTmp->nAlloc = nNew;
298  }
299  memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt);
300  pTmp->nSize = MAX(pTmp->nSize, iOfst+iAmt);
301  return SQLITE_OK;
302}
303
304/*
305** Truncate a tmp-file.
306*/
307static int tmpTruncate(sqlite3_file *pFile, sqlite_int64 size){
308  tmp_file *pTmp = (tmp_file *)pFile;
309  pTmp->nSize = MIN(pTmp->nSize, size);
310  return SQLITE_OK;
311}
312
313/*
314** Sync a tmp-file.
315*/
316static int tmpSync(sqlite3_file *pFile, int flags){
317  return SQLITE_OK;
318}
319
320/*
321** Return the current file-size of a tmp-file.
322*/
323static int tmpFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
324  tmp_file *pTmp = (tmp_file *)pFile;
325  *pSize = pTmp->nSize;
326  return SQLITE_OK;
327}
328
329/*
330** Lock a tmp-file.
331*/
332static int tmpLock(sqlite3_file *pFile, int eLock){
333  return SQLITE_OK;
334}
335
336/*
337** Unlock a tmp-file.
338*/
339static int tmpUnlock(sqlite3_file *pFile, int eLock){
340  return SQLITE_OK;
341}
342
343/*
344** Check if another file-handle holds a RESERVED lock on a tmp-file.
345*/
346static int tmpCheckReservedLock(sqlite3_file *pFile, int *pResOut){
347  *pResOut = 0;
348  return SQLITE_OK;
349}
350
351/*
352** File control method. For custom operations on a tmp-file.
353*/
354static int tmpFileControl(sqlite3_file *pFile, int op, void *pArg){
355  return SQLITE_OK;
356}
357
358/*
359** Return the sector-size in bytes for a tmp-file.
360*/
361static int tmpSectorSize(sqlite3_file *pFile){
362  return 0;
363}
364
365/*
366** Return the device characteristic flags supported by a tmp-file.
367*/
368static int tmpDeviceCharacteristics(sqlite3_file *pFile){
369  return 0;
370}
371
372/*
373** Close an fs-file.
374*/
375static int fsClose(sqlite3_file *pFile){
376  int rc = SQLITE_OK;
377  fs_file *p = (fs_file *)pFile;
378  fs_real_file *pReal = p->pReal;
379
380  /* Decrement the real_file ref-count. */
381  pReal->nRef--;
382  assert(pReal->nRef>=0);
383
384  /* When the ref-count reaches 0, destroy the structure */
385  if( pReal->nRef==0 ){
386    *pReal->ppThis = pReal->pNext;
387    if( pReal->pNext ){
388      pReal->pNext->ppThis = pReal->ppThis;
389    }
390    rc = pReal->pFile->pMethods->xClose(pReal->pFile);
391    sqlite3_free(pReal);
392  }
393
394  return rc;
395}
396
397/*
398** Read data from an fs-file.
399*/
400static int fsRead(
401  sqlite3_file *pFile,
402  void *zBuf,
403  int iAmt,
404  sqlite_int64 iOfst
405){
406  int rc = SQLITE_OK;
407  fs_file *p = (fs_file *)pFile;
408  fs_real_file *pReal = p->pReal;
409  sqlite3_file *pF = pReal->pFile;
410
411  if( (p->eType==DATABASE_FILE && (iAmt+iOfst)>pReal->nDatabase)
412   || (p->eType==JOURNAL_FILE && (iAmt+iOfst)>pReal->nJournal)
413  ){
414    rc = SQLITE_IOERR_SHORT_READ;
415  }else if( p->eType==DATABASE_FILE ){
416    rc = pF->pMethods->xRead(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
417  }else{
418    /* Journal file. */
419    int iRem = iAmt;
420    int iBuf = 0;
421    int ii = iOfst;
422    while( iRem>0 && rc==SQLITE_OK ){
423      int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
424      int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
425
426      rc = pF->pMethods->xRead(pF, &((char *)zBuf)[iBuf], iRealAmt, iRealOff);
427      ii += iRealAmt;
428      iBuf += iRealAmt;
429      iRem -= iRealAmt;
430    }
431  }
432
433  return rc;
434}
435
436/*
437** Write data to an fs-file.
438*/
439static int fsWrite(
440  sqlite3_file *pFile,
441  const void *zBuf,
442  int iAmt,
443  sqlite_int64 iOfst
444){
445  int rc = SQLITE_OK;
446  fs_file *p = (fs_file *)pFile;
447  fs_real_file *pReal = p->pReal;
448  sqlite3_file *pF = pReal->pFile;
449
450  if( p->eType==DATABASE_FILE ){
451    if( (iAmt+iOfst+BLOCKSIZE)>(pReal->nBlob-pReal->nJournal) ){
452      rc = SQLITE_FULL;
453    }else{
454      rc = pF->pMethods->xWrite(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
455      if( rc==SQLITE_OK ){
456        pReal->nDatabase = MAX(pReal->nDatabase, iAmt+iOfst);
457      }
458    }
459  }else{
460    /* Journal file. */
461    int iRem = iAmt;
462    int iBuf = 0;
463    int ii = iOfst;
464    while( iRem>0 && rc==SQLITE_OK ){
465      int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
466      int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
467
468      if( iRealOff<(pReal->nDatabase+BLOCKSIZE) ){
469        rc = SQLITE_FULL;
470      }else{
471        rc = pF->pMethods->xWrite(pF, &((char *)zBuf)[iBuf], iRealAmt,iRealOff);
472        ii += iRealAmt;
473        iBuf += iRealAmt;
474        iRem -= iRealAmt;
475      }
476    }
477    if( rc==SQLITE_OK ){
478      pReal->nJournal = MAX(pReal->nJournal, iAmt+iOfst);
479    }
480  }
481
482  return rc;
483}
484
485/*
486** Truncate an fs-file.
487*/
488static int fsTruncate(sqlite3_file *pFile, sqlite_int64 size){
489  fs_file *p = (fs_file *)pFile;
490  fs_real_file *pReal = p->pReal;
491  if( p->eType==DATABASE_FILE ){
492    pReal->nDatabase = MIN(pReal->nDatabase, size);
493  }else{
494    pReal->nJournal = MIN(pReal->nJournal, size);
495  }
496  return SQLITE_OK;
497}
498
499/*
500** Sync an fs-file.
501*/
502static int fsSync(sqlite3_file *pFile, int flags){
503  fs_file *p = (fs_file *)pFile;
504  fs_real_file *pReal = p->pReal;
505  sqlite3_file *pRealFile = pReal->pFile;
506  int rc = SQLITE_OK;
507
508  if( p->eType==DATABASE_FILE ){
509    unsigned char zSize[4];
510    zSize[0] = (pReal->nDatabase&0xFF000000)>>24;
511    zSize[1] = (pReal->nDatabase&0x00FF0000)>>16;
512    zSize[2] = (pReal->nDatabase&0x0000FF00)>>8;
513    zSize[3] = (pReal->nDatabase&0x000000FF);
514    rc = pRealFile->pMethods->xWrite(pRealFile, zSize, 4, 0);
515  }
516  if( rc==SQLITE_OK ){
517    rc = pRealFile->pMethods->xSync(pRealFile, flags&(~SQLITE_SYNC_DATAONLY));
518  }
519
520  return rc;
521}
522
523/*
524** Return the current file-size of an fs-file.
525*/
526static int fsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
527  fs_file *p = (fs_file *)pFile;
528  fs_real_file *pReal = p->pReal;
529  if( p->eType==DATABASE_FILE ){
530    *pSize = pReal->nDatabase;
531  }else{
532    *pSize = pReal->nJournal;
533  }
534  return SQLITE_OK;
535}
536
537/*
538** Lock an fs-file.
539*/
540static int fsLock(sqlite3_file *pFile, int eLock){
541  return SQLITE_OK;
542}
543
544/*
545** Unlock an fs-file.
546*/
547static int fsUnlock(sqlite3_file *pFile, int eLock){
548  return SQLITE_OK;
549}
550
551/*
552** Check if another file-handle holds a RESERVED lock on an fs-file.
553*/
554static int fsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
555  *pResOut = 0;
556  return SQLITE_OK;
557}
558
559/*
560** File control method. For custom operations on an fs-file.
561*/
562static int fsFileControl(sqlite3_file *pFile, int op, void *pArg){
563  return SQLITE_OK;
564}
565
566/*
567** Return the sector-size in bytes for an fs-file.
568*/
569static int fsSectorSize(sqlite3_file *pFile){
570  return BLOCKSIZE;
571}
572
573/*
574** Return the device characteristic flags supported by an fs-file.
575*/
576static int fsDeviceCharacteristics(sqlite3_file *pFile){
577  return 0;
578}
579
580/*
581** Open an fs file handle.
582*/
583static int fsOpen(
584  sqlite3_vfs *pVfs,
585  const char *zName,
586  sqlite3_file *pFile,
587  int flags,
588  int *pOutFlags
589){
590  fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
591  fs_file *p = (fs_file *)pFile;
592  fs_real_file *pReal = 0;
593  int eType;
594  int nName;
595  int rc = SQLITE_OK;
596
597  if( 0==(flags&(SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL)) ){
598    tmp_file *p = (tmp_file *)pFile;
599    memset(p, 0, sizeof(*p));
600    p->base.pMethods = &tmp_io_methods;
601    return SQLITE_OK;
602  }
603
604  eType = ((flags&(SQLITE_OPEN_MAIN_DB))?DATABASE_FILE:JOURNAL_FILE);
605  p->base.pMethods = &fs_io_methods;
606  p->eType = eType;
607
608  assert(strlen("-journal")==8);
609  nName = strlen(zName)-((eType==JOURNAL_FILE)?8:0);
610  pReal=pFsVfs->pFileList;
611  for(; pReal && strncmp(pReal->zName, zName, nName); pReal=pReal->pNext);
612
613  if( !pReal ){
614    int real_flags = (flags&~(SQLITE_OPEN_MAIN_DB))|SQLITE_OPEN_TEMP_DB;
615    sqlite3_int64 size;
616    sqlite3_file *pRealFile;
617    sqlite3_vfs *pParent = pFsVfs->pParent;
618    assert(eType==DATABASE_FILE);
619
620    pReal = (fs_real_file *)sqlite3_malloc(sizeof(*pReal)+pParent->szOsFile);
621    if( !pReal ){
622      rc = SQLITE_NOMEM;
623      goto open_out;
624    }
625    memset(pReal, 0, sizeof(*pReal)+pParent->szOsFile);
626    pReal->zName = zName;
627    pReal->pFile = (sqlite3_file *)(&pReal[1]);
628
629    rc = pParent->xOpen(pParent, zName, pReal->pFile, real_flags, pOutFlags);
630    if( rc!=SQLITE_OK ){
631      goto open_out;
632    }
633    pRealFile = pReal->pFile;
634
635    rc = pRealFile->pMethods->xFileSize(pRealFile, &size);
636    if( rc!=SQLITE_OK ){
637      goto open_out;
638    }
639    if( size==0 ){
640      rc = pRealFile->pMethods->xWrite(pRealFile, "\0", 1, BLOBSIZE-1);
641      pReal->nBlob = BLOBSIZE;
642    }else{
643      unsigned char zS[4];
644      pReal->nBlob = size;
645      rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, 0);
646      pReal->nDatabase = (zS[0]<<24)+(zS[1]<<16)+(zS[2]<<8)+zS[3];
647      if( rc==SQLITE_OK ){
648        rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, pReal->nBlob-4);
649        if( zS[0] || zS[1] || zS[2] || zS[3] ){
650          pReal->nJournal = pReal->nBlob;
651        }
652      }
653    }
654
655    if( rc==SQLITE_OK ){
656      pReal->pNext = pFsVfs->pFileList;
657      if( pReal->pNext ){
658        pReal->pNext->ppThis = &pReal->pNext;
659      }
660      pReal->ppThis = &pFsVfs->pFileList;
661      pFsVfs->pFileList = pReal;
662    }
663  }
664
665open_out:
666  if( pReal ){
667    if( rc==SQLITE_OK ){
668      p->pReal = pReal;
669      pReal->nRef++;
670    }else{
671      if( pReal->pFile->pMethods ){
672        pReal->pFile->pMethods->xClose(pReal->pFile);
673      }
674      sqlite3_free(pReal);
675    }
676  }
677  return rc;
678}
679
680/*
681** Delete the file located at zPath. If the dirSync argument is true,
682** ensure the file-system modifications are synced to disk before
683** returning.
684*/
685static int fsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
686  int rc = SQLITE_OK;
687  fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
688  fs_real_file *pReal;
689  sqlite3_file *pF;
690  int nName = strlen(zPath) - 8;
691
692  assert(strlen("-journal")==8);
693  assert(strcmp("-journal", &zPath[nName])==0);
694
695  pReal = pFsVfs->pFileList;
696  for(; pReal && strncmp(pReal->zName, zPath, nName); pReal=pReal->pNext);
697  if( pReal ){
698    pF = pReal->pFile;
699    rc = pF->pMethods->xWrite(pF, "\0\0\0\0", 4, pReal->nBlob-BLOCKSIZE);
700    if( rc==SQLITE_OK ){
701      pReal->nJournal = 0;
702    }
703  }
704  return rc;
705}
706
707/*
708** Test for access permissions. Return true if the requested permission
709** is available, or false otherwise.
710*/
711static int fsAccess(
712  sqlite3_vfs *pVfs,
713  const char *zPath,
714  int flags,
715  int *pResOut
716){
717  fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
718  fs_real_file *pReal;
719  int isJournal = 0;
720  int nName = strlen(zPath);
721
722  if( flags!=SQLITE_ACCESS_EXISTS ){
723    sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
724    return pParent->xAccess(pParent, zPath, flags, pResOut);
725  }
726
727  assert(strlen("-journal")==8);
728  if( nName>8 && strcmp("-journal", &zPath[nName-8])==0 ){
729    nName -= 8;
730    isJournal = 1;
731  }
732
733  pReal = pFsVfs->pFileList;
734  for(; pReal && strncmp(pReal->zName, zPath, nName); pReal=pReal->pNext);
735
736  *pResOut = (pReal && (!isJournal || pReal->nJournal>0));
737  return SQLITE_OK;
738}
739
740/*
741** Populate buffer zOut with the full canonical pathname corresponding
742** to the pathname in zPath. zOut is guaranteed to point to a buffer
743** of at least (FS_MAX_PATHNAME+1) bytes.
744*/
745static int fsFullPathname(
746  sqlite3_vfs *pVfs,            /* Pointer to vfs object */
747  const char *zPath,            /* Possibly relative input path */
748  int nOut,                     /* Size of output buffer in bytes */
749  char *zOut                    /* Output buffer */
750){
751  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
752  return pParent->xFullPathname(pParent, zPath, nOut, zOut);
753}
754
755/*
756** Open the dynamic library located at zPath and return a handle.
757*/
758static void *fsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
759  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
760  return pParent->xDlOpen(pParent, zPath);
761}
762
763/*
764** Populate the buffer zErrMsg (size nByte bytes) with a human readable
765** utf-8 string describing the most recent error encountered associated
766** with dynamic libraries.
767*/
768static void fsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
769  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
770  pParent->xDlError(pParent, nByte, zErrMsg);
771}
772
773/*
774** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
775*/
776static void (*fsDlSym(sqlite3_vfs *pVfs, void *pH, const char *zSym))(void){
777  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
778  return pParent->xDlSym(pParent, pH, zSym);
779}
780
781/*
782** Close the dynamic library handle pHandle.
783*/
784static void fsDlClose(sqlite3_vfs *pVfs, void *pHandle){
785  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
786  pParent->xDlClose(pParent, pHandle);
787}
788
789/*
790** Populate the buffer pointed to by zBufOut with nByte bytes of
791** random data.
792*/
793static int fsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
794  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
795  return pParent->xRandomness(pParent, nByte, zBufOut);
796}
797
798/*
799** Sleep for nMicro microseconds. Return the number of microseconds
800** actually slept.
801*/
802static int fsSleep(sqlite3_vfs *pVfs, int nMicro){
803  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
804  return pParent->xSleep(pParent, nMicro);
805}
806
807/*
808** Return the current time as a Julian Day number in *pTimeOut.
809*/
810static int fsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
811  sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
812  return pParent->xCurrentTime(pParent, pTimeOut);
813}
814
815/*
816** This procedure registers the fs vfs with SQLite. If the argument is
817** true, the fs vfs becomes the new default vfs. It is the only publicly
818** available function in this file.
819*/
820int fs_register(void){
821  if( fs_vfs.pParent ) return SQLITE_OK;
822  fs_vfs.pParent = sqlite3_vfs_find(0);
823  fs_vfs.base.mxPathname = fs_vfs.pParent->mxPathname;
824  fs_vfs.base.szOsFile = MAX(sizeof(tmp_file), sizeof(fs_file));
825  return sqlite3_vfs_register(&fs_vfs.base, 0);
826}
827
828#ifdef SQLITE_TEST
829  int SqlitetestOnefile_Init() {return fs_register();}
830#endif
831