/***************************************************************************
       fs_base.cpp  -  Base class of all File System with common code
                             -------------------
    begin                : Thu Jul 13 2000
    copyright            : (C) 2000 by François Dupoux
    email                : fdupoux@partimage.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#include "fs_base.h"
#include "partimage.h"
#include "imagefile.h"
#include "common.h"
#include "cbitmap.h"
#include "exceptions.h"

// =======================================================
CFSBase::CFSBase(char *szDevice, FILE *fDeviceFile, QWORD qwPartSize)
{
  BEGIN;

  m_fDeviceFile = fDeviceFile;
  strncpy(m_szDevice, szDevice, MAX_DEVICENAMELEN);
  memset(&m_header, 0, sizeof(CLocalHeader));
  m_qwPartSize = qwPartSize;
  showDebug(1, "qwPartSize=%llu=%llu * 512\n", qwPartSize, qwPartSize/512LL);
  m_bCheckSuperBlockUsedSize = false;
  m_bCheckSuperBlockFreeSize = false;
  m_bCanBeMore = false;

  RETURN;
}

// =======================================================
CFSBase::~CFSBase()
{
}

// =======================================================
int CFSBase::readData(void *cBuffer, QWORD qwOffset, DWORD dwLength)
{
   BEGIN;

   int nRes;

   errno = 0;
   nRes = bigFseek(m_fDeviceFile, qwOffset);
   if (nRes != 0)
   {
      showDebug(4, "CFSBase::readData(qwOffset=%llu): bigFseek() error %d=%s\n", qwOffset, errno, strerror(errno));
      RETURN_int(-1);
   }

   errno = 0;
   nRes = fread((char *)cBuffer, dwLength, 1, m_fDeviceFile);
   if (nRes != 1)
   {
      showDebug(4, "CFSBase::readData(dwLength=%lu): fread() error %d=%s\n", dwLength, errno, strerror(errno));
      RETURN_int(-1);
   }
  
   RETURN_int(0); // success
}


// =======================================================
void checkForEscape()
{
  int nRes;

  if (g_bSigInt == true)
    {
      nRes = g_interface->msgBoxYesNo(i18n("Exit partimage ?"), i18n("Do you really want to cancel the current operation ?"));
      if (nRes == MSGBOX_YES)
	{
	  errno = 0;
	  THROW(ERR_CANCELED, errno);
	}
      else // NO
	{
	  g_bSigInt = false;
	}
    }
}

// =======================================================
// may throw: errno, readbitmap, reading, writing
void CFSBase::saveUsedBlocksToImageFile(COptions *options)
{
  BEGIN;

  char cBuffer[COPYBUF_SIZE];
  bool bUsed;
  DWORD dwCount;
  int nRes;
  CCheck check;
  QWORD i;
  char *cBegin;
  DWORD dwStats;

  DWORD dwSavedBlocks = 0L;
  DWORD dwBlocksInBuffer;
  DWORD dwCheckCount = 0L;
  DWORD dwDataSize;
  DWORD dwTemp;

  // init ESC key
  g_bSigInt = false;

  // go to the beginning of the disk
  nRes = fseek(m_fDeviceFile, 0L, SEEK_SET);
  if (nRes == -1)
    THROW(ERR_ERRNO, errno);
  
  // init the bitmap optimizations
  m_bitmap.setMaxFreeBlocksCopiedPerOper(MAX_FREE_BLOCKS_SKIPPED); // could be another value
  dwBlocksInBuffer = (COPYBUF_SIZE-(COPYBUF_SIZE % m_header.qwBlockSize)) / (m_header.qwBlockSize); 
  m_bitmap.setMaxUsedBlocksCopiedPerOper(dwBlocksInBuffer);
  showDebug(3, "CPY: setMax=%lu\n", dwBlocksInBuffer);
  check.dwCRC = increaseCrc32(0, NULL, 0);

  // set magic
  check.cMagic[0] = 'C';
  check.cMagic[1] = 'H';
  check.cMagic[2] = 'K';
  check.cMagic[3] = 0;

  dwStats = 0;
  i = 0;
  while (i < m_header.qwBlocksCount) // while there are blocks to copy
    {
      nRes = m_bitmap.readBitmap(i, &bUsed, &dwCount);
      if (nRes == -1)
        THROW(ERR_READ_BITMAP, i);

      // stop at the end of the partition
      dwCount = my_min(dwCount, m_header.qwBlocksCount-i);

      showDebug(3, "CPY: dwCount=%lu, bUsed=%d\n", dwCount, bUsed);

      if (bUsed) // if used blocks are marked in the bitmap
	{
	  dwDataSize = m_header.qwBlockSize*dwCount;
	  
	  errno = 0;
	  nRes = fread(cBuffer, dwDataSize, 1, m_fDeviceFile);
	  if (nRes != 1)
	    {
              THROW(ERR_READING, (DWORD)i, errno);
	    }
	  
	  // ---- begin -----

	  cBegin = cBuffer;
	  while (dwDataSize > 0)
	    {
	      // calculate length of data to write
	      if (dwCheckCount + dwDataSize < CHECK_FREQUENCY) // no check to write
		dwTemp = dwDataSize;
	      else // there is a check to write
		dwTemp = CHECK_FREQUENCY - dwCheckCount;
	      
	      // increase CRC
	      check.dwCRC = increaseCrc32(check.dwCRC, cBegin, dwTemp);
	      //for (j=0; j < dwTemp; j++)
		//check.qwCRC += *(cBegin+j);

	      try { m_image->write(cBegin, dwTemp, true); }
	      catch (CExceptions * excep)
		{
                  showDebug(1, "saveUsedBlocksToImageFile: excep %d\n", 
                     excep->GetExcept());
                  throw excep;
		}

	      dwCheckCount += dwTemp;

	      if (dwCheckCount == CHECK_FREQUENCY)
		{
		  showDebug(3, "CHECKING(%lu): check=%lu and CRC=%lu\n", i, dwCheckCount, check.dwCRC);
	      
		  check.qwPos = i; 
                  showDebug(3, "BEFORE WRITING CHK\n");
		  try { m_image->write((char*)&check, sizeof(CCheck), true); }
		  catch (CExceptions * excep)
		    {
                      showDebug(1, "saveUsedBlocksToImageFile: excep %d\n", 
                         excep->GetExcept());
                      throw excep;
		    }

		  check.dwCRC = increaseCrc32(0, NULL, 0);
		  dwCheckCount = 0;
		  
		  // show stats
		  if (dwStats == 8)
		    {
		      m_guiSave->showStats(g_timeStart, m_header.qwBlockSize, dwSavedBlocks, m_header.qwUsedBlocks, options->szFullyBatchMode);
		      dwStats = 0;
		    }
		  dwStats++;
		}

	      cBegin += dwTemp;
	      dwDataSize -= dwTemp;
	      
	      checkForEscape();
	    }
	  
	  // ---- end -----

	  dwSavedBlocks += dwCount;
	}
      else // if free blocks were marked
	{
	  nRes = fseek(m_fDeviceFile, m_header.qwBlockSize*dwCount, SEEK_CUR);
	  if (nRes == -1)
            {
              THROW(ERR_ERRNO, errno);
            }
	}

      i += dwCount;

      // draw progress
      //m_guiSave->showStats(g_timeStart, m_header.qwBlockSize, dwSavedBlocks, m_header.qwUsedBlocks, options->szFullyBatchMode);
    }

  m_guiSave->showStats(g_timeStart, m_header.qwBlockSize, dwSavedBlocks, m_header.qwUsedBlocks, options->szFullyBatchMode);
  g_qwCopiedBytesCount = m_header.qwBlockSize * dwSavedBlocks; // for final message box

  RETURN;
}

// =======================================================
// may raise errno, check_num, check_crc,zer, writing, wrong_magic
// NEW optimized version
void CFSBase::restoreUsedBlocksToDisk(COptions *options)
{
  BEGIN;

  char cZeroBuffer[ZERO_BLOCK_SIZE];
  char cBuffer[COPYBUF_SIZE];
  bool bUsed;
  DWORD dwCount;
  int nRes;
  QWORD i;
  DWORD dwBlocksInBuffer;
  DWORD dwTemp;
  DWORD dwCheckCount = 0L;
  DWORD dwDataSize;
  DWORD dwSavedBlocks = 0L;
  CCheck check;
  char *cBegin;
  DWORD dwCRC;
  DWORD dwStats;

  memset(cZeroBuffer, 0, ZERO_BLOCK_SIZE);
  dwCRC = increaseCrc32(0, NULL, 0);  

  // init ESC key
  g_bSigInt = false;

  // go to the beginning of the disk
  nRes = fseek(m_fDeviceFile, 0L, SEEK_SET);
  if (nRes == -1)
    THROW(ERR_ERRNO, errno);

  // init the bitmap optimizations
  m_bitmap.setMaxFreeBlocksCopiedPerOper(MAX_FREE_BLOCKS_SKIPPED); // could be another value
  dwBlocksInBuffer = (COPYBUF_SIZE-(COPYBUF_SIZE % m_header.qwBlockSize)) / 
     (m_header.qwBlockSize); 
  m_bitmap.setMaxUsedBlocksCopiedPerOper(dwBlocksInBuffer);
  showDebug(3, "CPY: setMax=%lu\n", dwBlocksInBuffer);
  
  dwStats = 0;
  i = 0;
  while (i < m_header.qwBlocksCount)
    {
      showDebug(2, "i = %lld, m_header.qwBlocksCount = %lld\n", i,
        m_header.qwBlocksCount);

      m_bitmap.readBitmap(i, &bUsed, &dwCount);
      dwCount = my_min(dwCount, m_header.qwBlocksCount-i); // stop at the end of the partition

      showDebug(3, "CPY: dwCount=%lu, bUsed=%d\n", dwCount, bUsed);

      if (bUsed) // if used blocks are marked in the bitmap
	{
	  dwDataSize = m_header.qwBlockSize*dwCount;
	  dwSavedBlocks += dwCount;

	  // ---- begin -----

	  cBegin = cBuffer;
	  while (dwDataSize > 0)
	    {
              showDebug(8, "dwDataSize=%ld\n", dwDataSize);
	      // calculate length of data to write
	      if (dwCheckCount + dwDataSize < CHECK_FREQUENCY) // no check to write
		dwTemp = dwDataSize;
	      else // there is a check to write
		dwTemp = CHECK_FREQUENCY - dwCheckCount;
	      
	      try { m_image->read(cBegin, dwTemp, true); }
	      catch (CExceptions * excep)
		{
                  showDebug(1, "restoreUsedBlocksToDisk: except %d\n",
                     excep -> GetExcept());
                  throw excep;
		}	

	      // increase CRC
              dwCRC = increaseCrc32(dwCRC, cBegin, dwTemp);
	      showDebug(3, "CRC = %lu\n", dwCRC);

	      dwCheckCount += dwTemp;
	      showDebug(3, "dwCheckCount= %ld\n", dwCheckCount);

	      if (dwCheckCount == CHECK_FREQUENCY)
		{
                  showDebug(3, "BEFORE READING CHK\n");
		  try { m_image->read((char*)&check, sizeof(CCheck), true); }
		  catch (CExceptions * excep)
		    {
                      showDebug(1, "restoreUsedBlocksToDisk: except %d\n",
                         excep -> GetExcept());
                      throw excep;
		    }

		  showDebug(3, "CHECKING(%lu): check=%lu and CRC=%lu\n", i, 
                     dwCheckCount, dwCRC);

		  // check magic
		  if ((check.cMagic[0] != 'C') || (check.cMagic[1] != 'H')
                     || (check.cMagic[2] != 'K'))
		    {
		      showDebug(1, "CHECK-MAGIC error %3s %3s\n", "CHK", check.cMagic);
		      nRes=g_interface->msgBoxYesNo(i18n("local check error"),
                         i18n("Impossible to find a local check structure. "
                         "Data may be damaged. Do you want to continue ?"));
		      if (nRes == MSGBOX_NO)
		        THROW(ERR_WRONGMAGIC);
		    }
                  else
                    {
		      if (i != check.qwPos)
    		        {
		          showDebug(1, "CHECK-POS: old=%llu and new=%llu\n",
                             check.qwPos, i);
		     
		          nRes=g_interface->msgBoxYesNo(i18n("local CRC error"),
                             i18n("There was a position error in CCheck. "
                             "Data may be damaged. Do you want to continue ?"));
		          if (nRes == MSGBOX_NO)
			    THROW(ERR_CHECK_NUM, i, check.qwPos);
		        }

		      if (dwCRC != check.dwCRC)
		        {
		          showDebug(1, "CHECK-CRC(%lu): old=%lu and new=%lu\n", 
                             i, check.dwCRC, dwCRC);

		          nRes=g_interface->msgBoxYesNo(i18n("local CRC error"),
                             i18n("There was a CRC error in CCheck. "
                             "Data may be damaged. Do you want to continue ?"));
		          if (nRes == MSGBOX_NO)
			    THROW(ERR_CHECK_CRC, dwCRC, check.dwCRC);
		        }
                    }

                  dwCRC = increaseCrc32(0, NULL, 0);
		  dwCheckCount = 0;
		  
		  // show stats
		  if (dwStats == 8)
		    {
		      // update stats
		      m_guiRestore->showStats(g_timeStart,
                         m_header.qwBlockSize, dwSavedBlocks,
                         m_header.qwUsedBlocks,m_header.qwBlocksCount,
			 options->bEraseWithNull, options->szFullyBatchMode);
		      dwStats = 0;
		    }
		  dwStats++;

		}

	      errno = 0;
	      if (!options->bSimulateMode)
		{
		  nRes = fwrite(cBegin, dwTemp, 1, m_fDeviceFile);
		  if (nRes != 1)
		    {
                      THROW(ERR_WRITING, (DWORD) 0, errno);
		    }				
		}
	      else
		showDebug(10, "fwrite simulated\n");

	      cBegin += dwTemp;
	      dwDataSize -= dwTemp;

	      checkForEscape();
	    }
          showDebug(9, "end while dwDataSize\n");
	  
	  // ---- end -----
	  
	}
      else // if free blocks were marked in the bitmap
	{
	  if (options->bEraseWithNull == true) // if free and we must write 0 bytes on unused clusters
	    {
	      dwTemp = m_header.qwBlockSize * dwCount; // data to copy
	      
	      while (dwTemp > 0)
		{
		  errno = 0;
                  if (!options->bSimulateMode)
		    {
		      nRes = fwrite(cZeroBuffer, my_min(ZERO_BLOCK_SIZE, dwTemp),
                         1, m_fDeviceFile);
		      if (nRes != 1)
                        THROW(ERR_ZEROING, errno);
		    }
                  else
                    showDebug(10, "fwrite simulated\n");
		  
		  dwTemp -= my_min(ZERO_BLOCK_SIZE, dwTemp);
		}
	      dwSavedBlocks += dwCount;
	    }
	  else // don't write zero bytes
	    {
	      nRes = fseek(m_fDeviceFile, m_header.qwBlockSize*dwCount,
                 SEEK_CUR);
	      if (nRes == -1)
                THROW(ERR_ERRNO, errno);
	    }			
	}
      i += dwCount;
    }
  
  // update stats
  m_guiRestore->showStats(g_timeStart, m_header.qwBlockSize, dwSavedBlocks,
     m_header.qwUsedBlocks, m_header.qwBlocksCount, options->bEraseWithNull, options->szFullyBatchMode);
  g_qwCopiedBytesCount = m_header.qwBlockSize * dwSavedBlocks; // for final message box
  
  RETURN;
}

// =======================================================
void CFSBase::saveToImage(CImage *image, COptions *options, CSavingWindow *gui)
{
  m_guiSave = gui;

  BEGIN;
  
  m_image = image;
  
  // ---- 0. Initializations
  
  // read informations
  showDebug(3, "TRACE: 001 (init)\n");  
  g_interface -> StatusLine(i18n("reading partition properties"));
  try { readSuperBlock(); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "saveToImage: excep %d\n", excep->GetExcept());
      THROW(ERR_READ_SUPERBLOCK);
    }	
  
  // check the file system
  showDebug(3, "TRACE: 002 (fsck)\n");  
  if (options->bCheckBeforeSaving == true)
    {
      g_interface -> StatusLine(i18n("checking the file system with fsck"));

      try { fsck(); }
      catch ( CExceptions * excep )
        { throw excep; }
    }

  // read bitmap into memory (must be done before writing header)
  showDebug(3, "TRACE: 003 (readBitmap)\n");  
  showDebug(1, "begin of readBitmap()\n");
  readBitmap(options);
  showDebug(1, "end of readBitmap()\n");

  // check the bitmap has been read without error
  checkBitmapInfos();

  // print informations	
  if (options->bBatchMode == false)
    printfInformations();
  
  // write header
  showDebug(3, "TRACE: 004 (writeHeader)\n");  
  g_interface -> StatusLine(i18n("writing header"));
  try { m_image->writeMagic(MAGIC_BEGIN_LOCALHEADER); }
  catch ( CExceptions * excep )
    { throw excep; }
  errno = 0;
  try { m_image->write(&m_header, sizeof(CLocalHeader), true); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "saveToImage: excep %d\n", excep->GetExcept());
      throw excep;
    }

  // write CRC of CLocaleader
  m_image->writeCRC((char*)&m_header, sizeof(CLocalHeader));
  
  // copy bitmap to image file
  showDebug(3, "TRACE: 005 (writeBitmap)\n");  
  try { m_image->writeMagic(MAGIC_BEGIN_BITMAP); }
  catch ( CExceptions * excep )
    { throw excep; }

  try { m_image->write(m_bitmap.data(), m_header.qwBitmapSize, true); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "saveToImage: excep %d\n", excep->GetExcept());
      throw excep;
    }

  // write informations specific for the FS
  showDebug(3, "TRACE: 006 (writeInfo)\n");  
  try { m_image->writeMagic(MAGIC_BEGIN_INFO); }
  catch ( CExceptions * excep )
    { throw excep; }

  try { m_image->write((char*)getInfos(), INFOS_STRUCT_SIZE, true); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "saveToImage: excep %d\n", excep->GetExcept());
      throw excep;
    }
  
  // write CRC of info
  m_image->writeCRC((char*)getInfos(), INFOS_STRUCT_SIZE);

  // write magic string
  try { m_image->writeMagic(MAGIC_BEGIN_DATABLOCKS); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "saveToImage: excep %d\n", excep->GetExcept());
      throw excep;
    }
  
  // time of beginning
  g_timeStart = time(0);
    
  // ---- write all used blocks
  showDebug(3, "TRACE: 007 (copyUsedBlocks)\n");  
  g_interface -> StatusLine(i18n("copying used data blocks"));
  if (strlen(options->szFullyBatchMode)>0)
      fprintf(stderr,"File Name    Size     T:Elapsed/Estimated  Rate/min     Progress\n");

  showDebug(3, "begin of saveUsedBlocksToImageFile()\n");  
  try { saveUsedBlocksToImageFile(options); }
  catch ( CExceptions * excep)
    {
      showDebug(1, "saveToImage: excep %d\n", excep->GetExcept());
      throw excep;
    }
  
  RETURN;
}

// =======================================================
void CFSBase::restoreFromImage(CImage *image, COptions *options, CRestoringWindow *gui)
{
  int nRes;

  BEGIN;
  
  // init
  m_image = image;
  m_guiRestore = gui;
  
  g_interface -> StatusLine(i18n("reading partition informations"));
  
  // 1. ---- Read header
  g_interface -> StatusLine(i18n("reading header"));
  showDebug(3, "TRACE: 001 (readHeader)\n");  
  try { m_image->readAndCheckMagic(MAGIC_BEGIN_LOCALHEADER); } 
  catch ( CExceptions * excep )
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }

  try {m_image->read((char*)&m_header, sizeof(CLocalHeader), true);}
  catch(CExceptions * excep)
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }

  // read CRC of info
  m_image->readAndCheckCRC((char*)&m_header, sizeof(CLocalHeader));

  
  // check the current format is supported
  /*if (strncmp(headReiser.szAppFormat, CURRENT_REISER_FORMAT, 
    strlen(CURRENT_REISER_FORMAT)) < 0)
    {
    g_interface->ErrorNewerRelease();
    RETURN_int(-1);
    }*/	
    
  // 2. ---- Print informations and ask confirmation to restore
  /*showDebug(1, "TRACE: 002 (printfInfos)\n");  
    if (options->bBatchMode == false)
    {
    printfInformations();
      
    // ask confirmation
    nRes = g_interface -> askRestore(m_szDevice,
    image->get_szImageFilename());
    if (nRes == 2) // 2 --> cancel
    RETURN_int(-1);
    }*/
  
  // time of beginning
  g_timeStart = time(0);
  
  // ---- 5. read bitmap
  showDebug(3, "TRACE: 003 (readBitmap)\n");  
  try { m_image->readAndCheckMagic(MAGIC_BEGIN_BITMAP); } 
  catch ( CExceptions * excep )
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }
  
  // init bitmap size
  m_bitmap.init(m_header.qwBitmapSize);
  
  // copy bitmap blocks to bitmap in memory
  try{m_image->read((char*)m_bitmap.data(), (long)(m_header.qwBitmapSize), true);}
  catch (CExceptions * excep)
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }

  // write informations specific for the FS
  showDebug(3, "TRACE: 004 (readInfo)\n");  
  try {m_image->readAndCheckMagic(MAGIC_BEGIN_INFO); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }

  try { m_image->read((char*)getInfos(), INFOS_STRUCT_SIZE,true); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }

  // read CRC of info
  m_image->readAndCheckCRC((char*)getInfos(), INFOS_STRUCT_SIZE);

  // 2. ---- Print informations and ask confirmation to restore
  showDebug(3, "TRACE: 002 (printfInfos)\n");  
  if (options->bBatchMode == false)
    {
      printfInformations();
      
      // ask confirmation
      if (!options->bSimulateMode)
	{
	  nRes = g_interface -> askRestore(m_szDevice, image->get_szImageFilename());
	  if (nRes == MSGBOX_NO) // 2 --> cancel
	    THROW(ERR_CANCELED);
	}
    }

  // read and check magic string
  showDebug(3, "TRACE: 005 (copyUsedBlocks)\n");  
  try { m_image->readAndCheckMagic(MAGIC_BEGIN_DATABLOCKS); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }
  
  // ---- 7. Restore all used blocks
  errno = 0;
  g_interface -> StatusLine(i18n("copying used data blocks"));
  if(strlen(options->szFullyBatchMode)>0)
     fprintf(stderr,"File name    Level    T:Elapsed/Estimated  Rate/min     Progress\n");
  showDebug(1, "begin of restoreUsedBlocksToDisk()\n");  
  try { restoreUsedBlocksToDisk(options); }
  catch ( CExceptions * excep )
    {
      showDebug(1, "restoreFromImage: %d\n", excep->GetExcept()); 
      throw excep;
    }
  showDebug(9, "end of restoreUsedBlocksToDisk()\n");  
  
  RETURN;
}

