"Fossies" - the Fresh Open Source Software archive

Member "testdisk-6.13/src/partgpt.c" of archive testdisk-6.13.tar.gz:


/*

    File: partgpt.c

    Copyright (C) 2007-2009 Christophe GRENIER <grenier@cgsecurity.org>

    This software is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write the Free Software Foundation, Inc., 51
    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <ctype.h>      /* tolower */
#include "types.h"
#if defined(HAVE_UUID_H)
#include <uuid.h>
#elif defined(HAVE_UUID_UUID_H)
#include <uuid/uuid.h>
#endif
#if defined(HAVE_SYS_UUID_H)
#include <sys/uuid.h>
#endif
#include "common.h"
#include "fnctdsk.h"
#include "lang.h"
#include "intrf.h"
#include "analyse.h"
#include "chgtype.h"
#include "partgpt.h"
#include "savehdr.h"
#include "exfat.h"
#include "fat.h"
#include "hfs.h"
#include "hfsp.h"
#include "lvm.h"
#include "ntfs.h"
#include "log.h"
#include "log_part.h"
#include "md.h"
#include "guid_cmp.h"
#include "guid_cpy.h"
#include "unicode.h"
#include "crc.h"

static int check_part_gpt(disk_t *disk, const int verbose, partition_t *partition, const int saveheader);
static list_part_t *read_part_gpt(disk_t *disk_car, const int verbose, const int saveheader);
static list_part_t *init_part_order_gpt(const disk_t *disk_car, list_part_t *list_part);
static void set_next_status_gpt(const disk_t *disk_car, partition_t *partition);
static int test_structure_gpt(list_part_t *list_part);
static int is_part_known_gpt(const partition_t *partition);
static void init_structure_gpt(const disk_t *disk_car,list_part_t *list_part, const int verbose);
static const char *get_partition_typename_gpt(const partition_t *partition);
static const char *get_gpt_typename(const efi_guid_t part_type_gpt);

const struct systypes_gtp gpt_sys_types[] = {
  { GPT_ENT_TYPE_EFI, 			"EFI System"		},
  { GPT_ENT_TYPE_MBR,			"MBR"			},
  { GPT_ENT_TYPE_FREEBSD,		"FreeBSD"		},
  { GPT_ENT_TYPE_FREEBSD_SWAP,		"FreeBSD Swap"		},
  { GPT_ENT_TYPE_FREEBSD_UFS,		"FreeBSD UFS"		},
  { GPT_ENT_TYPE_FREEBSD_VINUM,		"FreeBSD Vinum"		},
//  { GPT_ENT_TYPE_FREEBSD_UFS2,		"FreeBSD UFS2"		},
  { GPT_ENT_TYPE_FREEBSD_ZFS,		"FreeBSD ZFS"		},
  { GPT_ENT_TYPE_MS_RESERVED,		"MS Reserved"		},
  { GPT_ENT_TYPE_MS_BASIC_DATA,		"MS Data"		},
  { GPT_ENT_TYPE_MS_LDM_METADATA,	"MS LDM MetaData"	},
  { GPT_ENT_TYPE_MS_LDM_DATA,		"MS LDM Data"		},
//  { GPT_ENT_TYPE_LINUX_DATA
  { GPT_ENT_TYPE_LINUX_RAID,		"Linux Raid"		},
  { GPT_ENT_TYPE_LINUX_SWAP,		"Linux Swap"		},
  { GPT_ENT_TYPE_LINUX_LVM,		"Linux LVM"		},
  { GPT_ENT_TYPE_LINUX_RESERVED,	"Linux Reserved"	},
  { GPT_ENT_TYPE_HPUX_DATA,		"HPUX Data"		},
  { GPT_ENT_TYPE_HPUX_SERVICE,		"HPUX Service"		},
  { GPT_ENT_TYPE_MAC_HFS,		"Mac HFS"		},
  { GPT_ENT_TYPE_MAC_UFS,		"Mac UFS"		},
  { GPT_ENT_TYPE_MAC_RAID,		"Mac Raid"		},
  { GPT_ENT_TYPE_MAC_RAID_OFFLINE,	"Mac Raid (Offline)"	},
  { GPT_ENT_TYPE_MAC_BOOT,		"Mac Boot"		},
  { GPT_ENT_TYPE_MAC_LABEL,		"Mac Label"		},
  { GPT_ENT_TYPE_MAC_TV_RECOVERY,	"Mac TV Recovery"	},
  { GPT_ENT_TYPE_SOLARIS_BOOT,		"Solaris /boot"		},
  { GPT_ENT_TYPE_SOLARIS_ROOT,		"Solaris /"		},
  { GPT_ENT_TYPE_SOLARIS_SWAP,		"Solaris Swap"		},
  { GPT_ENT_TYPE_SOLARIS_BACKUP,	"Solaris Backup"	},
  { GPT_ENT_TYPE_SOLARIS_USR,		"Solaris /usr"		},
  { GPT_ENT_TYPE_SOLARIS_VAR,		"Solaris /var"		},
  { GPT_ENT_TYPE_SOLARIS_HOME,		"Solaris /home"		},
  { GPT_ENT_TYPE_SOLARIS_EFI_ALTSCTR,	"Solaris EFI Alt."	},
  { GPT_ENT_TYPE_SOLARIS_RESERVED1,	"Solaris Reserved1"	},
  { GPT_ENT_TYPE_SOLARIS_RESERVED2,	"Solaris Reserved2"	},
  { GPT_ENT_TYPE_SOLARIS_RESERVED3,	"Solaris Reserved3"	},
  { GPT_ENT_TYPE_SOLARIS_RESERVED4,	"Solaris Reserved4"	},
  { GPT_ENT_TYPE_SOLARIS_RESERVED5,	"Solaris Reserved5"	},
  { GPT_ENT_TYPE_UNUSED,  NULL }
 };

