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> 187585f49aSdrh #if SQLITE_OS_KV 197585f49aSdrh 207585f49aSdrh /***************************************************************************** 217585f49aSdrh ** Debugging logic 227585f49aSdrh */ 237585f49aSdrh 247585f49aSdrh /* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ 257585f49aSdrh #if 0 267585f49aSdrh #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 337585f49aSdrh #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); 747585f49aSdrh static int kvvfsFileControl(sqlite3_file*, int op, void *pArg); 757585f49aSdrh static int kvvfsSectorSize(sqlite3_file*); 767585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file*); 777585f49aSdrh 787585f49aSdrh /* 797585f49aSdrh ** Methods for sqlite3_vfs 807585f49aSdrh */ 817585f49aSdrh static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); 827585f49aSdrh static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); 837585f49aSdrh static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); 847585f49aSdrh static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); 857585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); 867585f49aSdrh static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); 877585f49aSdrh static int kvvfsSleep(sqlite3_vfs*, int microseconds); 887585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs*, double*); 897585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); 907585f49aSdrh 917585f49aSdrh static sqlite3_vfs kvvfs_vfs = { 927585f49aSdrh 1, /* iVersion */ 937585f49aSdrh sizeof(KVVfsFile), /* szOsFile */ 947585f49aSdrh 1024, /* mxPathname */ 957585f49aSdrh 0, /* pNext */ 967585f49aSdrh "kvvfs", /* zName */ 977585f49aSdrh 0, /* pAppData */ 987585f49aSdrh kvvfsOpen, /* xOpen */ 997585f49aSdrh kvvfsDelete, /* xDelete */ 1007585f49aSdrh kvvfsAccess, /* xAccess */ 1017585f49aSdrh kvvfsFullPathname, /* xFullPathname */ 1027585f49aSdrh kvvfsDlOpen, /* xDlOpen */ 1037585f49aSdrh 0, /* xDlError */ 1047585f49aSdrh 0, /* xDlSym */ 1057585f49aSdrh 0, /* xDlClose */ 1067585f49aSdrh kvvfsRandomness, /* xRandomness */ 1077585f49aSdrh kvvfsSleep, /* xSleep */ 1087585f49aSdrh kvvfsCurrentTime, /* xCurrentTime */ 1097585f49aSdrh 0, /* xGetLastError */ 1107585f49aSdrh kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ 1117585f49aSdrh }; 1127585f49aSdrh 1137585f49aSdrh /* Methods for sqlite3_file objects referencing a database file 1147585f49aSdrh */ 1157585f49aSdrh static sqlite3_io_methods kvvfs_db_io_methods = { 1167585f49aSdrh 1, /* iVersion */ 1177585f49aSdrh kvvfsClose, /* xClose */ 1187585f49aSdrh kvvfsReadDb, /* xRead */ 1197585f49aSdrh kvvfsWriteDb, /* xWrite */ 1207585f49aSdrh kvvfsTruncateDb, /* xTruncate */ 1217585f49aSdrh kvvfsSyncDb, /* xSync */ 1227585f49aSdrh kvvfsFileSizeDb, /* xFileSize */ 1237585f49aSdrh kvvfsLock, /* xLock */ 1247585f49aSdrh kvvfsUnlock, /* xUnlock */ 1257585f49aSdrh kvvfsCheckReservedLock, /* xCheckReservedLock */ 1267585f49aSdrh kvvfsFileControl, /* xFileControl */ 1277585f49aSdrh kvvfsSectorSize, /* xSectorSize */ 1287585f49aSdrh kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 1297585f49aSdrh 0, /* xShmMap */ 1307585f49aSdrh 0, /* xShmLock */ 1317585f49aSdrh 0, /* xShmBarrier */ 1327585f49aSdrh 0, /* xShmUnmap */ 1337585f49aSdrh 0, /* xFetch */ 1347585f49aSdrh 0 /* xUnfetch */ 1357585f49aSdrh }; 1367585f49aSdrh 1377585f49aSdrh /* Methods for sqlite3_file objects referencing a rollback journal 1387585f49aSdrh */ 1397585f49aSdrh static sqlite3_io_methods kvvfs_jrnl_io_methods = { 1407585f49aSdrh 1, /* iVersion */ 1417585f49aSdrh kvvfsClose, /* xClose */ 1427585f49aSdrh kvvfsReadJrnl, /* xRead */ 1437585f49aSdrh kvvfsWriteJrnl, /* xWrite */ 1447585f49aSdrh kvvfsTruncateJrnl, /* xTruncate */ 1457585f49aSdrh kvvfsSyncJrnl, /* xSync */ 1467585f49aSdrh kvvfsFileSizeJrnl, /* xFileSize */ 1477585f49aSdrh kvvfsLock, /* xLock */ 1487585f49aSdrh kvvfsUnlock, /* xUnlock */ 1497585f49aSdrh kvvfsCheckReservedLock, /* xCheckReservedLock */ 1507585f49aSdrh kvvfsFileControl, /* xFileControl */ 1517585f49aSdrh kvvfsSectorSize, /* xSectorSize */ 1527585f49aSdrh kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 1537585f49aSdrh 0, /* xShmMap */ 1547585f49aSdrh 0, /* xShmLock */ 1557585f49aSdrh 0, /* xShmBarrier */ 1567585f49aSdrh 0, /* xShmUnmap */ 1577585f49aSdrh 0, /* xFetch */ 1587585f49aSdrh 0 /* xUnfetch */ 1597585f49aSdrh }; 1607585f49aSdrh 1617585f49aSdrh /****** Storage subsystem **************************************************/ 1627585f49aSdrh #include <sys/types.h> 1637585f49aSdrh #include <sys/stat.h> 1647585f49aSdrh #include <unistd.h> 1657585f49aSdrh 1667585f49aSdrh /* Forward declarations for the low-level storage engine 1677585f49aSdrh */ 168*c6c9c6aaSdrh #define KVSTORAGE_KEY_SZ 32 1697585f49aSdrh static int kvstorageWrite(const char*, const char *zKey, const char *zData); 1707585f49aSdrh static int kvstorageDelete(const char*, const char *zKey); 1717585f49aSdrh static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); 1727585f49aSdrh 1737585f49aSdrh 1747585f49aSdrh /* Expand the key name with an appropriate prefix and put the result 175*c6c9c6aaSdrh ** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least 176*c6c9c6aaSdrh ** KVSTORAGE_KEY_SZ bytes. 1777585f49aSdrh */ 1787585f49aSdrh static void kvstorageMakeKey( 1797585f49aSdrh const char *zClass, 1807585f49aSdrh const char *zKeyIn, 1817585f49aSdrh char *zKeyOut 1827585f49aSdrh ){ 183*c6c9c6aaSdrh sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); 1847585f49aSdrh } 1857585f49aSdrh 186*c6c9c6aaSdrh /* Write content into a key. zClass is the particular namespace of the 187*c6c9c6aaSdrh ** underlying key/value store to use - either "local" or "session". 188*c6c9c6aaSdrh ** 189*c6c9c6aaSdrh ** 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 214*c6c9c6aaSdrh /* Delete a key (with its corresponding data) from the key/value 215*c6c9c6aaSdrh ** namespace given by zClass. If the key does not previously exist, 216*c6c9c6aaSdrh ** 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 226*c6c9c6aaSdrh /* Read the value associated with a zKey from the key/value namespace given 227*c6c9c6aaSdrh ** 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 229*c6c9c6aaSdrh ** enough to hold it all. The value put into zBuf must always be zero 230*c6c9c6aaSdrh ** 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 ** 236*c6c9c6aaSdrh ** 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 } 256*c6c9c6aaSdrh 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 } 2807585f49aSdrh 2817585f49aSdrh /****** Utility subroutines ************************************************/ 2827585f49aSdrh 2837585f49aSdrh /* 2847585f49aSdrh ** Encode binary into the text encoded used to persist on disk. 2857585f49aSdrh ** The output text is stored in aOut[], which must be at least 2867585f49aSdrh ** nData+1 bytes in length. 2877585f49aSdrh ** 2887585f49aSdrh ** Return the actual length of the encoded text, not counting the 2897585f49aSdrh ** zero terminator at the end. 2907585f49aSdrh ** 2917585f49aSdrh ** Encoding format 2927585f49aSdrh ** --------------- 2937585f49aSdrh ** 2947585f49aSdrh ** * Non-zero bytes are encoded as upper-case hexadecimal 2957585f49aSdrh ** 2967585f49aSdrh ** * A sequence of one or more zero-bytes that are not at the 2977585f49aSdrh ** beginning of the buffer are encoded as a little-endian 2987585f49aSdrh ** base-26 number using a..z. "a" means 0. "b" means 1, 2997585f49aSdrh ** "z" means 25. "ab" means 26. "ac" means 52. And so forth. 3007585f49aSdrh ** 3017585f49aSdrh ** * Because there is no overlap between the encoding characters 3027585f49aSdrh ** of hexadecimal and base-26 numbers, it is always clear where 3037585f49aSdrh ** one stops and the next begins. 3047585f49aSdrh */ 3057585f49aSdrh static int kvvfsEncode(const char *aData, int nData, char *aOut){ 3067585f49aSdrh int i, j; 3077585f49aSdrh const unsigned char *a = (const unsigned char*)aData; 3087585f49aSdrh for(i=j=0; i<nData; i++){ 3097585f49aSdrh unsigned char c = a[i]; 3107585f49aSdrh if( c!=0 ){ 3117585f49aSdrh aOut[j++] = "0123456789ABCDEF"[c>>4]; 3127585f49aSdrh aOut[j++] = "0123456789ABCDEF"[c&0xf]; 3137585f49aSdrh }else{ 3147585f49aSdrh /* A sequence of 1 or more zeros is stored as a little-endian 3157585f49aSdrh ** base-26 number using a..z as the digits. So one zero is "b". 3167585f49aSdrh ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", 3177585f49aSdrh ** and so forth. 3187585f49aSdrh */ 3197585f49aSdrh int k; 3207585f49aSdrh for(k=1; i+k<nData && a[i+k]==0; k++){} 3217585f49aSdrh i += k-1; 3227585f49aSdrh while( k>0 ){ 3237585f49aSdrh aOut[j++] = 'a'+(k%26); 3247585f49aSdrh k /= 26; 3257585f49aSdrh } 3267585f49aSdrh } 3277585f49aSdrh } 3287585f49aSdrh aOut[j] = 0; 3297585f49aSdrh return j; 3307585f49aSdrh } 3317585f49aSdrh 3327585f49aSdrh /* Convert hex to binary */ 3337585f49aSdrh static char kvvfsHexToBinary(char c){ 3347585f49aSdrh if( c>='0' && c<='9' ) return c - '0'; 3357585f49aSdrh if( c>='A' && c<='F' ) return c - 'A' + 10; 3367585f49aSdrh return 0; 3377585f49aSdrh } 3387585f49aSdrh 3397585f49aSdrh /* 3407585f49aSdrh ** Decode the text encoding back to binary. The binary content is 3417585f49aSdrh ** written into pOut, which must be at least nOut bytes in length. 3427585f49aSdrh ** 3437585f49aSdrh ** The return value is the number of bytes actually written into aOut[]. 3447585f49aSdrh */ 3457585f49aSdrh static int kvvfsDecode(const char *aIn, char *aOut, int nOut){ 3467585f49aSdrh int i, j; 3477585f49aSdrh int c; 3487585f49aSdrh i = 0; 3497585f49aSdrh j = 0; 3507585f49aSdrh while( (c = aIn[i])!=0 ){ 3517585f49aSdrh if( c>='a' ){ 3527585f49aSdrh int n = 0; 3537585f49aSdrh int mult = 1; 3547585f49aSdrh while( c>='a' && c<='z' ){ 3557585f49aSdrh n += (c - 'a')*mult; 3567585f49aSdrh mult *= 26; 3577585f49aSdrh c = aIn[++i]; 3587585f49aSdrh } 3597585f49aSdrh if( j+n>nOut ) return -1; 3607585f49aSdrh while( n-->0 ){ 3617585f49aSdrh aOut[j++] = 0; 3627585f49aSdrh } 3637585f49aSdrh }else{ 3647585f49aSdrh if( j>nOut ) return -1; 3657585f49aSdrh aOut[j] = kvvfsHexToBinary(aIn[i++])<<4; 3667585f49aSdrh aOut[j++] += kvvfsHexToBinary(aIn[i++]); 3677585f49aSdrh } 3687585f49aSdrh } 3697585f49aSdrh return j; 3707585f49aSdrh } 3717585f49aSdrh 3727585f49aSdrh /* 3737585f49aSdrh ** Decode a complete journal file. Allocate space in pFile->aJrnl 3747585f49aSdrh ** and store the decoding there. Or leave pFile->aJrnl set to NULL 3757585f49aSdrh ** if an error is encountered. 3767585f49aSdrh ** 3777585f49aSdrh ** The first few characters of the text encoding will be a little-endian 3787585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes 3797585f49aSdrh ** in the decoded journal file image. This base-26 number is followed 3807585f49aSdrh ** by a single space, then the encoding of the journal. The space 3817585f49aSdrh ** separator is required to act as a terminator for the base-26 number. 3827585f49aSdrh */ 3837585f49aSdrh static void kvvfsDecodeJournal( 3847585f49aSdrh KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ 3857585f49aSdrh const char *zTxt, /* Text encoding. Zero-terminated */ 3867585f49aSdrh int nTxt /* Bytes in zTxt, excluding zero terminator */ 3877585f49aSdrh ){ 3887585f49aSdrh unsigned int n; 3897585f49aSdrh int c, i, mult; 3907585f49aSdrh i = 0; 3917585f49aSdrh mult = 1; 3927585f49aSdrh while( (c = zTxt[i++])>='a' && c<='z' ){ 3937585f49aSdrh n += (zTxt[i] - 'a')*mult; 3947585f49aSdrh mult *= 26; 3957585f49aSdrh } 3967585f49aSdrh sqlite3_free(pFile->aJrnl); 3977585f49aSdrh pFile->aJrnl = sqlite3_malloc64( n ); 3987585f49aSdrh if( pFile->aJrnl==0 ){ 3997585f49aSdrh pFile->nJrnl = 0; 4007585f49aSdrh return; 4017585f49aSdrh } 4027585f49aSdrh pFile->nJrnl = n; 4037585f49aSdrh n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); 4047585f49aSdrh if( n<pFile->nJrnl ){ 4057585f49aSdrh sqlite3_free(pFile->aJrnl); 4067585f49aSdrh pFile->aJrnl = 0; 4077585f49aSdrh pFile->nJrnl = 0; 4087585f49aSdrh } 4097585f49aSdrh } 4107585f49aSdrh 4117585f49aSdrh /* 4127585f49aSdrh ** Read or write the "sz" element, containing the database file size. 4137585f49aSdrh */ 4147585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ 4157585f49aSdrh char zData[50]; 4167585f49aSdrh zData[0] = 0; 4177585f49aSdrh kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1); 4187585f49aSdrh return strtoll(zData, 0, 0); 4197585f49aSdrh } 4207585f49aSdrh static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ 4217585f49aSdrh char zData[50]; 4227585f49aSdrh sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); 4237585f49aSdrh kvstorageWrite(pFile->zClass, "sz", zData); 4247585f49aSdrh } 4257585f49aSdrh 4267585f49aSdrh /****** sqlite3_io_methods methods ******************************************/ 4277585f49aSdrh 4287585f49aSdrh /* 4297585f49aSdrh ** Close an kvvfs-file. 4307585f49aSdrh */ 4317585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){ 4327585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 4337585f49aSdrh 4347585f49aSdrh SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, 4357585f49aSdrh pFile->isJournal ? "journal" : "db")); 4367585f49aSdrh sqlite3_free(pFile->aJrnl); 4377585f49aSdrh return SQLITE_OK; 4387585f49aSdrh } 4397585f49aSdrh 4407585f49aSdrh /* 4417585f49aSdrh ** Read from the -journal file. 4427585f49aSdrh */ 4437585f49aSdrh static int kvvfsReadJrnl( 4447585f49aSdrh sqlite3_file *pProtoFile, 4457585f49aSdrh void *zBuf, 4467585f49aSdrh int iAmt, 4477585f49aSdrh sqlite_int64 iOfst 4487585f49aSdrh ){ 4497585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 4507585f49aSdrh assert( pFile->isJournal ); 4517585f49aSdrh SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 4527585f49aSdrh if( pFile->aJrnl==0 ){ 453*c6c9c6aaSdrh int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); 4547585f49aSdrh char *aTxt; 4557585f49aSdrh if( szTxt<=4 ){ 4567585f49aSdrh return SQLITE_IOERR; 4577585f49aSdrh } 4587585f49aSdrh aTxt = sqlite3_malloc64( szTxt+1 ); 4597585f49aSdrh if( aTxt==0 ) return SQLITE_NOMEM; 4607585f49aSdrh kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); 4617585f49aSdrh kvvfsDecodeJournal(pFile, aTxt, szTxt); 4627585f49aSdrh sqlite3_free(aTxt); 4637585f49aSdrh if( pFile->aJrnl==0 ) return SQLITE_IOERR; 4647585f49aSdrh } 4657585f49aSdrh if( iOfst+iAmt>pFile->nJrnl ){ 4667585f49aSdrh return SQLITE_IOERR_SHORT_READ; 4677585f49aSdrh } 4687585f49aSdrh memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); 4697585f49aSdrh return SQLITE_OK; 4707585f49aSdrh } 4717585f49aSdrh 4727585f49aSdrh /* 4737585f49aSdrh ** Read from the database file. 4747585f49aSdrh */ 4757585f49aSdrh static int kvvfsReadDb( 4767585f49aSdrh sqlite3_file *pProtoFile, 4777585f49aSdrh void *zBuf, 4787585f49aSdrh int iAmt, 4797585f49aSdrh sqlite_int64 iOfst 4807585f49aSdrh ){ 4817585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 4827585f49aSdrh unsigned int pgno; 4837585f49aSdrh int got, n; 4847585f49aSdrh char zKey[30]; 4857585f49aSdrh char aData[131073]; 4867585f49aSdrh assert( iOfst>=0 ); 4877585f49aSdrh assert( iAmt>=0 ); 4887585f49aSdrh SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 4897585f49aSdrh if( iOfst+iAmt>=512 ){ 4907585f49aSdrh if( (iOfst % iAmt)!=0 ){ 4917585f49aSdrh return SQLITE_IOERR_READ; 4927585f49aSdrh } 4937585f49aSdrh if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ 4947585f49aSdrh return SQLITE_IOERR_READ; 4957585f49aSdrh } 4967585f49aSdrh pFile->szPage = iAmt; 4977585f49aSdrh pgno = 1 + iOfst/iAmt; 4987585f49aSdrh }else{ 4997585f49aSdrh pgno = 1; 5007585f49aSdrh } 5017585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 5027585f49aSdrh got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1); 5037585f49aSdrh if( got<0 ){ 5047585f49aSdrh n = 0; 5057585f49aSdrh }else{ 5067585f49aSdrh aData[got] = 0; 5077585f49aSdrh if( iOfst+iAmt<512 ){ 5087585f49aSdrh n = kvvfsDecode(aData, &aData[1000], 1000); 5097585f49aSdrh if( n>=iOfst+iAmt ){ 5107585f49aSdrh memcpy(zBuf, &aData[1000+iOfst], iAmt); 5117585f49aSdrh n = iAmt; 5127585f49aSdrh }else{ 5137585f49aSdrh n = 0; 5147585f49aSdrh } 5157585f49aSdrh }else{ 5167585f49aSdrh n = kvvfsDecode(aData, zBuf, iAmt); 5177585f49aSdrh } 5187585f49aSdrh } 5197585f49aSdrh if( n<iAmt ){ 5207585f49aSdrh memset(zBuf+n, 0, iAmt-n); 5217585f49aSdrh return SQLITE_IOERR_SHORT_READ; 5227585f49aSdrh } 5237585f49aSdrh return SQLITE_OK; 5247585f49aSdrh } 5257585f49aSdrh 5267585f49aSdrh 5277585f49aSdrh /* 5287585f49aSdrh ** Write into the -journal file. 5297585f49aSdrh */ 5307585f49aSdrh static int kvvfsWriteJrnl( 5317585f49aSdrh sqlite3_file *pProtoFile, 5327585f49aSdrh const void *zBuf, 5337585f49aSdrh int iAmt, 5347585f49aSdrh sqlite_int64 iOfst 5357585f49aSdrh ){ 5367585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 5377585f49aSdrh sqlite3_int64 iEnd = iOfst+iAmt; 5387585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 5397585f49aSdrh if( iEnd>=0x10000000 ) return SQLITE_FULL; 5407585f49aSdrh if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){ 5417585f49aSdrh char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd); 5427585f49aSdrh if( aNew==0 ){ 5437585f49aSdrh return SQLITE_IOERR_NOMEM; 5447585f49aSdrh } 5457585f49aSdrh pFile->aJrnl = aNew; 5467585f49aSdrh if( pFile->nJrnl<iOfst ){ 5477585f49aSdrh memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); 5487585f49aSdrh } 5497585f49aSdrh pFile->nJrnl = iEnd; 5507585f49aSdrh } 5517585f49aSdrh memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); 5527585f49aSdrh return SQLITE_OK; 5537585f49aSdrh } 5547585f49aSdrh 5557585f49aSdrh /* 5567585f49aSdrh ** Write into the database file. 5577585f49aSdrh */ 5587585f49aSdrh static int kvvfsWriteDb( 5597585f49aSdrh sqlite3_file *pProtoFile, 5607585f49aSdrh const void *zBuf, 5617585f49aSdrh int iAmt, 5627585f49aSdrh sqlite_int64 iOfst 5637585f49aSdrh ){ 5647585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 5657585f49aSdrh unsigned int pgno; 5667585f49aSdrh char zKey[30]; 5677585f49aSdrh char aData[131073]; 5687585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 5697585f49aSdrh assert( iAmt>=512 && iAmt<=65536 ); 5707585f49aSdrh assert( (iAmt & (iAmt-1))==0 ); 5717585f49aSdrh pgno = 1 + iOfst/iAmt; 5727585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 5737585f49aSdrh kvvfsEncode(zBuf, iAmt, aData); 5747585f49aSdrh kvstorageWrite(pFile->zClass, zKey, aData); 5757585f49aSdrh if( iOfst+iAmt > pFile->szDb ){ 5767585f49aSdrh pFile->szDb = iOfst + iAmt; 5777585f49aSdrh } 5787585f49aSdrh return SQLITE_OK; 5797585f49aSdrh } 5807585f49aSdrh 5817585f49aSdrh /* 5827585f49aSdrh ** Truncate an kvvfs-file. 5837585f49aSdrh */ 5847585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ 5857585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 5867585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); 5877585f49aSdrh assert( size==0 ); 5887585f49aSdrh kvstorageDelete(pFile->zClass, "jrnl"); 5897585f49aSdrh sqlite3_free(pFile->aJrnl); 5907585f49aSdrh pFile->aJrnl = 0; 5917585f49aSdrh pFile->nJrnl = 0; 5927585f49aSdrh return SQLITE_OK; 5937585f49aSdrh } 5947585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ 5957585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 5967585f49aSdrh if( pFile->szDb>size 5977585f49aSdrh && pFile->szPage>0 5987585f49aSdrh && (size % pFile->szPage)==0 5997585f49aSdrh ){ 6007585f49aSdrh char zKey[50]; 6017585f49aSdrh unsigned int pgno, pgnoMax; 6027585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); 6037585f49aSdrh pgno = 1 + size/pFile->szPage; 6047585f49aSdrh pgnoMax = 2 + pFile->szDb/pFile->szPage; 6057585f49aSdrh while( pgno<=pgnoMax ){ 6067585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 6077585f49aSdrh kvstorageDelete(pFile->zClass, zKey); 6087585f49aSdrh pgno++; 6097585f49aSdrh } 6107585f49aSdrh pFile->szDb = size; 6117585f49aSdrh kvvfsWriteFileSize(pFile, size); 6127585f49aSdrh return SQLITE_OK; 6137585f49aSdrh } 6147585f49aSdrh return SQLITE_IOERR; 6157585f49aSdrh } 6167585f49aSdrh 6177585f49aSdrh /* 6187585f49aSdrh ** Sync an kvvfs-file. 6197585f49aSdrh */ 6207585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ 6217585f49aSdrh int i, n; 6227585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6237585f49aSdrh char *zOut; 6247585f49aSdrh SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); 6257585f49aSdrh if( pFile->nJrnl<=0 ){ 6267585f49aSdrh return kvvfsTruncateJrnl(pProtoFile, 0); 6277585f49aSdrh } 6287585f49aSdrh zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); 6297585f49aSdrh if( zOut==0 ){ 6307585f49aSdrh return SQLITE_IOERR_NOMEM; 6317585f49aSdrh } 6327585f49aSdrh n = pFile->nJrnl; 6337585f49aSdrh i = 0; 6347585f49aSdrh do{ 6357585f49aSdrh zOut[i++] = 'a' + (n%26); 6367585f49aSdrh n /= 26; 6377585f49aSdrh }while( n>0 ); 6387585f49aSdrh zOut[i++] = ' '; 6397585f49aSdrh kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); 6407585f49aSdrh kvstorageWrite(pFile->zClass, "jrnl", zOut); 6417585f49aSdrh sqlite3_free(zOut); 6427585f49aSdrh return SQLITE_OK; 6437585f49aSdrh } 6447585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ 6457585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6467585f49aSdrh SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); 6477585f49aSdrh if( pFile->szDb>0 ){ 6487585f49aSdrh kvvfsWriteFileSize(pFile, pFile->szDb); 6497585f49aSdrh } 6507585f49aSdrh return SQLITE_OK; 6517585f49aSdrh } 6527585f49aSdrh 6537585f49aSdrh /* 6547585f49aSdrh ** Return the current file-size of an kvvfs-file. 6557585f49aSdrh */ 6567585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 6577585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6587585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); 6597585f49aSdrh *pSize = pFile->nJrnl; 6607585f49aSdrh return SQLITE_OK; 6617585f49aSdrh } 6627585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 6637585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6647585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); 6657585f49aSdrh if( pFile->szDb>=0 ){ 6667585f49aSdrh *pSize = pFile->szDb; 6677585f49aSdrh }else{ 6687585f49aSdrh *pSize = kvvfsReadFileSize(pFile); 6697585f49aSdrh } 6707585f49aSdrh return SQLITE_OK; 6717585f49aSdrh } 6727585f49aSdrh 6737585f49aSdrh /* 6747585f49aSdrh ** Lock an kvvfs-file. 6757585f49aSdrh */ 6767585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ 6777585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6787585f49aSdrh assert( !pFile->isJournal ); 6797585f49aSdrh SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); 6807585f49aSdrh 6817585f49aSdrh if( eLock!=SQLITE_LOCK_NONE ){ 6827585f49aSdrh pFile->szDb = kvvfsReadFileSize(pFile); 6837585f49aSdrh } 6847585f49aSdrh return SQLITE_OK; 6857585f49aSdrh } 6867585f49aSdrh 6877585f49aSdrh /* 6887585f49aSdrh ** Unlock an kvvfs-file. 6897585f49aSdrh */ 6907585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ 6917585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6927585f49aSdrh assert( !pFile->isJournal ); 6937585f49aSdrh SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); 6947585f49aSdrh if( eLock==SQLITE_LOCK_NONE ){ 6957585f49aSdrh pFile->szDb = -1; 6967585f49aSdrh } 6977585f49aSdrh return SQLITE_OK; 6987585f49aSdrh } 6997585f49aSdrh 7007585f49aSdrh /* 7017585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file. 7027585f49aSdrh */ 7037585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ 7047585f49aSdrh SQLITE_KV_LOG(("xCheckReservedLock\n")); 7057585f49aSdrh *pResOut = 0; 7067585f49aSdrh return SQLITE_OK; 7077585f49aSdrh } 7087585f49aSdrh 7097585f49aSdrh /* 7107585f49aSdrh ** File control method. For custom operations on an kvvfs-file. 7117585f49aSdrh */ 7127585f49aSdrh static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){ 7137585f49aSdrh return SQLITE_NOTFOUND; 7147585f49aSdrh } 7157585f49aSdrh 7167585f49aSdrh /* 7177585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file. 7187585f49aSdrh */ 7197585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){ 7207585f49aSdrh return 512; 7217585f49aSdrh } 7227585f49aSdrh 7237585f49aSdrh /* 7247585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file. 7257585f49aSdrh */ 7267585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ 7277585f49aSdrh return 0; 7287585f49aSdrh } 7297585f49aSdrh 7307585f49aSdrh /****** sqlite3_vfs methods *************************************************/ 7317585f49aSdrh 7327585f49aSdrh /* 7337585f49aSdrh ** Open an kvvfs file handle. 7347585f49aSdrh */ 7357585f49aSdrh static int kvvfsOpen( 7367585f49aSdrh sqlite3_vfs *pProtoVfs, 7377585f49aSdrh const char *zName, 7387585f49aSdrh sqlite3_file *pProtoFile, 7397585f49aSdrh int flags, 7407585f49aSdrh int *pOutFlags 7417585f49aSdrh ){ 7427585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 7437585f49aSdrh SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); 7447585f49aSdrh if( strcmp(zName, "local")==0 7457585f49aSdrh || strcmp(zName, "session")==0 7467585f49aSdrh ){ 7477585f49aSdrh pFile->isJournal = 0; 7487585f49aSdrh pFile->base.pMethods = &kvvfs_db_io_methods; 7497585f49aSdrh }else 7507585f49aSdrh if( strcmp(zName, "local-journal")==0 7517585f49aSdrh || strcmp(zName, "session-journal")==0 7527585f49aSdrh ){ 7537585f49aSdrh pFile->isJournal = 1; 7547585f49aSdrh pFile->base.pMethods = &kvvfs_jrnl_io_methods; 7557585f49aSdrh }else{ 7567585f49aSdrh return SQLITE_CANTOPEN; 7577585f49aSdrh } 7587585f49aSdrh if( zName[0]=='s' ){ 759*c6c9c6aaSdrh pFile->zClass = "session"; 7607585f49aSdrh }else{ 761*c6c9c6aaSdrh pFile->zClass = "local"; 7627585f49aSdrh } 7637585f49aSdrh pFile->aJrnl = 0; 7647585f49aSdrh pFile->nJrnl = 0; 7657585f49aSdrh pFile->szPage = -1; 7667585f49aSdrh pFile->szDb = -1; 7677585f49aSdrh return SQLITE_OK; 7687585f49aSdrh } 7697585f49aSdrh 7707585f49aSdrh /* 7717585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true, 7727585f49aSdrh ** ensure the file-system modifications are synced to disk before 7737585f49aSdrh ** returning. 7747585f49aSdrh */ 7757585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 7767585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 777*c6c9c6aaSdrh kvstorageDelete("local", "jrnl"); 7787585f49aSdrh }else 7797585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 780*c6c9c6aaSdrh kvstorageDelete("session", "jrnl"); 7817585f49aSdrh } 7827585f49aSdrh return SQLITE_OK; 7837585f49aSdrh } 7847585f49aSdrh 7857585f49aSdrh /* 7867585f49aSdrh ** Test for access permissions. Return true if the requested permission 7877585f49aSdrh ** is available, or false otherwise. 7887585f49aSdrh */ 7897585f49aSdrh static int kvvfsAccess( 7907585f49aSdrh sqlite3_vfs *pProtoVfs, 7917585f49aSdrh const char *zPath, 7927585f49aSdrh int flags, 7937585f49aSdrh int *pResOut 7947585f49aSdrh ){ 7957585f49aSdrh SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); 7967585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 797*c6c9c6aaSdrh *pResOut = kvstorageRead("local", "jrnl", 0, 0)>0; 7987585f49aSdrh }else 7997585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 800*c6c9c6aaSdrh *pResOut = kvstorageRead("session", "jrnl", 0, 0)>0; 8017585f49aSdrh }else 8027585f49aSdrh if( strcmp(zPath, "local")==0 ){ 803*c6c9c6aaSdrh *pResOut = kvstorageRead("local", "sz", 0, 0)>0; 8047585f49aSdrh }else 8057585f49aSdrh if( strcmp(zPath, "session")==0 ){ 806*c6c9c6aaSdrh *pResOut = kvstorageRead("session", "sz", 0, 0)>0; 8077585f49aSdrh }else 8087585f49aSdrh { 8097585f49aSdrh *pResOut = 0; 8107585f49aSdrh } 8117585f49aSdrh SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); 8127585f49aSdrh return SQLITE_OK; 8137585f49aSdrh } 8147585f49aSdrh 8157585f49aSdrh /* 8167585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding 8177585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer 8187585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes. 8197585f49aSdrh */ 8207585f49aSdrh static int kvvfsFullPathname( 8217585f49aSdrh sqlite3_vfs *pVfs, 8227585f49aSdrh const char *zPath, 8237585f49aSdrh int nOut, 8247585f49aSdrh char *zOut 8257585f49aSdrh ){ 8267585f49aSdrh size_t nPath = strlen(zPath); 8277585f49aSdrh SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); 8287585f49aSdrh if( nOut<nPath+1 ) nPath = nOut - 1; 8297585f49aSdrh memcpy(zOut, zPath, nPath); 8307585f49aSdrh zOut[nPath] = 0; 8317585f49aSdrh return SQLITE_OK; 8327585f49aSdrh } 8337585f49aSdrh 8347585f49aSdrh /* 8357585f49aSdrh ** Open the dynamic library located at zPath and return a handle. 8367585f49aSdrh */ 8377585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 8387585f49aSdrh return 0; 8397585f49aSdrh } 8407585f49aSdrh 8417585f49aSdrh /* 8427585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of 8437585f49aSdrh ** random data. 8447585f49aSdrh */ 8457585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 8467585f49aSdrh memset(zBufOut, 0, nByte); 8477585f49aSdrh return nByte; 8487585f49aSdrh } 8497585f49aSdrh 8507585f49aSdrh /* 8517585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds 8527585f49aSdrh ** actually slept. 8537585f49aSdrh */ 8547585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){ 8557585f49aSdrh return SQLITE_OK; 8567585f49aSdrh } 8577585f49aSdrh 8587585f49aSdrh /* 8597585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut. 8607585f49aSdrh */ 8617585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 8627585f49aSdrh *pTimeOut = 2459829.13362986; 8637585f49aSdrh return SQLITE_OK; 8647585f49aSdrh } 8657585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ 8667585f49aSdrh *pTimeOut = (sqlite3_int64)(2459829.13362986*86400000.0); 8677585f49aSdrh return SQLITE_OK; 8687585f49aSdrh } 8697585f49aSdrh 8707585f49aSdrh /* 8717585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS. 8727585f49aSdrh */ 8737585f49aSdrh int sqlite3_os_init(void){ 8747585f49aSdrh return sqlite3_vfs_register(&kvvfs_vfs, 1); 8757585f49aSdrh } 8767585f49aSdrh int sqlite3_os_end(void){ 8777585f49aSdrh return SQLITE_OK; 8787585f49aSdrh } 8797585f49aSdrh #endif /* SQLITE_OS_KV */ 880