/*static char *SCCSID = "@(#)map.c      6.2 92/02/19";*/
/*static char *SCCSID = "@(#)map.c      6.2 92/02/19";*/
/***    MAP.C - Routines to manipulate disk maps
 *
 *      CHKDSK
 *      Gregory A. Jones
 *      Copyright 1988 Microsoft Corporation
 *
 *      Modification history:
 *      G.A. Jones      06/02/88        Original for Pinball 1.0.
 *
 *      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.
 */

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


static  USHORT  usNewPercent = 0;
static  USHORT  usOldPercent = 0;

/***    chk_mark_deleted - mark a run of sectors as deleted
 *
 *      This routine is called to mark a run of sectors "deleted"
 *      in a disk usage map.  This is done so that if a structure
 *      is deleted after use_sectors has been called on it, it will
 *      still be seen as orphaned in the same CHKDSK run.
 *
 *      chk_mark_deleted (sector, length)
 *
 *      ENTRY           sector - sector number of run start
 *                      length - number of sectors to mark
 *
 *      EXIT            No return value
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Changes global disk usage map
 */
void chk_mark_deleted (sector, length)
  ULONG sector;
  ULONG length;
{
/*UCHAR huge *p;                                                  69658 -rjp */

  if ((sector >= number_of_sectors) || (sector+length > number_of_sectors))
    return;

  if ((sector >= sb.lsnFirstDirBlk) && (sector <= sb.lsnLastDirBlk))
  {                                                                /* 69658 -rjp */
    sector -= sb.lsnFirstDirBlk;/* point at sector in db map */    /* 69658 -rjp */
    for (; length; length--)    /* loop for "length" sectors */    /* 69658 -rjp */
      SetUseMapBits ( chk_db_usemap, sector++, DISK_DELETED );     /* 69658 -rjp */
  }                                                                /* 69658 -rjp */
  else
  {                                                                /* 69658 -rjp */
    for (; length; length--)    /* loop for "length" sectors */    /* 69658 -rjp */
      SetHugeUseMapBits ( chk_disk_map, sector++, DISK_DELETED );  /* 69658 -rjp */
  }                                                                /* 69658 -rjp */

}


/***    chk_unuse - decrement usage count in disk usage map
 *
 *      This routine is called to undo the effects of a use_sectors.
 *      It decrements the usage count for each sector in the run in
 *      either the disk map or the dirblk usage map.
 *
 *      chk_unuse (sector, length)
 *
 *      ENTRY           sector - sector number of run start
 *                      length - number of sectors to unuse
 *
 *      EXIT            No return value
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Changes global disk usage map
 */
/* This function is not used and is not converted to the new disk usage maps                  69658 -rjp */
/* void chk_unuse (sector, length)                                                            69658 -rjp */
/*   ULONG sector;                                                                            69658 -rjp */
/*   ULONG length;                                                                            69658 -rjp */
/* {                                                                                          69658 -rjp */
/*   UCHAR huge *p;                                                                           69658 -rjp */
/*                                                                                            69658 -rjp */
/*   if ((sector >= number_of_sectors) || (sector+length > number_of_sectors))                69658 -rjp */
/*     return;                                                                                69658 -rjp */
/*                                                                                            69658 -rjp */
/*   if ((sector >= sb.lsnFirstDirBlk) && (sector <= sb.lsnLastDirBlk))                       69658 -rjp */
/*     p = &chk_db_usemap [sector - sb.lsnFirstDirBlk];    / * point at sector in db map * /  69658 -rjp */
/*   else                                                                                     69658 -rjp */
/*     p = &chk_disk_map [sector];                 / * point at sector in main map * /        69658 -rjp */
/*                                                                                            69658 -rjp */
/*   for (; length; length--)                      / * loop for "length" sectors * /          69658 -rjp */
/*     --(*(p++));                                 / * decrement byte, advance ptr * /        69658 -rjp */
/* }                                                                                          69658 -rjp */

