/*static char *SCCSID = "@(#)delhelp.c  6.1 90/11/18";*/
/*static char *SCCSID = "@(#)delhelp.c 13.8 90/07/23";*/
/***    DELHELP.C - helper routines for file deletion
 *
 *
 *      Modification history:
 *      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>
#include <pr_fsys.h>


/*
 * initcache - allocate and initialize our cache
 */
void initcache (void)
{
  register USHORT i;

  memset ((char *)chk_cache, '\0', CACHESIZE * sizeof ( BUFNODE));

  for (i=0; i<CACHESIZE; i++)
    chk_cache [i].pblkData = (long)_fmalloc (SPB * SECSIZE);
}

/*
 * freecache - deallocate our cache
 */
void freecache (void)
{
  register USHORT i;

  for (i=0; i<CACHESIZE; i++) {
    _ffree ((char *)chk_cache [i].pblkData);
    chk_cache [i].pblkData = NULL;
  }
}

/*
 * readbuf - read a cache block
 */
char *readbuf (sect, hdr)
  ULONG sect;
   BUFNODE **hdr;
{
  register USHORT i;

/*
 * First, look for a buffer which already contains the data we want.
 * That buffer will have the same sector number, truncated to the nearest
 * 4 sector boundary.
 */

  for (i=0; i<CACHESIZE; i++) {
    if ((ULONG)chk_cache [i].lsnSec == (sect & ~((ULONG)SPBMASK))) {
      *hdr = &chk_cache [i];
      return ((char *)((*hdr)->pblkData) + ((sect % SPB) * SECSIZE));
    }
  }

/*
 * Next, look through our cache for a buffer which hasn't been used yet.
 * Such a buffer must have a sector number of 0.
 */

  for (i=0; i<CACHESIZE; i++) {
    if (!chk_cache [i].lsnSec) {
      chk_cache [i].lsnSec = sect & ~((ULONG)SPBMASK);
      if( read_scratch (chk_cache [i].lsnSec,
                            (char *)chk_cache [i].pblkData, SPB) ){

          /*  Can't read these sectors--return error  */
          *hdr = NULL;
          return NULL;
      }
      *hdr = &chk_cache [i];
      return ((char *)((*hdr)->pblkData) + ((sect % SPB) * SECSIZE));
    }
  }

/*
 * Okay, all the buffers have been used.  Look for one that's not held
 * or dirty.
 */

  for (i=0; i<CACHESIZE; i++) {
    if (!chk_cache [i].cusHolds && !chk_cache [i].bDirtMask) {
      chk_cache [i].lsnSec = sect & ~((ULONG)SPBMASK);
      if( read_scratch (chk_cache [i].lsnSec,
                            (char *)chk_cache [i].pblkData, SPB) ) {

          /*  Can't read these sectors--return error  */
          *hdr = NULL;
          return NULL;
      }
      *hdr = &chk_cache [i];
      return ((char *)((*hdr)->pblkData) + ((sect % SPB) * SECSIZE));
    }
  }

/*
 * Rats.  We can't get one that's not dirty.  We'll have to find one
 * that isn't locked or held, and write it to disk to clean it.
 */

  for (i=0; i<CACHESIZE; i++) {
    if (!chk_cache [i].cusHolds) {
      chk_cache [i].bDirtMask = 0;
      write_scratch (chk_cache [i].lsnSec, (char *)chk_cache [i].pblkData, SPB);
      chk_cache [i].lsnSec = sect & ~((ULONG)SPBMASK);
      if( read_scratch (chk_cache [i].lsnSec,
                            (char *)chk_cache [i].pblkData, SPB) ) {

          /*  Can't read these sectors--return error  */
          *hdr = NULL;
          return NULL;
      }
      *hdr = &chk_cache [i];
      return ((char *)((*hdr)->pblkData) + ((sect-chk_cache [i].lsnSec) * SECSIZE));
    }
  }

/*
 * No buffers available that aren't locked or held.  We have no cache
 * space left.
 */

#ifdef TRACE
  fprintf (stderr, "readbuf: when you're out of cache, you're out of luck.\n");
  fflush (stderr);
  getch ();
#endif

  return (NULL);
}

