Index: trunk/lib/regfi.c
===================================================================
--- trunk/lib/regfi.c	(revision 155)
+++ trunk/lib/regfi.c	(revision 157)
@@ -471,8 +471,8 @@
 
 
-/*******************************************************************
+/******************************************************************************
  * Given an offset and an hbin, is the offset within that hbin?
  * The offset is a virtual file offset.
- *******************************************************************/
+ ******************************************************************************/
 static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
 {
@@ -489,14 +489,28 @@
 
 
-/*******************************************************************
- * Provide a virtual offset and receive the correpsonding HBIN 
+/******************************************************************************
+ * Provide a physical offset and receive the correpsonding HBIN
  * block for it.  NULL if one doesn't exist.
- *******************************************************************/
-const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset)
-{
-  return (const REGFI_HBIN*)range_list_find_data(file->hbins, 
-						 voffset+REGFI_REGF_SIZE);
-}
-
+ ******************************************************************************/
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 offset)
+{
+  return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
+}
+
+
+/******************************************************************************
+ * Calculate the largest possible cell size given a physical offset.
+ * Largest size is based on the HBIN the offset is currently a member of.
+ * Returns negative values on error.
+ * (Since cells can only be ~2^31 in size, this works out.)
+ ******************************************************************************/
+int32 regfi_calc_maxsize(REGFI_FILE* file, uint32 offset)
+{
+  const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
+  if(hbin == NULL)
+    return -1;
+
+  return (hbin->block_size + hbin->file_off) - offset;
+}
 
 
@@ -542,6 +556,6 @@
   REGFI_SUBKEY_LIST* ret_val;
   REGFI_SUBKEY_LIST** sublists;
-  const REGFI_HBIN* sublist_hbin;
-  uint32 i, num_sublists, off, max_length;
+  uint32 i, num_sublists, off;
+  int32 sublist_maxsize;
 
   if(depth_left == 0)
@@ -565,13 +579,11 @@
     {
       off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
-      sublist_hbin = regfi_lookup_hbin(file, ret_val->elements[i].offset);
-      if(sublist_hbin == NULL)
+
+      sublist_maxsize = regfi_calc_maxsize(file, off);
+      if(sublist_maxsize < 0)
 	sublists[i] = NULL;
       else
-      {
-	max_length = sublist_hbin->block_size + sublist_hbin->file_off - off;
-	sublists[i] = regfi_load_subkeylist_aux(file, off, max_length, strict,
-						depth_left-1);
-      }
+	sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, 
+						strict, depth_left-1);
     }
     talloc_free(ret_val);
@@ -792,5 +804,5 @@
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
-     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
@@ -808,6 +820,6 @@
   ret_val->desc_size = IVAL(sk_header, 0x10);
 
-  if(ret_val->prev_sk_off != (ret_val->prev_sk_off & 0xFFFFFFF8) 
-     || ret_val->next_sk_off != (ret_val->next_sk_off & 0xFFFFFFF8))
+  if((ret_val->prev_sk_off & 0x00000007) != 0
+     || (ret_val->next_sk_off & 0x00000007) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
@@ -873,5 +885,5 @@
   }
 
