/*static char *SCCSID = "@(#)check.c    6.3 92/02/19";*/
/*static char *SCCSID = "@(#)check.c    6.3 92/02/19";*/
/***    CHECK.C - Routines to check file system consistency
 *
 *      CHKDSK
 *      Gregory A. Jones
 *      Copyright 1988 Microsoft Corporation
 *
 *      Modification history:
 *      G.A. Jones      06/06/88                Original for Pinball 1.0.
 *      P.A. Williams   05/16/89        Rewrote chk_check_name to check
 *                                                              any string given a length parameter
 *      P.A. Williams   05/22/89        PTR B783383-Added DIRENT name check under
 *                                                              DIRBLK section of chk_validate_structure.
 *      P.A. Williams   05/25/89        In chk_gather_structure (TYPE_AB section),
 *                                                              if an AB is bad it is not marked
 *                                                              deleted because it will be picked up under
 *                                                              the deleted FNODE anyway.
 *      P.A. Williams   05/30/89        Added function chk_check_sparedb that is
 *                                                              called from chk_validate_elementary to
 *                                                              check consistency of the spare DB list.
 *                                                              PTR B783730
 *      P.A. Williams   06/05/89        Added the function chk_check_codepages
 *                                                              to check integrity of code page pointers
 *                                                              in spare block. Changed memcmp call to
 *                                                              com_strcmpi for name comparison.
 *      P.A. Williams   06/08/89        Changed hotfix block size to 1 sector.
 *      P.A. Williams   06/16/89        Changed chk_check_name to be codepage
 *                                                              sensitive and also to detect illegal
 *                                                              trailing blanks or dots.
 *      P.A. Williams   06/21/89        Changed DIRBLK section of chk_validate_
 *                                                              structure to check the DF_NEEDEAS bit
 *                                                              in the DIRENT.
 *      P.A. Williams   07/09/89        Added TRACE info to chk_validate_structure
 *                                                              to flag bad DIRENT name ordering.
 *      P.A. Williams   07/11/89        In chk_validate_structure, we check that
 *                                                              the cchThisEntry parameter of a DIRENT is
 *                                                              valid before we use DOWN_PTR().
 *      P.A. Williams   08/09/89        chk_check_name now checks for invalid
 *                                                              code page indices.
 *      P.A. Williams   08/22/89        Optimized searching of bitmaps. Made
 *                                                              complete scanning of bitmaps DEBUG code
 *                                                              in the case of informational msgs so that
 *                                                              normally we can stop scanning when the first
 *                                                              lost data is found.
 *      56340   11/05/92 BJH    Changed to recognize bitmap indirect larger than one
 *                              block(2K).
 *
 *      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.
 * @d01 73790   93SEP03   GY    <Support V4FORMAT.  GHASSAN@AUSVM1>
 * @124067      95SEP20  DAA    Don't call use_sectors until we load bitmap.
 */

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

//pcr cutter port // static     USHORT  usNewPercent = 0;
//pcr cutter port // static     USHORT  usOldPercent = 0;

#ifdef TESTING
UCHAR ch='\0', chr='\0';
#endif

/***    chk_check_name - check a name for invalid characters
 *
 *      This function checks the name of an object (eg filename, EA name)
 *      to make sure that all characters in the name are valid. If any
 *      are invalid, it returns an error code.  The string of invalid
 *      filename characters is a global variable. Note that NULL is not
 *      considered a valid characters so the len parameter should reflect
 *      the number of characters to check before the NULL.
 *
 *      This routine also checks that there are no trailing blanks or
 *      dots at the end of the name because this is illegal.
 *
 *      chk_check_name (name, cp_index, len)
 *
 *      ENTRY           name     - pointer to a string of characters
 *                      cp_index - code page casemap index
 *                      len      - the number of characters to be checked
 *
 *      EXIT            Returns TRUE if the name is OK, FALSE if not
 *
 *      CALLS           strchr
 *                      memcmp
 *                      chk_check_dbcs
 *
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */
USHORT chk_check_name (PUCHAR name, PBYTE cp_index, USHORT len)
{
  register USHORT i;
  USHORT bad_trail_cnt=0;       /* Keeps track of no of bad trailing  */
                                /* dots and blanks                    */

  /*
   *  Special case:  \001\001 is a valid name
   *  (for the special '..' entry)
   */
  if( (len == 2) && (name[0] == '\001') && (name[1] == '\001') ){

    return( TRUE );
  }

  /* BUGBUG - the first condition is temporary  */
  if (*cp_index == 0xff)
        *cp_index = 0;

  if (spb.culCP &&
  ((*cp_index >= (BYTE)chk_cp_count) || !chk_valid_cps[*cp_index])) {

        /*  check code page index */

#if defined(TRACE) || defined(CPTRACE)
        printf ("chk_check_name: invalid cp_index = %x\n", *cp_index);
        fflush (stdout);
#endif
#if 0
        *cp_index = 0;      /* set to 0 because this codepage will always */
                            /* have internal chkdsk tables defined for it */
                            /* because the end and .. dirent use it.      */
        *name = '\2';
#endif
        return (FALSE);
        }

  if ((len == 2) && !memcmp (name, "\001\001", 2))
    return (TRUE);

  for (i = 0; i < len; i++)
  {
    if (chk_check_dbcs (name[i], *cp_index))    /* check DBCS chars  */
    {
           /* BUGBUG - what are legal values for trail byte */
           i++;
           bad_trail_cnt = 0;
    }
    else
    {
      if (strchr (iv_chars, name [i]))  // look for this char in invalid list
             return (FALSE);                 // found it -- it's invalid
      if ((name [i] == '.') || (name [i] == ' '))
             bad_trail_cnt++;
           else
             bad_trail_cnt=0;
    }
  }

  /*
   *  If there are no bad trailing characters,
   *  the name is valid.
   */
  return (bad_trail_cnt == 0);

}

