/*static char *SCCSID = "@(#)rbldbit.c  6.3 92/02/19";*/
/***    REBUILD.C - Routines to rebuild a badly trashed system.
 *
 *      CHKDSK
 *      Gregory A. Jones
 *      Copyright 1988 Microsoft Corporation
 *
 *      Modification history:
 *      G.A. Jones      07/12/88        Removed from RECOVER.C due to size.
 *      56340   11/05/92 BJH    Changed to rebuild bitmap indirect larger than one
 *                              block(2K) if necessary. Also fixed math error when
 *                              computing number of dirblks needed.
 *
 *      69658   06/15/93 rjp    Ported OS/2 1.3 fix from defect 5466:
 *                              Memory reduction. Reduce chk_disk_map and
 *                              chk_db_usemap from 8 bits per sector to 2 bits
 *                              per sector.
 *      74246   09/24/93  gy    Support V4FORMAT for HPFS386 extensions
 *                              (GHASSAN @ AUSVM1)
 */

#include "ldefs.h"
#include "ltypes.h"
#include "globals.h"
#include <pr_chk.h>

/***    chk_rebuild_bitmaps -- reallocate bit maps on disk
 *
 *      This function is called after the file system has been rebuilt.
 *      It first scans the global disk map, and marks as "free" anything
 *      which is still unknown.  Bitmaps are then allocated from the
 *      free space, and the memory bit map is saved to them.  Since the
 *      files were previously allocated around the bit maps, and since
 *      this function uses the same placement algorithm that FORMAT
 *      uses, there is an excellent chance that the old slots are
 *      still free, and optimal bitmap placement will result.
 *
 *      We do the bitmaps first and bad block lists second because,
 *      in case of low disk space, we'd rather have bitmaps than bad
 *      block lists -- the former are essential to filesystem operation,
 *      while the latter are merely a convenience for FORMAT and CHKDSK.
 *
 *      chk_rebuild_bitmaps (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            No return value
 *
 *      CALLS           allocate_early_sectors
 *                      allocate_middle_sectors
 *                      chk_exit_error
 *                      set_bit
 *                      write_scratch
 *
 *      EFFECTS         Modifies bit map and global disk map
 *                      Allocates disk space
 *                      Writes bit maps to disk
 *
 *      WARNINGS        May terminate program if insufficient disk space
 */
