From ff0f02008bdf78a4d9e9ebda7dbd942b9f993670 Mon Sep 17 00:00:00 2001
From: Ann Priestman <apriestman@basistech.com>
Date: Thu, 13 Oct 2016 14:27:34 -0400
Subject: [PATCH] Add support for loading secondary GPT

---
 tsk/vs/gpt.c     | 148 ++++++++++++++++++++++++++++++++---------------
 tsk/vs/tsk_gpt.h |   5 ++
 2 files changed, 105 insertions(+), 48 deletions(-)

diff --git a/tsk/vs/gpt.c b/tsk/vs/gpt.c
index 22754c193..d14bd1bb4 100644
--- a/tsk/vs/gpt.c
+++ b/tsk/vs/gpt.c
@@ -22,7 +22,7 @@
  * It is loaded into the internal sorted list 
  */
 static uint8_t
-gpt_load_table(TSK_VS_INFO * vs)
+gpt_load_table(TSK_VS_INFO * vs, GPT_TYPE_ENUM gpt_type)
 {
     gpt_head *head;
     gpt_entry *ent;
@@ -32,68 +32,94 @@ gpt_load_table(TSK_VS_INFO * vs)
     char *safe_str, *head_str, *tab_str, *ent_buf;
     ssize_t cnt;
     char *sect_buf;
-    TSK_DADDR_T taddr = vs->offset / vs->block_size + GPT_PART_SOFFSET;
     TSK_DADDR_T max_addr = (vs->img_info->size - vs->offset) / vs->block_size;  // max sector
+    TSK_DADDR_T gpt_relative_addr;
+    TSK_DADDR_T gpt_absolute_addr;
+    TSK_DADDR_T dos_sect_relative_addr;
+    TSK_DADDR_T dos_sect_absolute_addr;
+
+    if(gpt_type == PRIMARY_TABLE){
+        dos_sect_relative_addr = GPT_PART_SOFFSET;
+        dos_sect_absolute_addr = vs->offset / vs->block_size + GPT_PART_SOFFSET;
+        gpt_relative_addr = GPT_PART_SOFFSET + 1;
+        gpt_absolute_addr = vs->offset / vs->block_size + GPT_PART_SOFFSET + 1;
+    } else {
+        gpt_relative_addr = ((vs->img_info->size - vs->offset) / vs->block_size) - 1;
+        gpt_absolute_addr = (vs->img_info->size / vs->block_size) - 1;
+    }
+
 
     if (tsk_verbose)
         tsk_fprintf(stderr, "gpt_load_table: Sector: %" PRIuDADDR "\n",
-            taddr);
+            gpt_absolute_addr);
 
     if ((sect_buf = tsk_malloc(vs->block_size)) == NULL)
         return 1;
-    dos_part = (dos_sect *) sect_buf;
 
-    cnt = tsk_vs_read_block
-        (vs, GPT_PART_SOFFSET, sect_buf, vs->block_size);
-    /* if -1, then tsk_errno is already set */
-    if (cnt != vs->block_size) {
-        if (cnt >= 0) {
-            tsk_error_reset();
-            tsk_error_set_errno(TSK_ERR_VS_READ);
+    if(gpt_type == PRIMARY_TABLE){
+        dos_part = (dos_sect *) sect_buf;
+
+        cnt = tsk_vs_read_block
+            (vs, dos_sect_relative_addr, sect_buf, vs->block_size);
+        /* if -1, then tsk_errno is already set */
+        if (cnt != vs->block_size) {
+            if (cnt >= 0) {
+                tsk_error_reset();
+                tsk_error_set_errno(TSK_ERR_VS_READ);
+            }
+            tsk_error_set_errstr2
+                ("Error reading DOS safety partition table in Sector: %"
+                PRIuDADDR, dos_sect_absolute_addr);
+            free(sect_buf);
+            return 1;
         }
-        tsk_error_set_errstr2
-            ("Error reading DOS safety partition table in Sector: %"
-            PRIuDADDR, taddr);
-        free(sect_buf);
-        return 1;
-    }
 
-    /* Sanity Check */
-    if (tsk_vs_guessu16(vs, dos_part->magic, DOS_MAGIC)) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
-        tsk_error_set_errstr
-            ("Missing DOS safety partition (invalid magic) (Sector: %"
-            PRIuDADDR ")", taddr);
-        free(sect_buf);
-        return 1;
-    }
+        /* Sanity Check */
+        if (tsk_vs_guessu16(vs, dos_part->magic, DOS_MAGIC)) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr
+                ("Missing DOS safety partition (invalid magic) (Sector: %"
+                PRIuDADDR ")", dos_sect_absolute_addr);
+            free(sect_buf);
+            return 1;
+        }
 
-    if (dos_part->ptable[0].ptype != GPT_DOS_TYPE) {
-        tsk_error_reset();
-        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
-        tsk_error_set_errstr
-            ("Missing DOS safety partition (invalid type in table: %d)",
-            dos_part->ptable[0].ptype);
-        free(sect_buf);
-        return 1;
+        if (dos_part->ptable[0].ptype != GPT_DOS_TYPE) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr
+                ("Missing DOS safety partition (invalid type in table: %d)",
+                dos_part->ptable[0].ptype);
+            free(sect_buf);
+            return 1;
+        }
     }
 
     /* Read the GPT header */
     head = (gpt_head *) sect_buf;
     cnt = tsk_vs_read_block
