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 #define KVSTORAGE_KEY_SZ 32 170 171 /* Expand the key name with an appropriate prefix and put the result 172 ** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least 173 ** KVSTORAGE_KEY_SZ bytes. 174 */ 175 static void kvstorageMakeKey( 176 const char *zClass, 177 const char *zKeyIn, 178 char *zKeyOut 179 ){ 180 sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); 181 } 182 183 #ifdef __EMSCRIPTEN__ 184 /* Provide Emscripten-based impls of kvstorageWrite/Read/Delete()... */ 185 #include <emscripten.h> 186 #include <emscripten/console.h> 187 188 /* 189 ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not 190 ** Emscripten-specific. It explicitly includes marked functions for 191 ** export into the target wasm file without requiring explicit listing 192 ** of those functions in Emscripten's -sEXPORTED_FUNCTIONS=... list 193 ** (or equivalent in other build platforms). Any function with neither 194 ** this attribute nor which is listed as an explicit export will not 195 ** be exported from the wasm file (but may still be used internally 196 ** within the wasm file). 197 ** 198 ** The functions in this filewhich require exporting are marked with 199 ** this flag. They may also be added to any explicit build-time export 200 ** list but need not be. All of these APIs are intended for use only 201 ** within the project's own JS/WASM code, and not by client code, so 202 ** an argument can be made for reducing their visibility by not 203 ** including them in any build-time export lists. 204 ** 205 ** 2022-09-11: it's not yet _proven_ that this approach works in 206 ** non-Emscripten builds. If not, such builds will need to export 207 ** those using the --export=... wasm-ld flag (or equivalent). As of 208 ** this writing we are tied to Emscripten for various reasons 209 ** and cannot test the library with other build environments. 210 */ 211 #define WASM_KEEP __attribute__((used,visibility("default"))) 212 /* 213 ** An internal level of indirection for accessing the static 214 ** kvstorageMakeKey() from EM_JS()-generated functions. This must be 215 ** made available for export via Emscripten but is not intended to be 216 ** used from client code. If called with a NULL zKeyOut it is a no-op. 217 ** It returns KVSTORAGE_KEY_SZ, so JS code (which cannot see that 218 ** constant) may call it with NULL arguments to get the size of the 219 ** allocation they'll need for a kvvfs key. 220 ** 221 ** Maintenance reminder: Emscripten will install this in the Module 222 ** init scope and will prefix its name with "_". 223 */ 224 WASM_KEEP 225 int sqlite3_wasm__kvvfsMakeKey(const char *zClass, const char *zKeyIn, 226 char *zKeyOut){ 227 if( 0!=zKeyOut ) kvstorageMakeKey(zClass, zKeyIn, zKeyOut); 228 return KVSTORAGE_KEY_SZ; 229 } 230 /* 231 ** Internal helper for kvstorageWrite/Read/Delete() which creates a 232 ** storage key for the given zClass/zKeyIn combination. Returns a 233 ** pointer to the key: a C string allocated on the WASM stack, or 0 if 234 ** allocation fails. It is up to the caller to save/restore the stack 235 ** before/after this operation. 236 */ 237 EM_JS(const char *, kvstorageMakeKeyOnJSStack, 238 (const char *zClass, const char *zKeyIn),{ 239 if( 0==zClass || 0==zKeyIn) return 0; 240 const zXKey = stackAlloc(_sqlite3_wasm__kvvfsMakeKey(0,0,0)); 241 if(zXKey) _sqlite3_wasm__kvvfsMakeKey(zClass, zKeyIn, zXKey); 242 return zXKey; 243 }); 244 245 /* 246 ** JS impl of kvstorageWrite(). Main docs are in the C impl. This impl 247 ** writes zData to the global sessionStorage (if zClass starts with 248 ** 's') or localStorage, using a storage key derived from zClass and 249 ** zKey. 250 */ 251 EM_JS(int, kvstorageWrite, 252 (const char *zClass, const char *zKey, const char *zData),{ 253 const stack = stackSave(); 254 try { 255 const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); 256 if(!zXKey) return 1/*OOM*/; 257 const jKey = UTF8ToString(zXKey); 258 /** 259 We could simplify this function and eliminate the 260 kvstorageMakeKey() symbol acrobatics if we'd simply hard-code 261 the key algo into the 3 functions which need it: 262 263 const jKey = "kvvfs-"+UTF8ToString(zClass)+"-"+UTF8ToString(zKey); 264 */ 265 ((115/*=='s'*/===getValue(zClass)) 266 ? sessionStorage : localStorage).setItem(jKey, UTF8ToString(zData)); 267 }catch(e){ 268 console.error("kvstorageWrite()",e); 269 return 1; // Can't access SQLITE_xxx from here 270 }finally{ 271 stackRestore(stack); 272 } 273 return 0; 274 }); 275 276 /* 277 ** JS impl of kvstorageDelete(). Main docs are in the C impl. This 278 ** impl generates a key derived from zClass and zKey, and removes the 279 ** matching entry (if any) from global sessionStorage (if zClass 280 ** starts with 's') or localStorage. 281 */ 282 EM_JS(int, kvstorageDelete, 283 (const char *zClass, const char *zKey),{ 284 const stack = stackSave(); 285 try { 286 const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); 287 if(!zXKey) return 1/*OOM*/; 288 const jKey = UTF8ToString(zXKey); 289 ((115/*=='s'*/===getValue(zClass)) 290 ? sessionStorage : localStorage).removeItem(jKey); 291 }catch(e){ 292 console.error("kvstorageDelete()",e); 293 return 1; 294 }finally{ 295 stackRestore(stack); 296 } 297 return 0; 298 }); 299 300 /* 301 ** JS impl of kvstorageRead(). Main docs are in the C impl. This impl 302 ** reads its data from the global sessionStorage (if zClass starts 303 ** with 's') or localStorage, using a storage key derived from zClass 304 ** and zKey. 305 */ 306 EM_JS(int, kvstorageRead, 307 (const char *zClass, const char *zKey, char *zBuf, int nBuf),{ 308 const stack = stackSave(); 309 try { 310 const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); 311 if(!zXKey) return -3/*OOM*/; 312 const jKey = UTF8ToString(zXKey); 313 const jV = ((115/*=='s'*/===getValue(zClass)) 314 ? sessionStorage : localStorage).getItem(jKey); 315 if(!jV) return -1; 316 const nV = jV.length /* Note that we are relying 100% on v being 317 ASCII so that jV.length is equal to the 318 C-string's byte length. */; 319 if(nBuf<=0) return nV; 320 else if(1===nBuf){ 321 setValue(zBuf, 0); 322 return nV; 323 } 324 const zV = allocateUTF8OnStack(jV); 325 if(nBuf > nV + 1) nBuf = nV + 1; 326 HEAPU8.copyWithin(zBuf, zV, zV + nBuf - 1); 327 setValue( zBuf + nBuf - 1, 0 ); 328 return nBuf - 1; 329 }catch(e){ 330 console.error("kvstorageRead()",e); 331 return -2; 332 }finally{ 333 stackRestore(stack); 334 } 335 }); 336 337 /* 338 ** This function exists for (1) WASM testing purposes and (2) as a 339 ** hook to get Emscripten to export several EM_JS()-generated 340 ** functions (if we don't reference them from exported C functions 341 ** then they get stripped away at build time). It is not part of the 342 ** public API and its signature and semantics may change at any time. 343 ** It's not even part of the private API, for that matter - it's part 344 ** of the Emscripten C/JS/WASM glue. 345 */ 346 WASM_KEEP 347 int sqlite3__wasm_emjs_kvvfs(int whichOp){ 348 int rc = 0; 349 const char * zClass = 350 "sezzion" /*don't collide with "session" records!*/; 351 const char * zKey = "hello"; 352 switch( whichOp ){ 353 case 0: break; 354 case 1: 355 rc = kvstorageWrite(zClass, zKey, "world"); 356 break; 357 case 2: { 358 char buffer[128] = {0}; 359 char * zBuf = &buffer[0]; 360 rc = kvstorageRead(zClass, zKey, zBuf, (int)sizeof(buffer)); 361 emscripten_console_logf("kvstorageRead()=%d %s\n", rc, zBuf); 362 break; 363 } 364 case 3: 365 kvstorageDelete(zClass, zKey); 366 break; 367 case 4: 368 kvstorageMakeKeyOnJSStack(0,0); 369 break; 370 default: break; 371 } 372 return rc; 373 } 374 375 #undef WASM_KEEP 376 #else /* end ifdef __EMSCRIPTEN__ */ 377 /* Forward declarations for the low-level storage engine 378 */ 379 static int kvstorageWrite(const char*, const char *zKey, const char *zData); 380 static int kvstorageDelete(const char*, const char *zKey); 381 static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); 382 383 /* Write content into a key. zClass is the particular namespace of the 384 ** underlying key/value store to use - either "local" or "session". 385 ** 386 ** Both zKey and zData are zero-terminated pure text strings. 387 ** 388 ** Return the number of errors. 389 */ 390 static int kvstorageWrite( 391 const char *zClass, 392 const char *zKey, 393 const char *zData 394 ){ 395 FILE *fd; 396 char zXKey[KVSTORAGE_KEY_SZ]; 397 kvstorageMakeKey(zClass, zKey, zXKey); 398 fd = fopen(zXKey, "wb"); 399 if( fd ){ 400 SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, 401 (int)strlen(zData), zData, 402 strlen(zData)>50 ? "..." : "")); 403 fputs(zData, fd); 404 fclose(fd); 405 return 0; 406 }else{ 407 return 1; 408 } 409 } 410 411 /* Delete a key (with its corresponding data) from the key/value 412 ** namespace given by zClass. If the key does not previously exist, 413 ** this routine is a no-op. 414 */ 415 static int kvstorageDelete(const char *zClass, const char *zKey){ 416 char zXKey[KVSTORAGE_KEY_SZ]; 417 kvstorageMakeKey(zClass, zKey, zXKey); 418 unlink(zXKey); 419 SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); 420 return 0; 421 } 422 423 /* Read the value associated with a zKey from the key/value namespace given 424 ** by zClass and put the text data associated with that key in the first 425 ** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large 426 ** enough to hold it all. The value put into zBuf must always be zero 427 ** terminated, even if it gets truncated because nBuf is not large enough. 428 ** 429 ** Return the total number of bytes in the data, without truncation, and 430 ** not counting the final zero terminator. Return -1 if the key does 431 ** not exist. 432 ** 433 ** If nBuf<=0 then this routine simply returns the size of the data without 434 ** actually reading it. 435 */ 436 static int kvstorageRead( 437 const char *zClass, 438 const char *zKey, 439 char *zBuf, 440 int nBuf 441 ){ 442 FILE *fd; 443 struct stat buf; 444 char zXKey[KVSTORAGE_KEY_SZ]; 445 kvstorageMakeKey(zClass, zKey, zXKey); 446 if( access(zXKey, R_OK)!=0 447 || stat(zXKey, &buf)!=0 448 || !S_ISREG(buf.st_mode) 449 ){ 450 SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); 451 return -1; 452 } 453 if( nBuf<=0 ){ 454 return (int)buf.st_size; 455 }else if( nBuf==1 ){ 456 zBuf[0] = 0; 457 SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, 458 (int)buf.st_size)); 459 return (int)buf.st_size; 460 } 461 if( nBuf > buf.st_size + 1 ){ 462 nBuf = buf.st_size + 1; 463 } 464 fd = fopen(zXKey, "rb"); 465 if( fd==0 ){ 466 SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); 467 return -1; 468 }else{ 469 sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); 470 fclose(fd); 471 zBuf[n] = 0; 472 SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, 473 n, zBuf, n>50 ? "..." : "")); 474 return (int)n; 475 } 476 } 477 #endif /* ifdef __EMSCRIPTEN__ */ 478 479 /****** Utility subroutines ************************************************/ 480 481 /* 482 ** Encode binary into the text encoded used to persist on disk. 483 ** The output text is stored in aOut[], which must be at least 484 ** nData+1 bytes in length. 485 ** 486 ** Return the actual length of the encoded text, not counting the 487 ** zero terminator at the end. 488 ** 489 ** Encoding format 490 ** --------------- 491 ** 492 ** * Non-zero bytes are encoded as upper-case hexadecimal 493 ** 494 ** * A sequence of one or more zero-bytes that are not at the 495 ** beginning of the buffer are encoded as a little-endian 496 ** base-26 number using a..z. "a" means 0. "b" means 1, 497 ** "z" means 25. "ab" means 26. "ac" means 52. And so forth. 498 ** 499 ** * Because there is no overlap between the encoding characters 500 ** of hexadecimal and base-26 numbers, it is always clear where 501 ** one stops and the next begins. 502 */ 503 static int kvvfsEncode(const char *aData, int nData, char *aOut){ 504 int i, j; 505 const unsigned char *a = (const unsigned char*)aData; 506 for(i=j=0; i<nData; i++){ 507 unsigned char c = a[i]; 508 if( c!=0 ){ 509 aOut[j++] = "0123456789ABCDEF"[c>>4]; 510 aOut[j++] = "0123456789ABCDEF"[c&0xf]; 511 }else{ 512 /* A sequence of 1 or more zeros is stored as a little-endian 513 ** base-26 number using a..z as the digits. So one zero is "b". 514 ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", 515 ** and so forth. 516 */ 517 int k; 518 for(k=1; i+k<nData && a[i+k]==0; k++){} 519 i += k-1; 520 while( k>0 ){ 521 aOut[j++] = 'a'+(k%26); 522 k /= 26; 523 } 524 } 525 } 526 aOut[j] = 0; 527 return j; 528 } 529 530 static const signed char kvvfsHexValue[256] = { 531 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 532 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 533 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 534 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 535 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 536 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 537 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 538 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 539 540 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 541 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 542 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 543 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 544 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 545 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 546 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 547 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 548 }; 549 550 /* 551 ** Decode the text encoding back to binary. The binary content is 552 ** written into pOut, which must be at least nOut bytes in length. 553 ** 554 ** The return value is the number of bytes actually written into aOut[]. 555 */ 556 static int kvvfsDecode(const char *a, char *aOut, int nOut){ 557 int i, j; 558 int c; 559 const unsigned char *aIn = (const unsigned char*)a; 560 i = 0; 561 j = 0; 562 while( 1 ){ 563 c = kvvfsHexValue[aIn[i]]; 564 if( c<0 ){ 565 int n = 0; 566 int mult = 1; 567 c = aIn[i]; 568 if( c==0 ) break; 569 while( c>='a' && c<='z' ){ 570 n += (c - 'a')*mult; 571 mult *= 26; 572 c = aIn[++i]; 573 } 574 if( j+n>nOut ) return -1; 575 memset(&aOut[j], 0, n); 576 j += n; 577 c = aIn[i]; 578 if( c==0 ) break; 579 }else{ 580 aOut[j] = c<<4; 581 c = kvvfsHexValue[aIn[++i]]; 582 if( c<0 ) break; 583 aOut[j++] += c; 584 i++; 585 } 586 } 587 return j; 588 } 589 590 /* 591 ** Decode a complete journal file. Allocate space in pFile->aJrnl 592 ** and store the decoding there. Or leave pFile->aJrnl set to NULL 593 ** if an error is encountered. 594 ** 595 ** The first few characters of the text encoding will be a little-endian 596 ** base-26 number (digits a..z) that is the total number of bytes 597 ** in the decoded journal file image. This base-26 number is followed 598 ** by a single space, then the encoding of the journal. The space 599 ** separator is required to act as a terminator for the base-26 number. 600 */ 601 static void kvvfsDecodeJournal( 602 KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ 603 const char *zTxt, /* Text encoding. Zero-terminated */ 604 int nTxt /* Bytes in zTxt, excluding zero terminator */ 605 ){ 606 unsigned int n = 0; 607 int c, i, mult; 608 i = 0; 609 mult = 1; 610 while( (c = zTxt[i++])>='a' && c<='z' ){ 611 n += (zTxt[i] - 'a')*mult; 612 mult *= 26; 613 } 614 sqlite3_free(pFile->aJrnl); 615 pFile->aJrnl = sqlite3_malloc64( n ); 616 if( pFile->aJrnl==0 ){ 617 pFile->nJrnl = 0; 618 return; 619 } 620 pFile->nJrnl = n; 621 n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); 622 if( n<pFile->nJrnl ){ 623 sqlite3_free(pFile->aJrnl); 624 pFile->aJrnl = 0; 625 pFile->nJrnl = 0; 626 } 627 } 628 629 /* 630 ** Read or write the "sz" element, containing the database file size. 631 */ 632 static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ 633 char zData[50]; 634 zData[0] = 0; 635 kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1); 636 return strtoll(zData, 0, 0); 637 } 638 static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ 639 char zData[50]; 640 sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); 641 return kvstorageWrite(pFile->zClass, "sz", zData); 642 } 643 644 /****** sqlite3_io_methods methods ******************************************/ 645 646 /* 647 ** Close an kvvfs-file. 648 */ 649 static int kvvfsClose(sqlite3_file *pProtoFile){ 650 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 651 652 SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, 653 pFile->isJournal ? "journal" : "db")); 654 sqlite3_free(pFile->aJrnl); 655 return SQLITE_OK; 656 } 657 658 /* 659 ** Read from the -journal file. 660 */ 661 static int kvvfsReadJrnl( 662 sqlite3_file *pProtoFile, 663 void *zBuf, 664 int iAmt, 665 sqlite_int64 iOfst 666 ){ 667 KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 668 assert( pFile->isJournal ); 669 SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 670 if( pFile->aJrnl==0 ){ 671 int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); 672 char *aTxt; 673 if( szTxt<=4 ){ 674 return SQLITE_IOERR; 675 } 676 aTxt = sqlite3_malloc64( szTxt+1 ); 677 if( aTxt==0 ) return SQLITE_NOMEM; 678 kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); 679 kvvfsDecodeJournal(pFile, aTxt, szTxt); 680 sqlite3_free(aTxt); 681 if( pFile->aJrnl==0 ) return SQLITE_IOERR; 682 } 683 if( iOfst+iAmt>pFile->nJrnl ){ 684 return SQLITE_IOERR_SHORT_READ; 685 } 686 memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); 687 return SQLITE_OK; 688 } 689 690 /* 691 ** Read from the database file. 692 */ 693 static int kvvfsReadDb( 694 sqlite3_file *pProtoFile, 695 void *zBuf, 696 int iAmt, 697 sqlite_int64 iOfst 698 ){ 699 KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 700 unsigned int pgno; 701 int got, n; 702 char zKey[30]; 703 char aData[133073]; 704 assert( iOfst>=0 ); 705 assert( iAmt>=0 ); 706 SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 707 if( iOfst+iAmt>=512 ){ 708 if( (iOfst % iAmt)!=0 ){ 709 return SQLITE_IOERR_READ; 710 } 711 if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ 712 return SQLITE_IOERR_READ; 713 } 714 pFile->szPage = iAmt; 715 pgno = 1 + iOfst/iAmt; 716 }else{ 717 pgno = 1; 718 } 719 sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 720 got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1); 721 if( got<0 ){ 722 n = 0; 723 }else{ 724 aData[got] = 0; 725 if( iOfst+iAmt<512 ){ 726 int k = iOfst+iAmt; 727 aData[k*2] = 0; 728 n = kvvfsDecode(aData, &aData[2000], sizeof(aData)-2000); 729 if( n>=iOfst+iAmt ){ 730 memcpy(zBuf, &aData[2000+iOfst], iAmt); 731 n = iAmt; 732 }else{ 733 n = 0; 734 } 735 }else{ 736 n = kvvfsDecode(aData, zBuf, iAmt); 737 } 738 } 739 if( n<iAmt ){ 740 memset(zBuf+n, 0, iAmt-n); 741 return SQLITE_IOERR_SHORT_READ; 742 } 743 return SQLITE_OK; 744 } 745 746 747 /* 748 ** Write into the -journal file. 749 */ 750 static int kvvfsWriteJrnl( 751 sqlite3_file *pProtoFile, 752 const void *zBuf, 753 int iAmt, 754 sqlite_int64 iOfst 755 ){ 756 KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 757 sqlite3_int64 iEnd = iOfst+iAmt; 758 SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 759 if( iEnd>=0x10000000 ) return SQLITE_FULL; 760 if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){ 761 char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd); 762 if( aNew==0 ){ 763 return SQLITE_IOERR_NOMEM; 764 } 765 pFile->aJrnl = aNew; 766 if( pFile->nJrnl<iOfst ){ 767 memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); 768 } 769 pFile->nJrnl = iEnd; 770 } 771 memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); 772 return SQLITE_OK; 773 } 774 775 /* 776 ** Write into the database file. 777 */ 778 static int kvvfsWriteDb( 779 sqlite3_file *pProtoFile, 780 const void *zBuf, 781 int iAmt, 782 sqlite_int64 iOfst 783 ){ 784 KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 785 unsigned int pgno; 786 char zKey[30]; 787 char aData[131073]; 788 SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 789 assert( iAmt>=512 && iAmt<=65536 ); 790 assert( (iAmt & (iAmt-1))==0 ); 791 pgno = 1 + iOfst/iAmt; 792 sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 793 kvvfsEncode(zBuf, iAmt, aData); 794 if( kvstorageWrite(pFile->zClass, zKey, aData) ){ 795 return SQLITE_IOERR; 796 } 797 if( iOfst+iAmt > pFile->szDb ){ 798 pFile->szDb = iOfst + iAmt; 799 } 800 return SQLITE_OK; 801 } 802 803 /* 804 ** Truncate an kvvfs-file. 805 */ 806 static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ 807 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 808 SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); 809 assert( size==0 ); 810 kvstorageDelete(pFile->zClass, "jrnl"); 811 sqlite3_free(pFile->aJrnl); 812 pFile->aJrnl = 0; 813 pFile->nJrnl = 0; 814 return SQLITE_OK; 815 } 816 static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ 817 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 818 if( pFile->szDb>size 819 && pFile->szPage>0 820 && (size % pFile->szPage)==0 821 ){ 822 char zKey[50]; 823 unsigned int pgno, pgnoMax; 824 SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); 825 pgno = 1 + size/pFile->szPage; 826 pgnoMax = 2 + pFile->szDb/pFile->szPage; 827 while( pgno<=pgnoMax ){ 828 sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 829 kvstorageDelete(pFile->zClass, zKey); 830 pgno++; 831 } 832 pFile->szDb = size; 833 return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; 834 } 835 return SQLITE_IOERR; 836 } 837 838 /* 839 ** Sync an kvvfs-file. 840 */ 841 static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ 842 int i, n; 843 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 844 char *zOut; 845 SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); 846 if( pFile->nJrnl<=0 ){ 847 return kvvfsTruncateJrnl(pProtoFile, 0); 848 } 849 zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); 850 if( zOut==0 ){ 851 return SQLITE_IOERR_NOMEM; 852 } 853 n = pFile->nJrnl; 854 i = 0; 855 do{ 856 zOut[i++] = 'a' + (n%26); 857 n /= 26; 858 }while( n>0 ); 859 zOut[i++] = ' '; 860 kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); 861 i = kvstorageWrite(pFile->zClass, "jrnl", zOut); 862 sqlite3_free(zOut); 863 return i ? SQLITE_IOERR : SQLITE_OK; 864 } 865 static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ 866 return SQLITE_OK; 867 } 868 869 /* 870 ** Return the current file-size of an kvvfs-file. 871 */ 872 static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 873 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 874 SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); 875 *pSize = pFile->nJrnl; 876 return SQLITE_OK; 877 } 878 static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 879 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 880 SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); 881 if( pFile->szDb>=0 ){ 882 *pSize = pFile->szDb; 883 }else{ 884 *pSize = kvvfsReadFileSize(pFile); 885 } 886 return SQLITE_OK; 887 } 888 889 /* 890 ** Lock an kvvfs-file. 891 */ 892 static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ 893 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 894 assert( !pFile->isJournal ); 895 SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); 896 897 if( eLock!=SQLITE_LOCK_NONE ){ 898 pFile->szDb = kvvfsReadFileSize(pFile); 899 } 900 return SQLITE_OK; 901 } 902 903 /* 904 ** Unlock an kvvfs-file. 905 */ 906 static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ 907 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 908 assert( !pFile->isJournal ); 909 SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); 910 if( eLock==SQLITE_LOCK_NONE ){ 911 pFile->szDb = -1; 912 } 913 return SQLITE_OK; 914 } 915 916 /* 917 ** Check if another file-handle holds a RESERVED lock on an kvvfs-file. 918 */ 919 static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ 920 SQLITE_KV_LOG(("xCheckReservedLock\n")); 921 *pResOut = 0; 922 return SQLITE_OK; 923 } 924 925 /* 926 ** File control method. For custom operations on an kvvfs-file. 927 */ 928 static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){ 929 SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op)); 930 return SQLITE_NOTFOUND; 931 } 932 static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){ 933 SQLITE_KV_LOG(("xFileControl(%d) on database\n", op)); 934 if( op==SQLITE_FCNTL_SYNC ){ 935 KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 936 int rc = SQLITE_OK; 937 SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); 938 if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ 939 rc = SQLITE_IOERR; 940 } 941 return rc; 942 } 943 return SQLITE_NOTFOUND; 944 } 945 946 /* 947 ** Return the sector-size in bytes for an kvvfs-file. 948 */ 949 static int kvvfsSectorSize(sqlite3_file *pFile){ 950 return 512; 951 } 952 953 /* 954 ** Return the device characteristic flags supported by an kvvfs-file. 955 */ 956 static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ 957 return 0; 958 } 959 960 /****** sqlite3_vfs methods *************************************************/ 961 962 /* 963 ** Open an kvvfs file handle. 964 */ 965 static int kvvfsOpen( 966 sqlite3_vfs *pProtoVfs, 967 const char *zName, 968 sqlite3_file *pProtoFile, 969 int flags, 970 int *pOutFlags 971 ){ 972 KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 973 SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); 974 if( strcmp(zName, "local")==0 975 || strcmp(zName, "session")==0 976 ){ 977 pFile->isJournal = 0; 978 pFile->base.pMethods = &kvvfs_db_io_methods; 979 }else 980 if( strcmp(zName, "local-journal")==0 981 || strcmp(zName, "session-journal")==0 982 ){ 983 pFile->isJournal = 1; 984 pFile->base.pMethods = &kvvfs_jrnl_io_methods; 985 }else{ 986 return SQLITE_CANTOPEN; 987 } 988 if( zName[0]=='s' ){ 989 pFile->zClass = "session"; 990 }else{ 991 pFile->zClass = "local"; 992 } 993 pFile->aJrnl = 0; 994 pFile->nJrnl = 0; 995 pFile->szPage = -1; 996 pFile->szDb = -1; 997 return SQLITE_OK; 998 } 999 1000 /* 1001 ** Delete the file located at zPath. If the dirSync argument is true, 1002 ** ensure the file-system modifications are synced to disk before 1003 ** returning. 1004 */ 1005 static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 1006 if( strcmp(zPath, "local-journal")==0 ){ 1007 kvstorageDelete("local", "jrnl"); 1008 }else 1009 if( strcmp(zPath, "session-journal")==0 ){ 1010 kvstorageDelete("session", "jrnl"); 1011 } 1012 return SQLITE_OK; 1013 } 1014 1015 /* 1016 ** Test for access permissions. Return true if the requested permission 1017 ** is available, or false otherwise. 1018 */ 1019 static int kvvfsAccess( 1020 sqlite3_vfs *pProtoVfs, 1021 const char *zPath, 1022 int flags, 1023 int *pResOut 1024 ){ 1025 SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); 1026 if( strcmp(zPath, "local-journal")==0 ){ 1027 *pResOut = kvstorageRead("local", "jrnl", 0, 0)>0; 1028 }else 1029 if( strcmp(zPath, "session-journal")==0 ){ 1030 *pResOut = kvstorageRead("session", "jrnl", 0, 0)>0; 1031 }else 1032 if( strcmp(zPath, "local")==0 ){ 1033 *pResOut = kvstorageRead("local", "sz", 0, 0)>0; 1034 }else 1035 if( strcmp(zPath, "session")==0 ){ 1036 *pResOut = kvstorageRead("session", "sz", 0, 0)>0; 1037 }else 1038 { 1039 *pResOut = 0; 1040 } 1041 SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); 1042 return SQLITE_OK; 1043 } 1044 1045 /* 1046 ** Populate buffer zOut with the full canonical pathname corresponding 1047 ** to the pathname in zPath. zOut is guaranteed to point to a buffer 1048 ** of at least (INST_MAX_PATHNAME+1) bytes. 1049 */ 1050 static int kvvfsFullPathname( 1051 sqlite3_vfs *pVfs, 1052 const char *zPath, 1053 int nOut, 1054 char *zOut 1055 ){ 1056 size_t nPath; 1057 #ifdef SQLITE_OS_KV_ALWAYS_LOCAL 1058 zPath = "local"; 1059 #endif 1060 nPath = strlen(zPath); 1061 SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); 1062 if( nOut<nPath+1 ) nPath = nOut - 1; 1063 memcpy(zOut, zPath, nPath); 1064 zOut[nPath] = 0; 1065 return SQLITE_OK; 1066 } 1067 1068 /* 1069 ** Open the dynamic library located at zPath and return a handle. 1070 */ 1071 static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 1072 return 0; 1073 } 1074 1075 /* 1076 ** Populate the buffer pointed to by zBufOut with nByte bytes of 1077 ** random data. 1078 */ 1079 static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 1080 memset(zBufOut, 0, nByte); 1081 return nByte; 1082 } 1083 1084 /* 1085 ** Sleep for nMicro microseconds. Return the number of microseconds 1086 ** actually slept. 1087 */ 1088 static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){ 1089 return SQLITE_OK; 1090 } 1091 1092 /* 1093 ** Return the current time as a Julian Day number in *pTimeOut. 1094 */ 1095 static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 1096 sqlite3_int64 i = 0; 1097 int rc; 1098 rc = kvvfsCurrentTimeInt64(0, &i); 1099 *pTimeOut = i/86400000.0; 1100 return rc; 1101 } 1102 #include <sys/time.h> 1103 static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ 1104 static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; 1105 struct timeval sNow; 1106 (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ 1107 *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; 1108 return SQLITE_OK; 1109 } 1110 #endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */ 1111 1112 #if SQLITE_OS_KV 1113 /* 1114 ** This routine is called initialize the KV-vfs as the default VFS. 1115 */ 1116 int sqlite3_os_init(void){ 1117 return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1); 1118 } 1119 int sqlite3_os_end(void){ 1120 return SQLITE_OK; 1121 } 1122 #endif /* SQLITE_OS_KV */ 1123 1124 #if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) 1125 int sqlite3KvvfsInit(void){ 1126 return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0); 1127 } 1128 #endif 1129