/***    chk_delete_name - cache a filename for later deletion
 *
 *      This function is called to store the pathname for an inconsistent
 *      file, so that it can be deleted later.  The file cannot be deleted
 *      during the checking phase, since BTREE reshuffling would cause us
 *      to lose our place -- we'd check some files twice, some not at all,
 *      etc.
 *
 *      The full pathname to the inconsistent file is stored in an
 *      allocated buffer.  These names are kept in a dynamic array
 *      of pointers, called "chk_namecache".
 *
 *      The pathname is built from two components:  the directory that the
 *      file is in, and the filename itself.  The directory name is assumed
 *      to already end with a backslash.
 *
 *      chk_delete_name (path, dp)
 *
 *      ENTRY           path - directory the name is in
 *                      dp   - pointer to DIRENT containing name to delete
 *
 *      EXIT            No return value
 *
 *      CALLS           malloc
 *                      realloc
 *
 *      WARNINGS        If insufficient memory, the name will not be added
 *                      to the list.  It will thus not be deleted during
 *                      the deletion phase;  the user can still run CHKDSK
 *                      again to catch more files the second time around.
 *
 *      EFFECTS         Allocates memory
 */
void chk_delete_name (path, dp)
  UCHAR *path;
   DIRENT *dp;
{
  UCHAR *p, **q;
  USHORT len;

#ifdef TRACE
  fprintf (stderr, "chk_delete_name (`%s', `%s' (len %d))\n", path,
                    dp->bName, dp->cchName);
  fflush (stderr);
#endif

  len = strlen (path) + dp->cchName;                    /* length of complete pathname */
  if ((p=malloc (len+1)) == NULL)               /* try to get memory for name */
    return;                                     /* no memory - can't save it */

  if (chk_ndel == chk_maxdel) {                 /* overflowed current cache */
    if ((q=malloc ((chk_maxdel+16) * sizeof (UCHAR *))) == NULL) {
      free (p);                                 /* free name buffer */
      return;                                   /* no memory to expand */
    }
    if (chk_namecache != NULL) {
      memcpy ((UCHAR *)q, (UCHAR *)chk_namecache, chk_ndel * sizeof (UCHAR *));
      free (chk_namecache);                     /* free old name cache */
    }
    chk_namecache = q;                          /* point to bigger buffer */
    chk_maxdel += 16;                           /* room for 16 more now */
  }

  strcpy (p, path);                             /* copy path to buffer */
  strncat (p, dp->bName, dp->cchName); /* append filename */
  p [len] = '\0';                               /* null-terminate the name */
  chk_strnupr(&p[len-dp->cchName], dp->bCodePage, /* casemap the name now */
              dp->cchName);


  chk_namecache [chk_ndel++] = p;               /* point to name buffer */

  chk_no_writing (CHK_DELETE_FILE_STR, &p);
}

/***    chk_display_name - print the name of the file we're working on
 *
 *      This function is called when the user has given the /V switch.
 *      It displays the full pathname of the file or directory being
 *      processed by CHKDSK's validation process.
 *
 *      chk_display_name (path, name)
 *
 *      ENTRY           path - pathname of the directory containing the file
 *                      ent  - pointer to the file's DIRENT
 *
 *      EXIT            No return value
 *
 *      CALLS           printf
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Affects display
 */
void chk_display_name (path, ent)
  UCHAR *path;
   DIRENT *ent;
{
  UCHAR *p=outbuf;

  strcpy (outbuf, disk_name);
  strcat (outbuf, path);
  strncat (outbuf, ent->bName, ent->cchName);
  outbuf [strlen (disk_name) + strlen (path) + ent->cchName] = '\0';
  if (ent->fAttr & ATTR_DIRECTORY)
    CHK_OUT_MSG( SYSTEM_MSG, CHK_DIRECTORY_STR, p);
  else {
   printf ("\t%s\n", outbuf);
   fflush (stdout);
  }
}


/***    chk_check_sparedb - check out spare DIRBLK structures
 *
 *      This function is called by chk_validate_elementary to check out
 *      the consistency of the spare DIRBLK structures in the spare block.
 *
 *      This function sets the spare DIRBLKs in the disk bitmaps and
 *      replenishes the list of spare DIRBLKs if there are any used
 *      entries. Fields in the spare block which indicate the status of
 *      the spare DIRBLKs are check for validiaty.
 *
 *      chk_check_sparedb (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            none
 *
 *      CALLS           use_sectors
 *                      write_scratch
 *                      allocate_middle_sectors
 *                      out_msg
 *                      chk_no_writing
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Modifies global disk map
 *                      Replenishes spare DIRBLK list.
 */