-        (vs, GPT_PART_SOFFSET + 1, sect_buf, vs->block_size);
+        (vs, gpt_relative_addr, sect_buf, vs->block_size);
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
             tsk_error_set_errno(TSK_ERR_VS_READ);
         }
         tsk_error_set_errstr2("GPT Header structure in Sector: %"
-            PRIuDADDR, taddr + 1);
+            PRIuDADDR, gpt_absolute_addr);
         free(sect_buf);
         return 1;
     }
 
+    /* Do the endianness test for the secondary table since the test in the dos safety table was skipped */
+    if(gpt_type == SECONDARY_TABLE){
+        if(tsk_getu64(TSK_BIG_ENDIAN, head->head_lba) == gpt_relative_addr){
+            vs->endian = TSK_BIG_ENDIAN;
+        } else {
+            vs->endian = TSK_LIT_ENDIAN;
+        }
+    }
+
     if (tsk_getu64(vs->endian, &head->signature) != GPT_HEAD_SIG) {
         tsk_error_reset();
         tsk_error_set_errno(TSK_ERR_VS_MAGIC);
@@ -109,21 +135,22 @@ gpt_load_table(TSK_VS_INFO * vs)
         return 1;
     }
 
-    snprintf(safe_str, 16, "Safety Table");
-    if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) 0, (TSK_DADDR_T) 1,
-            TSK_VS_PART_FLAG_META, safe_str, -1, -1)) {
-        free(sect_buf);
-        return 1;
+    if(gpt_type == PRIMARY_TABLE){
+        snprintf(safe_str, 16, "Safety Table");
+        if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) 0, (TSK_DADDR_T) 1,
+                TSK_VS_PART_FLAG_META, safe_str, -1, -1)) {
+            free(sect_buf);
+            return 1;
+        }
     }
 
-
     if ((head_str = tsk_malloc(16)) == NULL) {
         free(sect_buf);
         return 1;
     }
 
     snprintf(head_str, 16, "GPT Header");
-    if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) 1,
+    if (NULL == tsk_vs_part_add(vs, gpt_relative_addr,
             (TSK_DADDR_T) ((tsk_getu32(vs->endian,
                         &head->head_size_b) + (vs->block_size -
                         1)) / vs->block_size), TSK_VS_PART_FLAG_META,
@@ -303,7 +330,7 @@ tsk_vs_gpt_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset)
     vs->close = gpt_close;
 
     /* Load the partitions into the sorted list */
-    if (gpt_load_table(vs)) {
+    if (gpt_load_table(vs, PRIMARY_TABLE)) {
         int found = 0;
         if (tsk_verbose)
             tsk_fprintf(stderr, "gpt_open: Trying other sector sizes\n");
@@ -315,7 +342,7 @@ tsk_vs_gpt_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset)
                 tsk_fprintf(stderr, "gpt_open: Trying sector size: %d\n",
                     vs->block_size);
 
-            if (gpt_load_table(vs)) {
+            if (gpt_load_table(vs, PRIMARY_TABLE)) {
                 vs->block_size *= 2;
                 continue;
             }
@@ -324,8 +351,33 @@ tsk_vs_gpt_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset)
         }
 
         if (found == 0) {
-            gpt_close(vs);
-            return NULL;
+            /* Look for the secondary GPT at the end of the image */
+            if (tsk_verbose)
+                tsk_fprintf(stderr, "gpt_open: Trying secondary table\n");
+            vs->block_size = img_info->sector_size;
+            if(gpt_load_table(vs, SECONDARY_TABLE)){
+
+                /* Try other sector sizes again */
+                vs->block_size = 512;
+                while (vs->block_size <= 8192) {
+                    if (tsk_verbose)
+                        tsk_fprintf(stderr, "gpt_open: Trying secondary table sector size: %d\n",
+                            vs->block_size);
+
+                    if (gpt_load_table(vs, SECONDARY_TABLE)) {
+                        vs->block_size *= 2;
+                        continue;
+                    }
+                    found = 1;
+                    break;
+                }
+
+                if(found == 0){
+                    gpt_close(vs);
+                    return NULL;
+                }
+            }
+
         }
     }
 
diff --git a/tsk/vs/tsk_gpt.h b/tsk/vs/tsk_gpt.h
index b265bcc2b..d74a4fef4 100644
--- a/tsk/vs/tsk_gpt.h
+++ b/tsk/vs/tsk_gpt.h
@@ -57,6 +57,11 @@ extern "C" {
         uint8_t name[72];       /* name in unicode */
     } gpt_entry;
 
+    typedef enum {
+        PRIMARY_TABLE,
+        SECONDARY_TABLE,
+    } GPT_TYPE_ENUM;
+
 
 #ifdef __cplusplus
 }
-- 
GitLab