/*
 * rdbufdb - special read buffer routine, does some extra checking
 * for dirblks.  Doesn't apply for CHKDSK.
 */



/*
 * flushcache - write any dirty cache blocks
 */
void flushcache (void)
{
  register USHORT i;

  for (i=0; i<CACHESIZE; i++) {
    if (chk_cache [i].bDirtMask) {
      chk_cache [i].bDirtMask = 0;
      write_scratch (chk_cache [i].lsnSec, (char *)chk_cache [i].pblkData, SPB);
    }
  }
}

/*
 * wipecache - clear out all unheld cache blocks
 *           - This is necessary because other parts of CHKDSK could
 *             change the disk without going through the cache, thus
 *             making the cache and the disk inconsistent.
 *           - All blocks are unheld and flushed.  Don't call this
 *             if you want your held blocks to stick around!
 */
void wipecache (void)
{
  register USHORT i;

  for (i=0; i<CACHESIZE; i++)
    chk_cache [i].cusHolds = 0;

  flushcache ();

  for (i=0; i<CACHESIZE; i++)
    if (!chk_cache [i].cusHolds)
      chk_cache [i].lsnSec = 0L;
}

/*
 * _adr_to_bhdr -- convert data address to buffer header address
 */
 BUFNODE *_adr_to_bhdr (adr)
   DIRBLK *adr;
{
  USHORT i;

  for (i=0; i<CACHESIZE; i++) {
    if (((long)adr >= chk_cache [i].pblkData) &&
        ((long)adr < chk_cache [i].pblkData + SPB * SECSIZE))
      return (&chk_cache [i]);
  }
  return (NULL);
}

#ifdef DEBUG
/*
 * _chkheld -- make sure buffer is held
 */
void _chkheld (buf)
   BUFNODE *buf;
{
  if (!buf->cusHolds) {
    fprintf (stderr, "_chkheld (): buffer should be held but isn't!");
    fflush (stderr);
    DOSBEEP
  }
}
#endif

/*
 * _closeup -- close up directory entry
 */
void _closeup (blk, entry)
   DIRBLK *blk;
   DIRENT *entry;
{
  USHORT len;

  blk->offulFirstFree -= entry->cchThisEntry;
  len = sizeof (struct DIRBLK) -
            (((UCHAR *)entry - (UCHAR *)blk) + entry->cchThisEntry);
  memmove ((UCHAR *)entry, ((UCHAR *)entry) + entry->cchThisEntry, len);
  blk->culChange += 2;
}

/*
 * findpnode -- find parent directory node
 */
void findpnode (blk, sect, prev, pt)
   DIRBLK *blk;
  long sect;
   DIRENT **prev;
   DIRENT **pt;
{
  union dp dp, dp2;

  dp.p = DIR_START (*blk);
  dp2.p = NULL;

  while (TRUE) {
    if (DOWN_PTR (dp) == sect)
      break;

    if (dp.d->fFlags & DF_END) {
#ifdef TRACE
      fprintf (stderr, "findpnode: downpointer not found!\n");
      fflush (stderr);
#endif
      break;
    }
    dp2.p = dp.p;
    GETNEXT_ENTRY (dp);
  }
  *prev = dp2.d;
  *pt = dp.d;
}

/*
 * _FreeDB - deallocate a DIRBLK from the bitmap
 */