/***    chk_sector_usage - see if an object is cross-linked
 *
 *      This routine is called during recovery to check whether
 *      an object is cross-linked or not.  This is primarily for
 *      objects that have been deleted and recovered in the same
 *      CHKDSK run.  Since use_sectors was called for the object
 *      during validation, calling it during recovery would
 *      incorrectly report a cross-link where none exists.  Not
 *      calling some function like this would recover cross-links
 *      intact.
 *
 *      If the sector number is out of range, no cross-link is
 *      reported, so the structure containing the pointer can be
 *      edited.
 *
 *      chk_sector_usage (lsn, nsect, level)
 *
 *      ENTRY           lsn   - sector number that the run starts at
 *                      nsect - number of sectors in run
 *                      level - max diskmap value that's OK
 *                              0 or 1 are the only valid inputs for level!!
 *
 *      EXIT            Returns DISK_XLINK if a cross-link exists, 0 otherwise
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */
USHORT chk_sector_usage (lsn, nsect, level)
  ULONG lsn;
  ULONG nsect;
  UCHAR level;
{
/*UCHAR huge *p;                                                  69658 -rjp */
  register USHORT cUse;                                        /* 69658 -rjp */

  if ((lsn >= number_of_sectors) || (lsn+nsect > number_of_sectors))
    return (FALSE);

  if ((lsn >= sb.lsnFirstDirBlk) && (lsn <= sb.lsnLastDirBlk) &&
      (nsect <= SECTORS_PER_DIRBLK))
  {                                                            /* 69658 -rjp */
    lsn -= sb.lsnFirstDirBlk;                                  /* 69658 -rjp */
    for (; nsect; nsect--)                                     /* 69658 -rjp */
    {                                                          /* 69658 -rjp */
      cUse = QueryUseMapBits ( chk_db_usemap, lsn );           /* 69658 -rjp */
      if ((cUse > level) && (cUse != DISK_DELETED))            /* 69658 -rjp */
        return (DISK_XLINK);                                   /* 69658 -rjp */
      else                                                     /* 69658 -rjp */
        lsn++;                                                 /* 69658 -rjp */
    }                                                          /* 69658 -rjp */
  }                                                            /* 69658 -rjp */
  else
  {                                                            /* 69658 -rjp */
    for (; nsect; nsect--)                                     /* 69658 -rjp */
    {                                                          /* 69658 -rjp */
      cUse = QueryHugeUseMapBits ( chk_disk_map, lsn );        /* 69658 -rjp */
      if ((cUse > level) && (cUse != DISK_DELETED))            /* 69658 -rjp */
        return (DISK_XLINK);                                   /* 69658 -rjp */
      else                                                     /* 69658 -rjp */
        lsn++;                                                 /* 69658 -rjp */
    }                                                          /* 69658 -rjp */
  }                                                            /* 69658 -rjp */


  return (FALSE);
}

/***    chk_hotfixed - find out if a sector has been hotfixed
 *
 *      This routine is called by use_sectors when an apparently
 *      cross-linked object is found.  It looks through the hotfix
 *      table to see if any of the hotfix records contain the sector
 *      in question.  If so, a TRUE value is returned, so that
 *      use_sectors can return DISK_FIXED to the caller instead of
 *      DISK_XLINK.
 *
 *      chk_hotfixed (sec)
 *
 *      ENTRY           sec - sector to check
 *
 *      EXIT            Returns TRUE if sector was hotfixed, FALSE otherwise
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */
USHORT chk_hotfixed (sec)
  ULONG sec;
{
  register USHORT i;

  for (i=0; i < (USHORT)spb.culHotFixes; i++)
    if (sec == hotfix_tbl [i])
      return (TRUE);
  return (FALSE);
}

