/*

    File: ntfs_adv.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_STRING_H
#include <string.h>
#endif
#include <ctype.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "types.h"
#include "common.h"
#include "intrf.h"
#include "intrfn.h"
#include "dirpart.h"
#include "ntfs.h"
#include "fnctdsk.h"
#include "lang.h"
#include "io_redir.h"
#include "log.h"

#define INTER_NTFS_X 0
#define INTER_NTFS_Y 23

#define MAX_INFO_MFT 10

typedef struct s_info_mft info_mft_t;
struct s_info_mft
{
  uint64_t sector;
  uint64_t mft_lcn;
  uint64_t mftmirr_lcn;
};

static int create_ntfs_boot_sector(disk_t *disk_car, partition_t *partition, const int interface, const unsigned int cluster_size, const uint64_t mft_lcn, const uint64_t mftmirr_lcn, const uint32_t mft_record_size, const uint32_t index_block_size, char**current_cmd);
static int dump_2ntfs_info(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2);
static int dump_ntfs_info(const struct ntfs_boot_sector *ntfs_header);
static int testdisk_ffs(int x);
static int read_mft_info(disk_t *disk_car, partition_t *partition, const uint64_t mft_sector, const int debug, unsigned int *sectors_per_cluster, uint64_t *mft_lcn, uint64_t *mftmirr_lcn, unsigned int *mft_record_size);


static int create_ntfs_boot_sector(disk_t *disk_car, partition_t *partition, const int interface, const unsigned int cluster_size, const uint64_t mft_lcn, const uint64_t mftmirr_lcn, const uint32_t mft_record_size, const uint32_t index_block_size, char**current_cmd)
{
  unsigned char orgboot[DEFAULT_SECTOR_SIZE];
  unsigned char newboot[DEFAULT_SECTOR_SIZE];
  struct ntfs_boot_sector *org_ntfs_header=(struct ntfs_boot_sector *)&orgboot;
  struct ntfs_boot_sector *ntfs_header=(struct ntfs_boot_sector *)&newboot;
  int error=0;
  if(disk_car->read(disk_car,sizeof(orgboot), &orgboot, partition->part_offset)!=0)
  {
    log_error("create_ntfs_boot_sector: Can't read boot sector.\n");
    memset(&orgboot,0,sizeof(orgboot));
  }
  if(cluster_size==0)
  {
    error=1;
  }
  if(error)
  {
    display_message("NTFS Bad extrapolation.\n");
    return 1;
  }
  memcpy(&newboot,&orgboot,sizeof(newboot));
  memcpy(ntfs_header->system_id,"NTFS    ",8);
  ntfs_header->sector_size[0]=disk_car->sector_size & 0xFF;
  ntfs_header->sector_size[1]=disk_car->sector_size>>8;
  ntfs_header->sectors_per_cluster=cluster_size/disk_car->sector_size;
  ntfs_header->reserved=0;
  ntfs_header->fats=0;
  ntfs_header->dir_entries[0]=0;
  ntfs_header->dir_entries[1]=0;
  ntfs_header->sectors[0]=0;
  ntfs_header->sectors[1]=0;
  ntfs_header->media=0xF8;
  ntfs_header->fat_length=le16(0);
  ntfs_header->secs_track=le16(disk_car->CHS.sector);
  ntfs_header->heads=le16(disk_car->CHS.head+1);
  /* absolute sector address from the beginning of the disk (!= FAT) */
  ntfs_header->hidden=le32(partition->part_offset/disk_car->sector_size);
  ntfs_header->total_sect=le32(0);
  ntfs_header->sectors_nbr=le64(partition->part_size/disk_car->sector_size-1);
  ntfs_header->mft_lcn=le64(mft_lcn);
  ntfs_header->mftmirr_lcn=le64(mftmirr_lcn);
  ntfs_header->clusters_per_mft_record=(mft_record_size >= cluster_size?mft_record_size / cluster_size:
      -(testdisk_ffs(mft_record_size) - 1));
  ntfs_header->clusters_per_index_record =(index_block_size >= cluster_size?index_block_size / cluster_size:
      -(testdisk_ffs(index_block_size) - 1));
  ntfs_header->reserved0[0]=0;
  ntfs_header->reserved0[1]=0;
  ntfs_header->reserved0[2]=0;
  ntfs_header->reserved1[0]=0;
  ntfs_header->reserved1[1]=0;
  ntfs_header->reserved1[2]=0;
  /*
  {
    uint32_t *u;
    uint32_t checksum;
    for (checksum = 0,u=(uint32_t*)ntfs_header; u < (uint32_t*)(&ntfs_header->checksum); u++)
      checksum += NTFS_GETU32(u);
    ntfs_header->checksum=checksum;
  }
  */
  ntfs_header->checksum=0;
  ntfs_header->marker=0xAA55;
  if(memcmp(newboot,orgboot,sizeof(newboot)))
  {
    log_warning("             New / Current boot sector\n");
    log_ntfs2_dump(ntfs_header,org_ntfs_header);
    log_warning("Extrapolated boot sector and current boot sector are different.\n");
  }
  else
  {
    log_info("Extrapolated boot sector and current boot sector are identical.\n");
  }
  /* */
  if(interface)
  {
    struct MenuItem menuSaveBoot[]=
    {
      { 'D', "Dump", "Dump sector" },
      { 'L', "List", "List directories and files" },
      { 'W', "Write","Write boot"},
      { 'Q',"Quit","Quit this section"},
      { 0, NULL, NULL }
    };
    const char *options="DLQ";
    int do_write=0;
    int do_exit=0;
    int no_confirm=0;
    int command;
    do
    {
      aff_copy(stdscr);
      wmove(stdscr,4,0);
      wdoprintf(stdscr,"%s",disk_car->description(disk_car));
      mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG);
      wmove(stdscr,6,0);
      aff_part(stdscr,AFF_PART_ORDER,disk_car,partition);
      wmove(stdscr,8,0);
      if(memcmp(newboot,orgboot,sizeof(newboot)))	/* Only compare the first sector */
      {
	options="DLWQ";
	dump_2ntfs_info(ntfs_header, org_ntfs_header);
	log_ntfs2_dump(ntfs_header, org_ntfs_header);
	wdoprintf(stdscr,"Extrapolated boot sector and current boot sector are different.\n");
	if(error)
	  log_error("Warning: Extrapolated boot sector have incorrect values.\n");
      }
      else
      {
	dump_ntfs_info(ntfs_header);
	log_ntfs_dump(ntfs_header);
	wdoprintf(stdscr,"Extrapolated boot sector and current boot sector are identical.\n");
      }

      if(*current_cmd!=NULL)
      {
	command='Q';
	while(*current_cmd[0]==',')
	  (*current_cmd)++;
	if(strncmp(*current_cmd,"list",4)==0)
	{
	  command='L';
	  (*current_cmd)+=4;
	}
	else if(strncmp(*current_cmd,"dump",4)==0)
	{
	  (*current_cmd)+=4;
	  command='D';
	}
	else if(strncmp(*current_cmd,"noconfirm",9)==0)
	{
	  command=0;	/* do nothing */
	  no_confirm=1;
	  (*current_cmd)+=9;
	}
      	else if(strncmp(*current_cmd,"write",5)==0)
	{
	  command='W';
	  (*current_cmd)+=5;
	}
      }
      else
	command=wmenuSelect(stdscr,INTER_DUMP_Y, INTER_DUMP_X, menuSaveBoot,8,options,MENU_HORIZ | MENU_BUTTON, 1);
      switch(command)
      {
	case 'w':
	case 'W':
	  if(strchr(options,'W')!=NULL)
	    do_write=1;
	  break;
	case 'd':
	case 'D':
	  if(strchr(options,'D')!=NULL)
	  {
	    WINDOW *window=newwin(0,0,0,0);	/* full screen */
	    keypad(window, TRUE); /* Need it to get arrow key */
	    aff_copy(window);
	    wmove(window,4,0);
	    wdoprintf(window,"%s",disk_car->description(disk_car));
	    wmove(window,5,0);
	    aff_part(window,AFF_PART_ORDER,disk_car,partition);
	    log_info("     Rebuild Boot sector           Boot sector\n");
	    mvwaddstr(window,6,0, "     Rebuild Boot sector           Boot sector");
	    dump2(window,newboot,orgboot, sizeof(newboot));
	    delwin(window);
	    (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
	    touchwin(stdscr);
#endif
	  }
	  break;
	case 'l':
	case 'L':
	  io_redir_add_redir(disk_car,partition->part_offset,sizeof(newboot),0,newboot);
	  dir_partition(disk_car, partition, 0,current_cmd);
	  io_redir_del_redir(disk_car,partition->part_offset);
	  break;
	case 'q':
	case 'Q':
	  do_exit=1;
	  break;
      }
    } while(do_write==0 && do_exit==0);
    if(do_write!=0 && (no_confirm!=0 || ask_confirmation("Write new NTFS boot sector, confirm ? (Y/N)")!=0))
    {
      log_info("Write new boot!\n");
      /* Write boot sector and backup boot sector */
      if(disk_car->write(disk_car,sizeof(newboot), &newboot, partition->part_offset))
      {
	display_message("Write error: Can't write new NTFS boot sector\n");
      }
      if(disk_car->write(disk_car,sizeof(newboot), &newboot, partition->part_offset+partition->part_size-disk_car->sector_size)!=0)
      {
	display_message("Write error: Can't write new NTFS backup boot sector\n");
      }
      return 0;
    }
  }
  log_info("Don't write new NTFS boot sector and backup boot sector!\n");
  return 1;
}

