Major DB shakeup:
authorSteve McIntyre <steve@einval.com>
Wed, 6 Apr 2005 01:32:16 +0000 (01:32 +0000)
committerSteve McIntyre <steve@einval.com>
Wed, 6 Apr 2005 01:32:16 +0000 (01:32 +0000)
 * Added two extra tables: template blocks and compressed blocks
 * Cope with 3 different tables in results_callback()
 * Rearranged fields in existing files table

jigdb-sql.c
jigdb.h

index 6dba1c9..072526e 100644 (file)
@@ -10,11 +10,24 @@ typedef struct
     sqlite3 *db;
 } db_state_t;
 
+enum result_type
+{
+    RES_TEMPLATE,
+    RES_FILES,
+    RES_BLOCKS
+};
+
 struct results
 {
     struct results *next;
     struct results *prev;
-    db_file_entry_t entry;
+    enum result_type type;
+    union
+    {
+        db_template_entry_t template;
+        db_file_entry_t file;
+        db_compressed_entry_t block;
+    } data;
 };
 
 struct results *res_head = NULL;
@@ -23,11 +36,153 @@ struct results *res_tail = NULL;
 
 char sql_command[2 * PATH_MAX];
 
+static int db_create_template_table(db_state_t *dbp)
+{
+    int error = 0;
+    char *open_error;
+    
+    /* We can't access the table. Delete it and create new */
+    error = sqlite3_exec(dbp->db, "DROP TABLE template;", NULL, NULL, NULL);
+    sprintf(sql_command, "CREATE TABLE template ("
+            "image_offset INTEGER,"
+            "size INTEGER,"
+            "uncomp_offset INTEGER,"
+            "type INTEGER,"
+            "templatename VARCHAR(%d),"
+            "md5 VARCHAR(32));", PATH_MAX);
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_template_table: got error %d (%s) from create\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    /* Create indices */
+    sprintf(sql_command, "CREATE INDEX template_offset ON template (uncomp_offset);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_template_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }                                 
+    sprintf(sql_command, "CREATE INDEX template_template ON template (templatename);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_template_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+
+    return 0;
+}
+
+static int db_create_files_table(db_state_t *dbp)
+{
+    int error = 0;
+    char *open_error;
+    
+    /* We can't access the table. Delete it and create new */
+    error = sqlite3_exec(dbp->db, "DROP TABLE files;", NULL, NULL, NULL);
+    sprintf(sql_command, "CREATE TABLE files ("
+            "size INTEGER,"
+            "mtime INTEGER,"
+            "age INTEGER,"
+            "filetype INTEGER,"
+            "md5 VARCHAR(32),"
+            "filename VARCHAR(%d),"
+            "extra VARCHAR(%d));", PATH_MAX, PATH_MAX);
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_files_table: got error %d (%s) from create\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    /* Create indices */
+    sprintf(sql_command, "CREATE INDEX files_md5 ON files (md5);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_files_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }                                 
+    sprintf(sql_command, "CREATE INDEX files_name ON files (filename);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_files_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    sprintf(sql_command, "CREATE INDEX files_age ON files (age);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_files_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+
+    return 0;
+}
+
+static int db_create_comp_blocks_table(db_state_t *dbp)
+{
+    int error = 0;
+    char *open_error;
+    
+    /* We can't access the table. Delete it and create new */
+    error = sqlite3_exec(dbp->db, "DROP TABLE blocks;", NULL, NULL, NULL);
+    sprintf(sql_command, "CREATE TABLE blocks ("
+            "comp_offset INTEGER,"
+            "uncomp_offset INTEGER,"
+            "uncomp_size INTEGER,"
+            "templatename VARCHAR(%d));", PATH_MAX);
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_comp_blocks_table: got error %d (%s) from create\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    /* Create indices */
+    sprintf(sql_command, "CREATE INDEX blocks_offset ON blocks (uncomp_offset);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_comp_blocks_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }                                 
+    sprintf(sql_command, "CREATE INDEX blocks_template ON blocks (templatename);");
+    error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_create_comp_blocks_table: got error %d (%s) from create index\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+
+    return 0;
+}
+
 JIGDB *db_open(char *db_name)
 {
     db_state_t *dbp = NULL;
     int error = 0;            /* function return value */
-    char *open_error;
 
     /* Allocate state structure */
     dbp = calloc(1, sizeof(*dbp));
@@ -48,55 +203,29 @@ JIGDB *db_open(char *db_name)
         error = sqlite3_exec(dbp->db, "SELECT COUNT(*) FROM files;", NULL, NULL, NULL);
         if (SQLITE_ERROR == error)
         {
-            /* We can't access the table. Delete it and create new */
-            error = sqlite3_exec(dbp->db, "DROP TABLE files;", NULL, NULL, NULL);
-            sprintf(sql_command, "CREATE TABLE files ("
-                    "md5 VARCHAR(32),"
-                    "filetype INTEGER,"
-                    "mtime INTEGER,"
-                    "age INTEGER,"
-                    "size INTEGER,"
-                    "filename VARCHAR(%d),"
-                    "extra VARCHAR(%d));", PATH_MAX, PATH_MAX);
-            error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
-            if (error)
-            {
-                fprintf(stderr, "db_open: got error %d (%s) from create\n", error, open_error);
-                if (open_error)
-                    sqlite3_free(open_error);
-                errno = error;
-                return NULL;
-            }
-            /* Create indices */
-            sprintf(sql_command, "CREATE INDEX files_md5 ON files (md5);");
-            error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+            /* No table found, so create new */
+            /* First, the template table */
+            error = db_create_template_table(dbp);
             if (error)
             {
-                fprintf(stderr, "db_open: got error %d (%s) from create index\n", error, open_error);
-                if (open_error)
-                    sqlite3_free(open_error);
                 sqlite3_close(dbp->db);
                 errno = error;
                 return NULL;
-            }                                 
-            sprintf(sql_command, "CREATE INDEX files_name ON files (filename);");
-            error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+            }
+
+            /* 2. The files table */
+            error = db_create_files_table(dbp);
             if (error)
             {
-                fprintf(stderr, "db_open: got error %d (%s) from create index\n", error, open_error);
-                if (open_error)
-                    sqlite3_free(open_error);
                 sqlite3_close(dbp->db);
                 errno = error;
                 return NULL;
             }
-            sprintf(sql_command, "CREATE INDEX files_age ON files (age);");
-            error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
+
+            /* 3. The compressed blocks table */
+            error = db_create_comp_blocks_table(dbp);
             if (error)
             {
-                fprintf(stderr, "db_open: got error %d (%s) from create index\n", error, open_error);
-                if (open_error)
-                    sqlite3_free(open_error);
                 sqlite3_close(dbp->db);
                 errno = error;
                 return NULL;
@@ -117,27 +246,40 @@ int db_close(JIGDB *dbp)
     return 0;
 }
 
-int db_store_file(JIGDB *dbp, db_file_entry_t *entry)
+/* Delete ALL the template and compressed block entries for a
+ * specified template file */
+int db_delete_template_cache(JIGDB *dbp, char *template_name)
 {
     int error = 0;
     db_state_t *state = dbp;
     char *open_error;
-    
-    sprintf(sql_command, "INSERT INTO files VALUES('%s', %d , %ld , %ld , %lld , '%s', '%s');",
-            entry->md5, entry->type, entry->mtime, entry->age,
-            entry->file_size, entry->filename, entry->extra);
+
+    sprintf(sql_command, "DELETE FROM template WHERE templatename == '%s';", template_name);
     error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
     if (error)
+        fprintf(stderr, "db_delete_template_cache: Failed to delete template entries, error %d (%s)\n", error, open_error);
+    else
     {
-        fprintf(stderr, "db_store_file: Failed to write entry, error %d (%s)\n", error, open_error);
-        if (open_error)
-            sqlite3_free(open_error);
-        return error;
+        sprintf(sql_command, "DELETE FROM blocks WHERE templatename == '%s';", template_name);
+        error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
+        if (error)
+            fprintf(stderr, "db_delete_template_cache: Failed to delete block entries, error %d (%s)\n", error, open_error);
     }
     return error;
 }
 
-void free_results(void)
+/* Does nothing at the moment... */
+int db_dump(JIGDB *dbp)
+{
+    int error = 0;
+/*    int num_records = 0;
+    db_file_entry_t *entry = NULL;
+    db_state_t *state = dbp; */
+
+    return error;
+}
+
+static void free_results(void)
 {
     struct results *entry = res_head;
     struct results *current = res_head;
@@ -156,6 +298,7 @@ void free_results(void)
 static int results_callback(void *pArg, int argc, char **argv, char **columnNames)
 {
     struct results *entry = calloc(1, sizeof (*entry));
+    enum result_type *type = pArg;
     
     if (res_tail)
         res_tail->next = entry;
@@ -164,24 +307,132 @@ static int results_callback(void *pArg, int argc, char **argv, char **columnName
 
     entry->prev = res_tail;
     res_tail = entry;
-    if (argv[0])
-        strncpy(entry->entry.md5, argv[0], sizeof(entry->entry.md5));
-    if (argv[1])
-        entry->entry.type = strtol(argv[1], NULL, 10);
-    if (argv[2])
-        entry->entry.mtime = strtoul(argv[2], NULL, 10);
-    if (argv[3])
-        entry->entry.age = strtoul(argv[3], NULL, 10);
-    if (argv[4])
-        entry->entry.file_size = strtoull(argv[4], NULL, 10);
-    if (argv[5])
-        strncpy(entry->entry.filename, argv[5], sizeof(entry->entry.filename));
-    if (argv[6])
-        strncpy(entry->entry.extra, argv[6], sizeof(entry->entry.extra));
 
+    switch (*type)
+    {
+        case RES_TEMPLATE:
+            if (argv[0])
+                entry->data.template.image_offset = strtoull(argv[0], NULL, 10);
+            if (argv[1])
+                entry->data.template.size = strtoull(argv[1], NULL, 10);
+            if (argv[2])
+                entry->data.template.uncomp_offset = strtoull(argv[2], NULL, 10);
+            if (argv[3])
+                entry->data.template.type = strtoul(argv[3], NULL, 10);
+            if (argv[4])
+                strncpy(entry->data.template.template_name, argv[4], sizeof(entry->data.template.template_name));
+            if (argv[5])
+                strncpy(entry->data.template.md5, argv[5], sizeof(entry->data.template.md5));
+            break;
+        case RES_FILES:
+            if (argv[0])
+                entry->data.file.file_size = strtoull(argv[0], NULL, 10);
+            if (argv[1])
+                entry->data.file.mtime = strtoul(argv[1], NULL, 10);
+            if (argv[2])
+                entry->data.file.age = strtoul(argv[2], NULL, 10);
+            if (argv[3])
+                entry->data.file.type = strtol(argv[3], NULL, 10);
+            if (argv[4])
+                strncpy(entry->data.file.md5, argv[4], sizeof(entry->data.file.md5));
+            if (argv[5])
+                strncpy(entry->data.file.filename, argv[5], sizeof(entry->data.file.filename));
+            if (argv[6])
+                strncpy(entry->data.file.extra, argv[6], sizeof(entry->data.file.extra));
+            break;
+        case RES_BLOCKS:
+            if (argv[0])
+                entry->data.block.comp_offset = strtoull(argv[0], NULL, 10);
+            if (argv[1])
+                entry->data.block.uncomp_offset = strtoull(argv[1], NULL, 10);
+            if (argv[2])
+                entry->data.block.uncomp_size = strtoull(argv[2], NULL, 10);
+            if (argv[3])
+                strncpy(entry->data.block.template_name, argv[3], sizeof(entry->data.block.template_name));            
+            break;
+    }
+    
     return 0;
 }
 
+int db_store_template(JIGDB *dbp, db_template_entry_t *entry)
+{
+    int error = 0;
+    db_state_t *state = dbp;
+    char *open_error;
+
+    sprintf(sql_command, "INSERT INTO template VALUES(%lld,%lld,%lld,%d,'%s','%s');",
+            entry->image_offset, entry->size, entry->uncomp_offset, entry->type,
+            entry->template_name, entry->md5);
+    error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_store_template: Failed to write entry, error %d (%s)\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    return error;
+}
+    
+int db_lookup_template_by_offset(JIGDB *dbp, char *template_name,
+                                 unsigned long long image_offset, db_template_entry_t **out)
+{
+    int error = 0;
+    db_state_t *state = dbp;
+    char *open_error;
+    int result_type = RES_TEMPLATE;
+
+    free_results();
+
+    sprintf(sql_command,
+            "SELECT * FROM template WHERE template_name == '%s' "
+            "AND image_offset <= %lld "
+            "AND (image_offset + size) > %lld;", template_name, image_offset, image_offset);
+    error = sqlite3_exec(state->db, sql_command, results_callback, &result_type, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_lookup_template_by_offset: Failed to lookup, error %d (%s)\n", error, open_error);
+        return error;
+    }
+
+    res_current = res_head;
+    if (res_current)
+    {
+        *out = &res_current->data.template;
+        res_current = res_current->next;
+    }
+    else
+        error = ENOENT;
+
+    return error;
+}
+
+int db_store_file(JIGDB *dbp, db_file_entry_t *entry)
+{
+    int error = 0;
+    db_state_t *state = dbp;
+    char *open_error;
+    
+    sprintf(sql_command, "INSERT INTO files VALUES(%lld,%ld,%ld,%d,'%s','%s','%s');",
+            entry->file_size,
+            entry->mtime,
+            entry->age,
+            entry->type,
+            entry->md5,
+            entry->filename,
+            entry->extra);
+    error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_store_file: Failed to write entry, error %d (%s)\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    return error;
+}
+
 /* Look up the most recent record that is older than the specified
  * age */
 int db_lookup_file_by_age(JIGDB *dbp, time_t age, db_file_entry_t **out)
@@ -189,11 +440,12 @@ int db_lookup_file_by_age(JIGDB *dbp, time_t age, db_file_entry_t **out)
     int error = 0;
     db_state_t *state = dbp;
     char *open_error;
+    int result_type = RES_FILES;
 
     free_results();
 
     sprintf(sql_command, "SELECT * FROM files WHERE age > %ld", age);
-    error = sqlite3_exec(state->db, sql_command, results_callback, NULL, &open_error);
+    error = sqlite3_exec(state->db, sql_command, results_callback, &result_type, &open_error);
     if (error)
     {
         fprintf(stderr, "db_lookup_file_by_age: Failed to lookup, error %d (%s)\n", error, open_error);
@@ -203,7 +455,7 @@ int db_lookup_file_by_age(JIGDB *dbp, time_t age, db_file_entry_t **out)
     res_current = res_head;
     if (res_current)
     {
-        *out = &res_current->entry;
+        *out = &res_current->data.file;
         res_current = res_current->next;
     }
     else
@@ -222,7 +474,7 @@ int db_lookup_file_older(JIGDB *dbp, db_file_entry_t **out)
     
     if (res_current)
     {
-        *out = &res_current->entry;
+        *out = &res_current->data.file;
         res_current = res_current->next;
     }
     else
@@ -236,11 +488,12 @@ int db_lookup_file_by_md5(JIGDB *dbp, char *md5, db_file_entry_t **out)
     int error = 0;
     db_state_t *state = dbp;
     char *open_error;
+    int result_type = RES_FILES;
 
     free_results();
 
     sprintf(sql_command, "SELECT * FROM files WHERE md5 == '%s' ORDER BY filetype ASC;", md5);
-    error = sqlite3_exec(state->db, sql_command, results_callback, NULL, &open_error);
+    error = sqlite3_exec(state->db, sql_command, results_callback, &result_type, &open_error);
     if (error)
     {
         fprintf(stderr, "db_lookup_file_by_md5: Failed to lookup, error %d (%s)\n", error, open_error);
@@ -250,7 +503,7 @@ int db_lookup_file_by_md5(JIGDB *dbp, char *md5, db_file_entry_t **out)
     res_current = res_head;
     if (res_current)
     {
-        *out = &res_current->entry;
+        *out = &res_current->data.file;
         res_current = res_current->next;
     }
     else
@@ -264,11 +517,12 @@ int db_lookup_file_by_name(JIGDB *dbp, char *filename, db_file_entry_t **out)
     int error = 0;
     db_state_t *state = dbp;
     char *open_error;
+    int result_type = RES_FILES;
 
     free_results();
 
     sprintf(sql_command, "SELECT * FROM files WHERE filename == '%s';", filename);
-    error = sqlite3_exec(state->db, sql_command, results_callback, NULL, &open_error);
+    error = sqlite3_exec(state->db, sql_command, results_callback, &result_type, &open_error);
     if (error)
     {
         fprintf(stderr, "db_lookup_file_by_name: Failed to lookup, error %d (%s)\n", error, open_error);
@@ -278,7 +532,7 @@ int db_lookup_file_by_name(JIGDB *dbp, char *filename, db_file_entry_t **out)
     res_current = res_head;
     if (res_current)
     {
-        *out = &res_current->entry;
+        *out = &res_current->data.file;
         res_current = res_current->next;
     }
     else
@@ -293,7 +547,7 @@ int db_delete_file(JIGDB *dbp, char *md5, enum filetype type, char *filename)
     db_state_t *state = dbp;
     char *open_error;
 
-    sprintf(sql_command, "DELETE FROM files WHERE md5 == '%s' AND type == '%d' AND filename == '%s';", md5, type, filename);
+    sprintf(sql_command, "DELETE FROM files WHERE md5 == '%s' AND filetype == '%d' AND filename == '%s';", md5, type, filename);
     error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
     if (error)
         fprintf(stderr, "db_delete_file: Failed to delete, error %d (%s)\n", error, open_error);
@@ -301,12 +555,54 @@ int db_delete_file(JIGDB *dbp, char *md5, enum filetype type, char *filename)
     return error;
 }
 
-int db_dump(JIGDB *dbp)
+int db_store_block(JIGDB *dbp, db_compressed_entry_t *entry)
 {
     int error = 0;
-/*    int num_records = 0;
-    db_file_entry_t *entry = NULL;
-    db_state_t *state = dbp; */
+    db_state_t *state = dbp;
+    char *open_error;
+
+    sprintf(sql_command, "INSERT INTO blocks VALUES(%ld,%ld,%ld,'%s');",
+            entry->comp_offset, entry->uncomp_offset, entry->uncomp_size, entry->template_name);
+    error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_store_block: Failed to write entry, error %d (%s)\n", error, open_error);
+        if (open_error)
+            sqlite3_free(open_error);
+        return error;
+    }
+    return error;
+}
+
+int db_lookup_block_by_offset(JIGDB *dbp, char *template_name,
+                              unsigned long uncomp_offset, db_compressed_entry_t **out)
+{
+    int error = 0;
+    db_state_t *state = dbp;
+    char *open_error;
+    int result_type = RES_BLOCKS;
+
+    free_results();
+
+    sprintf(sql_command,
+            "SELECT * FROM blocks WHERE template_name == '%s' "
+            "AND uncomp_offset <= %ld "
+            "AND (uncomp_offset + size) > %ld;", template_name, uncomp_offset, uncomp_offset);
+    error = sqlite3_exec(state->db, sql_command, results_callback, &result_type, &open_error);
+    if (error)
+    {
+        fprintf(stderr, "db_lookup_template_by_offset: Failed to lookup, error %d (%s)\n", error, open_error);
+        return error;
+    }
+
+    res_current = res_head;
+    if (res_current)
+    {
+        *out = &res_current->data.block;
+        res_current = res_current->next;
+    }
+    else
+        error = ENOENT;
 
     return error;
 }
diff --git a/jigdb.h b/jigdb.h
index 91e87f7..3b843d3 100644 (file)
--- a/jigdb.h
+++ b/jigdb.h
@@ -6,6 +6,28 @@
 
 #define JIGDB void
 
+/* Details about the blocks in the template file; each points to a
+ * matched file, or a compressed lump of unmatched data in the
+ * template file */
+enum blocktype
+{
+    BT_FILE = 0,   /* md5 pointer to a file */
+    BT_BLOCK       /* pointer to a block of data from the template file */
+};
+
+typedef struct
+{
+    unsigned long long image_offset;            /* Start offset within the image */
+    unsigned long long size;                    /* Size of the lump */
+    unsigned long long uncomp_offset;           /* Offset within the compressed template
+                                                   data iff type is BT_BLOCK */
+    enum blocktype     type;
+    unsigned char      template_name[PATH_MAX]; /* The template we're caching */
+    unsigned char      md5[32];                 /* MD5 iff type is BT_FILE */
+} db_template_entry_t;
+
+/* Details about files we know about: local mirror, files in a local
+ * ISO image, files listed in the jigdo, etc. */
 enum filetype
 {
     FT_LOCAL = 0,  /* We have the file directly-accessible in a local filesystem */
@@ -16,28 +38,102 @@ enum filetype
 
 typedef struct
 {
-    unsigned char      md5[32];
+    unsigned long long file_size;          /* size of the file in bytes */
+    time_t             mtime;              /* mtime of the file when we saw it */
+    time_t             age;                /* UINT_MAX - time when added */
     enum filetype      type;
-    time_t             mtime;
-    time_t             age;  /* UINT_MAX - time when added */
-    unsigned long long file_size;
-    char               filename[PATH_MAX];
+    unsigned char      md5[32];            /* md5sum of the file */
+    char               filename[PATH_MAX]; /* path to the file */
     char               extra[PATH_MAX];    /* empty for local files;
                                             * path to ISO for local ISO loopbacks;
                                             * base URL for remote files*/
-} db_file_entry_t;    
+} db_file_entry_t;
+
+/* Details about the compressed blocks of unmatched data within the
+ * template file. The data is stored within the template file as a
+ * series of compressed blocks, but we have no way to seek to the
+ * right compressed block; if we want to read from the middle of the
+ * image we have to decompress each in turn. Cache the details of each
+ * block so we can work from an uncompressed offset directly to the
+ * right compressed block. */
+typedef struct
+{
+    unsigned long      comp_offset;             /* Start offset of this compressed block,
+                                                   measured from the beginning of the
+                                                   template file */
+    unsigned long      uncomp_offset;           /* Start offset when uncompressed */
+    unsigned long      uncomp_size;             /* Size of uncompressed block */
+    unsigned char      template_name[PATH_MAX]; /* The template we're caching */
+} db_compressed_entry_t;
 
+/*******************
+ *
+ * Common interfaces
+ *
+ ******************/
+
+/* Open/create the database */
 JIGDB *db_open(char *db_name);
+
+/* Close the database */
 int db_close(JIGDB *dbp);
+
+/* Delete ALL the template and compressed block entries for a
+ * specified template file */
+int db_delete_template_cache(JIGDB *dbp, char *template_name);
+
+/* Dump the contents of the DB for debug */
+int db_dump(JIGDB *dbp);
+
+/********************
+ *
+ * Template functions
+ *
+ *******************/
+
+/* Store a template match / non-match block entry */
+int db_store_template(JIGDB *dbp, db_template_entry_t *entry);
+
+/* Lookup a template entry by output offset. The specified offset will
+ * be within the range covered by the returned entry, or ENOENT. */
+int db_lookup_template_by_offset(JIGDB *dbp, char *template_name,
+                                 unsigned long long image_offset, db_template_entry_t **out);
+
+/****************
+ *
+ * File functions
+ *
+ ***************/
+
+/* Store details of a file */
 int db_store_file(JIGDB *dbp, db_file_entry_t *entry);
+
+/* Lookup files added older than a specified age */
 int db_lookup_file_by_age(JIGDB *dbp, time_t added, db_file_entry_t **out);
+
+/* Lookup the next older file */
 int db_lookup_file_older(JIGDB *dbp, db_file_entry_t **out);
+
+/* Lookup a file by md5 */
 int db_lookup_file_by_md5(JIGDB *dbp, char *md5, db_file_entry_t **out);
+
+/* Lookup a file by name */
 int db_lookup_file_by_name(JIGDB *dbp, char *filename, db_file_entry_t **out);
+
+/* Delete a file entry */
 int db_delete_file(JIGDB *dbp, char *md5, enum filetype type, char *filename);
-int db_dump(JIGDB *dbp);
 
-#endif /* JIGDB_H */
+/****************************
+ *
+ * Compressed block functions
+ *
+ ***************************/
 
+/* Store details of a block */
+int db_store_block(JIGDB *dbp, db_compressed_entry_t *entry);
 
+/* Lookup a block by its UNCOMPRESSED offset */
+int db_lookup_block_by_offset(JIGDB *dbp, char *template_name,
+                              unsigned long uncomp_offset, db_compressed_entry_t **out);
 
+#endif /* JIGDB_H */