// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024 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 "adaptor.h"
#include "exception.h"
#include "folder_impl.h"
#include <cstring>
#include <stdexcept>
#include <tsk/libtsk.h>

namespace
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief img_info for mobius::io::reader
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
struct TSK_IMG_INFO_MOBIUS
{
  TSK_IMG_INFO img_info;
  mobius::io::reader reader;
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief read data from reader
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static ssize_t
mobius_read (TSK_IMG_INFO *img, TSK_OFF_T off, char *buf, size_t len)
{
  TSK_IMG_INFO_MOBIUS *p = (TSK_IMG_INFO_MOBIUS *) img;
  ssize_t ret = -1;

  try
    {
      p->reader.seek (off);
      auto data = p->reader.read (len);
      memcpy (buf, data.data (), data.size ());
      ret = data.size ();
    }
  catch (const std::exception& e)
    {
      tsk_error_set_errstr2 ("%s", e.what ());
      ret = -1;
    }

  return ret;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief close TSK_IMG_INFO_MOBIUS
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
mobius_close (TSK_IMG_INFO *img)
{
  TSK_IMG_INFO_MOBIUS *p = (TSK_IMG_INFO_MOBIUS *) img;
  delete p;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief show img status
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
mobius_imgstat (TSK_IMG_INFO *, FILE *)
{
}

} // local (unnamed) namespace

namespace mobius::vfs::tsk
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
//! \param reader generic reader
//! \param offset offset in bytes from the beginning of the stream
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
adaptor::adaptor (const mobius::io::reader reader, std::uint64_t offset)
  : reader_ (reader), offset_ (offset)
{
  // create image info structure
  TSK_IMG_INFO_MOBIUS *p = new TSK_IMG_INFO_MOBIUS;
  memset (p, 0, sizeof (TSK_IMG_INFO)); //_MOBIUS));
  p->reader = reader;

  constexpr int SECTOR_SIZE = 512;      // unused

  img_info_ = tsk_img_open_external (
                p,
                p->reader.get_size (),
                SECTOR_SIZE,
                mobius_read,
                mobius_close,
                mobius_imgstat
              );

  if (!img_info_)
    throw std::runtime_error (TSK_EXCEPTION_MSG);

  // create filesystem info structure
  fs_info_ = tsk_fs_open_img (img_info_, offset, TSK_FS_TYPE_DETECT);

  if (!fs_info_)
    throw std::runtime_error (TSK_EXCEPTION_MSG);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief destructor
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
adaptor::~adaptor ()
{
  if (fs_info_)
    {
      tsk_fs_close (fs_info_);
      fs_info_ = nullptr;
    }

  if (img_info_)
    {
      tsk_img_close (img_info_);
      img_info_ = nullptr;
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Get filesystem root folder
//! \return Root folder
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::io::folder
adaptor::get_root_folder () const
{
  if (!fs_info_)
    _create_tsk ();

  TSK_FS_FILE *p = tsk_fs_file_open_meta (fs_info_, 0, fs_info_->root_inum);

  if (!p)
    throw std::runtime_error (TSK_EXCEPTION_MSG);

  fs_file fs_file (p);
  return mobius::io::folder (std::make_shared <folder_impl> (fs_file));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief create TSK image and filesystem objects
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
adaptor::_create_tsk () const
{
  // create image info structure
  TSK_IMG_INFO_MOBIUS *p = new TSK_IMG_INFO_MOBIUS;
  memset (p, 0, sizeof (TSK_IMG_INFO_MOBIUS));
  p->reader = reader_;

  constexpr int SECTOR_SIZE = 512;      // unused

  img_info_ = tsk_img_open_external (
                p,
                p->reader.get_size (),
                SECTOR_SIZE,
                mobius_read,
                mobius_close,
                mobius_imgstat
              );

  if (!img_info_)
    throw std::runtime_error (TSK_EXCEPTION_MSG);

  // create filesystem info structure
  fs_info_ = tsk_fs_open_img (img_info_, offset_, TSK_FS_TYPE_DETECT);

  if (!fs_info_)
    throw std::runtime_error (TSK_EXCEPTION_MSG);
}

} // namespace mobius::vfs::tsk
