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*/
42typedef 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*/
47struct 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*/
60static int kvvfsClose(sqlite3_file*);
61static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
62static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
63static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
64static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
65static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size);
66static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size);
67static int kvvfsSyncDb(sqlite3_file*, int flags);
68static int kvvfsSyncJrnl(sqlite3_file*, int flags);
69static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize);
70static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize);
71static int kvvfsLock(sqlite3_file*, int);
72static int kvvfsUnlock(sqlite3_file*, int);
73static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
74static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg);
75static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg);
76static int kvvfsSectorSize(sqlite3_file*);
77static int kvvfsDeviceCharacteristics(sqlite3_file*);
78
79/*
80** Methods for sqlite3_vfs
81*/
82static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
83static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
84static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
85static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
86static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
87static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
88static int kvvfsSleep(sqlite3_vfs*, int microseconds);
89static int kvvfsCurrentTime(sqlite3_vfs*, double*);
90static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
91
92static 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*/
116static 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*/
140static 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*/
169static int kvstorageWrite(const char*, const char *zKey, const char *zData);
170static int kvstorageDelete(const char*, const char *zKey);
171static 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*/
178static 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*/
193static 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*/
218static 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*/
239static 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*/
292typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
293struct 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
311const
312#endif
313sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
314kvstorageRead,
315kvstorageWrite,
316kvstorageDelete,
317KVSTORAGE_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*/
344static 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
371static 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*/
397static 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*/
442static 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*/
473static 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}
479static 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*/
490static 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*/
502static 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*/
534static 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*/
591static 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*/
619static 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*/
649static 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}
659static 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*/
684static 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}
708static 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*/
715static 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}
721static 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*/
735static 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*/
749static 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*/
762static 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*/
771static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){
772 SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op));
773 return SQLITE_NOTFOUND;
774}
775static 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*/
792static int kvvfsSectorSize(sqlite3_file *pFile){
793 return 512;
794}
795
796/*
797** Return the device characteristic flags supported by an kvvfs-file.
798*/
799static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){
800 return 0;
801}
802
803/****** sqlite3_vfs methods *************************************************/
804
805/*
806** Open an kvvfs file handle.
807*/
808static 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*/
849static 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*/
863static 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*/
894static 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*/
915static 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*/
923static 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*/
932static 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*/
939static 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>
947static 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*/
960int sqlite3_os_init(void){
961 return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1);
962}
963int 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)
969int sqlite3KvvfsInit(void){
970 return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0);
971}
972#endif
973