/***    use_sectors - set bytes in the disk usage map
 *
 *      This routine is called to set a range of bytes in the disk
 *      usage map.  This indicates to other components of CHKDSK
 *      how the sectors are used by the file system.  An error is
 *      returned if any sectors in the range are already in use.
 *
 *      If the run extends into free space, that space is allocated
 *      in the bitmap.
 *
 *      use_sectors (lsn, nsect, type)
 *
 *      ENTRY           lsn   - sector number that the run starts at
 *                      nsect - number of sectors in run
 *                      type  - how the sectors are used
 *
 *      EXIT            Return value 0 if success, 0xfe if no map allocated,
 *                      0xff if bad sector number, usage type if sectors in use
 *
 *      CALLS           clr_bit
 *                      chk_no_writing
 *
 *      EFFECTS         Updates the global disk map
 *
 *      WARNINGS        Disk map and bitmap should be allocated first
 */
USHORT use_sectors (lsn, nsect, type)
  ULONG lsn;
  ULONG nsect;
  USHORT type;
{
  ULONG i, l;
  USHORT flag=FALSE, fixfound=FALSE;
  USHORT rc=DISK_UNKNOWN;
  register USHORT cUse;                                        /* 69658 -rjp */

#ifdef TRACE
  fprintf (stderr, "use_sectors (%lx, %lx, DISK_%s)\n",lsn,nsect,
                    chk_types[type==0xff ? 8 : type]);
  fflush (stderr);
#endif

  if (!chk_disk_map || !bitmap) {       /* disk map is not allocated yet */
#ifdef TRACE
    fprintf (stderr, "use_sectors:  no disk map allocated.\n");
    fflush (stderr);
#endif
    return (0xfe);                      /* return error condition */
  }

  if ((lsn >= number_of_sectors) || (lsn+(ULONG)nsect > number_of_sectors)) {
#ifdef TRACE
    fprintf (stderr, "use_sectors:  run extends beyond %lx.\n", number_of_sectors);
    fflush (stderr);
#endif
    return (0xff);                      /* return error condition */
  }

/*
 * Check to see if it's a DIRBLK in the special DIRBLK band.  If so, we check
 * the DIRBLK bitmap instead of the ordinary bitmap, and the DIRBLK usage map
 * instead of the normal disk map.
 */
  if ((lsn >= sb.lsnFirstDirBlk) && (lsn < sb.lsnLastDirBlk-1) &&
      (nsect <= SECTORS_PER_DIRBLK)) {
    lsn -= sb.lsnFirstDirBlk;
    for (i=nsect, l=lsn; i; l++, i--) {
      cUse = QueryUseMapBits ( chk_db_usemap, l );             /* 69658 -rjp */
      if ( cUse < DISK_XLINK )                                 /* 69658 -rjp */
      {                                                        /* 69658 -rjp */
        SetUseMapBits ( chk_db_usemap, l, cUse + 1 );          /* 69658 -rjp */
      }                                                        /* 69658 -rjp */
      if ( cUse )                                              /* 69658 -rjp */
      {
#ifdef TRACE
        fprintf (stderr, "use_sectors:  dirblk at lsn %lx is ref'd %d times.\n",
                 l + sb.lsnFirstDirBlk, cUse + 1 );            /* 69658 -rjp */
        fflush (stderr);
#endif
        return (DISK_XLINK);
      }
    }
    lsn /= SECTORS_PER_DIRBLK;              /* convert to a dirblk NUMBER */
    if (type != DISK_FIXED) {
      if (db_bitmap [lsn >> 3] & (1 << (lsn & 7))) {/* sector is free */
            if (!chk_recovering && !flag) { /* only error if checking     */
              flag = TRUE;
              chk_no_writing (CHK_USES_FREE_STR, NULL);/* display error msg*/
            }
            clr_db_bit (lsn);                       /* mark sector as in use      */
      }
    }
    chk_dirblk_sectors += nsect / SECTORS_PER_DIRBLK;
    return (0);                                         /* it's okay!                 */
  }

/*
 * It's not a DIRBLK, or it's not in the DIRBLK band.  Check as normal.
 * If we find that the region of sectors contains sectors we already know
 * about, we return the type already there as an error code.  However, we
 * don't do this immediately;  we want to account for all the sectors.
 *
 * For example, if a large data run is hotfixed near the beginning, sectors
 * near the beginning are marked DISK_FIXED.  However, the rest of the data
 * is OK, and is not lost, so we need to account for the rest of the data
 * run before returning an error.
 */
  for (i=nsect; i; lsn++, i--) {        /* process each sector in the run */
    cUse = QueryHugeUseMapBits ( chk_disk_map, lsn );          /* 69658 -rjp */
    if ( cUse < DISK_XLINK )                                   /* 69658 -rjp */
    {                                                          /* 69658 -rjp */
      SetHugeUseMapBits ( chk_disk_map, lsn, cUse + 1 );       /* 69658 -rjp */
    }                                                          /* 69658 -rjp */
    if ( cUse )                                                /* 69658 -rjp */
    {                                   /* use sector, check for cross-link */
#ifdef TRACE
      fprintf (stderr, "use_sectors:  block at lsn %lx should is ref'd %d times.\n",
               lsn, cUse + 1 );                                /* 69658 -rjp */
      fflush (stderr);
#endif
      if (spb.culHotFixes && chk_hotfixed (lsn)) {    /* is it hotfixed? */
            fixfound = TRUE;            /* hotfix is more important */
            rc = DISK_FIXED;            /* tell caller it's hotfixed */
      }
      else if (!fixfound)               /* not hotfixed, no hotfix found yet */
        rc = DISK_XLINK;                /* tell caller it's cross-linked */
    }

    if (type != DISK_FIXED) {           /* don't allocate hotfixed sectors */
      if (bitmap [lsn >> 3] & (1 << (lsn & 7))) {       /* sector is free */
            if (!chk_recovering && !flag) {     /* only error if checking */
             flag = TRUE;
             chk_no_writing (CHK_USES_FREE_STR, NULL);  /* display error msg */
            }
            clr_bit (lsn);                              /* mark sector as in use */
      }
    }
  }

  switch (type) {
    case DISK_DATA: chk_data_sectors += nsect; break;
    case DISK_FNODE: chk_fnode_sectors += nsect; break;
    case DISK_SUBDIR: chk_subdir_sectors += nsect; break;
    case DISK_AB: chk_ab_sectors += nsect; break;
    case DISK_DIRBLK: chk_dirblk_sectors += nsect; break;
    case DISK_CONTROL: chk_control_sectors += nsect; break;
    case DISK_EADATA: chk_ea_sectors += nsect; break;
  }

  return (rc);                  /* successfully used all sectors */
}