void chk_check_sparedb (void)
{
  USHORT rc, i;
  ULONG sect;

  if (spb.cdbMaxSpare > SPAREDB) {          /* make sure value does not  */
     spb.cdbMaxSpare = SPAREDB;             /* exceed max possible       */
     chk_no_writing(CHK_BAD_SPAREDB_FIELD_STR, NULL);
  }

  if (spb.cdbSpares == spb.cdbMaxSpare) {
     if ((spb.bFlag & SPF_SPARE) &&             /* is spare bit set correctly */
         !chk_no_writing(CHK_BAD_SPAREDB_FIELD_STR, NULL))
         spb.bFlag &= ~SPF_SPARE;
  }
  else if (spb.cdbSpares > spb.cdbMaxSpare) {   /* make sure count doesn't */
          spb.cdbSpares = spb.cdbMaxSpare;      /* exceed max */
          chk_no_writing(CHK_BAD_SPAREDB_FIELD_STR, NULL);
  }


  /*
  Check the existing spare DB list. If any blocks in it are cross-linked
  or out of band, replace them with a fresh block. If we can't get
  a fresh block then use one from the end of the spare DB list to fill
  in the gap.
  */

  i = 0;
  while (i < (USHORT)spb.cdbSpares) {          /* set the unused DBs in the bitmap */
    rc = use_sectors (spb.alsnSpareDirBlks [i], (ULONG)SECTORS_PER_DIRBLK,
                      DISK_CONTROL);

    /* replace sectors if they are out of band or cross-linked */

    if ((rc == DISK_XLINK) || (rc == 0xff))
      if (!chk_no_writing (CHK_BAD_SPAREDB_FIELD_STR, NULL)) {

         if (!(sect = allocate_middle_sectors (SECTORS_PER_DIRBLK,
                                               SECTORS_PER_DIRBLK))) {
            CHK_OUT_MSG( SYSTEM_MSG, CHK_NO_HOTFIX_SPACE_STR, NULL);
                            /* can't get new DB */
            if (i < (USHORT)(spb.cdbSpares-1)) {/* take DB from end of list if can */
              spb.alsnSpareDirBlks[i] = spb.alsnSpareDirBlks[--spb.cdbSpares];
              continue;                 /* go back and check this block */
            }

         }
         else {
           spb.alsnSpareDirBlks[i] = sect;      /* put the fresh DB into the list */
           use_sectors (sect, (ULONG)SECTORS_PER_DIRBLK, DISK_CONTROL);
         }
      }
    i++;
  } /* while */


/* replensish the spare DB list if we are allowed to change the disk */

  if (spb.cdbSpares < spb.cdbMaxSpare)          /* are any spare DBs used? */
     if (!chk_delete_inconsistency)             /* can't replenish, tell user */
        CHK_OUT_MSG( SYSTEM_MSG, CHK_HOTFIXES_USED_STR, NULL);
     else {                                     /* replenish list */

        for (i = (USHORT)spb.cdbSpares; i < (USHORT)spb.cdbMaxSpare; i++) {
           if (!(sect = allocate_middle_sectors (SECTORS_PER_DIRBLK,
                                                 SECTORS_PER_DIRBLK))) {
           CHK_OUT_MSG( SYSTEM_MSG, CHK_NO_HOTFIX_SPACE_STR, NULL);
           break;
           }
           else {
              spb.alsnSpareDirBlks[i] = sect; /* put the fresh DB into the list */
              use_sectors (sect, (ULONG)SECTORS_PER_DIRBLK, DISK_CONTROL);
           }

        } /* for */

        spb.cdbSpares = i;                /* indicate new spare DB list size */

        if (spb.cdbSpares == spb.cdbMaxSpare) /* if list full, reset DB flag */
           spb.bFlag &= ~SPF_SPARE;

        write_scratch ((ULONG)SEC_SPAREB, &spb, 1);/* update spare block info */
        CHK_OUT_MSG( SYSTEM_MSG, CHK_HOTFIX_FIXED_STR, NULL);

     } /* if */
}


/***    chk_validate_elementary - check out elementary file system structures
 *
 *      This function is called to check the consistency of basic file
 *      system structures such as the superblock, root FNODE, root DIRBLK,
 *      bitmap indirect list, and bad block list.  It also reads in the
 *      bitmaps, to make sure that no bitmap sectors have gone bad.
 *      This function assumes that the chk_allocate_maps function has been
 *      called first to allocate the bitmaps and disk map.
 *
 *      This function also validates the hotfix replacement information.
 *      All replacement sectors are checked to make sure they are readable
 *      and writable.  If any are in use, and if disk changes are authorized,
 *      the files which use the replacement sectors are reallocated and the
 *      replacements are returned to the hotfix list.
 *
 *      If changes to the disk are allowed, this routine sets the
 *      CHKDSK timestamp in the superblock to the current time.
 *
 *      chk_validate_elementary (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            OK      if elementary structures are consistent
 *                      NOT_OK  if elementary structures are inconsistent
 *
 *      CALLS           chk_exit_error
 *                      chk_check_sparedb
 *                      chk_check_codepages
 *                      read_scratch
 *                      chk_read_sectors
 *                      use_sectors
 *                      write_scratch
 *
 *      WARNINGS        May terminate program prematurely if insufficient mem.
 *                      Bitmaps should be allocated first
 *
 *      EFFECTS         Reads in bitmaps
 *                      Modifies global disk map
 *                      Sets the global variable root_fnode
 */
