xref: /sqlite-3.40.0/src/os_kv.c (revision f7b58da8)
17585f49aSdrh /*
27585f49aSdrh ** 2022-09-06
37585f49aSdrh **
47585f49aSdrh ** The author disclaims copyright to this source code.  In place of
57585f49aSdrh ** a legal notice, here is a blessing:
67585f49aSdrh **
77585f49aSdrh **    May you do good and not evil.
87585f49aSdrh **    May you find forgiveness for yourself and forgive others.
97585f49aSdrh **    May you share freely, never taking more than you give.
107585f49aSdrh **
117585f49aSdrh ******************************************************************************
127585f49aSdrh **
137585f49aSdrh ** This file contains an experimental VFS layer that operates on a
147585f49aSdrh ** Key/Value storage engine where both keys and values must be pure
157585f49aSdrh ** text.
167585f49aSdrh */
177585f49aSdrh #include <sqliteInt.h>
1820a9ed1dSdrh #if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL))
197585f49aSdrh 
207585f49aSdrh /*****************************************************************************
217585f49aSdrh ** Debugging logic
227585f49aSdrh */
237585f49aSdrh 
247585f49aSdrh /* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */
257585f49aSdrh #if 0
26d35b5587Sstephan #define SQLITE_KV_TRACE(X)  printf X
277585f49aSdrh #else
287585f49aSdrh #define SQLITE_KV_TRACE(X)
297585f49aSdrh #endif
307585f49aSdrh 
317585f49aSdrh /* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */
327585f49aSdrh #if 0
33d35b5587Sstephan #define SQLITE_KV_LOG(X)  printf X
347585f49aSdrh #else
357585f49aSdrh #define SQLITE_KV_LOG(X)
367585f49aSdrh #endif
377585f49aSdrh 
387585f49aSdrh 
397585f49aSdrh /*
407585f49aSdrh ** Forward declaration of objects used by this VFS implementation
417585f49aSdrh */
427585f49aSdrh typedef struct KVVfsFile KVVfsFile;
437585f49aSdrh 
447585f49aSdrh /* A single open file.  There are only two files represented by this
457585f49aSdrh ** VFS - the database and the rollback journal.
467585f49aSdrh */
477585f49aSdrh struct KVVfsFile {
487585f49aSdrh   sqlite3_file base;              /* IO methods */
497585f49aSdrh   const char *zClass;             /* Storage class */
507585f49aSdrh   int isJournal;                  /* True if this is a journal file */
517585f49aSdrh   unsigned int nJrnl;             /* Space allocated for aJrnl[] */
527585f49aSdrh   char *aJrnl;                    /* Journal content */
537585f49aSdrh   int szPage;                     /* Last known page size */
547585f49aSdrh   sqlite3_int64 szDb;             /* Database file size.  -1 means unknown */
557585f49aSdrh };
567585f49aSdrh 
577585f49aSdrh /*
587585f49aSdrh ** Methods for KVVfsFile
597585f49aSdrh */
607585f49aSdrh static int kvvfsClose(sqlite3_file*);
617585f49aSdrh static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
627585f49aSdrh static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
637585f49aSdrh static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
647585f49aSdrh static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
657585f49aSdrh static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size);
667585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size);
677585f49aSdrh static int kvvfsSyncDb(sqlite3_file*, int flags);
687585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file*, int flags);
697585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize);
707585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize);
717585f49aSdrh static int kvvfsLock(sqlite3_file*, int);
727585f49aSdrh static int kvvfsUnlock(sqlite3_file*, int);
737585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
7484e50767Sdrh static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg);
7584e50767Sdrh static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg);
767585f49aSdrh static int kvvfsSectorSize(sqlite3_file*);
777585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file*);
787585f49aSdrh 
797585f49aSdrh /*
807585f49aSdrh ** Methods for sqlite3_vfs
817585f49aSdrh */
827585f49aSdrh static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
837585f49aSdrh static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
847585f49aSdrh static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
857585f49aSdrh static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
867585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
877585f49aSdrh static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
887585f49aSdrh static int kvvfsSleep(sqlite3_vfs*, int microseconds);
897585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs*, double*);
907585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
917585f49aSdrh 
9220a9ed1dSdrh static sqlite3_vfs sqlite3OsKvvfsObject = {
937585f49aSdrh   1,                              /* iVersion */
947585f49aSdrh   sizeof(KVVfsFile),              /* szOsFile */
957585f49aSdrh   1024,                           /* mxPathname */
967585f49aSdrh   0,                              /* pNext */
977585f49aSdrh   "kvvfs",                        /* zName */
987585f49aSdrh   0,                              /* pAppData */
997585f49aSdrh   kvvfsOpen,                      /* xOpen */
1007585f49aSdrh   kvvfsDelete,                    /* xDelete */
1017585f49aSdrh   kvvfsAccess,                    /* xAccess */
1027585f49aSdrh   kvvfsFullPathname,              /* xFullPathname */
1037585f49aSdrh   kvvfsDlOpen,                    /* xDlOpen */
1047585f49aSdrh   0,                              /* xDlError */
1057585f49aSdrh   0,                              /* xDlSym */
1067585f49aSdrh   0,                              /* xDlClose */
1077585f49aSdrh   kvvfsRandomness,                /* xRandomness */
1087585f49aSdrh   kvvfsSleep,                     /* xSleep */
1097585f49aSdrh   kvvfsCurrentTime,               /* xCurrentTime */
1107585f49aSdrh   0,                              /* xGetLastError */
1117585f49aSdrh   kvvfsCurrentTimeInt64           /* xCurrentTimeInt64 */
1127585f49aSdrh };
1137585f49aSdrh 
1147585f49aSdrh /* Methods for sqlite3_file objects referencing a database file
1157585f49aSdrh */
1167585f49aSdrh static sqlite3_io_methods kvvfs_db_io_methods = {
1177585f49aSdrh   1,                              /* iVersion */
1187585f49aSdrh   kvvfsClose,                     /* xClose */
1197585f49aSdrh   kvvfsReadDb,                    /* xRead */
1207585f49aSdrh   kvvfsWriteDb,                   /* xWrite */
1217585f49aSdrh   kvvfsTruncateDb,                /* xTruncate */
1227585f49aSdrh   kvvfsSyncDb,                    /* xSync */
1237585f49aSdrh   kvvfsFileSizeDb,                /* xFileSize */
1247585f49aSdrh   kvvfsLock,                      /* xLock */
1257585f49aSdrh   kvvfsUnlock,                    /* xUnlock */
1267585f49aSdrh   kvvfsCheckReservedLock,         /* xCheckReservedLock */
12784e50767Sdrh   kvvfsFileControlDb,             /* xFileControl */
1287585f49aSdrh   kvvfsSectorSize,                /* xSectorSize */
1297585f49aSdrh   kvvfsDeviceCharacteristics,     /* xDeviceCharacteristics */
1307585f49aSdrh   0,                              /* xShmMap */
1317585f49aSdrh   0,                              /* xShmLock */
1327585f49aSdrh   0,                              /* xShmBarrier */
1337585f49aSdrh   0,                              /* xShmUnmap */
1347585f49aSdrh   0,                              /* xFetch */
1357585f49aSdrh   0                               /* xUnfetch */
1367585f49aSdrh };
1377585f49aSdrh 
1387585f49aSdrh /* Methods for sqlite3_file objects referencing a rollback journal
1397585f49aSdrh */
1407585f49aSdrh static sqlite3_io_methods kvvfs_jrnl_io_methods = {
1417585f49aSdrh   1,                              /* iVersion */
1427585f49aSdrh   kvvfsClose,                     /* xClose */
1437585f49aSdrh   kvvfsReadJrnl,                  /* xRead */
1447585f49aSdrh   kvvfsWriteJrnl,                 /* xWrite */
1457585f49aSdrh   kvvfsTruncateJrnl,              /* xTruncate */
1467585f49aSdrh   kvvfsSyncJrnl,                  /* xSync */
1477585f49aSdrh   kvvfsFileSizeJrnl,              /* xFileSize */
1487585f49aSdrh   kvvfsLock,                      /* xLock */
1497585f49aSdrh   kvvfsUnlock,                    /* xUnlock */
1507585f49aSdrh   kvvfsCheckReservedLock,         /* xCheckReservedLock */
15184e50767Sdrh   kvvfsFileControlJrnl,           /* xFileControl */
1527585f49aSdrh   kvvfsSectorSize,                /* xSectorSize */
1537585f49aSdrh   kvvfsDeviceCharacteristics,     /* xDeviceCharacteristics */
1547585f49aSdrh   0,                              /* xShmMap */
1557585f49aSdrh   0,                              /* xShmLock */
1567585f49aSdrh   0,                              /* xShmBarrier */
1577585f49aSdrh   0,                              /* xShmUnmap */
1587585f49aSdrh   0,                              /* xFetch */
1597585f49aSdrh   0                               /* xUnfetch */
1607585f49aSdrh };
1617585f49aSdrh 
1627585f49aSdrh /****** Storage subsystem **************************************************/
1637585f49aSdrh #include <sys/types.h>
1647585f49aSdrh #include <sys/stat.h>
1657585f49aSdrh #include <unistd.h>
1667585f49aSdrh 
1677585f49aSdrh /* Forward declarations for the low-level storage engine
1687585f49aSdrh */
169d35b5587Sstephan static int kvstorageWrite(const char*, const char *zKey, const char *zData);
170d35b5587Sstephan static int kvstorageDelete(const char*, const char *zKey);
171d35b5587Sstephan static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
172c6c9c6aaSdrh #define KVSTORAGE_KEY_SZ  32
1737585f49aSdrh 
1747585f49aSdrh /* Expand the key name with an appropriate prefix and put the result
175c6c9c6aaSdrh ** zKeyOut[].  The zKeyOut[] buffer is assumed to hold at least
176c6c9c6aaSdrh ** KVSTORAGE_KEY_SZ bytes.
1777585f49aSdrh */
1787585f49aSdrh static void kvstorageMakeKey(
1797585f49aSdrh   const char *zClass,
1807585f49aSdrh   const char *zKeyIn,
1817585f49aSdrh   char *zKeyOut
1827585f49aSdrh ){
183c6c9c6aaSdrh   sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn);
1847585f49aSdrh }
1857585f49aSdrh 
186c6c9c6aaSdrh /* Write content into a key.  zClass is the particular namespace of the
187c6c9c6aaSdrh ** underlying key/value store to use - either "local" or "session".
188c6c9c6aaSdrh **
189c6c9c6aaSdrh ** Both zKey and zData are zero-terminated pure text strings.
1907585f49aSdrh **
1917585f49aSdrh ** Return the number of errors.
1927585f49aSdrh */
1937585f49aSdrh static int kvstorageWrite(
1947585f49aSdrh   const char *zClass,
1957585f49aSdrh   const char *zKey,
1967585f49aSdrh   const char *zData
1977585f49aSdrh ){
1987585f49aSdrh   FILE *fd;
1997585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
2007585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
2017585f49aSdrh   fd = fopen(zXKey, "wb");
2027585f49aSdrh   if( fd ){
2037585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-WRITE  %-15s (%d) %.50s%s\n", zXKey,
2047585f49aSdrh                  (int)strlen(zData), zData,
2057585f49aSdrh                  strlen(zData)>50 ? "..." : ""));
2067585f49aSdrh     fputs(zData, fd);
2077585f49aSdrh     fclose(fd);
2087585f49aSdrh     return 0;
2097585f49aSdrh   }else{
2107585f49aSdrh     return 1;
2117585f49aSdrh   }
2127585f49aSdrh }
2137585f49aSdrh 
214c6c9c6aaSdrh /* Delete a key (with its corresponding data) from the key/value
215c6c9c6aaSdrh ** namespace given by zClass.  If the key does not previously exist,
216c6c9c6aaSdrh ** this routine is a no-op.
2177585f49aSdrh */
2187585f49aSdrh static int kvstorageDelete(const char *zClass, const char *zKey){
2197585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
2207585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
2217585f49aSdrh   unlink(zXKey);
2227585f49aSdrh   SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey));
2237585f49aSdrh   return 0;
2247585f49aSdrh }
2257585f49aSdrh 
226c6c9c6aaSdrh /* Read the value associated with a zKey from the key/value namespace given
227c6c9c6aaSdrh ** by zClass and put the text data associated with that key in the first
2287585f49aSdrh ** nBuf bytes of zBuf[].  The value might be truncated if zBuf is not large
229c6c9c6aaSdrh ** enough to hold it all.  The value put into zBuf must always be zero
230c6c9c6aaSdrh ** terminated, even if it gets truncated because nBuf is not large enough.
2317585f49aSdrh **
2327585f49aSdrh ** Return the total number of bytes in the data, without truncation, and
2337585f49aSdrh ** not counting the final zero terminator.   Return -1 if the key does
2347585f49aSdrh ** not exist.
2357585f49aSdrh **
236c6c9c6aaSdrh ** If nBuf<=0 then this routine simply returns the size of the data without
2377585f49aSdrh ** actually reading it.
2387585f49aSdrh */
2397585f49aSdrh static int kvstorageRead(
2407585f49aSdrh   const char *zClass,
2417585f49aSdrh   const char *zKey,
2427585f49aSdrh   char *zBuf,
2437585f49aSdrh   int nBuf
2447585f49aSdrh ){
2457585f49aSdrh   FILE *fd;
2467585f49aSdrh   struct stat buf;
2477585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
2487585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
2497585f49aSdrh   if( access(zXKey, R_OK)!=0
2507585f49aSdrh    || stat(zXKey, &buf)!=0
2517585f49aSdrh    || !S_ISREG(buf.st_mode)
2527585f49aSdrh   ){
2537585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (-1)\n", zXKey));
2547585f49aSdrh     return -1;
2557585f49aSdrh   }
256c6c9c6aaSdrh   if( nBuf<=0 ){
2577585f49aSdrh     return (int)buf.st_size;
2587585f49aSdrh   }else if( nBuf==1 ){
2597585f49aSdrh     zBuf[0] = 0;
2607585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (%d)\n", zXKey,
2617585f49aSdrh                  (int)buf.st_size));
2627585f49aSdrh     return (int)buf.st_size;
2637585f49aSdrh   }
2647585f49aSdrh   if( nBuf > buf.st_size + 1 ){
2657585f49aSdrh     nBuf = buf.st_size + 1;
2667585f49aSdrh   }
2677585f49aSdrh   fd = fopen(zXKey, "rb");
2687585f49aSdrh   if( fd==0 ){
2697585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (-1)\n", zXKey));
2707585f49aSdrh     return -1;
2717585f49aSdrh   }else{
2727585f49aSdrh     sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd);
2737585f49aSdrh     fclose(fd);
2747585f49aSdrh     zBuf[n] = 0;
2757585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (%lld) %.50s%s\n", zXKey,
2767585f49aSdrh                  n, zBuf, n>50 ? "..." : ""));
2777585f49aSdrh     return (int)n;
2787585f49aSdrh   }
2797585f49aSdrh }
280d35b5587Sstephan 
281d35b5587Sstephan /*
282d35b5587Sstephan ** An internal level of indirection which enables us to replace the
283d35b5587Sstephan ** kvvfs i/o methods with JavaScript implementations in WASM builds.
284d35b5587Sstephan ** Maintenance reminder: if this struct changes in any way, the JSON
285d35b5587Sstephan ** rendering of its structure must be updated in
286d35b5587Sstephan ** sqlite3_wasm_enum_json(). There are no binary compatibility
287d35b5587Sstephan ** concerns, so it does not need an iVersion member. This file is
288d35b5587Sstephan ** necessarily always compiled together with sqlite3_wasm_enum_json(),
289d35b5587Sstephan ** and JS code dynamically creates the mapping of members based on
290d35b5587Sstephan ** that JSON description.
291d35b5587Sstephan */
292d35b5587Sstephan typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
293d35b5587Sstephan struct sqlite3_kvvfs_methods {
294d35b5587Sstephan   int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
295d35b5587Sstephan   int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
296d35b5587Sstephan   int (*xDelete)(const char *zClass, const char *zKey);
297d35b5587Sstephan   const int nKeySize;
298d35b5587Sstephan };
299d35b5587Sstephan 
300d35b5587Sstephan /*
301d35b5587Sstephan ** This object holds the kvvfs I/O methods which may be swapped out
302d35b5587Sstephan ** for JavaScript-side implementations in WASM builds. In such builds
303d35b5587Sstephan ** it cannot be const, but in native builds it should be so that
304d35b5587Sstephan ** the compiler can hopefully optimize this level of indirection out.
305d35b5587Sstephan ** That said, kvvfs is intended primarily for use in WASM builds.
306d35b5587Sstephan **
307d35b5587Sstephan ** Note that this is not explicitly flagged as static because the
308d35b5587Sstephan ** amalgamation build will tag it with SQLITE_PRIVATE.
309d35b5587Sstephan */
310d35b5587Sstephan #ifndef SQLITE_WASM
311d35b5587Sstephan const
312d35b5587Sstephan #endif
313d35b5587Sstephan sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
314d35b5587Sstephan kvstorageRead,
315d35b5587Sstephan kvstorageWrite,
316d35b5587Sstephan kvstorageDelete,
317d35b5587Sstephan KVSTORAGE_KEY_SZ
318d35b5587Sstephan };
3197585f49aSdrh 
3207585f49aSdrh /****** Utility subroutines ************************************************/
3217585f49aSdrh 
3227585f49aSdrh /*
3237585f49aSdrh ** Encode binary into the text encoded used to persist on disk.
3247585f49aSdrh ** The output text is stored in aOut[], which must be at least
3257585f49aSdrh ** nData+1 bytes in length.
3267585f49aSdrh **
3277585f49aSdrh ** Return the actual length of the encoded text, not counting the
3287585f49aSdrh ** zero terminator at the end.
3297585f49aSdrh **
3307585f49aSdrh ** Encoding format
3317585f49aSdrh ** ---------------
3327585f49aSdrh **
3337585f49aSdrh **   *  Non-zero bytes are encoded as upper-case hexadecimal
3347585f49aSdrh **
3357585f49aSdrh **   *  A sequence of one or more zero-bytes that are not at the
3367585f49aSdrh **      beginning of the buffer are encoded as a little-endian
3377585f49aSdrh **      base-26 number using a..z.  "a" means 0.  "b" means 1,
3387585f49aSdrh **      "z" means 25.  "ab" means 26.  "ac" means 52.  And so forth.
3397585f49aSdrh **
3407585f49aSdrh **   *  Because there is no overlap between the encoding characters
3417585f49aSdrh **      of hexadecimal and base-26 numbers, it is always clear where
3427585f49aSdrh **      one stops and the next begins.
3437585f49aSdrh */
3447585f49aSdrh static int kvvfsEncode(const char *aData, int nData, char *aOut){
3457585f49aSdrh   int i, j;
3467585f49aSdrh   const unsigned char *a = (const unsigned char*)aData;
3477585f49aSdrh   for(i=j=0; i<nData; i++){
3487585f49aSdrh     unsigned char c = a[i];
3497585f49aSdrh     if( c!=0 ){
3507585f49aSdrh       aOut[j++] = "0123456789ABCDEF"[c>>4];
3517585f49aSdrh       aOut[j++] = "0123456789ABCDEF"[c&0xf];
3527585f49aSdrh     }else{
3537585f49aSdrh       /* A sequence of 1 or more zeros is stored as a little-endian
3547585f49aSdrh       ** base-26 number using a..z as the digits. So one zero is "b".
3557585f49aSdrh       ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb",
3567585f49aSdrh       ** and so forth.
3577585f49aSdrh       */
3587585f49aSdrh       int k;
3597585f49aSdrh       for(k=1; i+k<nData && a[i+k]==0; k++){}
3607585f49aSdrh       i += k-1;
3617585f49aSdrh       while( k>0 ){
3627585f49aSdrh         aOut[j++] = 'a'+(k%26);
3637585f49aSdrh         k /= 26;
3647585f49aSdrh       }
3657585f49aSdrh     }
3667585f49aSdrh   }
3677585f49aSdrh   aOut[j] = 0;
3687585f49aSdrh   return j;
3697585f49aSdrh }
3707585f49aSdrh 
3716acd7b0aSdrh static const signed char kvvfsHexValue[256] = {
3726acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3736acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3746acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3756acd7b0aSdrh    0,  1,  2,  3,  4,  5,  6,  7,    8,  9, -1, -1, -1, -1, -1, -1,
3766acd7b0aSdrh   -1, 10, 11, 12, 13, 14, 15, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3776acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3786acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3796acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3806acd7b0aSdrh 
3816acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3826acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3836acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3846acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3856acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3866acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3876acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
3886acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1
3896acd7b0aSdrh };
3907585f49aSdrh 
3917585f49aSdrh /*
3927585f49aSdrh ** Decode the text encoding back to binary.  The binary content is
3937585f49aSdrh ** written into pOut, which must be at least nOut bytes in length.
3947585f49aSdrh **
3957585f49aSdrh ** The return value is the number of bytes actually written into aOut[].
3967585f49aSdrh */
3976acd7b0aSdrh static int kvvfsDecode(const char *a, char *aOut, int nOut){
3987585f49aSdrh   int i, j;
3997585f49aSdrh   int c;
4006acd7b0aSdrh   const unsigned char *aIn = (const unsigned char*)a;
4017585f49aSdrh   i = 0;
4027585f49aSdrh   j = 0;
4036acd7b0aSdrh   while( 1 ){
4046acd7b0aSdrh     c = kvvfsHexValue[aIn[i]];
4056acd7b0aSdrh     if( c<0 ){
4067585f49aSdrh       int n = 0;
4077585f49aSdrh       int mult = 1;
4086acd7b0aSdrh       c = aIn[i];
4096acd7b0aSdrh       if( c==0 ) break;
4107585f49aSdrh       while( c>='a' && c<='z' ){
4117585f49aSdrh         n += (c - 'a')*mult;
4127585f49aSdrh         mult *= 26;
4137585f49aSdrh         c = aIn[++i];
4147585f49aSdrh       }
4157585f49aSdrh       if( j+n>nOut ) return -1;
4166acd7b0aSdrh       memset(&aOut[j], 0, n);
4176acd7b0aSdrh       j += n;
4186acd7b0aSdrh       c = aIn[i];
4196acd7b0aSdrh       if( c==0 ) break;
4207585f49aSdrh     }else{
4216acd7b0aSdrh       aOut[j] = c<<4;
4226acd7b0aSdrh       c = kvvfsHexValue[aIn[++i]];
4236acd7b0aSdrh       if( c<0 ) break;
4246acd7b0aSdrh       aOut[j++] += c;
4256acd7b0aSdrh       i++;
4267585f49aSdrh     }
4277585f49aSdrh   }
4287585f49aSdrh   return j;
4297585f49aSdrh }
4307585f49aSdrh 
4317585f49aSdrh /*
4327585f49aSdrh ** Decode a complete journal file.  Allocate space in pFile->aJrnl
4337585f49aSdrh ** and store the decoding there.  Or leave pFile->aJrnl set to NULL
4347585f49aSdrh ** if an error is encountered.
4357585f49aSdrh **
4367585f49aSdrh ** The first few characters of the text encoding will be a little-endian
4377585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes
4387585f49aSdrh ** in the decoded journal file image.  This base-26 number is followed
4397585f49aSdrh ** by a single space, then the encoding of the journal.  The space
4407585f49aSdrh ** separator is required to act as a terminator for the base-26 number.
4417585f49aSdrh */
4427585f49aSdrh static void kvvfsDecodeJournal(
4437585f49aSdrh   KVVfsFile *pFile,      /* Store decoding in pFile->aJrnl */
4447585f49aSdrh   const char *zTxt,      /* Text encoding.  Zero-terminated */
4457585f49aSdrh   int nTxt               /* Bytes in zTxt, excluding zero terminator */
4467585f49aSdrh ){
447a50d3b7aSdrh   unsigned int n = 0;
4487585f49aSdrh   int c, i, mult;
4497585f49aSdrh   i = 0;
4507585f49aSdrh   mult = 1;
4517585f49aSdrh   while( (c = zTxt[i++])>='a' && c<='z' ){
4527585f49aSdrh     n += (zTxt[i] - 'a')*mult;
4537585f49aSdrh     mult *= 26;
4547585f49aSdrh   }
4557585f49aSdrh   sqlite3_free(pFile->aJrnl);
4567585f49aSdrh   pFile->aJrnl = sqlite3_malloc64( n );
4577585f49aSdrh   if( pFile->aJrnl==0 ){
4587585f49aSdrh     pFile->nJrnl = 0;
4597585f49aSdrh     return;
4607585f49aSdrh   }
4617585f49aSdrh   pFile->nJrnl = n;
4627585f49aSdrh   n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl);
4637585f49aSdrh   if( n<pFile->nJrnl ){
4647585f49aSdrh     sqlite3_free(pFile->aJrnl);
4657585f49aSdrh     pFile->aJrnl = 0;
4667585f49aSdrh     pFile->nJrnl = 0;
4677585f49aSdrh   }
4687585f49aSdrh }
4697585f49aSdrh 
4707585f49aSdrh /*
4717585f49aSdrh ** Read or write the "sz" element, containing the database file size.
4727585f49aSdrh */
4737585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){
4747585f49aSdrh   char zData[50];
4757585f49aSdrh   zData[0] = 0;
476d35b5587Sstephan   sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1);
4777585f49aSdrh   return strtoll(zData, 0, 0);
4787585f49aSdrh }
479d22cfa8cSstephan static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){
4807585f49aSdrh   char zData[50];
4817585f49aSdrh   sqlite3_snprintf(sizeof(zData), zData, "%lld", sz);
482d35b5587Sstephan   return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData);
4837585f49aSdrh }
4847585f49aSdrh 
4857585f49aSdrh /****** sqlite3_io_methods methods ******************************************/
4867585f49aSdrh 
4877585f49aSdrh /*
4887585f49aSdrh ** Close an kvvfs-file.
4897585f49aSdrh */
4907585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){
4917585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
4927585f49aSdrh 
4937585f49aSdrh   SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass,
4947585f49aSdrh              pFile->isJournal ? "journal" : "db"));
4957585f49aSdrh   sqlite3_free(pFile->aJrnl);
4967585f49aSdrh   return SQLITE_OK;
4977585f49aSdrh }
4987585f49aSdrh 
4997585f49aSdrh /*
5007585f49aSdrh ** Read from the -journal file.
5017585f49aSdrh */
5027585f49aSdrh static int kvvfsReadJrnl(
5037585f49aSdrh   sqlite3_file *pProtoFile,
5047585f49aSdrh   void *zBuf,
5057585f49aSdrh   int iAmt,
5067585f49aSdrh   sqlite_int64 iOfst
5077585f49aSdrh ){
5087585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
5097585f49aSdrh   assert( pFile->isJournal );
5107585f49aSdrh   SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
5117585f49aSdrh   if( pFile->aJrnl==0 ){
512c6c9c6aaSdrh     int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0);
5137585f49aSdrh     char *aTxt;
5147585f49aSdrh     if( szTxt<=4 ){
5157585f49aSdrh       return SQLITE_IOERR;
5167585f49aSdrh     }
5177585f49aSdrh     aTxt = sqlite3_malloc64( szTxt+1 );
5187585f49aSdrh     if( aTxt==0 ) return SQLITE_NOMEM;
5197585f49aSdrh     kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1);
5207585f49aSdrh     kvvfsDecodeJournal(pFile, aTxt, szTxt);
5217585f49aSdrh     sqlite3_free(aTxt);
5227585f49aSdrh     if( pFile->aJrnl==0 ) return SQLITE_IOERR;
5237585f49aSdrh   }
5247585f49aSdrh   if( iOfst+iAmt>pFile->nJrnl ){
5257585f49aSdrh     return SQLITE_IOERR_SHORT_READ;
5267585f49aSdrh   }
5277585f49aSdrh   memcpy(zBuf, pFile->aJrnl+iOfst, iAmt);
5287585f49aSdrh   return SQLITE_OK;
5297585f49aSdrh }
5307585f49aSdrh 
5317585f49aSdrh /*
5327585f49aSdrh ** Read from the database file.
5337585f49aSdrh */
5347585f49aSdrh static int kvvfsReadDb(
5357585f49aSdrh   sqlite3_file *pProtoFile,
5367585f49aSdrh   void *zBuf,
5377585f49aSdrh   int iAmt,
5387585f49aSdrh   sqlite_int64 iOfst
5397585f49aSdrh ){
5407585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
5417585f49aSdrh   unsigned int pgno;
5427585f49aSdrh   int got, n;
5437585f49aSdrh   char zKey[30];
544756440feSdrh   char aData[133073];
5457585f49aSdrh   assert( iOfst>=0 );
5467585f49aSdrh   assert( iAmt>=0 );
5477585f49aSdrh   SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
5487585f49aSdrh   if( iOfst+iAmt>=512 ){
5497585f49aSdrh     if( (iOfst % iAmt)!=0 ){
5507585f49aSdrh       return SQLITE_IOERR_READ;
5517585f49aSdrh     }
5527585f49aSdrh     if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){
5537585f49aSdrh       return SQLITE_IOERR_READ;
5547585f49aSdrh     }
5557585f49aSdrh     pFile->szPage = iAmt;
5567585f49aSdrh     pgno = 1 + iOfst/iAmt;
5577585f49aSdrh   }else{
5587585f49aSdrh     pgno = 1;
5597585f49aSdrh   }
5607585f49aSdrh   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
561d35b5587Sstephan   got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, aData, sizeof(aData)-1);
5627585f49aSdrh   if( got<0 ){
5637585f49aSdrh     n = 0;
5647585f49aSdrh   }else{
5657585f49aSdrh     aData[got] = 0;
5667585f49aSdrh     if( iOfst+iAmt<512 ){
567756440feSdrh       int k = iOfst+iAmt;
568756440feSdrh       aData[k*2] = 0;
569756440feSdrh       n = kvvfsDecode(aData, &aData[2000], sizeof(aData)-2000);
5707585f49aSdrh       if( n>=iOfst+iAmt ){
571756440feSdrh         memcpy(zBuf, &aData[2000+iOfst], iAmt);
5727585f49aSdrh         n = iAmt;
5737585f49aSdrh       }else{
5747585f49aSdrh         n = 0;
5757585f49aSdrh       }
5767585f49aSdrh     }else{
5777585f49aSdrh       n = kvvfsDecode(aData, zBuf, iAmt);
5787585f49aSdrh     }
5797585f49aSdrh   }
5807585f49aSdrh   if( n<iAmt ){
5817585f49aSdrh     memset(zBuf+n, 0, iAmt-n);
5827585f49aSdrh     return SQLITE_IOERR_SHORT_READ;
5837585f49aSdrh   }
5847585f49aSdrh   return SQLITE_OK;
5857585f49aSdrh }
5867585f49aSdrh 
5877585f49aSdrh 
5887585f49aSdrh /*
5897585f49aSdrh ** Write into the -journal file.
5907585f49aSdrh */
5917585f49aSdrh static int kvvfsWriteJrnl(
5927585f49aSdrh   sqlite3_file *pProtoFile,
5937585f49aSdrh   const void *zBuf,
5947585f49aSdrh   int iAmt,
5957585f49aSdrh   sqlite_int64 iOfst
5967585f49aSdrh ){
5977585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
5987585f49aSdrh   sqlite3_int64 iEnd = iOfst+iAmt;
5997585f49aSdrh   SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
6007585f49aSdrh   if( iEnd>=0x10000000 ) return SQLITE_FULL;
6017585f49aSdrh   if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){
6027585f49aSdrh     char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd);
6037585f49aSdrh     if( aNew==0 ){
6047585f49aSdrh       return SQLITE_IOERR_NOMEM;
6057585f49aSdrh     }
6067585f49aSdrh     pFile->aJrnl = aNew;
6077585f49aSdrh     if( pFile->nJrnl<iOfst ){
6087585f49aSdrh       memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl);
6097585f49aSdrh     }
6107585f49aSdrh     pFile->nJrnl = iEnd;
6117585f49aSdrh   }
6127585f49aSdrh   memcpy(pFile->aJrnl+iOfst, zBuf, iAmt);
6137585f49aSdrh   return SQLITE_OK;
6147585f49aSdrh }
6157585f49aSdrh 
6167585f49aSdrh /*
6177585f49aSdrh ** Write into the database file.
6187585f49aSdrh */
6197585f49aSdrh static int kvvfsWriteDb(
6207585f49aSdrh   sqlite3_file *pProtoFile,
6217585f49aSdrh   const void *zBuf,
6227585f49aSdrh   int iAmt,
6237585f49aSdrh   sqlite_int64 iOfst
6247585f49aSdrh ){
6257585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
6267585f49aSdrh   unsigned int pgno;
6277585f49aSdrh   char zKey[30];
6287585f49aSdrh   char aData[131073];
6297585f49aSdrh   SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
6307585f49aSdrh   assert( iAmt>=512 && iAmt<=65536 );
6317585f49aSdrh   assert( (iAmt & (iAmt-1))==0 );
632*f7b58da8Sdrh   assert( pFile->szPage<0 || pFile->szPage==iAmt );
633*f7b58da8Sdrh   pFile->szPage = iAmt;
6347585f49aSdrh   pgno = 1 + iOfst/iAmt;
6357585f49aSdrh   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
6367585f49aSdrh   kvvfsEncode(zBuf, iAmt, aData);
637d35b5587Sstephan   if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){
638d22cfa8cSstephan     return SQLITE_IOERR;
639d22cfa8cSstephan   }
6407585f49aSdrh   if( iOfst+iAmt > pFile->szDb ){
6417585f49aSdrh     pFile->szDb = iOfst + iAmt;
6427585f49aSdrh   }
6437585f49aSdrh   return SQLITE_OK;
6447585f49aSdrh }
6457585f49aSdrh 
6467585f49aSdrh /*
6477585f49aSdrh ** Truncate an kvvfs-file.
6487585f49aSdrh */
6497585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){
6507585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
6517585f49aSdrh   SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size));
6527585f49aSdrh   assert( size==0 );
653d35b5587Sstephan   sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl");
6547585f49aSdrh   sqlite3_free(pFile->aJrnl);
6557585f49aSdrh   pFile->aJrnl = 0;
6567585f49aSdrh   pFile->nJrnl = 0;
6577585f49aSdrh   return SQLITE_OK;
6587585f49aSdrh }
6597585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){
6607585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
6617585f49aSdrh   if( pFile->szDb>size
6627585f49aSdrh    && pFile->szPage>0
6637585f49aSdrh    && (size % pFile->szPage)==0
6647585f49aSdrh   ){
6657585f49aSdrh     char zKey[50];
6667585f49aSdrh     unsigned int pgno, pgnoMax;
6677585f49aSdrh     SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size));
6687585f49aSdrh     pgno = 1 + size/pFile->szPage;
6697585f49aSdrh     pgnoMax = 2 + pFile->szDb/pFile->szPage;
6707585f49aSdrh     while( pgno<=pgnoMax ){
6717585f49aSdrh       sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
672d35b5587Sstephan       sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey);
6737585f49aSdrh       pgno++;
6747585f49aSdrh     }
6757585f49aSdrh     pFile->szDb = size;
676d22cfa8cSstephan     return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK;
6777585f49aSdrh   }
6787585f49aSdrh   return SQLITE_IOERR;
6797585f49aSdrh }
6807585f49aSdrh 
6817585f49aSdrh /*
6827585f49aSdrh ** Sync an kvvfs-file.
6837585f49aSdrh */
6847585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){
6857585f49aSdrh   int i, n;
6867585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
6877585f49aSdrh   char *zOut;
6887585f49aSdrh   SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass));
6897585f49aSdrh   if( pFile->nJrnl<=0 ){
6907585f49aSdrh     return kvvfsTruncateJrnl(pProtoFile, 0);
6917585f49aSdrh   }
6927585f49aSdrh   zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 );
6937585f49aSdrh   if( zOut==0 ){
6947585f49aSdrh     return SQLITE_IOERR_NOMEM;
6957585f49aSdrh   }
6967585f49aSdrh   n = pFile->nJrnl;
6977585f49aSdrh   i = 0;
6987585f49aSdrh   do{
6997585f49aSdrh     zOut[i++] = 'a' + (n%26);
7007585f49aSdrh     n /= 26;
7017585f49aSdrh   }while( n>0 );
7027585f49aSdrh   zOut[i++] = ' ';
7037585f49aSdrh   kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]);
704d35b5587Sstephan   i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut);
7057585f49aSdrh   sqlite3_free(zOut);
706d22cfa8cSstephan   return i ? SQLITE_IOERR : SQLITE_OK;
7077585f49aSdrh }
7087585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){
70984e50767Sdrh   return SQLITE_OK;
7107585f49aSdrh }
7117585f49aSdrh 
7127585f49aSdrh /*
7137585f49aSdrh ** Return the current file-size of an kvvfs-file.
7147585f49aSdrh */
7157585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
7167585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
7177585f49aSdrh   SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass));
7187585f49aSdrh   *pSize = pFile->nJrnl;
7197585f49aSdrh   return SQLITE_OK;
7207585f49aSdrh }
7217585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
7227585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
7237585f49aSdrh   SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass));
7247585f49aSdrh   if( pFile->szDb>=0 ){
7257585f49aSdrh     *pSize = pFile->szDb;
7267585f49aSdrh   }else{
7277585f49aSdrh     *pSize = kvvfsReadFileSize(pFile);
7287585f49aSdrh   }
7297585f49aSdrh   return SQLITE_OK;
7307585f49aSdrh }
7317585f49aSdrh 
7327585f49aSdrh /*
7337585f49aSdrh ** Lock an kvvfs-file.
7347585f49aSdrh */
7357585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){
7367585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
7377585f49aSdrh   assert( !pFile->isJournal );
7387585f49aSdrh   SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock));
7397585f49aSdrh 
7407585f49aSdrh   if( eLock!=SQLITE_LOCK_NONE ){
7417585f49aSdrh     pFile->szDb = kvvfsReadFileSize(pFile);
7427585f49aSdrh   }
7437585f49aSdrh   return SQLITE_OK;
7447585f49aSdrh }
7457585f49aSdrh 
7467585f49aSdrh /*
7477585f49aSdrh ** Unlock an kvvfs-file.
7487585f49aSdrh */
7497585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){
7507585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
7517585f49aSdrh   assert( !pFile->isJournal );
7527585f49aSdrh   SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock));
7537585f49aSdrh   if( eLock==SQLITE_LOCK_NONE ){
7547585f49aSdrh     pFile->szDb = -1;
7557585f49aSdrh   }
7567585f49aSdrh   return SQLITE_OK;
7577585f49aSdrh }
7587585f49aSdrh 
7597585f49aSdrh /*
7607585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file.
7617585f49aSdrh */
7627585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){
7637585f49aSdrh   SQLITE_KV_LOG(("xCheckReservedLock\n"));
7647585f49aSdrh   *pResOut = 0;
7657585f49aSdrh   return SQLITE_OK;
7667585f49aSdrh }
7677585f49aSdrh 
7687585f49aSdrh /*
7697585f49aSdrh ** File control method. For custom operations on an kvvfs-file.
7707585f49aSdrh */
77184e50767Sdrh static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){
77284e50767Sdrh   SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op));
77384e50767Sdrh   return SQLITE_NOTFOUND;
77484e50767Sdrh }
77584e50767Sdrh static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){
77684e50767Sdrh   SQLITE_KV_LOG(("xFileControl(%d) on database\n", op));
77784e50767Sdrh   if( op==SQLITE_FCNTL_SYNC ){
77884e50767Sdrh     KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
77984e50767Sdrh     int rc = SQLITE_OK;
78084e50767Sdrh     SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass));
78184e50767Sdrh     if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){
78284e50767Sdrh       rc = SQLITE_IOERR;
78384e50767Sdrh     }
78484e50767Sdrh     return rc;
78584e50767Sdrh   }
7867585f49aSdrh   return SQLITE_NOTFOUND;
7877585f49aSdrh }
7887585f49aSdrh 
7897585f49aSdrh /*
7907585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file.
7917585f49aSdrh */
7927585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){
7937585f49aSdrh   return 512;
7947585f49aSdrh }
7957585f49aSdrh 
7967585f49aSdrh /*
7977585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file.
7987585f49aSdrh */
7997585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){
8007585f49aSdrh   return 0;
8017585f49aSdrh }
8027585f49aSdrh 
8037585f49aSdrh /****** sqlite3_vfs methods *************************************************/
8047585f49aSdrh 
8057585f49aSdrh /*
8067585f49aSdrh ** Open an kvvfs file handle.
8077585f49aSdrh */
8087585f49aSdrh static int kvvfsOpen(
8097585f49aSdrh   sqlite3_vfs *pProtoVfs,
8107585f49aSdrh   const char *zName,
8117585f49aSdrh   sqlite3_file *pProtoFile,
8127585f49aSdrh   int flags,
8137585f49aSdrh   int *pOutFlags
8147585f49aSdrh ){
8157585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
8167585f49aSdrh   SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName));
8177585f49aSdrh   if( strcmp(zName, "local")==0
8187585f49aSdrh    || strcmp(zName, "session")==0
8197585f49aSdrh   ){
8207585f49aSdrh     pFile->isJournal = 0;
8217585f49aSdrh     pFile->base.pMethods = &kvvfs_db_io_methods;
8227585f49aSdrh   }else
8237585f49aSdrh   if( strcmp(zName, "local-journal")==0
8247585f49aSdrh    || strcmp(zName, "session-journal")==0
8257585f49aSdrh   ){
8267585f49aSdrh     pFile->isJournal = 1;
8277585f49aSdrh     pFile->base.pMethods = &kvvfs_jrnl_io_methods;
8287585f49aSdrh   }else{
8297585f49aSdrh     return SQLITE_CANTOPEN;
8307585f49aSdrh   }
8317585f49aSdrh   if( zName[0]=='s' ){
832c6c9c6aaSdrh     pFile->zClass = "session";
8337585f49aSdrh   }else{
834c6c9c6aaSdrh     pFile->zClass = "local";
8357585f49aSdrh   }
8367585f49aSdrh   pFile->aJrnl = 0;
8377585f49aSdrh   pFile->nJrnl = 0;
8387585f49aSdrh   pFile->szPage = -1;
8397585f49aSdrh   pFile->szDb = -1;
8407585f49aSdrh   return SQLITE_OK;
8417585f49aSdrh }
8427585f49aSdrh 
8437585f49aSdrh /*
8447585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true,
8457585f49aSdrh ** ensure the file-system modifications are synced to disk before
8467585f49aSdrh ** returning.
8477585f49aSdrh */
8487585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
8497585f49aSdrh   if( strcmp(zPath, "local-journal")==0 ){
850d35b5587Sstephan     sqlite3KvvfsMethods.xDelete("local", "jrnl");
8517585f49aSdrh   }else
8527585f49aSdrh   if( strcmp(zPath, "session-journal")==0 ){
853d35b5587Sstephan     sqlite3KvvfsMethods.xDelete("session", "jrnl");
8547585f49aSdrh   }
8557585f49aSdrh   return SQLITE_OK;
8567585f49aSdrh }
8577585f49aSdrh 
8587585f49aSdrh /*
8597585f49aSdrh ** Test for access permissions. Return true if the requested permission
8607585f49aSdrh ** is available, or false otherwise.
8617585f49aSdrh */
8627585f49aSdrh static int kvvfsAccess(
8637585f49aSdrh   sqlite3_vfs *pProtoVfs,
8647585f49aSdrh   const char *zPath,
8657585f49aSdrh   int flags,
8667585f49aSdrh   int *pResOut
8677585f49aSdrh ){
8687585f49aSdrh   SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath));
8697585f49aSdrh   if( strcmp(zPath, "local-journal")==0 ){
870d35b5587Sstephan     *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0;
8717585f49aSdrh   }else
8727585f49aSdrh   if( strcmp(zPath, "session-journal")==0 ){
873d35b5587Sstephan     *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0;
8747585f49aSdrh   }else
8757585f49aSdrh   if( strcmp(zPath, "local")==0 ){
876d35b5587Sstephan     *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0;
8777585f49aSdrh   }else
8787585f49aSdrh   if( strcmp(zPath, "session")==0 ){
879d35b5587Sstephan     *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0;
8807585f49aSdrh   }else
8817585f49aSdrh   {
8827585f49aSdrh     *pResOut = 0;
8837585f49aSdrh   }
8847585f49aSdrh   SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut));
8857585f49aSdrh   return SQLITE_OK;
8867585f49aSdrh }
8877585f49aSdrh 
8887585f49aSdrh /*
8897585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding
8907585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer
8917585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes.
8927585f49aSdrh */
8937585f49aSdrh static int kvvfsFullPathname(
8947585f49aSdrh   sqlite3_vfs *pVfs,
8957585f49aSdrh   const char *zPath,
8967585f49aSdrh   int nOut,
8977585f49aSdrh   char *zOut
8987585f49aSdrh ){
8996acd7b0aSdrh   size_t nPath;
900bb195124Sdrh #ifdef SQLITE_OS_KV_ALWAYS_LOCAL
9016acd7b0aSdrh   zPath = "local";
902bb195124Sdrh #endif
9036acd7b0aSdrh   nPath = strlen(zPath);
9047585f49aSdrh   SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath));
9057585f49aSdrh   if( nOut<nPath+1 ) nPath = nOut - 1;
9067585f49aSdrh   memcpy(zOut, zPath, nPath);
9077585f49aSdrh   zOut[nPath] = 0;
9087585f49aSdrh   return SQLITE_OK;
9097585f49aSdrh }
9107585f49aSdrh 
9117585f49aSdrh /*
9127585f49aSdrh ** Open the dynamic library located at zPath and return a handle.
9137585f49aSdrh */
9147585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
9157585f49aSdrh   return 0;
9167585f49aSdrh }
9177585f49aSdrh 
9187585f49aSdrh /*
9197585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of
9207585f49aSdrh ** random data.
9217585f49aSdrh */
9227585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
9237585f49aSdrh   memset(zBufOut, 0, nByte);
9247585f49aSdrh   return nByte;
9257585f49aSdrh }
9267585f49aSdrh 
9277585f49aSdrh /*
9287585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds
9297585f49aSdrh ** actually slept.
9307585f49aSdrh */
9317585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){
9327585f49aSdrh   return SQLITE_OK;
9337585f49aSdrh }
9347585f49aSdrh 
9357585f49aSdrh /*
9367585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut.
9377585f49aSdrh */
9387585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
939d8bb0b5aSdrh   sqlite3_int64 i = 0;
940d8bb0b5aSdrh   int rc;
941d8bb0b5aSdrh   rc = kvvfsCurrentTimeInt64(0, &i);
942d8bb0b5aSdrh   *pTimeOut = i/86400000.0;
943d8bb0b5aSdrh   return rc;
9447585f49aSdrh }
945d8bb0b5aSdrh #include <sys/time.h>
9467585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
947d8bb0b5aSdrh   static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
948d8bb0b5aSdrh   struct timeval sNow;
949d8bb0b5aSdrh   (void)gettimeofday(&sNow, 0);  /* Cannot fail given valid arguments */
950d8bb0b5aSdrh   *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
9517585f49aSdrh   return SQLITE_OK;
9527585f49aSdrh }
95320a9ed1dSdrh #endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */
9547585f49aSdrh 
95520a9ed1dSdrh #if SQLITE_OS_KV
9567585f49aSdrh /*
9577585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS.
9587585f49aSdrh */
9597585f49aSdrh int sqlite3_os_init(void){
96020a9ed1dSdrh   return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1);
9617585f49aSdrh }
9627585f49aSdrh int sqlite3_os_end(void){
9637585f49aSdrh   return SQLITE_OK;
9647585f49aSdrh }
9657585f49aSdrh #endif /* SQLITE_OS_KV */
96620a9ed1dSdrh 
96720a9ed1dSdrh #if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
96820a9ed1dSdrh int sqlite3KvvfsInit(void){
96920a9ed1dSdrh   return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0);
97020a9ed1dSdrh }
97120a9ed1dSdrh #endif
972