-
Samuel H. Kenyon authoredSamuel H. Kenyon authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sqlite_index.cpp 22.62 KiB
/*
* The Sleuth Kit
*
* Brian Carrier [carrier <at> sleuthkit [dot] org]
* Copyright (c) 2003-2011 Brian Carrier. All rights reserved
*
*
* This software is distributed under the Common Public License 1.0
*
*/
#include "tsk_hashdb_i.h"
/**
* \file sqlite_index.c
* Contains functions for creating a SQLite format hash index
*/
/**
* Static sqlite statements, prepared initially and bound before each use
*/
static sqlite3_stmt *m_stmt = NULL;
static bool need_SQL_index = false;
/**
* Prototypes
*/
//int8_t sqlite_v1_get_properties(TSK_HDB_INFO * hdb_info);
uint8_t sqlite_v1_addentry_bin(TSK_HDB_INFO * hdb_info, uint8_t* hvalue, int hlen, TSK_OFF_T offset);
uint8_t addentry_text(TSK_HDB_INFO * hdb_info, char* hvalue, TSK_OFF_T offset);
int8_t lookup_text(TSK_HDB_INFO * hdb_info, const char* hvalue, TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, void *ptr);
static int attempt(int resultCode, int expectedResultCode,
const char *errfmt, sqlite3 * sqlite)
{
if (resultCode != expectedResultCode) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr(errfmt, sqlite3_errmsg(sqlite), resultCode);
return 1;
}
return 0;
}
static int attempt_exec(const char *sql, int (*callback) (void *, int, char **, char **),
void *callback_arg, const char *errfmt, sqlite3 * sqlite)
{
char * errmsg;
if(sqlite3_exec(sqlite, sql, callback, callback_arg, &errmsg) != SQLITE_OK) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr(errfmt, errmsg);
sqlite3_free(errmsg);
return 1;
}
return 0;
}
static int attempt_exec_nocallback(const char *sql, const char *errfmt, sqlite3 * sqlite)
{
return attempt_exec(sql, NULL, NULL, errfmt, sqlite);
}
static int finalize_stmt(sqlite3_stmt * stmt)
{
if (sqlite3_finalize(stmt) != SQLITE_OK) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error finalizing SQL statement\n");
tsk_error_print(stderr);
return 1;
}
return 0;
}
static int prepare_stmt(const char *sql, sqlite3_stmt ** ppStmt, sqlite3 * sqlite)
{
///@todo possible performance increase by using strlen(sql)+1 instead of -1
if (sqlite3_prepare_v2(sqlite, sql, -1, ppStmt, NULL) != SQLITE_OK) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error preparing SQL statement: %s\n", sql);
tsk_error_print(stderr);
return 1;
}
return 0;
}
static uint8_t tsk_hdb_begin_transaction(TSK_IDX_INFO * idx_info) {
return attempt_exec_nocallback("BEGIN", "Error beginning transaction %s\n", idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
}
static uint8_t tsk_hdb_commit_transaction(TSK_IDX_INFO * idx_info) {
return attempt_exec_nocallback("COMMIT", "Error committing transaction %s\n", idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
}
/** Init prepared statements. Call before adding to the database. Call finalize() when done.
*
* @param hdb_info Hash database state structure
*
* @return 1 on error and 0 on success
*
*/
uint8_t
sqlite_v1_begin(TSK_HDB_INFO * hdb_info)
{
char * insertStmt;
if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) {
insertStmt = "INSERT INTO hashes (md5, database_offset) VALUES (?, ?)";
} else if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) {
insertStmt = "INSERT INTO hashes (sha1, database_offset) VALUES (?, ?)";
} else {
return 1;
}
prepare_stmt(insertStmt, &m_stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
if (tsk_hdb_begin_transaction(hdb_info->idx_info)) {
return 1;
} else {
return 0;
}
}
/** Initialize the TSK hash DB index file by creating tables, etc..
*
* @param hdb_info Hash database state structure
* @param htype String of index type to create
*
* @return 1 on error and 0 on success
*
*/
uint8_t
sqlite_v1_initialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
{
char stmt[1024];
if (attempt_exec_nocallback("PRAGMA synchronous = OFF;",
"Error setting PRAGMA synchronous: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
if (attempt_exec_nocallback
("CREATE TABLE properties (name TEXT, value TEXT);",
"Error creating properties table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
snprintf(stmt, 1024,
"INSERT INTO properties (name, value) VALUES ('%s', '%s');",
IDX_SCHEMA_VER, IDX_VERSION_NUM);
if (attempt_exec_nocallback(stmt, "Error adding schema info to properties: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
snprintf(stmt, 1024,
"INSERT INTO properties (name, value) VALUES ('%s', '%s');",
IDX_HASHSET_NAME, hdb_info->db_name);
if (attempt_exec_nocallback(stmt, "Error adding name to properties: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
snprintf(stmt, 1024,
"INSERT INTO properties (name, value) VALUES ('%s', '%s');",
IDX_HASHSET_UPDATEABLE, (hdb_info->idx_info->updateable == 1) ? "true" : "false");
if (attempt_exec_nocallback(stmt, "Error adding updateable to properties: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
#ifdef IDX_SQLITE_STORE_TEXT
if (attempt_exec_nocallback
("CREATE TABLE hashes (id INTEGER PRIMARY KEY AUTOINCREMENT, md5 TEXT UNIQUE, sha1 TEXT, sha2_256 TEXT, database_offset INTEGER);",
"Error creating hashes table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
#else
if (attempt_exec_nocallback
("CREATE TABLE hashes (id INTEGER PRIMARY KEY AUTOINCREMENT, md5 BINARY(16) UNIQUE, sha1 BINARY(20), sha2_256 BINARY(32), database_offset INTEGER);",
"Error creating hashes table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
#endif
// The names table enables the user to optionally map one or many names to each hash.
// "name" should be the filename without the path.
if (attempt_exec_nocallback
("CREATE TABLE names (name TEXT, hash_id INTEGER);",
"Error creating names table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
return 1;
}
need_SQL_index = true;
return sqlite_v1_begin(hdb_info);
}
/**
* Add a string representation of a hash value to the index.
*
* @param hdb_info Hash database state info
* @param hvalue String of hash value to add
* @param offset Byte offset of hash entry in original database.
* @return 1 on error and 0 on success
*/
uint8_t
sqlite_v1_addentry(TSK_HDB_INFO * hdb_info, char* hvalue,
TSK_OFF_T offset)
{
if (strlen(hvalue) != hdb_info->hash_len) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Hash length doesn't match index type: %s\n", hvalue);
tsk_error_print(stderr);
return 1;
}
#ifdef IDX_SQLITE_STORE_TEXT
uint8_t ret = addentry_text(hdb_info, hvalue, offset);
#else
const size_t len = (hdb_info->hash_len)/2;
uint8_t* hash = (uint8_t*) tsk_malloc(len+1);
size_t count;
// We use an intermediate short to be compatible with Microsoft's implementation of the scanf family format
short unsigned int binval;
for (count = 0; count < len; count++) {
int r = sscanf(hvalue, "%2hx", &binval);
hash[count] = (uint8_t) binval;
hvalue += 2 * sizeof(char);
}
uint8_t ret = sqlite_v1_addentry_bin(hdb_info, hash, len, offset);
delete [] hash;
#endif
return ret;
}
/**
* Add a binary representation of a hash value into the index.
*
* @param hdb_info Hash database state info
* @param hvalue Array of integers of hash value to add
* @param hlen Number of bytes in hvalue
* @param offset Byte offset of hash entry in original database.
* @return 1 on error and 0 on success
*/
uint8_t
sqlite_v1_addentry_bin(TSK_HDB_INFO * hdb_info, uint8_t* hvalue, int hlen,
TSK_OFF_T offset)
{
if (attempt(sqlite3_bind_blob(m_stmt, 1, hvalue, hlen, SQLITE_TRANSIENT),
SQLITE_OK,
"Error binding binary blob: %s\n",
hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) ||
attempt(sqlite3_bind_int64(m_stmt, 2, offset),
SQLITE_OK,
"Error binding entry offset: %s\n",
hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) ) {
return 1;
}
// Don't report error on constraint -- we just will silently not add that duplicate hash
int r = sqlite3_step(m_stmt);
if ((r != SQLITE_DONE) && (r != SQLITE_CONSTRAINT) ) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error stepping: %s\n", sqlite3_errmsg( hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite), r);
return 1;
}
r = sqlite3_reset(m_stmt);
if ((r != SQLITE_OK) && (r != SQLITE_CONSTRAINT) ) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error resetting: %s\n", sqlite3_errmsg( hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite), r);
return 1;
}
return 0;
}
/**
* Add a text representation of a hash value into the index.
*
* @param hdb_info Hash database state info
* @param hvalue String of hash value to add
* @param hlen Number of bytes in hvalue
* @param offset Byte offset of hash entry in original database.
* @return 1 on error and 0 on success
*/
uint8_t
addentry_text(TSK_HDB_INFO * hdb_info, char* hvalue, TSK_OFF_T offset)
{
if (attempt(sqlite3_bind_text(m_stmt, 1, hvalue, strlen(hvalue), SQLITE_TRANSIENT),
SQLITE_OK,
"Error binding text: %s\n",
hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) ||
attempt(sqlite3_bind_int64(m_stmt, 2, offset),
SQLITE_OK,
"Error binding entry offset: %s\n",
hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) ) {
return 1;
}
// Don't report error on constraint -- we just will silently not add that duplicate hash
int r = sqlite3_step(m_stmt);
if ((r != SQLITE_DONE) && (r != SQLITE_CONSTRAINT) ) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error stepping: %s\n", sqlite3_errmsg( hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite), r);
return 1;
}
r = sqlite3_reset(m_stmt);
if ((r != SQLITE_OK) && (r != SQLITE_CONSTRAINT) ) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Error resetting: %s\n", sqlite3_errmsg( hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite), r);
return 1;
}
return 0;
}
/**
* Finalize index creation process
*
* @param hdb_info Hash database state info structure.
* @return 1 on error and 0 on success
*/
uint8_t
sqlite_v1_finalize(TSK_HDB_INFO * hdb_info)
{
if (tsk_hdb_commit_transaction(hdb_info->idx_info)) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_AUTO_DB);
tsk_error_set_errstr("Failed to commit transaction\n");
tsk_error_print(stderr);
return 1;
}
// We create the indexes at the end in order to make adding the initial batch of data (e.g. indexing an NSRL db)
// faster. Updates after indexing can be slower since the index has to update as well.
if (need_SQL_index) {
need_SQL_index = false;
return attempt_exec_nocallback
("CREATE INDEX md5_index ON hashes(md5);",
"Error creating md5_index on md5: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) ||
attempt_exec_nocallback
("CREATE INDEX sha1_index ON hashes(sha1);",
"Error creating sha1_index on sha1: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
} else {
return 0;
}
}
/** \internal
* Setup the internal variables to read an index or database. This
* opens the index and sets the needed size information.
*
* @param hdb_info Hash database to analyze
* @param htype The hash type that was used to make the index.
*
* @return 1 on error and 0 on success
*/
uint8_t
sqlite_v1_open(TSK_HDB_INFO * hdb_info, TSK_IDX_INFO * idx_info, uint8_t htype)
{
sqlite3 * sqlite = NULL;
if ((idx_info->idx_struct.idx_sqlite_v1 =
(TSK_IDX_SQLITE_V1 *) tsk_malloc
(sizeof(TSK_IDX_SQLITE_V1))) == NULL) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_HDB_ARG);
tsk_error_set_errstr(
"sqlite_v1_open: Malloc error");
return 1;
}
if ((htype != TSK_HDB_HTYPE_MD5_ID)
&& (htype != TSK_HDB_HTYPE_SHA1_ID)
&& (htype != TSK_HDB_HTYPE_SHA2_256_ID)) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_HDB_ARG);
tsk_error_set_errstr(
"hdb_setupindex: Invalid hash type : %d", htype);
return 1;
}
#ifdef TSK_WIN32
{
if (attempt(sqlite3_open16(idx_info->idx_fname, &sqlite),
SQLITE_OK,
"Can't open index: %s\n", sqlite)) {
sqlite3_close(sqlite);
return 1;
}
}
#else
{
if (attempt(sqlite3_open(idx_info->idx_fname, &sqlite),
SQLITE_OK,
"Can't open index: %s\n", sqlite)) {
sqlite3_close(sqlite);
return 1;
}
}
#endif
sqlite3_extended_result_codes(sqlite, 1);
idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite = sqlite;
return 0;
}
/**
* \ingroup hashdblib
* Search the index for a text/ASCII hash value
*
* @param hdb_info Open hash database (with index)
* @param hash Hash value to search for (NULL terminated string)
* @param flags Flags to use in lookup
* @param action Callback function to call for each hash db entry
* (not called if QUICK flag is given)
* @param ptr Pointer to data to pass to each callback
*
* @return -1 on error, 0 if hash value not found, and 1 if value was found.
*/
int8_t
sqlite_v1_lookup_str(TSK_HDB_INFO * hdb_info, const char* hvalue,
TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action,
void *ptr)
{
int8_t ret = 0;
#ifdef IDX_SQLITE_STORE_TEXT
ret = lookup_text(hdb_info, hvalue, flags, action, ptr);
#else
const size_t len = strlen(hvalue)/2;
uint8_t * hashBlob = (uint8_t *) tsk_malloc(len+1);
const char * pos = hvalue;
size_t count = 0;
for(count = 0; count < len; count++) {
sscanf(pos, "%2hx", (short unsigned int *) &(hashBlob[count]));
pos += 2 * sizeof(char);
}
ret = sqlite_v1_lookup_raw(hdb_info, hashBlob, len, flags, action, ptr);
#endif
if ((ret == 1) && (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)
&& !(flags & TSK_HDB_FLAG_QUICK) && (action != NULL)) {
//name is blank because we don't have a name in this case
///@todo query the names table for associations
char * name = "";
action(hdb_info, hvalue, name, ptr);
}
return ret;
}
/**
* \ingroup hashdblib
* Search the index for the given hash value given (in binary form).
*
* @param hdb_info Open hash database (with index)
* @param hash Array with binary hash value to search for
* @param len Number of bytes in binary hash value
* @param flags Flags to use in lookup
* @param action Callback function to call for each hash db entry
* (not called if QUICK flag is given)
* @param ptr Pointer to data to pass to each callback
*
* @return -1 on error, 0 if hash value not found, and 1 if value was found.
*/
int8_t
sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hvalue, uint8_t len,
TSK_HDB_FLAG_ENUM flags,
TSK_HDB_LOOKUP_FN action, void *ptr)
{
char hashbuf[TSK_HDB_HTYPE_SHA1_LEN + 1];
int8_t ret = 0;
int i;
static const char hex[] = "0123456789abcdef";
TSK_OFF_T offset;
char * selectStmt;
sqlite3_stmt* stmt = NULL;
tsk_take_lock(&hdb_info->lock);
/* Sanity check */
if ((hdb_info->hash_len)/2 != len) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_HDB_ARG);
tsk_error_set_errstr("hdb_lookup: Hash passed is different size than expected: %d vs %d",
hdb_info->hash_len, (len * 2));
ret = -1;
} else {
if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) {
selectStmt = "SELECT md5,database_offset from hashes where md5=? limit 1";
} else if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) {
selectStmt = "SELECT sha1,database_offset from hashes where sha1=? limit 1";
} else {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_HDB_ARG);
tsk_error_set_errstr("Unknown hash type: %d\n", hdb_info->hash_type);
ret = -1;
}
if (ret != -1) {
prepare_stmt(selectStmt, &stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
if (attempt(sqlite3_bind_blob(stmt, 1, hvalue, len, free),
SQLITE_OK,
"Error binding binary blob: %s\n",
hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) {
ret = -1;
} else {
// Found a match
if (sqlite3_step(stmt) == SQLITE_ROW) {
if ((flags & TSK_HDB_FLAG_QUICK)
|| (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)) {
// There is just an index, so no other info to get
///@todo Look up a name in the sqlite db
ret = 1;
} else {
// Use offset to get more info
for (i = 0; i < len; i++) {
hashbuf[2 * i] = hex[(hvalue[i] >> 4) & 0xf];
hashbuf[2 * i + 1] = hex[hvalue[i] & 0xf];
}
hashbuf[2 * len] = '\0';
offset = sqlite3_column_int64(stmt, 1);
if (hdb_info->getentry(hdb_info, hashbuf, offset, flags, action, ptr)) {
tsk_error_set_errstr2("hdb_lookup");
ret = -1;
} else {
ret = 1;
}
}
}
}
sqlite3_reset(stmt);
if (stmt) {
finalize_stmt(stmt);
}
}
}
tsk_release_lock(&hdb_info->lock);
return ret;
}
/**
* \ingroup hashdblib
* Search the index for the given hash value given (in string form).
*
* @param hdb_info Open hash database (with index)
* @param hash String hash value to search for
* @param flags Flags to use in lookup
* @param action Callback function to call for each hash db entry
* (not called if QUICK flag is given)
* @param ptr Pointer to data to pass to each callback
*
* @return -1 on error, 0 if hash value not found, and 1 if value was found.
*/
///@todo refactor so as not to duplicate code with sqlite_v1_lookup_raw()
int8_t
lookup_text(TSK_HDB_INFO * hdb_info, const char* hvalue, TSK_HDB_FLAG_ENUM flags,
TSK_HDB_LOOKUP_FN action, void *ptr)
{
int8_t ret = 0;
TSK_OFF_T offset;
char selectStmt[1024];
sqlite3_stmt* stmt = NULL;
int len = strlen(hvalue);
tsk_take_lock(&hdb_info->lock);
/* Sanity check */
if (hdb_info->hash_len != len) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_HDB_ARG);
tsk_error_set_errstr("hdb_lookup: Hash passed is different size than expected: %d vs %d",
hdb_info->hash_len, len);
ret = -1;
} else {
if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) {
snprintf(selectStmt, 1024,
"SELECT md5,database_offset from hashes where md5='%s' limit 1",
hvalue);
} else if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) {
snprintf(selectStmt, 1024,
"SELECT sha1,database_offset from hashes where sha1='%s' limit 1",
hvalue);
} else {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_HDB_ARG);
tsk_error_set_errstr("Unknown hash type: %d\n", hdb_info->hash_type);
ret = -1;
}
if (ret != -1) {
prepare_stmt(selectStmt, &stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
// Found a match
if (sqlite3_step(stmt) == SQLITE_ROW) {
if ((flags & TSK_HDB_FLAG_QUICK)
|| (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)) {
// There is just an index, so no other info to get
///@todo Look up a name in the sqlite db
ret = 1;
} else {
// Use offset to get more info
offset = sqlite3_column_int64(stmt, 1);
if (hdb_info->getentry(hdb_info, hvalue, offset, flags, action, ptr)) {
tsk_error_set_errstr2("hdb_lookup");
ret = -1;
} else {
ret = 1;
}
}
}
sqlite3_reset(stmt);
if (stmt) {
finalize_stmt(stmt);
}
}
}
tsk_release_lock(&hdb_info->lock);
return ret;
}
/**
* \ingroup hashdblib
* Sets the updateable flag in the hdb_info argument based on querying the index props table.
*
* @param hdb_info Open hash database (with index)
* @return -1 on error, 0 on success.
*/
int8_t
sqlite_v1_get_properties(TSK_HDB_INFO * hdb_info)
{
int8_t ret = 0;
sqlite3_stmt* stmt = NULL;
char selectStmt[1024];
tsk_take_lock(&hdb_info->lock);
snprintf(selectStmt, 1024, "SELECT value from properties where name='%s'", IDX_HASHSET_UPDATEABLE);
prepare_stmt(selectStmt, &stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char* value = (const char *)sqlite3_column_text(stmt, 0);
if (value == NULL) {
tsk_error_set_errstr2("sqlite_v1_get_properties: null value");
ret = -1;
} else {
// Set the updateable flag
if (strcmp(value, "true") == 0) {
hdb_info->idx_info->updateable = 1;
}
}
} else {
tsk_error_set_errstr2("sqlite_v1_get_properties");
ret = -1;
}
sqlite3_reset(stmt);
if (stmt) {
finalize_stmt(stmt);
}
///@todo load db name property as well?
tsk_release_lock(&hdb_info->lock);
return ret;
}
/*
* Close the sqlite index handle
* @param idx_info the index to close
*/
void
sqlite_v1_close(TSK_IDX_INFO * idx_info)
{
if (m_stmt) {
finalize_stmt(m_stmt);
}
m_stmt = NULL;
if (idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) {
sqlite3_close(idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite);
}
}
/**
* Test the file to see if it is an sqlite database (== index only)
*
* @param hFile File handle to hash database
*
* @return 1 if sqlite and 0 if not
*/
uint8_t
sqlite3_test(FILE * hFile)
{
const int header_size = 16;
char header[header_size];
if (hFile) {
if (1 != fread(header, header_size, 1, hFile)) {
///@todo should this actually be an error?
return 0;
}
else if (strncmp(header,
IDX_SQLITE_V1_HEADER,
strlen(IDX_SQLITE_V1_HEADER)) == 0) {
return 1;
}
}
return 0;
}