int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_key: entered db=%d pKey=%s nKey=%d\n", db, pKey, nKey)); /* attach key if db and pKey are not null and nKey is > 0 */ if(db && pKey && nKey) { sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db return SQLITE_OK; } return SQLITE_ERROR; }
int sqlite3_key_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_key_v2: entered db=%p zDb=%s pKey=%s nKey=%d\n", db, zDb, (char *)pKey, nKey)); /* attach key if db and pKey are not null and nKey is > 0 */ if(db && pKey && nKey) { int db_index = sqlcipher_find_db_index(db, zDb); return sqlite3CodecAttach(db, db_index, pKey, nKey); } return SQLITE_ERROR; }
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) { /* attach key if db and pKey are not null and nKey is > 0 */ if(db && pKey && nKey) { int i; for(i=0; i<db->nDb; i++){ sqlite3CodecAttach(db, i, pKey, nKey); } return SQLITE_OK; } return SQLITE_ERROR; }
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) { //RAWLOG_INFO("sqlite3_key"); if ( db && pKey && nKey ) { sqlite3CodecAttach(db, 0, pKey, nKey); return SQLITE_OK; } return SQLITE_ERROR; }
int sqlite3_key_v2( sqlite3 *db, const char *zDbName, const void *key, int nkey) { int backend; const char *dbname; // NULL is an alias of the "main" database. if (zDbName == NULL) dbname = "main"; else dbname = zDbName; for(backend = 0; backend < db->nDb; backend++) { if (db->aDb[backend].zName == NULL) continue; if (sqlite3StrICmp(db->aDb[backend].zName, dbname) == 0) break; } if (backend == db->nDb) return SQLITE_NOTFOUND; return sqlite3CodecAttach(db, backend, key, nkey); }
/* ** An SQL user-function registered to do the work of an ATTACH statement. The ** three arguments to the function come directly from an attach statement: ** ** ATTACH DATABASE x AS y KEY z ** ** SELECT sqlite_attach(x, y, z) ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the ** third argument. */ static void attachFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv ){ int i; int rc = 0; sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; Db *aNew; char *zErrDyn = 0; UNUSED_PARAMETER(NotUsed); zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; /* Check for the following errors: ** ** * Too many attached databases, ** * Transaction currently open ** * Specified database name already being used. */ if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", db->aLimit[SQLITE_LIMIT_ATTACHED] ); goto attach_error; } if( !db->autoCommit ){ zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction"); goto attach_error; } for(i=0; i<db->nDb; i++){ char *z = db->aDb[i].zName; assert( z && zName ); if( sqlite3StrICmp(z, zName)==0 ){ zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); goto attach_error; } } /* Allocate the new entry in the db->aDb[] array and initialise the schema ** hash tables. */ if( db->aDb==db->aDbStatic ){ aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 ); if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE, db->openFlags | SQLITE_OPEN_MAIN_DB, &aNew->pBt); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Pager *pPager; aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); if( !aNew->pSchema ){ rc = SQLITE_NOMEM; }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } pPager = sqlite3BtreePager(aNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); sqlite3PagerJournalMode(pPager, db->dfltJournalMode); } aNew->zName = sqlite3DbStrDup(db, zName); aNew->safety_level = 3; #if SQLITE_HAS_CODEC { 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]); sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; } } #endif /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and ** remove the entry from the db->aDb[] array. i.e. put everything back the way ** we found it. */ if( rc==SQLITE_OK ){ (void)sqlite3SafetyOn(db); sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrDyn); sqlite3BtreeLeaveAll(db); (void)sqlite3SafetyOff(db); } if( rc ){ int iDb = db->nDb - 1; assert( iDb>=2 ); if( db->aDb[iDb].pBt ){ sqlite3BtreeClose(db->aDb[iDb].pBt); db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } sqlite3ResetInternalSchema(db, 0); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; sqlite3DbFree(db, zErrDyn); zErrDyn = sqlite3MPrintf(db, "out of memory"); }else if( zErrDyn==0 ){ zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); } goto attach_error; } return; attach_error: /* Return an error if we get here */ if( zErrDyn ){ sqlite3_result_error(context, zErrDyn, -1); sqlite3DbFree(db, zErrDyn); } if( rc ) sqlite3_result_error_code(context, rc); }
int sqlite3_key(sqlite3 *db, const void *zKey, int nKey) { /* The key is only set for the main database, not the temp database */ return sqlite3CodecAttach(db, 0, zKey, nKey); }
/* * sqlite3_key() is called only to set the encryption key for the "main" * backend database, that is db->aDb[0]. The encryption key for attached * databases is set directly via sqlite3CodecAttach(). */ int sqlite3_key(sqlite3 *db, const void *key, int nkey) { return sqlite3CodecAttach(db, 0, key, nkey); }
/* ** An SQL user-function registered to do the work of an ATTACH statement. The ** three arguments to the function come directly from an attach statement: ** ** ATTACH DATABASE x AS y KEY z ** ** SELECT sqlite_attach(x, y, z) ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the ** third argument. */ static void attachFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ int i; int rc = 0; sqlite3 *db = sqlite3_user_data(context); const char *zName; const char *zFile; Db *aNew; char zErr[128]; char *zErrDyn = 0; zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); /* Check for the following errors: ** ** * Too many attached databases, ** * Transaction currently open ** * Specified database name already being used. */ if( db->nDb>=MAX_ATTACHED+2 ){ sqlite3_snprintf( 127, zErr, "too many attached databases - max %d", MAX_ATTACHED ); goto attach_error; } if( !db->autoCommit ){ strcpy(zErr, "cannot ATTACH database within transaction"); goto attach_error; } for(i=0; i<db->nDb; i++){ char *z = db->aDb[i].zName; if( z && sqlite3StrICmp(z, zName)==0 ){ sqlite3_snprintf(127, zErr, "database %s is already in use", zName); goto attach_error; } } /* Allocate the new entry in the db->aDb[] array and initialise the schema ** hash tables. */ if( db->aDb==db->aDbStatic ){ aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); if( aNew==0 ){ return; } memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ){ return; } } db->aDb = aNew; aNew = &db->aDb[db->nDb++]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); if( rc==SQLITE_OK ){ aNew->pSchema = sqlite3SchemaGet(aNew->pBt); if( !aNew->pSchema ){ rc = SQLITE_NOMEM; }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ strcpy(zErr, "attached databases must use the same text encoding as main database"); goto attach_error; } } aNew->zName = sqliteStrDup(zName); aNew->safety_level = 3; #if SQLITE_HAS_CODEC { extern int sqlite3CodecAttach(sqlite3*, int, 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 = sqliteStrDup("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]); sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; } } #endif /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and ** remove the entry from the db->aDb[] array. i.e. put everything back the way ** we found it. */ if( rc==SQLITE_OK ){ sqlite3SafetyOn(db); rc = sqlite3Init(db, &zErrDyn); sqlite3SafetyOff(db); } if( rc ){ int i = db->nDb - 1; assert( i>=2 ); if( db->aDb[i].pBt ){ sqlite3BtreeClose(db->aDb[i].pBt); db->aDb[i].pBt = 0; db->aDb[i].pSchema = 0; } sqlite3ResetInternalSchema(db, 0); db->nDb = i; sqlite3_snprintf(127, zErr, "unable to open database: %s", zFile); goto attach_error; } return; attach_error: /* Return an error if we get here */ if( zErrDyn ){ sqlite3_result_error(context, zErrDyn, -1); sqliteFree(zErrDyn); }else{ zErr[sizeof(zErr)-1] = 0; sqlite3_result_error(context, zErr, -1); } }
int sqlite3_key(sqlite3 *db, const void *zKey, int nKey) { BOTANSQLITE_TRACE("sqlite3_key"); // The key is only set for the main database, not the temp database return sqlite3CodecAttach(db, 0, zKey, nKey); }
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; }
/* 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; }
// We do not attach this key to the temp store, only the main database. int sqlite3_key(sqlite3 *db, const unsigned char *pKey, int nKeySize) { return sqlite3CodecAttach(db, 0, pKey, nKeySize); }
/* ** An SQL user-function registered to do the work of an ATTACH statement. The ** three arguments to the function come directly from an attach statement: ** ** ATTACH DATABASE x AS y KEY z ** ** SELECT sqlite_attach(x, y, z) ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the ** third argument. */ static void attachFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv ){ int i; int rc = 0; sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; char *zPath = 0; char *zErr = 0; unsigned int flags; Db *aNew; char *zErrDyn = 0; sqlite3_vfs *pVfs; UNUSED_PARAMETER(NotUsed); zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; /* Check for the following errors: ** ** * Too many attached databases, ** * Transaction currently open ** * Specified database name already being used. */ if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", db->aLimit[SQLITE_LIMIT_ATTACHED] ); goto attach_error; } if( !db->autoCommit ){ zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction"); goto attach_error; } for(i=0; i<db->nDb; i++){ char *z = db->aDb[i].zName; assert( z && zName ); if( sqlite3StrICmp(z, zName)==0 ){ zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); goto attach_error; } } /* Allocate the new entry in the db->aDb[] array and initialize the schema ** hash tables. */ if( db->aDb==db->aDbStatic ){ aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialized. */ flags = db->openFlags; rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); sqlite3_result_error(context, zErr, -1); sqlite3_free(zErr); return; } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags); sqlite3_free( zPath ); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Pager *pPager; aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); if( !aNew->pSchema ){ rc = SQLITE_NOMEM; }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } sqlite3BtreeEnter(aNew->pBt); pPager = sqlite3BtreePager(aNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); sqlite3BtreeSecureDelete(aNew->pBt, sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); #ifndef SQLITE_OMIT_PAGER_PRAGMAS sqlite3BtreeSetPagerFlags(aNew->pBt, PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK)); #endif sqlite3BtreeLeave(aNew->pBt); } aNew->safety_level = 3; aNew->zName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && aNew->zName==0 ){ rc = SQLITE_NOMEM; } #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 the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); if( nKey>0 || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); } break; } } #endif /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and ** remove the entry from the db->aDb[] array. i.e. put everything back the way ** we found it. */ if( rc==SQLITE_OK ){ sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrDyn); sqlite3BtreeLeaveAll(db); } #ifdef SQLITE_USER_AUTHENTICATION if( rc==SQLITE_OK ){ u8 newAuth = 0; rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); if( newAuth<db->auth.authLevel ){ rc = SQLITE_AUTH_USER; } } #endif if( rc ){ int iDb = db->nDb - 1; assert( iDb>=2 ); if( db->aDb[iDb].pBt ){ sqlite3BtreeClose(db->aDb[iDb].pBt); db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } sqlite3ResetAllSchemasOfConnection(db); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ sqlite3OomFault(db); sqlite3DbFree(db, zErrDyn); zErrDyn = sqlite3MPrintf(db, "out of memory"); }else if( zErrDyn==0 ){ zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); } goto attach_error; } return; attach_error: /* Return an error if we get here */ if( zErrDyn ){ sqlite3_result_error(context, zErrDyn, -1); sqlite3DbFree(db, zErrDyn); } if( rc ) sqlite3_result_error_code(context, rc); }
static void attachFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv ){ int i; int rc = 0; sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; Db *aNew; char *zErrDyn = 0; UNUSED_PARAMETER(NotUsed); zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", db->aLimit[SQLITE_LIMIT_ATTACHED] ); goto attach_error; } if( !db->autoCommit ){ zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction"); goto attach_error; } for(i=0; i<db->nDb; i++){ char *z = db->aDb[i].zName; assert( z && zName ); if( sqlite3StrICmp(z, zName)==0 ){ zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); goto attach_error; } } if( db->aDb==db->aDbStatic ){ aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 ); if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0, db->openFlags | SQLITE_OPEN_MAIN_DB); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Pager *pPager; aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); if( !aNew->pSchema ){ rc = SQLITE_NOMEM; }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } pPager = sqlite3BtreePager(aNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); sqlite3BtreeSecureDelete(aNew->pBt, sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); } aNew->safety_level = 3; aNew->zName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && aNew->zName==0 ){ rc = SQLITE_NOMEM; } #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: sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); } break; } } #endif if( rc==SQLITE_OK ){ sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrDyn); sqlite3BtreeLeaveAll(db); } if( rc ){ int iDb = db->nDb - 1; assert( iDb>=2 ); if( db->aDb[iDb].pBt ){ sqlite3BtreeClose(db->aDb[iDb].pBt); db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } sqlite3ResetInternalSchema(db, -1); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; sqlite3DbFree(db, zErrDyn); zErrDyn = sqlite3MPrintf(db, "out of memory"); }else if( zErrDyn==0 ){ zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); } goto attach_error; } return; attach_error: if( zErrDyn ){ sqlite3_result_error(context, zErrDyn, -1); sqlite3DbFree(db, zErrDyn); } if( rc ) sqlite3_result_error_code(context, 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); }
/* Close or free all handles and commit or rollback the transaction. */ static int backupCleanup(sqlite3_backup *p) { int rc, rc2, ret; void *app; DB *db; rc = rc2 = SQLITE_OK; if (!p || p->rc == SQLITE_OK) return rc; rc2 = sqlite3BtreeCloseCursor(&p->destCur); if (rc2 != SQLITE_OK) rc = rc2; if (p->srcCur) { db = p->srcCur->dbp; app = db->app_private; if ((ret = p->srcCur->close(p->srcCur)) == 0) ret = db->close(db, DB_NOSYNC); rc2 = dberr2sqlite(ret); /* * The KeyInfo was allocated in btreeSetupIndex, * so have to deallocate it here. */ if (app) sqlite3DbFree(p->pSrcDb, app); } if (rc2 != SQLITE_OK) rc = rc2; p->srcCur = 0; /* * May retry on a locked or busy error, so keep * these values. */ if (p->rc != SQLITE_LOCKED && p->rc != SQLITE_BUSY) { if (p->srcName) sqlite3_free(p->srcName); if (p->destName != 0) sqlite3_free(p->destName); p->srcName = p->destName = NULL; } if (p->tables != 0) sqlite3_free(p->tables); p->tables = NULL; if (p->pSrc->nBackup) p->pSrc->nBackup--; if (p->pDest != NULL && p->pDest->nBackup) p->pDest->nBackup--; if (p->srcTxn) { if (p->rc == SQLITE_DONE) ret = p->srcTxn->commit(p->srcTxn, 0); else ret = p->srcTxn->abort(p->srcTxn); rc2 = dberr2sqlite(ret); } p->srcTxn = 0; if (rc2 != SQLITE_OK && sqlite3BtreeIsInTrans(p->pDest)) { rc = rc2; if (p->rc == SQLITE_DONE) rc2 = sqlite3BtreeCommit(p->pDest); else rc2 = sqlite3BtreeRollback(p->pDest); if (rc2 != SQLITE_OK) rc = rc2; } if (p->pDest && p->openDest) { char path[512]; /* * If successfully done then delete the old backup, if * an error then delete the current database and restore * the old backup. */ sqlite3_snprintf(sizeof(path), path, "%s%s", p->fullName, BACKUP_SUFFIX); if (p->rc == SQLITE_DONE) { rc2 = btreeDeleteEnvironment(p->pDest, path, 0); } else { rc2 = btreeDeleteEnvironment(p->pDest, p->fullName, 0); if (!__os_exists(NULL, path, 0)) __os_rename(NULL, path, p->fullName, 0); } if (rc == SQLITE_OK) rc = rc2; if (rc == SQLITE_OK) { p->pDest = NULL; p->pDestDb->aDb[p->iDb].pBt = NULL; p->openDest = 0; rc = sqlite3BtreeOpen(p->fullName, p->pDestDb, &p->pDest, SQLITE_DEFAULT_CACHE_SIZE | SQLITE_OPEN_MAIN_DB, p->pDestDb->openFlags); p->pDestDb->aDb[p->iDb].pBt = p->pDest; if (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 (rc == SQLITE_OK) p->pDest->pBt->db_oflags |= DB_CREATE; /* * Have to delete the schema here on error to avoid * assert failure. */ if (p->pDest == NULL && p->pDestDb->aDb[p->iDb].pSchema != NULL) { sqlite3SchemaClear( p->pDestDb->aDb[p->iDb].pSchema); p->pDestDb->aDb[p->iDb].pSchema = NULL; } #ifdef SQLITE_HAS_CODEC if (rc == SQLITE_OK) { if (p->iDb == 0) rc = sqlite3_key(p->pDestDb, p->pSrc->pBt->encrypt_pwd, p->pSrc->pBt->encrypt_pwd_len); else rc = sqlite3CodecAttach(p->pDestDb, p->iDb, p->pSrc->pBt->encrypt_pwd, p->pSrc->pBt->encrypt_pwd_len); } #endif } } if (p->rc != SQLITE_LOCKED && p->rc != SQLITE_BUSY) { if (p->fullName != 0) sqlite3_free(p->fullName); p->fullName = NULL; } p->lastUpdate = p->pSrc->updateDuringBackup; return rc; }
int sqlite3_key_v2(sqlite3 *db, const char *zDbName, const void *zKey, int nKey) { /* The key is only set for the main database, not the temp database */ int dbIndex = dbFindIndex(db, zDbName); return sqlite3CodecAttach(db, dbIndex, zKey, nKey); }
/* ** This routine is called by the parser to process an ATTACH statement: ** ** ATTACH DATABASE filename AS dbname ** ** The pFilename and pDbname arguments are the tokens that define the ** filename and dbname in the ATTACH statement. */ void sqlite3Attach( Parse *pParse, /* The parser context */ Token *pFilename, /* Name of database file */ Token *pDbname, /* Name of the database to use internally */ int keyType, /* 0: no key. 1: TEXT, 2: BLOB */ Token *pKey /* Text of the key for keytype 1 and 2 */ ){ Db *aNew; int rc, i; char *zFile, *zName; sqlite3 *db; Vdbe *v; v = sqlite3GetVdbe(pParse); if( !v ) return; sqlite3VdbeAddOp(v, OP_Halt, 0, 0); if( pParse->explain ) return; db = pParse->db; if( db->nDb>=MAX_ATTACHED+2 ){ sqlite3ErrorMsg(pParse, "too many attached databases - max %d", MAX_ATTACHED); pParse->rc = SQLITE_ERROR; return; } if( !db->autoCommit ){ sqlite3ErrorMsg(pParse, "cannot ATTACH database within transaction"); pParse->rc = SQLITE_ERROR; return; } zFile = sqlite3NameFromToken(pFilename);; if( zFile==0 ) return; #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){ sqliteFree(zFile); return; } #endif /* SQLITE_OMIT_AUTHORIZATION */ zName = sqlite3NameFromToken(pDbname); if( zName==0 ) return; for(i=0; i<db->nDb; i++){ char *z = db->aDb[i].zName; if( z && sqlite3StrICmp(z, zName)==0 ){ sqlite3ErrorMsg(pParse, "database %z is already in use", zName); pParse->rc = SQLITE_ERROR; sqliteFree(zFile); return; } } if( db->aDb==db->aDbStatic ){ aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; aNew = &db->aDb[db->nDb++]; memset(aNew, 0, sizeof(*aNew)); sqlite3HashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0); sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); aNew->zName = zName; aNew->safety_level = 3; rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); if( rc ){ sqlite3ErrorMsg(pParse, "unable to open database: %s", zFile); } #if SQLITE_HAS_CODEC { extern int sqlite3CodecAttach(sqlite3*, int, void*, int); char *zKey; int nKey; if( keyType==0 ){ /* No key specified. Use the key from the main database */ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); }else if( keyType==1 ){ /* Key specified as text */ zKey = sqlite3NameFromToken(pKey); nKey = strlen(zKey); }else{ /* Key specified as a BLOB */ char *zTemp; assert( keyType==2 ); pKey->z++; pKey->n--; zTemp = sqlite3NameFromToken(pKey); zKey = sqlite3HexToBlob(zTemp); sqliteFree(zTemp); } sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); if( keyType ){ sqliteFree(zKey); } } #endif sqliteFree(zFile); db->flags &= ~SQLITE_Initialized; if( pParse->nErr==0 && rc==SQLITE_OK ){ rc = sqlite3ReadSchema(pParse); } if( rc ){ int i = db->nDb - 1; assert( i>=2 ); if( db->aDb[i].pBt ){ sqlite3BtreeClose(db->aDb[i].pBt); db->aDb[i].pBt = 0; } sqlite3ResetInternalSchema(db, 0); if( 0==pParse->nErr ){ pParse->nErr++; pParse->rc = SQLITE_ERROR; } } }
/* 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; }