xref: /sqlite-3.40.0/src/os_kv.c (revision 7585f49a)
1*7585f49aSdrh /*
2*7585f49aSdrh ** 2022-09-06
3*7585f49aSdrh **
4*7585f49aSdrh ** The author disclaims copyright to this source code.  In place of
5*7585f49aSdrh ** a legal notice, here is a blessing:
6*7585f49aSdrh **
7*7585f49aSdrh **    May you do good and not evil.
8*7585f49aSdrh **    May you find forgiveness for yourself and forgive others.
9*7585f49aSdrh **    May you share freely, never taking more than you give.
10*7585f49aSdrh **
11*7585f49aSdrh ******************************************************************************
12*7585f49aSdrh **
13*7585f49aSdrh ** This file contains an experimental VFS layer that operates on a
14*7585f49aSdrh ** Key/Value storage engine where both keys and values must be pure
15*7585f49aSdrh ** text.
16*7585f49aSdrh */
17*7585f49aSdrh #include <sqliteInt.h>
18*7585f49aSdrh #if SQLITE_OS_KV
19*7585f49aSdrh 
20*7585f49aSdrh /*****************************************************************************
21*7585f49aSdrh ** Debugging logic
22*7585f49aSdrh */
23*7585f49aSdrh 
24*7585f49aSdrh /* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */
25*7585f49aSdrh #if 0
26*7585f49aSdrh #define SQLITE_KV_TRACE(X)  printf X;
27*7585f49aSdrh #else
28*7585f49aSdrh #define SQLITE_KV_TRACE(X)
29*7585f49aSdrh #endif
30*7585f49aSdrh 
31*7585f49aSdrh /* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */
32*7585f49aSdrh #if 0
33*7585f49aSdrh #define SQLITE_KV_LOG(X)  printf X;
34*7585f49aSdrh #else
35*7585f49aSdrh #define SQLITE_KV_LOG(X)
36*7585f49aSdrh #endif
37*7585f49aSdrh 
38*7585f49aSdrh 
39*7585f49aSdrh /*
40*7585f49aSdrh ** Forward declaration of objects used by this VFS implementation
41*7585f49aSdrh */
42*7585f49aSdrh typedef struct KVVfsFile KVVfsFile;
43*7585f49aSdrh 
44*7585f49aSdrh /* A single open file.  There are only two files represented by this
45*7585f49aSdrh ** VFS - the database and the rollback journal.
46*7585f49aSdrh */
47*7585f49aSdrh struct KVVfsFile {
48*7585f49aSdrh   sqlite3_file base;              /* IO methods */
49*7585f49aSdrh   const char *zClass;             /* Storage class */
50*7585f49aSdrh   int isJournal;                  /* True if this is a journal file */
51*7585f49aSdrh   unsigned int nJrnl;             /* Space allocated for aJrnl[] */
52*7585f49aSdrh   char *aJrnl;                    /* Journal content */
53*7585f49aSdrh   int szPage;                     /* Last known page size */
54*7585f49aSdrh   sqlite3_int64 szDb;             /* Database file size.  -1 means unknown */
55*7585f49aSdrh };
56*7585f49aSdrh 
57*7585f49aSdrh /*
58*7585f49aSdrh ** Methods for KVVfsFile
59*7585f49aSdrh */
60*7585f49aSdrh static int kvvfsClose(sqlite3_file*);
61*7585f49aSdrh static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
62*7585f49aSdrh static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
63*7585f49aSdrh static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
64*7585f49aSdrh static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
65*7585f49aSdrh static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size);
66*7585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size);
67*7585f49aSdrh static int kvvfsSyncDb(sqlite3_file*, int flags);
68*7585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file*, int flags);
69*7585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize);
70*7585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize);
71*7585f49aSdrh static int kvvfsLock(sqlite3_file*, int);
72*7585f49aSdrh static int kvvfsUnlock(sqlite3_file*, int);
73*7585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
74*7585f49aSdrh static int kvvfsFileControl(sqlite3_file*, int op, void *pArg);
75*7585f49aSdrh static int kvvfsSectorSize(sqlite3_file*);
76*7585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file*);
77*7585f49aSdrh 
78*7585f49aSdrh /*
79*7585f49aSdrh ** Methods for sqlite3_vfs
80*7585f49aSdrh */
81*7585f49aSdrh static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
82*7585f49aSdrh static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
83*7585f49aSdrh static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
84*7585f49aSdrh static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
85*7585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
86*7585f49aSdrh static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
87*7585f49aSdrh static int kvvfsSleep(sqlite3_vfs*, int microseconds);
88*7585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs*, double*);
89*7585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
90*7585f49aSdrh 
91*7585f49aSdrh static sqlite3_vfs kvvfs_vfs = {
92*7585f49aSdrh   1,                              /* iVersion */
93*7585f49aSdrh   sizeof(KVVfsFile),              /* szOsFile */
94*7585f49aSdrh   1024,                           /* mxPathname */
95*7585f49aSdrh   0,                              /* pNext */
96*7585f49aSdrh   "kvvfs",                        /* zName */
97*7585f49aSdrh   0,                              /* pAppData */
98*7585f49aSdrh   kvvfsOpen,                      /* xOpen */
99*7585f49aSdrh   kvvfsDelete,                    /* xDelete */
100*7585f49aSdrh   kvvfsAccess,                    /* xAccess */
101*7585f49aSdrh   kvvfsFullPathname,              /* xFullPathname */
102*7585f49aSdrh   kvvfsDlOpen,                    /* xDlOpen */
103*7585f49aSdrh   0,                              /* xDlError */
104*7585f49aSdrh   0,                              /* xDlSym */
105*7585f49aSdrh   0,                              /* xDlClose */
106*7585f49aSdrh   kvvfsRandomness,                /* xRandomness */
107*7585f49aSdrh   kvvfsSleep,                     /* xSleep */
108*7585f49aSdrh   kvvfsCurrentTime,               /* xCurrentTime */
109*7585f49aSdrh   0,                              /* xGetLastError */
110*7585f49aSdrh   kvvfsCurrentTimeInt64           /* xCurrentTimeInt64 */
111*7585f49aSdrh };
112*7585f49aSdrh 
113*7585f49aSdrh /* Methods for sqlite3_file objects referencing a database file
114*7585f49aSdrh */
115*7585f49aSdrh static sqlite3_io_methods kvvfs_db_io_methods = {
116*7585f49aSdrh   1,                              /* iVersion */
117*7585f49aSdrh   kvvfsClose,                     /* xClose */
118*7585f49aSdrh   kvvfsReadDb,                    /* xRead */
119*7585f49aSdrh   kvvfsWriteDb,                   /* xWrite */
120*7585f49aSdrh   kvvfsTruncateDb,                /* xTruncate */
121*7585f49aSdrh   kvvfsSyncDb,                    /* xSync */
122*7585f49aSdrh   kvvfsFileSizeDb,                /* xFileSize */
123*7585f49aSdrh   kvvfsLock,                      /* xLock */
124*7585f49aSdrh   kvvfsUnlock,                    /* xUnlock */
125*7585f49aSdrh   kvvfsCheckReservedLock,         /* xCheckReservedLock */
126*7585f49aSdrh   kvvfsFileControl,               /* xFileControl */
127*7585f49aSdrh   kvvfsSectorSize,                /* xSectorSize */
128*7585f49aSdrh   kvvfsDeviceCharacteristics,     /* xDeviceCharacteristics */
129*7585f49aSdrh   0,                              /* xShmMap */
130*7585f49aSdrh   0,                              /* xShmLock */
131*7585f49aSdrh   0,                              /* xShmBarrier */
132*7585f49aSdrh   0,                              /* xShmUnmap */
133*7585f49aSdrh   0,                              /* xFetch */
134*7585f49aSdrh   0                               /* xUnfetch */
135*7585f49aSdrh };
136*7585f49aSdrh 
137*7585f49aSdrh /* Methods for sqlite3_file objects referencing a rollback journal
138*7585f49aSdrh */
139*7585f49aSdrh static sqlite3_io_methods kvvfs_jrnl_io_methods = {
140*7585f49aSdrh   1,                              /* iVersion */
141*7585f49aSdrh   kvvfsClose,                     /* xClose */
142*7585f49aSdrh   kvvfsReadJrnl,                  /* xRead */
143*7585f49aSdrh   kvvfsWriteJrnl,                 /* xWrite */
144*7585f49aSdrh   kvvfsTruncateJrnl,              /* xTruncate */
145*7585f49aSdrh   kvvfsSyncJrnl,                  /* xSync */
146*7585f49aSdrh   kvvfsFileSizeJrnl,              /* xFileSize */
147*7585f49aSdrh   kvvfsLock,                      /* xLock */
148*7585f49aSdrh   kvvfsUnlock,                    /* xUnlock */
149*7585f49aSdrh   kvvfsCheckReservedLock,         /* xCheckReservedLock */
150*7585f49aSdrh   kvvfsFileControl,               /* xFileControl */
151*7585f49aSdrh   kvvfsSectorSize,                /* xSectorSize */
152*7585f49aSdrh   kvvfsDeviceCharacteristics,     /* xDeviceCharacteristics */
153*7585f49aSdrh   0,                              /* xShmMap */
154*7585f49aSdrh   0,                              /* xShmLock */
155*7585f49aSdrh   0,                              /* xShmBarrier */
156*7585f49aSdrh   0,                              /* xShmUnmap */
157*7585f49aSdrh   0,                              /* xFetch */
158*7585f49aSdrh   0                               /* xUnfetch */
159*7585f49aSdrh };
160*7585f49aSdrh 
161*7585f49aSdrh /****** Storage subsystem **************************************************/
162*7585f49aSdrh #include <sys/types.h>
163*7585f49aSdrh #include <sys/stat.h>
164*7585f49aSdrh #include <unistd.h>
165*7585f49aSdrh 
166*7585f49aSdrh /* Forward declarations for the low-level storage engine
167*7585f49aSdrh */
168*7585f49aSdrh #define KVSTORAGE_KEY_SZ  24
169*7585f49aSdrh #define KVSTORAGE_KEY_PREFIX  "kvvfs-"
170*7585f49aSdrh static int kvstorageWrite(const char*, const char *zKey, const char *zData);
171*7585f49aSdrh static int kvstorageDelete(const char*, const char *zKey);
172*7585f49aSdrh static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
173*7585f49aSdrh 
174*7585f49aSdrh 
175*7585f49aSdrh /* Expand the key name with an appropriate prefix and put the result
176*7585f49aSdrh ** in pStore->zKey[]
177*7585f49aSdrh */
178*7585f49aSdrh static void kvstorageMakeKey(
179*7585f49aSdrh   const char *zClass,
180*7585f49aSdrh   const char *zKeyIn,
181*7585f49aSdrh   char *zKeyOut
182*7585f49aSdrh ){
183*7585f49aSdrh   sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "%s-%s", zClass, zKeyIn);
184*7585f49aSdrh }
185*7585f49aSdrh 
186*7585f49aSdrh /* Write content into a key.  zKey is of limited size.  zData should be
187*7585f49aSdrh ** pure text.  In other words, zData has already been encoded.
188*7585f49aSdrh **
189*7585f49aSdrh ** Return the number of errors.
190*7585f49aSdrh */
191*7585f49aSdrh static int kvstorageWrite(
192*7585f49aSdrh   const char *zClass,
193*7585f49aSdrh   const char *zKey,
194*7585f49aSdrh   const char *zData
195*7585f49aSdrh ){
196*7585f49aSdrh   FILE *fd;
197*7585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
198*7585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
199*7585f49aSdrh   fd = fopen(zXKey, "wb");
200*7585f49aSdrh   if( fd ){
201*7585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-WRITE  %-15s (%d) %.50s%s\n", zXKey,
202*7585f49aSdrh                  (int)strlen(zData), zData,
203*7585f49aSdrh                  strlen(zData)>50 ? "..." : ""));
204*7585f49aSdrh     fputs(zData, fd);
205*7585f49aSdrh     fclose(fd);
206*7585f49aSdrh     return 0;
207*7585f49aSdrh   }else{
208*7585f49aSdrh     return 1;
209*7585f49aSdrh   }
210*7585f49aSdrh }
211*7585f49aSdrh 
212*7585f49aSdrh /* Delete a key
213*7585f49aSdrh */
214*7585f49aSdrh static int kvstorageDelete(const char *zClass, const char *zKey){
215*7585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
216*7585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
217*7585f49aSdrh   unlink(zXKey);
218*7585f49aSdrh   SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey));
219*7585f49aSdrh   return 0;
220*7585f49aSdrh }
221*7585f49aSdrh 
222*7585f49aSdrh /* Read the value associated with a key and put the result in the first
223*7585f49aSdrh ** nBuf bytes of zBuf[].  The value might be truncated if zBuf is not large
224*7585f49aSdrh ** enough to hold it all.  The value put into zBuf will always be zero
225*7585f49aSdrh ** terminated.
226*7585f49aSdrh **
227*7585f49aSdrh ** Return the total number of bytes in the data, without truncation, and
228*7585f49aSdrh ** not counting the final zero terminator.   Return -1 if the key does
229*7585f49aSdrh ** not exist.
230*7585f49aSdrh **
231*7585f49aSdrh ** If nBuf==0 then this routine simply returns the size of the data without
232*7585f49aSdrh ** actually reading it.
233*7585f49aSdrh */
234*7585f49aSdrh static int kvstorageRead(
235*7585f49aSdrh   const char *zClass,
236*7585f49aSdrh   const char *zKey,
237*7585f49aSdrh   char *zBuf,
238*7585f49aSdrh   int nBuf
239*7585f49aSdrh ){
240*7585f49aSdrh   FILE *fd;
241*7585f49aSdrh   struct stat buf;
242*7585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
243*7585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
244*7585f49aSdrh   if( access(zXKey, R_OK)!=0
245*7585f49aSdrh    || stat(zXKey, &buf)!=0
246*7585f49aSdrh    || !S_ISREG(buf.st_mode)
247*7585f49aSdrh   ){
248*7585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (-1)\n", zXKey));
249*7585f49aSdrh     return -1;
250*7585f49aSdrh   }
251*7585f49aSdrh   if( nBuf<0 ){
252*7585f49aSdrh     return (int)buf.st_size;
253*7585f49aSdrh   }else if( nBuf==1 ){
254*7585f49aSdrh     zBuf[0] = 0;
255*7585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (%d)\n", zXKey,
256*7585f49aSdrh                  (int)buf.st_size));
257*7585f49aSdrh     return (int)buf.st_size;
258*7585f49aSdrh   }
259*7585f49aSdrh   if( nBuf > buf.st_size + 1 ){
260*7585f49aSdrh     nBuf = buf.st_size + 1;
261*7585f49aSdrh   }
262*7585f49aSdrh   fd = fopen(zXKey, "rb");
263*7585f49aSdrh   if( fd==0 ){
264*7585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (-1)\n", zXKey));
265*7585f49aSdrh     return -1;
266*7585f49aSdrh   }else{
267*7585f49aSdrh     sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd);
268*7585f49aSdrh     fclose(fd);
269*7585f49aSdrh     zBuf[n] = 0;
270*7585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (%lld) %.50s%s\n", zXKey,
271*7585f49aSdrh                  n, zBuf, n>50 ? "..." : ""));
272*7585f49aSdrh     return (int)n;
273*7585f49aSdrh   }
274*7585f49aSdrh }
275*7585f49aSdrh 
276*7585f49aSdrh /****** Utility subroutines ************************************************/
277*7585f49aSdrh 
278*7585f49aSdrh 
279*7585f49aSdrh /*
280*7585f49aSdrh ** Encode binary into the text encoded used to persist on disk.
281*7585f49aSdrh ** The output text is stored in aOut[], which must be at least
282*7585f49aSdrh ** nData+1 bytes in length.
283*7585f49aSdrh **
284*7585f49aSdrh ** Return the actual length of the encoded text, not counting the
285*7585f49aSdrh ** zero terminator at the end.
286*7585f49aSdrh **
287*7585f49aSdrh ** Encoding format
288*7585f49aSdrh ** ---------------
289*7585f49aSdrh **
290*7585f49aSdrh **   *  Non-zero bytes are encoded as upper-case hexadecimal
291*7585f49aSdrh **
292*7585f49aSdrh **   *  A sequence of one or more zero-bytes that are not at the
293*7585f49aSdrh **      beginning of the buffer are encoded as a little-endian
294*7585f49aSdrh **      base-26 number using a..z.  "a" means 0.  "b" means 1,
295*7585f49aSdrh **      "z" means 25.  "ab" means 26.  "ac" means 52.  And so forth.
296*7585f49aSdrh **
297*7585f49aSdrh **   *  Because there is no overlap between the encoding characters
298*7585f49aSdrh **      of hexadecimal and base-26 numbers, it is always clear where
299*7585f49aSdrh **      one stops and the next begins.
300*7585f49aSdrh */
301*7585f49aSdrh static int kvvfsEncode(const char *aData, int nData, char *aOut){
302*7585f49aSdrh   int i, j;
303*7585f49aSdrh   const unsigned char *a = (const unsigned char*)aData;
304*7585f49aSdrh   for(i=j=0; i<nData; i++){
305*7585f49aSdrh     unsigned char c = a[i];
306*7585f49aSdrh     if( c!=0 ){
307*7585f49aSdrh       aOut[j++] = "0123456789ABCDEF"[c>>4];
308*7585f49aSdrh       aOut[j++] = "0123456789ABCDEF"[c&0xf];
309*7585f49aSdrh     }else{
310*7585f49aSdrh       /* A sequence of 1 or more zeros is stored as a little-endian
311*7585f49aSdrh       ** base-26 number using a..z as the digits. So one zero is "b".
312*7585f49aSdrh       ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb",
313*7585f49aSdrh       ** and so forth.
314*7585f49aSdrh       */
315*7585f49aSdrh       int k;
316*7585f49aSdrh       for(k=1; i+k<nData && a[i+k]==0; k++){}
317*7585f49aSdrh       i += k-1;
318*7585f49aSdrh       while( k>0 ){
319*7585f49aSdrh         aOut[j++] = 'a'+(k%26);
320*7585f49aSdrh         k /= 26;
321*7585f49aSdrh       }
322*7585f49aSdrh     }
323*7585f49aSdrh   }
324*7585f49aSdrh   aOut[j] = 0;
325*7585f49aSdrh   return j;
326*7585f49aSdrh }
327*7585f49aSdrh 
328*7585f49aSdrh /* Convert hex to binary */
329*7585f49aSdrh static char kvvfsHexToBinary(char c){
330*7585f49aSdrh   if( c>='0' && c<='9' ) return c - '0';
331*7585f49aSdrh   if( c>='A' && c<='F' ) return c - 'A' + 10;
332*7585f49aSdrh   return 0;
333*7585f49aSdrh }
334*7585f49aSdrh 
335*7585f49aSdrh /*
336*7585f49aSdrh ** Decode the text encoding back to binary.  The binary content is
337*7585f49aSdrh ** written into pOut, which must be at least nOut bytes in length.
338*7585f49aSdrh **
339*7585f49aSdrh ** The return value is the number of bytes actually written into aOut[].
340*7585f49aSdrh */
341*7585f49aSdrh static int kvvfsDecode(const char *aIn, char *aOut, int nOut){
342*7585f49aSdrh   int i, j;
343*7585f49aSdrh   int c;
344*7585f49aSdrh   i = 0;
345*7585f49aSdrh   j = 0;
346*7585f49aSdrh   while( (c = aIn[i])!=0 ){
347*7585f49aSdrh     if( c>='a' ){
348*7585f49aSdrh       int n = 0;
349*7585f49aSdrh       int mult = 1;
350*7585f49aSdrh       while( c>='a' && c<='z' ){
351*7585f49aSdrh         n += (c - 'a')*mult;
352*7585f49aSdrh         mult *= 26;
353*7585f49aSdrh         c = aIn[++i];
354*7585f49aSdrh       }
355*7585f49aSdrh       if( j+n>nOut ) return -1;
356*7585f49aSdrh       while( n-->0 ){
357*7585f49aSdrh         aOut[j++] = 0;
358*7585f49aSdrh       }
359*7585f49aSdrh     }else{
360*7585f49aSdrh       if( j>nOut ) return -1;
361*7585f49aSdrh       aOut[j] = kvvfsHexToBinary(aIn[i++])<<4;
362*7585f49aSdrh       aOut[j++] += kvvfsHexToBinary(aIn[i++]);
363*7585f49aSdrh     }
364*7585f49aSdrh   }
365*7585f49aSdrh   return j;
366*7585f49aSdrh }
367*7585f49aSdrh 
368*7585f49aSdrh /*
369*7585f49aSdrh ** Decode a complete journal file.  Allocate space in pFile->aJrnl
370*7585f49aSdrh ** and store the decoding there.  Or leave pFile->aJrnl set to NULL
371*7585f49aSdrh ** if an error is encountered.
372*7585f49aSdrh **
373*7585f49aSdrh ** The first few characters of the text encoding will be a little-endian
374*7585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes
375*7585f49aSdrh ** in the decoded journal file image.  This base-26 number is followed
376*7585f49aSdrh ** by a single space, then the encoding of the journal.  The space
377*7585f49aSdrh ** separator is required to act as a terminator for the base-26 number.
378*7585f49aSdrh */
379*7585f49aSdrh static void kvvfsDecodeJournal(
380*7585f49aSdrh   KVVfsFile *pFile,      /* Store decoding in pFile->aJrnl */
381*7585f49aSdrh   const char *zTxt,      /* Text encoding.  Zero-terminated */
382*7585f49aSdrh   int nTxt               /* Bytes in zTxt, excluding zero terminator */
383*7585f49aSdrh ){
384*7585f49aSdrh   unsigned int n;
385*7585f49aSdrh   int c, i, mult;
386*7585f49aSdrh   i = 0;
387*7585f49aSdrh   mult = 1;
388*7585f49aSdrh   while( (c = zTxt[i++])>='a' && c<='z' ){
389*7585f49aSdrh     n += (zTxt[i] - 'a')*mult;
390*7585f49aSdrh     mult *= 26;
391*7585f49aSdrh   }
392*7585f49aSdrh   sqlite3_free(pFile->aJrnl);
393*7585f49aSdrh   pFile->aJrnl = sqlite3_malloc64( n );
394*7585f49aSdrh   if( pFile->aJrnl==0 ){
395*7585f49aSdrh     pFile->nJrnl = 0;
396*7585f49aSdrh     return;
397*7585f49aSdrh   }
398*7585f49aSdrh   pFile->nJrnl = n;
399*7585f49aSdrh   n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl);
400*7585f49aSdrh   if( n<pFile->nJrnl ){
401*7585f49aSdrh     sqlite3_free(pFile->aJrnl);
402*7585f49aSdrh     pFile->aJrnl = 0;
403*7585f49aSdrh     pFile->nJrnl = 0;
404*7585f49aSdrh   }
405*7585f49aSdrh }
406*7585f49aSdrh 
407*7585f49aSdrh /*
408*7585f49aSdrh ** Read or write the "sz" element, containing the database file size.
409*7585f49aSdrh */
410*7585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){
411*7585f49aSdrh   char zData[50];
412*7585f49aSdrh   zData[0] = 0;
413*7585f49aSdrh   kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1);
414*7585f49aSdrh   return strtoll(zData, 0, 0);
415*7585f49aSdrh }
416*7585f49aSdrh static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){
417*7585f49aSdrh   char zData[50];
418*7585f49aSdrh   sqlite3_snprintf(sizeof(zData), zData, "%lld", sz);
419*7585f49aSdrh   kvstorageWrite(pFile->zClass, "sz", zData);
420*7585f49aSdrh }
421*7585f49aSdrh 
422*7585f49aSdrh /****** sqlite3_io_methods methods ******************************************/
423*7585f49aSdrh 
424*7585f49aSdrh /*
425*7585f49aSdrh ** Close an kvvfs-file.
426*7585f49aSdrh */
427*7585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){
428*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
429*7585f49aSdrh 
430*7585f49aSdrh   SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass,
431*7585f49aSdrh              pFile->isJournal ? "journal" : "db"));
432*7585f49aSdrh   sqlite3_free(pFile->aJrnl);
433*7585f49aSdrh   return SQLITE_OK;
434*7585f49aSdrh }
435*7585f49aSdrh 
436*7585f49aSdrh /*
437*7585f49aSdrh ** Read from the -journal file.
438*7585f49aSdrh */
439*7585f49aSdrh static int kvvfsReadJrnl(
440*7585f49aSdrh   sqlite3_file *pProtoFile,
441*7585f49aSdrh   void *zBuf,
442*7585f49aSdrh   int iAmt,
443*7585f49aSdrh   sqlite_int64 iOfst
444*7585f49aSdrh ){
445*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
446*7585f49aSdrh   assert( pFile->isJournal );
447*7585f49aSdrh   SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
448*7585f49aSdrh   if( pFile->aJrnl==0 ){
449*7585f49aSdrh     int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, -1);
450*7585f49aSdrh     char *aTxt;
451*7585f49aSdrh     if( szTxt<=4 ){
452*7585f49aSdrh       return SQLITE_IOERR;
453*7585f49aSdrh     }
454*7585f49aSdrh     aTxt = sqlite3_malloc64( szTxt+1 );
455*7585f49aSdrh     if( aTxt==0 ) return SQLITE_NOMEM;
456*7585f49aSdrh     kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1);
457*7585f49aSdrh     kvvfsDecodeJournal(pFile, aTxt, szTxt);
458*7585f49aSdrh     sqlite3_free(aTxt);
459*7585f49aSdrh     if( pFile->aJrnl==0 ) return SQLITE_IOERR;
460*7585f49aSdrh   }
461*7585f49aSdrh   if( iOfst+iAmt>pFile->nJrnl ){
462*7585f49aSdrh     return SQLITE_IOERR_SHORT_READ;
463*7585f49aSdrh   }
464*7585f49aSdrh   memcpy(zBuf, pFile->aJrnl+iOfst, iAmt);
465*7585f49aSdrh   return SQLITE_OK;
466*7585f49aSdrh }
467*7585f49aSdrh 
468*7585f49aSdrh /*
469*7585f49aSdrh ** Read from the database file.
470*7585f49aSdrh */
471*7585f49aSdrh static int kvvfsReadDb(
472*7585f49aSdrh   sqlite3_file *pProtoFile,
473*7585f49aSdrh   void *zBuf,
474*7585f49aSdrh   int iAmt,
475*7585f49aSdrh   sqlite_int64 iOfst
476*7585f49aSdrh ){
477*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
478*7585f49aSdrh   unsigned int pgno;
479*7585f49aSdrh   int got, n;
480*7585f49aSdrh   char zKey[30];
481*7585f49aSdrh   char aData[131073];
482*7585f49aSdrh   assert( iOfst>=0 );
483*7585f49aSdrh   assert( iAmt>=0 );
484*7585f49aSdrh   SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
485*7585f49aSdrh   if( iOfst+iAmt>=512 ){
486*7585f49aSdrh     if( (iOfst % iAmt)!=0 ){
487*7585f49aSdrh       return SQLITE_IOERR_READ;
488*7585f49aSdrh     }
489*7585f49aSdrh     if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){
490*7585f49aSdrh       return SQLITE_IOERR_READ;
491*7585f49aSdrh     }
492*7585f49aSdrh     pFile->szPage = iAmt;
493*7585f49aSdrh     pgno = 1 + iOfst/iAmt;
494*7585f49aSdrh   }else{
495*7585f49aSdrh     pgno = 1;
496*7585f49aSdrh   }
497*7585f49aSdrh   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
498*7585f49aSdrh   got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1);
499*7585f49aSdrh   if( got<0 ){
500*7585f49aSdrh     n = 0;
501*7585f49aSdrh   }else{
502*7585f49aSdrh     aData[got] = 0;
503*7585f49aSdrh     if( iOfst+iAmt<512 ){
504*7585f49aSdrh       n = kvvfsDecode(aData, &aData[1000], 1000);
505*7585f49aSdrh       if( n>=iOfst+iAmt ){
506*7585f49aSdrh         memcpy(zBuf, &aData[1000+iOfst], iAmt);
507*7585f49aSdrh         n = iAmt;
508*7585f49aSdrh       }else{
509*7585f49aSdrh         n = 0;
510*7585f49aSdrh       }
511*7585f49aSdrh     }else{
512*7585f49aSdrh       n = kvvfsDecode(aData, zBuf, iAmt);
513*7585f49aSdrh     }
514*7585f49aSdrh   }
515*7585f49aSdrh   if( n<iAmt ){
516*7585f49aSdrh     memset(zBuf+n, 0, iAmt-n);
517*7585f49aSdrh     return SQLITE_IOERR_SHORT_READ;
518*7585f49aSdrh   }
519*7585f49aSdrh   return SQLITE_OK;
520*7585f49aSdrh }
521*7585f49aSdrh 
522*7585f49aSdrh 
523*7585f49aSdrh /*
524*7585f49aSdrh ** Write into the -journal file.
525*7585f49aSdrh */
526*7585f49aSdrh static int kvvfsWriteJrnl(
527*7585f49aSdrh   sqlite3_file *pProtoFile,
528*7585f49aSdrh   const void *zBuf,
529*7585f49aSdrh   int iAmt,
530*7585f49aSdrh   sqlite_int64 iOfst
531*7585f49aSdrh ){
532*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
533*7585f49aSdrh   sqlite3_int64 iEnd = iOfst+iAmt;
534*7585f49aSdrh   SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
535*7585f49aSdrh   if( iEnd>=0x10000000 ) return SQLITE_FULL;
536*7585f49aSdrh   if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){
537*7585f49aSdrh     char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd);
538*7585f49aSdrh     if( aNew==0 ){
539*7585f49aSdrh       return SQLITE_IOERR_NOMEM;
540*7585f49aSdrh     }
541*7585f49aSdrh     pFile->aJrnl = aNew;
542*7585f49aSdrh     if( pFile->nJrnl<iOfst ){
543*7585f49aSdrh       memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl);
544*7585f49aSdrh     }
545*7585f49aSdrh     pFile->nJrnl = iEnd;
546*7585f49aSdrh   }
547*7585f49aSdrh   memcpy(pFile->aJrnl+iOfst, zBuf, iAmt);
548*7585f49aSdrh   return SQLITE_OK;
549*7585f49aSdrh }
550*7585f49aSdrh 
551*7585f49aSdrh /*
552*7585f49aSdrh ** Write into the database file.
553*7585f49aSdrh */
554*7585f49aSdrh static int kvvfsWriteDb(
555*7585f49aSdrh   sqlite3_file *pProtoFile,
556*7585f49aSdrh   const void *zBuf,
557*7585f49aSdrh   int iAmt,
558*7585f49aSdrh   sqlite_int64 iOfst
559*7585f49aSdrh ){
560*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
561*7585f49aSdrh   unsigned int pgno;
562*7585f49aSdrh   char zKey[30];
563*7585f49aSdrh   char aData[131073];
564*7585f49aSdrh   SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
565*7585f49aSdrh   assert( iAmt>=512 && iAmt<=65536 );
566*7585f49aSdrh   assert( (iAmt & (iAmt-1))==0 );
567*7585f49aSdrh   pgno = 1 + iOfst/iAmt;
568*7585f49aSdrh   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
569*7585f49aSdrh   kvvfsEncode(zBuf, iAmt, aData);
570*7585f49aSdrh   kvstorageWrite(pFile->zClass, zKey, aData);
571*7585f49aSdrh   if( iOfst+iAmt > pFile->szDb ){
572*7585f49aSdrh     pFile->szDb = iOfst + iAmt;
573*7585f49aSdrh   }
574*7585f49aSdrh   return SQLITE_OK;
575*7585f49aSdrh }
576*7585f49aSdrh 
577*7585f49aSdrh /*
578*7585f49aSdrh ** Truncate an kvvfs-file.
579*7585f49aSdrh */
580*7585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){
581*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
582*7585f49aSdrh   SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size));
583*7585f49aSdrh   assert( size==0 );
584*7585f49aSdrh   kvstorageDelete(pFile->zClass, "jrnl");
585*7585f49aSdrh   sqlite3_free(pFile->aJrnl);
586*7585f49aSdrh   pFile->aJrnl = 0;
587*7585f49aSdrh   pFile->nJrnl = 0;
588*7585f49aSdrh   return SQLITE_OK;
589*7585f49aSdrh }
590*7585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){
591*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
592*7585f49aSdrh   if( pFile->szDb>size
593*7585f49aSdrh    && pFile->szPage>0
594*7585f49aSdrh    && (size % pFile->szPage)==0
595*7585f49aSdrh   ){
596*7585f49aSdrh     char zKey[50];
597*7585f49aSdrh     unsigned int pgno, pgnoMax;
598*7585f49aSdrh     SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size));
599*7585f49aSdrh     pgno = 1 + size/pFile->szPage;
600*7585f49aSdrh     pgnoMax = 2 + pFile->szDb/pFile->szPage;
601*7585f49aSdrh     while( pgno<=pgnoMax ){
602*7585f49aSdrh       sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
603*7585f49aSdrh       kvstorageDelete(pFile->zClass, zKey);
604*7585f49aSdrh       pgno++;
605*7585f49aSdrh     }
606*7585f49aSdrh     pFile->szDb = size;
607*7585f49aSdrh     kvvfsWriteFileSize(pFile, size);
608*7585f49aSdrh     return SQLITE_OK;
609*7585f49aSdrh   }
610*7585f49aSdrh   return SQLITE_IOERR;
611*7585f49aSdrh }
612*7585f49aSdrh 
613*7585f49aSdrh /*
614*7585f49aSdrh ** Sync an kvvfs-file.
615*7585f49aSdrh */
616*7585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){
617*7585f49aSdrh   int i, n;
618*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
619*7585f49aSdrh   char *zOut;
620*7585f49aSdrh   SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass));
621*7585f49aSdrh   if( pFile->nJrnl<=0 ){
622*7585f49aSdrh     return kvvfsTruncateJrnl(pProtoFile, 0);
623*7585f49aSdrh   }
624*7585f49aSdrh   zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 );
625*7585f49aSdrh   if( zOut==0 ){
626*7585f49aSdrh     return SQLITE_IOERR_NOMEM;
627*7585f49aSdrh   }
628*7585f49aSdrh   n = pFile->nJrnl;
629*7585f49aSdrh   i = 0;
630*7585f49aSdrh   do{
631*7585f49aSdrh     zOut[i++] = 'a' + (n%26);
632*7585f49aSdrh     n /= 26;
633*7585f49aSdrh   }while( n>0 );
634*7585f49aSdrh   zOut[i++] = ' ';
635*7585f49aSdrh   kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]);
636*7585f49aSdrh   kvstorageWrite(pFile->zClass, "jrnl", zOut);
637*7585f49aSdrh   sqlite3_free(zOut);
638*7585f49aSdrh   return SQLITE_OK;
639*7585f49aSdrh }
640*7585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){
641*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
642*7585f49aSdrh   SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass));
643*7585f49aSdrh   if( pFile->szDb>0 ){
644*7585f49aSdrh     kvvfsWriteFileSize(pFile, pFile->szDb);
645*7585f49aSdrh   }
646*7585f49aSdrh   return SQLITE_OK;
647*7585f49aSdrh }
648*7585f49aSdrh 
649*7585f49aSdrh /*
650*7585f49aSdrh ** Return the current file-size of an kvvfs-file.
651*7585f49aSdrh */
652*7585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
653*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
654*7585f49aSdrh   SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass));
655*7585f49aSdrh   *pSize = pFile->nJrnl;
656*7585f49aSdrh   return SQLITE_OK;
657*7585f49aSdrh }
658*7585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
659*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
660*7585f49aSdrh   SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass));
661*7585f49aSdrh   if( pFile->szDb>=0 ){
662*7585f49aSdrh     *pSize = pFile->szDb;
663*7585f49aSdrh   }else{
664*7585f49aSdrh     *pSize = kvvfsReadFileSize(pFile);
665*7585f49aSdrh   }
666*7585f49aSdrh   return SQLITE_OK;
667*7585f49aSdrh }
668*7585f49aSdrh 
669*7585f49aSdrh /*
670*7585f49aSdrh ** Lock an kvvfs-file.
671*7585f49aSdrh */
672*7585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){
673*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
674*7585f49aSdrh   assert( !pFile->isJournal );
675*7585f49aSdrh   SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock));
676*7585f49aSdrh 
677*7585f49aSdrh   if( eLock!=SQLITE_LOCK_NONE ){
678*7585f49aSdrh     pFile->szDb = kvvfsReadFileSize(pFile);
679*7585f49aSdrh   }
680*7585f49aSdrh   return SQLITE_OK;
681*7585f49aSdrh }
682*7585f49aSdrh 
683*7585f49aSdrh /*
684*7585f49aSdrh ** Unlock an kvvfs-file.
685*7585f49aSdrh */
686*7585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){
687*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
688*7585f49aSdrh   assert( !pFile->isJournal );
689*7585f49aSdrh   SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock));
690*7585f49aSdrh   if( eLock==SQLITE_LOCK_NONE ){
691*7585f49aSdrh     pFile->szDb = -1;
692*7585f49aSdrh   }
693*7585f49aSdrh   return SQLITE_OK;
694*7585f49aSdrh }
695*7585f49aSdrh 
696*7585f49aSdrh /*
697*7585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file.
698*7585f49aSdrh */
699*7585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){
700*7585f49aSdrh   SQLITE_KV_LOG(("xCheckReservedLock\n"));
701*7585f49aSdrh   *pResOut = 0;
702*7585f49aSdrh   return SQLITE_OK;
703*7585f49aSdrh }
704*7585f49aSdrh 
705*7585f49aSdrh /*
706*7585f49aSdrh ** File control method. For custom operations on an kvvfs-file.
707*7585f49aSdrh */
708*7585f49aSdrh static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){
709*7585f49aSdrh   return SQLITE_NOTFOUND;
710*7585f49aSdrh }
711*7585f49aSdrh 
712*7585f49aSdrh /*
713*7585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file.
714*7585f49aSdrh */
715*7585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){
716*7585f49aSdrh   return 512;
717*7585f49aSdrh }
718*7585f49aSdrh 
719*7585f49aSdrh /*
720*7585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file.
721*7585f49aSdrh */
722*7585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){
723*7585f49aSdrh   return 0;
724*7585f49aSdrh }
725*7585f49aSdrh 
726*7585f49aSdrh /****** sqlite3_vfs methods *************************************************/
727*7585f49aSdrh 
728*7585f49aSdrh /*
729*7585f49aSdrh ** Open an kvvfs file handle.
730*7585f49aSdrh */
731*7585f49aSdrh static int kvvfsOpen(
732*7585f49aSdrh   sqlite3_vfs *pProtoVfs,
733*7585f49aSdrh   const char *zName,
734*7585f49aSdrh   sqlite3_file *pProtoFile,
735*7585f49aSdrh   int flags,
736*7585f49aSdrh   int *pOutFlags
737*7585f49aSdrh ){
738*7585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
739*7585f49aSdrh   SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName));
740*7585f49aSdrh   if( strcmp(zName, "local")==0
741*7585f49aSdrh    || strcmp(zName, "session")==0
742*7585f49aSdrh   ){
743*7585f49aSdrh     pFile->isJournal = 0;
744*7585f49aSdrh     pFile->base.pMethods = &kvvfs_db_io_methods;
745*7585f49aSdrh   }else
746*7585f49aSdrh   if( strcmp(zName, "local-journal")==0
747*7585f49aSdrh    || strcmp(zName, "session-journal")==0
748*7585f49aSdrh   ){
749*7585f49aSdrh     pFile->isJournal = 1;
750*7585f49aSdrh     pFile->base.pMethods = &kvvfs_jrnl_io_methods;
751*7585f49aSdrh   }else{
752*7585f49aSdrh     return SQLITE_CANTOPEN;
753*7585f49aSdrh   }
754*7585f49aSdrh   if( zName[0]=='s' ){
755*7585f49aSdrh     pFile->zClass = KVSTORAGE_KEY_PREFIX "ses";
756*7585f49aSdrh   }else{
757*7585f49aSdrh     pFile->zClass = KVSTORAGE_KEY_PREFIX "loc";
758*7585f49aSdrh   }
759*7585f49aSdrh   pFile->aJrnl = 0;
760*7585f49aSdrh   pFile->nJrnl = 0;
761*7585f49aSdrh   pFile->szPage = -1;
762*7585f49aSdrh   pFile->szDb = -1;
763*7585f49aSdrh   return SQLITE_OK;
764*7585f49aSdrh }
765*7585f49aSdrh 
766*7585f49aSdrh /*
767*7585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true,
768*7585f49aSdrh ** ensure the file-system modifications are synced to disk before
769*7585f49aSdrh ** returning.
770*7585f49aSdrh */
771*7585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
772*7585f49aSdrh   if( strcmp(zPath, "local-journal")==0 ){
773*7585f49aSdrh     kvstorageDelete(KVSTORAGE_KEY_PREFIX "loc", "jrnl");
774*7585f49aSdrh   }else
775*7585f49aSdrh   if( strcmp(zPath, "session-journal")==0 ){
776*7585f49aSdrh     kvstorageDelete(KVSTORAGE_KEY_PREFIX "ses", "jrnl");
777*7585f49aSdrh   }
778*7585f49aSdrh   return SQLITE_OK;
779*7585f49aSdrh }
780*7585f49aSdrh 
781*7585f49aSdrh /*
782*7585f49aSdrh ** Test for access permissions. Return true if the requested permission
783*7585f49aSdrh ** is available, or false otherwise.
784*7585f49aSdrh */
785*7585f49aSdrh static int kvvfsAccess(
786*7585f49aSdrh   sqlite3_vfs *pProtoVfs,
787*7585f49aSdrh   const char *zPath,
788*7585f49aSdrh   int flags,
789*7585f49aSdrh   int *pResOut
790*7585f49aSdrh ){
791*7585f49aSdrh   SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath));
792*7585f49aSdrh   if( strcmp(zPath, "local-journal")==0 ){
793*7585f49aSdrh     *pResOut = kvstorageRead(KVSTORAGE_KEY_PREFIX "loc", "jrnl", 0, -1)>0;
794*7585f49aSdrh   }else
795*7585f49aSdrh   if( strcmp(zPath, "session-journal")==0 ){
796*7585f49aSdrh     *pResOut = kvstorageRead(KVSTORAGE_KEY_PREFIX "ses", "jrnl", 0, -1)>0;
797*7585f49aSdrh   }else
798*7585f49aSdrh   if( strcmp(zPath, "local")==0 ){
799*7585f49aSdrh     *pResOut = kvstorageRead(KVSTORAGE_KEY_PREFIX "loc", "sz", 0, -1)>0;
800*7585f49aSdrh   }else
801*7585f49aSdrh   if( strcmp(zPath, "session")==0 ){
802*7585f49aSdrh     *pResOut = kvstorageRead(KVSTORAGE_KEY_PREFIX "ses", "sz", 0, -1)>0;
803*7585f49aSdrh   }else
804*7585f49aSdrh   {
805*7585f49aSdrh     *pResOut = 0;
806*7585f49aSdrh   }
807*7585f49aSdrh   SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut));
808*7585f49aSdrh   return SQLITE_OK;
809*7585f49aSdrh }
810*7585f49aSdrh 
811*7585f49aSdrh /*
812*7585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding
813*7585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer
814*7585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes.
815*7585f49aSdrh */
816*7585f49aSdrh static int kvvfsFullPathname(
817*7585f49aSdrh   sqlite3_vfs *pVfs,
818*7585f49aSdrh   const char *zPath,
819*7585f49aSdrh   int nOut,
820*7585f49aSdrh   char *zOut
821*7585f49aSdrh ){
822*7585f49aSdrh   size_t nPath = strlen(zPath);
823*7585f49aSdrh   SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath));
824*7585f49aSdrh   if( nOut<nPath+1 ) nPath = nOut - 1;
825*7585f49aSdrh   memcpy(zOut, zPath, nPath);
826*7585f49aSdrh   zOut[nPath] = 0;
827*7585f49aSdrh   return SQLITE_OK;
828*7585f49aSdrh }
829*7585f49aSdrh 
830*7585f49aSdrh /*
831*7585f49aSdrh ** Open the dynamic library located at zPath and return a handle.
832*7585f49aSdrh */
833*7585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
834*7585f49aSdrh   return 0;
835*7585f49aSdrh }
836*7585f49aSdrh 
837*7585f49aSdrh /*
838*7585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of
839*7585f49aSdrh ** random data.
840*7585f49aSdrh */
841*7585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
842*7585f49aSdrh   memset(zBufOut, 0, nByte);
843*7585f49aSdrh   return nByte;
844*7585f49aSdrh }
845*7585f49aSdrh 
846*7585f49aSdrh /*
847*7585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds
848*7585f49aSdrh ** actually slept.
849*7585f49aSdrh */
850*7585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){
851*7585f49aSdrh   return SQLITE_OK;
852*7585f49aSdrh }
853*7585f49aSdrh 
854*7585f49aSdrh /*
855*7585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut.
856*7585f49aSdrh */
857*7585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
858*7585f49aSdrh   *pTimeOut = 2459829.13362986;
859*7585f49aSdrh   return SQLITE_OK;
860*7585f49aSdrh }
861*7585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
862*7585f49aSdrh   *pTimeOut = (sqlite3_int64)(2459829.13362986*86400000.0);
863*7585f49aSdrh   return SQLITE_OK;
864*7585f49aSdrh }
865*7585f49aSdrh 
866*7585f49aSdrh /*
867*7585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS.
868*7585f49aSdrh */
869*7585f49aSdrh int sqlite3_os_init(void){
870*7585f49aSdrh   return sqlite3_vfs_register(&kvvfs_vfs, 1);
871*7585f49aSdrh }
872*7585f49aSdrh int sqlite3_os_end(void){
873*7585f49aSdrh   return SQLITE_OK;
874*7585f49aSdrh }
875*7585f49aSdrh #endif /* SQLITE_OS_KV */
876