USHORT chk_validate_elementary (void)
{
  UCHAR inbuf [80], *boot;
   FNODE *f;
  ULONG *scratch, ptr, nbitmaps, nbadblocks;
  USHORT i;
  UCHAR huge *p;
  BOOL fHotfixChanges = FALSE;
  USHORT cBitMaps, cIndBlks;                                    //56340

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

/*
 * Validate the super block
 */

  if (read_scratch ((ULONG)SEC_SUPERB, &sb, 1) ||       /* superb is 1 sect */
      read_scratch ((ULONG)SEC_SPAREB, &spb, 1) ||      /* spareb is 1 sect */
      ((boot=chk_read_sectors (0L, SEC_SUPERB)) == NULL)) {
#ifdef TRACE
    fprintf (stderr, "chk_validate_elementary:  couldn't read superblock.\n");
    fflush (stderr);
#endif
    chk_exit_error (INSF_MEM_ERROR);
  }

// @124067 code moved from here
/*
 * Look for the superblock's signatures.  If they're not there, then
 * this disk is either completely roached or is not Pinball at all.
 * Ask the user if he really wants to continue.  If he does, then fix
 * the signatures and save the superblock.  That way, if it's just the
 * sigs that are wrong, we won't have to go through rebuilding the whole
 * tree.
 */

  if ((sb.sig1 != SBSIG1) || (sb.sig2 != SBSIG2) ||
      (spb.sig1 != SPSIG1) || (spb.sig2 != SPSIG2) ||
      strncmp (boot+54, "HPFS    ", 8)) {
    CHK_COMPUTMSG( SYSTEM_MSG, NULL, 0, CHK_NON_PINBALL_STR, msgfilename, STDERR_CODE,
               YES_NO_RESPONSE, inbuf, 80);
    if (inbuf [0] == NO_ANS)
      chk_exit_error (NOT_PB_ERROR);
    sb.sig1 = SBSIG1;
    sb.sig2 = SBSIG2;
    spb.sig1 = SPSIG1;
    spb.sig2 = SPSIG2;
    strncpy (boot+54, "HPFS    ", 8);
    if (chk_delete_inconsistency) {
      write_scratch (0L, boot, SEC_SUPERB);
      write_scratch ((ULONG)SEC_SUPERB, &sb, 1);
      write_scratch ((ULONG)SEC_SPAREB, &spb, 1);
#ifdef  V4FORMAT                /* @d01         */
      chk_repair_disk++;        /* play it safe */
#endif
    }
  }
  FREE_BLOCK (boot);

/*
 * Set the CHKDSK timestamp and save the superblock.
 */

  if (chk_delete_inconsistency) {
    sb.datLastChkdsk = get_time ();
    write_scratch ((ULONG)SEC_SUPERB, &sb, 1);
  }

  number_of_sectors &= ~3L;             /* make vol size a cache block boundary */

  if ((sb.culSectsOnVol != number_of_sectors) || /* sblk and part. table conflict */
      (sb.culSectsOnVol < sb.culNumBadSects)) {       /* more bad sectors than sectors */
#ifdef TRACE
    fprintf (stderr,
             "v_e:  superblock #sectors is %lx, part table #sectors is %lx.\n",
             sb.culSectsOnVol, number_of_sectors);
    fflush (stderr);
#endif
    return (NOT_OK);
  }

  /*
   * lsnFirstDirBlk and lsnLastDirBlk must describe properly
   *  aligned DIRBLKS--i.e. lsnFirstDirBlk and lsnLastDirBlk+1
   *  must both be multiples of sectors-per-dirblk.
   */
  if ( (sb.lsnFirstDirBlk % SECTORS_PER_DIRBLK != 0) ||
       ((sb.lsnLastDirBlk + 1) % SECTORS_PER_DIRBLK != 0) ||
       (sb.clsnDirBlkBand > MAX_DBS_PER_DIRBAND * SECTORS_PER_DIRBLK) ){
#ifdef TRACE
    fprintf (stderr,
             "v_e:  superblock DIRBLK band runs from %lx to %lx.\n",
             sb.lsnFirstDirBlk, sb.lsnLastDirBlk );
    fflush (stderr);
#endif
    return(NOT_OK);
  }

/*
 * Validate the root FNODE
 */

  root_fnode = sb.lsnRootFNode;         /* save for later use */
  if ((f=chk_read_sectors (root_fnode, SECTORS_PER_FNODE)) == NULL) {
#ifdef TRACE
    fprintf (stderr, "v_e:  can't read root fnode.\n");
    fflush (stderr);
#endif
    return (NOT_OK);
  }

  if (f->sig != FNSIGVAL) {             /* check root FNODE's signature */
#ifdef TRACE
    fprintf (stderr, "v_e:  root fnode has bad signature\n");
    fflush (stderr);
#endif
    FREE_BLOCK (f);
    return (NOT_OK);
  }
  FREE_BLOCK (f);               /* we don't need the root FNODE anymore */

/*
 * Now check the number of bitmap blocks
 */
  cBitMaps = (USHORT)((number_of_sectors - 1) / (BYTES_PER_BITMAP * 8)) + 1;    //56340
  cIndBlks = (( (cBitMaps * sizeof (ULONG)) - 1) / (SECSIZE * SPB)) + 1;        //56340

// @124067 removed code from here

  if ( (scratch=chk_read_sectors (sb.rspBitMapIndBlk.lsnMain, (cIndBlks * SPB) )) == NULL) {    //56340
#ifdef TRACE
    fprintf (stderr, "v_e:  can't read bitmap indirect\n");
    fflush (stderr);
#endif
    return (NOT_OK);
  }

  for (nbitmaps = 0; nbitmaps < ((cIndBlks*SECSIZE*SPB)/sizeof (ULONG)); nbitmaps++)    /* find last bitmap */  //56340
    if (!scratch [nbitmaps])            /* break if end of list */
      break;

  if (nbitmaps != cBitMaps) {                                                   //56340
#ifdef TRACE
    fprintf (stderr, "v_e:  have %d bitmaps, should have %d.\n", nbitmaps,
             cBitMaps);                                                         //56340
    fflush (stderr);
#endif
    FREE_BLOCK (scratch);
    return (NOT_OK);
  }

/*
 * Now read in the bitmaps
 */

  for (i=0, p=bitmap; i < (USHORT)nbitmaps; i++, p += BYTES_PER_BITMAP) {
    if (read_scratch (scratch [i], p, SPB)) {
#ifdef TRACE
      fprintf (stderr, "v_e:  can't read primary bitmap %d.\n", i);
      fflush (stderr);
#endif
      FREE_BLOCK (scratch);
      return (NOT_OK);
    }
    if (use_sectors (scratch [i], (ULONG)SPB, DISK_CONTROL)) {
      FREE_BLOCK (scratch);
           return (NOT_OK);
    }

  } /* for */

// @124067 move as much code AFTER the bitmap load as possible, so
// @124067 use_sectors can check the bitmap alloc for those objects.

/*
 * Mark the boot block and superblock in the disk map
 */

  use_sectors (0L, (ULONG)SEC_SUPERB+SPB, DISK_CONTROL);

  use_sectors (sb.rspBitMapIndBlk.lsnMain, (ULONG)(cIndBlks * SPB), DISK_CONTROL);

// @124067 ends

  if ( use_sectors (spb.lsnHotFix, (ULONG)SPB, DISK_CONTROL) ||
       read_scratch (spb.lsnHotFix, hotfix_tbl, SPB) ) {
#ifdef TRACE
    fprintf (stderr, "v_e:  can't read hotfix table sector %lx.\n", spb.lsnHotFix);
    fflush (stderr);
#endif
    FREE_BLOCK (scratch);
    return (NOT_OK);
  }

/*
 * Check the validity of culMaxHotFixes.  If it's not equal to what FORMAT
 * usually sets it to, we use the hotfix table to make sure it's correct.
 */

  if (spb.culMaxHotFixes != ((number_of_sectors > 40000L) ?
                      100L : number_of_sectors / 400L))
    chk_verify_hfmax ();

  if (spb.culHotFixes > spb.culMaxHotFixes) {
    spb.culHotFixes = spb.culMaxHotFixes;
    if (!chk_no_writing (CHK_BAD_HFMAX_STR, NULL))
      write_scratch ((ULONG)SEC_SPAREB, &spb, 1);
  }

  if (spb.culHotFixes) {                        /* some hotfix replacements are in use */
    if (chk_delete_inconsistency) {
      chk_hotfix_masks = _fmalloc ((size_t)spb.culMaxHotFixes);
      chk_clean_hotfixes (&spb, hotfix_tbl);  /* reallocate their files */
    }
    else
      chk_no_writing (CHK_HOTFIXES_USED_STR, NULL);
  }

  for (i=0; i < (USHORT)spb.culMaxHotFixes; i++) {
#ifdef CHECK_HOTFIXES
    if (read_scratch (hotfix_tbl [i+spb.culMaxHotFixes], scratch, 1) ||
        write_scratch (hotfix_tbl [i+spb.culMaxHotFixes], scratch, 1))
      printf ("Replacement sector %08lx is bad.\n", hotfix_tbl [i+spb.culMaxHotFixes]);
#endif

    if( (hotfix_tbl [i+spb.culMaxHotFixes] <= SEC_SUPERB) ||
        (hotfix_tbl [i+spb.culMaxHotFixes] > number_of_sectors) ) {

        /*
         *  This replacement sector LSN is not in a usable area
         *  of the disk.  If we can write to the disk, we'll
         *  allocate a new replacement sector.
         */
        if ( chk_delete_inconsistency ) {

            hotfix_tbl [i+spb.culMaxHotFixes] = allocate_early_sectors( 1, 1 );
            fHotfixChanges = TRUE;
        }
    }


    if (use_sectors (hotfix_tbl [i+spb.culMaxHotFixes], 1L, DISK_CONTROL)) {
        FREE_BLOCK(scratch);
        return(NOT_OK);
    }
  }

  if( fHotfixChanges ) {

    /*
     *  We allocated a new replacement sector in place of
     *  an unusable one, so we have to write the hotfix sector.
     */
    write_scratch (spb.lsnHotFix, hotfix_tbl, SPB);
  }

/*
 * Account for the DIRBLK banding stuff.
 */

  if (read_scratch (sb.lsnDirBlkMap, db_bitmap, SPB)) {
#ifdef TRACE
    fprintf (stderr, "v_e:  can't read db_bitmap, sector %lx.\n", sb.lsnDirBlkMap);
    fflush (stderr);
#endif
    return (NOT_OK);
  }

  if( use_sectors (sb.lsnFirstDirBlk, sb.clsnDirBlkBand, DISK_CONTROL) ||
      use_sectors (sb.lsnDirBlkMap, (ULONG)SPB, DISK_CONTROL) ||
      use_sectors (sb.lsnSidTab, 8L, DISK_CONTROL) ) {

      FREE_BLOCK(scratch);
      return(NOT_OK);
  }

/*
 * Now check the number of blocks in the bad block chain
 */

  ptr = sb.rspBadBlkList.lsnMain;               /* start at top of bad block list */
  nbadblocks = 0;               /* no bad blocks yet */
  do {
    if (read_scratch (ptr, scratch, SPB)) {
#ifdef TRACE
      fprintf (stderr, "v_e:  can't read bad block list sector %lx.\n", ptr);
      fflush (stderr);
#endif
      FREE_BLOCK (scratch);
      return (NOT_OK);
    }

    if (use_sectors (ptr, (ULONG)SPB, DISK_CONTROL)) {
        FREE_BLOCK(scratch);
        return(NOT_OK);
    }

    for (i=1; (i<512) && scratch [i]; i++)
      use_sectors (scratch [i], 1L, DISK_BAD);  /* mark blocks as bad */
    nbadblocks++;                               /* count up bad blocks */
    ptr = scratch [0];                          /* chain to forward pointer */
  } while (ptr);                                /* end if zero forward pointer */

  if ((nbadblocks * 511 < sb.culNumBadSects) ||        /* too few bad list blocks */
      (nbadblocks * 511 > sb.culNumBadSects+511)) {     /* or too many */
#ifdef TRACE
    fprintf (stderr, "v_e:  are %d bad list blocks, should be %d.\n",
             nbadblocks, (sb.culNumBadSects-1) / 511 + 1);
    fflush (stderr);
#endif
    FREE_BLOCK (scratch);
    return (NOT_OK);
  }

  chk_check_sparedb ();

  /*
   *  check the integrity of the codepages and
   *  load them from disk.
   */
  chk_set_cp_tables ();


  FREE_BLOCK (scratch);         /* deallocate our scratch areas */
  return (OK);                  /* elementary structures check out */
}

