/*

    File: partmac.c

    Copyright (C) 1998-2007 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
 
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <ctype.h>      /* tolower */
#include "types.h"
#include "common.h"
#include "testdisk.h"
#include "fnctdsk.h"
#include "lang.h"
#include "intrf.h"
#include "intrfn.h"
#include "chgtype.h"
#include "partmac.h"
#include "savehdr.h"
#include "cramfs.h"
#include "ext2.h"
#include "hfs.h"
#include "hfsp.h"
#include "jfs_superblock.h"
#include "jfs.h"
#include "rfs.h"
#include "xfs.h"
#include "log.h"

static int check_part_mac(disk_t *disk_car, const int debug,partition_t *partition,const int saveheader);
static list_part_t *read_part_mac(disk_t *disk_car, const int debug, const int saveheader);
static int write_part_mac(disk_t *disk_car, const list_part_t *list_part, const int ro , const int debug, const int align);
static list_part_t *init_part_order_mac(const disk_t *disk_car, list_part_t *list_part);
static list_part_t *add_partition_mac(disk_t *disk_car,list_part_t *list_part, const int debug, char **current_cmd);
static void set_next_status_mac(const disk_t *disk_car, partition_t *partition);
static int test_structure_mac(list_part_t *list_part);
static int set_part_type_mac(partition_t *partition, unsigned int part_type_mac);
static int is_part_known_mac(const partition_t *partition);
static void init_structure_mac(const disk_t *disk_car,list_part_t *list_part, const int debug);
static const char *get_partition_name_mac(const partition_t *partition);
static const char *get_partition_name_mac_aux(const unsigned int part_type_mac);
static unsigned int get_part_type_mac(const partition_t *partition);

static const struct systypes mac_sys_types[] = {
  { PMAC_DRIVER43,  "Driver43"		},
  { PMAC_DRIVERATA,  "Driver_ATA"		},
  { PMAC_DRIVERIO,  "Driver_IOKit"	},
  { PMAC_FREE,  "Free"		},
  { PMAC_FWDRIVER,  "FWDriver"		},
  { PMAC_SWAP, "Swap"		},
  { PMAC_LINUX, "Linux"		},
  { PMAC_HFS, "HFS"		},
  { PMAC_MAP,  "partition_map"	},
  { PMAC_PATCHES,  "Patches"		},
  { PMAC_UNK,  "Unknown"		},
  { PMAC_NewWorld,  "NewWorld"		},
  { PMAC_DRIVER, "Driver"		},
  { PMAC_MFS, "MFS"		},
  { PMAC_PRODOS, "ProDOS"		},
  { PMAC_UNK,		NULL }
};

arch_fnct_t arch_mac=
{
  .part_name="Mac",
  .part_name_option="partition_mac",
  .msg_part_type="                P=Primary  D=Deleted",
  .read_part=read_part_mac,
  .write_part=write_part_mac,
  .init_part_order=init_part_order_mac,
  .get_geometry_from_mbr=NULL,
  .check_part=check_part_mac,
  .write_MBR_code=NULL,
  .add_partition=add_partition_mac,
  .set_prev_status=set_next_status_mac,
  .set_next_status=set_next_status_mac,
  .test_structure=test_structure_mac,
  .set_part_type=set_part_type_mac,
  .is_part_known=is_part_known_mac,
  .init_structure=init_structure_mac,
  .erase_list_part=NULL,
  .get_partition_name=get_partition_name_mac,
  .get_part_type=get_part_type_mac
};

static unsigned int get_part_type_mac(const partition_t *partition)
{
  return partition->part_type_mac;
}

