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