/***    chk_validate_subdir - check out a subdirectory
 *
 *      This function is used to call chk_validate_structure and allocate
 *      extra variables on the stack, which are not needed for some
 *      structures.  This is done so that the depth of directory BTREEs
 *      can be checked.
 *
 *      chk_validate_subdir (lsn, parent, fnode, type, name, mem)
 *
 *      ENTRY           All parameters as for chk_validate_structure
 *
 *      EXIT            Return code as from chk_validate_structure
 *
 *      CALLS           chk_enter_recursive
 *                      chk_exit_recursive
 *                      chk_validate_structure (indirectly recursive)
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Modifies and restores global pointers
 *                      chk_depth, chk_maxdepth
 */

/*
 * The following macro equals the number of bytes used in each call to
 * chk_validate_subdir, used by the stack checking routines.  If any local
 * variables or parameters are added, this macro should be updated.
 */

#define VSUB_STACK 32

USHORT chk_validate_subdir (lsn, parent, fnode, type, name, b)
  ULONG lsn;
  ULONG parent;
  ULONG fnode;
  USHORT type;
  UCHAR *name;
  union blk *b;
{
  USHORT d=0, m=0, retcode;
  USHORT *p, *q;

  if (chk_enter_recursive (VSUB_STACK)) {       /* check stack depth */
    CHK_ERROR_MSG( CHK_INSF_MEM_CONTINUING_STR);        /* display error message */
    chk_exit_recursive (VSUB_STACK);            /* readjust stack depth */
    return (OK);                                /* don't delete parent */
  }

  p = chk_depth;
  q = chk_maxdepth;

  chk_depth = &d;
  chk_maxdepth = &m;

  retcode = chk_validate_structure (lsn, parent, fnode, type, name, b);

  chk_depth = p;
  chk_maxdepth = q;
  chk_exit_recursive (VSUB_STACK);
  return (retcode);
}