list_part_t *read_part_mac(disk_t *disk_car, const int debug, const int saveheader)
{
  unsigned char buffer[DEFAULT_SECTOR_SIZE];
  list_part_t *new_list_part=NULL;
  unsigned int i;
  unsigned int limit=1;
  aff_buffer(BUFFER_RESET,"Q");
  if(disk_car->read(disk_car,sizeof(buffer), &buffer, 0)!=0)
    return new_list_part;
  {
    mac_Block0 *maclabel=(mac_Block0*)&buffer;
    if (be16(maclabel->sbSig) != BLOCK0_SIGNATURE)
    {
      aff_buffer(BUFFER_ADD,"\nBad MAC partition, invalid block0 signature\n");
      return NULL;
    }
  }
  for(i=1;i<=limit;i++)
  {
    mac_DPME *dpme=(mac_DPME *)buffer;
    if(disk_car->read(disk_car,sizeof(buffer), &buffer, (uint64_t)i*PBLOCK_SIZE)!=0)
      return new_list_part;
    if(be16(dpme->dpme_signature) != DPME_SIGNATURE)
    {
      aff_buffer(BUFFER_ADD,"\nread_part_mac: bad DPME signature");
      return new_list_part;
    }
    {
      partition_t *new_partition=partition_new();
      partition_reset(new_partition);
      new_partition->order=i;
      if (strcmp(dpme->dpme_type, "Apple_UNIX_SVR2")==0)
      {
	if (!strcmp(dpme->dpme_name, "Swap") || !strcmp(dpme->dpme_name, "swap"))
	  new_partition->part_type_mac=PMAC_SWAP;
	else
	  new_partition->part_type_mac=PMAC_LINUX;
      }
      else if (strcmp(dpme->dpme_type, "Apple_Bootstrap")==0)
	new_partition->part_type_mac=PMAC_NewWorld;
      else if (strcmp(dpme->dpme_type, "Apple_Scratch")==0)
	new_partition->part_type_mac=PMAC_SWAP;
      else if(strcmp(dpme->dpme_type,"Apple_Driver")==0)
	new_partition->part_type_mac=PMAC_DRIVER;
      else if(strcmp(dpme->dpme_type,"Apple_Driver43")==0)
	new_partition->part_type_mac=PMAC_DRIVER43;
      else if(strcmp(dpme->dpme_type,"Apple_Driver_ATA")==0)
	new_partition->part_type_mac=PMAC_DRIVERATA;
      else if(strcmp(dpme->dpme_type,"Apple_Driver_IOKit")==0)
	new_partition->part_type_mac=PMAC_DRIVERIO;
      else if(strcmp(dpme->dpme_type,"Apple_Free")==0)
	new_partition->part_type_mac=PMAC_FREE;
      else if(strcmp(dpme->dpme_type,"Apple_FWDriver")==0)
	new_partition->part_type_mac=PMAC_FWDRIVER;
      else if(strcmp(dpme->dpme_type,"Apple_partition_map")==0)
	new_partition->part_type_mac=PMAC_MAP;
      else if(strcmp(dpme->dpme_type,"Apple_Patches")==0)
	new_partition->part_type_mac=PMAC_PATCHES;
      else if(strcmp(dpme->dpme_type,"Apple_HFS")==0)
	new_partition->part_type_mac=PMAC_HFS;
      else if(strcmp(dpme->dpme_type,"Apple_MFS")==0)
	new_partition->part_type_mac=PMAC_MFS;
      else if(strcmp(dpme->dpme_type,"Apple_PRODOS")==0)
	new_partition->part_type_mac=PMAC_PRODOS;
      else
      {
	new_partition->part_type_mac=PMAC_UNK;
	log_error("%s\n",dpme->dpme_type);
      }
      new_partition->part_offset=(uint64_t)be32(dpme->dpme_pblock_start)*PBLOCK_SIZE;
      new_partition->part_size=(uint64_t)be32(dpme->dpme_pblocks)*PBLOCK_SIZE;
      new_partition->status=STATUS_PRIM;
      disk_car->arch->check_part(disk_car,debug,new_partition,saveheader);
      new_list_part=insert_new_partition(new_list_part,new_partition);
      aff_part_buffer(AFF_PART_ORDER,disk_car,new_partition);
    }
    if(i==1)
    {
      limit=be32(dpme->dpme_map_entries);
    }
  }
  return new_list_part;
}