// =======================================================
int CFSBase::getStdInfos(char *szDest, int nMaxLen, bool bShowBlocksInfo)
{
  BEGIN;

  char szTemp1[512];
  char szTemp2[512];
  char szTemp3[512];
  char szBlocksInfo[8192];

  if (bShowBlocksInfo)
    {
      SNPRINTF(szBlocksInfo, i18n("Block size....................%"PRIu64" bytes\n"
				  "Total blocks count............%"PRIu64"\n"
				  "Used blocks count.............%"PRIu64"\n"
				  "Free blocks count.............%"PRIu64"\n"),
	       m_header.qwBlockSize, 
	       m_header.qwBlocksCount,
	       m_header.qwUsedBlocks,
	       m_header.qwBlocksCount-m_header.qwUsedBlocks);     
    }
  else
    {
      *szBlocksInfo = 0; // empty
    }
 
  snprintf(szDest, nMaxLen, i18n("%s" // Blocks infos
				 "Space usage:..................%"PRIu64" %%\n"
				 "Used space....................%s\n"
				 "Free space....................%s\n"
				 "Bitmap size...................%s\n"
				 "Label.........................%s\n"),
	   szBlocksInfo,
	   (100 * m_header.qwUsedBlocks)/m_header.qwBlocksCount, 
	   formatSize(m_header.qwUsedBlocks * m_header.qwBlockSize, szTemp1),
	   // used space
	   formatSize((m_header.qwBlocksCount-m_header.qwUsedBlocks) *
		      m_header.qwBlockSize, szTemp2), // free space
	   formatSize(m_header.qwBitmapSize, szTemp3),
	   m_header.szLabel);

  showDebug(1, "\n====================\n%s====================\n\n", szDest);
 
  RETURN_int(0);
}			