/***    chk_check_system - check out file system
 *
 *      This function is called by the main routine to check the consistency
 *      of the file system structure.  It assumes that the main routine has
 *      already called chk_validate_elementary() to read in the bit maps and
 *      check out the basic file system structures.  First, the consistency
 *      of the file system structures is checked by recursively tracing
 *      from the root FNODE.  Once this is done, all space referenced by
 *      the file system has been marked in the disk map, and recovery can
 *      start.
 *
 *      The user may wish to recover data only from "orphaned" space on the
 *      disk (space which is not used by the file system, but is marked as
 *      "in use" in the bitmaps) or look for recognizable structures in free
 *      space as well.  At this stage, all space used by the file system is
 *      marked as "known" in the disk map;  all other areas are marked as
 *      DISK_UNKNOWN.  To search free space and orphaned space, we simply
 *      check all UNKNOWN sectors.  To avoid free space, and just check
 *      orphaned blocks, we can first use the bitmap to mark free sectors
 *      as "known".
 *
 *      Each UNKNOWN sector will be read and checked for a signature.  If
 *      a signature is present, chk_recover_block() is called to recover that
 *      lost structure.
 *
 *      Finally, the bit maps will be updated on disk, and any recovered
 *      structures will be saved to the disk in the "/found" directory.
 *
 *      chk_check_system (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            No return value
 *
 *      CALLS           chk_recover_block
 *                      chk_save_structures
 *                      chk_validate_subdir
 *                      write_bit_maps
 *
 *      EFFECTS         May fix up disk by deleting inconsistent structures and
 *                      leaving their space marked as in use.
 */