void _FreeDB (lsn)
  ULONG lsn;
{
    USHORT i;

    if ((lsn >= sb.lsnFirstDirBlk) && (lsn <= sb.lsnLastDirBlk)) {  /* DB in DB band */
      set_db_bit ((lsn - sb.lsnFirstDirBlk) / SECTORS_PER_DIRBLK);
      for (i=0; i<SECTORS_PER_DIRBLK; i++)
        SetUseMapBits ( chk_db_usemap, lsn-sb.lsnFirstDirBlk+i, DISK_UNKNOWN );  /* 69658 -rjp */
    }
    else {                                   /* DB not in DB band */
      for (i=0; i<SECTORS_PER_DIRBLK; i++) {
        SetHugeUseMapBits ( chk_disk_map, lsn+i, DISK_UNKNOWN );  /* mark as no longer a DIRBLK */ /* 69658 -rjp */
        set_bit (lsn+i);                      /* mark as free in bitmap */
      }
    }
}


/*
 * _namecmp - compare file names
 */
long _namecmp (first, second)
   DIRENT *first;
   DIRENT *second;
{
  return ((long)chk_namcmp (second->bName, second->bCodePage, first, (USHORT)second->cchName));
}

/*
 * NewDirRoot - build a new top-level DIRBLK
 *
 *  ENTRY   promote = ptr to DIRENT to put into new root
 *          upper   = ptr to DIRBLK of elder child
 *          lower   = ptr to DIRBLK of younger child
 *          parbuf  = ptr to return area for PBUFNODE of new root
 *          parblk  = ptr to return area for PNDIRBLK of new root
 */
void NewDirRoot (promote, upper, lower, parbuf, parblk)
   DIRENT *promote;
   DIRBLK *upper;
   DIRBLK *lower;
   BUFNODE **parbuf;
   DIRBLK **parblk;
{
  ULONG sect, fnsec;
   DIRBLK *new_db;
   BUFNODE *hdr;
  union dp dp;
   FNODE *f;

  if (!(sect=chk_write_data (upper->lsnParent, lower, SECTORS_PER_DIRBLK, TRUE))) {
#ifdef TRACE
    fprintf (stderr, "NewDirRoot: cannot allocate DIRBLK!\n");
    fflush (stderr);
#endif
    return;
  }
  use_sectors (sect, (ULONG)SECTORS_PER_DIRBLK, DISK_DIRBLK);

  new_db = ( DIRBLK *)readbuf (sect, &hdr);
  memset (new_db, '\0', sizeof ( DIRBLK));

  hdr->cusHolds++;
  *parbuf = hdr;
  *parblk = new_db;

  new_db->sig = DBSIGVAL;
  new_db->culChange = 1;
  new_db->lsnParent = upper->lsnParent;
  new_db->lsnThisDir = sect;
  memmove (&new_db->bFirst, (UCHAR *)promote, promote->cchThisEntry);

  dp.p = DIR_START (*new_db);
  GETNEXT_ENTRY (dp);

  dp.d->cchThisEntry = (sizeof ( DIRENT) + 4 + 3) & ~3;
  dp.d->fFlags = DF_END | DF_BTP;
  dp.d->fAttr  = 0;
  dp.d->cchName = 1;
  dp.d->bName[0] = 0xff;
  GETNEXT_ENTRY (dp);
  new_db->offulFirstFree = dp.p - (UCHAR *)new_db;

  upper->lsnParent = sect;
  hdr = _adr_to_bhdr (upper);
  hdr->cusHolds--;
  hdr->bDirtMask = TRUE;

  fnsec = lower->lsnParent;
  lower->lsnParent = sect;
  DOWN_PTR (dp) = lower->lsnThisDir;
  hdr = _adr_to_bhdr (lower);
  hdr->cusHolds--;
  hdr->bDirtMask = TRUE;

  f = ( FNODE *)readbuf (fnsec, &hdr);

  f->fst.a.aall [0].lsnPhys = sect;
  hdr->bDirtMask = TRUE;
}


/*
 * _setbit - set bit in bitmap
 */
void _setbit (start, nbits)
  long start;
  long nbits;
{
  long i;

  for (i=start; i<start+nbits; i++)
    set_bit (i);
}


#ifdef DEBUG
/*
 * trap - debugging trap
 */
void trap (void)
{
  fprintf (stderr, "trap()\n");
  fflush (stderr);
  DOSBEEP;
}
#endif