// =======================================================
int CFSBase::calculateSpaceFromBitmap()
{ 
  BEGIN;

  QWORD qwUsedBlocksCount = 0;
  QWORD qwFreeBlocksCount = 0;
  QWORD i;

  showDebug(1, "BITMAP: m_header.qwBlocksCount = %llu\n", m_header.qwBlocksCount);
  for (i=0; i < m_header.qwBlocksCount; i++)
    {
      if (m_bitmap.isBitSet(i))
	qwUsedBlocksCount++;
      else
	qwFreeBlocksCount++;
    }

  m_header.qwUsedBlocks = qwUsedBlocksCount;

  showDebug(1, "BITMAP: qwUsedBlocks = %llu\n", qwUsedBlocksCount);
  showDebug(1, "BITMAP: qwFreeBlocks = %llu\n", qwFreeBlocksCount);

  RETURN_int(0);
}

// =======================================================
/*int CFSBase::checkBitmapInfos()
{
  QWORD qwBitmapUsedSize;
  QWORD qwBitmapFreeSize;
  qwBitmapUsedSize = m_header.qwUsedBlocks * m_header.qwBlockSize;
  qwBitmapFreeSize = (m_header.qwBlocksCount - m_header.qwUsedBlocks) * m_header.qwBlockSize;

  showDebug(1, "CHECK BITMAP: qwSuperBlockUsedSize.....%llu = %llu sectors\n", m_qwSuperBlockUsedSize, m_qwSuperBlockUsedSize/512LL);
  showDebug(1, "CHECK BITMAP: qwBitmapUsedSize.........%llu = %llu sectors\n", qwBitmapUsedSize, qwBitmapUsedSize/512LL);

  if ((m_bCheckSuperBlockUsedSize) && (qwBitmapUsedSize != m_qwSuperBlockUsedSize))
    {
      showDebug(1, "ERROR BITMAP Used\n");
      THROW(ERR_READ_BITMAP, (DWORD)0);
    }

  showDebug(1, "CHECK BITMAP: qwSuperBlockFreeSize.....%llu = %llu sectors\n", m_qwSuperBlockFreeSize, m_qwSuperBlockFreeSize/512LL);
  showDebug(1, "CHECK BITMAP: qwBitmapFreeSize.........%llu = %llu sectors\n", qwBitmapFreeSize, qwBitmapFreeSize/512LL);

  if ((m_bCheckSuperBlockFreeSize) && (qwBitmapFreeSize != m_qwSuperBlockFreeSize))
    {
      showDebug(1, "ERROR BITMAP Free\n");
      THROW(ERR_READ_BITMAP, (DWORD)0);
    }

  RETURN_int(0);
}*/