void chk_rebuild_bitmaps (void)
{
  USHORT mapno, cBitMaps, cIndBlks, dbsize, j;          //56340
  BOOL rebuild_dbband = FALSE;
  ULONG *ind1, i, *l, start, end, max, maxstart, sig;

#ifdef TRACE
  fprintf (stderr, "chk_rebuild_bitmaps ()\n");
  fflush (stderr);
#endif


/*
 * See if the DIRBLK band information in the superblock makes sense,
 * by making sure that every sector in it is either free or a DIRBLK.
 * If that's the case, use the band as-is.
 *
 * Ignore this information if the values are inconsistent:  lower bound
 * greater than upper bound, not 4-sector aligned, etc.
 */

  if (sb.lsnFirstDirBlk < sb.lsnLastDirBlk &&
      sb.lsnLastDirBlk - sb.lsnFirstDirBlk + 1 == sb.clsnDirBlkBand &&
      !(sb.lsnFirstDirBlk & (SPB - 1)) && !(sb.clsnDirBlkBand & (SPB - 1)) &&
      sb.clsnDirBlkBand <= MAX_DBS_PER_DIRBAND * SECTORS_PER_DIRBLK) {

    for (i=sb.lsnFirstDirBlk; i<=sb.lsnLastDirBlk; i++) {
      if ( ( QueryHugeUseMapBits ( chk_disk_map, i ) == DISK_UNKNOWN ) ||       /* 69658 -rjp */
           read_scratch (i, scratchbuf, 1))                                     /* 69658 -rjp */
        continue;               /* unused sector or hard error, keep going */

      sig = *(ULONG *)scratchbuf;

      if (i & (SECTORS_PER_DIRBLK - 1)) {       /* not DIRBLK aligned */
        if (sig == FNSIGVAL || sig == ABSIGVAL || sig == CPINFOVAL ||
            sig == CPDATAVAL)
          break;                /* it's another structure, we're hosed  */
      }
      else {                    /* it's DIRBLK aligned  */
        if (sig != DBSIGVAL)    /* but not a DIRBLK -- we're hosed */
          break;
      }
    }
    if (i <= sb.lsnLastDirBlk)
      rebuild_dbband = TRUE;    /* broke out early, rebuild the band */
  }
  else
    rebuild_dbband = TRUE;      /* things are hosed, rebuild the band */

  if (rebuild_dbband) {

    /*
    * Create the DIRBLK band.
    */

    dbsize = (USHORT)(number_of_sectors / ( 1048576L / SECSIZE )); /* dbsize = MB of disk */    //56340
    if (dbsize > 10)
      dbsize = 50 + ((dbsize - 10) * 5);
    else
      dbsize = 50;
    if (dbsize > MAX_DBS_PER_DIRBAND)   /* dbsize == # of DB's in band */ //56340
      dbsize = MAX_DBS_PER_DIRBAND;
    dbsize *= SECTORS_PER_DIRBLK;                 /* dbsize = # of sects in band */

    /*
     * Look through the disk map for an area big enough to be the DIRBLK
     * band.  This area must contain only unused sectors and used DIRBLKs.
     *
     * "start" is set to the place where FORMAT most likely located the
     * DIRBLK band.  This is near the beginning of the middlemost band,
     * or right after the hotfix table and hotfix replacement sectors if
     * there are only one or two bands.
     *
     * Note that start must be a multiple of SECTORS_PER_DIRBLK
     */

    start = (number_of_sectors / (8L * 2048L)) / 2L;
#ifdef TRACE
    fprintf (stderr, "  start (%lX) = number_of_sectors (%lX) / 8L * 2048L)) / 2L\n",
                     start, number_of_sectors);
    fflush (stderr);
#endif

    if (!start)
    {
      start = ((number_of_sectors > 40000L) ? 400L : number_of_sectors / 100L) +
              SEC_SUPERB + SPB + SPB;
#ifdef TRACE
    fprintf (stderr, "  start(%1X) = #sec/100 + SEC_SUPERB(%lX) + SPB (%lX) + SPB\n ",
                     start, SEC_SUPERB, SPB);
    fflush (stderr);
#endif
    }

    start -= start % SECTORS_PER_DIRBLK;
#ifdef TRACE
    fprintf (stderr, "  start(%1X) = start - start %% SECTORS_PER_DIRBLK (%lX)\n ",
                     start, SECTORS_PER_DIRBLK);
    fflush (stderr);
#endif


#ifdef TRACE
    fprintf (stderr, "start (%lX), number_of_sectors (%lX), dbsize (%X)\n",
                     start, number_of_sectors, dbsize);
    fflush (stderr);
#endif

    for (i=start, end=start, max=0L, maxstart=0L;
         (end < number_of_sectors) && (end - start < dbsize); )
    {
      if ( ( QueryHugeUseMapBits ( chk_disk_map, end ) != DISK_UNKNOWN ) &&     /* 69658 -rjp */
           !(end & 3))                                                          /* 69658 -rjp */
      {
#ifdef TRACE
        fprintf (stderr, "(chk_disk_map [end] && !(end & 3))\n");
        fprintf (stderr, "chk_disk_map [%lX] = %X\n", end, chk_disk_map [end]);
        fflush (stderr);
#endif
        read_scratch (end, scratchbuf, 1);
             if (*(ULONG *)scratchbuf == DBSIGVAL)
        {
#ifdef TRACE
          fprintf (stderr, "(*(ULONG *)scratchbuf (%lX) == DBSIGVAL)\n",
                   *(ULONG *)scratchbuf);
          fflush (stderr);
#endif
          end += SECTORS_PER_DIRBLK;
        }
        else
        {
#ifdef TRACE
          fprintf (stderr, "(*(ULONG *)scratchbuf (%lX) != DBSIGVAL)\n",
                   *(ULONG *)scratchbuf);
          fflush (stderr);
#endif
          if (end - start > max)
            max = end - start;
          maxstart = start;
          start = end + SPB;
#ifdef TRACE
          fprintf (stderr, "  start(%1X) = end (%lX) + SPB (%lX)\n",
                   start, end, SPB);
          fflush (stderr);
#endif
          end = start;
             }
      }
      else
      {
#ifdef TRACE
        fprintf (stderr, "NOT (chk_disk_map [end] && !(end & 3))\n");
        fprintf (stderr, "chk_disk_map [%lX] = %X\n", end, chk_disk_map [end]);
        fflush (stderr);
#endif
        end++;
      }
    }

    if (end - start < dbsize) {
      for (start=SEC_SUPERB+SPB, end=SEC_SUPERB+SPB;
           (end < i) && (end - start < dbsize); ) {
#ifdef TRACE
    fprintf (stderr, "    loop start = %lX\n ",
                     start);
    fflush (stderr);
#endif

        if ( ( QueryHugeUseMapBits ( chk_disk_map, end ) != DISK_UNKNOWN ) &&     /* 69658 -rjp */
             !(end & 3)) {                                                        /* 69658 -rjp */
          read_scratch (end, scratchbuf, 1);
          if (*(ULONG *)scratchbuf == DBSIGVAL)
            end += SECTORS_PER_DIRBLK;
          else {
            if (end - start > max)
              max = end - start;
            maxstart = start;
            start = end + SPB;
#ifdef TRACE
    fprintf (stderr, "      loop start(%1X) = end (%lX) + SPB (%lX)\n ",
                     start, end, SPB);
    fflush (stderr);
#endif
            end = start;
          }
        }
        else
          end++;
      }
    }

    if (end - start < dbsize) {           /* didn't find a big enough area */
      start = maxstart;                   /* use largest available area */
#ifdef TRACE
    fprintf (stderr, "  start(%1X) = maxstart\n ",
                     start);
    fflush (stderr);
#endif
      end = maxstart + max;
    }
  }
  else {
    start = sb.lsnFirstDirBlk;
#ifdef TRACE
    fprintf (stderr, "  start(%1X) = sb.lsnFirstDirBlk\n ",
                     start);
    fflush (stderr);
#endif
    end = sb.lsnLastDirBlk + 1L;
  }


/*
 * Determine the contents of the DIRBLK bitmap from the disk map.
 */

  memset (db_bitmap, '\0', (size_t)((end - start) / (SECTORS_PER_DIRBLK * 8)));

  sb.clsnDirBlkBand = end - start;              /* so set_db_bit will work */

  for (i = start; i < end; i += SECTORS_PER_DIRBLK) {
    if ( QueryHugeUseMapBits ( chk_disk_map, i ) == DISK_KNOWN) /* 69658 -rjp */
      SetUseMapBits ( chk_db_usemap, i - start, DISK_KNOWN );   /* 69658 -rjp */
    else {
      SetUseMapBits ( chk_db_usemap, i - start, DISK_UNKNOWN ); /* 69658 -rjp */
      set_db_bit ((i - start) / SECTORS_PER_DIRBLK);
    }
  }

  use_sectors (start, end - start, DISK_CONTROL);
  sb.lsnFirstDirBlk = start;                    /* mark beginning and end of area */
  sb.lsnLastDirBlk = end - 1L;

  for (i = start; i < end; i++)
    clr_bit (i);

/*
 * Free anything we still don't know about.  The user can still run
 * CHKDSK /F to recover any other data.
 */

  for (i=0L; i<number_of_sectors; i++)
    if (QueryHugeUseMapBits ( chk_disk_map, i ) == DISK_UNKNOWN)        /* 69658 -rjp */
      set_bit (i);

  for (i=0L; i<SEC_SUPERB + SPB; i++)           /* mark boot blk and superb */
    clr_bit (i);                                /* in the bit map */

  if ((ind1=ALLOCATE_BLOCK (SPB*2)) == NULL)
    chk_exit_error (INSF_MEM_ERROR);

/*
 * Make a fresh super block
 */

  sb.sig1 = SBSIG1;                     /* install both signatures */
  sb.sig2 = SBSIG2;
  sb.culSectsOnVol = number_of_sectors & ~3L;                   /* BIGDISK */
  sb.bVersion = SBBASEV;

#ifdef   V4FORMAT
/*
 * We are rebuilding the SuperBlock/SpareBlock.  We can't stamp 2.4
 * on the disk because 1) HPFS.IFS would not be able to mount the
 * volume and 2) We don't know what 2.4 extensions were active on
 * the disk.
 *
 */
  if (sb.culSectsOnVol < CSEC_BIG_DISK) {                       /* BIGDISK */
    /* normal sized disk, use old version number */             /* BIGDISK */
    sb.bFuncVersion = SBBASEOLDFV;                              /* BIGDISK */
  } else {                                                      /* BIGDISK */
    /* disk is too big for old hpfs, set new version number */  /* BIGDISK */
    sb.bFuncVersion = SBBASEOLDFV_BIGDISK;                      /* BIGDISK */
  }
#else
  if (sb.culSectsOnVol < CSEC_BIG_DISK) {                       /* BIGDISK */
    /* normal sized disk, use old version number */             /* BIGDISK */
    sb.bFuncVersion = SBBASEOLDFV;                              /* BIGDISK */
  } else {                                                      /* BIGDISK */
    /* disk is too big for old hpfs, set new version number */  /* BIGDISK */
    sb.bFuncVersion = SBBASEFV;                                 /* BIGDISK */
  }                                                              /* BIGDISK */
#endif

  sb.usDummy = 0;
  sb.datLastChkdsk = get_time ();
  sb.datLastOptimize = 0L;
  sb.lsnRootFNode = root_fnode;         /* where the root FNODE is */

  sb.culSectsOnVol = number_of_sectors & ~3L;   /* number of sectors on volume */
  sb.culNumBadSects = 0L;                       /* run CHKDSK /f to get these */

  for (i=sb.culSectsOnVol; i<number_of_sectors; i++)
    clr_bit (i);                        /* mark partial cache block "in use" */

/*
 * Make a fresh spareblock
 */

  spb.bFlag = 0L;
  spb.sig1 = SPSIG1;
  spb.sig2 = SPSIG2;

  spb.culHotFixes = 0L;
  spb.culMaxHotFixes = (number_of_sectors > 40000L) ? 100L : number_of_sectors / 400L;

  memset ((UCHAR *)ind1, '\0', 2 * SPB * SECSIZE);

  l = &ind1 [spb.culMaxHotFixes];
  for (j=0; j < (USHORT)spb.culMaxHotFixes; j++) {
    if (!(*l = chk_write_data (0L, ind1, 1, TRUE)))
      chk_exit_error (INSF_DISK_ERROR);
    use_sectors (*l, 1L, DISK_CONTROL);
    l++;
  }
  if (!(spb.lsnHotFix = chk_write_data (0L, ind1, SPB, TRUE)))
    chk_exit_error (INSF_DISK_ERROR);

  use_sectors (spb.lsnHotFix, (ULONG)SPB, DISK_CONTROL);

  memset ((UCHAR *)ind1, '\0', 2 * SPB * SECSIZE);

/*
 * Allocate the emergency DIRBLKs.
 */

  for (j=0; j<SPAREDB; j++)
    spb.alsnSpareDirBlks [j] = chk_write_data (sb.lsnLastDirBlk, ind1,
                                         SECTORS_PER_DIRBLK, TRUE);
  spb.cdbSpares = SPAREDB;
  spb.cdbMaxSpare = SPAREDB;

  if (!(sb.lsnSidTab = chk_write_data (0L, ind1, 8, TRUE)))                             //56340
    chk_exit_error (INSF_DISK_ERROR);

/*
 * Now find new bitmap blocks.
 */

  FREE_BLOCK (ind1);                                                            //56340
  cBitMaps = (USHORT)((number_of_sectors - 1) / (BYTES_PER_BITMAP * 8)) + 1;    //56340
  cIndBlks = (( (cBitMaps * sizeof (ULONG)) - 1) / (SECSIZE * SPB)) + 1;        //56340
                                                                                //56340
  if ((ind1=ALLOCATE_BLOCK (cIndBlks*SPB)) == NULL)                             //56340
    chk_exit_error (INSF_MEM_ERROR);                                            //56340
  memset ((UCHAR *)ind1, '\0', cIndBlks * SPB * SECSIZE);                       //56340

  for (mapno = 0; mapno < cBitMaps; mapno++) {
    if (!(ind1 [mapno] = allocate_bitmap_sectors (mapno)))
      chk_exit_error (INSF_DISK_ERROR);
    use_sectors (ind1 [mapno], (ULONG)SPB, DISK_CONTROL);
  }

/*
 * Save the bitmap indirect lists
 */

  if (!(sb.rspBitMapIndBlk.lsnMain = chk_write_data (number_of_sectors / 2, ind1, cIndBlks * SPB, TRUE))) //56340
    chk_exit_error (INSF_DISK_ERROR);
  sb.rspBitMapIndBlk.lsnSpare = 0L;
  use_sectors (sb.rspBitMapIndBlk.lsnMain, (ULONG)(cIndBlks * SPB), DISK_CONTROL);       //56340

/*
 * Create the DIRBLK bitmap.
 */

  if (!(sb.lsnDirBlkMap = chk_write_data (number_of_sectors / 2, db_bitmap, SPB, TRUE))) //56340
    chk_exit_error (INSF_DISK_ERROR);

/*
 * Initialize the bad block lists to all zeros.  He'll have to find the
 * bad sectors by trial and error.
 */

  l = ALLOCATE_BLOCK (SPB);
  memset ((UCHAR *)l, '\0', SPB * SECSIZE);

  sb.rspBadBlkList.lsnMain = chk_write_data (number_of_sectors / 2, l, SPB, TRUE);
  use_sectors (sb.rspBadBlkList.lsnMain, (ULONG)SPB, DISK_CONTROL);
  sb.rspBadBlkList.lsnSpare = 0L;

  FREE_BLOCK (l);

/*
 * If we couldn't find the root FNODE, create a new root directory, and
 * just hope we don't land on anything valuable.
 */

  if (!sb.lsnRootFNode)
    sb.lsnRootFNode = chk_rebuild_root ();

/*
 * We've allocated all the bitmaps and bad block lists.  Save the super block
 * and the bitmaps.  We save the bitmaps down here since saving them earlier
 * would cause some of the allocate_xxx_sectors calls to remain unsaved.
 */

  for (mapno = 0; mapno < cBitMaps; mapno++)
    write_scratch (ind1 [mapno], bitmap + (mapno * BYTES_PER_BITMAP), SPB);

  write_scratch ((ULONG)SEC_SUPERB, &sb, 1);
  write_scratch ((ULONG)SEC_SPAREB, &spb, 1);
  FREE_BLOCK (ind1);
}