arch_fnct_t arch_gpt=
{
  .part_name="EFI GPT",
  .part_name_option="partition_gpt",
  .msg_part_type="                P=Primary  D=Deleted",
  .read_part=read_part_gpt,
  .write_part=write_part_gpt,
  .init_part_order=init_part_order_gpt,
  .get_geometry_from_mbr=NULL,
  .check_part=check_part_gpt,
  .write_MBR_code=NULL,
  .set_prev_status=set_next_status_gpt,
  .set_next_status=set_next_status_gpt,
  .test_structure=test_structure_gpt,
  .get_part_type=NULL,
  .set_part_type=NULL,
  .init_structure=init_structure_gpt,
  .erase_list_part=NULL,
  .get_partition_typename=get_partition_typename_gpt,
  .is_part_known=&is_part_known_gpt
};

list_part_t *read_part_gpt(disk_t *disk_car, const int verbose, const int saveheader)
{
  struct gpt_hdr *gpt;
  struct gpt_ent* gpt_entries;
  list_part_t *new_list_part=NULL;
  unsigned int i;
  uint32_t gpt_entries_size;
  uint64_t gpt_entries_offset;

  gpt=(struct gpt_hdr*)MALLOC(disk_car->sector_size);
  screen_buffer_reset();
  if((unsigned)disk_car->pread(disk_car, gpt, disk_car->sector_size, disk_car->sector_size) != disk_car->sector_size)
  {
    free(gpt);
    return NULL;
  }
  if(memcmp(gpt->hdr_sig, GPT_HDR_SIG, 8)!=0)
  {
    screen_buffer_add("Bad GPT partition, invalid signature.\n");
    free(gpt);
    return NULL;
  }
  if(verbose>0)
  {
    log_info("hdr_size=%llu\n", (long long unsigned)le32(gpt->hdr_size));
    log_info("hdr_lba_self=%llu\n", (long long unsigned)le64(gpt->hdr_lba_self));
    log_info("hdr_lba_alt=%llu (expected %llu)\n",
	(long long unsigned)le64(gpt->hdr_lba_alt),
	(long long unsigned)((disk_car->disk_size-1)/disk_car->sector_size));
    log_info("hdr_lba_start=%llu\n", (long long unsigned)le64(gpt->hdr_lba_start));
    log_info("hdr_lba_end=%llu\n", (long long unsigned)le64(gpt->hdr_lba_end));
    log_info("hdr_lba_table=%llu\n",
	(long long unsigned)le64(gpt->hdr_lba_table));
    log_info("hdr_entries=%llu\n", (long long unsigned)le32(gpt->hdr_entries));
    log_info("hdr_entsz=%llu\n", (long long unsigned)le32(gpt->hdr_entsz));
  }
  /* Check header size */
  if(le32(gpt->hdr_size)<92 || le32(gpt->hdr_size) > disk_car->sector_size)
  {
    screen_buffer_add("GPT: invalid header size.\n");
    free(gpt);
    return NULL;
  }
  { /* CRC check */
    uint32_t crc;
    uint32_t origcrc;
    origcrc=le32(gpt->hdr_crc_self);
    gpt->hdr_crc_self=le32(0);
    crc=get_crc32(gpt, le32(gpt->hdr_size), 0xFFFFFFFF)^0xFFFFFFFF;
    if(crc!=origcrc)
    {
      screen_buffer_add("Bad GPT partition, invalid header checksum.\n");
      free(gpt);
      return NULL;
    }
    gpt->hdr_crc_self=le32(origcrc);
  }
  if(le64(gpt->hdr_lba_self)!=1)
  {
    screen_buffer_add("Bad GPT partition, invalid LBA self location.\n");
    free(gpt);
    return NULL;
  }
  if(le64(gpt->hdr_lba_start) >= le64(gpt->hdr_lba_end))
  {
    screen_buffer_add("Bad GPT partition, invalid LBA start/end location.\n");
    free(gpt);
    return NULL;
  }
  if(le32(gpt->hdr_revision)!=GPT_HDR_REVISION)
  {
    screen_buffer_add("GPT: Warning - not revision 1.0\n");
  }
  if(le32(gpt->__reserved)!=0)
  {
    screen_buffer_add("GPT: Warning - __reserved!=0\n");
  }
  if(le32(gpt->hdr_entries)==0 || le32(gpt->hdr_entries)>4096)
  {
    screen_buffer_add("GPT: invalid number (%u) of partition entries.\n",
        (unsigned int)le32(gpt->hdr_entries));
    free(gpt);
    return NULL;
  }
  /* le32(gpt->hdr_entsz)==128 */
  if(le32(gpt->hdr_entsz)%8!=0 || le32(gpt->hdr_entsz)<128 || le32(gpt->hdr_entsz)>4096)
  {
    screen_buffer_add("GPT: invalid partition entry size.\n");
    free(gpt);
    return NULL;
  }

  gpt_entries_size=le32(gpt->hdr_entries) * le32(gpt->hdr_entsz);
  if(gpt_entries_size<16384)
  {
    screen_buffer_add("GPT: A minimum of 16,384 bytes of space must be reserved for the GUID Partition Entry array.\n");
    free(gpt);
    return NULL;
  }
  gpt_entries_offset=(uint64_t)le64(gpt->hdr_lba_table) * disk_car->sector_size;
  if((uint64_t) le64(gpt->hdr_lba_self) + le32(gpt->hdr_size) - 1 >= gpt_entries_offset ||
      gpt_entries_offset >= le64(gpt->hdr_lba_start) * disk_car->sector_size)
  {
    screen_buffer_add( "GPT: The primary GUID Partition Entry array must be located after the primary GUID Partition Table Header and end before the FirstUsableLBA.\n");
    free(gpt);
    return NULL;
  }

  gpt_entries=(struct gpt_ent*)MALLOC(gpt_entries_size);
  if((unsigned)disk_car->pread(disk_car, gpt_entries, gpt_entries_size, gpt_entries_offset) != gpt_entries_size)
  {
    free(gpt_entries);
    free(gpt);
    return new_list_part;
  }
  { /* CRC check */
    uint32_t crc;
    crc=get_crc32(gpt_entries, gpt_entries_size, 0xFFFFFFFF)^0xFFFFFFFF;
    if(crc!=le32(gpt->hdr_crc_table))
    {
      screen_buffer_add("Bad GPT partition entries, invalid checksum.\n");
      free(gpt_entries);
      free(gpt);
      return NULL;
    }
  }
  for(i=0;i<le32(gpt->hdr_entries);i++)
  {
    const struct gpt_ent* gpt_entry;
    gpt_entry=(const struct gpt_ent*)((const char*)gpt_entries + (unsigned long)i*le32(gpt->hdr_entsz));
    if(guid_cmp(gpt_entry->ent_type, GPT_ENT_TYPE_UNUSED)!=0 &&
        le64(gpt_entry->ent_lba_start) < le64(gpt_entry->ent_lba_end))
    {
      int insert_error=0;
      partition_t *new_partition=partition_new(&arch_gpt);
      new_partition->order=i+1;
      guid_cpy(&new_partition->part_uuid, &gpt_entry->ent_uuid);
      guid_cpy(&new_partition->part_type_gpt, &gpt_entry->ent_type);
      new_partition->part_offset=(uint64_t)le64(gpt_entry->ent_lba_start)*disk_car->sector_size;
      new_partition->part_size=(uint64_t)(le64(gpt_entry->ent_lba_end) -
          le64(gpt_entry->ent_lba_start)+1) * disk_car->sector_size;
      new_partition->status=STATUS_PRIM;
      UCSle2str(new_partition->partname, (const uint16_t *)&gpt_entry->ent_name, sizeof(gpt_entry->ent_name)/2);
      new_partition->arch->check_part(disk_car,verbose,new_partition,saveheader);
      /* log_debug("%u ent_attr %08llx\n", new_partition->order, (long long unsigned)le64(gpt_entry->ent_attr)); */
      aff_part_buffer(AFF_PART_ORDER|AFF_PART_STATUS,disk_car,new_partition);
      new_list_part=insert_new_partition(new_list_part, new_partition, 0, &insert_error);
      if(insert_error>0)
        free(new_partition);
    }
  }
  /* TODO: The backup GUID Partition Entry array must be
     located after the LastUsableLBA and end before the backup GUID Partition Table Header.
   */
  free(gpt_entries);
  free(gpt);
  return new_list_part;
}