-  if(cell_length != (cell_length & 0xFFFFFFF8))
+  if((cell_length & 0x00000007) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
@@ -925,5 +937,5 @@
        *      with partial files. */
       if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
-	 || ((ret_val->elements[i] & 0xFFFFFFF8) != ret_val->elements[i]))
+	 || ((ret_val->elements[i] & 0x00000007) != 0))
       {
 	regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
@@ -946,15 +958,12 @@
 {
   REGFI_VK_REC* ret_val = NULL;
-  const REGFI_HBIN* hbin;
-  uint32 data_offset, data_maxsize;
   REGFI_BUFFER data;
-
-  hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
-  if(!hbin)
-    return NULL;
-  
-  ret_val = regfi_parse_vk(file, offset, 
-			   hbin->block_size + hbin->file_off - offset, strict);
-
+  int32 max_size;
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size < 0)
+    return NULL;
+  
+  ret_val = regfi_parse_vk(file, offset, max_size, strict);
   if(ret_val == NULL)
     return NULL;
@@ -964,34 +973,8 @@
   else
   {
-    if(ret_val->data_in_offset)
-    {
-      data = regfi_load_data(file, ret_val->type, ret_val->data_off,
-			      ret_val->data_size, 4,
-			      ret_val->data_in_offset, strict);
-      ret_val->data = data.buf;
-      ret_val->data_size = data.len;
-    }
-    else
-    {
-      hbin = regfi_lookup_hbin(file, ret_val->data_off);
-      if(hbin)
-      {
-	data_offset = ret_val->data_off+REGFI_REGF_SIZE;
-	data_maxsize = hbin->block_size + hbin->file_off - data_offset;
-	data = regfi_load_data(file, ret_val->type, data_offset, 
-				ret_val->data_size, data_maxsize, 
-				ret_val->data_in_offset, strict);
-	ret_val->data = data.buf;
-	ret_val->data_size = data.len;
-
-      }
-      else
-      {
-	regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
-			  " while parsing VK record at offset 0x%.8X.", 
-			  ret_val->offset);
-	ret_val->data = NULL;
-      }
-    }
+    data = regfi_load_data(file, ret_val->data_off, ret_val->data_size,
+			   ret_val->data_in_offset, strict);
+    ret_val->data = data.buf;
+    ret_val->data_size = data.len;
 
     if(ret_val->data == NULL)
@@ -1041,16 +1024,14 @@
 REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
 {
-  const REGFI_HBIN* hbin;
-  const REGFI_HBIN* sub_hbin;
   REGFI_NK_REC* nk;
-  uint32 max_length, off;
-
-  hbin = regfi_lookup_hbin(file, offset-REGFI_REGF_SIZE);
-  if (hbin == NULL) 
+  uint32 off;
+  int32 max_size;
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if (max_size < 0) 
     return NULL;
 
   /* get the initial nk record */
-  max_length = hbin->block_size + hbin->file_off - offset;
-  if((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+  if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
   {
     regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
@@ -1062,9 +1043,7 @@
   if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
   {
-    sub_hbin = hbin;
-    if(!regfi_offset_in_hbin(hbin, nk->values_off)) 
-      sub_hbin = regfi_lookup_hbin(file, nk->values_off);
-    
-    if(sub_hbin == NULL)
+    off = nk->values_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0)
     {
       if(strict)
@@ -1079,8 +1058,6 @@
     else
     {
-      off = nk->values_off + REGFI_REGF_SIZE;
-      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
-      nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length, 
-					true);
+      nk->values = regfi_load_valuelist(file, off, nk->num_values, 
+					max_size, true);
       if(nk->values == NULL)
       {
@@ -1100,9 +1077,7 @@
   if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
   {
-    sub_hbin = hbin;
-    if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
-      sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
-
-    if(sub_hbin == NULL) 
+    off = nk->subkeys_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0) 
     {
       if(strict)
@@ -1116,8 +1091,6 @@
     else
     {
-      off = nk->subkeys_off + REGFI_REGF_SIZE;
-      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
       nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
-					  max_length, true);
+					  max_size, true);
 
       if(nk->subkeys == NULL)
@@ -1140,6 +1113,5 @@
 {
   REGFI_SK_REC* ret_val = NULL;
-  const REGFI_HBIN* hbin;
-  uint32 max_length;
+  int32 max_size;
   void* failure_ptr = NULL;
   
@@ -1153,10 +1125,9 @@
   if(ret_val == NULL)
   {
-    hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
-    if(hbin == NULL)
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
       return NULL;
 
-    max_length = hbin->block_size + hbin->file_off - offset;
-    ret_val = regfi_parse_sk(file, offset, max_length, strict);
+    ret_val = regfi_parse_sk(file, offset, max_size, strict);
     if(ret_val == NULL)
     { /* Cache the parse failure and bail out. */
@@ -1868,8 +1839,8 @@
 {
   uint8 nk_header[REGFI_NK_MIN_LENGTH];
-  const REGFI_HBIN *hbin;
   REGFI_NK_REC* ret_val;
   uint32 length,cell_length;
-  uint32 class_offset, class_maxsize;
+  uint32 class_offset;
+  int32 class_maxsize;
   bool unalloc = false;
 
@@ -1906,5 +1877,5 @@
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
-     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
@@ -2004,9 +1975,8 @@
   if(ret_val->classname_off != REGFI_OFFSET_NONE)
   {
-    hbin = regfi_lookup_hbin(file, ret_val->classname_off);
-    if(hbin)
-    {
-      class_offset = ret_val->classname_off+REGFI_REGF_SIZE;
-      class_maxsize = hbin->block_size + hbin->file_off - class_offset;
+    class_offset = ret_val->classname_off + REGFI_REGF_SIZE;
+    class_maxsize = regfi_calc_maxsize(file, class_offset);
+    if(class_maxsize > 0)
+    {
       ret_val->classname
 	= regfi_parse_classname(file, class_offset, &ret_val->classname_length, 
@@ -2044,5 +2014,5 @@
 
   if(*name_length > 0 && offset != REGFI_OFFSET_NONE 
-     && offset == (offset & 0xFFFFFFF8))
+     && (offset & 0x00000007) == 0)
   {
     if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
@@ -2053,5 +2023,5 @@
     }
 
-    if((cell_length & 0xFFFFFFF8) != cell_length)
+    if((cell_length & 0x0000007) != 0)
     {
       regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
@@ -2129,5 +2099,5 @@
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
-     || ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
+     || (ret_val->cell_size & 0x00000007) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
@@ -2154,4 +2124,7 @@
   raw_data_size = IVAL(vk_header, 0x4);
   ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+  /* The data is typically stored in the offset if the size <= 4, 
+   * in which case this flag is set. 
+   */
   ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
   ret_val->data_off = IVAL(vk_header, 0x8);
@@ -2215,35 +2188,28 @@
 
 /******************************************************************************
-*******************************************************************************/
-REGFI_BUFFER regfi_load_data(REGFI_FILE* file, 
-			     uint32 data_type, uint32 offset,
-			     uint32 length, uint32 max_size, 
-			     bool data_in_offset, bool strict)
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32 voffset, 
+			     uint32 length, bool data_in_offset,
+			     bool strict)
 {
   REGFI_BUFFER ret_val;
-  uint32 read_length, cell_length;
-  uint8 i;
+  uint32 cell_length, offset;
+  int32 max_size;
   bool unalloc;
   
-  /* The data is typically stored in the offset if the size <= 4 */
   if(data_in_offset)
-  {
-    if(length > 4)
-    {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
-			" while parsing data record at offset 0x%.8X.", 
-			offset);
+    return regfi_parse_little_data(file, voffset, length, strict);
+  else
+  {
+    offset = voffset + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
+			" at offset 0x%.8X.", offset);
       goto fail;
     }
-
-    if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
-      goto fail;
-    ret_val.len = length;
-
-    for(i = 0; i < length; i++)
-      ret_val.buf[i] = (uint8)((offset >> i*8) & 0xFF);
-  }
-  else
-  {
+    
     if(!regfi_parse_cell(file->fd, offset, NULL, 0,
 			 &cell_length, &unalloc))
@@ -2254,5 +2220,5 @@
     }
 
-    if((cell_length & 0xFFFFFFF8) != cell_length)
+    if((cell_length & 0x00000007) != 0)
     {
       regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
@@ -2267,8 +2233,5 @@
 			" while parsing data record at offset 0x%.8X.",
 			offset);
-      if(strict)
-	goto fail;
-      else
-	cell_length = max_size;
+      goto fail;
     }
 
@@ -2282,5 +2245,6 @@
       {
 	/* Attempt to parse a big data record */
-	return regfi_load_big_data(file, offset, length, cell_length, strict);
+	return regfi_load_big_data(file, offset, length, cell_length, 
+				   NULL, strict);
       }
       else
@@ -2297,17 +2261,5 @@
     }
 
-    if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
-      goto fail;
-    ret_val.len = length;
-
-    read_length = length;
-    if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
-       || read_length != length)
-    {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
-			" parsing data record at offset 0x%.8X.", offset);
-      talloc_free(ret_val.buf);
-      goto fail;
-    }
+    ret_val = regfi_parse_data(file, offset, length, strict);
   }
 
@@ -2322,115 +2274,346 @@
 
 /******************************************************************************
+ * Parses the common case data records stored in a single cell.
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32 offset,
+			      uint32 length, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32 read_length;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  
+  if(lseek(file->fd, offset+4, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
+		      "reading data at offset 0x%.8X.", offset);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  read_length = length;
+  if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
+     || read_length != length)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
+		      " parsing data record at offset 0x%.8X.", offset);
+    talloc_free(ret_val.buf);
+    ret_val.buf = NULL;
+    ret_val.buf = 0;
+  }
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32 voffset,
+				     uint32 length, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint8 i;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+
+  if(length > 4)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
+		      " while parsing data record. (voffset=0x%.8X, length=%d)",
+		      voffset, length);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  for(i = 0; i < length; i++)
+    ret_val.buf[i] = (uint8)((voffset >> i*8) & 0xFF);
+
+  return ret_val;
+}
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32 offset, 
+					 uint32 max_size, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32 cell_length;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+  ret_val.buf = (uint8*)talloc_array(NULL, uint8, REGFI_BIG_DATA_MIN_LENGTH);
+  if(ret_val.buf == NULL)
+    goto fail;
+
+  if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
+		      "while parsing big data header at offset 0x%.8X.",offset);
+    goto fail;
+  }
+
+  if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+		      " parsing big data header at offset 0x%.8X.", offset);
+    goto fail;
+  }
+
+  if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
+		      " (0x%.2X, 0x%.2X) encountered while parsing"
+		      " big data header at offset 0x%.8X.", 
+		      ret_val.buf[0], ret_val.buf[1], offset);
+    goto fail;
+  }
+
+  ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
+  return ret_val;
+
+ fail:
+  if(ret_val.buf != NULL)
+  {
+    talloc_free(ret_val.buf);
+    ret_val.buf = NULL;
+  }
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+uint32* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32 offset,
+				      uint16 num_chunks, bool strict)
+{
+  uint32* ret_val;
+  uint32 indirect_length;
+  int32 max_size;
+  uint16 i;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if((max_size < 0) || (num_chunks*sizeof(uint32) + 4 > max_size))
+    return NULL;
+
+  ret_val = (uint32*)talloc_array(NULL, uint32, num_chunks);
+  if(ret_val == NULL)
+    goto fail;
+
+  if(!regfi_parse_cell(file->fd, offset, (uint8*)ret_val,
+		       num_chunks*sizeof(uint32),
+		       &indirect_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+		      " parsing big data indirect record at offset 0x%.8X.", 
+		      offset);
+    goto fail;
+  }
+
+  /* Convert pointers to proper endianess, verify they are aligned. */
+  for(i=0; i<num_chunks; i++)
+  {
+    ret_val[i] = IVAL(ret_val, i*sizeof(uint32));
+    if((ret_val[i] & 0x00000007) != 0)
+      goto fail;
+  }
+  
+  return ret_val;
+
+ fail:
+  if(ret_val != NULL)
+    talloc_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Arguments:
+ *  file       --
+ *  offsets    -- list of virtual offsets.
+ *  num_chunks -- 
+ *  strict     --
+ *
+ * Returns:
+ *  A range_list with physical offsets and complete lengths 
+ *  (including cell headers) of associated cells.  
+ *  No data in range_list elements.
+ ******************************************************************************/
+range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32* offsets,
+				       uint16 num_chunks, bool strict)
+{
+  uint32 cell_length, chunk_offset, data_left;
+  range_list* ret_val;
+  uint16 i;
+  bool unalloc;
+  
+  /* XXX: do something with unalloc? */
+  ret_val = range_list_new();
+  if(ret_val == NULL)
+    goto fail;
+  
+  for(i=0; (i<num_chunks) && (data_left>0); i++)
+  {
+    chunk_offset = offsets[i]+REGFI_REGF_SIZE;
+    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+			 &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+			" parsing big data chunk at offset 0x%.8X.", 
+			chunk_offset);
+      goto fail;
+    }
+
+    if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
+      goto fail;
+  }
+
+  return ret_val;
+
+ fail:
+  if(ret_val != NULL)
+    range_list_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
 *******************************************************************************/
 REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
 				 uint32 offset, uint32 data_length, 
-				 uint32 cell_length, bool strict)
+				 uint32 cell_length, range_list* used_ranges,
+				 bool strict)
 {
   REGFI_BUFFER ret_val;
   uint16 num_chunks, i;
-  uint32 indirect_offset, indirect_length, chunk_length, chunk_offset;
-  uint32 read_length, data_left;
-  bool unalloc;
-  uint32* indirect_ptrs;
-  uint8* big_data_cell = (uint8*)malloc(cell_length*sizeof(uint8));
-  if(big_data_cell == NULL)
+  uint32 read_length, data_left, tmp_len, indirect_offset;
+  uint32* indirect_ptrs = NULL;
+  REGFI_BUFFER bd_header;
+  range_list* bd_cells = NULL;
+  const range_list_element* cell_info;
+
+  ret_val.buf = NULL;
+
+  /* XXX: Add better error/warning messages */
+
+  bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
+  if(bd_header.buf == NULL)
     goto fail;
 
-  if(!regfi_parse_cell(file->fd, offset, big_data_cell, cell_length-4,
-		       &cell_length, &unalloc))
-  {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-		      " parsing big data record at offset 0x%.8X.", offset);
-    free(big_data_cell);
-    goto fail;
-  }
-  
-  if((big_data_cell[0] != 'd') || (big_data_cell[1] != 'b'))
-  {
-    regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
-		      " (0x%.2X, 0x%.2X) encountered while parsing"
-		      " big data record at offset 0x%.8X.", 
-		      big_data_cell[0], big_data_cell[1], offset);
-    free(big_data_cell);
-    goto fail;
-  }
-  num_chunks = SVAL(big_data_cell, 0x2);
-  /* XXX: Should check sanity of pointers here and in the indirect
-   *      block.  At least ensure they are a multiple of 8. 
-   */
-  indirect_offset = IVAL(big_data_cell, 0x4) + REGFI_REGF_SIZE;
-  free(big_data_cell);
-
-  indirect_ptrs = (uint32*)malloc(num_chunks*sizeof(uint32));
+  /* Keep track of used space for use by reglookup-recover */
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, offset, cell_length, NULL))
+      goto fail;
+
+  num_chunks = SVAL(bd_header.buf, 0x2);
+  indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
+  talloc_free(bd_header.buf);
+
+  indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
+						num_chunks, strict);
   if(indirect_ptrs == NULL)
     goto fail;
 
-  if(!regfi_parse_cell(file->fd, indirect_offset, (uint8*)indirect_ptrs,
-		       num_chunks*sizeof(uint32),
-		       &indirect_length, &unalloc))
-  {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-		      " parsing big data indirect record at offset 0x%.8X.", 
-		      offset);
-    free(indirect_ptrs);
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
+      goto fail;
+  
+  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
     goto fail;
-  }
-  
-  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
-  {
-    free(indirect_ptrs);
+  data_left = data_length;
+
+  bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
+  if(bd_cells == NULL)
     goto fail;
-  }
-  data_left = data_length;
-
+
+  talloc_free(indirect_ptrs);
+  indirect_ptrs = NULL;
+  
   for(i=0; (i<num_chunks) && (data_left>0); i++)
   {
-    /* Fix endianness of pointer and convert to physical offset */
-    chunk_offset = IVAL(indirect_ptrs, i*4) + REGFI_REGF_SIZE;
-    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0, 
-			 &chunk_length, &unalloc))
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-			" parsing big data chunk at offset 0x%.8X.", 
-			chunk_offset);
-      if(strict)
-	goto chunk_fail;
-      else
-	break;
-    }
-
-    /* XXX: This should be "chunk_length-4" to account for the 4 byte cell 
+    cell_info = range_list_get(bd_cells, i);
+    if(cell_info == NULL)
+      goto fail;
+
+    /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
      *      length.  However, it has been observed that some (all?) chunks
      *      have an additional 4 bytes of 0 at the end of their cells that 
      *      isn't part of the data, so we're trimming that off too. 
+     *      Perhaps it's just an 8 byte alignment requirement...
      */