static int write_part_mac(disk_t *disk_car, const list_part_t *list_part, const int ro, const int debug, const int align)
{ /* TODO: Implement it */
  if(ro==0)
  {
    /* not_implemented("write_part_mac"); */
    WINDOW *window=newwin(0,0,0,0);	/* full screen */
    aff_copy(window);
    wmove(window,7,0);
    wdoprintf(window,"Function write_part_mac not implemented");
    log_warning("Function write_part_mac not implemented\n");
    wmove(window,8,0);
    wdoprintf(window,"Use pdisk to recreate the missing partition");
    wmove(window,9,0);
    wdoprintf(window,"using values displayed by TestDisk");
    wmove(window,22,0);
    wattrset(window, A_REVERSE);
    wdoprintf(window,"[ Abort ]");
    wattroff(window, A_REVERSE);
    wrefresh(window);
    while(wgetch(window)==ERR);
    delwin(window);
    (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
    touchwin(stdscr);
#endif
  }
  return 0;
}

static list_part_t *init_part_order_mac(const disk_t *disk_car, list_part_t *list_part)
{
  return list_part;
}

static list_part_t *add_partition_mac(disk_t *disk_car,list_part_t *list_part, const int debug, char **current_cmd)
{
  int position=0;
  int done = FALSE;
  partition_t *new_partition=partition_new();
  new_partition->part_offset=disk_car->sector_size;
  new_partition->part_size=disk_car->disk_size-disk_car->sector_size;
  while (done==FALSE)
  {
    int command;
    static struct MenuItem menuGeometry[]=
    {
      { 's', "Sector", 	"Change starting sector" },
      { 'S', "Sector", 	"Change ending sector" },
      { 'T' ,"Type",		"Change partition type"},
      { 'd', "Done", "" },
      { 0, NULL, NULL }
    };
    aff_copy(stdscr);
    wmove(stdscr,4,0);
    wdoprintf(stdscr,"%s",disk_car->description(disk_car));
    wmove(stdscr,10, 0);
    wclrtoeol(stdscr);
    aff_part(stdscr,AFF_PART_SHORT,disk_car,new_partition);
    wmove(stdscr,INTER_GEOM_Y, INTER_GEOM_X);
    wclrtoeol(stdscr);
    wrefresh(stdscr);
    command=wmenuSimple(stdscr,menuGeometry, position);
    switch (command) {
      case 's':
	{
	  uint64_t part_offset;
	  part_offset=new_partition->part_offset;
	  wmove(stdscr, INTER_GEOM_Y, INTER_GEOM_X);
	  new_partition->part_offset=(uint64_t)ask_number(
	      new_partition->part_offset/disk_car->sector_size,
	      disk_car->sector_size,
	      (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;
	  position=1;
	}
	break;
      case 'S':
	wmove(stdscr, INTER_GEOM_Y, INTER_GEOM_X);
	new_partition->part_size=(uint64_t)ask_number(
	      (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;
	position=2;
	break;
      case 'T':
      case 't':
	change_part_type(disk_car,new_partition,current_cmd);
	position=3;
	break;
      case key_ESC:
      case 'd':
      case 'D':
      case 'q':
      case 'Q':
	done = TRUE;
	break;
    }
  }
  if(new_partition->part_size>0 && new_partition->part_type_mac>0)
  {
    list_part_t *new_list_part=insert_new_partition(list_part, new_partition);
    new_partition->status=STATUS_PRIM;
    if(test_structure_mac(list_part)!=0)
    {
      new_partition->status=STATUS_DELETED;
    }
    return new_list_part;
  }
  free(new_partition);
  return list_part;
}

static void set_next_status_mac(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_mac(list_part_t *list_part)
{ /* Return 1 if bad*/
  list_part_t *new_list_part=NULL;
  list_part_t *element;
  list_part_t *new_element;
  int res=0;
  /* Sort list_part in new_list_part */
  for(element=list_part;element!=NULL;element=element->next)
  {
    if(element->part->status!=STATUS_DELETED)
      new_list_part=insert_new_partition(new_list_part,element->part);
  }
  /* Test overlapping */
  for(element=new_list_part;element!=NULL;element=element->next)
  {
    if(((element->prev!=NULL) && (element->part->part_offset<=element->prev->part->part_offset+element->prev->part->part_size-1)) ||
	((element->next!=NULL) && (element->part->part_offset+element->part->part_size-1>=element->next->part->part_offset)))
    {
      res=1;
    }
  }
  for(element=new_list_part;element!=NULL;element=new_element)
  {
    new_element=element->next;
    free(element);
  }
  return res;
}

static int set_part_type_mac(partition_t *partition, unsigned int part_type_mac)
{
  if(part_type_mac>0 && part_type_mac <= 255)
  {
    partition->part_type_mac=part_type_mac;
    return 0;
  }
  return 1;
}

static int is_part_known_mac(const partition_t *partition)
{
  return (partition->part_type_mac!=PMAC_UNK);
}

static void init_structure_mac(const disk_t *disk_car,list_part_t *list_part, const int debug)
{
  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 to_be_removed=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)
      {
	to_be_removed=1;
	element2->to_be_removed=1;
      }
    if(to_be_removed)
      element->to_be_removed=1;
    if(element->to_be_removed==0)
      new_list_part=insert_new_partition_aux(new_list_part,element_new(element->part),1);
  }
#ifdef DEBUG
  check_list_part(new_list_part);
#endif
    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;
  }
  {
    /* free */
    list_part_t *new_element;
    for(element=new_list_part;element!=NULL;element=new_element)
    {
      new_element=element->next;
      free(element);
    }
  }
#ifdef DEBUG
  check_list_part(list_part);
#endif
}

static int check_part_mac(disk_t *disk_car,const int debug,partition_t *partition, const int saveheader)
{
  int ret=0;
  switch(partition->part_type_mac)
  {
    case PMAC_DRIVER43:
    case PMAC_DRIVERATA:
    case PMAC_DRIVERIO:
    case PMAC_FREE:
    case PMAC_FWDRIVER:
    case PMAC_SWAP:
    case PMAC_MAP:
    case PMAC_PATCHES:
    case PMAC_UNK:
    case PMAC_NewWorld:
    case PMAC_DRIVER:
    case PMAC_MFS:
    case PMAC_PRODOS:
      break;
    case PMAC_LINUX:
      ret=check_JFS(disk_car,partition,debug);
      if(ret!=0)
      {
	ret=check_rfs(disk_car,partition,debug);
      }
      if(ret!=0)
      {
	ret=check_EXT2(disk_car,partition,debug);
      }
      if(ret!=0)
      {
	ret=check_cramfs(disk_car,partition,debug);
      }
      if(ret!=0)
      {
	ret=check_xfs(disk_car,partition,debug);
      }
      if(ret!=0)
      { aff_buffer(BUFFER_ADD,"No EXT2, JFS, Reiser, cramfs or XFS marker\n"); }
      break;
    case PMAC_HFS:
      ret=check_HFSP(disk_car,partition,debug);
      if(ret!=0)
      {
	ret=check_HFS(disk_car,partition,debug);
      }
      break;
    default:
      if(debug>0)
      {
	log_info("check_part_mac %u type %02X: no test\n",partition->order,partition->part_type_mac);
      }
      break;
  }
  if(ret!=0)
  {
    log_error("check_part_mac failed for partition type %02X\n", partition->part_type_mac);
    aff_part_buffer(AFF_PART_ORDER,disk_car,partition);
    if(saveheader>0)
    {
      save_header(disk_car,partition,debug);
    }
  }
  return ret;
}

static const char *get_partition_name_mac_aux(const unsigned int part_type_mac)
{
  int i;
  for (i=0; mac_sys_types[i].name!=NULL; i++)
    if (mac_sys_types[i].part_type == part_type_mac)
      return mac_sys_types[i].name;
  return NULL;
}

static const char *get_partition_name_mac(const partition_t *partition)
{
  return get_partition_name_mac_aux(partition->part_type_mac);
}


syntax highlighted by Code2HTML, v. 0.9.1