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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | } |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | } |
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 | */ |
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 | } |
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 | */ |
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 | } |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | } |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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> |
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 | */ |
960 | int sqlite3_os_init(void){ |
961 | return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1); |
962 | } |
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) |
969 | int sqlite3KvvfsInit(void){ |
970 | return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0); |
971 | } |
972 | #endif |
973 | |