From ba6367543d882f6f0562629130461004f90a0728 Mon Sep 17 00:00:00 2001 From: Stephen Lombardo Date: Wed, 13 May 2020 16:33:13 -0400 Subject: [PATCH] reverse SQLite check-in 5a877221 --- src/attach.c | 36 ++++++++++ src/backup.c | 31 +++++++++ src/btree.c | 6 ++ src/btreeInt.h | 3 + src/ctime.c | 3 + src/global.c | 9 ++- src/main.c | 38 +++++++++++ src/pager.c | 155 +++++++++++++++++++++++++++++++++++++++++-- src/pager.h | 7 ++ src/pragma.c | 50 +++++++++++++- src/pragma.h | 133 ++++++++++++++++++++++++------------- src/sqlite.h.in | 45 +++++++++++++ src/sqliteInt.h | 6 +- src/tclsqlite.c | 32 ++++++++- src/test1.c | 28 ++++++++ src/test_config.c | 4 ++ src/test_thread.c | 16 +++++ src/util.c | 4 +- src/vacuum.c | 11 +++ src/wal.c | 8 +++ tool/mkpragmatab.tcl | 34 +++++++++- 21 files changed, 601 insertions(+), 58 deletions(-) diff --git a/src/attach.c b/src/attach.c index 21da21f..fdd7185 100644 --- a/src/attach.c +++ b/src/attach.c @@ -187,6 +187,42 @@ static void attachFunc( if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } + + +#ifdef SQLITE_HAS_CODEC + if( rc==SQLITE_OK ){ + extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); + extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); + int nKey; + char *zKey; + int t = sqlite3_value_type(argv[2]); + switch( t ){ + case SQLITE_INTEGER: + case SQLITE_FLOAT: + zErrDyn = sqlite3DbStrDup(db, "Invalid key value"); + rc = SQLITE_ERROR; + break; + + case SQLITE_TEXT: + case SQLITE_BLOB: + nKey = sqlite3_value_bytes(argv[2]); + zKey = (char *)sqlite3_value_blob(argv[2]); + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + break; + + case SQLITE_NULL: + /* No key specified. Use the key from URI filename, or if none, + ** use the key from the main database. */ + if( sqlite3CodecQueryParameters(db, zName, zPath)==0 ){ + sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); + if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + } + } + break; + } + } +#endif sqlite3_free( zPath ); /* If the file was opened successfully, read the schema for the new database. diff --git a/src/backup.c b/src/backup.c index 6e559c9..8035a65 100644 --- a/src/backup.c +++ b/src/backup.c @@ -256,6 +256,13 @@ static int backupOnePage( int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; +#ifdef SQLITE_HAS_CODEC + /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is + ** guaranteed that the shared-mutex is held by this thread, handle + ** p->pSrc may not actually be the owner. */ + int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc); + int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest); +#endif int rc = SQLITE_OK; i64 iOff; @@ -272,6 +279,26 @@ static int backupOnePage( rc = SQLITE_READONLY; } +#ifdef SQLITE_HAS_CODEC + /* Backup is not possible if the page size of the destination is changing + ** and a codec is in use. + */ + if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){ + rc = SQLITE_READONLY; + } + + /* Backup is not possible if the number of bytes of reserve space differ + ** between source and destination. If there is a difference, try to + ** fix the destination to agree with the source. If that is not possible, + ** then the backup cannot proceed. + */ + if( nSrcReserve!=nDestReserve ){ + u32 newPgsz = nSrcPgsz; + rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); + if( rc==SQLITE_OK && newPgsz!=(u32)nSrcPgsz ) rc = SQLITE_READONLY; + } +#endif + /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset ** of the destination page. @@ -767,6 +794,10 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ b.pDest = pTo; b.iNext = 1; +#ifdef SQLITE_HAS_CODEC + sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom)); +#endif + /* 0x7FFFFFFF is the hard limit for the number of pages in a database ** file. By passing this as the number of pages to copy to ** sqlite3_backup_step(), we can guarantee that the copy finishes diff --git a/src/btree.c b/src/btree.c index de05fd9..4661587 100644 --- a/src/btree.c +++ b/src/btree.c @@ -2859,6 +2859,9 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ BtShared *pBt = p->pBt; assert( nReserve>=-1 && nReserve<=255 ); sqlite3BtreeEnter(p); +#if SQLITE_HAS_CODEC + if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve; +#endif if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ sqlite3BtreeLeave(p); return SQLITE_READONLY; @@ -2919,6 +2922,9 @@ int sqlite3BtreeGetOptimalReserve(Btree *p){ int n; sqlite3BtreeEnter(p); n = sqlite3BtreeGetReserveNoMutex(p); +#ifdef SQLITE_HAS_CODEC + if( npBt->optimalReserve ) n = p->pBt->optimalReserve; +#endif sqlite3BtreeLeave(p); return n; } diff --git a/src/btreeInt.h b/src/btreeInt.h index e5149d9..7687a0f 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -417,6 +417,9 @@ struct BtShared { #endif u8 inTransaction; /* Transaction state */ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ +#ifdef SQLITE_HAS_CODEC + u8 optimalReserve; /* Desired amount of reserved space per page */ +#endif u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ diff --git a/src/ctime.c b/src/ctime.c index 7a2ace9..013a0c2 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -355,6 +355,9 @@ static const char * const sqlite3azCompileOpt[] = { #if SQLITE_FTS5_NO_WITHOUT_ROWID "FTS5_NO_WITHOUT_ROWID", #endif +#if SQLITE_HAS_CODEC + "HAS_CODEC", +#endif #if HAVE_ISNAN || SQLITE_HAVE_ISNAN "HAVE_ISNAN", #endif diff --git a/src/global.c b/src/global.c index aeddada..a2c51f4 100644 --- a/src/global.c +++ b/src/global.c @@ -135,9 +135,16 @@ const unsigned char sqlite3CtypeMap[256] = { ** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** SQLITE_USE_URI symbol defined. +** +** URI filenames are enabled by default if SQLITE_HAS_CODEC is +** enabled. */ #ifndef SQLITE_USE_URI -# define SQLITE_USE_URI 0 +# ifdef SQLITE_HAS_CODEC +# define SQLITE_USE_URI 1 +# else +# define SQLITE_USE_URI 0 +# endif #endif /* EVIDENCE-OF: R-38720-18127 The default setting is determined by the diff --git a/src/main.c b/src/main.c index 6f61f4a..dda3f1c 100644 --- a/src/main.c +++ b/src/main.c @@ -3007,6 +3007,41 @@ static const char *uriParameter(const char *zFilename, const char *zParam){ return 0; } +#if defined(SQLITE_HAS_CODEC) +/* +** Process URI filename query parameters relevant to the SQLite Encryption +** Extension. Return true if any of the relevant query parameters are +** seen and return false if not. +*/ +int sqlite3CodecQueryParameters( + sqlite3 *db, /* Database connection */ + const char *zDb, /* Which schema is being created/attached */ + const char *zUri /* URI filename */ +){ + const char *zKey; + if( zUri==0 ){ + return 0; + }else if( (zKey = uriParameter(zUri, "hexkey"))!=0 && zKey[0] ){ + u8 iByte; + int i; + char zDecoded[40]; + for(i=0, iByte=0; ixCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } +# define CODEC2(P,D,N,X,E,O) \ + if( P->xCodec==0 ){ O=(char*)D; }else \ + if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; } +#else +# define CODEC1(P,D,N,X,E) /* NO-OP */ +# define CODEC2(P,D,N,X,E,O) O=(char*)D +#endif + /* ** The maximum allowed sector size. 64KiB. If the xSectorsize() method ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. @@ -691,6 +705,12 @@ struct Pager { #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ +#ifdef SQLITE_HAS_CODEC + void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ + void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ + void (*xCodecFree)(void*); /* Destructor for the codec */ + void *pCodec; /* First argument to xCodec... methods */ +#endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ #ifndef SQLITE_OMIT_WAL @@ -817,6 +837,9 @@ static const unsigned char aJournalMagic[] = { int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +#ifdef SQLITE_HAS_CODEC + if( pPager->xCodec!=0 ) return 0; +#endif #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; @@ -1050,7 +1073,11 @@ static void setGetterMethod(Pager *pPager){ if( pPager->errCode ){ pPager->xGet = getPageError; #if SQLITE_MAX_MMAP_SIZE>0 - }else if( USEFETCH(pPager) ){ + }else if( USEFETCH(pPager) +#ifdef SQLITE_HAS_CODEC + && pPager->xCodec==0 +#endif + ){ pPager->xGet = getPageMMap; #endif /* SQLITE_MAX_MMAP_SIZE>0 */ }else{ @@ -2198,6 +2225,35 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ return cksum; } +/* +** Report the current page size and number of reserved bytes back +** to the codec. +*/ +#ifdef SQLITE_HAS_CODEC +static void pagerReportSize(Pager *pPager){ + if( pPager->xCodecSizeChng ){ + pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize, + (int)pPager->nReserve); + } +} +#else +# define pagerReportSize(X) /* No-op if we do not support a codec */ +#endif + +#ifdef SQLITE_HAS_CODEC +/* +** Make sure the number of reserved bits is the same in the destination +** pager as it is in the source. This comes up when a VACUUM changes the +** number of reserved bits to the "optimal" amount. +*/ +void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){ + if( pDest->nReserve!=pSrc->nReserve ){ + pDest->nReserve = pSrc->nReserve; + pagerReportSize(pDest); + } +} +#endif + /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. @@ -2249,6 +2305,11 @@ static int pager_playback_one_page( char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ int isSynced; /* True if journal page is synced */ +#ifdef SQLITE_HAS_CODEC + /* The jrnlEnc flag is true if Journal pages should be passed through + ** the codec. It is false for pure in-memory journals. */ + const int jrnlEnc = (isMainJrnl || pPager->subjInMemory==0); +#endif assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ @@ -2311,6 +2372,7 @@ static int pager_playback_one_page( */ if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ pPager->nReserve = ((u8*)aData)[20]; + pagerReportSize(pPager); } /* If the pager is in CACHEMOD state, then there must be a copy of this @@ -2378,12 +2440,26 @@ static int pager_playback_one_page( ** is if the data was just read from an in-memory sub-journal. In that ** case it must be encrypted here before it is copied into the database ** file. */ +#ifdef SQLITE_HAS_CODEC + if( !jrnlEnc ){ + CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); + rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); + CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); + }else +#endif rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } if( pPager->pBackup ){ +#ifdef SQLITE_HAS_CODEC + if( jrnlEnc ){ + CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); + sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); + CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); + }else +#endif sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); } }else if( !isMainJrnl && pPg==0 ){ @@ -2434,6 +2510,11 @@ static int pager_playback_one_page( if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } + + /* Decode the page just read from disk */ +#if SQLITE_HAS_CODEC + if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } +#endif sqlite3PcacheRelease(pPg); } return rc; @@ -2993,6 +3074,8 @@ static int readDbPage(PgHdr *pPg){ memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); } } + CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT); + PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno)); @@ -3736,6 +3819,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ if( nReserve<0 ) nReserve = pPager->nReserve; assert( nReserve>=0 && nReserve<1000 ); pPager->nReserve = (i16)nReserve; + pagerReportSize(pPager); pagerFixMaplimit(pPager); } return rc; @@ -4131,6 +4215,11 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ sqlite3OsClose(pPager->fd); sqlite3PageFree(pTmp); sqlite3PcacheClose(pPager->pPCache); + +#ifdef SQLITE_HAS_CODEC + if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); +#endif + assert( !pPager->aSavepoint && !pPager->pInJournal ); assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); @@ -4381,7 +4470,8 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); if( pList->pgno==1 ) pager_write_changecounter(pList); - pData = pList->pData; + /* Encode the database */ + CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); /* Write out the page data. */ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); @@ -4470,6 +4560,12 @@ static int subjournalPage(PgHdr *pPg){ void *pData = pPg->pData; i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; + +#if SQLITE_HAS_CODEC + if( !pPager->subjInMemory ){ + CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); + }else +#endif pData2 = pData; PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); @@ -5551,6 +5647,9 @@ static int getPageMMap( ); assert( USEFETCH(pPager) ); +#ifdef SQLITE_HAS_CODEC + assert( pPager->xCodec==0 ); +#endif /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here ** allows the compiler optimizer to reuse the results of the "pgno>1" @@ -5879,7 +5978,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); assert( pPager->journalHdr<=pPager->journalOff ); - pData2 = pPg->pData; + CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); cksum = pager_cksum(pPager, (u8*)pData2); /* Even if an IO or diskfull error occurs while journalling the @@ -6244,7 +6343,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ if( DIRECT_MODE ){ const void *zBuf; assert( pPager->dbFileSize>0 ); - zBuf = pPgHdr->pData; + CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); pPager->aStat[PAGER_STAT_WRITE]++; @@ -7003,6 +7102,54 @@ const char *sqlite3PagerJournalname(Pager *pPager){ return pPager->zJournal; } +#ifdef SQLITE_HAS_CODEC +/* +** Set or retrieve the codec for this pager +*/ +void sqlite3PagerSetCodec( + Pager *pPager, + void *(*xCodec)(void*,void*,Pgno,int), + void (*xCodecSizeChng)(void*,int,int), + void (*xCodecFree)(void*), + void *pCodec +){ + if( pPager->xCodecFree ){ + pPager->xCodecFree(pPager->pCodec); + }else{ + pager_reset(pPager); + } + pPager->xCodec = pPager->memDb ? 0 : xCodec; + pPager->xCodecSizeChng = xCodecSizeChng; + pPager->xCodecFree = xCodecFree; + pPager->pCodec = pCodec; + setGetterMethod(pPager); + pagerReportSize(pPager); +} +void *sqlite3PagerGetCodec(Pager *pPager){ + return pPager->pCodec; +} + +/* +** This function is called by the wal module when writing page content +** into the log file. +** +** This function returns a pointer to a buffer containing the encrypted +** page content. If a malloc fails, this function may return NULL. +*/ +void *sqlite3PagerCodec(PgHdr *pPg){ + void *aData = 0; + CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); + return aData; +} + +/* +** Return the current pager state +*/ +int sqlite3PagerState(Pager *pPager){ + return pPager->eState; +} +#endif /* SQLITE_HAS_CODEC */ + #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Move the page pPg to location pgno in the file. diff --git a/src/pager.h b/src/pager.h index 2c99d67..9042789 100644 --- a/src/pager.h +++ b/src/pager.h @@ -128,6 +128,9 @@ int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); +#ifdef SQLITE_HAS_CODEC +void sqlite3PagerAlignReserve(Pager*,Pager*); +#endif int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); int sqlite3PagerSetSpillsize(Pager*, int); @@ -221,6 +224,10 @@ void sqlite3PagerTruncateImage(Pager*,Pgno); void sqlite3PagerRekey(DbPage*, Pgno, u16); +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) +void *sqlite3PagerCodec(DbPage *); +#endif + /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) Pgno sqlite3PagerPagenumber(DbPage*); diff --git a/src/pragma.c b/src/pragma.c index c7e62b5..331dfdb 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -2230,11 +2230,59 @@ void sqlite3Pragma( } #endif -#if defined(SQLITE_ENABLE_CEROD) +#ifdef SQLITE_HAS_CODEC + /* Pragma iArg + ** ---------- ------ + ** key 0 + ** rekey 1 + ** hexkey 2 + ** hexrekey 3 + ** textkey 4 + ** textrekey 5 + */ + case PragTyp_KEY: { + if( zRight ){ + char zBuf[40]; + const char *zKey = zRight; + int n; + if( pPragma->iArg==2 || pPragma->iArg==3 ){ + u8 iByte; + int i; + for(i=0, iByte=0; iiArg<4 ? sqlite3Strlen30(zRight) : -1; + } + if( (pPragma->iArg & 1)==0 ){ + rc = sqlite3_key_v2(db, zDb, zKey, n); + }else{ + rc = sqlite3_rekey_v2(db, zDb, zKey, n); + } + if( rc==SQLITE_OK && n!=0 ){ + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "ok", SQLITE_STATIC); + returnSingleText(v, "ok"); + } + } + break; + } +#endif +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){ +#ifdef SQLITE_HAS_CODEC + if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ + sqlite3_activate_see(&zRight[4]); + } +#endif +#ifdef SQLITE_ENABLE_CEROD if( sqlite3StrNICmp(zRight, "cerod-", 6)==0 ){ sqlite3_activate_cerod(&zRight[6]); } +#endif } break; #endif diff --git a/src/pragma.h b/src/pragma.h index 7046695..3edf5c1 100644 --- a/src/pragma.h +++ b/src/pragma.h @@ -5,50 +5,51 @@ */ /* The various pragma types */ -#define PragTyp_ACTIVATE_EXTENSIONS 0 -#define PragTyp_HEADER_VALUE 1 -#define PragTyp_AUTO_VACUUM 2 -#define PragTyp_FLAG 3 -#define PragTyp_BUSY_TIMEOUT 4 -#define PragTyp_CACHE_SIZE 5 -#define PragTyp_CACHE_SPILL 6 -#define PragTyp_CASE_SENSITIVE_LIKE 7 -#define PragTyp_COLLATION_LIST 8 -#define PragTyp_COMPILE_OPTIONS 9 -#define PragTyp_DATA_STORE_DIRECTORY 10 -#define PragTyp_DATABASE_LIST 11 -#define PragTyp_DEFAULT_CACHE_SIZE 12 -#define PragTyp_ENCODING 13 -#define PragTyp_FOREIGN_KEY_CHECK 14 -#define PragTyp_FOREIGN_KEY_LIST 15 -#define PragTyp_FUNCTION_LIST 16 -#define PragTyp_HARD_HEAP_LIMIT 17 -#define PragTyp_INCREMENTAL_VACUUM 18 -#define PragTyp_INDEX_INFO 19 -#define PragTyp_INDEX_LIST 20 -#define PragTyp_INTEGRITY_CHECK 21 -#define PragTyp_JOURNAL_MODE 22 -#define PragTyp_JOURNAL_SIZE_LIMIT 23 -#define PragTyp_LOCK_PROXY_FILE 24 -#define PragTyp_LOCKING_MODE 25 -#define PragTyp_PAGE_COUNT 26 -#define PragTyp_MMAP_SIZE 27 -#define PragTyp_MODULE_LIST 28 -#define PragTyp_OPTIMIZE 29 -#define PragTyp_PAGE_SIZE 30 -#define PragTyp_PRAGMA_LIST 31 -#define PragTyp_SECURE_DELETE 32 -#define PragTyp_SHRINK_MEMORY 33 -#define PragTyp_SOFT_HEAP_LIMIT 34 -#define PragTyp_SYNCHRONOUS 35 -#define PragTyp_TABLE_INFO 36 -#define PragTyp_TEMP_STORE 37 -#define PragTyp_TEMP_STORE_DIRECTORY 38 -#define PragTyp_THREADS 39 -#define PragTyp_WAL_AUTOCHECKPOINT 40 -#define PragTyp_WAL_CHECKPOINT 41 -#define PragTyp_LOCK_STATUS 42 -#define PragTyp_STATS 43 +#define PragTyp_HEADER_VALUE 0 +#define PragTyp_AUTO_VACUUM 1 +#define PragTyp_FLAG 2 +#define PragTyp_BUSY_TIMEOUT 3 +#define PragTyp_CACHE_SIZE 4 +#define PragTyp_CACHE_SPILL 5 +#define PragTyp_CASE_SENSITIVE_LIKE 6 +#define PragTyp_COLLATION_LIST 7 +#define PragTyp_COMPILE_OPTIONS 8 +#define PragTyp_DATA_STORE_DIRECTORY 9 +#define PragTyp_DATABASE_LIST 10 +#define PragTyp_DEFAULT_CACHE_SIZE 11 +#define PragTyp_ENCODING 12 +#define PragTyp_FOREIGN_KEY_CHECK 13 +#define PragTyp_FOREIGN_KEY_LIST 14 +#define PragTyp_FUNCTION_LIST 15 +#define PragTyp_HARD_HEAP_LIMIT 16 +#define PragTyp_INCREMENTAL_VACUUM 17 +#define PragTyp_INDEX_INFO 18 +#define PragTyp_INDEX_LIST 19 +#define PragTyp_INTEGRITY_CHECK 20 +#define PragTyp_JOURNAL_MODE 21 +#define PragTyp_JOURNAL_SIZE_LIMIT 22 +#define PragTyp_LOCK_PROXY_FILE 23 +#define PragTyp_LOCKING_MODE 24 +#define PragTyp_PAGE_COUNT 25 +#define PragTyp_MMAP_SIZE 26 +#define PragTyp_MODULE_LIST 27 +#define PragTyp_OPTIMIZE 28 +#define PragTyp_PAGE_SIZE 29 +#define PragTyp_PRAGMA_LIST 30 +#define PragTyp_SECURE_DELETE 31 +#define PragTyp_SHRINK_MEMORY 32 +#define PragTyp_SOFT_HEAP_LIMIT 33 +#define PragTyp_SYNCHRONOUS 34 +#define PragTyp_TABLE_INFO 35 +#define PragTyp_TEMP_STORE 36 +#define PragTyp_TEMP_STORE_DIRECTORY 37 +#define PragTyp_THREADS 38 +#define PragTyp_WAL_AUTOCHECKPOINT 39 +#define PragTyp_WAL_CHECKPOINT 40 +#define PragTyp_ACTIVATE_EXTENSIONS 41 +#define PragTyp_KEY 42 +#define PragTyp_LOCK_STATUS 43 +#define PragTyp_STATS 44 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -132,7 +133,7 @@ typedef struct PragmaName { u64 iArg; /* Extra argument */ } PragmaName; static const PragmaName aPragmaName[] = { -#if defined(SQLITE_ENABLE_CEROD) +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) {/* zName: */ "activate_extensions", /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS, /* ePragFlg: */ 0, @@ -328,6 +329,18 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, +#if defined(SQLITE_HAS_CODEC) + {/* zName: */ "hexkey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 2 }, + {/* zName: */ "hexrekey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 3 }, +#endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_CHECK) {/* zName: */ "ignore_check_constraints", @@ -380,6 +393,13 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif +#if defined(SQLITE_HAS_CODEC) + {/* zName: */ "key", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 0 }, +#endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "legacy_alter_table", /* ePragTyp: */ PragTyp_FLAG, @@ -487,6 +507,15 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_RecTriggers }, +#endif +#if defined(SQLITE_HAS_CODEC) + {/* zName: */ "rekey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 1 }, +#endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "reverse_unordered_selects", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, @@ -570,6 +599,18 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, +#endif +#if defined(SQLITE_HAS_CODEC) + {/* zName: */ "textkey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 4 }, + {/* zName: */ "textrekey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 5 }, #endif {/* zName: */ "threads", /* ePragTyp: */ PragTyp_THREADS, @@ -639,4 +680,4 @@ static const PragmaName aPragmaName[] = { /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 66 on by default, 76 total. */ +/* Number of pragmas: 66 on by default, 82 total. */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 367242b..6b32b64 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5817,6 +5817,51 @@ int sqlite3_collation_needed16( void(*)(void*,sqlite3*,int eTextRep,const void*) ); +#ifdef SQLITE_HAS_CODEC +/* +** Specify the key for an encrypted database. This routine should be +** called right after sqlite3_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +int sqlite3_key( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The key */ +); +int sqlite3_key_v2( + sqlite3 *db, /* Database to be rekeyed */ + const char *zDbName, /* Name of the database */ + const void *pKey, int nKey /* The key */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +int sqlite3_rekey( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); +int sqlite3_rekey_v2( + sqlite3 *db, /* Database to be rekeyed */ + const char *zDbName, /* Name of the database */ + const void *pKey, int nKey /* The new key */ +); + +/* +** Specify the activation key for a SEE database. Unless +** activated, none of the SEE routines will work. +*/ +void sqlite3_activate_see( + const char *zPassPhrase /* Activation phrase */ +); +#endif + #ifdef SQLITE_ENABLE_CEROD /* ** Specify the activation key for a CEROD database. Unless diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 76b37cb..f0a63b1 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4113,7 +4113,11 @@ void sqlite3AddGenerated(Parse*,Expr*,Token*); void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); -#define sqlite3CodecQueryParameters(A,B,C) 0 +#ifdef SQLITE_HAS_CODEC + int sqlite3CodecQueryParameters(sqlite3*,const char*,const char*); +#else +# define sqlite3CodecQueryParameters(A,B,C) 0 +#endif Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 69da2cc..2cae580 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -3094,10 +3094,22 @@ deserialize_error: ** Change the encryption key on the currently open database. */ case DB_REKEY: { +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + int nKey; + void *pKey; +#endif if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "KEY"); return TCL_ERROR; } +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); + rc = sqlite3_rekey(pDb->db, pKey, nKey); + if( rc ){ + Tcl_AppendResult(interp, sqlite3_errstr(rc), (char*)0); + rc = TCL_ERROR; + } +#endif break; } @@ -3666,6 +3678,9 @@ static int sqliteCmdUsage( "HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" " ?-nofollow BOOLEAN?" " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + " ?-key CODECKEY?" +#endif ); return TCL_ERROR; } @@ -3700,6 +3715,10 @@ static int SQLITE_TCLAPI DbMain( const char *zVfs = 0; int flags; Tcl_DString translatedFilename; +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + void *pKey = 0; + int nKey = 0; +#endif int rc; /* In normal use, each TCL interpreter runs in a single thread. So @@ -3726,7 +3745,11 @@ static int SQLITE_TCLAPI DbMain( return TCL_OK; } if( strcmp(zArg,"-has-codec")==0 ){ +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + Tcl_AppendResult(interp,"1",(char*)0); +#else Tcl_AppendResult(interp,"0",(char*)0); +#endif return TCL_OK; } if( zArg[0]=='-' ) return sqliteCmdUsage(interp, objv); @@ -3741,7 +3764,9 @@ static int SQLITE_TCLAPI DbMain( if( i==objc-1 ) return sqliteCmdUsage(interp, objv); i++; if( strcmp(zArg,"-key")==0 ){ - /* no-op */ +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + pKey = Tcl_GetByteArrayFromObj(objv[i], &nKey); +#endif }else if( strcmp(zArg, "-vfs")==0 ){ zVfs = Tcl_GetString(objv[i]); }else if( strcmp(zArg, "-readonly")==0 ){ @@ -3817,6 +3842,11 @@ static int SQLITE_TCLAPI DbMain( }else{ zErrMsg = sqlite3_mprintf("%s", sqlite3_errstr(rc)); } +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + if( p->db ){ + sqlite3_key(p->db, pKey, nKey); + } +#endif if( p->db==0 ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); Tcl_Free((char*)p); diff --git a/src/test1.c b/src/test1.c index 29207d5..5b07aef 100644 --- a/src/test1.c +++ b/src/test1.c @@ -655,6 +655,20 @@ static int SQLITE_TCLAPI test_key( int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + sqlite3 *db; + const char *zKey; + int nKey; + if( argc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " FILENAME\"", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; + zKey = argv[2]; + nKey = strlen(zKey); + sqlite3_key(db, zKey, nKey); +#endif return TCL_OK; } @@ -669,6 +683,20 @@ static int SQLITE_TCLAPI test_rekey( int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ +#ifdef SQLITE_HAS_CODEC + sqlite3 *db; + const char *zKey; + int nKey; + if( argc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " FILENAME\"", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; + zKey = argv[2]; + nKey = strlen(zKey); + sqlite3_rekey(db, zKey, nKey); +#endif return TCL_OK; } diff --git a/src/test_config.c b/src/test_config.c index 362e92a..e6c0330 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -226,7 +226,11 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "json1", "0", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_HAS_CODEC + Tcl_SetVar2(interp, "sqlite_options", "has_codec", "1", TCL_GLOBAL_ONLY); +#else Tcl_SetVar2(interp, "sqlite_options", "has_codec", "0", TCL_GLOBAL_ONLY); +#endif #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS Tcl_SetVar2(interp, "sqlite_options", "like_match_blobs", "0", TCL_GLOBAL_ONLY); diff --git a/src/test_thread.c b/src/test_thread.c index de0fdb4..20b4cf1 100644 --- a/src/test_thread.c +++ b/src/test_thread.c @@ -287,6 +287,22 @@ static int SQLITE_TCLAPI sqlthread_open( zFilename = Tcl_GetString(objv[2]); sqlite3_open(zFilename, &db); +#ifdef SQLITE_HAS_CODEC + if( db && objc>=4 ){ + const char *zKey; + int nKey; + int rc; + zKey = Tcl_GetStringFromObj(objv[3], &nKey); + rc = sqlite3_key(db, zKey, nKey); + if( rc!=SQLITE_OK ){ + char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db)); + sqlite3_close(db); + Tcl_AppendResult(interp, zErrMsg, (char*)0); + sqlite3_free(zErrMsg); + return TCL_ERROR; + } + } +#endif Md5_Register(db, 0, 0); sqlite3_busy_handler(db, xBusy, 0); diff --git a/src/util.c b/src/util.c index 693759b..3e3a924 100644 --- a/src/util.c +++ b/src/util.c @@ -1256,7 +1256,7 @@ u8 sqlite3HexToInt(int h){ return (u8)(h & 0xf); } -#if !defined(SQLITE_OMIT_BLOB_LITERAL) +#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* ** Convert a BLOB literal of the form "x'hhhhhh'" into its binary ** value. Return a pointer to its binary value. Space to hold the @@ -1277,7 +1277,7 @@ void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ } return zBlob; } -#endif /* !SQLITE_OMIT_BLOB_LITERAL */ +#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ /* ** Log an error that is an API call on a connection pointer that should diff --git a/src/vacuum.c b/src/vacuum.c index ce9db64..e8555ef 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -235,6 +235,17 @@ SQLITE_NOINLINE int sqlite3RunVacuum( } nRes = sqlite3BtreeGetOptimalReserve(pMain); + /* A VACUUM cannot change the pagesize of an encrypted database. */ +#ifdef SQLITE_HAS_CODEC + if( db->nextPagesize ){ + extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); + int nKey; + char *zKey; + sqlite3CodecGetKey(db, iDb, (void**)&zKey, &nKey); + if( nKey ) db->nextPagesize = 0; + } +#endif + sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); diff --git a/src/wal.c b/src/wal.c index 034a162..9873f8b 100644 --- a/src/wal.c +++ b/src/wal.c @@ -3253,7 +3253,11 @@ static int walWriteOneFrame( int rc; /* Result code from subfunctions */ void *pData; /* Data actually written */ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ +#if defined(SQLITE_HAS_CODEC) + if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT; +#else pData = pPage->pData; +#endif walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); if( rc ) return rc; @@ -3436,7 +3440,11 @@ int sqlite3WalFrames( if( pWal->iReCksum==0 || iWriteiReCksum ){ pWal->iReCksum = iWrite; } +#if defined(SQLITE_HAS_CODEC) + if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM; +#else pData = p->pData; +#endif rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff); if( rc ) return rc; p->flags &= ~PGHDR_WAL_APPEND; diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl index 7c8ffd3..9ce5bd4 100644 --- a/tool/mkpragmatab.tcl +++ b/tool/mkpragmatab.tcl @@ -370,8 +370,38 @@ set pragma_def { COLS: database status IF: defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + NAME: key + TYPE: KEY + ARG: 0 + IF: defined(SQLITE_HAS_CODEC) + + NAME: rekey + TYPE: KEY + ARG: 1 + IF: defined(SQLITE_HAS_CODEC) + + NAME: hexkey + TYPE: KEY + ARG: 2 + IF: defined(SQLITE_HAS_CODEC) + + NAME: hexrekey + TYPE: KEY + ARG: 3 + IF: defined(SQLITE_HAS_CODEC) + + NAME: textkey + TYPE: KEY + ARG: 4 + IF: defined(SQLITE_HAS_CODEC) + + NAME: textrekey + TYPE: KEY + ARG: 5 + IF: defined(SQLITE_HAS_CODEC) + NAME: activate_extensions - IF: defined(SQLITE_ENABLE_CEROD) + IF: defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) NAME: soft_heap_limit FLAG: Result0 @@ -466,7 +496,7 @@ record_one set allnames [lsort [array names allbyname]] # Generate #defines for all pragma type names. Group the pragmas that are -# omit in default builds (ex: defined(SQLITE_DEBUG)) +# omit in default builds (defined(SQLITE_DEBUG) and defined(SQLITE_HAS_CODEC)) # at the end. # puts $fd "\n/* The various pragma types */"