xref: /sqlite-3.40.0/src/test_journal.c (revision 922b3580)
1a0fc7296Sdanielk1977 /*
2a0fc7296Sdanielk1977 ** 2008 Jan 22
3a0fc7296Sdanielk1977 **
4a0fc7296Sdanielk1977 ** The author disclaims copyright to this source code.  In place of
5a0fc7296Sdanielk1977 ** a legal notice, here is a blessing:
6a0fc7296Sdanielk1977 **
7a0fc7296Sdanielk1977 **    May you do good and not evil.
8a0fc7296Sdanielk1977 **    May you find forgiveness for yourself and forgive others.
9a0fc7296Sdanielk1977 **    May you share freely, never taking more than you give.
10a0fc7296Sdanielk1977 **
11a0fc7296Sdanielk1977 ******************************************************************************
12a0fc7296Sdanielk1977 **
13a0fc7296Sdanielk1977 ** This file contains code for a VFS layer that acts as a wrapper around
14e86a5b77Sdanielk1977 ** an existing VFS. The code in this file attempts to verify that SQLite
15e86a5b77Sdanielk1977 ** correctly populates and syncs a journal file before writing to a
16e86a5b77Sdanielk1977 ** corresponding database file.
17a12b6fa3Sdrh **
18e86a5b77Sdanielk1977 ** INTERFACE
19e86a5b77Sdanielk1977 **
20e86a5b77Sdanielk1977 **   The public interface to this wrapper VFS is two functions:
21e86a5b77Sdanielk1977 **
22e86a5b77Sdanielk1977 **     jt_register()
23e86a5b77Sdanielk1977 **     jt_unregister()
24e86a5b77Sdanielk1977 **
25e86a5b77Sdanielk1977 **   See header comments associated with those two functions below for
26e86a5b77Sdanielk1977 **   details.
27e86a5b77Sdanielk1977 **
28e86a5b77Sdanielk1977 ** LIMITATIONS
29e86a5b77Sdanielk1977 **
30e86a5b77Sdanielk1977 **   This wrapper will not work if "PRAGMA synchronous = off" is used.
31e86a5b77Sdanielk1977 **
32e86a5b77Sdanielk1977 ** OPERATION
33e86a5b77Sdanielk1977 **
34e86a5b77Sdanielk1977 **  Starting a Transaction:
35e86a5b77Sdanielk1977 **
36e86a5b77Sdanielk1977 **   When a write-transaction is started, the contents of the database is
37e86a5b77Sdanielk1977 **   inspected and the following data stored as part of the database file
38e86a5b77Sdanielk1977 **   handle (type struct jt_file):
39e86a5b77Sdanielk1977 **
40e86a5b77Sdanielk1977 **     a) The page-size of the database file.
41e86a5b77Sdanielk1977 **     b) The number of pages that are in the database file.
42e86a5b77Sdanielk1977 **     c) The set of page numbers corresponding to free-list leaf pages.
43e86a5b77Sdanielk1977 **     d) A check-sum for every page in the database file.
44e86a5b77Sdanielk1977 **
45be217793Sshane **   The start of a write-transaction is deemed to have occurred when a
46e86a5b77Sdanielk1977 **   28-byte journal header is written to byte offset 0 of the journal
47e86a5b77Sdanielk1977 **   file.
48e86a5b77Sdanielk1977 **
49e86a5b77Sdanielk1977 **  Syncing the Journal File:
50e86a5b77Sdanielk1977 **
51e86a5b77Sdanielk1977 **   Whenever the xSync method is invoked to sync a journal-file, the
52e86a5b77Sdanielk1977 **   contents of the journal file are read. For each page written to
53e86a5b77Sdanielk1977 **   the journal file, a check-sum is calculated and compared to the
54e86a5b77Sdanielk1977 **   check-sum calculated for the corresponding database page when the
55e86a5b77Sdanielk1977 **   write-transaction was initialized. The success of the comparison
56e86a5b77Sdanielk1977 **   is assert()ed. So if SQLite has written something other than the
57e86a5b77Sdanielk1977 **   original content to the database file, an assert() will fail.
58e86a5b77Sdanielk1977 **
59e86a5b77Sdanielk1977 **   Additionally, the set of page numbers for which records exist in
60e86a5b77Sdanielk1977 **   the journal file is added to (unioned with) the set of page numbers
61e86a5b77Sdanielk1977 **   corresponding to free-list leaf pages collected when the
62e86a5b77Sdanielk1977 **   write-transaction was initialized. This set comprises the page-numbers
63e86a5b77Sdanielk1977 **   corresponding to those pages that SQLite may now safely modify.
64e86a5b77Sdanielk1977 **
65e86a5b77Sdanielk1977 **  Writing to the Database File:
66e86a5b77Sdanielk1977 **
67e86a5b77Sdanielk1977 **   When a block of data is written to a database file, the following
68e86a5b77Sdanielk1977 **   invariants are asserted:
69e86a5b77Sdanielk1977 **
70e86a5b77Sdanielk1977 **     a) That the block of data is an aligned block of page-size bytes.
71e86a5b77Sdanielk1977 **
72e86a5b77Sdanielk1977 **     b) That if the page being written did not exist when the
73e86a5b77Sdanielk1977 **        transaction was started (i.e. the database file is growing), then
74e86a5b77Sdanielk1977 **        the journal-file must have been synced at least once since
75e86a5b77Sdanielk1977 **        the start of the transaction.
76e86a5b77Sdanielk1977 **
77e86a5b77Sdanielk1977 **     c) That if the page being written did exist when the transaction
78e86a5b77Sdanielk1977 **        was started, then the page must have either been a free-list
79e86a5b77Sdanielk1977 **        leaf page at the start of the transaction, or else must have
80e86a5b77Sdanielk1977 **        been stored in the journal file prior to the most recent sync.
81e86a5b77Sdanielk1977 **
82e86a5b77Sdanielk1977 **  Closing a Transaction:
83e86a5b77Sdanielk1977 **
84e86a5b77Sdanielk1977 **   When a transaction is closed, all data collected at the start of
85e86a5b77Sdanielk1977 **   the transaction, or following an xSync of a journal-file, is
86e86a5b77Sdanielk1977 **   discarded. The end of a transaction is recognized when any one
87e86a5b77Sdanielk1977 **   of the following occur:
88e86a5b77Sdanielk1977 **
89e86a5b77Sdanielk1977 **     a) A block of zeroes (or anything else that is not a valid
90e86a5b77Sdanielk1977 **        journal-header) is written to the start of the journal file.
91e86a5b77Sdanielk1977 **
92e86a5b77Sdanielk1977 **     b) A journal file is truncated to zero bytes in size using xTruncate.
93e86a5b77Sdanielk1977 **
94e86a5b77Sdanielk1977 **     c) The journal file is deleted using xDelete.
95e86a5b77Sdanielk1977 */
96a12b6fa3Sdrh #if SQLITE_TEST          /* This file is used for testing only */
97a12b6fa3Sdrh 
98a12b6fa3Sdrh #include "sqlite3.h"
99a12b6fa3Sdrh #include "sqliteInt.h"
100e86a5b77Sdanielk1977 
101e86a5b77Sdanielk1977 /*
102a0fc7296Sdanielk1977 ** Maximum pathname length supported by the jt backend.
103a0fc7296Sdanielk1977 */
104a0fc7296Sdanielk1977 #define JT_MAX_PATHNAME 512
105a0fc7296Sdanielk1977 
106a0fc7296Sdanielk1977 /*
107a0fc7296Sdanielk1977 ** Name used to identify this VFS.
108a0fc7296Sdanielk1977 */
109a0fc7296Sdanielk1977 #define JT_VFS_NAME "jt"
110a0fc7296Sdanielk1977 
111a0fc7296Sdanielk1977 typedef struct jt_file jt_file;
112a0fc7296Sdanielk1977 struct jt_file {
113a0fc7296Sdanielk1977   sqlite3_file base;
114a0fc7296Sdanielk1977   const char *zName;       /* Name of open file */
115a0fc7296Sdanielk1977   int flags;               /* Flags the file was opened with */
116a0fc7296Sdanielk1977 
117a0fc7296Sdanielk1977   /* The following are only used by database file file handles */
118a0fc7296Sdanielk1977   int eLock;               /* Current lock held on the file */
119a0fc7296Sdanielk1977   u32 nPage;               /* Size of file in pages when transaction started */
120a0fc7296Sdanielk1977   u32 nPagesize;           /* Page size when transaction started */
121a0fc7296Sdanielk1977   Bitvec *pWritable;       /* Bitvec of pages that may be written to the file */
122cd1cbff3Sdanielk1977   u32 *aCksum;             /* Checksum for first nPage pages */
123e86a5b77Sdanielk1977   int nSync;               /* Number of times journal file has been synced */
124cd1cbff3Sdanielk1977 
125cd1cbff3Sdanielk1977   /* Only used by journal file-handles */
126cd1cbff3Sdanielk1977   sqlite3_int64 iMaxOff;   /* Maximum offset written to this transaction */
127a0fc7296Sdanielk1977 
128a0fc7296Sdanielk1977   jt_file *pNext;          /* All files are stored in a linked list */
129a0fc7296Sdanielk1977   sqlite3_file *pReal;     /* The file handle for the underlying vfs */
130a0fc7296Sdanielk1977 };
131a0fc7296Sdanielk1977 
132a0fc7296Sdanielk1977 /*
133a0fc7296Sdanielk1977 ** Method declarations for jt_file.
134a0fc7296Sdanielk1977 */
135a0fc7296Sdanielk1977 static int jtClose(sqlite3_file*);
136a0fc7296Sdanielk1977 static int jtRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
137a0fc7296Sdanielk1977 static int jtWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
138a0fc7296Sdanielk1977 static int jtTruncate(sqlite3_file*, sqlite3_int64 size);
139a0fc7296Sdanielk1977 static int jtSync(sqlite3_file*, int flags);
140a0fc7296Sdanielk1977 static int jtFileSize(sqlite3_file*, sqlite3_int64 *pSize);
141a0fc7296Sdanielk1977 static int jtLock(sqlite3_file*, int);
142a0fc7296Sdanielk1977 static int jtUnlock(sqlite3_file*, int);
143a0fc7296Sdanielk1977 static int jtCheckReservedLock(sqlite3_file*, int *);
144a0fc7296Sdanielk1977 static int jtFileControl(sqlite3_file*, int op, void *pArg);
145a0fc7296Sdanielk1977 static int jtSectorSize(sqlite3_file*);
146a0fc7296Sdanielk1977 static int jtDeviceCharacteristics(sqlite3_file*);
147a0fc7296Sdanielk1977 
148a0fc7296Sdanielk1977 /*
149a0fc7296Sdanielk1977 ** Method declarations for jt_vfs.
150a0fc7296Sdanielk1977 */
151a0fc7296Sdanielk1977 static int jtOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
152a0fc7296Sdanielk1977 static int jtDelete(sqlite3_vfs*, const char *zName, int syncDir);
153a0fc7296Sdanielk1977 static int jtAccess(sqlite3_vfs*, const char *zName, int flags, int *);
154a0fc7296Sdanielk1977 static int jtFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
155a0fc7296Sdanielk1977 static void *jtDlOpen(sqlite3_vfs*, const char *zFilename);
156a0fc7296Sdanielk1977 static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
157a0fc7296Sdanielk1977 static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
158a0fc7296Sdanielk1977 static void jtDlClose(sqlite3_vfs*, void*);
159a0fc7296Sdanielk1977 static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
160a0fc7296Sdanielk1977 static int jtSleep(sqlite3_vfs*, int microseconds);
161a0fc7296Sdanielk1977 static int jtCurrentTime(sqlite3_vfs*, double*);
162007d6c3aSdrh static int jtCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
16305accd22Sdan static int jtGetLastError(sqlite3_vfs*, int, char*);
164a0fc7296Sdanielk1977 
165a0fc7296Sdanielk1977 static sqlite3_vfs jt_vfs = {
166007d6c3aSdrh   2,                             /* iVersion */
167a0fc7296Sdanielk1977   sizeof(jt_file),               /* szOsFile */
168a0fc7296Sdanielk1977   JT_MAX_PATHNAME,               /* mxPathname */
169a0fc7296Sdanielk1977   0,                             /* pNext */
170a0fc7296Sdanielk1977   JT_VFS_NAME,                   /* zName */
171a0fc7296Sdanielk1977   0,                             /* pAppData */
172a0fc7296Sdanielk1977   jtOpen,                        /* xOpen */
173a0fc7296Sdanielk1977   jtDelete,                      /* xDelete */
174a0fc7296Sdanielk1977   jtAccess,                      /* xAccess */
175a0fc7296Sdanielk1977   jtFullPathname,                /* xFullPathname */
176a0fc7296Sdanielk1977   jtDlOpen,                      /* xDlOpen */
177a0fc7296Sdanielk1977   jtDlError,                     /* xDlError */
178a0fc7296Sdanielk1977   jtDlSym,                       /* xDlSym */
179a0fc7296Sdanielk1977   jtDlClose,                     /* xDlClose */
180a0fc7296Sdanielk1977   jtRandomness,                  /* xRandomness */
181a0fc7296Sdanielk1977   jtSleep,                       /* xSleep */
182f2424c52Sdrh   jtCurrentTime,                 /* xCurrentTime */
18305accd22Sdan   jtGetLastError,                /* xGetLastError */
184007d6c3aSdrh   jtCurrentTimeInt64             /* xCurrentTimeInt64 */
185a0fc7296Sdanielk1977 };
186a0fc7296Sdanielk1977 
187a0fc7296Sdanielk1977 static sqlite3_io_methods jt_io_methods = {
188a0fc7296Sdanielk1977   1,                             /* iVersion */
189a0fc7296Sdanielk1977   jtClose,                       /* xClose */
190a0fc7296Sdanielk1977   jtRead,                        /* xRead */
191a0fc7296Sdanielk1977   jtWrite,                       /* xWrite */
192a0fc7296Sdanielk1977   jtTruncate,                    /* xTruncate */
193a0fc7296Sdanielk1977   jtSync,                        /* xSync */
194a0fc7296Sdanielk1977   jtFileSize,                    /* xFileSize */
195a0fc7296Sdanielk1977   jtLock,                        /* xLock */
196a0fc7296Sdanielk1977   jtUnlock,                      /* xUnlock */
197a0fc7296Sdanielk1977   jtCheckReservedLock,           /* xCheckReservedLock */
198a0fc7296Sdanielk1977   jtFileControl,                 /* xFileControl */
199a0fc7296Sdanielk1977   jtSectorSize,                  /* xSectorSize */
200a0fc7296Sdanielk1977   jtDeviceCharacteristics        /* xDeviceCharacteristics */
201a0fc7296Sdanielk1977 };
202a0fc7296Sdanielk1977 
203a0fc7296Sdanielk1977 struct JtGlobal {
204e86a5b77Sdanielk1977   sqlite3_vfs *pVfs;             /* Parent VFS */
205e86a5b77Sdanielk1977   jt_file *pList;                /* List of all open files */
206a0fc7296Sdanielk1977 };
207a0fc7296Sdanielk1977 static struct JtGlobal g = {0, 0};
208a0fc7296Sdanielk1977 
2091a321c32Sdanielk1977 /*
2101a321c32Sdanielk1977 ** Functions to obtain and relinquish a mutex to protect g.pList. The
2111a321c32Sdanielk1977 ** STATIC_PRNG mutex is reused, purely for the sake of convenience.
2121a321c32Sdanielk1977 */
enterJtMutex(void)2131a321c32Sdanielk1977 static void enterJtMutex(void){
2141a321c32Sdanielk1977   sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
2151a321c32Sdanielk1977 }
leaveJtMutex(void)2161a321c32Sdanielk1977 static void leaveJtMutex(void){
2171a321c32Sdanielk1977   sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
2181a321c32Sdanielk1977 }
2191a321c32Sdanielk1977 
220f7f33fb0Sdanielk1977 extern int sqlite3_io_error_pending;
2218ca82553Sdanielk1977 extern int sqlite3_io_error_hit;
stop_ioerr_simulation(int * piSave,int * piSave2)2228ca82553Sdanielk1977 static void stop_ioerr_simulation(int *piSave, int *piSave2){
223f7f33fb0Sdanielk1977   *piSave = sqlite3_io_error_pending;
2248ca82553Sdanielk1977   *piSave2 = sqlite3_io_error_hit;
225f7f33fb0Sdanielk1977   sqlite3_io_error_pending = -1;
2268ca82553Sdanielk1977   sqlite3_io_error_hit = 0;
227f7f33fb0Sdanielk1977 }
start_ioerr_simulation(int iSave,int iSave2)2288ca82553Sdanielk1977 static void start_ioerr_simulation(int iSave, int iSave2){
229f7f33fb0Sdanielk1977   sqlite3_io_error_pending = iSave;
2308ca82553Sdanielk1977   sqlite3_io_error_hit = iSave2;
231f7f33fb0Sdanielk1977 }
232f7f33fb0Sdanielk1977 
233e86a5b77Sdanielk1977 /*
234e86a5b77Sdanielk1977 ** The jt_file pointed to by the argument may or may not be a file-handle
235e86a5b77Sdanielk1977 ** open on a main database file. If it is, and a transaction is currently
236e86a5b77Sdanielk1977 ** opened on the file, then discard all transaction related data.
237e86a5b77Sdanielk1977 */
closeTransaction(jt_file * p)238f3107512Sdanielk1977 static void closeTransaction(jt_file *p){
239f3107512Sdanielk1977   sqlite3BitvecDestroy(p->pWritable);
240cd1cbff3Sdanielk1977   sqlite3_free(p->aCksum);
241f3107512Sdanielk1977   p->pWritable = 0;
242cd1cbff3Sdanielk1977   p->aCksum = 0;
243e86a5b77Sdanielk1977   p->nSync = 0;
244f3107512Sdanielk1977 }
245f3107512Sdanielk1977 
246a0fc7296Sdanielk1977 /*
247a0fc7296Sdanielk1977 ** Close an jt-file.
248a0fc7296Sdanielk1977 */
jtClose(sqlite3_file * pFile)249a0fc7296Sdanielk1977 static int jtClose(sqlite3_file *pFile){
250a0fc7296Sdanielk1977   jt_file **pp;
251a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
252a0fc7296Sdanielk1977 
253401b65edSdanielk1977   closeTransaction(p);
2541a321c32Sdanielk1977   enterJtMutex();
255a0fc7296Sdanielk1977   if( p->zName ){
256f3107512Sdanielk1977     for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext);
257a0fc7296Sdanielk1977     *pp = p->pNext;
258a0fc7296Sdanielk1977   }
2591a321c32Sdanielk1977   leaveJtMutex();
2608f2ce914Sdrh   sqlite3OsClose(p->pReal);
2618f2ce914Sdrh   return SQLITE_OK;
262a0fc7296Sdanielk1977 }
263a0fc7296Sdanielk1977 
264a0fc7296Sdanielk1977 /*
265a0fc7296Sdanielk1977 ** Read data from an jt-file.
266a0fc7296Sdanielk1977 */
jtRead(sqlite3_file * pFile,void * zBuf,int iAmt,sqlite_int64 iOfst)267a0fc7296Sdanielk1977 static int jtRead(
268a0fc7296Sdanielk1977   sqlite3_file *pFile,
269a0fc7296Sdanielk1977   void *zBuf,
270a0fc7296Sdanielk1977   int iAmt,
271a0fc7296Sdanielk1977   sqlite_int64 iOfst
272a0fc7296Sdanielk1977 ){
273a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
274a0fc7296Sdanielk1977   return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
275a0fc7296Sdanielk1977 }
276a0fc7296Sdanielk1977 
277e86a5b77Sdanielk1977 /*
278e86a5b77Sdanielk1977 ** Parameter zJournal is the name of a journal file that is currently
279e86a5b77Sdanielk1977 ** open. This function locates and returns the handle opened on the
280e86a5b77Sdanielk1977 ** corresponding database file by the pager that currently has the
281e86a5b77Sdanielk1977 ** journal file opened. This file-handle is identified by the
282e86a5b77Sdanielk1977 ** following properties:
283e86a5b77Sdanielk1977 **
284e86a5b77Sdanielk1977 **   a) SQLITE_OPEN_MAIN_DB was specified when the file was opened.
285e86a5b77Sdanielk1977 **
286e86a5b77Sdanielk1977 **   b) The file-name specified when the file was opened matches
287e86a5b77Sdanielk1977 **      all but the final 8 characters of the journal file name.
288e86a5b77Sdanielk1977 **
28905accd22Sdan **   c) There is currently a reserved lock on the file. This
29005accd22Sdan **      condition is waived if the noLock argument is non-zero.
291e86a5b77Sdanielk1977 **/
locateDatabaseHandle(const char * zJournal,int noLock)29205accd22Sdan static jt_file *locateDatabaseHandle(const char *zJournal, int noLock){
293e86a5b77Sdanielk1977   jt_file *pMain = 0;
2941a321c32Sdanielk1977   enterJtMutex();
295a0fc7296Sdanielk1977   for(pMain=g.pList; pMain; pMain=pMain->pNext){
29683cc1392Sdrh     int nName = (int)(strlen(zJournal) - strlen("-journal"));
297a0fc7296Sdanielk1977     if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
29883cc1392Sdrh      && ((int)strlen(pMain->zName)==nName)
299a0fc7296Sdanielk1977      && 0==memcmp(pMain->zName, zJournal, nName)
30005accd22Sdan      && ((pMain->eLock>=SQLITE_LOCK_RESERVED) || noLock)
301a0fc7296Sdanielk1977     ){
302a0fc7296Sdanielk1977       break;
303a0fc7296Sdanielk1977     }
304a0fc7296Sdanielk1977   }
3051a321c32Sdanielk1977   leaveJtMutex();
306a0fc7296Sdanielk1977   return pMain;
307a0fc7296Sdanielk1977 }
308a0fc7296Sdanielk1977 
309e86a5b77Sdanielk1977 /*
310e86a5b77Sdanielk1977 ** Parameter z points to a buffer of 4 bytes in size containing a
311e86a5b77Sdanielk1977 ** unsigned 32-bit integer stored in big-endian format. Decode the
312e86a5b77Sdanielk1977 ** integer and return its value.
313e86a5b77Sdanielk1977 */
decodeUint32(const unsigned char * z)314a0fc7296Sdanielk1977 static u32 decodeUint32(const unsigned char *z){
315a0fc7296Sdanielk1977   return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
316a0fc7296Sdanielk1977 }
317a0fc7296Sdanielk1977 
318e86a5b77Sdanielk1977 /*
319e86a5b77Sdanielk1977 ** Calculate a checksum from the buffer of length n bytes pointed to
320e86a5b77Sdanielk1977 ** by parameter z.
321e86a5b77Sdanielk1977 */
genCksum(const unsigned char * z,int n)322e86a5b77Sdanielk1977 static u32 genCksum(const unsigned char *z, int n){
323e86a5b77Sdanielk1977   int i;
324cd1cbff3Sdanielk1977   u32 cksum = 0;
325e86a5b77Sdanielk1977   for(i=0; i<n; i++){
326e86a5b77Sdanielk1977     cksum = cksum + z[i] + (cksum<<3);
327cd1cbff3Sdanielk1977   }
328e86a5b77Sdanielk1977   return cksum;
329a0fc7296Sdanielk1977 }
330a0fc7296Sdanielk1977 
331a0fc7296Sdanielk1977 /*
332a0fc7296Sdanielk1977 ** The first argument, zBuf, points to a buffer containing a 28 byte
333a0fc7296Sdanielk1977 ** serialized journal header. This function deserializes four of the
334a0fc7296Sdanielk1977 ** integer fields contained in the journal header and writes their
335a0fc7296Sdanielk1977 ** values to the output variables.
336e86a5b77Sdanielk1977 **
337e86a5b77Sdanielk1977 ** SQLITE_OK is returned if the journal-header is successfully
338e86a5b77Sdanielk1977 ** decoded. Otherwise, SQLITE_ERROR.
339a0fc7296Sdanielk1977 */
decodeJournalHdr(const unsigned char * zBuf,u32 * pnRec,u32 * pnPage,u32 * pnSector,u32 * pnPagesize)340a0fc7296Sdanielk1977 static int decodeJournalHdr(
341a0fc7296Sdanielk1977   const unsigned char *zBuf,         /* Input: 28 byte journal header */
342a0fc7296Sdanielk1977   u32 *pnRec,                        /* Out: Number of journalled records */
343a0fc7296Sdanielk1977   u32 *pnPage,                       /* Out: Original database page count */
344a0fc7296Sdanielk1977   u32 *pnSector,                     /* Out: Sector size in bytes */
345a0fc7296Sdanielk1977   u32 *pnPagesize                    /* Out: Page size in bytes */
346a0fc7296Sdanielk1977 ){
347a0fc7296Sdanielk1977   unsigned char aMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7 };
348e86a5b77Sdanielk1977   if( memcmp(aMagic, zBuf, 8) ) return SQLITE_ERROR;
349a0fc7296Sdanielk1977   if( pnRec ) *pnRec = decodeUint32(&zBuf[8]);
350a0fc7296Sdanielk1977   if( pnPage ) *pnPage = decodeUint32(&zBuf[16]);
351a0fc7296Sdanielk1977   if( pnSector ) *pnSector = decodeUint32(&zBuf[20]);
352a0fc7296Sdanielk1977   if( pnPagesize ) *pnPagesize = decodeUint32(&zBuf[24]);
353e86a5b77Sdanielk1977   return SQLITE_OK;
354e86a5b77Sdanielk1977 }
355e86a5b77Sdanielk1977 
356e86a5b77Sdanielk1977 /*
357e86a5b77Sdanielk1977 ** This function is called when a new transaction is opened, just after
358e86a5b77Sdanielk1977 ** the first journal-header is written to the journal file.
359e86a5b77Sdanielk1977 */
openTransaction(jt_file * pMain,jt_file * pJournal)360e86a5b77Sdanielk1977 static int openTransaction(jt_file *pMain, jt_file *pJournal){
361e86a5b77Sdanielk1977   unsigned char *aData;
362e86a5b77Sdanielk1977   sqlite3_file *p = pMain->pReal;
363e86a5b77Sdanielk1977   int rc = SQLITE_OK;
364e86a5b77Sdanielk1977 
3655198beadSdan   closeTransaction(pMain);
366e86a5b77Sdanielk1977   aData = sqlite3_malloc(pMain->nPagesize);
367e86a5b77Sdanielk1977   pMain->pWritable = sqlite3BitvecCreate(pMain->nPage);
368e86a5b77Sdanielk1977   pMain->aCksum = sqlite3_malloc(sizeof(u32) * (pMain->nPage + 1));
369e86a5b77Sdanielk1977   pJournal->iMaxOff = 0;
370e86a5b77Sdanielk1977 
371e86a5b77Sdanielk1977   if( !pMain->pWritable || !pMain->aCksum || !aData ){
372e86a5b77Sdanielk1977     rc = SQLITE_IOERR_NOMEM;
373e86a5b77Sdanielk1977   }else if( pMain->nPage>0 ){
374e86a5b77Sdanielk1977     u32 iTrunk;
375f7f33fb0Sdanielk1977     int iSave;
3768ca82553Sdanielk1977     int iSave2;
377f7f33fb0Sdanielk1977 
3788ca82553Sdanielk1977     stop_ioerr_simulation(&iSave, &iSave2);
379e86a5b77Sdanielk1977 
380e86a5b77Sdanielk1977     /* Read the database free-list. Add the page-number for each free-list
381e86a5b77Sdanielk1977     ** leaf to the jt_file.pWritable bitvec.
382e86a5b77Sdanielk1977     */
383e86a5b77Sdanielk1977     rc = sqlite3OsRead(p, aData, pMain->nPagesize, 0);
384dc110614Sdan     if( rc==SQLITE_OK ){
385dc110614Sdan       u32 nDbsize = decodeUint32(&aData[28]);
386dc110614Sdan       if( nDbsize>0 && memcmp(&aData[24], &aData[92], 4)==0 ){
387dc110614Sdan         u32 iPg;
388dc110614Sdan         for(iPg=nDbsize+1; iPg<=pMain->nPage; iPg++){
389dc110614Sdan           sqlite3BitvecSet(pMain->pWritable, iPg);
390dc110614Sdan         }
391dc110614Sdan       }
392dc110614Sdan     }
393e86a5b77Sdanielk1977     iTrunk = decodeUint32(&aData[32]);
394e86a5b77Sdanielk1977     while( rc==SQLITE_OK && iTrunk>0 ){
395e86a5b77Sdanielk1977       u32 nLeaf;
396e86a5b77Sdanielk1977       u32 iLeaf;
3977c3210e6Sdan       sqlite3_int64 iOff = (i64)(iTrunk-1)*pMain->nPagesize;
398e86a5b77Sdanielk1977       rc = sqlite3OsRead(p, aData, pMain->nPagesize, iOff);
399e86a5b77Sdanielk1977       nLeaf = decodeUint32(&aData[4]);
400e86a5b77Sdanielk1977       for(iLeaf=0; rc==SQLITE_OK && iLeaf<nLeaf; iLeaf++){
401e86a5b77Sdanielk1977         u32 pgno = decodeUint32(&aData[8+4*iLeaf]);
402e86a5b77Sdanielk1977         sqlite3BitvecSet(pMain->pWritable, pgno);
403e86a5b77Sdanielk1977       }
404e86a5b77Sdanielk1977       iTrunk = decodeUint32(aData);
405e86a5b77Sdanielk1977     }
406e86a5b77Sdanielk1977 
407e86a5b77Sdanielk1977     /* Calculate and store a checksum for each page in the database file. */
408e86a5b77Sdanielk1977     if( rc==SQLITE_OK ){
409e86a5b77Sdanielk1977       int ii;
4107da5fcb0Sdrh       for(ii=0; rc==SQLITE_OK && ii<(int)pMain->nPage; ii++){
411e86a5b77Sdanielk1977         i64 iOff = (i64)(pMain->nPagesize) * (i64)ii;
4124faa5f41Sdanielk1977         if( iOff==PENDING_BYTE ) continue;
413e86a5b77Sdanielk1977         rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
414e86a5b77Sdanielk1977         pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize);
41527b2f053Smistachkin         if( ii+1==(int)pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ){
41627b2f053Smistachkin           rc = SQLITE_OK;
41727b2f053Smistachkin         }
418e86a5b77Sdanielk1977       }
419e86a5b77Sdanielk1977     }
420f7f33fb0Sdanielk1977 
4218ca82553Sdanielk1977     start_ioerr_simulation(iSave, iSave2);
422e86a5b77Sdanielk1977   }
423e86a5b77Sdanielk1977 
424e86a5b77Sdanielk1977   sqlite3_free(aData);
425e86a5b77Sdanielk1977   return rc;
426a0fc7296Sdanielk1977 }
427a0fc7296Sdanielk1977 
428a0fc7296Sdanielk1977 /*
429a0fc7296Sdanielk1977 ** The first argument to this function is a handle open on a journal file.
430a0fc7296Sdanielk1977 ** This function reads the journal file and adds the page number for each
431a0fc7296Sdanielk1977 ** page in the journal to the Bitvec object passed as the second argument.
432a0fc7296Sdanielk1977 */
readJournalFile(jt_file * p,jt_file * pMain)433f3107512Sdanielk1977 static int readJournalFile(jt_file *p, jt_file *pMain){
434cd1cbff3Sdanielk1977   int rc = SQLITE_OK;
435a0fc7296Sdanielk1977   unsigned char zBuf[28];
436a0fc7296Sdanielk1977   sqlite3_file *pReal = p->pReal;
437a0fc7296Sdanielk1977   sqlite3_int64 iOff = 0;
438e86a5b77Sdanielk1977   sqlite3_int64 iSize = p->iMaxOff;
439cd1cbff3Sdanielk1977   unsigned char *aPage;
440f7f33fb0Sdanielk1977   int iSave;
4418ca82553Sdanielk1977   int iSave2;
442a0fc7296Sdanielk1977 
443cd1cbff3Sdanielk1977   aPage = sqlite3_malloc(pMain->nPagesize);
444cd1cbff3Sdanielk1977   if( !aPage ){
445cd1cbff3Sdanielk1977     return SQLITE_IOERR_NOMEM;
446cd1cbff3Sdanielk1977   }
447e86a5b77Sdanielk1977 
4488ca82553Sdanielk1977   stop_ioerr_simulation(&iSave, &iSave2);
449f7f33fb0Sdanielk1977 
450f3107512Sdanielk1977   while( rc==SQLITE_OK && iOff<iSize ){
451a0fc7296Sdanielk1977     u32 nRec, nPage, nSector, nPagesize;
452a0fc7296Sdanielk1977     u32 ii;
453e86a5b77Sdanielk1977 
454e86a5b77Sdanielk1977     /* Read and decode the next journal-header from the journal file. */
455f3107512Sdanielk1977     rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
456f3107512Sdanielk1977     if( rc!=SQLITE_OK
457f3107512Sdanielk1977      || decodeJournalHdr(zBuf, &nRec, &nPage, &nSector, &nPagesize)
458f3107512Sdanielk1977     ){
4590d519ca8Sdanielk1977       goto finish_rjf;
460a0fc7296Sdanielk1977     }
461a0fc7296Sdanielk1977     iOff += nSector;
462e86a5b77Sdanielk1977 
463f3107512Sdanielk1977     if( nRec==0 ){
4640d519ca8Sdanielk1977       /* A trick. There might be another journal-header immediately
4650d519ca8Sdanielk1977       ** following this one. In this case, 0 records means 0 records,
4660d519ca8Sdanielk1977       ** not "read until the end of the file". See also ticket #2565.
4670d519ca8Sdanielk1977       */
468cd1cbff3Sdanielk1977       if( iSize>=(iOff+nSector) ){
4690d519ca8Sdanielk1977         rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
4700d519ca8Sdanielk1977         if( rc!=SQLITE_OK || 0==decodeJournalHdr(zBuf, 0, 0, 0, 0) ){
4710d519ca8Sdanielk1977           continue;
4720d519ca8Sdanielk1977         }
4730d519ca8Sdanielk1977       }
4747da5fcb0Sdrh       nRec = (u32)((iSize-iOff) / (pMain->nPagesize+8));
475f3107512Sdanielk1977     }
476e86a5b77Sdanielk1977 
477e86a5b77Sdanielk1977     /* Read all the records that follow the journal-header just read. */
478f3107512Sdanielk1977     for(ii=0; rc==SQLITE_OK && ii<nRec && iOff<iSize; ii++){
479a0fc7296Sdanielk1977       u32 pgno;
480f3107512Sdanielk1977       rc = sqlite3OsRead(pReal, zBuf, 4, iOff);
481f3107512Sdanielk1977       if( rc==SQLITE_OK ){
482a0fc7296Sdanielk1977         pgno = decodeUint32(zBuf);
483a6417482Sdanielk1977         if( pgno>0 && pgno<=pMain->nPage ){
484cd1cbff3Sdanielk1977           if( 0==sqlite3BitvecTest(pMain->pWritable, pgno) ){
485cd1cbff3Sdanielk1977             rc = sqlite3OsRead(pReal, aPage, pMain->nPagesize, iOff+4);
486cd1cbff3Sdanielk1977             if( rc==SQLITE_OK ){
487e86a5b77Sdanielk1977               u32 cksum = genCksum(aPage, pMain->nPagesize);
488cd1cbff3Sdanielk1977               assert( cksum==pMain->aCksum[pgno-1] );
489cd1cbff3Sdanielk1977             }
490cd1cbff3Sdanielk1977           }
491a0fc7296Sdanielk1977           sqlite3BitvecSet(pMain->pWritable, pgno);
492a0fc7296Sdanielk1977         }
493cd1cbff3Sdanielk1977         iOff += (8 + pMain->nPagesize);
494f3107512Sdanielk1977       }
495a6417482Sdanielk1977     }
496a0fc7296Sdanielk1977 
497a0fc7296Sdanielk1977     iOff = ((iOff + (nSector-1)) / nSector) * nSector;
498a0fc7296Sdanielk1977   }
499f3107512Sdanielk1977 
5000d519ca8Sdanielk1977 finish_rjf:
5018ca82553Sdanielk1977   start_ioerr_simulation(iSave, iSave2);
502cd1cbff3Sdanielk1977   sqlite3_free(aPage);
5030d519ca8Sdanielk1977   if( rc==SQLITE_IOERR_SHORT_READ ){
5040d519ca8Sdanielk1977     rc = SQLITE_OK;
5050d519ca8Sdanielk1977   }
506f3107512Sdanielk1977   return rc;
507a0fc7296Sdanielk1977 }
508a0fc7296Sdanielk1977 
5098ca82553Sdanielk1977 /*
5108ca82553Sdanielk1977 ** Write data to an jt-file.
5118ca82553Sdanielk1977 */
jtWrite(sqlite3_file * pFile,const void * zBuf,int iAmt,sqlite_int64 iOfst)5128ca82553Sdanielk1977 static int jtWrite(
5138ca82553Sdanielk1977   sqlite3_file *pFile,
5148ca82553Sdanielk1977   const void *zBuf,
5158ca82553Sdanielk1977   int iAmt,
5168ca82553Sdanielk1977   sqlite_int64 iOfst
5178ca82553Sdanielk1977 ){
5188ca82553Sdanielk1977   int rc;
5198ca82553Sdanielk1977   jt_file *p = (jt_file *)pFile;
5208ca82553Sdanielk1977   if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
5218ca82553Sdanielk1977     if( iOfst==0 ){
52205accd22Sdan       jt_file *pMain = locateDatabaseHandle(p->zName, 0);
5238ca82553Sdanielk1977       assert( pMain );
5248ca82553Sdanielk1977 
5258ca82553Sdanielk1977       if( iAmt==28 ){
5268ca82553Sdanielk1977         /* Zeroing the first journal-file header. This is the end of a
5278ca82553Sdanielk1977         ** transaction. */
5288ca82553Sdanielk1977         closeTransaction(pMain);
5298ca82553Sdanielk1977       }else if( iAmt!=12 ){
5308ca82553Sdanielk1977         /* Writing the first journal header to a journal file. This happens
5318ca82553Sdanielk1977         ** when a transaction is first started.  */
5328ca82553Sdanielk1977         u8 *z = (u8 *)zBuf;
5338ca82553Sdanielk1977         pMain->nPage = decodeUint32(&z[16]);
5348ca82553Sdanielk1977         pMain->nPagesize = decodeUint32(&z[24]);
5358ca82553Sdanielk1977         if( SQLITE_OK!=(rc=openTransaction(pMain, p)) ){
5368ca82553Sdanielk1977           return rc;
5378ca82553Sdanielk1977         }
5388ca82553Sdanielk1977       }
5398ca82553Sdanielk1977     }
5408ca82553Sdanielk1977     if( p->iMaxOff<(iOfst + iAmt) ){
5418ca82553Sdanielk1977       p->iMaxOff = iOfst + iAmt;
5428ca82553Sdanielk1977     }
5438ca82553Sdanielk1977   }
5448ca82553Sdanielk1977 
5458ca82553Sdanielk1977   if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
5467da5fcb0Sdrh     if( iAmt<(int)p->nPagesize
5478ca82553Sdanielk1977      && p->nPagesize%iAmt==0
5488ca82553Sdanielk1977      && iOfst>=(PENDING_BYTE+512)
5498ca82553Sdanielk1977      && iOfst+iAmt<=PENDING_BYTE+p->nPagesize
5508ca82553Sdanielk1977     ){
5518ca82553Sdanielk1977       /* No-op. This special case is hit when the backup code is copying a
5528ca82553Sdanielk1977       ** to a database with a larger page-size than the source database and
5538ca82553Sdanielk1977       ** it needs to fill in the non-locking-region part of the original
5548ca82553Sdanielk1977       ** pending-byte page.
5558ca82553Sdanielk1977       */
5568ca82553Sdanielk1977     }else{
5577da5fcb0Sdrh       u32 pgno = (u32)(iOfst/p->nPagesize + 1);
558189143d3Smistachkin       assert( (iAmt==1||iAmt==(int)p->nPagesize) &&
559189143d3Smistachkin               ((iOfst+iAmt)%p->nPagesize)==0 );
5605b3a3b35Sdan       /* The following assert() statements may fail if this layer is used
5615b3a3b35Sdan       ** with a connection in "PRAGMA synchronous=off" mode. If they
5625b3a3b35Sdan       ** fail with sync=normal or sync=full, this may indicate problem.  */
563*922b3580Sdan       assert( p->nPage==0 || pgno<=p->nPage || p->nSync>0 );
5648ca82553Sdanielk1977       assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
5658ca82553Sdanielk1977     }
5668ca82553Sdanielk1977   }
5678ca82553Sdanielk1977 
5688ca82553Sdanielk1977   rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
5698ca82553Sdanielk1977   if( (p->flags&SQLITE_OPEN_MAIN_JOURNAL) && iAmt==12 ){
57005accd22Sdan     jt_file *pMain = locateDatabaseHandle(p->zName, 0);
5718ca82553Sdanielk1977     int rc2 = readJournalFile(p, pMain);
5728ca82553Sdanielk1977     if( rc==SQLITE_OK ) rc = rc2;
5738ca82553Sdanielk1977   }
5748ca82553Sdanielk1977   return rc;
5758ca82553Sdanielk1977 }
5768ca82553Sdanielk1977 
5778ca82553Sdanielk1977 /*
5788ca82553Sdanielk1977 ** Truncate an jt-file.
5798ca82553Sdanielk1977 */
jtTruncate(sqlite3_file * pFile,sqlite_int64 size)5808ca82553Sdanielk1977 static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){
5818ca82553Sdanielk1977   jt_file *p = (jt_file *)pFile;
5828ca82553Sdanielk1977   if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){
5838ca82553Sdanielk1977     /* Truncating a journal file. This is the end of a transaction. */
58405accd22Sdan     jt_file *pMain = locateDatabaseHandle(p->zName, 0);
5858ca82553Sdanielk1977     closeTransaction(pMain);
5868ca82553Sdanielk1977   }
5878ca82553Sdanielk1977   if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
5888ca82553Sdanielk1977     u32 pgno;
5898ca82553Sdanielk1977     u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1);
5907da5fcb0Sdrh     for(pgno=(u32)(size/p->nPagesize+1); pgno<=p->nPage; pgno++){
5918ca82553Sdanielk1977       assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) );
5928ca82553Sdanielk1977     }
5938ca82553Sdanielk1977   }
5948ca82553Sdanielk1977   return sqlite3OsTruncate(p->pReal, size);
5958ca82553Sdanielk1977 }
5968ca82553Sdanielk1977 
597a0fc7296Sdanielk1977 /*
598a0fc7296Sdanielk1977 ** Sync an jt-file.
599a0fc7296Sdanielk1977 */
jtSync(sqlite3_file * pFile,int flags)600a0fc7296Sdanielk1977 static int jtSync(sqlite3_file *pFile, int flags){
601a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
602a0fc7296Sdanielk1977 
603a0fc7296Sdanielk1977   if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
604f3107512Sdanielk1977     int rc;
605a0fc7296Sdanielk1977     jt_file *pMain;                   /* The associated database file */
606a0fc7296Sdanielk1977 
607a0fc7296Sdanielk1977     /* The journal file is being synced. At this point, we inspect the
608a0fc7296Sdanielk1977     ** contents of the file up to this point and set each bit in the
609a0fc7296Sdanielk1977     ** jt_file.pWritable bitvec of the main database file associated with
610a0fc7296Sdanielk1977     ** this journal file.
611a0fc7296Sdanielk1977     */
61205accd22Sdan     pMain = locateDatabaseHandle(p->zName, 0);
613a0fc7296Sdanielk1977 
614a0fc7296Sdanielk1977     /* Set the bitvec values */
61505accd22Sdan     if( pMain && pMain->pWritable ){
616e86a5b77Sdanielk1977       pMain->nSync++;
617f3107512Sdanielk1977       rc = readJournalFile(p, pMain);
618f3107512Sdanielk1977       if( rc!=SQLITE_OK ){
619f3107512Sdanielk1977         return rc;
620f3107512Sdanielk1977       }
621f3107512Sdanielk1977     }
622a0fc7296Sdanielk1977   }
623a0fc7296Sdanielk1977 
624a0fc7296Sdanielk1977   return sqlite3OsSync(p->pReal, flags);
625a0fc7296Sdanielk1977 }
626a0fc7296Sdanielk1977 
627a0fc7296Sdanielk1977 /*
628a0fc7296Sdanielk1977 ** Return the current file-size of an jt-file.
629a0fc7296Sdanielk1977 */
jtFileSize(sqlite3_file * pFile,sqlite_int64 * pSize)630a0fc7296Sdanielk1977 static int jtFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
631a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
632a0fc7296Sdanielk1977   return sqlite3OsFileSize(p->pReal, pSize);
633a0fc7296Sdanielk1977 }
634a0fc7296Sdanielk1977 
635a0fc7296Sdanielk1977 /*
636a0fc7296Sdanielk1977 ** Lock an jt-file.
637a0fc7296Sdanielk1977 */
jtLock(sqlite3_file * pFile,int eLock)638a0fc7296Sdanielk1977 static int jtLock(sqlite3_file *pFile, int eLock){
639a0fc7296Sdanielk1977   int rc;
640a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
641a0fc7296Sdanielk1977   rc = sqlite3OsLock(p->pReal, eLock);
642a0fc7296Sdanielk1977   if( rc==SQLITE_OK && eLock>p->eLock ){
643a0fc7296Sdanielk1977     p->eLock = eLock;
644a0fc7296Sdanielk1977   }
645a0fc7296Sdanielk1977   return rc;
646a0fc7296Sdanielk1977 }
647a0fc7296Sdanielk1977 
648a0fc7296Sdanielk1977 /*
649a0fc7296Sdanielk1977 ** Unlock an jt-file.
650a0fc7296Sdanielk1977 */
jtUnlock(sqlite3_file * pFile,int eLock)651a0fc7296Sdanielk1977 static int jtUnlock(sqlite3_file *pFile, int eLock){
652a0fc7296Sdanielk1977   int rc;
653a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
654a0fc7296Sdanielk1977   rc = sqlite3OsUnlock(p->pReal, eLock);
655a0fc7296Sdanielk1977   if( rc==SQLITE_OK && eLock<p->eLock ){
656a0fc7296Sdanielk1977     p->eLock = eLock;
657a0fc7296Sdanielk1977   }
658a0fc7296Sdanielk1977   return rc;
659a0fc7296Sdanielk1977 }
660a0fc7296Sdanielk1977 
661a0fc7296Sdanielk1977 /*
662a0fc7296Sdanielk1977 ** Check if another file-handle holds a RESERVED lock on an jt-file.
663a0fc7296Sdanielk1977 */
jtCheckReservedLock(sqlite3_file * pFile,int * pResOut)664a0fc7296Sdanielk1977 static int jtCheckReservedLock(sqlite3_file *pFile, int *pResOut){
665a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
666a0fc7296Sdanielk1977   return sqlite3OsCheckReservedLock(p->pReal, pResOut);
667a0fc7296Sdanielk1977 }
668a0fc7296Sdanielk1977 
669a0fc7296Sdanielk1977 /*
670a0fc7296Sdanielk1977 ** File control method. For custom operations on an jt-file.
671a0fc7296Sdanielk1977 */
jtFileControl(sqlite3_file * pFile,int op,void * pArg)672a0fc7296Sdanielk1977 static int jtFileControl(sqlite3_file *pFile, int op, void *pArg){
673a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
6749d69c5d1Sdan   return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
675a0fc7296Sdanielk1977 }
676a0fc7296Sdanielk1977 
677a0fc7296Sdanielk1977 /*
678a0fc7296Sdanielk1977 ** Return the sector-size in bytes for an jt-file.
679a0fc7296Sdanielk1977 */
jtSectorSize(sqlite3_file * pFile)680a0fc7296Sdanielk1977 static int jtSectorSize(sqlite3_file *pFile){
681a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
682a0fc7296Sdanielk1977   return sqlite3OsSectorSize(p->pReal);
683a0fc7296Sdanielk1977 }
684a0fc7296Sdanielk1977 
685a0fc7296Sdanielk1977 /*
686a0fc7296Sdanielk1977 ** Return the device characteristic flags supported by an jt-file.
687a0fc7296Sdanielk1977 */
jtDeviceCharacteristics(sqlite3_file * pFile)688a0fc7296Sdanielk1977 static int jtDeviceCharacteristics(sqlite3_file *pFile){
689a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
690a0fc7296Sdanielk1977   return sqlite3OsDeviceCharacteristics(p->pReal);
691a0fc7296Sdanielk1977 }
692a0fc7296Sdanielk1977 
693a0fc7296Sdanielk1977 /*
694a0fc7296Sdanielk1977 ** Open an jt file handle.
695a0fc7296Sdanielk1977 */
jtOpen(sqlite3_vfs * pVfs,const char * zName,sqlite3_file * pFile,int flags,int * pOutFlags)696a0fc7296Sdanielk1977 static int jtOpen(
697a0fc7296Sdanielk1977   sqlite3_vfs *pVfs,
698a0fc7296Sdanielk1977   const char *zName,
699a0fc7296Sdanielk1977   sqlite3_file *pFile,
700a0fc7296Sdanielk1977   int flags,
701a0fc7296Sdanielk1977   int *pOutFlags
702a0fc7296Sdanielk1977 ){
703a0fc7296Sdanielk1977   int rc;
704a0fc7296Sdanielk1977   jt_file *p = (jt_file *)pFile;
7055a2cc667Sdanielk1977   pFile->pMethods = 0;
706a0fc7296Sdanielk1977   p->pReal = (sqlite3_file *)&p[1];
707a6417482Sdanielk1977   p->pReal->pMethods = 0;
708a0fc7296Sdanielk1977   rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
709a6417482Sdanielk1977   assert( rc==SQLITE_OK || p->pReal->pMethods==0 );
710a6417482Sdanielk1977   if( rc==SQLITE_OK ){
711a0fc7296Sdanielk1977     pFile->pMethods = &jt_io_methods;
712a0fc7296Sdanielk1977     p->eLock = 0;
713a0fc7296Sdanielk1977     p->zName = zName;
714a0fc7296Sdanielk1977     p->flags = flags;
715a0fc7296Sdanielk1977     p->pNext = 0;
716f3107512Sdanielk1977     p->pWritable = 0;
717cd1cbff3Sdanielk1977     p->aCksum = 0;
7181a321c32Sdanielk1977     enterJtMutex();
719a0fc7296Sdanielk1977     if( zName ){
720a0fc7296Sdanielk1977       p->pNext = g.pList;
721a0fc7296Sdanielk1977       g.pList = p;
722a0fc7296Sdanielk1977     }
7231a321c32Sdanielk1977     leaveJtMutex();
724a0fc7296Sdanielk1977   }
725a0fc7296Sdanielk1977   return rc;
726a0fc7296Sdanielk1977 }
727a0fc7296Sdanielk1977 
728a0fc7296Sdanielk1977 /*
729a0fc7296Sdanielk1977 ** Delete the file located at zPath. If the dirSync argument is true,
730a0fc7296Sdanielk1977 ** ensure the file-system modifications are synced to disk before
731a0fc7296Sdanielk1977 ** returning.
732a0fc7296Sdanielk1977 */
jtDelete(sqlite3_vfs * pVfs,const char * zPath,int dirSync)733a0fc7296Sdanielk1977 static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
73483cc1392Sdrh   int nPath = (int)strlen(zPath);
735a0fc7296Sdanielk1977   if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){
736a0fc7296Sdanielk1977     /* Deleting a journal file. The end of a transaction. */
73705accd22Sdan     jt_file *pMain = locateDatabaseHandle(zPath, 0);
738f3107512Sdanielk1977     if( pMain ){
739f3107512Sdanielk1977       closeTransaction(pMain);
740f3107512Sdanielk1977     }
741a0fc7296Sdanielk1977   }
742a0fc7296Sdanielk1977 
743a0fc7296Sdanielk1977   return sqlite3OsDelete(g.pVfs, zPath, dirSync);
744a0fc7296Sdanielk1977 }
745a0fc7296Sdanielk1977 
746a0fc7296Sdanielk1977 /*
747a0fc7296Sdanielk1977 ** Test for access permissions. Return true if the requested permission
748a0fc7296Sdanielk1977 ** is available, or false otherwise.
749a0fc7296Sdanielk1977 */
jtAccess(sqlite3_vfs * pVfs,const char * zPath,int flags,int * pResOut)750a0fc7296Sdanielk1977 static int jtAccess(
751a0fc7296Sdanielk1977   sqlite3_vfs *pVfs,
752a0fc7296Sdanielk1977   const char *zPath,
753a0fc7296Sdanielk1977   int flags,
754a0fc7296Sdanielk1977   int *pResOut
755a0fc7296Sdanielk1977 ){
756a0fc7296Sdanielk1977   return sqlite3OsAccess(g.pVfs, zPath, flags, pResOut);
757a0fc7296Sdanielk1977 }
758a0fc7296Sdanielk1977 
759a0fc7296Sdanielk1977 /*
760a0fc7296Sdanielk1977 ** Populate buffer zOut with the full canonical pathname corresponding
761a0fc7296Sdanielk1977 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
762a0fc7296Sdanielk1977 ** of at least (JT_MAX_PATHNAME+1) bytes.
763a0fc7296Sdanielk1977 */
jtFullPathname(sqlite3_vfs * pVfs,const char * zPath,int nOut,char * zOut)764a0fc7296Sdanielk1977 static int jtFullPathname(
765a0fc7296Sdanielk1977   sqlite3_vfs *pVfs,
766a0fc7296Sdanielk1977   const char *zPath,
767a0fc7296Sdanielk1977   int nOut,
768a0fc7296Sdanielk1977   char *zOut
769a0fc7296Sdanielk1977 ){
770a0fc7296Sdanielk1977   return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
771a0fc7296Sdanielk1977 }
772a0fc7296Sdanielk1977 
773a0fc7296Sdanielk1977 /*
774a0fc7296Sdanielk1977 ** Open the dynamic library located at zPath and return a handle.
775a0fc7296Sdanielk1977 */
jtDlOpen(sqlite3_vfs * pVfs,const char * zPath)776a0fc7296Sdanielk1977 static void *jtDlOpen(sqlite3_vfs *pVfs, const char *zPath){
777e86a5b77Sdanielk1977   return g.pVfs->xDlOpen(g.pVfs, zPath);
778a0fc7296Sdanielk1977 }
779a0fc7296Sdanielk1977 
780a0fc7296Sdanielk1977 /*
781a0fc7296Sdanielk1977 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
782a0fc7296Sdanielk1977 ** utf-8 string describing the most recent error encountered associated
783a0fc7296Sdanielk1977 ** with dynamic libraries.
784a0fc7296Sdanielk1977 */
jtDlError(sqlite3_vfs * pVfs,int nByte,char * zErrMsg)785a0fc7296Sdanielk1977 static void jtDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
786e86a5b77Sdanielk1977   g.pVfs->xDlError(g.pVfs, nByte, zErrMsg);
787a0fc7296Sdanielk1977 }
788a0fc7296Sdanielk1977 
789a0fc7296Sdanielk1977 /*
790a0fc7296Sdanielk1977 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
791a0fc7296Sdanielk1977 */
jtDlSym(sqlite3_vfs * pVfs,void * p,const char * zSym)792a0fc7296Sdanielk1977 static void (*jtDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
793e86a5b77Sdanielk1977   return g.pVfs->xDlSym(g.pVfs, p, zSym);
794a0fc7296Sdanielk1977 }
795a0fc7296Sdanielk1977 
796a0fc7296Sdanielk1977 /*
797a0fc7296Sdanielk1977 ** Close the dynamic library handle pHandle.
798a0fc7296Sdanielk1977 */
jtDlClose(sqlite3_vfs * pVfs,void * pHandle)799a0fc7296Sdanielk1977 static void jtDlClose(sqlite3_vfs *pVfs, void *pHandle){
800e86a5b77Sdanielk1977   g.pVfs->xDlClose(g.pVfs, pHandle);
801a0fc7296Sdanielk1977 }
802a0fc7296Sdanielk1977 
803a0fc7296Sdanielk1977 /*
804a0fc7296Sdanielk1977 ** Populate the buffer pointed to by zBufOut with nByte bytes of
805a0fc7296Sdanielk1977 ** random data.
806a0fc7296Sdanielk1977 */
jtRandomness(sqlite3_vfs * pVfs,int nByte,char * zBufOut)807a0fc7296Sdanielk1977 static int jtRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
808a0fc7296Sdanielk1977   return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
809a0fc7296Sdanielk1977 }
810a0fc7296Sdanielk1977 
811a0fc7296Sdanielk1977 /*
812a0fc7296Sdanielk1977 ** Sleep for nMicro microseconds. Return the number of microseconds
813a0fc7296Sdanielk1977 ** actually slept.
814a0fc7296Sdanielk1977 */
jtSleep(sqlite3_vfs * pVfs,int nMicro)815a0fc7296Sdanielk1977 static int jtSleep(sqlite3_vfs *pVfs, int nMicro){
816a0fc7296Sdanielk1977   return sqlite3OsSleep(g.pVfs, nMicro);
817a0fc7296Sdanielk1977 }
818a0fc7296Sdanielk1977 
819a0fc7296Sdanielk1977 /*
820a0fc7296Sdanielk1977 ** Return the current time as a Julian Day number in *pTimeOut.
821a0fc7296Sdanielk1977 */
jtCurrentTime(sqlite3_vfs * pVfs,double * pTimeOut)822a0fc7296Sdanielk1977 static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
823b7e8ea20Sdrh   return g.pVfs->xCurrentTime(g.pVfs, pTimeOut);
824a0fc7296Sdanielk1977 }
825007d6c3aSdrh /*
826007d6c3aSdrh ** Return the current time as a Julian Day number in *pTimeOut.
827007d6c3aSdrh */
jtCurrentTimeInt64(sqlite3_vfs * pVfs,sqlite3_int64 * pTimeOut)828007d6c3aSdrh static int jtCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
829007d6c3aSdrh   return g.pVfs->xCurrentTimeInt64(g.pVfs, pTimeOut);
830007d6c3aSdrh }
831a0fc7296Sdanielk1977 
jtGetLastError(sqlite3_vfs * pVfs,int n,char * z)83205accd22Sdan static int jtGetLastError(sqlite3_vfs *pVfs, int n, char *z){
83305accd22Sdan   return g.pVfs->xGetLastError(g.pVfs, n, z);
83405accd22Sdan }
83505accd22Sdan 
836e86a5b77Sdanielk1977 /**************************************************************************
837e86a5b77Sdanielk1977 ** Start of public API.
838e86a5b77Sdanielk1977 */
839e86a5b77Sdanielk1977 
840e86a5b77Sdanielk1977 /*
841e86a5b77Sdanielk1977 ** Configure the jt VFS as a wrapper around the VFS named by parameter
842e86a5b77Sdanielk1977 ** zWrap. If the isDefault parameter is true, then the jt VFS is installed
843e86a5b77Sdanielk1977 ** as the new default VFS for SQLite connections. If isDefault is not
844e86a5b77Sdanielk1977 ** true, then the jt VFS is installed as non-default. In this case it
845e86a5b77Sdanielk1977 ** is available via its name, "jt".
846e86a5b77Sdanielk1977 */
jt_register(char * zWrap,int isDefault)847a0fc7296Sdanielk1977 int jt_register(char *zWrap, int isDefault){
848a0fc7296Sdanielk1977   g.pVfs = sqlite3_vfs_find(zWrap);
849a0fc7296Sdanielk1977   if( g.pVfs==0 ){
850a0fc7296Sdanielk1977     return SQLITE_ERROR;
851a0fc7296Sdanielk1977   }
852e86a5b77Sdanielk1977   jt_vfs.szOsFile = sizeof(jt_file) + g.pVfs->szOsFile;
853007d6c3aSdrh   if( g.pVfs->iVersion==1 ){
854007d6c3aSdrh     jt_vfs.iVersion = 1;
855007d6c3aSdrh   }else if( g.pVfs->xCurrentTimeInt64==0 ){
856007d6c3aSdrh     jt_vfs.xCurrentTimeInt64 = 0;
857007d6c3aSdrh   }
858a0fc7296Sdanielk1977   sqlite3_vfs_register(&jt_vfs, isDefault);
859a0fc7296Sdanielk1977   return SQLITE_OK;
860a0fc7296Sdanielk1977 }
861a0fc7296Sdanielk1977 
862e86a5b77Sdanielk1977 /*
863e86a5b77Sdanielk1977 ** Uninstall the jt VFS, if it is installed.
864e86a5b77Sdanielk1977 */
jt_unregister(void)86564aca191Sdanielk1977 void jt_unregister(void){
866a0fc7296Sdanielk1977   sqlite3_vfs_unregister(&jt_vfs);
867a0fc7296Sdanielk1977 }
868a0fc7296Sdanielk1977 
869a0fc7296Sdanielk1977 #endif
870