void chk_check_system (void)
{
  register ULONG l;
  USHORT i, percent;
  USHORT lost_data = FALSE;
  UCHAR *namebuf;
  UCHAR *p=outbuf;      /* used for message printing, must point at outbuf */
  union blk *b;
  register ULONG diskmap;                                      /* 69658 -rjp */
  register USHORT cUse;                                        /* 69658 -rjp */
  UCHAR huge *bmp;
  ULONG loop_reps = (number_of_sectors / 20L);
  ULONG reps;
  ULONG lsnFoundRoot;   /*  LSN of root dirblk of Found.xxx directory   */
  PCHAR pszBuf;         /*  Buffer for name compares                    */
  BOOL fSeenDotDot;     /*  Used when traversing Found.xxx directory    */

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

/*
 * Recursively check out the file system, starting at the root FNODE.
 */

  memset (chk_curpath, '\0', 1024);     /* clear current-path buffer */

  namebuf = malloc (MAX_NAME_LEN);      /* get a name comparison buffer */
  memset (namebuf, '\0', MAX_NAME_LEN); /* and clear it */

  if (chk_verbose_display) {            /* display initial "Directory D:\" */
    strcpy (outbuf, disk_name);
    strcat (outbuf, "\\");
    CHK_OUT_MSG( SYSTEM_MSG, CHK_DIRECTORY_STR, p);
  }

  b = (union blk *)chk_read_sectors (root_fnode, SECTORS_PER_FNODE);

  if ((i=chk_validate_subdir (root_fnode, root_fnode, root_fnode, TYPE_SUBDIR,
                              namebuf, b)) == SORT_ME)
    chk_sort_directory (root_fnode, NULL);  /* resort the whole directory */
  else if (i != OK)                     /* root directory is screwed up */
    chk_new_root (root_fnode);          /* create a new root DIRBLK */

#ifdef  V4FORMAT
/*
 * If we have an error validating the subdirectory AND we can repair
 * the disk (i.e. make changes to it) then we update the repair flag.
 */
  if( (i != OK) && chk_delete_inconsistency)
    chk_repair_disk++;                  /* disk has been repaired */
#endif

  FREE_BLOCK (b);                       /* deallocate the root FNODE */
  free (namebuf);                       /* free the name comparison buffer */

  if (chk_delete_inconsistency && chk_namecache) {  /* found something to delete! */
    for (i=0; i<chk_ndel; i++) {            /* for each cached name */
      chk_delete_file (chk_namecache [i]);  /* delete that file */
      free (chk_namecache [i]);         /* and deallocate the name buffer */
    }
    free ((UCHAR *)chk_namecache);      /* free the pointer array */
  }

  chk_recovering = 1;                   /* we're done checking now */

  if (chk_recover_data) {               /* we want to recover lost stuff */
    CHK_OUT_MSG( SYSTEM_MSG, CHK_SCANNING_ORPHANS_STR, NULL);

/*
 * If we don't want to search free space, mark it as FREE in the
 * disk map, instead of UNKNOWN.  That way the next loop will skip
 * the free sectors.  Do the same for the dirblk bitmap/usemap.
 */

    if (!chk_search_free) {             /* we don't want to look at free areas */
/*
 * Look through the dirblk band map for anything we don't know about yet.
 * Try to recover each such sector.
 */
        for (l=0; l<sb.clsnDirBlkBand/SECTORS_PER_DIRBLK; l++) {
                if ((QueryUseMapBits ( chk_db_usemap, l * SECTORS_PER_DIRBLK ) == DISK_UNKNOWN) && /* 69658 -rjp */
                        !(db_bitmap [l >> 3] & (1 << (l & 7))))  /* not found and not free */
                        chk_recover_block (l*SECTORS_PER_DIRBLK + sb.lsnFirstDirBlk);
    } /* for */
/*
 * Look through the disk map for anything we don't know about yet.
 * Try to recover each such sector.
 */
        percent = 0;

        for (l=0, bmp=bitmap, diskmap=0L, reps=loop_reps;      /* 69658 -rjp */
                l<number_of_sectors; l++, diskmap++) {

                if (!reps--) {                                          /* time to update the % display */
                        if (!stdout_redir) {
                           percent += 5;
                                itoa (percent, outbuf, 10);
                                out_msg( CHK_PERCENT_SCANNED_STR, p);
                                printf ("\r"); fflush (stdout);
                        }
                        reps = loop_reps;
                }

                cUse = QueryHugeUseMapBits ( chk_disk_map, diskmap );  /* 69658 -rjp */
                if ((( cUse == DISK_UNKNOWN) ||             /* haven't seen this one       */ /* 69658 -rjp */
                        ( cUse == DISK_DELETED)) &&         /* or deleted for recovery     */ /* 69658 -rjp */
                        !(*bmp & (1 << (l & 7))))           /* and not free                */
                        chk_recover_block (l);              /* try to recover this object  */

                        if (((USHORT)l & 7) == 7)           /* end of this bitmap byte?    */
                        bmp++;                              /* yes, advance bitmap pointer */
        } /* for */


                itoa (100, outbuf, 10);
                out_msg( CHK_PERCENT_SCANNED_STR, p);

} /* if !chk_search_free */

else {

      for (l=0; l<sb.clsnDirBlkBand; l += SECTORS_PER_DIRBLK) {
                        if (QueryUseMapBits ( chk_db_usemap, l ) == DISK_UNKNOWN)  /* if not known */ /* 69658 -rjp */
                                chk_recover_block (l + sb.lsnFirstDirBlk);
      }

        percent = 0;

        for (l=0, bmp=bitmap, diskmap=0L, reps=loop_reps;      /* 69658 -rjp */
                        l<number_of_sectors; l++, diskmap++) {

                if (!reps--) {                                          /* time to update the % display  */
                        if (!stdout_redir) {
                           percent += 5;
                                        itoa (percent, outbuf, 10);
                                        out_msg( CHK_PERCENT_SCANNED_STR, p);
                                        printf ("\r"); fflush (stdout);
                        }
                        reps = loop_reps;
        }

        cUse = QueryHugeUseMapBits ( chk_disk_map, diskmap );  /* 69658 -rjp */
        if (( cUse == DISK_UNKNOWN) ||          /* haven't seen this one        */ /* 69658 -rjp */
            ( cUse == DISK_DELETED))            /* or deleted for recovery      */ /* 69658 -rjp */
                chk_recover_block (l);          /* try to recover this object   */

        if (((USHORT)l & 7) == 7)               /* end of this bitmap byte?     */
          bmp++;                                /* yes, advance bitmap pointer  */

    } /* for */

                itoa (100, outbuf, 10);
                out_msg( CHK_PERCENT_SCANNED_STR, p);

    /* BUGBUG - recover raw data from orphaned areas here */
}

/*
 * Save any newly found bad blocks to the bad block list.
 */

  if (chk_delete_inconsistency && num_bad_sectors)
    save_bad ();

/*
 * All done recovering.  Whatever's left is unrecognizable, therefore
 * unrecoverable.  Mark it as free.  Don't save the bitmaps yet, because
 * we may need to modify them if we allocate DIRBLKs when recovering files.
 *
 * If we were forced to abort recovery because we ran out of heap,
 * we refrain from marking unvisited sectors as free--this will
 * allow the user to run chkdsk /f:2 again and recover more
 * lost chunks of the file system.
 */

    if ( !chk_fRecoverOutofMem ) {

        /*
         *  Mark all unvisited sectors of the dirblk
         *  band as free.
         */
        for (l=0L; l<sb.clsnDirBlkBand; l += SECTORS_PER_DIRBLK)
            if (QueryUseMapBits ( chk_db_usemap, l ) == DISK_UNKNOWN)  /* 69658 -rjp */
                set_db_bit (l / SECTORS_PER_DIRBLK);
        /*
         *  Mark all unvisited sectors outside of the dirblk
         *  band as free.
         */
        for (l=0L,diskmap=0L; l<number_of_sectors; l++,diskmap++)      /* 69658 -rjp */
          if ( QueryHugeUseMapBits ( chk_disk_map, diskmap ) == DISK_UNKNOWN)  /* 69658 -rjp */
            set_bit (l);
    }

    /*
     *  If we printed a %done display,
     *  advance to a new line.
     */
    if (!stdout_redir)
           printf ("\n"); fflush (stdout);

/*
 * Now that the bitmaps are saved and the file system's consistent,
 * we can safely create files.  Create a file or subdirectory for
 * each lost fragment.
 */

    if (chk_lost) {

      /*
       *  we found lost data on the disk--write it
       *  to the "/found" directory.
       */
      lsnFoundRoot = 0;
      chk_save_structures ( &lsnFoundRoot );

      /*
       *  If we haven't yet run out of heap,
       *  traverse this newly-created directory of
       *  recovered material, to ensure that all its
       *  subdirectories are well-ordered.
       */
      if( !chk_fRecoverOutofMem ) {

          pszBuf = malloc( MAX_DIRENT );

          if( pszBuf != NULL ) {

                      pszBuf[0] = '\0';
                      fSeenDotDot = FALSE;
                      chk_badly_ordered( lsnFoundRoot, pszBuf, &fSeenDotDot );
                      free( pszBuf );
          }
      }
    }

/*
 * We've processed everything that could conceivably use a hotfix
 * sector.  If there are still entries in the hotfix table, erase
 * them and reset culHotFixes.
 */

    if (spb.culHotFixes) {
      CHK_OUT_MSG( SYSTEM_MSG, CHK_UNUSED_HF_STR, NULL);
      memset (hotfix_tbl, '\0', (size_t)(spb.culMaxHotFixes * sizeof (ULONG)));
      spb.culHotFixes = 0;
      spb.bFlag &= ~SPF_HFUSED;
      if ((l=chk_write_data (spb.lsnHotFix, hotfix_tbl,
                             SPB, FALSE)) != spb.lsnHotFix)
        spb.lsnHotFix = l;

      write_scratch ((ULONG)SEC_SPAREB, &spb, 1);
    }
  } /* if chk_recover_data */

/*
 * The user doesn't want to recover any data.  Just print an informational
 * message if any orphaned sectors exist.
 */

  else {
    CHK_OUT_MSG( SYSTEM_MSG, CHK_SCANNING_FOR_ORPHANS_STR, NULL);

    for (l=0L; l<sb.clsnDirBlkBand / SECTORS_PER_DIRBLK; l++)
      if ((QueryUseMapBits ( chk_db_usemap, l * SECTORS_PER_DIRBLK ) == DISK_UNKNOWN) &&  /* 69658 -rjp */
         !(db_bitmap [l >> 3] & (1 << (l & 7)))) {
#ifdef DEBUG
        ltoa (l*SECTORS_PER_DIRBLK + sb.lsnFirstDirBlk, outbuf, 16);
#else
        *p = '\b';
        *(p+1) = '\0';
#endif

        CHK_OUT_MSG( SYSTEM_MSG, CHK_ORPHANS_EXIST_STR, p);

#ifndef DEBUG
        lost_data = TRUE;
        break;
#endif
      }

#ifndef DEBUG
        if (!lost_data) {       /* if there was already lost data in the dirblk band,  */
                                /* a msg has already been printed. So we don't scan    */
                                /* the main bitmap.                                    */
#endif

        percent = 0;

    for (l=0L, bmp=bitmap, diskmap=0L, reps=loop_reps;         /* 69658 -rjp */
                l<number_of_sectors; l++, diskmap++) {


                if (!reps--) {                                          /* time to update the % display  */
                        if (!stdout_redir) {
                                percent += 5;

                                itoa (percent, outbuf, 10);
                                out_msg( CHK_PERCENT_SCANNED_STR, p);
                                printf ("\r"); fflush (stdout);
                        }
                        reps = loop_reps;
        }

        if (( QueryHugeUseMapBits ( chk_disk_map, diskmap ) == DISK_UNKNOWN) &&  /* didn't find this sector */ /* 69658 -rjp */
                        !(*bmp & (1 << (l & 7)))) {     /* but it's marked as used  */
#ifdef DEBUG
                        ltoa (l, outbuf, 16);
#else
                        *p = '\b';
                        *(p+1) = '\0';
#endif
                        CHK_OUT_MSG( SYSTEM_MSG, CHK_ORPHANS_EXIST_STR, p);

#ifndef DEBUG
                        break;
#endif
        }

        if (((USHORT)l & 7) == 7)       /* end of this bitmap byte?     */
                        bmp++;          /* yes, advance bitmap pointer  */

    } /* for */


                itoa (100, outbuf, 10);                 /* put out 100% of the disk scanned  */
                out_msg( CHK_PERCENT_SCANNED_STR, p);

    if (!stdout_redir) /* if we did a %done display  */
        printf ("\n"); fflush (stdout);         /* get off the %done line     */

#ifndef DEBUG
        } /* if !lost data */
#endif
}

/*
 * The bitmaps didn't match, or we found bad blocks and allocated them,
 * or we found files which used free sectors.
 */

  if (chk_delete_inconsistency)
    chk_write_bitmaps (bitmap);

} /* chk_check_system */