-    if(chunk_length-8 >= data_left)
+    if(cell_info->length - 8 >= data_left)
+    {
+      if(i+1 != num_chunks)
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
+			  "while constructing big data at offset 0x%.8X "
+			  "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      }
       read_length = data_left;
+    }
     else
-      read_length = chunk_length-8;
-
+      read_length = cell_info->length - 8;
+
+
+    if(read_length > regfi_calc_maxsize(file, cell_info->offset))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
+			"while constructing big data at offset 0x%.8X "
+			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    if(lseek(file->fd, cell_info->offset+sizeof(uint32), SEEK_SET) == -1)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
+			"constructing big data at offset 0x%.8X "
+			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    tmp_len = read_length;
     if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), 
-		  &read_length) != 0)
+		  &read_length) != 0 || (read_length != tmp_len))
     {
       regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
-			" constructing big data at offset 0x%.8X.", 
-			chunk_offset);
-      if(strict)
-	goto chunk_fail;
-      else
-	break;
-    }
+			" constructing big data at offset 0x%.8X"
+			" (chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    if(used_ranges != NULL)
+      if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
+	goto fail;
 
     data_left -= read_length;
   }
-  free(indirect_ptrs);
+  range_list_free(bd_cells);
+
   ret_val.len = data_length-data_left;
-
-  return ret_val;
-
- chunk_fail:
-  free(indirect_ptrs);
-  talloc_free(ret_val.buf);
+  return ret_val;
+
  fail:
+  if(ret_val.buf != NULL)
+    talloc_free(ret_val.buf);
+  if(indirect_ptrs != NULL)
+    talloc_free(indirect_ptrs);
+  if(bd_cells != NULL)
+    range_list_free(bd_cells);
   ret_val.buf = NULL;
   ret_val.len = 0;
@@ -2466,5 +2649,5 @@
 	break;
       
-      if((cell_len == 0) || ((cell_len & 0xFFFFFFF8) != cell_len))
+      if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
       {
 	regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