static int read_mft_info(disk_t *disk_car, partition_t *partition, const uint64_t mft_sector, const int debug, unsigned int *sectors_per_cluster, uint64_t *mft_lcn, uint64_t *mftmirr_lcn, unsigned int *mft_record_size)
{
  char buffer[8*DEFAULT_SECTOR_SIZE];
  const char *attr=buffer;
  if(disk_car->read(disk_car,sizeof(buffer), &buffer, partition->part_offset+(uint64_t)mft_sector*disk_car->sector_size)!=0)
  {
    display_message("NTFS: Can't read mft_sector\n");
    return 1;
  }
  *mft_lcn=ntfs_get_attr(attr,0x80,partition,buffer+8*DEFAULT_SECTOR_SIZE,debug,0,NULL);
  *mft_record_size=NTFS_GETU32(attr + 0x1C);
  if(*mft_record_size==0)
  {
    if(debug>0)
      log_warning("read_mft_info failed: mft_record_size=0\n");
    return 2;
  }
  attr+= NTFS_GETU32(attr + 0x1C);
  *mftmirr_lcn=ntfs_get_attr(attr,0x80,partition,buffer+8*DEFAULT_SECTOR_SIZE,debug,0,NULL);
  /* Try to divide by the biggest number first */
  if(*mft_lcn<*mftmirr_lcn)
  {
    if(*mftmirr_lcn>0 && mft_sector%(*mftmirr_lcn)==0)
    {
      *sectors_per_cluster=mft_sector/(*mftmirr_lcn);
      switch(*sectors_per_cluster)
      {
	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
	  return 0;
	default:
	  break;
      }
    }
    if(*mft_lcn>0 && mft_sector%(*mft_lcn)==0)
    {
      *sectors_per_cluster=mft_sector/(*mft_lcn);
      switch(*sectors_per_cluster)
      {
	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
	  return 0;
	default:
	  break;
      }
    }
  }
  else
  {
    if(*mft_lcn>0 && mft_sector%(*mft_lcn)==0)
    {
      *sectors_per_cluster=mft_sector/(*mft_lcn);
      switch(*sectors_per_cluster)
      {
	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
	  return 0;
	default:
	  break;
      }
    }
    if(*mftmirr_lcn>0 && mft_sector%(*mftmirr_lcn)==0)
    {
      *sectors_per_cluster=mft_sector/(*mftmirr_lcn);
      switch(*sectors_per_cluster)
      {
	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
	  return 0;
	default:
	  break;
      }
    }
  }
  if(debug>0)
  {
    log_warning("read_mft_info failed\n");
    log_warning("ntfs_find_mft: sectors_per_cluster invalid\n");
    log_warning("ntfs_find_mft: mft_lcn             %lu\n",(long unsigned int)*mft_lcn);
    log_warning("ntfs_find_mft: mftmirr_lcn         %lu\n",(long unsigned int)*mftmirr_lcn);
    log_warning("ntfs_find_mft: mft_record_size     %u\n",*mft_record_size);
    log_warning("\n");
  }
  *sectors_per_cluster=0;
  return 3;
}