/*****************************************************************************/                   /* 69658 -rjp */
/*                                                                           */                   /* 69658 -rjp */
/*      SetHugeUseMapBits               for chk_disk_map (a huge pointer)    */                   /* 69658 -rjp */
/*      QueryHugeUseMapBits                                                  */                   /* 69658 -rjp */
/*                                                                           */                   /* 69658 -rjp */
/*      SetUseMapBits                   for chk_db_usemap (a large pointer)  */                   /* 69658 -rjp */
/*      QueryUseMapBits                                                      */                   /* 69658 -rjp */
/*                                                                           */                   /* 69658 -rjp */
/*      These routines are new for defect 69658                              */                   /* 69658 -rjp */
/*                                                                           */                   /* 69658 -rjp */
/*      This function was created to reduce the number of bits used per      */                   /* 69658 -rjp */
/*      sector for the usemaps from 8 bits (or one byte) to 2 bits.          */                   /* 69658 -rjp */
/*      This can be done since only 4 states need to maintained:             */                   /* 69658 -rjp */
/*      DISK_UNKNOWN    0        - type not yet known                        */                   /* 69658 -rjp */
/*      DISK_KNOWN      1        - we've found this guy                      */                   /* 69658 -rjp */
/*      DISK_XLINK      2        - found more than once -- a cross-link      */                   /* 69658 -rjp */
/*      DISK_DELETED    3        - we deleted this block for recovery later  */                   /* 69658 -rjp */
/*                                                                           */                   /* 69658 -rjp */
/*****************************************************************************/                   /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
/* Multiply by 2 for the bit field width and divide by 8 (bits per byte) */                       /* 69658 -rjp */
#define BITADDR(n)      ((n)>>2)        /* BITADDR = offset of byte containing bits */            /* 69658 -rjp */
/* Multiply by 2 for the bit field width and keep position in byte */                             /* 69658 -rjp */
#define BITPOS(n)       (6-((((USHORT)(n))<<1)&0x7))    /* BITPOS = 6, 4, 2 or 0 */               /* 69658 -rjp */
/* bitfield is 2 bits wide */                                                                     /* 69658 -rjp */
#define BITMASK         0x3                                                                       /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
USHORT SetHugeUseMapBits ( UCHAR huge * usemap, ULONG lsn, USHORT value )                         /* 69658 -rjp */
{                                                                                                 /* 69658 -rjp */
  register USHORT position;                                                                       /* 69658 -rjp */
  register USHORT mask;                                                                           /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  usemap += BITADDR ( lsn );            /* usemap now points to byte of interest */               /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  position = BITPOS ( lsn );            /* position = 6, 4, 2 or 0 */                             /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  mask = ( BITMASK << position );       /* mask = 11000000, 00110000, 00001100 or 00000011 */     /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  *usemap = ( *usemap & ~mask ) | ( ( value & BITMASK ) << position ); /* set bits in the byte */ /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  return ( value );                                                                               /* 69658 -rjp */
}                                                                                                 /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
USHORT QueryHugeUseMapBits ( UCHAR huge * usemap, ULONG lsn )                                     /* 69658 -rjp */
{                                                                                                 /* 69658 -rjp */
  register USHORT position;                                                                       /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  usemap += BITADDR ( lsn );            /* usemap now points to byte of interest */               /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  position = BITPOS ( lsn );            /* position = 6, 4, 2 or 0 */                             /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  return ( ( *usemap >> position ) & BITMASK );     /* query bits in the byte */                  /* 69658 -rjp */
}                                                                                                 /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
USHORT SetUseMapBits ( UCHAR * usemap, ULONG lsn, USHORT value )                                  /* 69658 -rjp */
{                                                                                                 /* 69658 -rjp */
  register USHORT position;                                                                       /* 69658 -rjp */
  register USHORT mask;                                                                           /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  usemap += BITADDR ( lsn );            /* usemap now points to byte of interest */               /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  position = BITPOS ( lsn );            /* position = 6, 4, 2 or 0 */                             /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  mask = ( BITMASK << position );       /* mask = 11000000, 00110000, 00001100 or 00000011 */     /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  *usemap = ( *usemap & ~mask ) | ( ( value & BITMASK ) << position ); /* set bits in the byte */ /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  return ( value );                                                                               /* 69658 -rjp */
}                                                                                                 /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
USHORT QueryUseMapBits ( UCHAR * usemap, ULONG lsn )                                              /* 69658 -rjp */
{                                                                                                 /* 69658 -rjp */
  register USHORT position;                                                                       /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  usemap += BITADDR ( lsn );            /* usemap now points to byte of interest */               /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  position = BITPOS ( lsn );            /* position = 6, 4, 2 or 0 */                             /* 69658 -rjp */
                                                                                                  /* 69658 -rjp */
  return ( ( *usemap >> position ) & BITMASK );     /* query bits in the byte */                  /* 69658 -rjp */
}                                                                                                 /* 69658 -rjp */
