int btreeVacuum(Btree *p, char **pzErrMsg) { sqlite3 *db; int rc; u_int32_t truncatedPages; db = p->db; /* Return directly if vacuum is on progress */ if (p->inVacuum) return SQLITE_OK; /* * We're going to do updates in this transaction at the Berkeley DB * Core level (i.e., call DB->compact), but we start it read-only at * the SQL level to avoid overhead from checkpoint-on-commit. */ if ((rc = sqlite3BtreeBeginTrans(p, 0)) != SQLITE_OK) { sqlite3SetString(pzErrMsg, db, "failed to begin a vacuum transaction"); return rc; } p->inVacuum = 1; truncatedPages = 0; /* Go through all tables */ do { rc = btreeIncrVacuum(p, &truncatedPages); } while (rc == SQLITE_OK); p->needVacuum = 0; if (rc != SQLITE_DONE) { sqlite3SetString(pzErrMsg, db, "error during vacuum, rolled back"); (void)sqlite3BtreeRollback(p); } else if ((rc = sqlite3BtreeCommit(p)) != SQLITE_OK) { sqlite3SetString(pzErrMsg, db, "failed to commit the vacuum transaction"); } p->inVacuum = 0; return rc; }
/* ** Check schema cookies in all databases. If any cookie is out ** of date set pParse->rc to SQLITE_SCHEMA. If all schema cookies ** make no changes to pParse->rc. */ static void schemaIsValid(Parse *pParse){ sqlite3 *db = pParse->db; int iDb; int rc; int cookie; assert( pParse->checkSchema ); assert( sqlite3_mutex_held(db->mutex) ); for(iDb=0; iDb<db->nDb; iDb++){ int openedTransaction = 0; /* True if a transaction is opened */ Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */ if( pBt==0 ) continue; /* If there is not already a read-only (or read-write) transaction opened ** on the b-tree database, open one now. If a transaction is opened, it ** will be closed immediately after reading the meta-value. */ if( !sqlite3BtreeIsInReadTrans(pBt) ){ rc = sqlite3BtreeBeginTrans(pBt, 0); if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; } if( rc!=SQLITE_OK ) return; openedTransaction = 1; } /* Read the schema cookie from the database. If it does not match the ** value stored as part of the in the in-memory schema representation, ** set Parse.rc to SQLITE_SCHEMA. */ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ pParse->rc = SQLITE_SCHEMA; } /* Close the transaction, if one was opened. */ if( openedTransaction ){ sqlite3BtreeCommit(pBt); } } }
/* ** Usage: btree_begin_transaction ID ** ** Start a new transaction */ static int btree_begin_transaction( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Btree *pBt; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pBt = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pBt); rc = sqlite3BtreeBeginTrans(pBt, 1); sqlite3BtreeLeave(pBt); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } return TCL_OK; }
int sqlite3_rekey(sqlite3 *db, const void *zKey, int nKey) { BOTANSQLITE_TRACE("sqlite3_rekey"); // Changes the encryption key for an existing database. int rc = SQLITE_ERROR; Btree *pbt = db->aDb[0].pBt; Pager *pPager = sqlite3BtreePager(pbt); void *pCodec = sqlite3PagerGetCodec(pPager); if ((!zKey || nKey <= 0) && !pCodec) { // Database not encrypted and key not specified. Do nothing return SQLITE_OK; } if (!pCodec) { // Database not encrypted, but key specified. Encrypt database pCodec = InitializeNewCodec(db); assert(nKey >= 0); SetWriteKey(pCodec, (const char*) zKey, (size_t) nKey); if (HandleError(pCodec)) { DeleteCodec(pCodec); return SQLITE_ERROR; } sqlite3PagerSetCodec(pPager, Codec, CodecSizeChange, PagerFreeCodec, pCodec); } else if (!zKey || nKey <= 0) { // Database encrypted, but key not specified. Decrypt database // Keep read key, drop write key DropWriteKey(pCodec); } else { // Database encrypted and key specified. Re-encrypt database with new key // Keep read key, change write key to new key assert(nKey >= 0); SetWriteKey(pCodec, (const char*) zKey, (size_t) nKey); if (HandleError(pCodec)) return SQLITE_ERROR; } // Start transaction rc = sqlite3BtreeBeginTrans(pbt, 1); if (rc == SQLITE_OK) { // Rewrite all pages using the new encryption key (if specified) int nPageCount = -1; sqlite3PagerPagecount(pPager, &nPageCount); Pgno nPage = (Pgno) nPageCount; Pgno nSkip = PAGER_MJ_PGNO(pPager); DbPage *pPage; Pgno n; for (n = 1; rc == SQLITE_OK && n <= nPage; n++) { if (n == nSkip) continue; rc = sqlite3PagerGet(pPager, n, &pPage, 0); if (rc == SQLITE_OK) { rc = sqlite3PagerWrite(pPage); sqlite3PagerUnref(pPage); } else { sqlite3ErrorWithMsg(db, SQLITE_ERROR, "%s", "Error while rekeying database page. Transaction Canceled."); } } } else { sqlite3ErrorWithMsg(db, SQLITE_ERROR, "%s", "Error beginning rekey transaction. Make sure that the current encryption key is correct."); } if (rc == SQLITE_OK) { // All good, commit rc = sqlite3BtreeCommit(pbt); if (rc == SQLITE_OK) { //Database rekeyed and committed successfully, update read key if (HasWriteKey(pCodec)) { SetReadIsWrite(pCodec); } else //No write key == no longer encrypted { sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); } } else { //FIXME: can't trigger this, not sure if rollback is needed, reference implementation didn't rollback sqlite3ErrorWithMsg(db, SQLITE_ERROR, "%s", "Could not commit rekey transaction."); } } else { // Rollback, rekey failed sqlite3BtreeRollback(pbt, SQLITE_ERROR, 0); // go back to read key if (HasReadKey(pCodec)) { SetWriteIsRead(pCodec); } else //Database wasn't encrypted to start with { sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); } } return rc; }
void testUpdate() { puts("testUpdate()"); sqlite3 *db; char * file = UPDATE_FILE; int rc = sqlite3_open(file, &db); if (rc != SQLITE_OK) { printf("failed open: %d \n", rc); return; } if (db->nDb == 0) { puts("backends count is zero"); goto close; } Btree *pBt = db->aDb[0].pBt; rc = sqlite3BtreeBeginTrans(pBt, 1); if (rc != SQLITE_OK) { printf("failed begin transaction: %d", rc); goto close; } char* pData = "Test data"; int nData = strlen(pData); BtCursor cur; int x = 0; for (x = 1; x <= 3; x++) { int table = 0; rc = sqlite3BtreeCreateTable(pBt, &table, BTREE_INTKEY | BTREE_LEAFDATA); if (rc != SQLITE_OK) { printf("failed create table: %d", rc); goto close; } memset(&cur, 0, sizeof(BtCursor)); rc = sqlite3BtreeCursor(pBt, table, 0, 0, &cur); if (rc != SQLITE_OK) { printf("failed get cursor: %d", rc); goto close; } int y = 0; for (y = 1; y <= 3; y++) { rc = sqlite3BtreeInsert(&cur, NULL, y, pData, nData, 0, 0); if (rc != SQLITE_OK) { printf("failed insert data: %d", rc); goto close; } } rc = sqlite3BtreeCloseCursor(&cur); if (rc != SQLITE_OK) { printf("failed close cursor: %d", rc); goto close; } } rc = sqlite3BtreeCommit(pBt); if (rc != SQLITE_OK) { printf("failed commit transaction: %d", rc); } else { puts("success commit transaction"); } sqlite3_close(db); rc = sqlite3_open(file, &db); if (rc != SQLITE_OK) { printf("failed open: %d \n", rc); return; } if (db->nDb == 0) { puts("backends count is zero"); goto close; } pBt = db->aDb[0].pBt; rc = sqlite3BtreeBeginTrans(pBt, 1); if (rc != SQLITE_OK) { printf("failed begin transaction: %d", rc); goto close; } int pages = 0; rc = sqlite3PagerPagecount(pBt->pBt->pPager, &pages); if (rc != SQLITE_OK) { printf("failed get pages count: %d \n", rc); goto close; } if (pages == 0) { puts("pages count is zero"); goto close; } printf("pages count: %d \n", pages); pData = "Data test"; nData = strlen(pData); x = 0; for (x = 1; x <= pages; x++) { memset(&cur, 0, sizeof(BtCursor)); rc = sqlite3BtreeCursor(pBt, x, 1, 0, &cur); if (rc != SQLITE_OK) { printf("failed get cursor: %d", rc); goto close; } int next = 0; rc = sqlite3BtreeFirst(&cur, &next); if (rc != SQLITE_OK) { printf("failed first(): %d \n", rc); goto closeCursor; } if (next != 0) { puts("cursor is empty"); goto closeCursor; } sqlite3BtreeCacheOverflow(&cur); do { rc = sqlite3BtreePutData(&cur,0,nData,pData); if (rc != SQLITE_OK) { printf("failed putData(): %d \n", rc); goto closeCursor; } puts("data updated"); } while (SQLITE_OK == sqlite3BtreeNext(&cur, &next) && next == 0); closeCursor: rc = sqlite3BtreeCloseCursor(&cur); if (rc != SQLITE_OK) { printf("failed close cursor: %d", rc); goto close; } } rc = sqlite3BtreeCommit(pBt); if (rc != SQLITE_OK) { printf("failed commit transaction: %d", rc); } else { puts("success commit transaction"); } close: sqlite3_close(db); }
/* CHANGE 1 of 3: Add function parameter nRes */ SQLITE_PRIVATE int sqlite3RunVacuumForRekey(char **pzErrMsg, sqlite3 *db, int iDb, int nRes){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; /* The temporary database we vacuum into */ u16 saved_mDbFlags; /* Saved value of db->mDbFlags */ u32 saved_flags; /* Saved value of db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ /* CHANGE 2 of 3: Do not define local variable nRes */ /*int nRes;*/ /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ if (!db->autoCommit) { sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } if (db->nVdbeActive>1) { sqlite3SetString(pzErrMsg, db, "cannot VACUUM - SQL statements in progress"); return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_mDbFlags = db->mDbFlags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows); db->mTrace = 0; zDbMain = db->aDb[iDb].zDbSName; pMain = db->aDb[iDb].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** ** An optimisation would be to use a non-journaled pager. ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but ** that actually made the VACUUM run slower. Very little journalling ** actually occurs when doing a vacuum since the vacuum_db is initially ** empty. Only the journal header is written. Apparently it takes more ** time to parse and run the PRAGMA to turn journalling off than it does ** to write the journal header file. */ nDb = db->nDb; rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db"); if (rc != SQLITE_OK) goto end_of_vacuum; assert((db->nDb - 1) == nDb); pDb = &db->aDb[nDb]; assert(strcmp(pDb->zDbSName, "vacuum_db") == 0); pTemp = pDb->pBt; /* The call to execSql() to attach the temp database has left the file ** locked (as there was more than one active statement when the transaction ** to read the schema was concluded. Unlock it here so that this doesn't ** cause problems for the call to BtreeSetPageSize() below. */ sqlite3BtreeCommit(pTemp); /* CHANGE 3 of 3: Do not call sqlite3BtreeGetOptimalReserve */ /* 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); /* Begin a transaction and take an exclusive lock on the main database ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, ** to ensure that we do not try to change the page-size on a WAL database. */ rc = execSql(db, pzErrMsg, "BEGIN"); if (rc != SQLITE_OK) goto end_of_vacuum; rc = sqlite3BtreeBeginTrans(pMain, 2); if (rc != SQLITE_OK) goto end_of_vacuum; /* Do not attempt to change the page size for a WAL database */ if (sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) == PAGER_JOURNALMODE_WAL) { db->nextPagesize = 0; } if (sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0) || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0)) || NEVER(db->mallocFailed) ) { rc = SQLITE_NOMEM_BKPT; goto end_of_vacuum; } #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac >= 0 ? db->nextAutovac : sqlite3BtreeGetAutoVacuum(pMain)); #endif /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */ rc = execSqlF(db, pzErrMsg, "SELECT sql FROM \"%w\".sqlite_master" " WHERE type='table'AND name<>'sqlite_sequence'" " AND coalesce(rootpage,1)>0", zDbMain ); if (rc != SQLITE_OK) goto end_of_vacuum; rc = execSqlF(db, pzErrMsg, "SELECT sql FROM \"%w\".sqlite_master" " WHERE type='index'", zDbMain ); if (rc != SQLITE_OK) goto end_of_vacuum; db->init.iDb = 0; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ rc = execSqlF(db, pzErrMsg, "SELECT'INSERT INTO vacuum_db.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" "FROM vacuum_db.sqlite_master " "WHERE type='table'AND coalesce(rootpage,1)>0", zDbMain ); assert((db->mDbFlags & DBFLAG_Vacuum) != 0); db->mDbFlags &= ~DBFLAG_Vacuum; if (rc != SQLITE_OK) goto end_of_vacuum; /* Copy the triggers, views, and virtual tables from the main database ** over to the temporary database. None of these objects has any ** associated storage, so all we have to do is copy their entries ** from the SQLITE_MASTER table. */ rc = execSqlF(db, pzErrMsg, "INSERT INTO vacuum_db.sqlite_master" " SELECT*FROM \"%w\".sqlite_master" " WHERE type IN('view','trigger')" " OR(type='table'AND rootpage=0)", zDbMain ); if (rc) goto end_of_vacuum; /* At this point, there is a write transaction open on both the ** vacuum database and the main database. Assuming no error occurs, ** both transactions are closed by this block - the main database ** transaction by sqlite3BtreeCopyFile() and the other by an explicit ** call to sqlite3BtreeCommit(). */ { u32 meta; int i; /* This array determines which meta meta values are preserved in the ** vacuum. Even entries are the meta value number and odd entries ** are an increment to apply to the meta value after the vacuum. ** The increment is used to increase the schema cookie so that other ** connections to the same database will know to reread the schema. */ static const unsigned char aCopy[] = { BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */ BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */ BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */ BTREE_USER_VERSION, 0, /* Preserve the user version */ BTREE_APPLICATION_ID, 0, /* Preserve the application id */ }; assert(1 == sqlite3BtreeIsInTrans(pTemp)); assert(1 == sqlite3BtreeIsInTrans(pMain)); /* Copy Btree meta values */ for (i = 0; i<ArraySize(aCopy); i += 2) { /* GetMeta() and UpdateMeta() cannot fail in this context because ** we already have page 1 loaded into cache and marked dirty. */ sqlite3BtreeGetMeta(pMain, aCopy[i], &meta); rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta + aCopy[i + 1]); if (NEVER(rc != SQLITE_OK)) goto end_of_vacuum; } rc = sqlite3BtreeCopyFile(pMain, pTemp); if (rc != SQLITE_OK) goto end_of_vacuum; rc = sqlite3BtreeCommit(pTemp); if (rc != SQLITE_OK) goto end_of_vacuum; #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp)); #endif } assert(rc == SQLITE_OK); rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes, 1); end_of_vacuum: /* Restore the original value of db->flags */ db->init.iDb = 0; db->mDbFlags = saved_mDbFlags; db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->mTrace = saved_mTrace; sqlite3BtreeSetPageSize(pMain, -1, -1, 1); /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; if (pDb) { sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; } /* This both clears the schemas and reduces the size of the db->aDb[] ** array. */ sqlite3ResetAllSchemasOfConnection(db); return rc; }
/* ** Reset an Agg structure. Delete all its contents. ** ** For installable aggregate functions, if the step function has been ** called, make sure the finalizer function has also been called. The ** finalizer might need to free memory that was allocated as part of its ** private context. If the finalizer has not been called yet, call it ** now. ** ** If db is NULL, then this is being called from sqliteVdbeReset(). In ** this case clean up all references to the temp-table used for ** aggregates (if it was ever opened). ** ** If db is not NULL, then this is being called from with an OP_AggReset ** opcode. Open the temp-table, if it has not already been opened and ** delete the contents of the table used for aggregate information, ready ** for the next round of aggregate processing. */ int sqlite3VdbeAggReset(sqlite3 *db, Agg *pAgg, KeyInfo *pKeyInfo){ int rc = 0; BtCursor *pCsr = pAgg->pCsr; assert( (pCsr && pAgg->nTab>0) || (!pCsr && pAgg->nTab==0) || sqlite3_malloc_failed ); /* If pCsr is not NULL, then the table used for aggregate information ** is open. Loop through it and free the AggElem* structure pointed at ** by each entry. If the finalizer has not been called for an AggElem, ** do that too. Finally, clear the btree table itself. */ if( pCsr ){ int res; assert( pAgg->pBtree ); assert( pAgg->nTab>0 ); rc=sqlite3BtreeFirst(pCsr, &res); while( res==0 && rc==SQLITE_OK ){ AggElem *pElem; rc = sqlite3BtreeData(pCsr, 0, sizeof(AggElem*), (char *)&pElem); if( res!=SQLITE_OK ){ return rc; } assert( pAgg->apFunc!=0 ); freeAggElem(pElem, pAgg); rc=sqlite3BtreeNext(pCsr, &res); } if( rc!=SQLITE_OK ){ return rc; } sqlite3BtreeCloseCursor(pCsr); sqlite3BtreeClearTable(pAgg->pBtree, pAgg->nTab); }else{ /* The cursor may not be open because the aggregator was never used, ** or it could be that it was used but there was no GROUP BY clause. */ if( pAgg->pCurrent ){ freeAggElem(pAgg->pCurrent, pAgg); } } /* If db is not NULL and we have not yet and we have not yet opened ** the temporary btree then do so and create the table to store aggregate ** information. ** ** If db is NULL, then close the temporary btree if it is open. */ if( db ){ if( !pAgg->pBtree ){ assert( pAgg->nTab==0 ); rc = sqlite3BtreeFactory(db, ":memory:", 0, TEMP_PAGES, &pAgg->pBtree); if( rc!=SQLITE_OK ) return rc; sqlite3BtreeBeginTrans(pAgg->pBtree, 1); rc = sqlite3BtreeCreateTable(pAgg->pBtree, &pAgg->nTab, 0); if( rc!=SQLITE_OK ) return rc; } assert( pAgg->nTab!=0 ); rc = sqlite3BtreeCursor(pAgg->pBtree, pAgg->nTab, 1, sqlite3VdbeRecordCompare, pKeyInfo, &pAgg->pCsr); if( rc!=SQLITE_OK ) return rc; }else{ if( pAgg->pBtree ){ sqlite3BtreeClose(pAgg->pBtree); pAgg->pBtree = 0; pAgg->nTab = 0; } pAgg->pCsr = 0; } if( pAgg->apFunc ){ sqliteFree(pAgg->apFunc); pAgg->apFunc = 0; } pAgg->pCurrent = 0; pAgg->nMem = 0; pAgg->searching = 0; return SQLITE_OK; }
/* sqlite3_rekey ** Given a database, this will reencrypt the database using a new key. ** There are two possible modes of operation. The first is rekeying ** an existing database that was not previously encrypted. The second ** is to change the key on an existing database. ** ** The proposed logic for this function follows: ** 1. Determine if there is already a key present ** 2. If there is NOT already a key present, create one and attach a codec (key would be null) ** 3. Initialize a ctx->rekey parameter of the codec ** ** Note: this will require modifications to the sqlite3Codec to support rekey ** */ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) { if(db && pKey && nKey) { int i, prepared_key_sz; int key_sz = EVP_CIPHER_key_length(CIPHER); void *key = sqlite3Malloc(key_sz); if(key == NULL) return SQLITE_NOMEM; for(i=0; i<db->nDb; i++){ struct Db *pDb = &db->aDb[i]; if(pDb->pBt) { codec_ctx *ctx; int rc, page_count; Pgno pgno; PgHdr *page; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx == NULL) { /* there was no codec attached to this database,so attach one now with a null password */ char *error; db->nextPagesize = sqlite3BtreeGetPageSize(pDb->pBt); pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */ sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_CIPHER_iv_length(CIPHER), 0); sqlite3RunVacuum(&error, db); sqlite3CodecAttach(db, i, pKey, nKey); sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); /* prepare this setup as if it had already been initialized */ RAND_pseudo_bytes(ctx->salt, FILE_HEADER_SZ); ctx->rekey_plaintext = 1; } codec_prepare_key(db, pKey, nKey, ctx->salt, FILE_HEADER_SZ, key, &prepared_key_sz); assert(prepared_key_sz == key_sz); ctx->rekey = key; /* set rekey to new key data - note that ctx->key is original encryption key */ /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx->rekey into ctx->key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */ rc = sqlite3PagerPagecount(pPager, &page_count); for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); //printf("sqlite3PagerWrite(%d)\n", pgno); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); } } } } /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ if(rc == SQLITE_OK) { rc = sqlite3BtreeCommit(pDb->pBt); memcpy(ctx->key, ctx->rekey, key_sz); if(ctx->pass) { memset(ctx->pass, 0, ctx->pass_sz); sqlite3_free(ctx->pass); } ctx->pass = sqlite3Malloc(nKey); if(ctx->pass == NULL) return SQLITE_NOMEM; memcpy(ctx->pass, pKey, nKey); ctx->pass_sz = nKey; } else { printf("error\n"); sqlite3BtreeRollback(pDb->pBt); } /* cleanup rekey data, make sure to overwrite rekey_plaintext or read errors will ensue */ ctx->rekey = NULL; ctx->rekey_plaintext = 0; } } /* clear and free temporary key data */ memset(key, 0, key_sz); sqlite3_free(key); return SQLITE_OK; } return SQLITE_ERROR; }
/* * Print statistics for tables and/or indexes using DB->stat_print() * * If msgfile is NULL, then statistics will be printed into stdout, otherwise * the statistics will be printed into the designated output stream. * * If name input is NULL, then statistics for all tables and indexes are * printed, otherwise only the statistics for that table or index is printed. * * Returns SQLITE_OK if there are no errors, -1 if an error occurred. */ SQLITE_API int bdbSqlDbStatPrint(sqlite3 *db, FILE *msgfile, char *name) { BtCursor cur, *pCur = NULL; Btree *p; char **azResult = NULL; char *zErrMsg = NULL; char *zSql = NULL; DB *dbp; FILE *out; int i, rc, nRow, iTable, openTransaction = 0; if (!db || !db->aDb) return -1; p = db->aDb[0].pBt; assert(p); if (!(out = msgfile)) out = stdout; /* Construct query to get root page number(s) */ if (!name) { zSql = sqlite3_mprintf( "SELECT type,name,rootpage FROM sqlite_master"); } else { zSql = sqlite3_mprintf( "SELECT type,name,rootpage FROM sqlite_master " "WHERE name='%q'", name); } if (!zSql) { fprintf(stderr, "Error: memory allocation failed\n"); goto err; } rc = sqlite3_get_table(db, zSql, &azResult, &nRow, 0, &zErrMsg); (void)sqlite3_free(zSql); if (zErrMsg) { fprintf(stderr, "Error: %s\n", zErrMsg); (void)sqlite3_free(zErrMsg); if (rc == SQLITE_OK) rc = -1; goto err; } else if (rc != SQLITE_OK) { fprintf(stderr, "Error: querying sqlite_master\n"); goto err; } else if (nRow < 1) goto err; rc = sqlite3BtreeBeginTrans(p, 0); if (rc != SQLITE_OK) { fprintf(stderr, "Error: could not enter a transaction\n"); goto err; } openTransaction = 1; /* Print stats for all tables in query's result */ for (i = 1; i <= nRow; i++) { fprintf(out, "Statistics for %s \"%s\"\n", azResult[3*i], azResult[3*i + 1]); iTable = atoi(azResult[3*i + 2]); pCur = &cur; (void)sqlite3BtreeCursorZero(pCur); /* Acquire a read cursor to retrieve the dbp for the table */ rc = sqlite3BtreeCursor(p, iTable, 0, NULL, pCur); if (pCur->eState == CURSOR_FAULT) rc = pCur->error; if (rc != SQLITE_OK) { fprintf(stderr, "Error: could not create cursor\n"); goto err; } assert(pCur->cached_db && pCur->cached_db->dbp); dbp = pCur->cached_db->dbp; (void)dbp->set_msgfile(dbp, out); (void)dbp->stat_print(dbp, DB_STAT_ALL); (void)sqlite3BtreeCloseCursor(&cur); pCur = NULL; } err: if (pCur) (void)sqlite3BtreeCloseCursor(pCur); if (openTransaction) sqlite3BtreeCommit(p); if (azResult) (void)sqlite3_free_table(azResult); return rc; }
/* ** Copy nPage pages from the source b-tree to the destination. */ int sqlite3_backup_step(sqlite3_backup *p, int nPage) { int returnCode, pages; Parse parse; DB_ENV *dbenv; BtShared *pBtDest, *pBtSrc; pBtDest = pBtSrc = NULL; if (p->rc != SQLITE_OK || nPage == 0) return p->rc; sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3_mutex_enter(p->pDestDb->mutex); /* * Make sure the schema has been read in, so the keyInfo * can be retrieved for the indexes. No-op if already read. * If the schema has not been read then an update must have * changed it, so backup will restart. */ memset(&parse, 0, sizeof(parse)); parse.db = p->pSrcDb; p->rc = sqlite3ReadSchema(&parse); if (p->rc != SQLITE_OK) goto err; /* * This process updated the source database, so * the backup process has to restart. */ if (p->pSrc->updateDuringBackup > p->lastUpdate) { p->rc = SQLITE_LOCKED; if ((p->rc = backupCleanup(p)) != SQLITE_OK) goto err; else backupReset(p); } pages = nPage; if (!p->cleaned) { const char *home; const char inmem[9] = ":memory:"; int storage; pBtDest = p->pDest->pBt; storage = p->pDest->pBt->dbStorage; if (storage == DB_STORE_NAMED) p->openDest = 1; p->rc = btreeDeleteEnvironment(p->pDest, p->fullName, 1); if (storage == DB_STORE_INMEM && strcmp(p->destName, "temp") != 0) home = inmem; else home = p->fullName; p->pDest = p->pDestDb->aDb[p->iDb].pBt; if (p->rc != SQLITE_OK) goto err; /* * Call sqlite3OpenTempDatabase instead of * sqlite3BtreeOpen, because sqlite3OpenTempDatabase * automatically chooses the right flags before calling * sqlite3BtreeOpen. */ if (strcmp(p->destName, "temp") == 0) { memset(&parse, 0, sizeof(parse)); parse.db = p->pDestDb; p->rc = sqlite3OpenTempDatabase(&parse); p->pDest = p->pDestDb->aDb[p->iDb].pBt; } else { p->rc = sqlite3BtreeOpen(home, p->pDestDb, &p->pDest, SQLITE_DEFAULT_CACHE_SIZE | SQLITE_OPEN_MAIN_DB, p->pDestDb->openFlags); p->pDestDb->aDb[p->iDb].pBt = p->pDest; if (p->rc == SQLITE_OK) { p->pDestDb->aDb[p->iDb].pSchema = sqlite3SchemaGet(p->pDestDb, p->pDest); if (!p->pDestDb->aDb[p->iDb].pSchema) p->rc = SQLITE_NOMEM; } else p->pDestDb->aDb[p->iDb].pSchema = NULL; } if (p->pDest) p->pDest->nBackup++; #ifdef SQLITE_HAS_CODEC /* * In the case of a temporary source database, use the * encryption of the main database. */ if (strcmp(p->srcName, "temp") == 0) { int iDb = sqlite3FindDbName(p->pSrcDb, "main"); pBtSrc = p->pSrcDb->aDb[iDb].pBt->pBt; } else pBtSrc = p->pSrc->pBt; if (p->rc == SQLITE_OK) { if (p->iDb == 0) p->rc = sqlite3_key(p->pDestDb, pBtSrc->encrypt_pwd, pBtSrc->encrypt_pwd_len); else p->rc = sqlite3CodecAttach(p->pDestDb, p->iDb, pBtSrc->encrypt_pwd, pBtSrc->encrypt_pwd_len); } #endif if (p->rc != SQLITE_OK) goto err; p->cleaned = 1; } /* * Begin a transaction, unfortuantely the lock on * the schema has to be released to allow the sqlite_master * table to be cleared, which could allow another thread to * alter it, however accessing the backup database during * backup is already an illegal condition with undefined * results. */ if (!sqlite3BtreeIsInTrans(p->pDest)) { if (!p->pDest->connected) { p->rc = btreeOpenEnvironment(p->pDest, 1); if (p->rc != SQLITE_OK) goto err; } if ((p->rc = sqlite3BtreeBeginTrans(p->pDest, 2)) != SQLITE_OK) goto err; } /* Only this process should be accessing the backup environment. */ if (p->pDest->pBt->nRef > 1) { p->rc = SQLITE_BUSY; goto err; } /* * Begin a transaction, a lock error or update could have caused * it to be released in a previous call to step. */ if (!p->srcTxn) { dbenv = p->pSrc->pBt->dbenv; if ((p->rc = dberr2sqlite(dbenv->txn_begin(dbenv, p->pSrc->family_txn, &p->srcTxn, 0))) != SQLITE_OK) goto err; } /* * An update could have dropped or created a table, so recalculate * the list of tables. */ if (!p->tables) { if ((p->rc = btreeGetPageCount(p->pSrc, &p->tables, &p->nPagecount, p->srcTxn)) != SQLITE_OK) { sqlite3Error(p->pSrcDb, p->rc, 0); goto err; } p->nRemaining = p->nPagecount; } /* Copy the pages. */ p->rc = btreeCopyPages(p, &pages); if (p->rc == SQLITE_DONE) { p->nRemaining = 0; sqlite3ResetInternalSchema(p->pDestDb, p->iDb); memset(&parse, 0, sizeof(parse)); parse.db = p->pDestDb; p->rc = sqlite3ReadSchema(&parse); if (p->rc == SQLITE_OK) p->rc = SQLITE_DONE; } else if (p->rc != SQLITE_OK) goto err; /* * The number of pages left to copy is an estimate, so * do not let the number go to zero unless we are really * done. */ if (p->rc != SQLITE_DONE) { if ((u32)pages >= p->nRemaining) p->nRemaining = 1; else p->nRemaining -= pages; } err: /* * This process updated the source database, so * the backup process has to restart. */ if (p->pSrc->updateDuringBackup > p->lastUpdate && (p->rc == SQLITE_OK || p->rc == SQLITE_DONE)) { int cleanCode; returnCode = p->rc; p->rc = SQLITE_LOCKED; if ((cleanCode = backupCleanup(p)) != SQLITE_OK) returnCode = p->rc = cleanCode; else backupReset(p); } else { returnCode = backupCleanup(p); if (returnCode == SQLITE_OK || (p->rc != SQLITE_OK && p->rc != SQLITE_DONE)) returnCode = p->rc; else p->rc = returnCode; } /* * On a locked or busy error the backup process is rolled back, * but can be restarted by the user. */ if ( returnCode == SQLITE_LOCKED || returnCode == SQLITE_BUSY ) backupReset(p); else if ( returnCode != SQLITE_OK && returnCode != SQLITE_DONE ) { sqlite3Error(p->pDestDb, p->rc, 0); } sqlite3_mutex_leave(p->pDestDb->mutex); sqlite3_mutex_leave(p->pSrcDb->mutex); return (returnCode); }
int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { u32 meta; int rc = 0; int command_idx = 0; int password_sz; int saved_flags; int saved_nChange; int saved_nTotalChange; void (*saved_xTrace)(void*,const char*); Db *pDb = 0; sqlite3 *db = ctx->pBt->db; const char *db_filename = sqlite3_db_filename(db, "main"); char *migrated_db_filename = sqlite3_mprintf("%s-migrated", db_filename); char *pragma_hmac_off = "PRAGMA cipher_use_hmac = OFF;"; char *pragma_4k_kdf_iter = "PRAGMA kdf_iter = 4000;"; char *pragma_1x_and_4k; char *set_user_version; char *key; int key_sz; int user_version = 0; int upgrade_1x_format = 0; int upgrade_4k_format = 0; static const unsigned char aCopy[] = { BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */ BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */ BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */ BTREE_USER_VERSION, 0, /* Preserve the user version */ BTREE_APPLICATION_ID, 0, /* Preserve the application id */ }; key_sz = ctx->read_ctx->pass_sz + 1; key = sqlcipher_malloc(key_sz); memset(key, 0, key_sz); memcpy(key, ctx->read_ctx->pass, ctx->read_ctx->pass_sz); if(db_filename){ const char* commands[5]; char *attach_command = sqlite3_mprintf("ATTACH DATABASE '%s-migrated' as migrate KEY '%q';", db_filename, key); int rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, "", &user_version); if(rc == SQLITE_OK){ CODEC_TRACE(("No upgrade required - exiting\n")); goto exit; } // Version 2 - check for 4k with hmac format rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_4k_kdf_iter, &user_version); if(rc == SQLITE_OK) { CODEC_TRACE(("Version 2 format found\n")); upgrade_4k_format = 1; } // Version 1 - check both no hmac and 4k together pragma_1x_and_4k = sqlite3_mprintf("%s%s", pragma_hmac_off, pragma_4k_kdf_iter); rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_1x_and_4k, &user_version); sqlite3_free(pragma_1x_and_4k); if(rc == SQLITE_OK) { CODEC_TRACE(("Version 1 format found\n")); upgrade_1x_format = 1; upgrade_4k_format = 1; } if(upgrade_1x_format == 0 && upgrade_4k_format == 0) { CODEC_TRACE(("Upgrade format not determined\n")); goto handle_error; } set_user_version = sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version); commands[0] = upgrade_4k_format == 1 ? pragma_4k_kdf_iter : ""; commands[1] = upgrade_1x_format == 1 ? pragma_hmac_off : ""; commands[2] = attach_command; commands[3] = "SELECT sqlcipher_export('migrate');"; commands[4] = set_user_version; for(command_idx = 0; command_idx < ArraySize(commands); command_idx++){ const char *command = commands[command_idx]; if(strcmp(command, "") == 0){ continue; } rc = sqlite3_exec(db, command, NULL, NULL, NULL); if(rc != SQLITE_OK){ break; } } sqlite3_free(attach_command); sqlite3_free(set_user_version); sqlcipher_free(key, key_sz); if(rc == SQLITE_OK){ Btree *pDest; Btree *pSrc; int i = 0; if( !db->autoCommit ){ CODEC_TRACE(("cannot migrate from within a transaction")); goto handle_error; } if( db->nVdbeActive>1 ){ CODEC_TRACE(("cannot migrate - SQL statements in progress")); goto handle_error; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; saved_xTrace = db->xTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin; db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder); db->xTrace = 0; pDest = db->aDb[0].pBt; pDb = &(db->aDb[db->nDb-1]); pSrc = pDb->pBt; rc = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL); rc = sqlite3BtreeBeginTrans(pSrc, 2); rc = sqlite3BtreeBeginTrans(pDest, 2); assert( 1==sqlite3BtreeIsInTrans(pDest) ); assert( 1==sqlite3BtreeIsInTrans(pSrc) ); sqlite3CodecGetKey(db, db->nDb - 1, (void**)&key, &password_sz); sqlite3CodecAttach(db, 0, key, password_sz); sqlite3pager_get_codec(pDest->pBt->pPager, (void**)&ctx); ctx->skip_read_hmac = 1; for(i=0; i<ArraySize(aCopy); i+=2){ sqlite3BtreeGetMeta(pSrc, aCopy[i], &meta); rc = sqlite3BtreeUpdateMeta(pDest, aCopy[i], meta+aCopy[i+1]); if( NEVER(rc!=SQLITE_OK) ) goto handle_error; } rc = sqlite3BtreeCopyFile(pDest, pSrc); ctx->skip_read_hmac = 0; if( rc!=SQLITE_OK ) goto handle_error; rc = sqlite3BtreeCommit(pDest); db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->xTrace = saved_xTrace; db->autoCommit = 1; if( pDb ){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; } sqlite3ResetAllSchemasOfConnection(db); remove(migrated_db_filename); sqlite3_free(migrated_db_filename); } else { CODEC_TRACE(("*** migration failure** \n\n")); } } goto exit; handle_error: CODEC_TRACE(("An error occurred attempting to migrate the database\n")); rc = SQLITE_ERROR; exit: return rc; }
/* ** Attempt to read the database schema and initialize internal ** data structures for a single database file. The index of the ** database file is given by iDb. iDb==0 is used for the main ** database. iDb==1 should never be used. iDb>=2 is used for ** auxiliary databases. Return one of the SQLITE_ error codes to ** indicate success or failure. */ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int rc; int i; int size; Table *pTab; Db *pDb; char const *azArg[4]; int meta[5]; InitData initData; char const *zMasterSchema; char const *zMasterName = SCHEMA_TABLE(iDb); int openedTransaction = 0; /* ** The master database table has a structure like this */ static const char master_schema[] = "CREATE TABLE sqlite_master(\n" " type text,\n" " name text,\n" " tbl_name text,\n" " rootpage integer,\n" " sql text\n" ")" ; #ifndef SQLITE_OMIT_TEMPDB static const char temp_master_schema[] = "CREATE TEMP TABLE sqlite_temp_master(\n" " type text,\n" " name text,\n" " tbl_name text,\n" " rootpage integer,\n" " sql text\n" ")" ; #else #define temp_master_schema 0 #endif assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pSchema ); assert( sqlite3_mutex_held(db->mutex) ); assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); /* zMasterSchema and zInitScript are set to point at the master schema ** and initialisation script appropriate for the database being ** initialised. zMasterName is the name of the master table. */ if( !OMIT_TEMPDB && iDb==1 ){ zMasterSchema = temp_master_schema; }else{ zMasterSchema = master_schema; } zMasterName = SCHEMA_TABLE(iDb); /* Construct the schema tables. */ azArg[0] = zMasterName; azArg[1] = "1"; azArg[2] = zMasterSchema; azArg[3] = 0; initData.db = db; initData.iDb = iDb; initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; (void)sqlite3SafetyOff(db); sqlite3InitCallback(&initData, 3, (char **)azArg, 0); (void)sqlite3SafetyOn(db); if( initData.rc ){ rc = initData.rc; goto error_out; } pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName); if( ALWAYS(pTab) ){ pTab->tabFlags |= TF_Readonly; } /* Create a cursor to hold the database open */ pDb = &db->aDb[iDb]; if( pDb->pBt==0 ){ if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){ DbSetProperty(db, 1, DB_SchemaLoaded); } return SQLITE_OK; } /* If there is not already a read-only (or read-write) transaction opened ** on the b-tree database, open one now. If a transaction is opened, it ** will be closed before this function returns. */ sqlite3BtreeEnter(pDb->pBt); if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ rc = sqlite3BtreeBeginTrans(pDb->pBt, 0); if( rc!=SQLITE_OK ){ sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); goto initone_error_out; } openedTransaction = 1; } /* Get the database meta information. ** ** Meta values are as follows: ** meta[0] Schema cookie. Changes with each schema change. ** meta[1] File format of schema layer. ** meta[2] Size of the page cache. ** meta[3] Largest rootpage (auto/incr_vacuum mode) ** meta[4] Db text encoding. 1:UTF-8 2:UTF-16LE 3:UTF-16BE ** meta[5] User version ** meta[6] Incremental vacuum mode ** meta[7] unused ** meta[8] unused ** meta[9] unused ** ** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to ** the possible values of meta[4]. */ for(i=0; i<ArraySize(meta); i++){ sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]); } pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; /* If opening a non-empty database, check the text encoding. For the ** main database, set sqlite3.enc to the encoding of the main database. ** For an attached db, it is an error if the encoding is not the same ** as sqlite3.enc. */ if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */ if( iDb==0 ){ u8 encoding; /* If opening the main database, set ENC(db). */ encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; if( encoding==0 ) encoding = SQLITE_UTF8; ENC(db) = encoding; db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0); }else{ /* If opening an attached database, the encoding much match ENC(db) */ if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){ sqlite3SetString(pzErrMsg, db, "attached databases must use the same" " text encoding as main database"); rc = SQLITE_ERROR; goto initone_error_out; } } }else{ DbSetProperty(db, iDb, DB_Empty); } pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ size = meta[BTREE_DEFAULT_CACHE_SIZE-1]; if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } if( size<0 ) size = -size; pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } /* ** file_format==1 Version 3.0.0. ** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN ** file_format==3 Version 3.1.4. // ditto but with non-NULL defaults ** file_format==4 Version 3.3.0. // DESC indices. Boolean constants */ pDb->pSchema->file_format = (u8)meta[BTREE_FILE_FORMAT-1]; if( pDb->pSchema->file_format==0 ){ pDb->pSchema->file_format = 1; } if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){ sqlite3SetString(pzErrMsg, db, "unsupported file format"); rc = SQLITE_ERROR; goto initone_error_out; } /* Ticket #2804: When we open a database in the newer file format, ** clear the legacy_file_format pragma flag so that a VACUUM will ** not downgrade the database and thus invalidate any descending ** indices that the user might have created. */ if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ db->flags &= ~SQLITE_LegacyFileFmt; } /* Read the schema information out of the schema tables */ assert( db->init.busy ); { char *zSql; zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s", db->aDb[iDb].zName, zMasterName); (void)sqlite3SafetyOff(db); #ifndef SQLITE_OMIT_AUTHORIZATION { int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); xAuth = db->xAuth; db->xAuth = 0; #endif rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = xAuth; } #endif if( rc==SQLITE_OK ) rc = initData.rc; (void)sqlite3SafetyOn(db); sqlite3DbFree(db, zSql); #ifndef SQLITE_OMIT_ANALYZE if( rc==SQLITE_OK ){ sqlite3AnalysisLoad(db, iDb); } #endif } if( db->mallocFailed ){ rc = SQLITE_NOMEM; sqlite3ResetInternalSchema(db, 0); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider ** the schema loaded, even if errors occurred. In this situation the ** current sqlite3_prepare() operation will fail, but the following one ** will attempt to compile the supplied statement against whatever subset ** of the schema was loaded before the error occurred. The primary ** purpose of this is to allow access to the sqlite_master table ** even when its contents have been corrupted. */ DbSetProperty(db, iDb, DB_SchemaLoaded); rc = SQLITE_OK; } /* Jump here for an error that occurs after successfully allocating ** curMain and calling sqlite3BtreeEnter(). For an error that occurs ** before that point, jump to error_out. */ initone_error_out: if( openedTransaction ){ sqlite3BtreeCommit(pDb->pBt); } sqlite3BtreeLeave(pDb->pBt); error_out: if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; } return rc; }
/* sqlite3_rekey ** Given a database, this will reencrypt the database using a new key. ** There are two possible modes of operation. The first is rekeying ** an existing database that was not previously encrypted. The second ** is to change the key on an existing database. ** ** The proposed logic for this function follows: ** 1. Determine if there is already a key present ** 2. If there is NOT already a key present, create one and attach a codec (key would be null) ** 3. Initialize a ctx->rekey parameter of the codec ** ** Note: this will require modifications to the sqlite3Codec to support rekey ** */ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey)); activate_openssl(); if(db && pKey && nKey) { struct Db *pDb = &db->aDb[0]; CODEC_TRACE(("sqlite3_rekey: database pDb=%d\n", pDb)); if(pDb->pBt) { codec_ctx *ctx; int rc, page_count; Pgno pgno; PgHdr *page; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx == NULL) { CODEC_TRACE(("sqlite3_rekey: no codec attached to db, attaching now\n")); /* there was no codec attached to this database,so attach one now with a null password */ sqlite3CodecAttach(db, 0, pKey, nKey); sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); /* prepare this setup as if it had already been initialized */ RAND_pseudo_bytes(ctx->kdf_salt, ctx->kdf_salt_sz); ctx->read_ctx->key_sz = ctx->read_ctx->iv_sz = ctx->read_ctx->pass_sz = 0; } sqlite3_mutex_enter(db->mutex); if(ctx->read_ctx->iv_sz != ctx->write_ctx->iv_sz) { char *error; CODEC_TRACE(("sqlite3_rekey: updating page size for iv_sz change from %d to %d\n", ctx->read_ctx->iv_sz, ctx->write_ctx->iv_sz)); db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE; pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */ sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_MAX_IV_LENGTH, 0); sqlite3RunVacuum(&error, db); } codec_set_pass_key(db, 0, pKey, nKey, 1); ctx->mode_rekey = 1; /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx->rekey into ctx->key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */ sqlite3PagerPagecount(pPager, &page_count); for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); //printf("sqlite3PagerWrite(%d)\n", pgno); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); } } } } /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ if(rc == SQLITE_OK) { CODEC_TRACE(("sqlite3_rekey: committing\n")); db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE; rc = sqlite3BtreeCommit(pDb->pBt); cipher_ctx_copy(ctx->read_ctx, ctx->write_ctx); } else { CODEC_TRACE(("sqlite3_rekey: rollback\n")); sqlite3BtreeRollback(pDb->pBt); } ctx->mode_rekey = 0; sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; } return SQLITE_ERROR; }
/* ** This routine implements the OP_Vacuum opcode of the VDBE. */ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ int rc = SQLITE_OK; /* Return code from service routines */ #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM const char *zFilename; /* full pathname of the database file */ int nFilename; /* number of characters in zFilename[] */ char *zTemp = 0; /* a temporary file in same directory as zFilename */ int i; /* Loop counter */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; char *zSql = 0; if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", (char*)0); rc = SQLITE_ERROR; goto end_of_vacuum; } /* Get the full pathname of the database file and create a ** temporary filename in the same directory as the original file. */ pMain = db->aDb[0].pBt; zFilename = sqlite3BtreeGetFilename(pMain); assert( zFilename ); if( zFilename[0]=='\0' ){ /* The in-memory database. Do nothing. Return directly to avoid causing ** an error trying to DETACH the vacuum_db (which never got attached) ** in the exit-handler. */ return SQLITE_OK; } nFilename = strlen(zFilename); zTemp = sqliteMalloc( nFilename+100 ); if( zTemp==0 ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } strcpy(zTemp, zFilename); i = 0; do { zTemp[nFilename] = '-'; randomName((unsigned char*)&zTemp[nFilename+1]); } while( i<10 && sqlite3OsFileExists(zTemp) ); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** ** An optimisation would be to use a non-journaled pager. */ zSql = sqlite3MPrintf("ATTACH '%q' AS vacuum_db;", zTemp); if( !zSql ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } rc = execSql(db, zSql); sqliteFree(zSql); zSql = 0; if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); pTemp = db->aDb[db->nDb-1].pBt; sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), sqlite3BtreeGetReserve(pMain)); assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) ); execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); /* Begin a transaction */ rc = execSql(db, "BEGIN;"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ rc = execExecSql(db, "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) " " FROM sqlite_master WHERE type='table' " "UNION ALL " "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000) " " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' " "UNION ALL " "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) " " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'" "UNION ALL " "SELECT 'CREATE VIEW vacuum_db.' || substr(sql,13,100000000) " " FROM sqlite_master WHERE type='view'" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy ** the contents to the temporary database. */ rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM ' || quote(name) || ';'" "FROM sqlite_master " "WHERE type = 'table';" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy the triggers from the main database to the temporary database. ** This was deferred before in case the triggers interfered with copying ** the data. It's possible the indices should be deferred until this ** point also. */ rc = execExecSql(db, "SELECT 'CREATE TRIGGER vacuum_db.' || substr(sql, 16, 1000000) " "FROM sqlite_master WHERE type='trigger'" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* At this point, unless the main db was completely empty, there is now a ** transaction open on the vacuum database, but not on the main database. ** Open a btree level transaction on the main database. This allows a ** call to sqlite3BtreeCopyFile(). The main database btree level ** transaction is then committed, so the SQL level never knows it was ** opened for writing. This way, the SQL transaction used to create the ** temporary database never needs to be committed. */ if( sqlite3BtreeIsInTrans(pTemp) ){ u32 meta; assert( 0==sqlite3BtreeIsInTrans(pMain) ); rc = sqlite3BtreeBeginTrans(pMain, 1); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta ** values 2 and 3, the default values of a couple of pragmas. */ rc = sqlite3BtreeGetMeta(pMain, 3, &meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeUpdateMeta(pTemp, 3, meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeGetMeta(pMain, 4, &meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeUpdateMeta(pTemp, 4, meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCopyFile(pMain, pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pMain); } end_of_vacuum: /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; if( rc==SQLITE_OK ){ rc = execSql(db, "DETACH vacuum_db;"); }else{ execSql(db, "DETACH vacuum_db;"); } if( zTemp ){ sqlite3OsDelete(zTemp); sqliteFree(zTemp); } if( zSql ) sqliteFree( zSql ); sqlite3ResetInternalSchema(db, 0); #endif return rc; }
// Changes the encryption key for an existing database. int sqlite3_rekey(sqlite3 *db, const unsigned char *pKey, int nKeySize) { Btree *pbt = db->aDb[0].pBt; Pager *p = sqlite3BtreePager(pbt); LPCRYPTBLOCK pBlock = (LPCRYPTBLOCK)sqlite3pager_get_codecarg(p); HCRYPTKEY hKey = DeriveKey(pKey, nKeySize); int rc = SQLITE_ERROR; if (hKey == MAXDWORD) { sqlite3Error(db, rc, SQLITECRYPTERROR_PROVIDER); return rc; } if (!pBlock && !hKey) return SQLITE_OK; // Wasn't encrypted to begin with // To rekey a database, we change the writekey for the pager. The readkey remains // the same if (!pBlock) // Encrypt an unencrypted database { pBlock = CreateCryptBlock(hKey, p, -1, NULL); if (!pBlock) return SQLITE_NOMEM; pBlock->hReadKey = 0; // Original database is not encrypted sqlite3PagerSetCodec(sqlite3BtreePager(pbt), sqlite3Codec, sqlite3CodecSizeChange, sqlite3CodecFree, pBlock); //db->aDb[0].pAux = pBlock; //db->aDb[0].xFreeAux = DestroyCryptBlock; } else // Change the writekey for an already-encrypted database { pBlock->hWriteKey = hKey; } // Start a transaction rc = sqlite3BtreeBeginTrans(pbt, 1); if (!rc) { // Rewrite all the pages in the database using the new encryption key Pgno nPage; Pgno nSkip = PAGER_MJ_PGNO(p); DbPage *pPage; Pgno n; rc = sqlite3PagerPagecount(p, &nPage); for(n = 1; rc == SQLITE_OK && n <= nPage; n ++) { if (n == nSkip) continue; rc = sqlite3PagerGet(p, n, &pPage); if(!rc) { rc = sqlite3PagerWrite(pPage); sqlite3PagerUnref(pPage); } } } // If we succeeded, try and commit the transaction if (!rc) { rc = sqlite3BtreeCommit(pbt); } // If we failed, rollback if (rc) { sqlite3BtreeRollback(pbt); } // If we succeeded, destroy any previous read key this database used // and make the readkey equal to the writekey if (!rc) { if (pBlock->hReadKey) { CryptDestroyKey(pBlock->hReadKey); } pBlock->hReadKey = pBlock->hWriteKey; } // We failed. Destroy the new writekey (if there was one) and revert it back to // the original readkey else { if (pBlock->hWriteKey) { CryptDestroyKey(pBlock->hWriteKey); } pBlock->hWriteKey = pBlock->hReadKey; } // If the readkey and writekey are both empty, there's no need for a codec on this // pager anymore. Destroy the crypt block and remove the codec from the pager. if (!pBlock->hReadKey && !pBlock->hWriteKey) { sqlite3PagerSetCodec(p, NULL, NULL, NULL, NULL); } return rc; }
int sqlite3_rekey_v2(sqlite3 *db, const char *zDbName, const void *zKey, int nKey) { /* Changes the encryption key for an existing database. */ int dbIndex = dbFindIndex(db, zDbName); int rc = SQLITE_ERROR; Btree* pbt = db->aDb[dbIndex].pBt; Pager* pPager = sqlite3BtreePager(pbt); Codec* codec = (Codec*) mySqlite3PagerGetCodec(pPager); if ((zKey == NULL || nKey == 0) && (codec == NULL || !CodecIsEncrypted(codec))) { /* // Database not encrypted and key not specified // therefore do nothing */ return SQLITE_OK; } if (codec == NULL || !CodecIsEncrypted(codec)) { /* // Database not encrypted, but key specified // therefore encrypt database */ if (codec == NULL) { codec = (Codec*) sqlite3_malloc(sizeof(Codec)); CodecInit(codec); } CodecSetIsEncrypted(codec, 1); CodecSetHasReadKey(codec, 0); /* Original database is not encrypted */ CodecSetHasWriteKey(codec, 1); CodecGenerateWriteKey(codec, (char*) zKey, nKey); CodecSetBtree(codec, pbt); #if (SQLITE_VERSION_NUMBER >= 3006016) mySqlite3PagerSetCodec(pPager, sqlite3Codec, sqlite3CodecSizeChange, sqlite3CodecFree, codec); #else #if (SQLITE_VERSION_NUMBER >= 3003014) sqlite3PagerSetCodec(pPager, sqlite3Codec, codec); #else sqlite3pager_set_codec(pPager, sqlite3Codec, codec); #endif db->aDb[dbIndex].pAux = codec; db->aDb[dbIndex].xFreeAux = sqlite3CodecFree; #endif } else if (zKey == NULL || nKey == 0) { /* // Database encrypted, but key not specified // therefore decrypt database // Keep read key, drop write key */ CodecSetHasWriteKey(codec, 0); } else { /* // Database encrypted and key specified // therefore re-encrypt database with new key // Keep read key, change write key to new key */ CodecGenerateWriteKey(codec, (char*) zKey, nKey); CodecSetHasWriteKey(codec, 1); } sqlite3_mutex_enter(db->mutex); /* Start transaction */ rc = sqlite3BtreeBeginTrans(pbt, 1); if (!rc) { int pageSize = sqlite3BtreeGetPageSize(pbt); Pgno nSkip = WX_PAGER_MJ_PGNO(pageSize); #if (SQLITE_VERSION_NUMBER >= 3003014) DbPage *pPage; #else void *pPage; #endif Pgno n; /* Rewrite all pages using the new encryption key (if specified) */ #if (SQLITE_VERSION_NUMBER >= 3007001) Pgno nPage; int nPageCount = -1; sqlite3PagerPagecount(pPager, &nPageCount); nPage = nPageCount; #elif (SQLITE_VERSION_NUMBER >= 3006000) int nPageCount = -1; int rc = sqlite3PagerPagecount(pPager, &nPageCount); Pgno nPage = (Pgno) nPageCount; #elif (SQLITE_VERSION_NUMBER >= 3003014) Pgno nPage = sqlite3PagerPagecount(pPager); #else Pgno nPage = sqlite3pager_pagecount(pPager); #endif for (n = 1; rc == SQLITE_OK && n <= nPage; n++) { if (n == nSkip) continue; #if (SQLITE_VERSION_NUMBER >= 3003014) rc = sqlite3PagerGet(pPager, n, &pPage); #else rc = sqlite3pager_get(pPager, n, &pPage); #endif if (!rc) { #if (SQLITE_VERSION_NUMBER >= 3003014) rc = sqlite3PagerWrite(pPage); sqlite3PagerUnref(pPage); #else rc = sqlite3pager_write(pPage); sqlite3pager_unref(pPage); #endif } } } if (rc == SQLITE_OK) { /* Commit transaction if all pages could be rewritten */ rc = sqlite3BtreeCommit(pbt); } if (rc != SQLITE_OK) { /* Rollback in case of error */ #if (SQLITE_VERSION_NUMBER >= 3007011) sqlite3BtreeRollback(pbt, SQLITE_OK); #else sqlite3BtreeRollback(pbt); #endif } sqlite3_mutex_leave(db->mutex); if (rc == SQLITE_OK) { /* Set read key equal to write key if necessary */ if (CodecHasWriteKey(codec)) { CodecCopyKey(codec, 0); CodecSetHasReadKey(codec, 1); } else { CodecSetIsEncrypted(codec, 0); } } else { /* Restore write key if necessary */ if (CodecHasReadKey(codec)) { CodecCopyKey(codec, 1); } else { CodecSetIsEncrypted(codec, 0); } } if (!CodecIsEncrypted(codec)) { /* Remove codec for unencrypted database */ #if (SQLITE_VERSION_NUMBER >= 3006016) mySqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); #else #if (SQLITE_VERSION_NUMBER >= 3003014) sqlite3PagerSetCodec(pPager, NULL, NULL); #else sqlite3pager_set_codec(pPager, NULL, NULL); #endif db->aDb[dbIndex].pAux = NULL; db->aDb[dbIndex].xFreeAux = NULL; sqlite3CodecFree(codec); #endif } return rc; }
/* ** Copy nPage pages from the source b-tree to the destination. */ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; int destMode; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ int pgszDest = 0; /* Destination page size */ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ) return SQLITE_MISUSE_BKPT; #endif sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3BtreeEnter(p->pSrc); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } rc = p->rc; if( !isFatalError(rc) ){ Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */ Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */ int ii; /* Iterator variable */ int nSrcPage = -1; /* Size of source db in pages */ int bCloseTrans = 0; /* True if src db requires unlocking */ /* If the source pager is currently in a write-transaction, return ** SQLITE_BUSY immediately. */ if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ rc = SQLITE_BUSY; }else{ rc = SQLITE_OK; } /* If there is no open read-transaction on the source database, open ** one now. If a transaction is opened here, then it will be closed ** before this function exits. */ if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0); bCloseTrans = 1; } /* If the destination database has not yet been locked (i.e. if this ** is the first call to backup_step() for the current backup operation), ** try to set its page size to the same as the source database. This ** is especially important on ZipVFS systems, as in that case it is ** not possible to create a database file that uses one page size by ** writing to it with another. */ if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){ rc = SQLITE_NOMEM; } /* Lock the destination database, if it is not locked already. */ if( SQLITE_OK==rc && p->bDestLocked==0 && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2, (int*)&p->iDestSchema)) ){ p->bDestLocked = 1; } /* Do not allow backup if the destination database is in WAL mode ** and the page sizes are different between source and destination */ pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); pgszDest = sqlite3BtreeGetPageSize(p->pDest); destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ rc = SQLITE_READONLY; } /* Now that there is a read-lock on the source database, query the ** source pager for the number of pages in the database. */ nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc); assert( nSrcPage>=0 ); for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){ const Pgno iSrcPg = p->iNext; /* Source page number */ if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ DbPage *pSrcPg; /* Source page object */ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY); if( rc==SQLITE_OK ){ rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0); sqlite3PagerUnref(pSrcPg); } } p->iNext++; } if( rc==SQLITE_OK ){ p->nPagecount = nSrcPage; p->nRemaining = nSrcPage+1-p->iNext; if( p->iNext>(Pgno)nSrcPage ){ rc = SQLITE_DONE; }else if( !p->isAttached ){ attachBackupObject(p); } } /* Update the schema version field in the destination database. This ** is to make sure that the schema-version really does change in ** the case where the source and destination databases have the ** same schema version. */ if( rc==SQLITE_DONE ){ if( nSrcPage==0 ){ rc = sqlite3BtreeNewDb(p->pDest); nSrcPage = 1; } if( rc==SQLITE_OK || rc==SQLITE_DONE ){ rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); } if( rc==SQLITE_OK ){ if( p->pDestDb ){ sqlite3ResetAllSchemasOfConnection(p->pDestDb); } if( destMode==PAGER_JOURNALMODE_WAL ){ rc = sqlite3BtreeSetVersion(p->pDest, 2); } } if( rc==SQLITE_OK ){ int nDestTruncate; /* Set nDestTruncate to the final number of pages in the destination ** database. The complication here is that the destination page ** size may be different to the source page size. ** ** If the source page size is smaller than the destination page size, ** round up. In this case the call to sqlite3OsTruncate() below will ** fix the size of the file. However it is important to call ** sqlite3PagerTruncateImage() here so that any pages in the ** destination file that lie beyond the nDestTruncate page mark are ** journalled by PagerCommitPhaseOne() before they are destroyed ** by the file truncation. */ assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) ); assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) ); if( pgszSrc<pgszDest ){ int ratio = pgszDest/pgszSrc; nDestTruncate = (nSrcPage+ratio-1)/ratio; if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){ nDestTruncate--; } }else{ nDestTruncate = nSrcPage * (pgszSrc/pgszDest); } assert( nDestTruncate>0 ); if( pgszSrc<pgszDest ){ /* If the source page-size is smaller than the destination page-size, ** two extra things may need to happen: ** ** * The destination may need to be truncated, and ** ** * Data stored on the pages immediately following the ** pending-byte page in the source database may need to be ** copied into the destination database. */ const i64 iSize = (i64)pgszSrc * (i64)nSrcPage; sqlite3_file * const pFile = sqlite3PagerFile(pDestPager); Pgno iPg; int nDstPage; i64 iOff; i64 iEnd; assert( pFile ); assert( nDestTruncate==0 || (i64)nDestTruncate*(i64)pgszDest >= iSize || ( nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest )); /* This block ensures that all data required to recreate the original ** database has been stored in the journal for pDestPager and the ** journal synced to disk. So at this point we may safely modify ** the database file in any way, knowing that if a power failure ** occurs, the original database will be reconstructed from the ** journal file. */ sqlite3PagerPagecount(pDestPager, &nDstPage); for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){ if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){ DbPage *pPg; rc = sqlite3PagerGet(pDestPager, iPg, &pPg, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pPg); sqlite3PagerUnref(pPg); } } } if( rc==SQLITE_OK ){ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); } /* Write the extra pages and truncate the database file as required */ iEnd = MIN(PENDING_BYTE + pgszDest, iSize); for( iOff=PENDING_BYTE+pgszSrc; rc==SQLITE_OK && iOff<iEnd; iOff+=pgszSrc ){ PgHdr *pSrcPg = 0; const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1); rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg, 0); if( rc==SQLITE_OK ){ u8 *zData = sqlite3PagerGetData(pSrcPg); rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff); } sqlite3PagerUnref(pSrcPg); } if( rc==SQLITE_OK ){ rc = backupTruncateFile(pFile, iSize); } /* Sync the database file to disk. */ if( rc==SQLITE_OK ){ rc = sqlite3PagerSync(pDestPager, 0); } }else{ sqlite3PagerTruncateImage(pDestPager, nDestTruncate); rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0)) ){ rc = SQLITE_DONE; } } } /* If bCloseTrans is true, then this function opened a read transaction ** on the source database. Close the read transaction here. There is ** no need to check the return values of the btree methods here, as ** "committing" a read-only transaction cannot fail. */ if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); assert( rc2==SQLITE_OK ); } if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM_BKPT; } p->rc = rc; }
/* sqlite3_rekey_v2 ** Given a database, this will reencrypt the database using a new key. ** There is only one possible modes of operation - to encrypt a database ** that is already encrpyted. If the database is not already encrypted ** this should do nothing ** The proposed logic for this function follows: ** 1. Determine if the database is already encryptped ** 2. If there is NOT already a key present do nothing ** 3. If there is a key present, re-encrypt the database with the new key */ int sqlite3_rekey_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_rekey_v2: entered db=%p zDb=%s pKey=%s, nKey=%d\n", db, zDb, (char *)pKey, nKey)); if(db && pKey && nKey) { int db_index = sqlcipher_find_db_index(db, zDb); struct Db *pDb = &db->aDb[db_index]; CODEC_TRACE(("sqlite3_rekey_v2: database pDb=%p db_index:%d\n", pDb, db_index)); if(pDb->pBt) { codec_ctx *ctx; int rc, page_count; Pgno pgno; PgHdr *page; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx == NULL) { /* there was no codec attached to this database, so this should do nothing! */ CODEC_TRACE(("sqlite3_rekey_v2: no codec attached to db, exiting\n")); return SQLITE_OK; } sqlite3_mutex_enter(db->mutex); codec_set_pass_key(db, db_index, pKey, nKey, CIPHER_WRITE_CTX); /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx->rekey into ctx->key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */ sqlite3PagerPagecount(pPager, &page_count); for(pgno = 1; rc == SQLITE_OK && pgno <= (unsigned int)page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); } else { CODEC_TRACE(("sqlite3_rekey_v2: error %d occurred writing page %d\n", rc, pgno)); } } else { CODEC_TRACE(("sqlite3_rekey_v2: error %d occurred getting page %d\n", rc, pgno)); } } } /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ if(rc == SQLITE_OK) { CODEC_TRACE(("sqlite3_rekey_v2: committing\n")); rc = sqlite3BtreeCommit(pDb->pBt); sqlcipher_codec_key_copy(ctx, CIPHER_WRITE_CTX); } else { CODEC_TRACE(("sqlite3_rekey_v2: rollback\n")); sqlite3BtreeRollback(pDb->pBt, SQLITE_ABORT_ROLLBACK); } sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; } return SQLITE_ERROR; }
/* ** Copy nPage pages from the source b-tree to the destination. */ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; int destMode; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ int pgszDest = 0; /* Destination page size */ sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3BtreeEnter(p->pSrc); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } rc = p->rc; if( !isFatalError(rc) ){ Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */ Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */ int ii; /* Iterator variable */ int nSrcPage = -1; /* Size of source db in pages */ int bCloseTrans = 0; /* True if src db requires unlocking */ /* If the source pager is currently in a write-transaction, return ** SQLITE_BUSY immediately. */ if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ rc = SQLITE_BUSY; }else{ rc = SQLITE_OK; } /* Lock the destination database, if it is not locked already. */ if( SQLITE_OK==rc && p->bDestLocked==0 && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) ){ p->bDestLocked = 1; sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); } /* If there is no open read-transaction on the source database, open ** one now. If a transaction is opened here, then it will be closed ** before this function exits. */ if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ rc = sqlite3BtreeBeginTrans(p->pSrc, 0); bCloseTrans = 1; } /* Do not allow backup if the destination database is in WAL mode ** and the page sizes are different between source and destination */ pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); pgszDest = sqlite3BtreeGetPageSize(p->pDest); destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ rc = SQLITE_READONLY; } /* Now that there is a read-lock on the source database, query the ** source pager for the number of pages in the database. */ nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc); assert( nSrcPage>=0 ); for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){ const Pgno iSrcPg = p->iNext; /* Source page number */ if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ DbPage *pSrcPg; /* Source page object */ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); if( rc==SQLITE_OK ){ rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg)); sqlite3PagerUnref(pSrcPg); } } p->iNext++; } if( rc==SQLITE_OK ){ p->nPagecount = nSrcPage; p->nRemaining = nSrcPage+1-p->iNext; if( p->iNext>(Pgno)nSrcPage ){ rc = SQLITE_DONE; }else if( !p->isAttached ){ attachBackupObject(p); } } /* Update the schema version field in the destination database. This ** is to make sure that the schema-version really does change in ** the case where the source and destination databases have the ** same schema version. */ if( rc==SQLITE_DONE && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK ){ int nDestTruncate; if( p->pDestDb ){ sqlite3ResetInternalSchema(p->pDestDb, 0); } /* Set nDestTruncate to the final number of pages in the destination ** database. The complication here is that the destination page ** size may be different to the source page size. ** ** If the source page size is smaller than the destination page size, ** round up. In this case the call to sqlite3OsTruncate() below will ** fix the size of the file. However it is important to call ** sqlite3PagerTruncateImage() here so that any pages in the ** destination file that lie beyond the nDestTruncate page mark are ** journalled by PagerCommitPhaseOne() before they are destroyed ** by the file truncation. */ assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) ); assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) ); if( pgszSrc<pgszDest ){ int ratio = pgszDest/pgszSrc; nDestTruncate = (nSrcPage+ratio-1)/ratio; if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){ nDestTruncate--; } }else{ nDestTruncate = nSrcPage * (pgszSrc/pgszDest); } sqlite3PagerTruncateImage(pDestPager, nDestTruncate); if( pgszSrc<pgszDest ){ /* If the source page-size is smaller than the destination page-size, ** two extra things may need to happen: ** ** * The destination may need to be truncated, and ** ** * Data stored on the pages immediately following the ** pending-byte page in the source database may need to be ** copied into the destination database. */ const i64 iSize = (i64)pgszSrc * (i64)nSrcPage; sqlite3_file * const pFile = sqlite3PagerFile(pDestPager); assert( pFile ); assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || ( nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest )); if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1)) && SQLITE_OK==(rc = backupTruncateFile(pFile, iSize)) && SQLITE_OK==(rc = sqlite3PagerSync(pDestPager)) ){ i64 iOff; i64 iEnd = MIN(PENDING_BYTE + pgszDest, iSize); for( iOff=PENDING_BYTE+pgszSrc; rc==SQLITE_OK && iOff<iEnd; iOff+=pgszSrc ){ PgHdr *pSrcPg = 0; const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1); rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); if( rc==SQLITE_OK ){ u8 *zData = sqlite3PagerGetData(pSrcPg); rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff); } sqlite3PagerUnref(pSrcPg); } } }else{ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest)) ){ rc = SQLITE_DONE; } } /* If bCloseTrans is true, then this function opened a read transaction ** on the source database. Close the read transaction here. There is ** no need to check the return values of the btree methods here, as ** "committing" a read-only transaction cannot fail. */ if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc); assert( rc2==SQLITE_OK ); } if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } p->rc = rc; }