int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int debug, const int dump_ind,const int interface, const unsigned int expert, char **current_cmd)
{
  uint64_t sector;
  char buffer[8*DEFAULT_SECTOR_SIZE];
  int ind_stop=FALSE;
  unsigned int sectors_per_cluster=0;
  uint64_t mft_lcn;
  uint64_t mftmirr_lcn;
  unsigned int mft_record_size=1024;
  info_mft_t info_mft[MAX_INFO_MFT];
  unsigned int nbr_mft=0;
  log_info("rebuild_NTFS_BS\n");
  if(interface)
  {
    aff_copy(stdscr);
    wmove(stdscr,4,0);
    wdoprintf(stdscr,"%s",disk_car->description(disk_car));
    mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG);
    wmove(stdscr,6,0);
    aff_part(stdscr,AFF_PART_ORDER,disk_car,partition);
    wmove(stdscr,22,0);
    wattrset(stdscr, A_REVERSE);
    waddstr(stdscr,"  Stop  ");
    wattroff(stdscr, A_REVERSE);
  }
  /* try to find MFT Backup first */
  for(sector=(partition->part_size/disk_car->sector_size/2-20>0?partition->part_size/disk_car->sector_size/2-20:1);(sector<partition->part_size/disk_car->sector_size)&&(sector<=partition->part_size/disk_car->sector_size/2+20)&&(ind_stop==FALSE);sector++)
  {
    if(disk_car->read(disk_car,2*DEFAULT_SECTOR_SIZE, &buffer, partition->part_offset+sector*(uint64_t)disk_car->sector_size)==0)
    {
      if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42)
	  &&(NTFS_GETU16(buffer+22)==1))	/* MFT_RECORD_IN_USE */
      {
	int res;
	res=ntfs_get_attr(buffer,0x30,partition,buffer+2*DEFAULT_SECTOR_SIZE,debug,0,"$MFT");
	if(res==1)
	{
	  int tmp;
	  log_info("mft at %lu, seq=%u, main=%u res=%d\n",(long unsigned)sector,NTFS_GETU8(buffer+0x10),(unsigned int)NTFS_GETU32(buffer+0x20),res);
	  tmp=read_mft_info(disk_car, partition, sector, debug, &sectors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size);
	  if(tmp==0)
	  {
	    log_info("ntfs_find_mft: mft_lcn             %lu\n",(long unsigned int)mft_lcn);
	    log_info("ntfs_find_mft: mftmirr_lcn         %lu\n",(long unsigned int)mftmirr_lcn);
	    if(expert==0 || ask_confirmation("Use MFT from %lu, confirm ? (Y/N)",(long unsigned int)mft_lcn)!=0)
	      ind_stop=TRUE;
	  }
	  else if(tmp==3)
	  {
	    if(nbr_mft<MAX_INFO_MFT)
	    {
	      info_mft[nbr_mft].sector=sector;
	      info_mft[nbr_mft].mft_lcn=mft_lcn;
	      info_mft[nbr_mft].mftmirr_lcn=mftmirr_lcn;
	      nbr_mft++;
	    }
	  }
	}
      }
    }
  }
  for(sector=1;(sector<partition->part_size/disk_car->sector_size)&&(ind_stop==FALSE);sector++)
  {
    if((interface!=0) &&(sector&0xffff)==0)
    {
      wmove(stdscr,9,0);
      wclrtoeol(stdscr);
      wdoprintf(stdscr,"Search mft %10lu/%lu", (long unsigned)sector,
	  (long unsigned)(partition->part_size/disk_car->sector_size));
      wrefresh(stdscr);
      if(check_enter_or_s(stdscr))
      {
	log_info("Search mft stopped: %10lu/%lu\n", (long unsigned)sector,
	    (long unsigned)(partition->part_size/disk_car->sector_size));
	ind_stop=TRUE;
      }
    }
    if(disk_car->read(disk_car,2*DEFAULT_SECTOR_SIZE, &buffer, partition->part_offset+sector*(uint64_t)disk_car->sector_size)==0)
    {
      if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42))
      {
	int res;
	res=ntfs_get_attr(buffer,0x30,partition,buffer+2*DEFAULT_SECTOR_SIZE,debug,0,"$MFT");
	if(res==1)
	{
	  int tmp;
	  log_info("mft at %lu, seq=%u, main=%u res=%d\n",(long unsigned)sector,NTFS_GETU8(buffer+0x10),(unsigned int)NTFS_GETU32(buffer+0x20),res);
	  tmp=read_mft_info(disk_car, partition, sector, debug, &sectors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size);
	  if(tmp==0)
	  {
	    log_info("ntfs_find_mft: mft_lcn             %lu\n",(long unsigned int)mft_lcn);
	    log_info("ntfs_find_mft: mftmirr_lcn         %lu\n",(long unsigned int)mftmirr_lcn);
	    if(expert==0 || ask_confirmation("Use MFT from %lu, confirm ? (Y/N)",(long unsigned int)mft_lcn)!=0)
	      ind_stop=TRUE;
	  }
	  else if(tmp==3)
	  {
	    if(nbr_mft<MAX_INFO_MFT)
	    {
	      info_mft[nbr_mft].sector=sector;
	      info_mft[nbr_mft].mft_lcn=mft_lcn;
	      info_mft[nbr_mft].mftmirr_lcn=mftmirr_lcn;
	      nbr_mft++;
	    }
	  }
	}
      }
    }
  }
  /* Find partition location using MFT information */
  {
    unsigned int i,j;
    int find_partition=0;
    uint64_t tmp=partition->part_offset;
    for(i=0;i<nbr_mft;i++)
    {
      for(j=i+1;j<nbr_mft;j++)
      {
	unsigned int sec_per_cluster=0;
	if(info_mft[i].mft_lcn > info_mft[j].mftmirr_lcn)
	{
	  if((info_mft[j].sector - info_mft[i].sector)%(info_mft[i].mft_lcn - info_mft[j].mftmirr_lcn)==0)
	    sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[i].mft_lcn - info_mft[j].mftmirr_lcn);
	}
	else if(info_mft[i].mft_lcn < info_mft[j].mftmirr_lcn)
	{
	  if((info_mft[j].sector - info_mft[i].sector)%(info_mft[j].mftmirr_lcn - info_mft[i].mft_lcn)==0)
	    sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[j].mftmirr_lcn - info_mft[i].mft_lcn);
	}
	if(sec_per_cluster!=0)
	{
	  partition->part_offset=partition->part_offset + (info_mft[i].sector -
	    info_mft[i].mft_lcn * sec_per_cluster) * disk_car->sector_size;
	  if(find_partition==0)
	    log_info("Potential partition:\n");
	  log_partition(disk_car, partition);
	  find_partition=1;
	}
	else
	{
	  if(info_mft[i].mftmirr_lcn > info_mft[j].mft_lcn)
	  {
	    if((info_mft[j].sector - info_mft[i].sector)/(info_mft[i].mftmirr_lcn - info_mft[j].mft_lcn)==0)
	      sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[i].mftmirr_lcn - info_mft[j].mft_lcn);
	  }
	  else if(info_mft[i].mftmirr_lcn < info_mft[j].mft_lcn)
	  {
	    if((info_mft[j].sector - info_mft[i].sector)/(info_mft[j].mft_lcn - info_mft[i].mftmirr_lcn)==0)
	      sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[j].mft_lcn - info_mft[i].mftmirr_lcn);
	  }
	  if(sec_per_cluster!=0)
	  {
	    partition->part_offset=partition->part_offset + (info_mft[i].sector -
		info_mft[i].mftmirr_lcn * sec_per_cluster) * disk_car->sector_size;
	    if(find_partition==0)
	      log_info("Potential partition:\n");
	    log_partition(disk_car, partition);
	    find_partition=1;
	  }
	}
      }
    }
    partition->part_offset=tmp;
  }
  if(interface>0 && expert>0)
  {
    wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X);
    sectors_per_cluster=ask_number(sectors_per_cluster,0,512,"Sectors per cluster ");
    wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X);
    mft_lcn=ask_number(mft_lcn,0,0,"MFT LCN ");
    wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X);
    mftmirr_lcn=ask_number(mftmirr_lcn,0,0,"MFTMIRR LCN ");
    wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X);
    mft_record_size=ask_number(mft_record_size,0,4096," mft record size ");
  }
  /* TODO read_mft_info(partition,sector,*sectors_per_cluster,*mft_lcn,*mftmirr_lcn,*mft_record_size); */
  if(sectors_per_cluster>0 && mft_record_size>0)
  {
    unsigned int index_block_size=4096;
    /* Read "root directory" in MFT */
    if(disk_car->read(disk_car,mft_record_size, &buffer, partition->part_offset+(uint64_t)mft_lcn*sectors_per_cluster*disk_car->sector_size+5*(uint64_t)mft_record_size)!=0)
    {
      display_message("NTFS Can't read \"root directory\" in MFT\n");
      return 1;
    }
    index_block_size=ntfs_get_attr(buffer,0x90,partition,buffer+mft_record_size,debug,0,NULL);
    if(index_block_size%512!=0)
      index_block_size=4096;
    log_info("ntfs_find_mft: sectors_per_cluster %u\n",sectors_per_cluster);
    log_info("ntfs_find_mft: mft_lcn             %lu\n",(long unsigned int)mft_lcn);
    log_info("ntfs_find_mft: mftmirr_lcn         %lu\n",(long unsigned int)mftmirr_lcn);
    log_info("ntfs_find_mft: mft_record_size     %u\n",mft_record_size);
    log_info("ntfs_find_mft: index_block_size    %u\n",index_block_size);
    create_ntfs_boot_sector(disk_car,partition, interface, sectors_per_cluster*disk_car->sector_size, mft_lcn, mftmirr_lcn, mft_record_size, index_block_size,current_cmd);
    /* TODO: ask if the user want to continue the search of MFT */
  }
  else
  {
    log_error("Failed to rebuild NTFS boot sector.\n");
  }
  return 0;
}