static list_part_t *init_part_order_gpt(const disk_t *disk_car, list_part_t *list_part)
{
  list_part_t *element;
  unsigned int order=1;
  for(element=list_part;element!=NULL;element=element->next)
  {
    if(element->part->part_size>0 &&
        guid_cmp(element->part->part_type_gpt, GPT_ENT_TYPE_UNUSED)!=0)
      element->part->order=order++;
  }
  return list_part;
}

list_part_t *add_partition_gpt_cli(disk_t *disk_car,list_part_t *list_part, char **current_cmd)
{
  partition_t *new_partition=partition_new(&arch_gpt);
  new_partition->part_offset=disk_car->sector_size;
  new_partition->part_size=disk_car->disk_size-new_partition->part_offset;
  while(*current_cmd[0]==',')
    (*current_cmd)++;
  while(1)
  {
    if(strncmp(*current_cmd,"s,",2)==0)
    {
      uint64_t part_offset;
      (*current_cmd)+=2;
      part_offset=new_partition->part_offset;
      new_partition->part_offset=(uint64_t)ask_number_cli(
          current_cmd,
          new_partition->part_offset/disk_car->sector_size,
          1,
          (disk_car->disk_size-1)/disk_car->sector_size,
          "Enter the starting sector ") *
        (uint64_t)disk_car->sector_size;
      new_partition->part_size=new_partition->part_size + part_offset - new_partition->part_offset;
    }
    else if(strncmp(*current_cmd,"S,",2)==0)
    {
      (*current_cmd)+=2;
      new_partition->part_size=(uint64_t)ask_number_cli(
          current_cmd,
          (new_partition->part_offset+new_partition->part_size-1)/disk_car->sector_size,
          new_partition->part_offset/disk_car->sector_size,
          (disk_car->disk_size-1)/disk_car->sector_size,
          "Enter the ending sector ") *
        (uint64_t)disk_car->sector_size +
        disk_car->sector_size - new_partition->part_offset;
    }
    else if(strncmp(*current_cmd,"T,",2)==0)
    {
      (*current_cmd)+=2;
      change_part_type_cli(disk_car,new_partition,current_cmd);
    }
    else if(new_partition->part_size>0 && guid_cmp(new_partition->part_type_gpt, GPT_ENT_TYPE_UNUSED)!=0)
    {
      int insert_error=0;
      list_part_t *new_list_part=insert_new_partition(list_part, new_partition, 0, &insert_error);
      if(insert_error>0)
      {
        free(new_partition);
        return new_list_part;
      }
      new_partition->status=STATUS_PRIM;
      if(test_structure_gpt(list_part)!=0)
        new_partition->status=STATUS_DELETED;
      return new_list_part;
    }
    else
    {
      free(new_partition);
      return list_part;
    }
  }
}