// =======================================================
int CFSBase::checkBitmapInfos()
{
  QWORD qwBitmapUsedSize;
  QWORD qwBitmapFreeSize;
  qwBitmapUsedSize = m_header.qwUsedBlocks * m_header.qwBlockSize;
  qwBitmapFreeSize = (m_header.qwBlocksCount - m_header.qwUsedBlocks) * m_header.qwBlockSize;

  showDebug(1, "CHECK BITMAP: qwSuperBlockUsedSize.....%llu = %llu sectors\n", m_qwSuperBlockUsedSize, m_qwSuperBlockUsedSize/512LL);
  showDebug(1, "CHECK BITMAP: qwBitmapUsedSize.........%llu = %llu sectors\n", qwBitmapUsedSize, qwBitmapUsedSize/512LL);
  showDebug(1, "CHECK BITMAP: qwSuperBlockFreeSize.....%llu = %llu sectors\n", m_qwSuperBlockFreeSize, m_qwSuperBlockFreeSize/512LL);
  showDebug(1, "CHECK BITMAP: qwBitmapFreeSize.........%llu = %llu sectors\n", qwBitmapFreeSize, qwBitmapFreeSize/512LL);

  if (m_bCheckSuperBlockUsedSize)
    {
      if ((!m_bCanBeMore && (qwBitmapUsedSize != m_qwSuperBlockUsedSize)) || (m_bCanBeMore && (qwBitmapUsedSize < m_qwSuperBlockUsedSize)))
	{
	  showDebug(1, "ERROR BITMAP Used and m_bCanBeMore=%d\n", m_bCanBeMore);
	  THROW(ERR_READ_BITMAP, (DWORD)0);
	}
    }
  
  if (m_bCheckSuperBlockFreeSize)
    {
      if ((!m_bCanBeMore && (qwBitmapFreeSize != m_qwSuperBlockFreeSize)) || (m_bCanBeMore && (qwBitmapFreeSize > m_qwSuperBlockFreeSize)))
	{
	  showDebug(1, "ERROR BITMAP Free and m_bCanBeMore=%d\n", m_bCanBeMore);
	  THROW(ERR_READ_BITMAP, (DWORD)0);
	}
    }
  
  RETURN_int(0);
}


// =======================================================
void CFSBase::setSuperBlockInfos(BOOL bCheckSuperBlockUsedSize, BOOL bCheckSuperBlockFreeSize, 
				 QWORD qwSuperBlockUsedSize, QWORD qwSuperBlockFreeSize, BOOL bCanBeMore/*=false*/) 
{
  m_bCanBeMore = bCanBeMore;
  m_qwSuperBlockUsedSize = qwSuperBlockUsedSize; m_qwSuperBlockFreeSize = qwSuperBlockFreeSize;
  m_bCheckSuperBlockUsedSize = bCheckSuperBlockUsedSize;
  m_bCheckSuperBlockFreeSize = bCheckSuperBlockFreeSize;
}