static int dump_ntfs_info(const struct ntfs_boot_sector *ntfs_header)
{
  wdoprintf(stdscr,"filesystem size           %llu\n", (long long unsigned)(le64(ntfs_header->sectors_nbr)+1));
  wdoprintf(stdscr,"sectors_per_cluster       %u\n",ntfs_header->sectors_per_cluster);
  wdoprintf(stdscr,"mft_lcn                   %lu\n",(long unsigned int)ntfs_header->mft_lcn);
  wdoprintf(stdscr,"mftmirr_lcn               %lu\n",(long unsigned int)ntfs_header->mftmirr_lcn);
  wdoprintf(stdscr,"clusters_per_mft_record   %d\n",ntfs_header->clusters_per_mft_record);
  wdoprintf(stdscr,"clusters_per_index_record %d\n",ntfs_header->clusters_per_index_record);
  return 0;
}

static int testdisk_ffs(int x)
{
  int r = 1;

  if (!x)
    return 0;
  if (!(x & 0xffff)) {
    x >>= 16;
    r += 16;
  }
  if (!(x & 0xff)) {
    x >>= 8;
    r += 8;
  }
  if (!(x & 0xf)) {
    x >>= 4;
    r += 4;
  }
  if (!(x & 3)) {
    x >>= 2;
    r += 2;
  }
  if (!(x & 1)) {
    x >>= 1;
    r += 1;
  }
  return r;
}