static void set_next_status_gpt(const disk_t *disk_car, partition_t *partition)
{
  if(partition->status==STATUS_DELETED)
    partition->status=STATUS_PRIM;
  else
    partition->status=STATUS_DELETED;
}

static int test_structure_gpt(list_part_t *list_part)
{ /* Return 1 if bad*/
  int res;
  list_part_t *new_list_part;
  new_list_part=gen_sorted_partition_list(list_part);
  res=is_part_overlapping(new_list_part);
  part_free_list_only(new_list_part);
  return res;
}

static int is_part_known_gpt(const partition_t *partition)
{
  return (guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_UNUSED)!=0);
}

static void init_structure_gpt(const disk_t *disk_car,list_part_t *list_part, const int verbose)
{
  list_part_t *element;
  list_part_t *new_list_part=NULL;
  /* Create new list */
  for(element=list_part;element!=NULL;element=element->next)
    element->to_be_removed=0;
  for(element=list_part;element!=NULL;element=element->next)
  {
    int insert_error=0;
    list_part_t *element2;
    for(element2=element->next;element2!=NULL;element2=element2->next)
    {
      if(element->part->part_offset+element->part->part_size-1 >= element2->part->part_offset)
      {
        element->to_be_removed=1;
        element2->to_be_removed=1;
      }
    }
    if(element->to_be_removed==0)
      new_list_part=insert_new_partition(new_list_part, element->part, 0, &insert_error);
  }
    for(element=new_list_part;element!=NULL;element=element->next)
      element->part->status=STATUS_PRIM;
  if(disk_car->arch->test_structure(new_list_part))
  {
    for(element=new_list_part;element!=NULL;element=element->next)
      element->part->status=STATUS_DELETED;
  }
  part_free_list_only(new_list_part);
}