VOID CHK_OUT_MSG (
  USHORT msgtype,
  USHORT msg,
  UCHAR  *subst)
{
  if ( chk_usepipe ) {
     psubstxyz = subst;
     usPipeMgr( (USHORT) msgtype,
                (psubstxyz) ? (&psubstxyz) : NULL,
                (psubstxyz) ? 1 : 0,
                (USHORT) msg,
                (CHAR *) msgfilename,
                (USHORT) hfileChkPipe,
                (USHORT) NO_RESPONSE,
                (CHAR *) NULL,
                (USHORT) 0 );
  }
  else
  {
#ifdef PMFRONTEND
    if (chk_pmfrontend)                           //PCR PM FRONT END
    {                                             //PCR PM FRONT END
      printf ("\nMSG_%u\n", msg);                 //PCR PM FRONT END
    }                                             //PCR PM FRONT END
#endif
    out_msg (msg, subst);
  }
}


VOID CHK_ERROR_MSG (
  USHORT msg)
{
  if ( chk_usepipe ) {

     usPipeMgr( ( USHORT )SUI_MSGTYPE_SYSTEM_MESSAGE,
                NULL,
                0,
                ( USHORT ) msg,
                ( CHAR * ) msgfilename,
                ( USHORT ) hfileChkPipe,
                ( USHORT ) NO_RESPONSE,
                NULL,
                0 );
  } else
  {
#ifdef PMFRONTEND
    if (chk_pmfrontend)                     //PCR PM FRONT END
    {                                       //PCR PM FRONT END
      printf ("\nMSG_%u\n", msg);           //PCR PM FRONT END
    }                                       //PCR PM FRONT END
#endif
    error_msg( msg );
  }
}


VOID CHK_COMPUTMSG (
  USHORT   msgtype,
  CHAR     **ivtable,
  USHORT   ivcount,
  USHORT   message_number,
  CHAR     *message_file_name,
  unsigned device_type ,
  USHORT   responce_type,
  CHAR     *key_buff,
  USHORT   key_buff_len)
{

  BOOL   fSuppress = FALSE;                      //PCR 64235
  USHORT i;                                      //PCR 64235

  if ( chk_usepipe ) {

     usPipeMgr( (USHORT)   msgtype,
                ( CHAR * * )ivtable,
                ( USHORT ) ivcount,
                (USHORT) message_number,
                ( CHAR * ) message_file_name,
                ( USHORT ) hfileChkPipe,
                ( USHORT ) responce_type,
                ( CHAR * ) key_buff,
                ( USHORT ) key_buff_len );

  }
  else
  {
#ifdef PMFRONTEND
    if (chk_pmfrontend)                          //PCR PM FRONT END
    {                                            //PCR PM FRONT END
      unsigned iv_i;                             //PCR PM FRONT END
                                                 //PCR PM FRONT END
      printf ("\nMSG_%u", message_number);       //PCR PM FRONT END
      if (ivcount != 0)                          //PCR PM FRONT END
      {                                          //PCR PM FRONT END
        for (iv_i = 0; iv_i < ivcount; iv_i++)   //PCR PM FRONT END
        {                                        //PCR PM FRONT END
          printf (",%s", * (ivtable + iv_i));    //PCR PM FRONT END
        }                                        //PCR PM FRONT END
      }                                          //PCR PM FRONT END
      printf ("\n");                             //PCR PM FRONT END
      fflush (stdout);                           //PCR PM FRONT END
    }                                            //PCR PM FRONT END
#endif

    for (i = 0; i < ivcount; i++)                //PCR 64235
    {                                            //PCR 64235
      if (strstr (*(ivtable+i), "SWAPPER"))      //PCR 64235
      {                                          //PCR 64235
        fSuppress = TRUE;                        //PCR 64235
      }                                          //PCR 64235
    }                                            //PCR 64235
    if (!fSuppress)                              //PCR 64235
    {                                            //PCR 64235
      computmsg(( char * *) ivtable,
                ( unsigned) ivcount,
                ( unsigned) message_number,
                ( char *  ) message_file_name,
                ( unsigned) device_type,
                ( unsigned) responce_type,
                ( char *  ) key_buff,
                ( unsigned) key_buff_len);
    }                                            //PCR 64235
  }
}