static int dump_2ntfs_info(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2)
{
  wdoprintf(stdscr,"filesystem size           %llu %llu\n",
      (long long unsigned)(le64(nh1->sectors_nbr)+1),
      (long long unsigned)(le64(nh2->sectors_nbr)+1));
  wdoprintf(stdscr,"sectors_per_cluster       %u %u\n",nh1->sectors_per_cluster,nh2->sectors_per_cluster);
  wdoprintf(stdscr,"mft_lcn                   %lu %lu\n",(long unsigned int)le64(nh1->mft_lcn),(long unsigned int)le64(nh2->mft_lcn));
  wdoprintf(stdscr,"mftmirr_lcn               %lu %lu\n",(long unsigned int)nh1->mftmirr_lcn,(long unsigned int)le64(nh2->mftmirr_lcn));
  wdoprintf(stdscr,"clusters_per_mft_record   %d %d\n",nh1->clusters_per_mft_record,nh2->clusters_per_mft_record);
  wdoprintf(stdscr,"clusters_per_index_record %d %d\n",nh1->clusters_per_index_record,nh2->clusters_per_index_record);
  return 0;
}

int log_ntfs2_dump(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2)
{
  log_info("filesystem size           %llu %llu\n",
      (long long unsigned)(le64(nh1->sectors_nbr)+1),
      (long long unsigned)(le64(nh2->sectors_nbr)+1));
  log_info("sectors_per_cluster       %u %u\n",nh1->sectors_per_cluster,nh2->sectors_per_cluster);
  log_info("mft_lcn                   %lu %lu\n",(long unsigned int)le64(nh1->mft_lcn),(long unsigned int)le64(nh2->mft_lcn));
  log_info("mftmirr_lcn               %lu %lu\n",(long unsigned int)le64(nh1->mftmirr_lcn),(long unsigned int)le64(nh2->mftmirr_lcn));
  log_info("clusters_per_mft_record   %d %d\n",nh1->clusters_per_mft_record,nh2->clusters_per_mft_record);
  log_info("clusters_per_index_record %d %d\n",nh1->clusters_per_index_record,nh2->clusters_per_index_record);
  return 0;
}




syntax highlighted by Code2HTML, v. 0.9.1