static int check_part_gpt(disk_t *disk, const int verbose,partition_t *partition, const int saveheader)
{
  int ret=0;
  unsigned int old_levels;
  old_levels=log_set_levels(0);
  if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_MS_BASIC_DATA)==0 ||
      guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_MS_RESERVED)==0)
  {
    ret=check_FAT(disk,partition,verbose);
    if(ret!=0)
      ret=check_EXFAT(disk, partition);
    if(ret!=0)
      ret=check_NTFS(disk,partition,verbose,0);
    if(ret!=0)
      ret=check_linux(disk, partition, verbose);
    if(ret!=0)
      screen_buffer_add("No FAT, NTFS, ext2, JFS, Reiser, cramfs or XFS marker\n"); 
  }
  else if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_LINUX_RAID)==0)
  {
    ret=check_MD(disk, partition, verbose);
    if(ret!=0)
      screen_buffer_add("Invalid RAID superblock\n");
  }
  else if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_LINUX_LVM)==0)
  {
    ret=check_LVM(disk, partition, verbose);
    if(ret!=0)
      ret=check_LVM2(disk, partition, verbose);
    if(ret!=0)
      screen_buffer_add("No LVM or LVM2 structure\n");
  }
  else if(guid_cmp(partition->part_type_gpt, GPT_ENT_TYPE_MAC_HFS)==0)
  {
    ret=check_HFS(disk, partition, verbose);
    if(ret!=0)
      ret=check_HFSP(disk, partition, verbose);
    if(ret!=0)
      screen_buffer_add("No HFS or HFS+ structure\n");
  }
  log_set_levels(old_levels);
  if(ret!=0)
  {
    log_error("check_part_gpt failed for partition\n");
    log_partition(disk, partition);
    aff_part_buffer(AFF_PART_ORDER|AFF_PART_STATUS,disk,partition);
    if(saveheader>0)
    {
      save_header(disk, partition, verbose);
    }
  }
  return ret;
}

static const char *get_gpt_typename(const efi_guid_t part_type_gpt)
{
  int i;
  for(i=0; gpt_sys_types[i].name!=NULL; i++)
    if(guid_cmp(gpt_sys_types[i].part_type, part_type_gpt)==0)
      return gpt_sys_types[i].name;
  return NULL;
}

static const char *get_partition_typename_gpt(const partition_t *partition)
{
  return get_gpt_typename(partition->part_type_gpt);
}