1 /*
2  *
3  *  The Sleuth Kit
4  *
5  *  Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
6  *  Copyright (c) 2010-2012 Basis Technology Corporation. All Rights
7  *  reserved.
8  *
9  *  This software is distributed under the Common Public License 1.0
10  */
11 
12 /**
13  * \file TskImgDBSqlite.cpp
14  * A SQLite based implementation of the framework data access layer.
15  */
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <cassert>
20 #include <iostream>
21 #include <sstream>
22 #include <iomanip>
23 #include <map>
24 #include <assert.h>
25 
26 #include "Poco/UnicodeConverter.h"
27 #include "Poco/Thread.h"
28 
29 #include "TskImgDBSqlite.h"
30 #include "TskServices.h"
31 #include "tsk/framework/utilities/TskException.h"
32 #include "TskDBBlackboard.h"
33 
34 #include "Poco/UnicodeConverter.h"
35 #include "Poco/NumberParser.h"
36 #include "Poco/Path.h"
37 
38 #define IMGDB_CHUNK_SIZE 1024*1024*1 // what size chunks should the database use when growing and shrinking
39 #define IMGDB_MAX_RETRY_COUNT 50    // how many times will we retry a SQL statement
40 #define IMGDB_RETRY_WAIT 100   // how long (in milliseconds) are we willing to wait between retries
41 
42 /**
43  * Set the database location.  Must call
44  * initialize() before the object can be used.
45  * @param a_outpath Directory to store the database in. This
46  * directory must already exist.
47 */
TskImgDBSqlite(const char * a_outpath)48 TskImgDBSqlite::TskImgDBSqlite(const char * a_outpath)
49 {
50     strncpy(m_outPath, a_outpath, 256);
51     // ensure that the path ends with a '/'
52     if (m_outPath[strlen(m_outPath)-1] != '/') {
53         int len1 = strlen(m_outPath);
54         m_outPath[len1] = '/';
55         m_outPath[len1+1] = '\0';
56     }
57     strncpy(m_dbFilePath, m_outPath, 256);
58     strncat(m_dbFilePath, "image.db", 256);
59     m_db = NULL;
60 }
61 
~TskImgDBSqlite()62 TskImgDBSqlite::~TskImgDBSqlite()
63 {
64     (void) close();
65 }
66 
close()67 int TskImgDBSqlite::close()
68 {
69     if (m_db) {
70         if (sqlite3_close(m_db) == SQLITE_OK)
71             m_db = NULL;
72         else
73             return 1;
74     }
75     return 0;
76 }
77 
dropTables()78 int TskImgDBSqlite::dropTables()
79 {
80     if (!m_db)
81         return 1;
82 
83     char * errmsg;
84     // Drop all the tables. No error checking just Teutonic Destruction...
85     sqlite3_exec(m_db, "DROP TABLE db_info",NULL, NULL, &errmsg);
86     sqlite3_exec(m_db, "DROP TABLE image_info",NULL, NULL, &errmsg);
87     sqlite3_exec(m_db, "DROP TABLE image_names",NULL, NULL, &errmsg);
88     sqlite3_exec(m_db, "DROP TABLE vol_info",NULL, NULL, &errmsg);
89     sqlite3_exec(m_db, "DROP TABLE fs_info",NULL, NULL, &errmsg);
90     sqlite3_exec(m_db, "DROP TABLE fs_files",NULL, NULL, &errmsg);
91     sqlite3_exec(m_db, "DROP TABLE fs_blocks",NULL, NULL, &errmsg);
92     sqlite3_exec(m_db, "DROP TABLE files",NULL, NULL, &errmsg);
93     sqlite3_exec(m_db, "DROP TABLE derived_files",NULL, NULL, &errmsg);
94     sqlite3_exec(m_db, "DROP TABLE carved_files",NULL, NULL, &errmsg);
95     sqlite3_exec(m_db, "DROP TABLE carved_sectors",NULL, NULL, &errmsg);
96     sqlite3_exec(m_db, "DROP TABLE alloc_unalloc_map", NULL, NULL, &errmsg);
97     sqlite3_exec(m_db, "DROP TABLE blackboard_artifacts", NULL, NULL, &errmsg);
98     sqlite3_exec(m_db, "DROP TABLE blackboard_attributes", NULL, NULL, &errmsg);
99     sqlite3_exec(m_db, "DROP TABLE blackboard_artifact_types", NULL, NULL, &errmsg);
100     sqlite3_exec(m_db, "DROP TABLE blackboard_attribute_types", NULL, NULL, &errmsg);
101     sqlite3_exec(m_db, "DROP TABLE file_hashes", NULL, NULL, &errmsg);
102     sqlite3_exec(m_db, "DROP TABLE modules", NULL, NULL, &errmsg);
103     sqlite3_exec(m_db, "DROP TABLE module_status", NULL, NULL, &errmsg);
104     sqlite3_exec(m_db, "DROP TABLE unalloc_img_status", NULL, NULL, &errmsg);
105     sqlite3_exec(m_db, "DROP TABLE unused_sectors", NULL, NULL, &errmsg);
106     sqlite3_exec(m_db, "DROP INDEX attrs_artifact_id", NULL, NULL, &errmsg);
107     sqlite3_exec(m_db, "DROP INDEX attrs_attribute_type", NULL, NULL, &errmsg);
108     sqlite3_exec(m_db, "DROP INDEX attrs_obj_id", NULL, NULL, &errmsg);
109 
110     return 0;
111 }
112 
initialize()113 int TskImgDBSqlite::initialize()
114 {
115     std::wstringstream infoMessage;
116     char * errmsg;
117 
118     // Open the database.
119     if (open() != 0)
120     {
121         // Error message will have been logged by open()
122         return 1;
123     }
124 
125     // Clean up the whole database.
126     dropTables();
127 
128     std::string stmt;
129 
130     sqlite3_stmt *statement;
131 
132     // set page size -- 4k is much faster on Windows than the default
133     executeStatement("PRAGMA page_size = 4096;", statement, "TskImgDBSqlite::initialize");
134     sqlite3_finalize(statement);
135 
136     // we don't have a mechanism to recover from a crash anyway
137     executeStatement("PRAGMA synchronous = 0;", statement, "TskImgDBSqlite::initialize");
138     sqlite3_finalize(statement);
139 
140     // ----- DB_INFO
141     stmt = "CREATE TABLE db_info (name TEXT PRIMARY KEY, version TEXT)";
142     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
143         infoMessage << L"TskImgDBSqlite::initialize - Error creating db_info table: " << errmsg;
144         LOGERROR(infoMessage.str());
145 
146         sqlite3_free(errmsg);
147         return 1;
148     }
149 
150     // ----- IMAGE_INFO
151     stmt = "CREATE TABLE image_info (type INTEGER, ssize INTEGER)";
152     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
153         infoMessage << L"TskImgDBSqlite::initialize - Error creating image_info table: " << errmsg;
154         LOGERROR(infoMessage.str());
155 
156         sqlite3_free(errmsg);
157         return 1;
158     }
159 
160     // ----- IMAGE_NAMES
161     stmt = "CREATE TABLE image_names (seq INTEGER PRIMARY KEY, name TEXT)";
162     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
163         infoMessage << L"TskImgDBSqlite::initialize - Error creating image_names table: " << errmsg;
164         LOGERROR(infoMessage.str());
165 
166         sqlite3_free(errmsg);
167         return 1;
168     }
169 
170     // ----- VOL_INFO
171     stmt = "CREATE TABLE vol_info (vol_id INTEGER PRIMARY KEY, sect_start INTEGER NOT NULL, "
172         "sect_len INTEGER NOT NULL, description TEXT, flags INTEGER)";
173     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
174         infoMessage << L"TskImgDBSqlite::initialize - Error creating vol_info table: " << errmsg;
175         LOGERROR(infoMessage.str());
176 
177         sqlite3_free(errmsg);
178         return 1;
179     }
180 
181     // ----- FS_INFO
182     stmt = "CREATE TABLE fs_info (fs_id INTEGER PRIMARY KEY, img_byte_offset INTEGER, "
183         "vol_id INTEGER NOT NULL, fs_type INTEGER, block_size INTEGER, "
184         "block_count INTEGER, root_inum INTEGER, first_inum INTEGER, last_inum INTEGER)";
185     if (sqlite3_exec(m_db, stmt.c_str() , NULL, NULL, &errmsg) != SQLITE_OK) {
186         infoMessage << L"TskImgDBSqlite::initialize - Error creating fs_info table: " << errmsg;
187         LOGERROR(infoMessage.str());
188 
189         sqlite3_free(errmsg);
190         return 1;
191     }
192 
193     // ----- FILES
194     stmt = "CREATE TABLE files (file_id INTEGER PRIMARY KEY, type_id INTEGER, "
195         "name TEXT, par_file_id INTEGER, dir_type INTEGER, meta_type INTEGER, "
196         "dir_flags INTEGER, meta_flags INTEGER, size INTEGER, ctime INTEGER, "
197         "crtime INTEGER, atime INTEGER, mtime INTEGER, mode INTEGER, uid INTEGER, "
198         "gid INTEGER, status INTEGER, full_path TEXT)";
199     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
200         infoMessage << L"TskImgDBSqlite::initialize - Error creating files table: " << errmsg;
201         LOGERROR(infoMessage.str());
202 
203         sqlite3_free(errmsg);
204         return 1;
205     }
206 
207     // ----- FS_FILES
208     stmt = "CREATE TABLE fs_files (file_id INTEGER PRIMARY KEY, fs_id INTEGER, "
209         "fs_file_id INTEGER, attr_type INTEGER, attr_id INTEGER)";
210     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
211     {
212         infoMessage << L"TskImgDBSqlite::initialize - Error creating fs_files table: " << errmsg;
213         LOGERROR(infoMessage.str());
214 
215         sqlite3_free(errmsg);
216         return 1;
217     }
218 
219     // ----- FS_BLOCKS
220     stmt = "CREATE TABLE fs_blocks (fs_id INTEGER NOT NULL, file_id INTEGER NOT NULL, seq INTEGER, "
221         "blk_start INTEGER NOT NULL, blk_len INTEGER NOT NULL)";
222     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
223     {
224         infoMessage << L"TskImgDBSqlite::initialize - Error creating fs_blocks table: " << errmsg;
225         LOGERROR(infoMessage.str());
226 
227         sqlite3_free(errmsg);
228         return 1;
229     }
230 
231     // ----- CARVED_FILES
232     stmt = "CREATE TABLE carved_files (file_id INTEGER PRIMARY KEY, vol_id INTEGER)";
233     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
234     {
235         infoMessage << L"TskImgDBSqlite::initialize - Error creating carved_files table: " << errmsg;
236         LOGERROR(infoMessage.str());
237 
238         sqlite3_free(errmsg);
239         return 1;
240     }
241 
242     // ----- SECTOR_LIST
243     stmt = "CREATE TABLE carved_sectors ("
244         "file_id INTEGER, seq INTEGER, sect_start INTEGER, sect_len INTEGER)";
245     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
246     {
247         infoMessage << L"TskImgDBSqlite::initialize - Error creating carved_sectors table: " << errmsg;
248         LOGERROR(infoMessage.str());
249 
250         sqlite3_free(errmsg);
251         return 1;
252     }
253 
254     // ----- DERIVED_FILES
255     stmt = "CREATE TABLE derived_files (file_id INTEGER PRIMARY KEY, derivation_details TEXT)";
256     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
257     {
258         infoMessage << L"TskImgDBSqlite::initialize - Error creating derived_files table: " << errmsg;
259         LOGERROR(infoMessage.str());
260 
261         sqlite3_free(errmsg);
262         return 1;
263     }
264 
265     // ----- ALLOC_UNALLOC_MAP
266     stmt = "CREATE TABLE alloc_unalloc_map (vol_id INTEGER, unalloc_img_id INTEGER, "
267         "unalloc_img_sect_start INTEGER, sect_len INTEGER, orig_img_sect_start INTEGER)";
268     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
269     {
270         infoMessage << L"TskImgDBSqlite::initialize - Error creating alloc_unalloc_map table: " << errmsg;
271         LOGERROR(infoMessage.str());
272 
273         sqlite3_free(errmsg);
274         return 1;
275     }
276 
277     // ----- FILE_HASHES
278     stmt = "CREATE TABLE file_hashes (file_id INTEGER PRIMARY KEY, md5 TEXT, sha1 TEXT, sha2_256 TEXT, sha2_512 TEXT, known INTEGER)";
279     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
280         infoMessage << L"TskImgDBSqlite::initialize - Error creating file_hashes table: " << errmsg;
281         LOGERROR(infoMessage.str());
282 
283         sqlite3_free(errmsg);
284         return 1;
285     }
286 
287     // ----- MODULES
288     stmt = "CREATE TABLE modules (module_id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, description TEXT)";
289     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
290         infoMessage << L"TskImgDBSqlite::initialize - Error creating module table: " << errmsg;
291         LOGERROR(infoMessage.str());
292 
293         sqlite3_free(errmsg);
294         return 1;
295     }
296 
297     // ----- MODULE_STATUS
298     stmt = "CREATE TABLE module_status (file_id INTEGER, module_id INTEGER, status INTEGER, PRIMARY KEY (file_id, module_id))";
299     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
300         infoMessage << L"TskImgDBSqlite::initialize - Error creating module_status table: " << errmsg;
301         LOGERROR(infoMessage.str());
302 
303         sqlite3_free(errmsg);
304         return 1;
305     }
306 
307     // ----- UNALLOC_IMG_STATUS
308     stmt = "CREATE TABLE unalloc_img_status (unalloc_img_id INTEGER PRIMARY KEY, status INTEGER)";
309     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
310         infoMessage << L"TskImgDBSqlite::initialize - Error creating unalloc_img_status table: " << errmsg;
311         LOGERROR(infoMessage.str());
312 
313         sqlite3_free(errmsg);
314         return 1;
315     }
316 
317     // ----- UNUSED_SECTORS
318     stmt = "CREATE TABLE unused_sectors (file_id INTEGER PRIMARY KEY, sect_start INTEGER, sect_len INTEGER, vol_id INTEGER)";
319     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
320         infoMessage << L"TskImgDBSqlite::initialize - Error creating unused_sectors table: " << errmsg;
321         LOGERROR(infoMessage.str());
322 
323         sqlite3_free(errmsg);
324         return 1;
325     }
326 
327     // ----- BLACKBOARD_ARTIFACTS
328     stmt = "CREATE TABLE blackboard_artifacts (artifact_id INTEGER PRIMARY KEY, obj_id INTEGER NOT NULL, artifact_type_id INTEGER)";
329     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
330         infoMessage << L"TskImgDBSqlite::initialize - Error creating blackboard_artifacts table: " << errmsg;
331         LOGERROR(infoMessage.str());
332 
333         sqlite3_free(errmsg);
334         return 1;
335     }
336 
337     // ----- BLACKBOARD_ATTRIBUTES
338     stmt = "CREATE TABLE blackboard_attributes (artifact_id INTEGER NOT NULL, source TEXT, context TEXT, attribute_type_id INTEGER NOT NULL, value_type INTEGER NOT NULL, "
339         "value_byte BLOB, value_text TEXT, value_int32 INTEGER, value_int64 INTEGER, value_double NUMERIC(20, 10), obj_id INTEGER NOT NULL)";
340     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
341         infoMessage << L"TskImgDBSqlite::initialize - Error creating blackboard_attributes table: " << errmsg;
342         LOGERROR(infoMessage.str());
343 
344         sqlite3_free(errmsg);
345         return 1;
346     }
347 
348     // ----- BLACKBOARD_ARTIFACT_TYPES
349     stmt = "CREATE TABLE blackboard_artifact_types (artifact_type_id INTEGER PRIMARY KEY, type_name TEXT, display_name TEXT)";
350     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
351         infoMessage << L"TskImgDBSqlite::initialize - Error creating blackboard_artifact_types table: " << errmsg;
352         LOGERROR(infoMessage.str());
353 
354         sqlite3_free(errmsg);
355         return 1;
356     }
357 
358     // ----- BLACKBOARD_ATTRIBUTE_TYPES
359     stmt = "CREATE TABLE blackboard_attribute_types (attribute_type_id INTEGER PRIMARY KEY, type_name TEXT, display_name TEXT)";
360     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
361         infoMessage << L"TskImgDBSqlite::initialize - Error creating blackboard_attribute_types table: " << errmsg;
362         LOGERROR(infoMessage.str());
363 
364         sqlite3_free(errmsg);
365         return 1;
366     }
367 
368     // ----- CREATE INDEXES
369     stmt = "CREATE INDEX attrs_artifact_id ON blackboard_attributes(artifact_id)";
370     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
371         infoMessage << L"TskImgDBSqlite::initialize - Error creating attrs_artifact_id index: " << errmsg;
372         LOGERROR(infoMessage.str());
373 
374         sqlite3_free(errmsg);
375         return 1;
376     }
377 
378     stmt = "CREATE INDEX attrs_attribute_type ON blackboard_attributes(attribute_type_id)";
379     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
380         infoMessage << L"TskImgDBSqlite::initialize - Error creating attrs_attribute_type index: " << errmsg;
381         LOGERROR(infoMessage.str());
382 
383         sqlite3_free(errmsg);
384         return 1;
385     }
386 
387     stmt = "CREATE INDEX attrs_obj_id ON blackboard_attributes(obj_id)";
388     if (sqlite3_exec(m_db, stmt.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
389         infoMessage << L"TskImgDBSqlite::initialize - Error creating attrs_obj_id index: " << errmsg;
390         LOGERROR(infoMessage.str());
391 
392         sqlite3_free(errmsg);
393         return 1;
394     }
395 
396     map<int, TskArtifactNames> artTypes = TskImgDB::getAllArtifactTypes();
397     for (map<int, TskArtifactNames>::iterator it = artTypes.begin(); it != artTypes.end(); it++) {
398         addArtifactType(it->first, it->second.typeName, it->second.displayName);
399     }
400     map<int, TskAttributeNames> attrTypes = TskImgDB::getAllAttributeTypes();
401     for (map<int, TskAttributeNames>::iterator it = attrTypes.begin(); it != attrTypes.end(); it++) {
402         addAttributeType(it->first, it->second.typeName, it->second.displayName);
403     }
404 
405     addToolInfo("DBSchema", IMGDB_SCHEMA_VERSION);
406     LOGINFO(L"ImgDB Created.");
407 
408     return 0;
409 }
410 
411 /*
412  * If the database file exists this method will open it otherwise
413  * it will create a new database.
414  * This method also configures the chunk size and the busy handler
415  * for the newly opened database.
416 */
open()417 int TskImgDBSqlite::open()
418 {
419     std::wstringstream infoMessage;
420 
421 #if 0
422     if (sqlite3_open16(m_dbFilePath, &m_db))
423 #else
424     if (sqlite3_open(m_dbFilePath, &m_db))
425 #endif
426     {
427         infoMessage << L"TskImgDBSqlite::open - Can't create new database: " << sqlite3_errmsg(m_db);
428         LOGERROR(infoMessage.str());
429 
430         sqlite3_close(m_db);
431         return 1;
432     }
433 
434     // The chunk size setting defines by how much the database will grow
435     // or shrink. The primary motivation behind this setting is to reduce
436     // database file fragmentation and potential performance improvements.
437     // We, however, are using this setting as a workaround for database
438     // corruption issues we have been experiencing when the database is
439     // updated by multiple concurrent processes.
440     // Database corruption was occurring when SQLite determined that the
441     // number of database pages in the database was greater than a value
442     // that it had previously cached.
443     // This workaround is a crude mechanism to get around that situation.
444     int chunkSize = IMGDB_CHUNK_SIZE;
445 
446     if (sqlite3_file_control(m_db, NULL, SQLITE_FCNTL_CHUNK_SIZE, &chunkSize) != SQLITE_OK)
447     {
448         infoMessage << L"TskImgDBSqlite::open - Failed to set chunk size: " << sqlite3_errmsg(m_db);
449         LOGERROR(infoMessage.str());
450 
451         sqlite3_close(m_db);
452         return 1;
453     }
454 
455     // Register a busy handler that will retry statements in situations
456     // where the database is locked by another process.
457     if (sqlite3_busy_handler(m_db, TskImgDBSqlite::busyHandler, m_db) != SQLITE_OK)
458     {
459         infoMessage <<  L"TskImgDBSqlite::open - Failed to set busy handler: " << sqlite3_errmsg(m_db);
460         LOGERROR(infoMessage.str());
461 
462         sqlite3_close(m_db);
463         return 1;
464     }
465 
466     LOGINFO(L"ImgDB Opened.");
467 
468     return 0;
469 }
470 
addToolInfo(const char * name,const char * version)471 int TskImgDBSqlite::addToolInfo(const char* name, const char* version)
472 {
473     char *errmsg;
474     char stmt[1024];
475 
476     if (!m_db)
477         return 1;
478 
479     sqlite3_snprintf(1024, stmt,
480         "INSERT INTO db_info (name, version) VALUES ('%q', '%q');",
481         name, version);
482     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK)
483     {
484         std::wstringstream infoMessage;
485         infoMessage << L"TskImgDBSqlite::addToolInfo - Error adding data to db_info table: " << errmsg;
486         LOGERROR(infoMessage.str());
487 
488         sqlite3_free(errmsg);
489         return 1;
490     }
491 
492     return 0;
493 }
494 
addImageInfo(int type,int size)495 int TskImgDBSqlite::addImageInfo(int type, int size)
496 {
497     char *errmsg;
498     std::stringstream stmt;
499 
500     if (!m_db)
501         return 1;
502 
503     stmt << "INSERT INTO image_info (type, ssize) VALUES ("<< type << ", " << size << ");";
504     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK)
505     {
506         std::wstringstream infoMessage;
507         infoMessage <<  L"TskImgDBSqlite::addImageInfo - Error adding data to image_info table: " << errmsg;
508         LOGERROR(infoMessage.str());
509 
510         sqlite3_free(errmsg);
511         return 1;
512     }
513 
514     return 0;
515 }
516 
addImageName(char const * imgPath)517 int TskImgDBSqlite::addImageName(char const *imgPath)
518 {
519     char *errmsg;
520     char stmt[1024];
521 
522     if (!m_db)
523         return 1;
524 
525     sqlite3_snprintf(1024, stmt,
526         "INSERT INTO image_names (seq, name) VALUES (NULL, '%q')",
527         imgPath);
528     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK)
529     {
530         std::wstringstream infoMessage;
531         infoMessage << L"TskImgDBSqlite::addImageName - Error adding data to image_names table: " << errmsg;
532         LOGERROR(infoMessage.str());
533 
534         sqlite3_free(errmsg);
535         return 1;
536     }
537     return 0;
538 }
539 
540 /*
541  * Adds the sector addresses of the volumes into the db.
542  */
addVolumeInfo(const TSK_VS_PART_INFO * vs_part)543 int TskImgDBSqlite::addVolumeInfo(const TSK_VS_PART_INFO * vs_part)
544 {
545     char stmt[1024];
546     char * errmsg;
547 
548     if (!m_db)
549         return 1;
550 
551     sqlite3_snprintf(1024, stmt,
552         "INSERT INTO vol_info (vol_id, sect_start, sect_len, description, flags) VALUES (%d,%"
553         PRIuOFF ",%" PRIuOFF ",'%q',%d)", (int)vs_part->addr,
554         vs_part->start, vs_part->len, vs_part->desc, vs_part->flags);
555 
556     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK) {
557         std::wstringstream infoMessage;
558         infoMessage << L"TskImgDBSqlite::addVolumeInfo - Error adding data to vol_info table: " << errmsg;
559         LOGERROR(infoMessage.str());
560 
561         sqlite3_free(errmsg);
562         return 1;
563     }
564 
565     return 0;
566 }
567 
addFsInfo(int volId,int fsId,const TSK_FS_INFO * fs_info)568 int TskImgDBSqlite::addFsInfo(int volId, int fsId, const TSK_FS_INFO * fs_info)
569 {
570     std::stringstream stmt;
571     char * errmsg;
572 
573     if (!m_db)
574         return 1;
575 
576     stmt <<
577         "INSERT INTO fs_info (fs_id, img_byte_offset, vol_id, fs_type, block_size, "
578         "block_count, root_inum, first_inum, last_inum) VALUES (" <<
579         fsId << ", " << fs_info->offset << ", " <<  volId << ", " <<
580         (int)fs_info->ftype << ", " <<  fs_info->block_size << ", " <<  fs_info->block_count << ", " <<
581         fs_info->root_inum << ", " <<  fs_info->first_inum << ", " <<  fs_info->last_inum << ")";
582 
583     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
584         std::wstringstream infoMessage;
585         infoMessage << L"TskImgDBSqlite::addFsInfo - Error adding data to fs_info table: " << errmsg;
586         LOGERROR(infoMessage.str());
587 
588         sqlite3_free(errmsg);
589         return 1;
590     }
591 
592     return 0;
593 }
594 
595 
596 /**
597  * Given a file system and fs_file_id, return the file_id.
598  */
getFileId(int a_fsId,uint64_t a_fsFileId) const599 uint64_t TskImgDBSqlite::getFileId(int a_fsId, uint64_t a_fsFileId) const
600 {
601     if (!m_db)
602         return 0;
603 
604     sqlite3_stmt * statement;
605     std::stringstream stmt;
606     uint64_t fileId = 0;
607     stmt << "SELECT file_id FROM fs_files WHERE fs_id=" << a_fsId << " and fs_file_id=" << a_fsFileId << ";";
608 
609     /********** FIND the unallocated volumes *************/
610     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
611         int result = sqlite3_step(statement);
612         if (result == SQLITE_ROW) {
613             fileId = (uint64_t)sqlite3_column_int64(statement, 0);
614         }
615         sqlite3_finalize(statement);
616     }
617     else {
618         std::wstringstream infoMessage;
619         infoMessage << L"TskImgDBSqlite::getFileId - Error querying fs_files table: " << sqlite3_errmsg(m_db);
620         LOGERROR(infoMessage.str());
621 
622         return 0;
623     }
624     return fileId;
625 }
626 
627 
getFileRecord(const uint64_t fileId,TskFileRecord & fileRecord) const628 int TskImgDBSqlite::getFileRecord(const uint64_t fileId, TskFileRecord& fileRecord) const
629 {
630     if (!m_db)
631         return -1;
632 
633     int ret = 0;
634 
635     sqlite3_stmt * statement;
636     std::stringstream stmt;
637 
638     stmt << "SELECT f.file_id, f.type_id, f.name, f.par_file_id, f.dir_type, f.meta_type, f.dir_flags, "
639         << "f.meta_flags, f.size, f.ctime, f.crtime, f.atime, f.mtime, f.mode, f.uid, f.gid, f.status, f.full_path, "
640         << "fh.md5, fh.sha1, fh.sha2_256, fh.sha2_512 "
641         << "FROM files f LEFT OUTER JOIN file_hashes fh ON f.file_id = fh.file_id WHERE f.file_id=" << fileId;
642 
643     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK)
644     {
645         int result = sqlite3_step(statement);
646 
647         if (result == SQLITE_ROW)
648         {
649             fileRecord.fileId       = sqlite3_column_int64(statement, 0);
650             fileRecord.typeId = (TskImgDB::FILE_TYPES)sqlite3_column_int(statement, 1);
651             fileRecord.name         = (char *)sqlite3_column_text(statement, 2);
652             fileRecord.parentFileId = sqlite3_column_int64(statement, 3);
653             fileRecord.dirType = (TSK_FS_NAME_TYPE_ENUM) sqlite3_column_int(statement, 4);
654             fileRecord.metaType = (TSK_FS_META_TYPE_ENUM) sqlite3_column_int(statement, 5);
655             fileRecord.dirFlags = (TSK_FS_NAME_FLAG_ENUM) sqlite3_column_int(statement, 6);
656             fileRecord.metaFlags = (TSK_FS_META_FLAG_ENUM) sqlite3_column_int(statement, 7);
657             fileRecord.size         = sqlite3_column_int64(statement, 8);
658             fileRecord.ctime        = sqlite3_column_int(statement, 9);
659             fileRecord.crtime       = sqlite3_column_int(statement, 10);
660             fileRecord.atime        = sqlite3_column_int(statement, 11);
661             fileRecord.mtime        = sqlite3_column_int(statement, 12);
662             fileRecord.mode = (TSK_FS_META_MODE_ENUM)sqlite3_column_int(statement, 13);
663             fileRecord.uid          = sqlite3_column_int(statement, 14);
664             fileRecord.gid          = sqlite3_column_int(statement, 15);
665             fileRecord.status = (TskImgDB::FILE_STATUS) sqlite3_column_int(statement, 16);
666             fileRecord.fullPath     = (char *)sqlite3_column_text(statement, 17);
667 
668             if (sqlite3_column_type(statement, 18) == SQLITE_TEXT)
669                 fileRecord.md5      = (char *)sqlite3_column_text(statement, 18);
670             if (sqlite3_column_type(statement, 19) == SQLITE_TEXT)
671                 fileRecord.sha1     = (char *)sqlite3_column_text(statement, 19);
672             if (sqlite3_column_type(statement, 20) == SQLITE_TEXT)
673                 fileRecord.sha2_256 = (char *)sqlite3_column_text(statement, 20);
674             if (sqlite3_column_type(statement, 21) == SQLITE_TEXT)
675                 fileRecord.sha2_512 = (char *)sqlite3_column_text(statement, 21);
676         }
677         else
678         {
679             std::wstringstream msg;
680             msg << L"TskImgDBSqlite::getFileRecord - Error querying files table for file id: " << fileId;
681             LOGERROR(msg.str());
682 
683             ret = -1;
684         }
685         sqlite3_finalize(statement);
686     }
687     else
688     {
689         std::wstringstream msg;
690         msg << L"TskImgDBSqlite::getFileRecord - Error querying files table for file id: " << fileId;
691         LOGERROR(msg.str());
692 
693         ret = -1;
694     }
695 
696     return ret;
697 }
698 
addFsFileInfo(int fileSystemID,const TSK_FS_FILE * fileSystemFile,const char * fileName,int fileSystemAttrType,int fileSystemAttrID,uint64_t & fileID,const char * filePath)699 int TskImgDBSqlite::addFsFileInfo(int fileSystemID, const TSK_FS_FILE *fileSystemFile, const char *fileName, int fileSystemAttrType, int fileSystemAttrID, uint64_t &fileID, const char *filePath)
700 {
701     const std::string msgPrefix = "TskImgDBSqlite::addFsFileInfo : ";
702     fileID = 0;
703 
704     if (!m_db)
705     {
706         return -1;
707     }
708 
709     // Construct the full path of the file within the image.
710     std::string fullpath(filePath);
711     fullpath.append(fileName);
712 
713     // Replace all single quotes in the file name with double single quotes to comply with SQLLite syntax.
714     std::string fileNameAsString(fileName);
715     size_t found = fileNameAsString.find("'");
716     if (found != std::string::npos) //Replace it and replace all its subsequent occurrences.
717     {
718         fileNameAsString.replace(found,1,"''");
719 
720         while ((found=fileNameAsString.find("'", found+2)) != std::string::npos)// found+2 because we want to move past the newly inserted single quote.
721         {
722             fileNameAsString.replace(found,1,"''");
723         }
724     }
725 
726     // Now remove all the control characters from the file name.
727     for (int codePoint=1; codePoint < 32; codePoint++)
728     {
729         char codePointAsHex[10];
730         codePointAsHex[0] = codePoint;
731         codePointAsHex[1] = '\0';
732         std::string stringToRemove(codePointAsHex);
733 
734         found = fileNameAsString.find(stringToRemove);
735         if (found != std::string::npos) //Replace it and replace all its subsequent occurrences.
736         {
737             fileNameAsString.replace(found,1,"");
738 
739             while ((found=fileNameAsString.find(stringToRemove,found+1)) != std::string::npos)// found+1 because the control characters are just 1 character.
740             {
741                 fileNameAsString.replace(found,1,"");
742             }
743         }
744     }
745 
746     fileName = fileNameAsString.c_str();
747 
748     // Get the file size.
749     TSK_OFF_T size = 0;
750     const TSK_FS_ATTR *fileSystemAttribute = tsk_fs_file_attr_get_id(const_cast<TSK_FS_FILE*>(fileSystemFile), fileSystemAttrID);
751     if (fileSystemAttribute)
752     {
753         size = fileSystemAttribute->size;
754     }
755 
756     // Get the file metadata, if it's available.
757     int mtime = 0;
758     int crtime = 0;
759     int ctime = 0;
760     int atime = 0;
761     int meta_type = 0;
762     int meta_flags = 0;
763     int meta_mode = 0;
764     int gid = 0;
765     int uid = 0;
766     if (fileSystemFile->meta)
767     {
768         mtime = static_cast<int>(fileSystemFile->meta->mtime);
769         atime = static_cast<int>(fileSystemFile->meta->atime);
770         ctime = static_cast<int>(fileSystemFile->meta->ctime);
771         crtime = static_cast<int>(fileSystemFile->meta->crtime);
772         meta_type = fileSystemFile->meta->type;
773         meta_flags = fileSystemFile->meta->flags;
774         meta_mode = fileSystemFile->meta->mode;
775         gid = fileSystemFile->meta->gid;
776         uid = fileSystemFile->meta->uid;
777     }
778 
779     // Insert into the files table.
780     char stmt[4096];
781     sqlite3_snprintf(4096, stmt,
782         "INSERT INTO files (file_id, type_id, status, name, par_file_id, dir_type, meta_type, "
783         "dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, full_path) VALUES (NULL, %d, %d,"
784         "'%q',%llu,%d,%d,%d,%d,%" PRIuOFF",%d,%d,%d,%d,%d,%d,%d,'%q')",
785         IMGDB_FILES_TYPE_FS, IMGDB_FILES_STATUS_READY_FOR_ANALYSIS, fileName,
786         findParObjId(fileSystemFile, fileSystemID),
787         fileSystemFile->name->type, meta_type,
788         fileSystemFile->name->flags, meta_flags, size, crtime, ctime, atime,
789         mtime, meta_mode, gid, uid, fullpath.c_str());
790     char *errmsg;
791     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK)
792     {
793         std::ostringstream msg;
794         msg << msgPrefix << "Error adding data to files table: " << errmsg;
795         LOGERROR(msg.str());
796 
797         sqlite3_free(errmsg);
798         return -1;
799     }
800 
801     // Get the file_id from the last insert.
802     fileID = sqlite3_last_insert_rowid(m_db);
803 
804     // Insert into the fs_files table.
805     sqlite3_snprintf(4096, stmt,
806         "INSERT INTO fs_files (file_id, fs_id, fs_file_id, attr_type, attr_id) VALUES (%llu,%d,%"
807         PRIuINUM ",%d,%d)", fileID, fileSystemID, fileSystemFile->name->meta_addr, fileSystemAttrType, fileSystemAttrID);
808 
809     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK)
810     {
811         std::ostringstream msg;
812         msg << msgPrefix << "Error adding data to fs_files table: " << errmsg;
813         LOGERROR(msg.str());
814 
815         sqlite3_free(errmsg);
816         return -1;
817     }
818 
819     //if dir, update parent id cache
820     if (meta_type == TSK_FS_META_TYPE_DIR) {
821         storeParObjId(fileSystemID, fileSystemFile, fileID);
822     }
823 
824     return 0;
825 }
826 
827 /**
828  * Add block info to the database.  This table stores the run information for each file so that we
829  * can map which blocks are used by what files.
830  * @param a_fsId Id that the file is located in
831  * @param a_fileId ID of the file
832  * @param a_sequence The sequence number of this run in the file (0 for the first run, 1 for the second run, etc.)
833  * @param a_blk_addr Block address (the address that the file system uses -- NOT the physical sector addr)
834  * @param a_len The number of blocks in the run
835  * @returns 1 on error
836  */
addFsBlockInfo(int a_fsId,uint64_t a_fileId,int a_sequence,uint64_t a_blk_addr,uint64_t a_len)837 int TskImgDBSqlite::addFsBlockInfo(int a_fsId, uint64_t a_fileId, int a_sequence, uint64_t a_blk_addr, uint64_t a_len)
838 {
839     std::stringstream stmt;
840     char * errmsg;
841 
842     if (!m_db)
843         return 1;
844 
845     stmt <<
846         "INSERT INTO fs_blocks (fs_id, file_id, seq, blk_start, blk_len) VALUES (" <<
847         a_fsId << "," << a_fileId << "," << a_sequence << "," << a_blk_addr << "," <<  a_len << ")";
848 
849     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
850         std::wstringstream infoMessage;
851         infoMessage << L"TskImgDBSqlite::addFsBlockInfo - Error adding data to fs_blocks table: " << errmsg;
852         LOGERROR(infoMessage.str());
853 
854         sqlite3_free(errmsg);
855         return 1;
856     }
857 
858     return 0;
859 }
860 
addAllocUnallocMapInfo(int a_volID,int unallocImgID,uint64_t unallocImgStart,uint64_t length,uint64_t origImgStart)861 int TskImgDBSqlite::addAllocUnallocMapInfo(int a_volID, int unallocImgID,
862                                            uint64_t unallocImgStart, uint64_t length, uint64_t origImgStart)
863 {
864     std::stringstream stmt;
865     char * errmsg;
866 
867     if (!m_db)
868         return 1;
869 
870     stmt <<
871         "INSERT INTO alloc_unalloc_map (vol_id, unalloc_img_id, unalloc_img_sect_start, "
872         "sect_len, orig_img_sect_start) VALUES (" <<
873         a_volID << "," << unallocImgID << "," <<
874         unallocImgStart << "," << length << "," <<  origImgStart << ")";
875 
876     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
877         std::wstringstream infoMessage;
878         infoMessage << L"TskImgDBSqlite::addAllocUnallocMapInfo - Error adding data to alloc_unalloc_map table: " << errmsg;
879         LOGERROR(infoMessage.str());
880 
881         sqlite3_free(errmsg);
882         return 1;
883     }
884 
885     return 0;
886 }
887 
888 /**
889  * Get information on all of the free sectors in an image.
890  *
891  * @return Info on unallocated runs (or NULL on error).  Caller must free this when done.
892  */
getFreeSectors() const893 SectorRuns * TskImgDBSqlite::getFreeSectors() const
894 {
895     std::stringstream infoMessage;
896     std::stringstream msg;
897 
898     if (!m_db)
899         return NULL;
900 
901     SectorRuns * sr = new SectorRuns();
902 
903     LOGINFO("TskImgDBSqlite::getFreeSectors - Identifying Unallocated Sectors");
904 
905     sqlite3_stmt * statement;
906 
907     /********** FIND the unallocated volumes *************/
908     if (sqlite3_prepare_v2(m_db,
909         "SELECT vol_id, sect_start, sect_len, flags FROM vol_info;",
910         -1, &statement, 0) == SQLITE_OK) {
911             while(true) {
912                 int result = sqlite3_step(statement);
913                 if (result == SQLITE_ROW) {
914                     int flags = sqlite3_column_int(statement, 3);
915 
916                     int vol_id = sqlite3_column_int(statement,0);
917                     int64_t start = sqlite3_column_int64(statement,1);
918                     int64_t len = sqlite3_column_int64(statement,2);
919 
920                     // add the unallocated volumes
921                     if (flags & TSK_VS_PART_FLAG_UNALLOC) {
922                         sr->addRun(start, len, vol_id);
923                     }
924                     // add the allocated volumes that don't have a known file system
925                     else {
926                         std::stringstream stmt;
927                         sqlite3_stmt *statement2;
928                         stmt << "SELECT fs_id FROM fs_info WHERE vol_id = " << vol_id << ";";
929                         if (sqlite3_prepare_v2(m_db, stmt.str().c_str() , -1, &statement2, 0) == SQLITE_OK) {
930                             if (sqlite3_step(statement2) != SQLITE_ROW) {
931                                 sr->addRun(start, len, vol_id);
932                             }
933                             sqlite3_finalize(statement2);
934                         }
935                     }
936                 }
937                 else {
938                     break;
939                 }
940             }
941             sqlite3_finalize(statement);
942     }
943     else {
944         infoMessage << "TskImgDBSqlite::getFreeSectors - Error querying vol_info table: " << sqlite3_errmsg(m_db);
945         LOGERROR(infoMessage.str());
946 
947         return NULL;
948     }
949 
950     /*************** Find the unallocated blocks in each file system *************/
951     // @@@ Need to make more dynamic
952     int blk_size[32];
953     memset(blk_size, 0, sizeof(blk_size));
954     uint64_t blk_count[32];
955     memset(blk_count, 0, sizeof(blk_count));
956     int vol_id[32];
957     uint64_t img_offset[32];
958 
959     // get basic info on each file system
960     if (sqlite3_prepare_v2(m_db, "SELECT fs_id, vol_id, img_byte_offset, block_size, block_count FROM fs_info;", -1, &statement, 0) == SQLITE_OK) {
961         LOGINFO("TskImgDBSqlite::getFreeSectors - START LOOP: Find the unallocated blocks in each file system.");
962         while(true)
963         {
964             int result = sqlite3_step(statement);
965             if (result == SQLITE_ROW)
966             {
967                 int fs_id = sqlite3_column_int(statement, 0);
968                 if (fs_id > 32)
969                 {
970                     infoMessage.str("");
971                     infoMessage << "TskImgDBSqlite::getFreeSectors - fs_id in fs_info is bigger than 32: " << fs_id;
972                     LOGERROR(infoMessage.str());
973                     break;
974                 }
975                 vol_id[fs_id] = sqlite3_column_int(statement, 1);
976                 img_offset[fs_id] = sqlite3_column_int64(statement, 2) / 512;
977                 blk_size[fs_id] = sqlite3_column_int(statement, 3) / 512;
978                 blk_count[fs_id] = sqlite3_column_int64(statement, 4);
979                 // Debug Info
980                 msg.str("");
981                 msg << "TskImgDBSqlite::getFreeSectors - fs_id=" << fs_id << " vol_id=" << vol_id[fs_id] << " img_offset=" << img_offset[fs_id] << " blk_size=" << blk_size[fs_id] <<
982                     " blk_count=" << blk_count[fs_id];
983                 LOGINFO(msg.str().c_str());
984             }
985             else
986             {
987                 break;
988             }
989         }
990         sqlite3_finalize(statement);
991         LOGINFO("TskImgDBSqlite::getFreeSectors - DONE: Find the unallocated blocks in each file system.");
992     }
993     else
994     {
995         infoMessage.str("");
996         infoMessage << "TskImgDBSqlite::getFreeSectors - Error querying fs_info table: " << sqlite3_errmsg(m_db);
997         LOGERROR(infoMessage.str());
998 
999         return NULL;
1000     }
1001 
1002     // see what blocks have been used and add them to a list
1003     TSK_LIST *seen[32];
1004     memset(seen, 0, 32*sizeof(TSK_LIST *));
1005 
1006     if (sqlite3_prepare_v2(m_db, "SELECT fs_id, file_id, blk_start, blk_len FROM fs_blocks;", -1, &statement, 0) == SQLITE_OK) {
1007         LOGINFO("TskImgDBSqlite::getFreeSectors - START LOOP: see what blocks have been used and add them to a list.");
1008         while(true) {
1009             int result = sqlite3_step(statement);
1010             if (result == SQLITE_ROW) {
1011                 int fs_id = sqlite3_column_int(statement, 0);
1012                 if (fs_id > 32) {
1013                     infoMessage.str("");
1014                     infoMessage << "TskImgDBSqlite::getFreeSectors - fs_id in fs_info is bigger than 32: " << fs_id;
1015                     LOGERROR(infoMessage.str());
1016                     continue;
1017                 }
1018                 uint64_t file_id = (uint64_t)sqlite3_column_int64(statement, 1);
1019                 int64_t addr = sqlite3_column_int64(statement, 2);
1020                 int64_t len = sqlite3_column_int64(statement, 3);
1021 
1022                 // We only want to consider the runs for files that we allocated.
1023                 std::stringstream stmt;
1024                 stmt << "SELECT meta_flags from files WHERE file_id=" << file_id;
1025 
1026                 sqlite3_stmt * statement2;
1027                 if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement2, 0) != SQLITE_OK) {
1028                     infoMessage.str("");
1029                     infoMessage << "TskImgDBSqlite::getFreeSectors - error finding flags for file " << file_id;
1030                     LOGERROR(infoMessage.str());
1031                     continue;
1032                 }
1033                 sqlite3_step(statement2);
1034                 int flags = sqlite3_column_int(statement2, 0);
1035                 sqlite3_finalize(statement2);
1036 
1037                 if (flags & TSK_FS_META_FLAG_UNALLOC)
1038                     continue;
1039 
1040                 // @@@ We can probably find a more efficient storage method than this...
1041                 int error = 0;
1042                 for (int64_t i = 0; i < len; i++) {
1043                     if (tsk_list_add(&seen[fs_id], addr+i)) {
1044                         infoMessage.str("");
1045                         infoMessage << "TskImgDBSqlite::getFreeSectors - Error adding seen block address to list";
1046                         LOGERROR(infoMessage.str());
1047 
1048                         error = 1;
1049                         break;
1050                     }
1051                 }
1052                 if (error)
1053                     break;
1054             }
1055             else {
1056                 break;
1057             }
1058         }
1059         sqlite3_finalize(statement);
1060         LOGINFO("TskImgDBSqlite::getFreeSectors - DONE: see what blocks have been used and add them to a list.");
1061     }
1062     else {
1063         infoMessage.str("");
1064         infoMessage << "TskImgDBSqlite::getFreeSectors - Error querying fs_block table: " << sqlite3_errmsg(m_db);
1065         LOGERROR(infoMessage.str());
1066 
1067         return NULL;
1068     }
1069 
1070     // cycle through each file system to find the unused blocks
1071     LOGINFO("TskImgDBSqlite::getFreeSectors - START LOOP: cycle through each file system to find the unused blocks.");
1072     for (int f = 0; f < 32; f++) {
1073         if (blk_count[f] == 0)
1074             continue;
1075 
1076         uint64_t st = 0;
1077         int len = 0;
1078         // we previously adjusted blk_size and img_offset to be in sectors
1079 
1080         msg.str("");
1081         msg << "blk_count[" << f << "]=" << blk_count[f];
1082         LOGINFO(msg.str().c_str());
1083 
1084         for (uint64_t a = 0; a < blk_count[f]; a++) {
1085             // see if this addr was used in a file
1086             if (tsk_list_find(seen[f], a) == 0) {
1087                 // we already have a run being defined
1088                 if (len) {
1089                     // same run, so add on to it
1090                     if (st + len == a) {
1091                         len++;
1092                     }
1093                     // different run, make a new one
1094                     else {
1095                         sr->addRun(img_offset[f]+st*blk_size[f], len*blk_size[f], vol_id[f]);
1096                         st = a;
1097                         len = 1;
1098                     }
1099                 }
1100                 // start a new run
1101                 else {
1102                     st = a;
1103                     len = 1;
1104                 }
1105             }
1106         }
1107         // add the final run
1108         if (len) {
1109             sr->addRun(img_offset[f]+st*blk_size[f], len*blk_size[f], vol_id[f]);
1110         }
1111         tsk_list_free(seen[f]);
1112         seen[f] = NULL;
1113     }
1114     LOGINFO("TskImgDBSqlite::getFreeSectors - DONE: cycle through each file system to find the unused blocks.");
1115 
1116     return sr;
1117 }
1118 
getImageBaseName() const1119 std::string TskImgDBSqlite::getImageBaseName() const
1120 {
1121     // There may be multiple file paths if the image is a split image. Oreder by sequence number to extract the file name from the first path.
1122     sqlite3_stmt *statement;
1123     executeStatement("SELECT name FROM image_names ORDER BY seq;", statement, "TskImgDBSqlite::getImageBaseName");
1124 
1125     int result = sqlite3_step(statement);
1126     if (result == SQLITE_ROW)
1127     {
1128         Poco::Path imagePath(reinterpret_cast<const char*>(sqlite3_column_text(statement, 0))); // Reinterpret from const unsigned char*
1129         return imagePath.getFileName();
1130     }
1131     else
1132     {
1133         return "";
1134     }
1135 }
1136 
getImageNamesW() const1137 std::vector<std::wstring> TskImgDBSqlite::getImageNamesW() const
1138 {
1139     std::vector<std::wstring> imgList;
1140 
1141     if (!m_db)
1142         return imgList;
1143 
1144     sqlite3_stmt *statement;
1145 
1146     if (sqlite3_prepare_v2(m_db, "SELECT name FROM image_names ORDER BY seq;",
1147         -1, &statement, 0) == SQLITE_OK)
1148     {
1149         while(true)
1150         {
1151             int result = sqlite3_step(statement);
1152             if (result == SQLITE_ROW) {
1153                 imgList.push_back((wchar_t *)sqlite3_column_text16(statement, 0));
1154             }
1155             else {
1156                 break;
1157             }
1158         }
1159 
1160         sqlite3_finalize(statement);
1161     }
1162 
1163     return imgList;
1164 }
1165 
1166 
getImageNames() const1167 std::vector<std::string> TskImgDBSqlite::getImageNames() const
1168 {
1169     std::vector<std::string> imgList;
1170 
1171     if (!m_db)
1172         return imgList;
1173 
1174     sqlite3_stmt *statement;
1175 
1176     if (sqlite3_prepare_v2(m_db, "SELECT name FROM image_names ORDER BY seq;",
1177         -1, &statement, 0) == SQLITE_OK)
1178     {
1179         while(true)
1180         {
1181             int result = sqlite3_step(statement);
1182             if (result == SQLITE_ROW) {
1183                 imgList.push_back((char *)sqlite3_column_text(statement, 0));
1184             }
1185             else {
1186                 break;
1187             }
1188         }
1189 
1190         sqlite3_finalize(statement);
1191     }
1192 
1193     return imgList;
1194 }
1195 
1196 
1197 
1198 
1199 /**
1200  * @param a_fileId  File id to get information about
1201  * @param a_fsOffset Byte offset of start of file system that the file is located in
1202  * @param a_fsFileId File system-specific id of the file
1203  * @param a_attrType Type of attribute for this file
1204  * @param a_attrId The ID of the attribute for this file
1205  * @returns -1 on error
1206  */
getFileUniqueIdentifiers(uint64_t a_fileId,uint64_t & a_fsOffset,uint64_t & a_fsFileId,int & a_attrType,int & a_attrId) const1207 int TskImgDBSqlite::getFileUniqueIdentifiers(uint64_t a_fileId, uint64_t &a_fsOffset, uint64_t &a_fsFileId, int &a_attrType, int &a_attrId) const
1208 {
1209     if (!m_db)
1210         return -1;
1211 
1212     sqlite3_stmt * statement;
1213     std::stringstream stmt;
1214 
1215     stmt <<
1216         "SELECT fs_file_id, attr_type, attr_id, fs_info.img_byte_offset "
1217         "FROM fs_files, fs_info WHERE file_id=" << a_fileId << " AND fs_info.fs_id = fs_files.fs_id;";
1218     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
1219         int result = sqlite3_step(statement);
1220         if (result == SQLITE_ROW) {
1221             a_fsFileId = sqlite3_column_int64(statement, 0);
1222             a_attrType = sqlite3_column_int(statement, 1);
1223             a_attrId = sqlite3_column_int(statement, 2);
1224             a_fsOffset = sqlite3_column_int64(statement, 3);
1225         }
1226         else {
1227             return -1;
1228         }
1229         sqlite3_finalize(statement);
1230     }
1231     else {
1232         std::wstringstream infoMessage;
1233         infoMessage << L"TskImgDBSqlite::getFileUniqueIdentifiers - Error querying fs_files table : " << sqlite3_errmsg(m_db);
1234         LOGERROR(infoMessage.str());
1235         return -1;
1236     }
1237 
1238     return 0;
1239 }
1240 
1241 /**
1242  * Get number of volumes in image.
1243  * @return Number of volumes in image or -1 on error
1244  */
getNumVolumes() const1245 int TskImgDBSqlite::getNumVolumes() const
1246 {
1247     if (!m_db)
1248         return 0;
1249 
1250     int count = 0;
1251     sqlite3_stmt * statement;
1252 
1253     /********** Get the number of volumes *************/
1254     if (sqlite3_prepare_v2(m_db, "SELECT count(*) from vol_info;", -1, &statement, 0) == SQLITE_OK)
1255     {
1256         int result = sqlite3_step(statement);
1257         if (result == SQLITE_ROW)
1258         {
1259             count = (int)sqlite3_column_int(statement, 0);
1260         }
1261         sqlite3_finalize(statement);
1262     }
1263     else
1264     {
1265         std::wstringstream msg;
1266         msg << L"TskImgDBSqlite::getNumVolumes - Error querying vol_info table: " << sqlite3_errmsg(m_db);
1267         LOGERROR(msg.str());
1268 
1269         return -1;
1270     }
1271 
1272     return count;
1273 }
1274 /**
1275  * Get number of files in image.
1276  * @return Number of files in image or -1 on error
1277  */
getNumFiles() const1278 int TskImgDBSqlite::getNumFiles() const
1279 {
1280     if (!m_db)
1281         return 0;
1282 
1283     std::string condition("");
1284     return getFileCount(condition);
1285 }
1286 
1287 /**
1288  * @returns the session_id or -1 on error.
1289  */
getSessionID() const1290 int TskImgDBSqlite::getSessionID() const
1291 {
1292     if (!m_db)
1293         return 0;
1294 
1295     sqlite3_stmt * statement;
1296     std::string stmt("SELECT version from db_info WHERE name=\"SID\";");
1297     int sessionId = -1;
1298 
1299     /********** FIND the unallocated volumes *************/
1300     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK) {
1301         int result = sqlite3_step(statement);
1302         if (result == SQLITE_ROW) {
1303             sessionId = (int)sqlite3_column_int(statement, 0);
1304         }
1305         sqlite3_finalize(statement);
1306     }
1307     else {
1308         std::wstringstream infoMessage;
1309         infoMessage << L"TskImgDBSqlite::getSessionID - Error querying db_info table for Session ID: " << sqlite3_errmsg(m_db);
1310         LOGERROR(infoMessage.str());
1311 
1312         return -1;
1313     }
1314     return sessionId;
1315 }
1316 
begin()1317 int TskImgDBSqlite::begin()
1318 {
1319     char *errmsg;
1320     if (!m_db)
1321         return 1;
1322 
1323     if (sqlite3_exec(m_db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) {
1324         std::wstringstream infoMessage;
1325         infoMessage << L"TskImgDBSqlite::begin - BEGIN Error: " << errmsg;
1326         LOGERROR(infoMessage.str());
1327 
1328         sqlite3_free(errmsg);
1329         return 1;
1330     }
1331     return 0;
1332 }
1333 
commit()1334 int TskImgDBSqlite::commit()
1335 {
1336     char *errmsg;
1337     if (!m_db)
1338         return 1;
1339 
1340     if (sqlite3_exec(m_db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) {
1341         std::wstringstream infoMessage;
1342         infoMessage << L"TskImgDBSqlite::commit - COMMIT Error: " << errmsg;
1343         LOGERROR(infoMessage.str());
1344 
1345         sqlite3_free(errmsg);
1346         return 1;
1347     }
1348     return 0;
1349 }
1350 
getUnallocRun(int a_unalloc_img_id,int a_file_offset) const1351 UnallocRun * TskImgDBSqlite::getUnallocRun(int a_unalloc_img_id, int a_file_offset) const
1352 {
1353     std::stringstream stmt;
1354     char * errmsg;
1355     if (!m_db)
1356         return NULL;
1357 
1358     stmt << "SELECT vol_id, unalloc_img_sect_start, sect_len, orig_img_sect_start FROM "
1359         "alloc_unalloc_map WHERE unalloc_img_id = " << a_unalloc_img_id <<
1360         " AND unalloc_img_sect_start <= " << a_file_offset << " ORDER BY unalloc_img_sect_start DESC";
1361 
1362     char **result;
1363     int nrow, ncol;
1364     if (sqlite3_get_table(m_db, stmt.str().c_str(), &result, &nrow, &ncol, &errmsg) != SQLITE_OK)
1365     {
1366         std::wstringstream infoMessage;
1367         infoMessage << L"TskImgDBSqlite::getUnallocRun - Error fetching data from alloc_unalloc_map table: " << errmsg;
1368         LOGERROR(infoMessage.str());
1369 
1370         sqlite3_free(errmsg);
1371 
1372         return new UnallocRun(-1, -1, -1, -1, -1);
1373     }
1374     else
1375     {
1376         int vol_id;
1377         int unalloc_img_sect_start;
1378         int sect_len;
1379         int orig_img_sect_start;
1380         // skip the headers
1381         // @@@ DO SOME ERROR CHECKING HERE to make sure that results has data...
1382         sscanf(result[4], "%d", &vol_id);
1383         sscanf(result[5], "%d", &unalloc_img_sect_start);
1384         sscanf(result[6], "%d", &sect_len);
1385         sscanf(result[7], "%d", &orig_img_sect_start);
1386         sqlite3_free_table(result);
1387         return new UnallocRun(vol_id, a_unalloc_img_id, unalloc_img_sect_start, sect_len, orig_img_sect_start);
1388     }
1389 }
1390 
1391 /**
1392  * Adds information about a carved file into the database.  This includes the sector layout
1393  * information.
1394  *
1395  * @param vol_id Volume in which the carved file was found in
1396  * @param name Name of the file
1397  * @param size Number of bytes in file
1398  * @param runStarts Array with starting sector (relative to start of image) for each run in file.
1399  * @param runLengths Array with number of sectors in each run
1400  * @param numRuns Number of entries in previous arrays
1401  * @param fileId Carved file Id (output)
1402  * @returns 0 on success or -1 on error.
1403  */
addCarvedFileInfo(int vol_id,const char * name,uint64_t size,uint64_t * runStarts,uint64_t * runLengths,int numRuns,uint64_t & fileId)1404 int TskImgDBSqlite::addCarvedFileInfo(int vol_id, const char *name, uint64_t size,
1405                                       uint64_t *runStarts, uint64_t *runLengths, int numRuns, uint64_t & fileId)
1406 {
1407     char stmt[1024];
1408     char * errmsg;
1409     std::wstringstream infoMessage;
1410 
1411     if (!m_db)
1412         return -1;
1413 
1414     // insert into files table
1415     sqlite3_snprintf(1024, stmt,
1416         "INSERT INTO files (file_id, type_id, name, par_file_id, dir_type, meta_type,"
1417         "dir_flags, meta_flags, size, ctime, crtime, atime, mtime, mode, uid, gid, status, full_path) "
1418         "VALUES (NULL, %d, '%q', NULL, %d, %d, %d, %d, %llu, 0, 0, 0, 0, NULL, NULL, NULL, %d, '%q')",
1419         IMGDB_FILES_TYPE_CARVED, name, (int)TSK_FS_NAME_TYPE_REG, (int)TSK_FS_META_TYPE_REG,
1420         (int)TSK_FS_NAME_FLAG_UNALLOC, (int)TSK_FS_META_FLAG_UNALLOC, size, IMGDB_FILES_STATUS_CREATED, name);
1421     // MAY-118 NOTE: addCarvedFileInfo insert entry into files table, but actual file on disk has not been created yet.
1422     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK) {
1423         infoMessage << L"TskImgDBSqlite::addCarvedFileInfo - Error adding data to file table for carved file: " << errmsg << L" " << stmt;
1424 
1425         LOGERROR(infoMessage.str());
1426 
1427         sqlite3_free(errmsg);
1428         return -1;
1429     }
1430 
1431     // get the assigned file_id
1432     fileId = (uint64_t)sqlite3_last_insert_rowid(m_db);
1433 
1434     // insert into the carved_files_table
1435     sqlite3_snprintf(1024, stmt, "INSERT INTO carved_files (file_id, vol_id)"
1436         "VALUES (%llu, %d)", fileId, vol_id);
1437     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK) {
1438         infoMessage << L"TskImgDBSqlite::addCarvedFileInfo - Error adding data to carved_files table: " << errmsg;
1439 
1440         LOGERROR(infoMessage.str());
1441         sqlite3_free(errmsg);
1442         return -1;
1443     }
1444 
1445     // insert into carved_sectors table
1446     for (int i = 0; i < numRuns; i++)
1447     {
1448         sqlite3_snprintf(1023, stmt,
1449             "INSERT INTO carved_sectors (file_id, seq, sect_start, sect_len) "
1450             "VALUES (%llu, %d, %llu, %llu)",
1451             fileId, i, runStarts[i], runLengths[i]);
1452         if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK) {
1453             infoMessage << L"TskImgDBSqlite::addCarvedFileInfo - Error adding data to carved_sectors table: " << errmsg;
1454 
1455             LOGERROR(infoMessage.str());
1456 
1457             sqlite3_free(errmsg);
1458             return -1;
1459         }
1460     }
1461 
1462     return 0;
1463 }
1464 
1465 /**
1466  * Adds information about derived files to the database.  Derived files typically come
1467  * from archives and may be compressed.
1468  *
1469  * @param name The name of the file.
1470  * @param parentId The id of the file from which this file is derived.
1471  * @param isDirectory True if entry is for a directory verus a file
1472  * @param size The size of the file.
1473  * @param details This is a string that may contain extra details related
1474  * to the particular type of mechanism that was used to derive this file,
1475  * e.g. files derived from zip archives may have extra information about the
1476  * compressed size of the file.
1477  * @param ctime Time file system file entry was changed.
1478  * @param crtime Time the file was created.
1479  * @param atime Last access time.
1480  * @param mtime Last modified time.
1481  * @param fileId Reference to location where file_id for file can be assigned
1482  * @param path Path of file
1483  *
1484  * @returns 0 on success or -1 on error.
1485  */
addDerivedFileInfo(const std::string & name,const uint64_t parentId,const bool isDirectory,const uint64_t size,const std::string & details,const int ctime,const int crtime,const int atime,const int mtime,uint64_t & fileId,std::string path)1486 int TskImgDBSqlite::addDerivedFileInfo(const std::string& name, const uint64_t parentId,
1487                                        const bool isDirectory, const uint64_t size,
1488                                        const std::string& details,
1489                                        const int ctime, const int crtime, const int atime, const int mtime,
1490                                        uint64_t &fileId, std::string path)
1491 {
1492     if (!m_db)
1493         return -1;
1494 
1495     char stmt[1024];
1496     char * errmsg;
1497 
1498     TSK_FS_NAME_TYPE_ENUM dirType = isDirectory ? TSK_FS_NAME_TYPE_DIR : TSK_FS_NAME_TYPE_REG;
1499     TSK_FS_META_TYPE_ENUM metaType = isDirectory ? TSK_FS_META_TYPE_DIR : TSK_FS_META_TYPE_REG;
1500 
1501     // insert into files table
1502     sqlite3_snprintf(1024, stmt,
1503         "INSERT INTO files (file_id, type_id, name, par_file_id, dir_type, meta_type, size, ctime, crtime, atime, mtime, status, full_path) "
1504         "VALUES (NULL, %d, '%q', %llu, %d, %d, %llu, %d, %d, %d, %d, %d, '%q')",
1505         IMGDB_FILES_TYPE_DERIVED, name.c_str(), parentId, dirType, metaType, size, ctime, crtime, atime, mtime, IMGDB_FILES_STATUS_CREATED, path.c_str());
1506 
1507     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK)
1508     {
1509         std::wstringstream msg;
1510         msg << L"TskImgDBSqlite::addDerivedFileInfo - Error adding data to file table for derived file: "
1511             << errmsg << L" " << stmt;
1512 
1513         LOGERROR(msg.str());
1514 
1515         sqlite3_free(errmsg);
1516         return -1;
1517     }
1518 
1519     // get the assigned file_id
1520     fileId = sqlite3_last_insert_rowid(m_db);
1521 
1522     // insert into the derived_files table
1523     sqlite3_snprintf(1024, stmt, "INSERT INTO derived_files (file_id, derivation_details) "
1524         "VALUES (%llu, '%q')", fileId, details.c_str());
1525     if (sqlite3_exec(m_db, stmt, NULL, NULL, &errmsg) != SQLITE_OK)
1526     {
1527         std::wstringstream msg;
1528         msg << L"TskImgDBSqlite::addDerivedFileInfo - Error adding data to derived_files table : "
1529             << errmsg;
1530 
1531         LOGERROR(msg.str());
1532         sqlite3_free(errmsg);
1533         return -1;
1534     }
1535 
1536     return 0;
1537 }
1538 
1539 /**
1540  * Fills outBuffer with file IDs that match the name fileName.
1541  * Returns the number of file IDs written into outBuffer or -1 on error.
1542  */
getFileIds(char * a_fileName,uint64_t * a_outBuffer,int a_buffSize) const1543 int TskImgDBSqlite::getFileIds(char *a_fileName, uint64_t *a_outBuffer, int a_buffSize) const
1544 {
1545 
1546     if (!m_db)
1547         return -1;
1548 
1549     int outIdx = 0;
1550 
1551     sqlite3_stmt * statement;
1552     std::stringstream stmt;
1553     stmt << "SELECT file_id FROM files WHERE name LIKE '" << a_fileName << "';";
1554     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
1555         while(sqlite3_step(statement) == SQLITE_ROW) {
1556             a_outBuffer[outIdx++] = (uint64_t)sqlite3_column_int64(statement, 0);
1557         }
1558         sqlite3_finalize(statement);
1559     }
1560     else {
1561         std::wstringstream infoMessage;
1562         infoMessage << L"TskImgDBSqlite::getFileIds - Error querying files table : " << sqlite3_errmsg(m_db);
1563         LOGERROR(infoMessage.str());
1564         return -1;
1565     }
1566 
1567     return outIdx;
1568 }
1569 
1570 /*
1571  * Return the minimum file id with status = READY_FOR_ANALYSIS in minFileId.
1572  * Return 0 on success, -1 if failed.
1573  */
getMinFileIdReadyForAnalysis(uint64_t & minFileId) const1574 int TskImgDBSqlite::getMinFileIdReadyForAnalysis(uint64_t & minFileId) const
1575 {
1576     if (!m_db)
1577         return -1;
1578 
1579     minFileId = 0;
1580 
1581     sqlite3_stmt * statement;
1582     std::stringstream stmt;
1583     stmt << "SELECT min(file_id) FROM files WHERE status = " <<
1584         TskImgDB::IMGDB_FILES_STATUS_READY_FOR_ANALYSIS << ";";
1585     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
1586         int result = sqlite3_step(statement);
1587         if (result == SQLITE_ROW) {
1588             minFileId = (uint64_t)sqlite3_column_int64(statement, 0);
1589         }
1590         sqlite3_finalize(statement);
1591     }
1592     else {
1593         std::wstringstream infoMessage;
1594         infoMessage << L"TskImgDBSqlite::getMinFileIdReadyForAnalysis - Error querying files table : " << sqlite3_errmsg(m_db);
1595         LOGERROR(infoMessage.str());
1596         return -1;
1597     }
1598     return 0;
1599 }
1600 
1601 /**
1602  * Given the last file ID ready for analysis, find the largest file ID ready of analysis (in maxFileId)
1603  * Returns 0 on success or -1 on error.
1604  */
getMaxFileIdReadyForAnalysis(uint64_t a_lastFileId,uint64_t & maxFileId) const1605 int TskImgDBSqlite::getMaxFileIdReadyForAnalysis(uint64_t a_lastFileId, uint64_t & maxFileId) const
1606 {
1607     if (!m_db)
1608         return -1;
1609 
1610     maxFileId = 0;
1611 
1612     sqlite3_stmt * statement;
1613     std::stringstream stmt;
1614     stmt << "SELECT max(file_id) FROM files WHERE status = " <<
1615         TskImgDB::IMGDB_FILES_STATUS_READY_FOR_ANALYSIS <<
1616         " AND file_id >= " <<  a_lastFileId << ";";
1617     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
1618         int result = sqlite3_step(statement);
1619         if (result == SQLITE_ROW) {
1620             maxFileId = (uint64_t)sqlite3_column_int64(statement, 0);
1621         }
1622         sqlite3_finalize(statement);
1623     }
1624     else {
1625         std::wstringstream infoMessage;
1626         infoMessage <<  L"TskImgDBSqlite::getMaxFileIdReadyForAnalysis - Error querying files table : " << sqlite3_errmsg(m_db);
1627         LOGERROR(infoMessage.str());
1628         return -1;
1629     }
1630     return 0;
1631 }
1632 
getFileSectors(uint64_t a_fileId) const1633 SectorRuns * TskImgDBSqlite::getFileSectors(uint64_t a_fileId) const
1634 {
1635     if (!m_db)
1636         return NULL;
1637 
1638     SectorRuns * sr = new SectorRuns();
1639 
1640     sqlite3_stmt * statement;
1641     std::stringstream stmt;
1642     int srCount = 0;
1643     stmt <<
1644         "SELECT fs_blocks.blk_start, fs_blocks.blk_len, "
1645         "fs_info.block_size, fs_info.img_byte_offset, fs_info.vol_id "
1646         "FROM files "
1647         "JOIN fs_files ON files.file_id = fs_files.file_id "
1648         "JOIN fs_blocks ON files.file_id = fs_blocks.file_id "
1649         "JOIN fs_info ON fs_blocks.fs_id = fs_info.fs_id "
1650         "WHERE files.file_id = " << a_fileId << " "
1651         "ORDER BY fs_blocks.seq;";
1652     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
1653         while(sqlite3_step(statement) == SQLITE_ROW) {
1654             uint64_t blkStart = (uint64_t)sqlite3_column_int64(statement, 0);
1655             uint64_t blkLength = (uint64_t)sqlite3_column_int64(statement, 1);
1656             int blkSize = sqlite3_column_int(statement, 2);
1657             uint64_t imgByteOffset = (uint64_t)sqlite3_column_int64(statement, 3);
1658             int volId = sqlite3_column_int(statement, 4);
1659 
1660             uint64_t start = (imgByteOffset + blkStart * blkSize) / 512;
1661             uint64_t len = (blkLength * blkSize) / 512;
1662 
1663             sr->addRun(start, len, volId);
1664             srCount++;
1665         }
1666 
1667         sqlite3_finalize(statement);
1668     }
1669     else {
1670         std::wstringstream infoMessage;
1671         infoMessage <<
1672             L"TskImgDBSqlite::getFileSectors - "
1673             L"Error finding block data for file_id=" << a_fileId << ": " << sqlite3_errmsg(m_db);
1674         LOGERROR(infoMessage.str());
1675         return NULL;
1676     }
1677 
1678     if (srCount < 1) {
1679         delete sr;
1680         sr = NULL;
1681     }
1682     return sr;
1683 }
1684 
1685 /**
1686  * This callback mechanism is registered with SQLite and is
1687  * called whenever an operation would result in SQLITE_BUSY.
1688  * Each time this method is called we will back off IMGDB_RETRY_WAIT
1689  * x count milliseconds. A non zero return value tells SQLite to
1690  * retry the statement and a zero return value tells SQLite to
1691  * stop retrying, in which case it will return SQLITE_BUSY or
1692  * SQLITE_IOERR_BLOCKED to the caller.
1693  *
1694  * @param pDB - a pointer to the sqlite3 structure
1695  * @param count - the number of times this handler has been
1696  * called for this blocking event.
1697  */
busyHandler(void * pDB,int count)1698 int TskImgDBSqlite::busyHandler(void * pDB, int count)
1699 {
1700     if (count < IMGDB_MAX_RETRY_COUNT)
1701     {
1702         Poco::Thread::sleep(IMGDB_RETRY_WAIT * count);
1703         return 1;
1704     }
1705 
1706     return 0;
1707 }
1708 
1709 
1710 
updateFileStatus(uint64_t a_file_id,FILE_STATUS a_status)1711 int TskImgDBSqlite::updateFileStatus(uint64_t a_file_id, FILE_STATUS a_status)
1712 {
1713     if (!m_db)
1714         return 1;
1715 
1716     std::stringstream stmt;
1717     char * errmsg;
1718 
1719     stmt << "UPDATE files SET status = " << a_status << " WHERE file_id = " << a_file_id << ";";
1720     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
1721         std::wstringstream infoMessage;
1722         infoMessage << L"TskImgDBSqlite::updateFileStatus - Error UPDATE file status: " << sqlite3_errmsg(m_db);
1723         LOGERROR(infoMessage.str());
1724         return 1;
1725     }
1726 
1727     return 0;
1728 }
1729 
1730 
updateKnownStatus(uint64_t a_file_id,KNOWN_STATUS a_status)1731 int TskImgDBSqlite::updateKnownStatus(uint64_t a_file_id, KNOWN_STATUS a_status)
1732 {
1733     if (!m_db)
1734         return 1;
1735 
1736     std::stringstream stmt;
1737     char * errmsg;
1738 
1739     stmt << "UPDATE file_hashes SET known = " << a_status << " WHERE file_id = " << a_file_id << ";";
1740     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
1741         std::wstringstream infoMessage;
1742         infoMessage << L"TskImgDBSqlite::updateFileStatus - Error UPDATE file status: " << sqlite3_errmsg(m_db);
1743         LOGERROR(infoMessage.str());
1744         return 1;
1745     }
1746 
1747     return 0;
1748 }
1749 
dbExist() const1750 bool TskImgDBSqlite::dbExist() const
1751 {
1752     if (m_db)
1753         return true;
1754     else
1755         return false;
1756 }
1757 
getCarvedFileInfo(const std::string & stmt,std::map<uint64_t,std::string> & results) const1758 void TskImgDBSqlite::getCarvedFileInfo(const std::string& stmt, std::map<uint64_t, std::string>& results) const
1759 {
1760     sqlite3_stmt * statement;
1761     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK)
1762     {
1763         while (sqlite3_step(statement) == SQLITE_ROW)
1764         {
1765             uint64_t fileId = (uint64_t)sqlite3_column_int64(statement, 0);
1766             std::string fileName = (char*)sqlite3_column_text(statement, 1);
1767             std::string cfileName = (char*)sqlite3_column_text(statement, 2);
1768 
1769             // Grab the extension and append it to the cfile name
1770             std::string::size_type pos = fileName.rfind('.');
1771             if (pos != std::string::npos)
1772                 cfileName.append(fileName.substr(pos));
1773 
1774             results[fileId] = cfileName;
1775         }
1776         sqlite3_finalize(statement);
1777     } else
1778     {
1779         std::wstringstream msg;
1780         msg << L"TskImgDBSqlite::getCarvedFileInfo - Error retrieving carved file details: "
1781             << sqlite3_errmsg(m_db);
1782         LOGERROR(msg.str());
1783     }
1784 }
1785 
getUniqueCarvedFiles(HASH_TYPE hashType) const1786 std::map<uint64_t, std::string> TskImgDBSqlite::getUniqueCarvedFiles(HASH_TYPE hashType) const
1787 {
1788     if (!m_db)
1789         throw TskException("No database.");
1790 
1791     std::map<uint64_t, std::string> results;
1792 
1793     string hash;
1794     switch (hashType) {
1795     case TskImgDB::MD5:
1796         hash = "md5";
1797         break;
1798     case TskImgDB::SHA1:
1799         hash = "sha1";
1800         break;
1801     case TskImgDB::SHA2_256:
1802         hash = "sha2_256";
1803         break;
1804     case TskImgDB::SHA2_512:
1805         hash = "sha2_512";
1806         break;
1807     default:
1808         std::wstringstream msg;
1809         msg << L"TskImgDBSqlite::getUniqueCarvedFiles - Unsupported hashType : " << hashType;
1810         LOGERROR(msg.str());
1811         return results;
1812     }
1813 
1814     stringstream stmt;
1815 
1816     // If hashes have not been calculated return all carved files
1817     stmt << "SELECT count(*) FROM file_hashes;";
1818 
1819     sqlite3_stmt * statement;
1820     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK)
1821     {
1822         if (sqlite3_step(statement) == SQLITE_ROW)
1823         {
1824             uint64_t counter = (uint64_t)sqlite3_column_int64(statement, 0);
1825             if (counter == 0)
1826             {
1827                 sqlite3_finalize(statement);
1828                 stmt.str("");
1829                 stmt << "select c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' "
1830                     << "|| c.file_id from files f, carved_files c, carved_sectors cs "
1831                     << "where c.file_id = cs.file_id and cs.seq = 0 and f.file_id = c.file_id order by c.file_id";
1832                 getCarvedFileInfo(stmt.str(), results);
1833                 return results;
1834             }
1835         }
1836         sqlite3_finalize(statement);
1837     } else
1838     {
1839         std::wstringstream infoMessage;
1840         infoMessage << L"TskImgDBSqlite::getUniqueCarvedFiles - Error getting file_hashes count: " << sqlite3_errmsg(m_db);
1841         LOGERROR(infoMessage.str());
1842     }
1843 
1844     stmt.str("");
1845 
1846     // Get the set of files for which the hash has been calculated.
1847     stmt << "select c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' "
1848         << "|| c.file_id from files f, carved_files c, carved_sectors cs "
1849         << "where c.file_id = cs.file_id and cs.seq = 0 and f.file_id = c.file_id and c.file_id in "
1850         << "(select min(file_id) from file_hashes where " << hash << " != '' group by " << hash << ") order by c.file_id";
1851 
1852     getCarvedFileInfo(stmt.str(), results);
1853 
1854     // Next get the set of files for which the hash has *not* been calculated.
1855     stmt.str("");
1856 
1857     stmt << "select c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' "
1858         << "|| c.file_id from files f, carved_files c, carved_sectors cs "
1859         << "where c.file_id = cs.file_id and cs.seq = 0 and f.file_id = c.file_id and c.file_id in "
1860         << "(select file_id from file_hashes where " << hash << " = '') order by c.file_id";
1861 
1862     getCarvedFileInfo(stmt.str(), results);
1863 
1864     // Finally, add file info for all of the carved files for which there are no hashes of any sort.
1865     // All of these files must be included because without hashes there is no way to determine uniqueness.
1866     stmt.clear();
1867     stmt.str("");
1868     stmt << "SELECT c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' || c.file_id "
1869          << "FROM files f, carved_files c, carved_sectors cs "
1870          << "WHERE c.file_id = cs.file_id AND cs.seq = 0 AND f.file_id = c.file_id AND c.file_id NOT IN "
1871          << "(SELECT fh.file_id FROM file_hashes fh) ORDER BY c.file_id";
1872     getCarvedFileInfo(stmt.str(), results);
1873 
1874     return results;
1875 }
1876 
getCarvedFileInfo(const std::string & query,bool getHash,std::vector<TskCarvedFileInfo> & carvedFileInfos) const1877 void TskImgDBSqlite::getCarvedFileInfo(const std::string &query, bool getHash, std::vector<TskCarvedFileInfo> &carvedFileInfos) const
1878 {
1879     sqlite3_stmt *statement;
1880     executeStatement(query, statement, "TskImgDBSqlite::getCarvedFileInfo");
1881 
1882     TskCarvedFileInfo info;
1883     while (sqlite3_step(statement) == SQLITE_ROW)
1884     {
1885         info.fileID = static_cast<uint64_t>(sqlite3_column_int64(statement, 0));
1886         std::string fileName = reinterpret_cast<const char*>(sqlite3_column_text(statement, 1)); // Reinterpret from unsigned Char*
1887         info.cFileName = reinterpret_cast<const char*>(sqlite3_column_text(statement, 2)); // Reinterpret from unsigned Char*
1888         if (getHash)
1889         {
1890             info.hash = reinterpret_cast<const char*>(sqlite3_column_text(statement, 3));
1891         }
1892 
1893         // Append the extension from the original file name to the constructed "cfile" name.
1894         std::string::size_type pos = fileName.rfind('.');
1895         if (pos != std::string::npos)
1896         {
1897             info.cFileName.append(fileName.substr(pos));
1898         }
1899 
1900         carvedFileInfos.push_back(info);
1901     }
1902 
1903     sqlite3_finalize(statement);
1904 }
1905 
getUniqueCarvedFilesInfo(HASH_TYPE hashType) const1906 std::vector<TskCarvedFileInfo> TskImgDBSqlite::getUniqueCarvedFilesInfo(HASH_TYPE hashType) const
1907 {
1908     const std::string msgPrefix = "TskImgDBSqlite::getUniqueCarvedFilesInfo : ";
1909 
1910     if (!m_db)
1911     {
1912         std::ostringstream msg;
1913         msg << msgPrefix << "no database connection";
1914         throw TskException(msg.str());
1915     }
1916 
1917     // Map the requested hash type to a file_hashes table column name.
1918     string hash;
1919     switch (hashType)
1920     {
1921     case TskImgDB::MD5:
1922         hash = "md5";
1923         break;
1924     case TskImgDB::SHA1:
1925         hash = "sha1";
1926         break;
1927     case TskImgDB::SHA2_256:
1928         hash = "sha2_256";
1929         break;
1930     case TskImgDB::SHA2_512:
1931         hash = "sha2_512";
1932         break;
1933     default:
1934         std::ostringstream msg;
1935         msg << msgPrefix << "unsupported hash type :" << hashType;
1936         throw TskException(msg.str());
1937     }
1938 
1939     std::vector<TskCarvedFileInfo> carvedFileInfos;
1940 
1941     // Do a quick check to see if any hashes have been calculated.
1942     std::ostringstream query;
1943     query << "SELECT COUNT(*) FROM file_hashes;";
1944     sqlite3_stmt *countStmt;
1945     executeStatement(query.str(), countStmt, "TskImgDBSqlite::getUniqueCarvedFiles");
1946     if (sqlite3_step(countStmt) == SQLITE_ROW && static_cast<uint64_t>(sqlite3_column_int64(countStmt, 0)) != 0)
1947     {
1948         // At least one type of hash has been calculated (presumably for all files, but this is not guaranteed).
1949         // First, add file info for the set of unique files among the carved files for which the specified type of hash is available.
1950         query.clear();
1951         query.str("");
1952         query << "SELECT c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' || c.file_id, fh." << hash << " "
1953               << "FROM files f, carved_files c, carved_sectors cs, file_hashes fh "
1954               << "WHERE c.file_id = cs.file_id AND cs.seq = 0 AND f.file_id = c.file_id AND c.file_id = fh.file_id AND c.file_id IN "
1955               << "(SELECT MIN(file_id) FROM file_hashes WHERE " << hash << " != '' GROUP BY " << hash << ") ORDER BY c.file_id";
1956         getCarvedFileInfo(query.str(), true, carvedFileInfos);
1957 
1958          // Next, add file info for all of the carved files for which the specified hash is not available.
1959          // All of these files must be included because without the specified hash there is no acceptable way to determine uniqueness.
1960         query.clear();
1961         query.str("");
1962         query << "SELECT c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' || c.file_id "
1963               << "FROM files f, carved_files c, carved_sectors cs "
1964               << "WHERE c.file_id = cs.file_id AND cs.seq = 0 AND f.file_id = c.file_id AND c.file_id IN "
1965               << "(SELECT file_id FROM file_hashes WHERE " << hash << " = '') ORDER BY c.file_id";
1966         getCarvedFileInfo(query.str(), false, carvedFileInfos);
1967 
1968         // Finally, add file info for all of the carved files for which there are no hashes of any sort.
1969         // All of these files must be included because without hashes there is no way to determine uniqueness.
1970         query.clear();
1971         query.str("");
1972         query << "SELECT c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' || c.file_id "
1973               << "FROM files f, carved_files c, carved_sectors cs "
1974               << "WHERE c.file_id = cs.file_id AND cs.seq = 0 AND f.file_id = c.file_id AND c.file_id NOT IN "
1975               << "(SELECT fh.file_id FROM file_hashes fh) ORDER BY c.file_id";
1976         getCarvedFileInfo(query.str(), false, carvedFileInfos);
1977     }
1978     else
1979     {
1980         // No hashes have been calculated.
1981         // Return carved file info all of the carved files because without hashes there is no way to determine uniqueness.
1982         query.clear();
1983         query.str("");
1984         query << "SELECT c.file_id, f.name, 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' || c.file_id "
1985               << "FROM files f, carved_files c, carved_sectors cs "
1986               << "WHERE c.file_id = cs.file_id AND cs.seq = 0 AND f.file_id = c.file_id ORDER BY c.file_id";
1987         getCarvedFileInfo(query.str(), false, carvedFileInfos);
1988 
1989         std::ostringstream msg;
1990         msg << msgPrefix << "no hashes available, returning all carved files";
1991         LOGWARN(msg.str());
1992     }
1993     sqlite3_finalize(countStmt);
1994 
1995     return carvedFileInfos;
1996 }
1997 
getCarvedFileIds() const1998 std::vector<uint64_t> TskImgDBSqlite::getCarvedFileIds() const
1999 {
2000     return getFileIdsWorker("carved_files");
2001 }
2002 
getUniqueFileIds(HASH_TYPE hashType) const2003 std::vector<uint64_t> TskImgDBSqlite::getUniqueFileIds(HASH_TYPE hashType) const
2004 {
2005     if (!m_db)
2006         throw TskException("No database.");
2007 
2008     std::vector<uint64_t> results;
2009 
2010     string hash;
2011     switch (hashType) {
2012     case TskImgDB::MD5:
2013         hash = "md5";
2014         break;
2015     case TskImgDB::SHA1:
2016         hash = "sha1";
2017         break;
2018     case TskImgDB::SHA2_256:
2019         hash = "sha2_256";
2020         break;
2021     case TskImgDB::SHA2_512:
2022         hash = "sha2_512";
2023         break;
2024     default:
2025         std::wstringstream errorMsg;
2026         errorMsg << L"TskImgDBSqlite::getUniqueFileIds - Unsupported hashType : " << hashType ;
2027         LOGERROR(errorMsg.str());
2028         return results;
2029     }
2030 
2031     stringstream stmt;
2032 
2033     stmt << "SELECT min(file_id) FROM file_hashes WHERE " << hash << " != '' group by " << hash ;
2034 
2035     sqlite3_stmt * statement;
2036     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2037         while (sqlite3_step(statement) == SQLITE_ROW) {
2038             uint64_t fileId = (uint64_t)sqlite3_column_int64(statement, 0);
2039             results.push_back(fileId);
2040         }
2041         sqlite3_finalize(statement);
2042     } else {
2043         std::wstringstream infoMessage;
2044         infoMessage << L"TskImgDBSqlite::getUniqueFileIds - Error querying file_hashes table: " << sqlite3_errmsg(m_db);
2045         LOGERROR(infoMessage.str());
2046     }
2047     return results;
2048 }
2049 
getFileIds() const2050 std::vector<uint64_t> TskImgDBSqlite::getFileIds() const
2051 {
2052     return getFileIdsWorker("files");
2053 }
2054 
getFileIdsWorker(std::string tableName,const std::string condition) const2055 std::vector<uint64_t> TskImgDBSqlite::getFileIdsWorker(std::string tableName, const std::string condition) const
2056 {
2057     if (!m_db)
2058         throw TskException("No database.");
2059 
2060     std::vector<uint64_t> results;
2061 
2062     stringstream stmt;
2063 
2064     stmt << "SELECT file_id FROM " << tableName;
2065     if (condition.compare("") != 0)
2066         stmt << " WHERE " << condition;
2067     stmt <<  " ORDER BY file_id";
2068 
2069     sqlite3_stmt * statement;
2070     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2071         while (sqlite3_step(statement) == SQLITE_ROW) {
2072             uint64_t fileId = (uint64_t)sqlite3_column_int64(statement, 0);
2073             results.push_back(fileId);
2074         }
2075         sqlite3_finalize(statement);
2076     } else {
2077         std::wstringstream infoMessage;
2078         infoMessage << L"TskImgDBSqlite::getFileIdsWorker - Error getting file ids from table " <<
2079             tableName.c_str() << ", " << sqlite3_errmsg(m_db);
2080         LOGERROR(infoMessage.str());
2081     }
2082     return results;
2083 }
2084 
2085 /**
2086  * Get the list of file ids that match the given criteria.
2087  * The given string will be appended to "select files.file_id from files".
2088  * See \ref img_db_schema_v1_5_page for tables and columns to include in
2089  * the selection criteria.
2090  *
2091  * @param condition Must be a valid SQL string defining the selection criteria.
2092  * @returns The collection of file ids matching the selection criteria. Throws
2093  * TskException if database not initialized.
2094  */
getFileIds(const std::string & condition) const2095 std::vector<uint64_t> TskImgDBSqlite::getFileIds(const std::string& condition) const
2096 {
2097     if (!m_db)
2098         throw TskException("Database not initialized.");
2099 
2100     std::vector<uint64_t> results;
2101 
2102     std::string stmt("SELECT files.file_id FROM files");
2103 
2104     constructStmt(stmt, condition);
2105 
2106     sqlite3_stmt * statement;
2107     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK)
2108     {
2109         while (sqlite3_step(statement) == SQLITE_ROW)
2110         {
2111             uint64_t fileId = (uint64_t)sqlite3_column_int64(statement, 0);
2112             results.push_back(fileId);
2113         }
2114         sqlite3_finalize(statement);
2115     } else
2116     {
2117         std::wstringstream msg;
2118         msg << L"TskImgDBSqlite::getFilesIds - Error getting file ids: " << sqlite3_errmsg(m_db);
2119         LOGERROR(msg.str());
2120     }
2121     return results;
2122 }
2123 /*
2124  * Get the list of file records that match the given criteria.
2125  * The given string will be appended to "select ... from files".
2126  *
2127  * @param condition Must be a valid SQL string defining the selection criteria.
2128  * @returns The collection of file records matching the selection criteria. Throws
2129  * TskException if database not initialized.
2130  */
getFileRecords(const std::string & condition) const2131 const std::vector<TskFileRecord> TskImgDBSqlite::getFileRecords(const std::string& condition) const
2132 {
2133     if (!m_db)
2134         throw TskException("Database not initialized.");
2135 
2136     std::vector<TskFileRecord> results;
2137 
2138     std::stringstream stmtstrm;
2139 
2140     stmtstrm << "SELECT f.file_id, f.type_id, f.name, f.par_file_id, f.dir_type, f.meta_type, f.dir_flags, "
2141         << "f.meta_flags, f.size, f.ctime, f.crtime, f.atime, f.mtime, f.mode, f.uid, f.gid, f.status, f.full_path, "
2142         << "fh.md5, fh.sha1, fh.sha2_256, fh.sha2_512 "
2143         << "FROM files f LEFT OUTER JOIN file_hashes fh ON f.file_id = fh.file_id ";
2144 
2145     std::string stmt = stmtstrm.str();
2146     constructStmt(stmt, condition);
2147 
2148     sqlite3_stmt * statement;
2149     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK)
2150     {
2151         while(sqlite3_step(statement) == SQLITE_ROW) {
2152             TskFileRecord fileRecord;
2153             fileRecord.fileId       = sqlite3_column_int64(statement, 0);
2154             fileRecord.typeId       = (TskImgDB::FILE_TYPES) sqlite3_column_int(statement, 1);
2155             fileRecord.name         = (char *)sqlite3_column_text(statement, 2);
2156             fileRecord.parentFileId = sqlite3_column_int64(statement, 3);
2157             fileRecord.dirType      = (TSK_FS_NAME_TYPE_ENUM) sqlite3_column_int(statement, 4);
2158             fileRecord.metaType     = (TSK_FS_META_TYPE_ENUM) sqlite3_column_int(statement, 5);
2159             fileRecord.dirFlags     = (TSK_FS_NAME_FLAG_ENUM) sqlite3_column_int(statement, 6);
2160             fileRecord.metaFlags    = (TSK_FS_META_FLAG_ENUM) sqlite3_column_int(statement, 7);
2161             fileRecord.size         = sqlite3_column_int64(statement, 8);
2162             fileRecord.ctime        = sqlite3_column_int(statement, 9);
2163             fileRecord.crtime       = sqlite3_column_int(statement, 10);
2164             fileRecord.atime        = sqlite3_column_int(statement, 11);
2165             fileRecord.mtime        = sqlite3_column_int(statement, 12);
2166             fileRecord.mode         = (TSK_FS_META_MODE_ENUM) sqlite3_column_int(statement, 13);
2167             fileRecord.uid          = sqlite3_column_int(statement, 14);
2168             fileRecord.gid          = sqlite3_column_int(statement, 15);
2169             fileRecord.status       = (TskImgDB::FILE_STATUS) sqlite3_column_int(statement, 16);
2170             fileRecord.fullPath     = (char *)sqlite3_column_text(statement, 17);
2171 
2172             if (sqlite3_column_type(statement, 18) == SQLITE_TEXT)
2173                 fileRecord.md5      = (char *)sqlite3_column_text(statement, 18);
2174             if (sqlite3_column_type(statement, 19) == SQLITE_TEXT)
2175                 fileRecord.sha1     = (char *)sqlite3_column_text(statement, 19);
2176             if (sqlite3_column_type(statement, 20) == SQLITE_TEXT)
2177                 fileRecord.sha2_256 = (char *)sqlite3_column_text(statement, 20);
2178             if (sqlite3_column_type(statement, 21) == SQLITE_TEXT)
2179                 fileRecord.sha2_512 = (char *)sqlite3_column_text(statement, 21);
2180             results.push_back(fileRecord);
2181         }
2182     }
2183     else
2184     {
2185         std::wstringstream msg;
2186         msg << L"TskImgDBSqlite::getFilesRecords - Error getting file reocrds: " << sqlite3_errmsg(m_db);
2187         LOGERROR(msg.str());
2188     }
2189     return results;
2190 }
2191 
2192 
2193 /**
2194  * Get the number of files that match the given criteria.
2195  * The given string will be appended to "select files.file_id from files".
2196  *
2197  * @param condition Must be a valid SQL string defining the selection criteria.
2198  * @returns The number of files matching the selection criteria.
2199  */
getFileCount(const std::string & condition) const2200 int TskImgDBSqlite::getFileCount(const std::string& condition) const
2201 {
2202     if (!m_db)
2203         throw TskException("Database not initialized.");
2204 
2205     int result = 0;
2206 
2207     std::string stmt("SELECT COUNT(files.file_id) FROM files");
2208 
2209     constructStmt(stmt, condition);
2210 
2211     sqlite3_stmt * statement;
2212     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK)
2213     {
2214         while (sqlite3_step(statement) == SQLITE_ROW)
2215         {
2216             result = (uint64_t)sqlite3_column_int(statement, 0);
2217         }
2218         sqlite3_finalize(statement);
2219     } else
2220     {
2221         std::wstringstream msg;
2222         msg << L"TskImgDBSqlite::getFileCount - Error getting file count: " << sqlite3_errmsg(m_db);
2223         LOGERROR(msg.str());
2224     }
2225     return result;
2226 }
2227 
tsk_strnicmp(const char * s1,const char * s2,size_t N)2228 int tsk_strnicmp(const char *s1, const char *s2, size_t N)
2229 {
2230     if (N == 0)
2231         return 0;
2232     int diff = 0;
2233     if (s1 && s2) {
2234         while (N-- > 0 && (diff = (toupper(*s1) - toupper(*s2))) == 0 && *s1 && *s2) {
2235             s1++;
2236             s2++;
2237         }
2238     }
2239     else if (s1)
2240         return +1;
2241     else if (s2)
2242         return -1;
2243     return diff;
2244 }
2245 
2246 /* Append condition to stmt to make a single SQL query.
2247  */
constructStmt(std::string & stmt,std::string condition) const2248 void TskImgDBSqlite::constructStmt(std::string& stmt, std::string condition) const
2249 {
2250     if (!condition.empty())
2251     {
2252         // Remove leading whitespace from condition
2253         condition.erase(0, condition.find_first_not_of(' '));
2254 
2255         std::string whereClause("WHERE");
2256         std::string joinClause("JOIN");
2257         std::string leftClause("LEFT");
2258         std::string orderClause("ORDER");
2259 
2260         /* If the condition doesn't start with one of the below statements
2261          * (WHERE, JOIN, etc.), then
2262          * it is presumably extending the FROM clause with
2263          * one or more table names. In this case we need to add the comma to
2264          * the statement. */
2265         if (tsk_strnicmp(condition.c_str(), whereClause.c_str(), whereClause.length()) != 0 &&
2266             tsk_strnicmp(condition.c_str(), joinClause.c_str(), joinClause.length()) != 0 &&
2267             tsk_strnicmp(condition.c_str(), leftClause.c_str(), leftClause.length()) != 0 &&
2268             tsk_strnicmp(condition.c_str(), orderClause.c_str(), orderClause.length()) != 0 &&
2269             condition[0] != ',')
2270         {
2271             stmt.append(",");
2272         }
2273     }
2274 
2275     stmt.append(" ");
2276     stmt.append(condition);
2277 }
2278 
2279 // Set file hash for hashType for a_file_id
2280 // Return 1 on failure, 0 on success.
setHash(const uint64_t a_file_id,const TskImgDB::HASH_TYPE hashType,const std::string & hash) const2281 int TskImgDBSqlite::setHash(const uint64_t a_file_id, const TskImgDB::HASH_TYPE hashType, const std::string& hash) const
2282 {
2283     if (!m_db)
2284         throw TskException("No database.");
2285 
2286     string hashTypeStr;
2287     switch (hashType) {
2288     case TskImgDB::MD5:
2289         hashTypeStr = "md5";
2290         break;
2291     case TskImgDB::SHA1:
2292         hashTypeStr = "sha1";
2293         break;
2294     case TskImgDB::SHA2_256:
2295         hashTypeStr = "sha2_256";
2296         break;
2297     case TskImgDB::SHA2_512:
2298         hashTypeStr = "sha2_512";
2299         break;
2300     default:
2301         std::wstringstream errorMsg;
2302         errorMsg << L"TskImgDBSqlite::setHash - Unsupported hashType : " << hashType ;
2303         LOGERROR(errorMsg.str());
2304         return 1;
2305     }
2306 
2307     stringstream stmt;
2308     std::string md5, sha1, sha2_256, sha2_512;
2309     int known = IMGDB_FILES_UNKNOWN;
2310     std::stringstream stream;
2311 
2312     stmt << "SELECT md5, sha1, sha2_256, sha2_512, known from file_hashes WHERE file_id = " << a_file_id;
2313 
2314     sqlite3_stmt * statement;
2315     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2316         int result = sqlite3_step(statement);
2317         if (result == SQLITE_ROW) {
2318             md5 = (char *)sqlite3_column_text(statement, 0);
2319             sha1 = (char *)sqlite3_column_text(statement, 1);
2320             sha2_256 = (char *)sqlite3_column_text(statement, 2);
2321             sha2_512 = (char *)sqlite3_column_text(statement, 3);
2322             known = (int)sqlite3_column_int(statement, 4);
2323         }
2324         sqlite3_finalize(statement);
2325     } else {
2326         ; // OK if not exists
2327     }
2328 
2329     // insert new record
2330     stmt.str("");
2331     stmt << "INSERT OR REPLACE INTO file_hashes (file_id, md5, sha1, sha2_256, sha2_512, known) VALUES (" << a_file_id;
2332     switch (hashType) {
2333     case TskImgDB::MD5:
2334         stmt << ", '" << hash << "'";
2335         stmt << ", '" << sha1 << "'";
2336         stmt << ", '" << sha2_256 << "'";
2337         stmt << ", '" << sha2_512 << "'";
2338         stream << known;
2339         stmt << ", " << stream.str();
2340         break;
2341     case TskImgDB::SHA1:
2342         stmt << ", '" << md5 << "'";
2343         stmt << ", '" << hash << "'";
2344         stmt << ", '" << sha2_256 << "'";
2345         stmt << ", '" << sha2_512 << "'";
2346         stream << known;
2347         stmt << ", " << stream.str();
2348         break;
2349     case TskImgDB::SHA2_256:
2350         stmt << ", '" << md5 << "'";
2351         stmt << ", '" << sha1 << "'";
2352         stmt << ", '" << hash << "'";
2353         stmt << ", '" << sha2_512 << "'";
2354         stream << known;
2355         stmt << ", " << stream.str();
2356         break;
2357     case TskImgDB::SHA2_512:
2358         stmt << ", '" << md5 << "'";
2359         stmt << ", '" << sha1 << "'";
2360         stmt << ", '" << sha2_256 << "'";
2361         stmt << ", '" << hash << "'";
2362         stream << known;
2363         stmt << ", " << stream.str();
2364         break;
2365     }
2366     stmt << ")";
2367 
2368     char *errmsg;
2369     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
2370         std::wstringstream infoMessage;
2371         infoMessage << L"TskImgDBSqlite::setHash - Error adding hash to file_hashes table: " << errmsg;
2372         LOGERROR(infoMessage.str());
2373         sqlite3_free(errmsg);
2374         return 1;
2375     }
2376 
2377     return 0;
2378 }
2379 
getCfileName(const uint64_t a_file_id) const2380 std::string TskImgDBSqlite::getCfileName(const uint64_t a_file_id) const
2381 {
2382     if (!m_db)
2383         throw TskException("No database.");
2384 
2385     std::string cfileName;
2386     stringstream stmt;
2387 
2388     stmt << "select 'cfile_' || c.vol_id || '_' || cs.sect_start || '_' || f.file_id"
2389         " from files f, carved_files c, carved_sectors cs where f.file_id = c.file_id and c.file_id = cs.file_id and cs.seq = 0"
2390         " and f.file_id = " << a_file_id;
2391 
2392     sqlite3_stmt * statement;
2393     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2394         int result = sqlite3_step(statement);
2395         if (result == SQLITE_ROW) {
2396             cfileName = (char *)sqlite3_column_text(statement, 0);
2397         }
2398         sqlite3_finalize(statement);
2399     } else {
2400         std::wstringstream infoMessage;
2401         infoMessage <<  L"TskImgDBSqlite::getCfileName - Error querying tables: %S" << sqlite3_errmsg(m_db);
2402         LOGERROR(infoMessage.str());
2403     }
2404 
2405     stmt.str("");
2406     stmt << "select f.name "
2407         " from files f, carved_files c, carved_sectors cs where f.file_id = c.file_id and c.file_id = cs.file_id and cs.seq = 0"
2408         " and f.file_id = " << a_file_id;
2409 
2410     std::string name;
2411     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2412         int result = sqlite3_step(statement);
2413         if (result == SQLITE_ROW) {
2414             name = (char *)sqlite3_column_text(statement, 0);
2415         }
2416         sqlite3_finalize(statement);
2417         size_t pos = name.rfind('.');
2418         if (pos != string::npos)
2419             cfileName += name.substr(pos);
2420     } else {
2421         std::wstringstream infoMessage;
2422         infoMessage << L"TskImgDBSqlite::getCfileName - Error querying tables: " << sqlite3_errmsg(m_db);
2423         LOGERROR(infoMessage.str());
2424     }
2425 
2426     return cfileName;
2427 }
2428 
2429 /**
2430  * Return the ImageInfo
2431  * @param type Image Type (output)
2432  * @param sectorSize Image sector size (output)
2433  * @returns 0 on success or -1 on error.
2434  */
getImageInfo(int & type,int & sectorSize) const2435 int TskImgDBSqlite::getImageInfo(int & type, int & sectorSize) const
2436 {
2437     int rc = -1;
2438     if (!m_db)
2439         return rc;
2440 
2441     stringstream stmt;
2442 
2443     stmt << "SELECT type, ssize FROM image_info";
2444 
2445     sqlite3_stmt * statement;
2446     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2447         int result = sqlite3_step(statement);
2448         if (result == SQLITE_ROW) {
2449             type = (int)sqlite3_column_int64(statement, 0);
2450             sectorSize = (int)sqlite3_column_int64(statement, 1);
2451             rc = 0;
2452         }
2453         sqlite3_finalize(statement);
2454     } else {
2455         std::wstringstream infoMessage;
2456         infoMessage <<  L"TskImgDBSqlite::getImageInfo - Error querying image_info table: " << sqlite3_errmsg(m_db);
2457         LOGERROR(infoMessage.str());
2458         return -1;
2459     }
2460     return rc;
2461 }
2462 
2463 /**
2464  * Return a list of TskVolumeInfoRecord
2465  * @param volumeInfoList A list of TskVolumeInfoRecord (output)
2466  * @returns 0 on success or -1 on error.
2467  */
getVolumeInfo(std::list<TskVolumeInfoRecord> & volumeInfoList) const2468 int TskImgDBSqlite::getVolumeInfo(std::list<TskVolumeInfoRecord> & volumeInfoList) const
2469 {
2470     std::list<TskVolumeInfoRecord> list;
2471 
2472     if (!m_db)
2473         return -1;
2474 
2475     stringstream stmt;
2476     stmt << "SELECT vol_id, sect_start, sect_len, description, flags FROM vol_info";
2477 
2478     sqlite3_stmt * statement;
2479     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2480         while (sqlite3_step(statement) == SQLITE_ROW) {
2481             TskVolumeInfoRecord vol_info;
2482             vol_info.vol_id = sqlite3_column_int(statement,0);
2483             vol_info.sect_start = sqlite3_column_int64(statement,1);
2484             vol_info.sect_len = sqlite3_column_int64(statement,2);
2485             vol_info.description.assign((char *)sqlite3_column_text(statement, 3));
2486             vol_info.flags = (TSK_VS_PART_FLAG_ENUM)sqlite3_column_int(statement, 4);
2487             volumeInfoList.push_back(vol_info);
2488         }
2489         sqlite3_finalize(statement);
2490     } else {
2491         std::wstringstream infoMessage;
2492         infoMessage << L"TskImgDBSqlite::getVolumeInfo - Error getting from vol_info table: " << sqlite3_errmsg(m_db);
2493         LOGERROR(infoMessage.str());
2494         return -1;
2495     }
2496     return 0;
2497 }
2498 
2499 /**
2500  * Return a list of TskFsInfoRecord
2501  * @param fsInfoList A list of TskFsInfoRecord (output)
2502  * @returns 0 on success or -1 on error.
2503  */
getFsInfo(std::list<TskFsInfoRecord> & fsInfoList) const2504 int TskImgDBSqlite::getFsInfo(std::list<TskFsInfoRecord> & fsInfoList) const
2505 {
2506     std::list<TskFsInfoRecord> list;
2507 
2508     if (!m_db)
2509         return -1;
2510 
2511     stringstream stmt;
2512     stmt << "SELECT fs_id, img_byte_offset, vol_id, fs_type, block_size, block_count, root_inum, first_inum, last_inum FROM fs_info";
2513 
2514     sqlite3_stmt * statement;
2515     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2516         while (sqlite3_step(statement) == SQLITE_ROW) {
2517             TskFsInfoRecord fs_info;
2518             fs_info.fs_id = sqlite3_column_int(statement,0);
2519             fs_info.img_byte_offset = sqlite3_column_int64(statement,1);
2520             fs_info.vol_id = sqlite3_column_int(statement,2);
2521             fs_info.fs_type = (TSK_FS_TYPE_ENUM)sqlite3_column_int(statement,3);
2522             fs_info.block_size = sqlite3_column_int(statement,4);
2523             fs_info.block_count = sqlite3_column_int64(statement,5);
2524             fs_info.root_inum = sqlite3_column_int64(statement,6);
2525             fs_info.first_inum = sqlite3_column_int64(statement,7);
2526             fs_info.last_inum = sqlite3_column_int64(statement,8);
2527             fsInfoList.push_back(fs_info);
2528         }
2529         sqlite3_finalize(statement);
2530     } else {
2531         std::wstringstream infoMessage;
2532         infoMessage << L"TskImgDBSqlite::getFsInfo - Error getting from fs_info table: " << sqlite3_errmsg(m_db);
2533         LOGERROR(infoMessage.str());
2534         return -1;
2535     }
2536     return 0;
2537 }
2538 
2539 typedef std::map<std::string, int> FileTypeMap_t;
2540 
getFileType(const char * name)2541 static std::string getFileType(const char *name)
2542 {
2543     std::string filename = name;
2544     size_t pos = filename.rfind('.');
2545     if (pos != std::string::npos) {
2546         std::string suffix = filename.substr(pos);
2547         std::string result;
2548         for (size_t i=0; i < suffix.size(); i++) {
2549             result += (char)tolower(suffix[i]);
2550         }
2551         return result;
2552     }
2553     else
2554         return std::string("");
2555 }
2556 
2557 /**
2558  * Return a list of TskFileTypeRecord for all files.
2559  * @param fileTypeInfoList A list of TskFileTypeRecord (output)
2560  * @returns 0 on success or -1 on error.
2561  */
getFileInfoSummary(std::list<TskFileTypeRecord> & fileTypeInfoList) const2562 int TskImgDBSqlite::getFileInfoSummary(std::list<TskFileTypeRecord> &fileTypeInfoList) const
2563 {
2564     std::stringstream stmt;
2565     stmt << "SELECT name FROM files WHERE dir_type = " << TSK_FS_NAME_TYPE_REG;
2566 
2567     return getFileTypeRecords(stmt.str(), fileTypeInfoList);
2568 }
2569 
2570 /**
2571  * Return a list of TskFileTypeRecord for fileType
2572  * @param fileType FILE_TYPE to report
2573  * @param fileTypeInfoList A list of TskFileTypeRecord (output)
2574  * @returns 0 on success or -1 on error.
2575  */
getFileInfoSummary(FILE_TYPES fileType,std::list<TskFileTypeRecord> & fileTypeInfoList) const2576 int TskImgDBSqlite::getFileInfoSummary(FILE_TYPES fileType, std::list<TskFileTypeRecord> & fileTypeInfoList) const
2577 {
2578     stringstream stmt;
2579     stmt << "SELECT name FROM files WHERE type_id = " << fileType << " AND dir_type = " << TSK_FS_NAME_TYPE_REG;
2580 
2581     return getFileTypeRecords(stmt.str(), fileTypeInfoList);
2582 }
2583 
2584 /**
2585  * Return a list of TskFileTypeRecords matching the given SQL statement.
2586  * @param stmt The SQL statement used to match file records.
2587  * @param fileTypeInfoList A list of TskFileTypeRecord (output)
2588  * @returns 0 on success of -1 on error.
2589  */
getFileTypeRecords(const std::string & stmt,std::list<TskFileTypeRecord> & fileTypeInfoList) const2590 int TskImgDBSqlite::getFileTypeRecords(const std::string& stmt, std::list<TskFileTypeRecord>& fileTypeInfoList) const
2591 {
2592     if (!m_db)
2593         return -1;
2594 
2595     std::list<TskFileTypeRecord> list;
2596 
2597     sqlite3_stmt * statement;
2598     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK) {
2599         FileTypeMap_t fileTypeMap;
2600         while (sqlite3_step(statement) == SQLITE_ROW) {
2601             char *name = (char *)sqlite3_column_text(statement, 0);
2602             std::string type = getFileType(name);
2603             FileTypeMap_t::iterator iter = fileTypeMap.find(type);
2604             if (iter != fileTypeMap.end()) {
2605                 // increment file counter
2606                 int count = iter->second;
2607                 fileTypeMap[type] = ++count;
2608             } else {
2609                 // add a new file type
2610                 fileTypeMap.insert(pair<std::string, int>(type, 1));
2611             }
2612         }
2613         for (FileTypeMap_t::const_iterator iter=fileTypeMap.begin(); iter != fileTypeMap.end(); iter++) {
2614             TskFileTypeRecord info;
2615             info.suffix.assign((*iter).first.c_str());
2616             info.count = (*iter).second;
2617             info.description.assign("File Type Description");
2618             fileTypeInfoList.push_back(info);
2619         }
2620         sqlite3_finalize(statement);
2621     } else {
2622         std::wstringstream infoMessage;
2623         infoMessage << L"TskImgDBSqlite::getFileTypeRecords - Error querying files table: " << sqlite3_errmsg(m_db);
2624         LOGERROR(infoMessage.str());
2625         return -1;
2626     }
2627     return 0;
2628 }
2629 
2630 /**
2631  * Insert the Module record, if module name does not already exist in modules table.
2632  * Returns Module Id associated with the Module record.
2633  * @param name Module name
2634  * @param description Module description
2635  * @param moduleId Module Id (output)
2636  * @returns 0 on success, -1 on error.
2637  */
addModule(const std::string & name,const std::string & description,int & moduleId)2638 int TskImgDBSqlite::addModule(const std::string& name, const std::string& description, int & moduleId)
2639 {
2640     if (!m_db)
2641         return -1;
2642 
2643     if (name.empty())
2644     {
2645         LOGWARN(L"TskImgDBSqlite::addModule - Given an empty module name.");
2646         return -1;
2647     }
2648 
2649     moduleId = 0;
2650 
2651     sqlite3_stmt * statement;
2652     char stmt[1024];
2653     sqlite3_snprintf(1024, stmt, "SELECT module_id FROM modules WHERE name = '%q';",
2654                      name.c_str());
2655 
2656     if (sqlite3_prepare_v2(m_db, stmt, -1, &statement, 0) == SQLITE_OK)
2657     {
2658         int result = sqlite3_step(statement);
2659         if (result == SQLITE_ROW)
2660         {
2661             // Already exists, return module_id
2662             moduleId = sqlite3_column_int(statement, 0);
2663         }
2664         else
2665         {
2666             // Create a new module record.
2667             char insertStmt[1024];
2668             char * errmsg;
2669             sqlite3_snprintf(1024, insertStmt,
2670                 "INSERT INTO modules (module_id, name, description) VALUES (NULL, '%q', '%q');",
2671                 name.c_str(), description.c_str());
2672             if (sqlite3_exec(m_db, insertStmt, NULL, NULL, &errmsg) == SQLITE_OK)
2673             {
2674                 moduleId = (int)sqlite3_last_insert_rowid(m_db);
2675             }
2676             else
2677             {
2678                 std::wstringstream msg;
2679                 msg << L"TskImgDBSqlite::addModule - Error adding record to modules table: " << errmsg;
2680                 LOGERROR(msg.str());
2681                 sqlite3_free(errmsg);
2682             }
2683         }
2684         sqlite3_finalize(statement);
2685     }
2686     else
2687     {
2688         std::wstringstream msg;
2689         msg << L"TskImgDBSqlite::addModule - Failed to prepare statement: " << stmt;
2690         LOGERROR(msg.str());
2691     }
2692 
2693     if (moduleId == 0)
2694         return -1;
2695 
2696     return 0;
2697 }
2698 
2699 /**
2700  * Insert the module status record.
2701  * @param file_id file_id
2702  * @param module_id module_id
2703  * @param status Status of module
2704  * @returns 0 on success, -1 on error.
2705  */
setModuleStatus(uint64_t file_id,int module_id,int status)2706 int TskImgDBSqlite::setModuleStatus(uint64_t file_id, int module_id, int status)
2707 {
2708     int rc = -1;
2709 
2710     if (!m_db)
2711         return rc;
2712 
2713     char * errmsg;
2714     std::stringstream stmt;
2715     stmt << "INSERT INTO module_status (file_id, module_id, status) VALUES (" <<
2716         file_id << ", " <<  module_id << ", " << status << ")";
2717 
2718     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) == SQLITE_OK) {
2719         rc = 0;
2720     } else {
2721         std::wstringstream infoMessage;
2722         infoMessage << L"TskImgDBSqlite::setModuleStatus - Error adding data to module_status table: " << errmsg;
2723         LOGERROR(infoMessage.str());
2724         sqlite3_free(errmsg);
2725     }
2726     return rc;
2727 }
2728 
2729 /**
2730  * Get a list of TskModuleStatus.
2731  * @param moduleInfoList A list of TskModuleStatus (output)
2732  * @returns 0 on success, -1 on error.
2733 */
getModuleInfo(std::vector<TskModuleInfo> & moduleInfoList) const2734 int TskImgDBSqlite::getModuleInfo(std::vector<TskModuleInfo> & moduleInfoList) const
2735 {
2736     int rc = -1;
2737 
2738     if (!m_db)
2739         return rc;
2740 
2741     stringstream stmt;
2742     stmt << "SELECT module_id, name, description FROM modules ORDER BY module_id";
2743 
2744     sqlite3_stmt * statement;
2745     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2746         TskModuleInfo moduleInfo;
2747         while (sqlite3_step(statement) == SQLITE_ROW) {
2748             moduleInfo.module_id = (int)sqlite3_column_int64(statement, 0);
2749             moduleInfo.module_name = (char *)sqlite3_column_text(statement, 1);
2750             moduleInfo.module_description = (char *)sqlite3_column_text(statement, 2);
2751             moduleInfoList.push_back(moduleInfo);
2752         }
2753         sqlite3_finalize(statement);
2754         rc = 0;
2755     } else {
2756         std::wstringstream infoMessage;
2757         infoMessage << L"TskImgDBSqlite::getModuleInfo - Error querying modules table: " << sqlite3_errmsg(m_db);
2758         LOGERROR(infoMessage.str());
2759     }
2760     return rc;
2761 }
2762 
2763 /**
2764  * Get a list of TskModuleStatus.
2765  * @param moduleStatusList A list of TskModuleStatus (output)
2766  * @returns 0 on success, -1 on error.
2767 */
getModuleErrors(std::vector<TskModuleStatus> & moduleStatusList) const2768 int TskImgDBSqlite::getModuleErrors(std::vector<TskModuleStatus> & moduleStatusList) const
2769 {
2770     int rc = -1;
2771 
2772     if (!m_db)
2773         return rc;
2774 
2775     stringstream stmt;
2776     stmt << "SELECT f.file_id, m.name, ms.status FROM module_status ms, files f, modules m"
2777         << " WHERE ms.status != 0 AND ms.file_id = f.file_id AND m.module_id = ms.module_id"
2778         << " ORDER BY f.file_id";
2779 
2780     sqlite3_stmt * statement;
2781     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2782         TskModuleStatus moduleStatus;
2783         while (sqlite3_step(statement) == SQLITE_ROW) {
2784             moduleStatus.file_id = (uint64_t)sqlite3_column_int64(statement, 0);
2785             moduleStatus.module_name = (char *)sqlite3_column_text(statement, 1);
2786             moduleStatus.status = (int)sqlite3_column_int(statement, 2);
2787             moduleStatusList.push_back(moduleStatus);
2788         }
2789         sqlite3_finalize(statement);
2790         rc = 0;
2791     } else {
2792         std::wstringstream infoMessage;
2793         infoMessage << L"TskImgDBSqlite::getModuleErrors - Error querying module_status table: " << sqlite3_errmsg(m_db);
2794         LOGERROR(infoMessage.str());
2795     }
2796     // Find report module errors. These have file_id = 0.
2797     stmt.str("");
2798     stmt << "SELECT 0, m.name, ms.status FROM module_status ms, modules m"
2799          << " WHERE ms.status != 0 AND ms.file_id = 0 AND m.module_id = ms.module_id";
2800 
2801     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2802         TskModuleStatus moduleStatus;
2803         while (sqlite3_step(statement) == SQLITE_ROW) {
2804             moduleStatus.file_id = (uint64_t)sqlite3_column_int64(statement, 0);
2805             moduleStatus.module_name = (char *)sqlite3_column_text(statement, 1);
2806             moduleStatus.status = (int)sqlite3_column_int(statement, 2);
2807             moduleStatusList.push_back(moduleStatus);
2808         }
2809         sqlite3_finalize(statement);
2810         rc = 0;
2811     } else {
2812         std::wstringstream infoMessage;
2813         infoMessage << L"TskImgDBSqlite::getModuleErrors - Error querying module_status table: " << sqlite3_errmsg(m_db);
2814         LOGERROR(infoMessage.str());
2815     }
2816     return rc;
2817 }
2818 
2819 /*
2820  * Return a file name associated with a file_id, prefer Cfilename, otherwise name in the files table.
2821  * @param file_id file id
2822  * @returns file name as std::string
2823  */
getFileName(uint64_t file_id) const2824 std::string TskImgDBSqlite::getFileName(uint64_t file_id) const
2825 {
2826     std::string name;
2827 
2828     if (!m_db)
2829         return name;
2830 
2831     name = getCfileName(file_id);
2832     if (name == "") {
2833         TskFileRecord fileRecord;
2834         if (getFileRecord(file_id, fileRecord) == 0)
2835             name = fileRecord.name;
2836     }
2837     return name;
2838 }
2839 
2840 
getKnownStatus(const uint64_t fileId) const2841 TskImgDB::KNOWN_STATUS TskImgDBSqlite::getKnownStatus(const uint64_t fileId) const
2842 {
2843     int retval = -1;
2844 
2845     if (!m_db)
2846         return (KNOWN_STATUS)retval;
2847 
2848     stringstream stmt;
2849     stmt << "SELECT known FROM file_hashes WHERE file_id = " << fileId;
2850 
2851     sqlite3_stmt * statement;
2852     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2853         if(sqlite3_step(statement) == SQLITE_ROW) {
2854             retval = (int)sqlite3_column_int(statement, 0);
2855         }
2856         sqlite3_finalize(statement);
2857     } else {
2858         std::wstringstream infoMessage;
2859         infoMessage << L"TskImgDBSqlite::getKnownStatus - Error getting known status " << sqlite3_errmsg(m_db);
2860         LOGERROR(infoMessage.str());
2861     }
2862 
2863     return (KNOWN_STATUS)retval;
2864 }
2865 
2866 
2867 /**
2868  * Add a new row to the unalloc_img_status table, returning the unalloc_img_id.
2869  * @param unallocImgId unalloc_img_id (output)
2870  * @returns -1 on error, 0 on success.
2871  */
addUnallocImg(int & unallocImgId)2872 int TskImgDBSqlite::addUnallocImg(int & unallocImgId)
2873 {
2874     int rc = -1;
2875 
2876     if (!m_db)
2877         return rc;
2878 
2879     std::stringstream stmt;
2880     stmt << "INSERT INTO unalloc_img_status (unalloc_img_id, status) VALUES (NULL, " << TskImgDB::IMGDB_UNALLOC_IMG_STATUS_CREATED << ")";
2881     char * errmsg;
2882     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) == SQLITE_OK) {
2883         unallocImgId = (int)sqlite3_last_insert_rowid(m_db);
2884         rc = 0;
2885     } else {
2886         std::wstringstream infoMessage;
2887         infoMessage << L"TskImgDBSqlite::addUnallocImg - Error adding unalloc_img_status table: " << errmsg;
2888         LOGERROR(infoMessage.str());
2889         sqlite3_free(errmsg);
2890     }
2891     return rc;
2892 }
2893 
2894 /**
2895  * Set the status in the unalloc_img_status table given the unalloc_img_id.
2896  * @param unallocImgId unalloc_img_id
2897  * @param status status of unalloc_img_id
2898  * @returns -1 on error, 0 on success.
2899  */
setUnallocImgStatus(int unallocImgId,TskImgDB::UNALLOC_IMG_STATUS status)2900 int TskImgDBSqlite::setUnallocImgStatus(int unallocImgId, TskImgDB::UNALLOC_IMG_STATUS status)
2901 {
2902     int rc = -1;
2903 
2904     if (!m_db)
2905         return rc;
2906 
2907     std::stringstream stmt;
2908     stmt << "UPDATE unalloc_img_status SET status = " << status << " WHERE unalloc_img_id = " << unallocImgId;
2909     char * errmsg;
2910     if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, &errmsg) == SQLITE_OK) {
2911         rc = 0;
2912     } else {
2913         std::wstringstream infoMessage;
2914         infoMessage << L"TskImgDBSqlite::addUnallocImg - Error adding unalloc_img_status table: " << errmsg;
2915         LOGERROR(infoMessage.str());
2916         sqlite3_free(errmsg);
2917     }
2918     return rc;
2919 }
2920 
2921 /**
2922  * Get the status of the unalloc_img_status table given the unalloc_img_id.
2923  * Can throws TskException.
2924  * @param unallocImgId unalloc_img_id
2925  * @returns TskImgDB::UNALLOC_IMG_STATUS
2926  */
getUnallocImgStatus(int unallocImgId) const2927 TskImgDB::UNALLOC_IMG_STATUS TskImgDBSqlite::getUnallocImgStatus(int unallocImgId) const
2928 {
2929     if (!m_db)
2930         throw TskException("Database not initialized.");
2931 
2932     int status = 0;
2933     stringstream stmt;
2934     stmt << "SELECT status FROM unalloc_img_status WHERE unalloc_img_id = " << unallocImgId;
2935 
2936     sqlite3_stmt * statement;
2937     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2938         if (sqlite3_step(statement) == SQLITE_ROW) {
2939             status = (int)sqlite3_column_int(statement, 0);
2940         }
2941         sqlite3_finalize(statement);
2942     } else {
2943         std::wstringstream infoMessage;
2944         infoMessage << L"TskImgDBSqlite::getUnallocImgStatus - Error getting unalloc_img_status: " << sqlite3_errmsg(m_db);
2945         LOGERROR(infoMessage.str());
2946     }
2947     return (TskImgDB::UNALLOC_IMG_STATUS)status;
2948 }
2949 
2950 /**
2951  * Get all the unalloc_img_status table.
2952  * @param unallocImgStatusList A vector of TskUnallocImgStatusRecord (output)
2953  * @returns -1 on error, 0 on success.
2954  */
getAllUnallocImgStatus(std::vector<TskUnallocImgStatusRecord> & unallocImgStatusList) const2955 int TskImgDBSqlite::getAllUnallocImgStatus(std::vector<TskUnallocImgStatusRecord> & unallocImgStatusList) const
2956 {
2957     int rc = -1;
2958     unallocImgStatusList.clear();
2959 
2960     if (!m_db)
2961         return rc;
2962 
2963     stringstream stmt;
2964     stmt << "SELECT unalloc_img_id, status FROM unalloc_img_status";
2965 
2966     sqlite3_stmt * statement;
2967     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
2968         while (sqlite3_step(statement) == SQLITE_ROW) {
2969             TskUnallocImgStatusRecord record;
2970             record.unallocImgId = (int)sqlite3_column_int(statement, 0);
2971             record.status = (TskImgDB::UNALLOC_IMG_STATUS)sqlite3_column_int(statement, 1);
2972             unallocImgStatusList.push_back(record);
2973         }
2974         rc = 0;
2975         sqlite3_finalize(statement);
2976     } else {
2977         std::wstringstream infoMessage;
2978         infoMessage << L"TskImgDBSqlite::getAllUnallocImgStatus - Error getting unalloc_img_status: " << sqlite3_errmsg(m_db);
2979         LOGERROR(infoMessage.str());
2980     }
2981     return rc;
2982 }
2983 
2984 /**
2985  * Find and add all the unused sectors (unallocated and uncarved bytes) in the given unallocImgId
2986  * @param unallocImgId The unalloc image id.
2987  * @param unusedSectorsList A vector of TskUnusedSectorsRecord
2988  * @returns -1 on error, 0 on success.
2989  */
addUnusedSectors(int unallocImgId,std::vector<TskUnusedSectorsRecord> & unusedSectorsList)2990 int TskImgDBSqlite::addUnusedSectors(int unallocImgId, std::vector<TskUnusedSectorsRecord> & unusedSectorsList)
2991 {
2992     assert(unallocImgId > 0);
2993     int rc = -1;
2994     if (!m_db)
2995         return rc;
2996 
2997     std::stringstream stmt;
2998     stmt << "SELECT vol_id, unalloc_img_sect_start, sect_len, orig_img_sect_start FROM alloc_unalloc_map "
2999         "WHERE unalloc_img_id = " << unallocImgId << " ORDER BY orig_img_sect_start ASC";
3000 
3001     sqlite3_stmt * statement;
3002     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3003         vector<TskAllocUnallocMapRecord> allocUnallocMapList;
3004 
3005         while (sqlite3_step(statement) == SQLITE_ROW) {
3006             TskAllocUnallocMapRecord record;
3007             record.vol_id = (int)sqlite3_column_int(statement, 0);
3008             record.unalloc_img_id = unallocImgId;
3009             record.unalloc_img_sect_start = (uint64_t)sqlite3_column_int64(statement, 1);
3010             record.sect_len = (uint64_t)sqlite3_column_int64(statement, 2);
3011             record.orig_img_sect_start = (uint64_t)sqlite3_column_int64(statement, 3);
3012             allocUnallocMapList.push_back(record);
3013         }
3014         sqlite3_finalize(statement);
3015 
3016         for (std::vector<TskAllocUnallocMapRecord>::const_iterator it = allocUnallocMapList.begin();
3017              it != allocUnallocMapList.end(); it++)
3018         {
3019             // Sector position tracks our position through the unallocated map record.
3020             uint64_t sectPos = it->orig_img_sect_start;
3021 
3022             uint64_t endSect = it->orig_img_sect_start + it->sect_len;
3023 
3024             // Retrieve all carved_sector records in range for this section of unallocated space.
3025             stmt.str("");
3026             stmt << "SELECT cs.sect_start, cs.sect_len FROM carved_files cf, carved_sectors cs"
3027                 << " WHERE cf.file_id = cs.file_id AND cs.sect_start >= " << it->orig_img_sect_start
3028                 << " AND cs.sect_start < " << endSect << " ORDER BY cs.sect_start ASC";
3029 
3030             if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3031                 while (sqlite3_step(statement) == SQLITE_ROW) {
3032                     uint64_t cfileSectStart = (uint64_t)sqlite3_column_int64(statement, 0);
3033                     uint64_t cfileSectLen = (uint64_t)sqlite3_column_int64(statement, 1);
3034                     if (cfileSectStart > sectPos)
3035                     {
3036                         // We have a block of unused sectors between this position in the unallocated map
3037                         // and the start of the carved file.
3038                         addUnusedSector(sectPos, cfileSectStart, it->vol_id, unusedSectorsList);
3039                     }
3040 
3041                     sectPos = cfileSectStart + cfileSectLen;
3042                 }
3043 
3044                 // Handle case where there is slack at the end of the unalloc range
3045                 if (sectPos < endSect)
3046                     addUnusedSector(sectPos, endSect, it->vol_id, unusedSectorsList);
3047 
3048                 sqlite3_finalize(statement);
3049             } else {
3050                 std::wstringstream infoMessage;
3051                 infoMessage << L"TskImgDBSqlite::addUnusedSectors - Error querying carved_files, carved_sectors table: " << sqlite3_errmsg(m_db);
3052                 LOGERROR(infoMessage.str());
3053             }
3054         }
3055     } else {
3056         std::wstringstream infoMessage;
3057         infoMessage << L"TskImgDBSqlite::addUnusedSectors - Error querying alloc_unalloc_map table: " << sqlite3_errmsg(m_db);
3058         LOGERROR(infoMessage.str());
3059     }
3060     return 0;
3061 }
3062 
3063 /**
3064  * Add one unused sector to the database, add it to the files and unused_sectors tables.
3065  * @param sectStart Unused sector start.
3066  * @param sectEnd Unused sector end.
3067  * @param volId Volume Id of the unused sector.
3068  * @param unusedSectorsList A vector of TskUnusedSectorsRecord (output)
3069  * @returns -1 on error, 0 on success.
3070  */
addUnusedSector(uint64_t sectStart,uint64_t sectEnd,int volId,std::vector<TskUnusedSectorsRecord> & unusedSectorsList)3071 int TskImgDBSqlite::addUnusedSector(uint64_t sectStart, uint64_t sectEnd, int volId, std::vector<TskUnusedSectorsRecord> & unusedSectorsList)
3072 {
3073     assert(sectEnd > sectStart);
3074     int rc = -1;
3075     if (!m_db)
3076         return rc;
3077 
3078     std::stringstream stmt;
3079 
3080     std::string maxUnused = GetSystemProperty("MAX_UNUSED_FILE_SIZE_BYTES");
3081     const uint64_t maxUnusedFileSizeBytes = maxUnused.empty() ? (50 * 1024 * 1024) : Poco::NumberParser::parse64(maxUnused);
3082 
3083     uint64_t maxUnusedSectorSize = maxUnusedFileSizeBytes / 512;
3084     uint64_t sectorIndex = 0;
3085     uint64_t sectorCount = (sectEnd - sectStart) / maxUnusedSectorSize;
3086 
3087     while (sectorIndex <= sectorCount) {
3088         uint64_t thisSectStart = sectStart + (sectorIndex * maxUnusedSectorSize);
3089         uint64_t thisSectEnd = thisSectStart + (std::min)(maxUnusedSectorSize, sectEnd - thisSectStart);
3090 
3091         stmt.str("");
3092         stmt << "INSERT INTO files (file_id, type_id, name, par_file_id, dir_type, meta_type,"
3093             "dir_flags, meta_flags, size, ctime, crtime, atime, mtime, mode, uid, gid, status, full_path) "
3094             "VALUES (NULL, " << IMGDB_FILES_TYPE_UNUSED << ", " << "'ufile'"
3095             << ", NULL, " <<  TSK_FS_NAME_TYPE_REG << ", " <<  TSK_FS_META_TYPE_REG << ", "
3096             << TSK_FS_NAME_FLAG_UNALLOC << ", " << TSK_FS_META_FLAG_UNALLOC << ", "
3097             << (thisSectEnd - thisSectStart) * 512 << ", NULL, NULL, NULL, NULL, NULL, NULL, NULL, " << IMGDB_FILES_STATUS_READY_FOR_ANALYSIS << "," << "'ufile'" << ")";
3098 
3099         if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, NULL) == SQLITE_OK) {
3100 
3101             TskUnusedSectorsRecord record;
3102 
3103             // get the file_id from the last insert
3104             record.fileId = sqlite3_last_insert_rowid(m_db);
3105             record.sectStart = thisSectStart;
3106             record.sectLen = thisSectEnd - thisSectStart;
3107 
3108             std::stringstream name;
3109             name << "ufile_" << thisSectStart << "_" << thisSectEnd << "_" << record.fileId;
3110             stmt.str("");
3111             char *item;
3112             item = sqlite3_mprintf("%Q", name.str().c_str());
3113             stmt << "UPDATE files SET name = " << item << ", full_path = "
3114                 << item << " WHERE file_id = " << record.fileId;
3115             sqlite3_free(item);
3116 
3117             if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, NULL) != SQLITE_OK) {
3118                 std::wstringstream infoMessage;
3119                 infoMessage << L"TskImgDBSqlite::addUnusedSector - Error update into files table: " << sqlite3_errmsg(m_db);
3120                 LOGERROR(infoMessage.str());
3121                 rc = -1;
3122                 break;
3123             }
3124 
3125             stmt.str("");
3126             stmt << "INSERT INTO unused_sectors (file_id, sect_start, sect_len, vol_id) VALUES ("
3127                  << record.fileId << ", " << record.sectStart << ", " << record.sectLen << ", " << volId << ")";
3128 
3129             if (sqlite3_exec(m_db, stmt.str().c_str(), NULL, NULL, NULL) != SQLITE_OK) {
3130                 std::wstringstream infoMessage;
3131                 infoMessage << L"TskImgDBSqlite::addUnusedSector - Error insert into unused_sectors table: " << sqlite3_errmsg(m_db);
3132                 LOGERROR(infoMessage.str());
3133                 rc = -1;
3134                 break;
3135             }
3136 
3137             unusedSectorsList.push_back(record);
3138             rc = 0;
3139 
3140         } else {
3141             std::wstringstream infoMessage;
3142             infoMessage << L"TskImgDBSqlite::addUnusedSector - Error insert into files table: " << sqlite3_errmsg(m_db);
3143             LOGERROR(infoMessage.str());
3144             rc = -1;
3145             break;
3146         }
3147         sectorIndex++;
3148     } // while
3149     return rc;
3150 }
3151 
3152 /**
3153  * Get unused sector record given a file id.
3154  * @param fileId File id of the unused sector.
3155  * @param unusedSectorsRecord TskUnusedSectorsRecord (output)
3156  * @returns -1 on error, 0 on success.
3157  */
getUnusedSector(uint64_t fileId,TskUnusedSectorsRecord & unusedSectorsRecord) const3158 int TskImgDBSqlite::getUnusedSector(uint64_t fileId, TskUnusedSectorsRecord & unusedSectorsRecord) const
3159 {
3160     int rc = -1;
3161     if (!m_db)
3162         return rc;
3163 
3164     std::stringstream stmt;
3165     stmt << "SELECT sect_start, sect_len FROM unused_sectors WHERE file_id = " << fileId;
3166 
3167     sqlite3_stmt * statement;
3168     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3169         if (sqlite3_step(statement) == SQLITE_ROW) {
3170             unusedSectorsRecord.fileId = fileId;
3171             unusedSectorsRecord.sectStart = (uint64_t)sqlite3_column_int64(statement, 0);
3172             unusedSectorsRecord.sectLen = (uint64_t)sqlite3_column_int64(statement, 1);
3173             rc = 0;
3174         } else {
3175             std::wstringstream msg;
3176             msg << L"TskDBSqlite::getUnusedSector - Error querying unused_sectors table for file_id "
3177                 << fileId ;
3178             LOGERROR(msg.str());
3179         }
3180     } else {
3181         std::wstringstream msg;
3182         msg << L"TskDBSqlite::getUnusedSector - Error querying unused_sectors table: "
3183             << sqlite3_errmsg(m_db) ;
3184         LOGERROR(msg.str());
3185     }
3186     return rc;
3187 }
3188 
3189 ///BLACKBOARD FUNCTIONS
3190 /**
3191  * Add the given blackboard attribute to the database
3192  * @param attr input attribute. should be fully populated
3193  */
addBlackboardAttribute(TskBlackboardAttribute attr)3194 void TskImgDBSqlite::addBlackboardAttribute(TskBlackboardAttribute attr)
3195 {
3196     if (!m_db)
3197         throw TskException("No database.");
3198 
3199     std::stringstream str;
3200     char *item;
3201     sqlite3_stmt * statement;
3202 
3203     str << "INSERT INTO blackboard_attributes (artifact_id, source, context, attribute_type_id, value_type, "
3204         "value_byte, value_text, value_int32, value_int64, value_double, obj_id) VALUES (";
3205         str << attr.getArtifactID() << ", ";
3206     item = sqlite3_mprintf("%Q", attr.getModuleName().c_str()); str << item << ", ";
3207     sqlite3_free(item);
3208     item = sqlite3_mprintf("%Q", attr.getContext().c_str()); str << item << ", ";
3209     sqlite3_free(item);
3210     str << attr.getAttributeTypeID() << ", ";
3211     str << attr.getValueType() << ", ";
3212     switch (attr.getValueType()) {
3213         case TSK_BYTE:
3214             str << " ?, '', 0, 0, 0.0";
3215             break;
3216         case TSK_STRING:
3217             item = sqlite3_mprintf("%Q", attr.getValueString().c_str());
3218             str << " '', " << item << ", 0, 0, 0.0";
3219             sqlite3_free(item);
3220             break;
3221         case TSK_INTEGER:
3222             str << " '', '', " << attr.getValueInt() << ",     0, 0.0";
3223             break;
3224         case TSK_LONG:
3225             str << " '', '', 0, " << attr.getValueLong() << ",     0.0";
3226             break;
3227         case TSK_DOUBLE:
3228             str << " '', '', 0, 0, " << setprecision(20) << attr.getValueDouble();
3229             break;
3230     };
3231     str << ", " << attr.getObjectID();
3232     str << ")";
3233 
3234     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3235         int result = SQLITE_OK;
3236         unsigned char *pBuf = 0;
3237         if (attr.getValueType() == TSK_BYTE) {
3238             // Bind the byte vector
3239             int a_size = attr.getValueBytes().size();
3240             pBuf = new unsigned char[a_size];
3241             for (int i = 0; i < a_size; i++) {
3242                 pBuf[i] = attr.getValueBytes()[i];
3243             }
3244             result = sqlite3_bind_blob(statement, 1, pBuf, a_size, SQLITE_STATIC);
3245         }
3246         if (result == SQLITE_OK) {
3247             result = sqlite3_step(statement);
3248             if (!(result == SQLITE_ROW || result == SQLITE_DONE)) {
3249                 sqlite3_finalize(statement);
3250                 if (pBuf) delete [] pBuf;
3251                 throw TskException("TskImgDBSqlite::addBlackboardAttribute - Insert failed");
3252             }
3253         } else {
3254             std::wstringstream infoMessage;
3255             infoMessage << L"TskImgDBSqlite::addBlackboardAttribute - Error in sqlite3_bind_blob: " << sqlite3_errmsg(m_db);
3256             LOGERROR(infoMessage.str());
3257             throw TskException("TskImgDBSqlite::addBlackboardAttribute - Insert failed");
3258         }
3259         sqlite3_finalize(statement);
3260         if (pBuf) delete [] pBuf;
3261     } else {
3262         std::wstringstream infoMessage;
3263         infoMessage << L"TskImgDBSqlite::addBlackboardAttribute - Error adding data to blackboard table: " << sqlite3_errmsg(m_db);
3264         LOGERROR(infoMessage.str());
3265         throw TskException("TskImgDBSqlite::addBlackboardAttribute - Insert failed");
3266     }
3267 }
3268 
3269 /**
3270  * Get the display name for the given artifact type id
3271  * @param artifactTypeID artifact type id
3272  * @returns display name
3273  */
getArtifactTypeDisplayName(int artifactTypeID)3274 string TskImgDBSqlite::getArtifactTypeDisplayName(int artifactTypeID){
3275     if (!m_db)
3276         throw TskException("No database.");
3277 
3278     std::stringstream str;
3279     sqlite3_stmt * statement;
3280     std::string displayName = "";
3281 
3282     str << "SELECT display_name FROM blackboard_artifact_types WHERE artifact_type_id = " << artifactTypeID;
3283 
3284     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK){
3285         int result = sqlite3_step(statement);
3286         if (result == SQLITE_ROW) {
3287             displayName = (char *)sqlite3_column_text(statement, 0);
3288         }
3289         else{
3290             std::wstringstream infoMessage;
3291             infoMessage << L"TskImgDBSqlite::getArtifactTypeDisplayName: " << sqlite3_errmsg(m_db);
3292             LOGERROR(infoMessage.str());
3293             throw TskException("TskImgDBSqlite::getArtifactTypeDisplayName - No artifact type with that ID");
3294         }
3295         sqlite3_finalize(statement);
3296         return displayName;
3297     }
3298     else{
3299         std::wstringstream infoMessage;
3300         infoMessage << L"TskImgDBSqlite::getArtifactTypeDisplayName: " << sqlite3_errmsg(m_db);
3301         LOGERROR(infoMessage.str());
3302         throw TskException("TskImgDBSqlite::getArtifactTypeDisplayName - Select failed");
3303     }
3304 }
3305 
3306 /**
3307  * Get the artifact type id for the given artifact type string
3308  * @param artifactTypeString display name
3309  * @returns artifact type id
3310  */
getArtifactTypeID(string artifactTypeString)3311 int TskImgDBSqlite::getArtifactTypeID(string artifactTypeString){
3312     if (!m_db)
3313         throw TskException("No database.");
3314 
3315     std::stringstream str;
3316     sqlite3_stmt * statement;
3317     int typeID;
3318 
3319     str << "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = " << artifactTypeString;
3320 
3321     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK){
3322         int result = sqlite3_step(statement);
3323         if (result == SQLITE_ROW) {
3324             typeID = (int) sqlite3_column_int(statement, 0);
3325         }
3326         else{
3327             std::wstringstream infoMessage;
3328             infoMessage << L"TskImgDBSqlite::getArtifactTypeID: " << sqlite3_errmsg(m_db);
3329             LOGERROR(infoMessage.str());
3330             throw TskException("TskImgDBSqlite::getArtifactTypeID - No artifact type with that name");
3331         }
3332         sqlite3_finalize(statement);
3333         return typeID;
3334     }
3335     else{
3336         std::wstringstream infoMessage;
3337         infoMessage << L"TskImgDBSqlite::getArtifactTypeID: " << sqlite3_errmsg(m_db);
3338         LOGERROR(infoMessage.str());
3339         throw TskException("TskImgDBSqlite::getArtifactTypeID - Select failed");
3340     }
3341 }
3342 
3343 /**
3344  * Get the artifact type name for the given artifact type id
3345  * @param artifactTypeID id
3346  * @returns artifact type name
3347  */
getArtifactTypeName(int artifactTypeID)3348 string TskImgDBSqlite::getArtifactTypeName(int artifactTypeID){
3349     if (!m_db)
3350         throw TskException("No database.");
3351 
3352     std::stringstream str;
3353     sqlite3_stmt * statement;
3354     std::string typeName = "";
3355 
3356     str << "SELECT type_name FROM blackboard_artifact_types WHERE artifact_type_id = " << artifactTypeID;
3357 
3358     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK){
3359         int result = sqlite3_step(statement);
3360         if (result == SQLITE_ROW) {
3361             typeName = (char *)sqlite3_column_text(statement, 0);
3362         }
3363         else{
3364             std::wstringstream infoMessage;
3365             infoMessage << L"TskImgDBSqlite::getArtifactTypeName: " << sqlite3_errmsg(m_db);
3366             LOGERROR(infoMessage.str());
3367             throw TskException("TskImgDBSqlite::getArtifactTypeName - No artifact type with that ID");
3368         }
3369         sqlite3_finalize(statement);
3370         return typeName;
3371     }
3372     else{
3373         std::wstringstream infoMessage;
3374         infoMessage << L"TskImgDBSqlite::getArtifactTypeName: " << sqlite3_errmsg(m_db);
3375         LOGERROR(infoMessage.str());
3376         throw TskException("TskImgDBSqlite::getArtifactTypeName - Select failed");
3377     }
3378 }
3379 
3380 /**
3381  * Get the display name for the given attribute type id
3382  * @param attributeTypeID attribute type id
3383  * @returns display name
3384  */
getAttributeTypeDisplayName(int attributeTypeID)3385 string TskImgDBSqlite::getAttributeTypeDisplayName(int attributeTypeID){
3386     if (!m_db)
3387         throw TskException("No database.");
3388 
3389     std::stringstream str;
3390     sqlite3_stmt * statement;
3391     std::string displayName = "";
3392 
3393     str << "SELECT display_name FROM blackboard_attribute_types WHERE attribute_type_id = " << attributeTypeID;
3394 
3395     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK){
3396         int result = sqlite3_step(statement);
3397         if (result == SQLITE_ROW) {
3398             displayName = (char *)sqlite3_column_text(statement, 0);
3399         }
3400         else{
3401             std::wstringstream infoMessage;
3402             infoMessage << L"TskImgDBSqlite::getAttributeTypeDisplayName: " << sqlite3_errmsg(m_db);
3403             LOGERROR(infoMessage.str());
3404             throw TskException("TskImgDBSqlite::getAttributeTypeDisplayName - No attribute type with that ID");
3405         }
3406         sqlite3_finalize(statement);
3407         return displayName;
3408     }
3409     else{
3410         std::wstringstream infoMessage;
3411         infoMessage << L"TskImgDBSqlite::getAttributeTypeDisplayName: " << sqlite3_errmsg(m_db);
3412         LOGERROR(infoMessage.str());
3413         throw TskException("TskImgDBSqlite::getAttributeTypeDisplayName - Select failed");
3414     }
3415 }
3416 
3417 /**
3418  * Get the attribute type id for the given artifact type string
3419  * @param attributeTypeString display name
3420  * @returns attribute type id
3421  */
getAttributeTypeID(string attributeTypeString)3422 int TskImgDBSqlite::getAttributeTypeID(string attributeTypeString){
3423     if (!m_db)
3424         throw TskException("No database.");
3425 
3426     std::stringstream str;
3427     sqlite3_stmt * statement;
3428     int typeID;
3429 
3430     str << "SELECT attribute_type_id FROM blackboard_attribute_types WHERE type_name = " << attributeTypeString;
3431 
3432     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK){
3433         int result = sqlite3_step(statement);
3434         if (result == SQLITE_ROW) {
3435             typeID = (int) sqlite3_column_int(statement, 0);
3436         }
3437         else{
3438             std::wstringstream infoMessage;
3439             infoMessage << L"TskImgDBSqlite::getAttributeTypeID: " << sqlite3_errmsg(m_db);
3440             LOGERROR(infoMessage.str());
3441             throw TskException("TskImgDBSqlite::getAttributeTypeID - No artifact type with that name");
3442         }
3443         sqlite3_finalize(statement);
3444         return typeID;
3445     }
3446     else{
3447         std::wstringstream infoMessage;
3448         infoMessage << L"TskImgDBSqlite::getAttributeTypeID: " << sqlite3_errmsg(m_db);
3449         LOGERROR(infoMessage.str());
3450         throw TskException("TskImgDBSqlite::getAttributeTypeID - Select failed");
3451     }
3452 }
3453 
3454 /**
3455  * Get the attribute type name for the given artifact type id
3456  * @param attributeTypeID id
3457  * @returns attribute type name
3458  */
getAttributeTypeName(int attributeTypeID)3459 string TskImgDBSqlite::getAttributeTypeName(int attributeTypeID){
3460     if (!m_db)
3461         throw TskException("No database.");
3462 
3463     std::stringstream str;
3464     sqlite3_stmt * statement;
3465     std::string typeName = "";
3466 
3467     str << "SELECT type_name FROM blackboard_attribute_types WHERE attribute_type_id = " << attributeTypeID;
3468 
3469     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK){
3470         int result = sqlite3_step(statement);
3471         if (result == SQLITE_ROW) {
3472             typeName = (char *)sqlite3_column_text(statement, 0);
3473         }
3474         else{
3475             std::wstringstream infoMessage;
3476             infoMessage << L"TskImgDBSqlite::getAttributeTypeName: " << sqlite3_errmsg(m_db);
3477             LOGERROR(infoMessage.str());
3478             throw TskException("TskImgDBSqlite::getAttributeTypeName - No attribute type with that ID");
3479         }
3480         sqlite3_finalize(statement);
3481         return typeName;
3482     }
3483     else{
3484         std::wstringstream infoMessage;
3485         infoMessage << L"TskImgDBSqlite::getAttributeTypeName: " << sqlite3_errmsg(m_db);
3486         LOGERROR(infoMessage.str());
3487         throw TskException("TskImgDBSqlite::getAttributeTypeName - Select failed");
3488     }
3489 }
3490 
3491 /**
3492  * Get all artifacts by performing a SQL Select statement with the given where clause.
3493  * @param condition The SQL select where clause that should be used in the query.
3494  * @returns vector of matching artifacts
3495  */
getMatchingArtifacts(string condition)3496 vector<TskBlackboardArtifact> TskImgDBSqlite::getMatchingArtifacts(string condition)
3497 {
3498     if (!m_db)
3499         throw TskException("No database.");
3500 
3501     vector<TskBlackboardArtifact> artifacts;
3502     std::string stmt("SELECT blackboard_artifacts.artifact_id, blackboard_artifacts.obj_id, blackboard_artifacts.artifact_type_id FROM blackboard_artifacts");
3503 
3504     constructStmt(stmt, condition);
3505 
3506     sqlite3_stmt * statement;
3507     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK)
3508     {
3509         while (sqlite3_step(statement) == SQLITE_ROW)
3510         {
3511             int artifactTypeID = sqlite3_column_int(statement, 2);
3512 
3513             artifacts.push_back(TskImgDB::createArtifact(sqlite3_column_int64(statement, 0), sqlite3_column_int64(statement, 1), artifactTypeID));
3514         }
3515         sqlite3_finalize(statement);
3516     } else
3517     {
3518         std::wstringstream msg;
3519         msg << L"TskImgDBSqlite::getMatchingArtifacts - Error getting artifacts: " << sqlite3_errmsg(m_db);
3520         LOGERROR(msg.str());
3521         throw TskException("TskImgDBSqlite::getMatchingArtifacts - Select failed");
3522     }
3523     return artifacts;
3524 }
3525 
3526 /**
3527  * Get all attributes with that match the given where clause
3528  * @param condition where clause to use for matching
3529  * @returns vector of matching attributes
3530  */
getMatchingAttributes(string condition)3531 vector<TskBlackboardAttribute> TskImgDBSqlite::getMatchingAttributes(string condition)
3532 {
3533     if (!m_db)
3534         throw TskException("No database.");
3535 
3536     vector<TskBlackboardAttribute> attributes;
3537     std::string stmt("SELECT blackboard_attributes.artifact_id, blackboard_attributes.source, blackboard_attributes.context, blackboard_attributes.attribute_type_id, blackboard_attributes.value_type, blackboard_attributes.value_byte, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double, blackboard_attributes.obj_id FROM blackboard_attributes ");
3538 
3539     constructStmt(stmt, condition);
3540 
3541     sqlite3_stmt * statement;
3542     if (sqlite3_prepare_v2(m_db, stmt.c_str(), -1, &statement, 0) == SQLITE_OK)
3543     {
3544         while (sqlite3_step(statement) == SQLITE_ROW)
3545         {
3546             int blobSize = sqlite3_column_bytes(statement, 6);
3547             const unsigned char *pBlob = (const unsigned char *)sqlite3_column_blob(statement, 6);
3548             vector<unsigned char> bytes;
3549             bytes.reserve(blobSize);
3550             for (int i = 0; i < blobSize; i++) {
3551                 bytes.push_back((unsigned char)pBlob[i]);
3552             }
3553 
3554             attributes.push_back(TskImgDB::createAttribute(sqlite3_column_int64(statement, 0),sqlite3_column_int(statement, 3), sqlite3_column_int64(statement, 10), std::string((char *)sqlite3_column_text(statement, 1)),
3555                 std::string((char *)sqlite3_column_text(statement, 2)), (TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE) sqlite3_column_int(statement, 4), sqlite3_column_int(statement, 7),
3556                 sqlite3_column_int64(statement, 8), sqlite3_column_double(statement, 9), std::string((char *)sqlite3_column_text(statement, 6)), bytes));
3557         }
3558         sqlite3_finalize(statement);
3559     } else {
3560         std::wstringstream msg;
3561         msg << L"TskImgDBSqlite::getMatchingAttributes - Error getting attributes: " << sqlite3_errmsg(m_db);
3562         LOGERROR(msg.str());
3563         throw TskException("TskImgDBSqlite::getMatchingAttributes - Select failed");
3564     }
3565     return attributes;
3566 }
3567 
3568 /**
3569  * Create a new blackboard artifact with the given type id and file id
3570  * @param artifactTypeID artifact type id
3571  * @param file_id associated file id
3572  * @returns the new artifact
3573  */
createBlackboardArtifact(uint64_t file_id,int artifactTypeID)3574 TskBlackboardArtifact TskImgDBSqlite::createBlackboardArtifact(uint64_t file_id, int artifactTypeID)
3575 {
3576     if (!m_db)
3577         throw TskException("No database.");
3578 
3579     uint64_t artifactId = 0;
3580     std::stringstream str;
3581     sqlite3_stmt * statement;
3582 
3583     str << "INSERT INTO blackboard_artifacts (artifact_id, obj_id, artifact_type_id) VALUES (NULL, " << file_id << ", " << artifactTypeID << ")";
3584 
3585     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3586         if (!(sqlite3_step(statement) == SQLITE_DONE)) {
3587             sqlite3_finalize(statement);
3588             throw TskException("TskImgDBSqlite::addBlackboardInfo - Insert failed");
3589         }
3590         // select max(artifact_id) from blackboard
3591         str.str("");
3592         str << "SELECT artifact_id from blackboard_artifacts WHERE obj_id = " << file_id << " AND artifact_type_id = " << artifactTypeID;
3593         sqlite3_finalize(statement);
3594         if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3595             while(sqlite3_step(statement) == SQLITE_ROW) {
3596                 uint64_t newID = sqlite3_column_int64(statement, 0);
3597                 if(newID > artifactId)
3598                     artifactId = newID;
3599             }
3600         } else {
3601             sqlite3_finalize(statement);
3602             throw TskException("TskImgDBSqlite::newBlackboardArtifact - Select artifact_id failed");
3603         }
3604         sqlite3_finalize(statement);
3605     } else {
3606         std::wstringstream infoMessage;
3607         infoMessage << L"TskImgDBSqlite::newBlackboardArtifact - Error adding new artifact: " << sqlite3_errmsg(m_db);
3608         LOGERROR(infoMessage.str());
3609         throw TskException("TskImgDBSqlite::newBlackboardArtifact - Insert failed");
3610     }
3611 
3612     return TskImgDB::createArtifact(artifactId, file_id, artifactTypeID);
3613 }
3614 
3615 /**
3616  * Add a new artifact type with the given name, display name and id
3617  * @param artifactTypeName type name
3618  * @param displayName display name
3619  * @param typeID type id
3620  */
addArtifactType(int typeID,string artifactTypeName,string displayName)3621 void TskImgDBSqlite::addArtifactType(int typeID, string artifactTypeName, string displayName)
3622 {
3623     if (!m_db)
3624         throw TskException("No database.");
3625 
3626     std::stringstream str;
3627     sqlite3_stmt * statement;
3628 
3629     str << "SELECT * FROM blackboard_artifact_types WHERE type_name = '" << artifactTypeName << "'";
3630 
3631     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3632         if (!(sqlite3_step(statement) == SQLITE_ROW)) {
3633             sqlite3_finalize(statement);
3634             str.str("");
3635             str << "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name) VALUES (" << typeID << " , '" << artifactTypeName << "', '" << displayName << "')";
3636             if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3637                 if (!(sqlite3_step(statement) == SQLITE_DONE)) {
3638                     sqlite3_finalize(statement);
3639                     std::wstringstream infoMessage;
3640                     infoMessage << L"TskImgDBSqlite::addArtifactType - Error adding data to blackboard table: " << sqlite3_errmsg(m_db);
3641                     LOGERROR(infoMessage.str());
3642                     throw TskException("TskImgDBSqlite::addArtifactType - Artifact type insert failed");
3643                 }
3644             }
3645         }
3646         else{
3647             sqlite3_finalize(statement);
3648             throw TskException("TskImgDBSqlite::addArtifactType - Artifact type with that name already exists");
3649         }
3650         sqlite3_finalize(statement);
3651     } else {
3652         std::wstringstream infoMessage;
3653         infoMessage << L"TskImgDBSqlite::addArtifactType - Error adding data to blackboard table: " << sqlite3_errmsg(m_db);
3654         LOGERROR(infoMessage.str());
3655         throw TskException("TskImgDBSqlite::addArtifactType - Insert failed");
3656     }
3657 }
3658 
3659 /**
3660  * Add a new attribute type with the given name, display name and id
3661  * @param attributeTypeName type name
3662  * @param displayName display name
3663  * @param typeID type id
3664  */
addAttributeType(int typeID,string attributeTypeName,string displayName)3665 void TskImgDBSqlite::addAttributeType(int typeID, string attributeTypeName, string displayName)
3666 {
3667     if (!m_db)
3668         throw TskException("No database.");
3669 
3670     std::stringstream str;
3671     sqlite3_stmt * statement;
3672 
3673     str << "SELECT * FROM blackboard_attribute_types WHERE type_name = '" << attributeTypeName << "'";
3674 
3675     if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3676         if (!(sqlite3_step(statement) == SQLITE_ROW)) {
3677             sqlite3_finalize(statement);
3678             str.str("");
3679             str << "INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name) VALUES (" << typeID << " , '" << attributeTypeName << "', '" << displayName << "')";
3680             if (sqlite3_prepare_v2(m_db, str.str().c_str(), -1, &statement, 0) == SQLITE_OK) {
3681                 if (!(sqlite3_step(statement) == SQLITE_DONE)) {
3682                     sqlite3_finalize(statement);
3683                     std::wstringstream infoMessage;
3684                     infoMessage << L"TskImgDBSqlite::addAttributeType - Error adding data to blackboard table: " << sqlite3_errmsg(m_db);
3685                     LOGERROR(infoMessage.str());
3686                     throw TskException("TskImgDBSqlite::addAttributeType - Attribute type insert failed");
3687                 }
3688             }
3689         } else {
3690             sqlite3_finalize(statement);
3691             throw TskException("TskImgDBSqlite::addAttributeType - Attribute type with that name already exists");
3692         }
3693         sqlite3_finalize(statement);
3694     } else {
3695         std::wstringstream infoMessage;
3696         infoMessage << L"TskImgDBSqlite::addAttributeType - Error adding data to blackboard table: " << sqlite3_errmsg(m_db);
3697         LOGERROR(infoMessage.str());
3698         throw TskException("TskImgDBSqlite::addAttributeType - Insert failed");
3699     }
3700 }
3701 
3702 /**
3703  * Get all artifacts with the given type id, type name, and file id
3704  * @param artifactTypeID type id
3705  * @param artifactTypeName type name
3706  * @param file_id file id
3707  */
getArtifactsHelper(uint64_t file_id,int artifactTypeID,string artifactTypeName)3708 vector<TskBlackboardArtifact> TskImgDBSqlite::getArtifactsHelper(uint64_t file_id, int artifactTypeID, string artifactTypeName)
3709 {
3710     if (!m_db)
3711         throw TskException("No database.");
3712 
3713     vector<TskBlackboardArtifact> artifacts;
3714     std::stringstream stmt;
3715     stmt << "SELECT artifact_id, obj_id, artifact_type_id FROM blackboard_artifacts WHERE obj_id = " << file_id << " AND artifact_type_id = " << artifactTypeID;
3716 
3717     sqlite3_stmt * statement;
3718     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK)
3719     {
3720         while (sqlite3_step(statement) == SQLITE_ROW)
3721         {
3722             int artifactTypeID = sqlite3_column_int(statement, 2);
3723 
3724             artifacts.push_back(TskImgDB::createArtifact(sqlite3_column_int64(statement, 0), file_id, artifactTypeID));
3725         }
3726         sqlite3_finalize(statement);
3727     } else {
3728         std::wstringstream msg;
3729         msg << L"TskImgDBSqlite::getArtifactsHelper - Error getting artifacts: " << sqlite3_errmsg(m_db);
3730         LOGERROR(msg.str());
3731         throw TskException("TskImgDBSqlite::getArtifactsHelper - Select failed");
3732     }
3733     return artifacts;
3734 }
3735 
findAttributeTypes(int artifactTypeId)3736 vector<int> TskImgDBSqlite::findAttributeTypes(int artifactTypeId)
3737 {
3738     if (!m_db) {
3739         throw TskException("No database.");
3740     }
3741     vector<int> attrTypes;
3742     std::stringstream stmt;
3743     stmt << "SELECT DISTINCT(attribute_type_id) FROM blackboard_attributes JOIN blackboard_artifacts ON blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id WHERE artifact_type_id = " << artifactTypeId;
3744 
3745     sqlite3_stmt * statement;
3746     if (sqlite3_prepare_v2(m_db, stmt.str().c_str(), -1, &statement, 0) == SQLITE_OK)
3747     {
3748         while (sqlite3_step(statement) == SQLITE_ROW)
3749         {
3750             int artifactTypeID = sqlite3_column_int(statement, 0);
3751 
3752             attrTypes.push_back(artifactTypeID);
3753         }
3754         sqlite3_finalize(statement);
3755     } else {
3756         std::wstringstream msg;
3757         msg << L"TskImgDBSqlite::findAttributeTypes - Error finding attribute types: " << sqlite3_errmsg(m_db);
3758         LOGERROR(msg.str());
3759         throw TskException("TskImgDBSqlite::findAttributeTypes - Select failed");
3760     }
3761     return attrTypes;
3762 }
3763 
quote(const std::string str) const3764 std::string TskImgDBSqlite::quote(const std::string str) const
3765 {
3766     char *item = sqlite3_mprintf("%Q", str.c_str());
3767     std::string returnStr(item);
3768     sqlite3_free(item);
3769     return returnStr;
3770 }
3771 
executeStatement(const std::string & stmtToExecute,sqlite3_stmt * & statement,const std::string & caller) const3772 void TskImgDBSqlite::executeStatement(const std::string &stmtToExecute, sqlite3_stmt *&statement, const std::string &caller) const
3773 {
3774     if (sqlite3_prepare_v2(m_db, stmtToExecute.c_str(), -1, &statement, 0) != SQLITE_OK)
3775     {
3776         sqlite3_finalize(statement);
3777         std::ostringstream msg;
3778         msg << caller << " : error executing " << stmtToExecute << " : " << sqlite3_errmsg(m_db);
3779         throw TskException(msg.str());
3780     }
3781 }
3782 
3783 
3784