// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017 Eduardo Aguiar
//
// This program 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, 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, see <http://www.gnu.org/licenses/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "filesystem_impl_ntfs.h"
#include <mobius/decoder/data_decoder.h>
#include <mobius/string_functions.h>

namespace mobius
{
namespace filesystem
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief read file record segment from MFT
//! \param impl NTFS implementation object
//! \param idx frs index
//! \return data
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static const mobius::bytearray
_read_frs (filesystem_impl_ntfs& impl, std::uint32_t idx)
{
  mobius::bytearray data;
  mobius::io::reader reader = impl.get_reader ();

  try
    {
      reader.seek (impl.get_mft_offset () + idx * impl.get_frs_size ());
      data = reader.read (impl.get_frs_size ());
    }
  catch (const std::exception&)
    {
      reader.seek (impl.get_mft_mirror_offset () + idx * impl.get_frs_size ());
      data = reader.read (impl.get_frs_size ());
    }

  return data;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief check if stream contains an instance of NTFS filesystem
//! \param reader stream reader
//! \param offset offset from the beginning of the stream
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
filesystem_impl_ntfs::is_instance (mobius::io::reader reader, std::uint64_t offset)
{
  reader.seek (offset + 3);
  auto data = reader.read (8);

  return data == "NTFS    ";
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
//! \param reader stream reader
//! \param offset offset from the beginning of the stream
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
filesystem_impl_ntfs::filesystem_impl_ntfs (mobius::io::reader reader, std::uint64_t offset)
  : filesystem_impl_base (reader, offset),
    tsk_adaptor (reader, offset)
{
  reader.seek (offset);
  mobius::decoder::data_decoder decoder (reader);

  // decode BPB
  decoder.skip (3);
  oem_name_ = mobius::string::rstrip (decoder.get_string_by_size (8));
  sector_size_ = decoder.get_uint16_le ();
  sectors_per_cluster_ = decoder.get_uint8 ();
  reserved_sectors_ = decoder.get_uint16_le ();
  decoder.skip (5);
  media_descriptor_ = decoder.get_uint8 ();
  decoder.skip (2);
  sectors_per_track_ = decoder.get_uint16_le ();
  heads_ = decoder.get_uint16_le ();

  // decode extended BPB
  hidden_sectors_ = decoder.get_uint32_le ();
  decoder.skip (8);
  sectors_ = decoder.get_uint64_le ();
  mft_cluster_ = decoder.get_uint64_le ();
  mft_mirror_cluster_ = decoder.get_uint64_le ();
  clusters_per_frs_ = decoder.get_uint32_le ();
  clusters_per_index_block_ = decoder.get_uint32_le ();
  volume_serial_number_ = decoder.get_uint64_le ();
  checksum_ = decoder.get_uint32_le ();

  // derived information
  set_size ((sectors_ + 1) * sector_size_);
  set_fs_type ("NTFS");
  cluster_size_ = sectors_per_cluster_ * sector_size_;
  frs_size_ = clusters_per_frs_ * cluster_size_;
  mft_offset_ = offset + mft_cluster_ * cluster_size_;
  mft_mirror_offset_ = offset + mft_mirror_cluster_ * cluster_size_;

  set_name ("NTFS (s/n: " + mobius::string::to_hex (volume_serial_number_, 16) + ')');

  //! \todo read volume version and volume name
  // volume_name
  //auto data = _read_frs (*this, 3);    // $Volume
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get metadata
//! \return metadata
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::metadata
filesystem_impl_ntfs::get_metadata () const
{
  return mobius::metadata
  {
    {
      "type",
      "type",
      "std::string",
      get_type ()
    },
    {
      "offset",
      "offset in bytes",
      "std::uint64_t",
      std::to_string (get_offset ())
    },
    {
      "size",
      "size",
      "size_type",
      std::to_string (get_size ()) + " bytes"
    },
    {
      "name",
      "name",
      "std::string",
      get_name ()
    },
    {
      "fs_type",
      "filesystem type",
      "std::string",
      get_fs_type ()
    },
    {
      "oem_name",
      "OEM name",
      "std::string",
      get_oem_name ()
    },
    {
      "sectors",
      "number of sectors",
      "std::uint64_t",
      std::to_string (get_sectors ())
    },
    {
      "sector_size",
      "sector size",
      "std::uint32_t",
      std::to_string (get_sector_size ()) + " bytes"
    },
    {
      "sectors_per_cluster",
      "sectors per cluster",
      "std::uint32_t",
      std::to_string (get_sectors_per_cluster ())
    },
    {
      "cluster_size",
      "cluster size",
      "std::uint32_t",
      std::to_string (get_cluster_size ()) + " bytes"
    },
    {
      "reserved_sectors",
      "number of reserved sectors",
      "std::uint32_t",
      std::to_string (get_reserved_sectors ())
    },
    {
      "media_descriptor",
      "media descriptor",
      "std::uint16_t",
      std::to_string (get_media_descriptor ())
    },
    {
      "sectors_per_track",
      "sectors per track",
      "std::uint32_t",
      std::to_string (get_sectors_per_track ())
    },
    {
      "heads",
      "number of heads",
      "std::uint32_t",
      std::to_string (get_heads ())
    },
    {
      "hidden_sectors",
      "number of hidden sectors",
      "std::uint64_t",
      std::to_string (get_hidden_sectors ())
    },
    {
      "mft_cluster",
      "$MFT cluster",
      "std::uint64_t",
      std::to_string (get_mft_cluster ())
    },
    {
      "mft_mirror_cluster",
      "$MFTMirr cluster",
      "std::uint64_t",
      std::to_string (get_mft_mirror_cluster ())
    },
    {
      "mft_offset",
      "$MFT offset",
      "std::uint64_t",
      std::to_string (get_mft_offset ())
    },
    {
      "mft_mirror_offset",
      "$MFTMirr offset",
      "std::uint64_t",
      std::to_string (get_mft_mirror_offset ())
    },
    {
      "clusters_per_frs",
      "clusters per file record segment",
      "std::uint32_t",
      std::to_string (get_clusters_per_frs ())
    },
    {
      "frs_size",
      "file record segment size",
      "std::uint32_t",
      std::to_string (get_frs_size ()) + " bytes"
    },
    {
      "clusters_per_index_block",
      "clusters per index block",
      "std::uint32_t",
      std::to_string (get_clusters_per_index_block ())
    },
    {
      "volume_serial_number",
      "volume serial number",
      "std::uint64_t",
      "0x" + mobius::string::to_hex (get_volume_serial_number (), 16)
    },
    {
      "checksum",
      "checksum",
      "std::uint32_t",
      std::to_string (get_checksum ())
    },
  };
}

} // namespace filesystem
} // namespace mobius
