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