/*static char *SCCSID = "@(#)filesys.c  6.3 91/10/29";*/
/***    FILESYS.C - Routines dealing with the pinball file system
 *
 *      Format
 *      Michael Walma
 *      Copyright March 1988, Microsoft Corp.
 *      March 28 1988
 *
 *      Modification History:
 *      P.A. Williams   06/01/89        Initialized new fields lsnCPInfo and
 * culCP in spare block, fFlex
 *                                      and bCodePage in dirents, aiACLBASE
 *                                      and ulRefCount in fnodes.
 *      P.A. Williams   06/09/89        Hotfix list now consists of single
 *                                      sectors and not 2K blocks. B784174
 *      P.A. Williams   A memset is used to clear the DIRBLK bitmap. A full
 *                                      4 sectors are written out now instead of the previous 2.
 *
 *      56340   11/05/92 BJH    Math error computing the number of dirblks for the
 *                              volume and in computing the amount of free space.
 *
 * @d01 73790    93SEP03 GY <Support V4FORMAT extensions.  GHASSAN@AUSVM1>
 */

#define INCL_DOSDEVICES
#define INCL_DOSMISC
#include "ldefs.h"
#include "ltypes.h"
#include "globals.h"
#include "pr_fmt.h"


#ifdef DUMPS
void fmt_dump (FILE *, UCHAR *, USHORT);
void fmt_dump_maps ();
#endif

/***    fmt_init_file_system
 *
 *      Create a simple Pinball file system consisting of a boot
 *      sector, a super block, an fnode associated with the root
 *      directory, and a root directory block containing the ".."
 *      entry.  The structures are filled in, but nothing
 *      is written to the disk.
 *
 *      All structures are defined globally at the beginning of this
 *      file.  The disk has been formatted, and the free maps defined.
 *      This routine uses the free maps to locate the structures on
 *      the disk.  The various values of the structure are either magic
 *      values, in which case they are defined in DEFS.H, or they are
 *      calculated here.  These values are either pointers to the other
 *      structures, or Boot_OEM and date information.
 *
 *      fmt_init_file_system (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            No return value
 *
 *      CALLS           allocate_early_sectors
 *                      allocate_middle_sectors
 *                      fmt_get_boot_id
 *                      fmt_query_volid
 *                      fmt_write_volid
 *                      DosGetVersion
 *
 *      WARNINGS        May terminate program prematurely if DosGetVersion
 *                      fails or if allocate_bitmap_sectors fails.
 *
 *      EFFECTS         Fills in global filesystem memory structures --
 *                        super block, root FNODE, root DIRBLK.
 *                      Allocates disk space from bitmaps
 */
void fmt_init_file_system (void)
{
 PDIRENT p;
  USHORT i, version, hi, lo, mb;
  ULONG *l, sec;
#ifdef DUMPS
  FILE *fp;
#endif

#ifdef  TRACE
  fprintf (stderr, "Initializing file system.\n");
  fflush (stderr);
#endif

/*
 * Copy the BPB structure as recommended by the driver, and as
 * modified to pinball specs, into the boot record, where it
 * will be written to the disk
 */

  pinboot.Extended_Boot.Boot_BPB = rec_param.dev_bpb;

/*
 * Fill in the rest of the boot record
 */

  pinboot.Extended_Boot.Boot_DriveNumber = (BYTE)((rec_param.device_type == HARD_DISK) ? FIXED : REMOVABLE);
  pinboot.Extended_Boot.Boot_Sig = NEW_BOOT;
  pinboot.Extended_Boot.Boot_Serial = fmt_get_boot_id ();
  strcpy (pinboot.Extended_Boot.Boot_OEM, VERSION_PREFIX);

/*
 * Get the version number, and break it up into version.release form.
 * The high byte contains the version number, the low, the release.
 */

  if (DosGetVersion ((USHORT *)&version))
    fmt_exit_error (INFO_ERROR);

  hi = (version & 0xff00) >> 8;
  lo = version & 0xff;
  pinboot.Extended_Boot.Boot_OEM [4] = (UCHAR)('0' + hi / 10);
  pinboot.Extended_Boot.Boot_OEM [5] = (UCHAR)('0' + (hi % 10));
  pinboot.Extended_Boot.Boot_OEM [6] = (UCHAR)('.');
  pinboot.Extended_Boot.Boot_OEM [7] = (UCHAR)('0' + (lo % 10));

#ifdef  TRACE
  fprintf (stderr, "version = %u\n", version);
  fprintf (stderr, "version string = %c%c%c%c%c%c%c%c\n",
           pinboot.Extended_Boot.Boot_OEM [0], pinboot.Extended_Boot.Boot_OEM [1],
           pinboot.Extended_Boot.Boot_OEM [2], pinboot.Extended_Boot.Boot_OEM [3],
           pinboot.Extended_Boot.Boot_OEM [4], pinboot.Extended_Boot.Boot_OEM [5],
           pinboot.Extended_Boot.Boot_OEM [6], pinboot.Extended_Boot.Boot_OEM [7]);
  fflush (stderr);
#endif

/*
 *      OS/2 v1.2 has a call that asserts the volume label, so for
 *      v1.2, the volume label is learned and asserted after the filesystem
 *      has been written to the disk.
*/

#ifndef V12
  if (!fmt_label_flag)
    fmt_query_volid ();

  fmt_write_volid (pinboot.Extended_Boot.Boot_Vol_label);
#endif

#ifdef  DUMPS
  fp = fopen ("boot.dmp", "wb");

  fmt_dump (fp, (char *)&pinboot, sizeof (struct boot_sector));

  fclose (fp);
#endif

#ifdef TRACE
  fprintf (stderr, "allocating free maps\n");
  fflush (stderr);
#endif

/*
 * First, allocate the bitmap indirect maps, near the middle of the disk
 * for easy access.
 */

#ifdef  TRACE
  fprintf (stderr, "allocating free indirect\n");
  fflush (stderr);
#endif

  sb.rspBitMapIndBlk.lsnMain = allocate_middle_sectors (fmt_cblocks_bii * SPB, SPB); /* BIGDISK */
  sb.rspBitMapIndBlk.lsnSpare = 0L;

/*
 * Locate the free maps on the disk.  Each bitmap block goes at the end
 * of the 8MB disk band that it describes.  If we could not allocate a
 * bitmap, terminate the program.
 */

  for (i=0; i<fmt_num_free_maps; i++) {
    fmt_free_ind [0][i] = allocate_bitmap_sectors (i);

    if( fmt_free_ind[0][i] == 0xffffffffL )
        fmt_exit_error( FORMAT_ERROR );

#ifdef  TRACE
    fprintf (stderr, "fmt_free_ind [0][%d] = %ld\n",
             i, fmt_free_ind [0][i]);
    fflush (stderr);
#endif
  }

#ifdef  TRACE
  fprintf (stderr, "allocating bad list\n");
  fflush (stderr);
#endif

/*
 * Allocate sectors for the bad block lists.
 */

#ifdef  TRACE
  fprintf (stderr, "allocating bad block list head\n");
  fflush (stderr);
#endif

  sb.rspBadBlkList.lsnMain = allocate_middle_sectors (SPB, 1);
  sb.rspBadBlkList.lsnSpare = 0L;

  for (i=0; i<fmt_num_bad_blocks-1; i++) {
    fmt_bad [i].fwd = allocate_middle_sectors (SPB, 1);
#ifdef  TRACE
    fprintf (stderr, "fmt_bad [%d].fwd = %ld\n", i, fmt_bad [i].fwd);
    fflush (stderr);
#endif
  }
  fmt_bad [i].fwd = 0L;         /* terminate the list */


/*
 * Fill in the super block structure.
 */

  sb.sig1 = SBSIG1;
  sb.sig2 = SBSIG2;
  sb.usDummy = 0;
  sb.datLastChkdsk = 0L;
  sb.datLastOptimize = 0L;
  sb.culSectsOnVol = number_of_sectors & ~3L;   /* make culSectsOnVol a multiple of 4 */
  sb.culNumBadSects = num_bad_sectors;
  sb.bVersion = SBBASEV;                                        /* BIGDISK */

#ifdef V4FORMAT

  if( fmt_cf ) {        /* Was the Contiguity Factor specified? */
    /* Contiguity Factor Specified:
     *  Initialize Functional Version Number in the SuperBlock, and
     *  Contiguity Factor field in the SpareBlock.
     */
    sb.bFuncVersion = SBBASEFV;         /* Functional Version Number */
    spb.bContigFactor = fmt_cf;         /* Init Contiguity Factor    */
    spb.bFlag2 |= SPF2_MM;              /* MultiMedia active on drive*/

  } else {

    if (sb.culSectsOnVol < CSEC_BIG_DISK) {
      /* normal sized disk, use old version number */
      sb.bFuncVersion = SBBASEOLDFV;
    } else {
      /* disk is too big for old hpfs, set new version number */
      sb.bFuncVersion = SBBASEOLDFV_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


  for (sec=sb.culSectsOnVol; sec < number_of_sectors; sec++)
    clr_bit (sec);      /* make sure the FS doesn't use the partial cache block */

/*
 * and some stuff for the spare block
 */

  spb.sig1 = SPSIG1;
  spb.sig2 = SPSIG2;

//#86635
  if (!fmt_format_track_flag)
    spb.bFlag = SPF_FASTFMT;        /* set Fast-Format flag to prevent */
   else                             /* CHKDSK /F:3                     */
    spb.bFlag = 0L;

/*
 * Set up the filesystem's hotfixing stuff.  We point SB_HFSEC to
 * a block containing an array of bad block numbers, an array of
 * blocks to replace them with, and an array of pointers to FNODEs
 * which reference the bad blocks.  Since there aren't any hotfixes
 * present on a newly formatted disk, we set the number of existing
 * hotfixes, SB_HFUSE, to zero.  But we fill in the middle array of
 * the hotfix list with a list of blocks the filesystem can use.
 * We also set SB_HFMAX to the number of hotfix blocks allocated --
 * either 100 blocks or 1% of the disk, whichever is less.
 *
 * Note that CHKDSK depends on the latter figure for culMaxHotFixes as
 * the default value.  If he finds a different culMaxHotFixes, he checks
 * the table to make sure it's right.  If the value for culMaxHotFixes
 * is changed here, it should be changed in CHKDSK too, so that he
 * won't take a long time to validate it.
 */

  spb.lsnHotFix = allocate_early_sectors (SPB, SPB);
  spb.culHotFixes = 0L;
#ifndef PAWc
  spb.culMaxHotFixes = (number_of_sectors > 40000L) ? 100L : number_of_sectors / 400L;
#else
  spb.culMaxHotFixes = (number_of_sectors > 40000L) ? 100L : number_of_sectors / 400L;
#endif

  memset ((UCHAR *)hotfix_tbl, '\0', SPB * SECSIZE);
  l = &hotfix_tbl [spb.culMaxHotFixes];
  for (i=0; i < (USHORT)spb.culMaxHotFixes; i++) {
#ifndef PAWc
    *l = allocate_early_sectors (SPB, SPB);
#else
    *l = allocate_early_sectors (1, 1);
#endif
    l++;
  }

/*
 * Figure out how big to make the DIRBLK band.  We allocate a minimum of
 * 50 dirblks, plus  5 more for each MB of disk beyond 10 MB, up to a
 * maximum of 1000.  So, for example, a 30 MB disk would have 150 dirblks
 * in the band (minimum 50, plus 5 for each of the 20 MB beyond 10 = 100).
 */

  mb = (USHORT)(number_of_sectors / ( 1048576L / SECSIZE ));                    //56340
  if (mb > 10)
    sb.clsnDirBlkBand = 50 + ((mb - 10) * 5);
  else
    sb.clsnDirBlkBand = 50;
  if (sb.clsnDirBlkBand > MAX_DBS_PER_DIRBAND) sb.clsnDirBlkBand = MAX_DBS_PER_DIRBAND;
  sb.clsnDirBlkBand *= SECTORS_PER_DIRBLK;

/*
 * Locate the dirblk band on disk.  Mark its sectors as "in use" in the
 * regular bitmap (so the filesystem won't put anything else there).
 * Set the superblock variables to point to the beginning and end of the
 * dirblk band.  The dirblk band should be allocated at the very front
 * of the middlemost band.
 *
 * The "2L" in the following calculation is intended to calculate the
 * band number of the middlemost band.  It is not the number of sectors
 * in a DIRBLK.
 */

  sec = (number_of_sectors / (8L * 2048L)) / 2L;

  sb.lsnFirstDirBlk = allocate_near (sec * 8L * 2048L, (USHORT)sb.clsnDirBlkBand, 4);
  sb.lsnLastDirBlk = sb.lsnFirstDirBlk + (sb.clsnDirBlkBand) - 1L;

/*
 * Locate the dirblk bitmap and root dirblk on disk.  They go at the end
 * of the previous band.  We try to allocate starting just before the bitmap
 * for that band to get the root DIRBLK;  then we try two sectors before that
 * for the dirblk map.
 *
 * If there's only one band on the disk, there is no "previous band" to
 * allocate from, so we put the root dirblk and the dirblk bitmap near the
 * middle of the disk.
 */

  if (!sec) {           /* only one band */
    fmt_root_dir.lsnThisDir = allocate_middle_sectors (SECTORS_PER_DIRBLK,
                                                   SECTORS_PER_DIRBLK);
    sb.lsnDirBlkMap = allocate_middle_sectors (SPB, SPB);
  }
  else {
    fmt_root_dir.lsnThisDir = allocate_near (sec * 8L * SPB * SECSIZE - 8L,
                                         SECTORS_PER_DIRBLK, SECTORS_PER_DIRBLK);
    sb.lsnDirBlkMap = allocate_near (fmt_root_dir.lsnThisDir-SPB, SPB, SPB);
  }

/*
 * Locate the spare dirblks on disk.  They are allocated after the ordinary
 * dirblk pool, and their sector numbers are stored in the superblock.  We
 * also set the number currently in use to zero.
 */

  for (i=0; i<SPAREDB; i++)
    spb.alsnSpareDirBlks [i] = allocate_near (sb.lsnLastDirBlk,
                                     SECTORS_PER_DIRBLK, SECTORS_PER_DIRBLK);
  spb.cdbSpares = SPAREDB;
  spb.cdbMaxSpare = SPAREDB;

/*
 * Initialize the code page information. Set the sector of the code page
 * on disk and its length in sectors.
 */

  spb.lsnCPInfo = 0L;
  spb.culCP = 0L;

/*
 * Initialize the dirblk bitmap.  We set as many bits as there are dirblks
 * in the db band (they're all free initially), and clear the rest of the
 * map.  Note that the dirblk pool is still marked "in use" in the general
 * bitmap, so that the file system won't allocate other structures there.
 * The filesystem relies on the fact that the unused bits in the last
 * byte be set to 0. This is done when the rest of the bitmap is zero-filled.
 */

  if ((db_bitmap=malloc (BYTES_PER_BITMAP)) == NULL)
    fmt_exit_error (INSUFFICENT_MEM);

  /*
   *  Zero the bitmap, then set the bits for the DIRBLK band.
   */
  memset (db_bitmap, '\0', BYTES_PER_BITMAP);

  for (i=0; i < (USHORT)(sb.clsnDirBlkBand/SECTORS_PER_DIRBLK); i++)
    set_db_bit ((ULONG)i);


/*
 * Allocate the userid mapping table, and erase the rest of the superblock.
 */
  sb.lsnSidTab = allocate_middle_sectors (8, SPB);   /* 8 quad-aligned sects */

  memset (sb.abFill, '\0', sizeof (sb.abFill));

/*
 * Fill in the root FNODE structure, and allocate space for the FNODE on disk.
 */

  memset ((char *)&fmt_root_fnode, '\0', sizeof (struct FNODE));
  fmt_root_fnode.sig  = FNSIGVAL;
  fmt_root_fnode.bFlag = FNF_DIR;
  fmt_root_fnode.usACLBase = FIELDOFFSET(struct FNODE, abFree[0]);
  fmt_root_fnode.ulRefCount = 0L;



#ifdef  TRACE
  fprintf (stderr, "allocating dir block\n");
  fflush (stderr);
#endif

  fmt_root_fnode.fst.alb.bFlag = 0;
  for (i=0; i<3; i++)
    fmt_root_fnode.fst.alb.bPad [i] = 0;
  fmt_root_fnode.fst.alb.cFree = LEAFPERFNODE - 1;
  fmt_root_fnode.fst.alb.cUsed = 1;
  fmt_root_fnode.fst.alb.oFree = 0x14;

  fmt_root_fnode.fst.a.aall [0].lsnLog = 0L;
  fmt_root_fnode.fst.a.aall [0].csecRun = 0L;
  fmt_root_fnode.fst.a.aall [0].lsnPhys = fmt_root_dir.lsnThisDir;

  fmt_root_fnode.fst.a.aall [1].lsnLog = -1L;
  fmt_root_fnode.fst.a.aall [1].csecRun = 0L;
  fmt_root_fnode.fst.a.aall [1].lsnPhys = 0L;

#ifdef  TRACE
  fprintf (stderr, "allocating root fnode\n");
  fflush (stderr);
#endif

  sb.lsnRootFNode = allocate_near (fmt_root_dir.lsnThisDir, 1, 1);
  fmt_root_fnode.lsnContDir = sb.lsnRootFNode;

#ifdef  DUMPS
  fp = fopen ("super.dmp", "wb");
  fmt_dump (fp, (char *)&sb, sizeof (struct SUPERB));
  fclose (fp);

  fp = fopen ("fnode.dmp", "wb");

  fmt_dump (fp, (char *)&fmt_root_fnode, sizeof (struct FNODE));

  fclose (fp);
#endif

/*
 * Fill in the root DIRBLK's header.
 */

  fmt_root_dir.sig = DBSIGVAL;
  fmt_root_dir.culChange = 1;
  fmt_root_dir.lsnParent = sb.lsnRootFNode;

#ifdef  TRACE
  fprintf (stderr, "db_ccnt = %ld\n", fmt_root_dir.culChange);
  fprintf (stderr, "db_par = %ld\n", fmt_root_dir.lsnParent);
  fprintf (stderr, "db_sec = %ld\n", fmt_root_dir.lsnThisDir);
  fprintf (stderr, "db_sig = %lx\n", fmt_root_dir.sig);
  fflush (stderr);
#endif

/*
 * Build the ".." entry.
 */

  p = (PDIRENT)&fmt_root_dir.bFirst;

#ifdef  TRACE
  fprintf (stderr, "first offset = %d\n", (UCHAR *)p - (UCHAR *)&fmt_root_dir);
  fflush (stderr);
#endif

  p->cchThisEntry       = (sizeof (struct DIRENT)+2+3) & 0xfc;
  p->fFlags     = DF_SPEC;      /* special entry */
  p->fAttr      = dir_attr;     /* give it a directory attribute */
  p->lsnFNode   = sb.lsnRootFNode;
  p->timLastMod = p->timLastAccess = p->timCreate = get_time ();
  p->cchFSize   = 5;
  p->ulEALen    = 0L;
  p->fFlex      = 0;
  p->bCodePage  = 0;
  p->cchName    = 2;
  strcpy (p->bName, "\001\001");

#ifdef  TRACE
  fprintf (stderr, "dir fFlags = %u\n", (USHORT)p->fFlags);
  fprintf (stderr, "dir fAttr = %u\n", (USHORT)p->fAttr);
  fprintf (stderr, "dir fn = %ld\n", p->lsnFNode);
  fprintf (stderr, "dir size = %ld\n", p->cchFSize);
  fprintf (stderr, "dir elen = %d\n", p->cchThisEntry);
  fprintf (stderr, "dir naml = %d\n", (int)p->cchName);
  fflush (stderr);
#endif

/*
 * Add the end-of-directory entry
 */

  p = (PDIRENT)((PUCHAR)p + p->cchThisEntry);

  p->fFlags     = (BYTE)DF_END;
  p->fAttr      = (BYTE)0;
  p->lsnFNode   = 0;
  p->cchFSize   = 0;
  p->cchThisEntry       = sizeof (struct DIRENT);
  p->cchName    = 1;
  *(p->bName)   = 0xff;

/*
 * Set the end-of-directory pointer in the DIRBLK header to point right
 * after the end record we just built.
 */

  p = (PDIRENT)((PUCHAR)p + p->cchThisEntry);
  fmt_root_dir.offulFirstFree = (UCHAR *)p - (UCHAR *)&fmt_root_dir;

/*
 *  Set the checksums in the spares block
 */
  spb.SPB_SUPERBSUM = ccs ((PUCHAR)&sb, sizeof (struct SUPERB));
  spb.SPB_SPAREBSUM = 0L;
  spb.SPB_SPAREBSUM = ccs ((PUCHAR)&spb, sizeof (struct SpareB));


#ifdef  DUMPS
  fp = fopen ("dir.dmp", "wb");

  fmt_dump (fp, (char *)&fmt_root_dir, sizeof (ROOT_DIR));

  fclose (fp);

  fmt_dump_maps ();
#endif
}

/***    fmt_init_bit_map - allocate and initialize free space maps
 *
 *      Determine how many bitmaps are required, based on the
 *      size of the disk, allocate the bitmap indirect blocks,
 *      and allocate the bit maps.
 *
 *      fmt_init_bit_map (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            No return value
 *
 *      CALLS           clr_bit
 *                      fmt_get_bit_map
 *                      fmt_get_indirect_block
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Initialises the disk maps
 *                      Reserves the first eight sectors for the boot block
 *                        and super block
 *                      Allocates memory
 */
void fmt_init_bit_map (void)
{
  register ULONG i;
  register UCHAR huge *j;
  ULONG num_free_bytes;
  BYTE num_free_bits;

#ifdef  TRACE
  fprintf (stderr, "Initializing bit map.\n");
  fflush (stderr);
#endif

/*
 * The number of free maps required is the number of sectors on the disk
 * divided by the number of bits in a bit map, rounded up.
 */

  fmt_num_free_maps = (USHORT)((number_of_sectors - 1L) /
                               (8L * SPB * SECSIZE)) + 1;

  fmt_cblocks_bii = (((fmt_num_free_maps * sizeof (ULONG)) -1) / /* BIGDISK */
                              (SECSIZE * SPB)) + 1;              /* BIGDISK */

#ifdef  TRACE
  fprintf (stderr, "initalise - fmt_num_free_maps = %d\n", fmt_num_free_maps);
  fprintf (stderr, "total sectors = %ld\n", number_of_sectors);
  fflush (stderr);
#endif

/*
 * Allocate the two free indirect blocks
 */

  fmt_free_ind [0] = fmt_get_indirect_block (fmt_cblocks_bii);   /* BIGDISK */

/*
 * While the free map will be written in pieces, it is easiest
 * to manipulate if it kept in memory as a contiguous chunk.
 */

  bitmap = fmt_get_bit_map (fmt_num_free_maps);
  num_free_bytes = number_of_sectors / 8L;
  num_free_bits = (BYTE)(number_of_sectors % 8L);
  j = bitmap;

/*
 * Mark the entire partition as free.  Space left over at the end of
 * the last bitmap block is marked as used.
 */

  for (i=0; i<num_free_bytes; i++, j++)
    *j = 0xff;

  if (num_free_bits) {
    *(j++) = (BYTE)(0xff >> (8 - num_free_bits));
    i++;
  }

  for (; i<(ULONG)fmt_num_free_maps * SPB * SECSIZE; i++, j++)
    *j = 0x00;

/*
 *      reserve the sectors for the boot record, super block, and spare block
 */

  for (i=0; i<SEC_SUPERB+SPB; i++)
    clr_bit (i);
}

/***    fmt_init_bad_block - allocate and initialize the bad block list
 *
 *      This routine allocates the first bad sector block (2K) and
 *      initializes it to zeroes.  It is filled in later by the
 *      formatting routines.
 *
 *      If the user does not want to format the disk, but simply wants
 *      a fresh filesystem installed, the old bad block list is read
 *      and saved for use in the new filesystem.  The bitmaps are also
 *      appropriately modified, to mark the bad sectors "in use".
 *
 *      fmt_init_bad_block (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            No return value
 *
 *      CALLS           fmt_get_bad_list
 *
 *      WARNINGS        fmt_init_bit_map MUST be called before this function
 *
 *      EFFECTS         initializes bad block list
 *                      allocates memory
 */
void fmt_init_bad_block (void)
{
  register USHORT i;

#ifdef  TRACE
  fprintf (stderr, "Initializing bad block list.\n");
  fflush (stderr);
#endif

/*
 * If the user doesn't want to format, get the old bad block list.
 */

  if (!fmt_format_flag) {
    ULONG sect;
    USHORT j, k;

    read_scratch ((ULONG)SEC_SUPERB, (PUCHAR)&sb, 1);
    num_bad_sectors = sb.culNumBadSects;
    sect = sb.rspBadBlkList.lsnMain;

    if (num_bad_sectors)
      fmt_num_bad_blocks = (USHORT)((num_bad_sectors - 1) / SECSIZE + 1);
    else
      fmt_num_bad_blocks = 1;
    fmt_bad  = fmt_get_bad_list (NULL, fmt_num_bad_blocks);

    for (i=0; i<fmt_num_bad_blocks; i++) {
      read_scratch (sect, (PUCHAR)&fmt_bad [i], SPB);
      sect = fmt_bad [i].fwd;
      fmt_bad [i].fwd = 0L;
      for (j=0; (j<SECSIZE-1) && fmt_bad [i].list [j]; j++)
        for (k=0; k<SPB; k++)
          clr_bit ((fmt_bad [i].list [j] & ~3L) + (ULONG)k);
    }

    return;
  }

/*
 * Allocate the bad block listhead in memory.
 */

  fmt_num_bad_blocks = 1;                   /* just 1 bad block right now */
  fmt_bad  = fmt_get_bad_list (NULL, 1);    /* allocate a single block    */

  for (i=0; i<SECSIZE-1; i++)           /* clear the block out */
    fmt_bad->list [i] = 0L;
  fmt_bad->fwd = 0L;                    /* clear its forward pointer */
}

/***    fmt_write_file_system - save the file system structures to disk
 *
 *      This routine writes the file system structures to the disk.
 *      First, interruptions are disallowed, then the various structures
 *      are written.  The volume label is learned and asserted.  The disk
 *      is reset, and the memory allocated to file system structures
 *      is released.
 *
 *      This function also reads the master boot record for the partition
 *      being formatted and sets its partition type to IFS_PART, indicating
 *      an IFS partition.
 *
 *      fmt_write_file_system (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            Returns TRUE if successful, FALSE if an error occurs
 *
 *      CALLS           allow_signals
 *                      fmt_disk_write
 *                      hold_signals
 *                      fmt_query_volid
 *                      redetermine_media
 *                      set_recomm_bpb
 *                      fmt_write_volid
 *
 *      WARNINGS        None
 *
 *      EFFECTS         Holds signals for the duration of the routine
 *                      Deallocates memory
 */
USHORT fmt_write_file_system (void)
{
  USHORT i, j;
  UCHAR *p [2];         /* for message printing */
  ULONG l=0L;
  ULONG v_free_space;
  struct _FSALLOCATE fsinfo_buf;

#ifdef TRACE
  fprintf (stderr, "writing file system\n");
  fflush (stderr);
#endif

  hold_signals ();

/*
 * Ensure that the first track of the partition is usable.  Since that's
 * the only place there are structures that can't be moved, the partition
 * is unusable if the first track is bad.
 */

  if (num_bad_sectors && (fmt_bad->list [0] < (long)prm.spt)) {
    FMT_COMPUTMSG( SYSTEM_MSG, NULL, 0, FMT_BAD_SYSTEM_BLOCK_STR, msgfilename, STDERR_CODE,
               NO_RESPONSE, NULL, 0);
    return (FALSE);
  }

/*
 * Get the master boot record, look for a DOS partition in it, and set
 * its partition type to 6.
 */

  fmt_read_mbr ();              /* get the master boot record in "ourmbr" */
  for (i=0; i<4; i++) {
    j = ourmbr.ptbl [i].systind;
    if ((j == F12_PART) || (j == F16_PART) || (j == BIG_PART) || (j == IFS_PART))
       {
       if (ourmbr.ptbl[i].lsn == rec_param.dev_bpb.HiddenSectors)
          break;
       }
  }

  if ((i < 4) && (j != IFS_PART)) {     /* found partition, needs changing */
    ourmbr.ptbl [i].systind = IFS_PART; /* set partition type to IFS */
    fmt_write_mbr ();                   /* save the master boot record */
  }

#ifdef TRACE
  fprintf (stderr, "writing boot record\n");
  fflush (stderr);
#endif

  if (!fmt_disk_write (0L, (PUCHAR)&pinboot, sizeof (struct boot_sector)))
    return (FALSE);

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

  if (!fmt_disk_write ((ULONG)SEC_SUPERB, (PUCHAR)&sb, sizeof (struct SUPERB)))
    return (FALSE);

  if (!fmt_disk_write ((ULONG)SEC_SPAREB, (PUCHAR)&spb, sizeof (struct SpareB)))
    return (FALSE);

#ifdef TRACE
  fprintf (stderr, "writing hotfix list\n");
  fflush (stderr);
#endif

  if (!fmt_disk_write (spb.lsnHotFix, (PUCHAR)hotfix_tbl, SPB * SECSIZE))
    return (FALSE);

#ifdef TRACE
  fprintf (stderr, "writing dirblk bitmap\n");
  fflush (stderr);
#endif

  if (!fmt_disk_write (sb.lsnDirBlkMap, (PUCHAR)db_bitmap, SPB * SECSIZE))
    return (FALSE);

#ifdef TRACE
  fprintf (stderr, "writing root fnode, %d bytes\n", sizeof (struct FNODE));
  fflush (stderr);
#endif

/*
 * Initialize the userid mapping table to 8 sectors of zeros and write it.
 */

  if ((p [0]=malloc (8 * SECSIZE)) == NULL)
    return (FALSE);
  memset (p [0], '\0', 8 * SECSIZE);
  if (!fmt_disk_write (sb.lsnSidTab, (PUCHAR)(p [0]), 8L * SECSIZE)) {
    free (p [0]);
    return (FALSE);
  }
  free (p [0]);

  if (!fmt_disk_write (sb.lsnRootFNode, (char *)&fmt_root_fnode, sizeof (struct FNODE)))
    return (FALSE);

#ifdef TRACE
  fprintf (stderr, "writing root dirblk\n");
  fflush (stderr);
#endif

  if (!fmt_disk_write (fmt_root_fnode.fst.a.aall [0].lsnPhys, (PUCHAR)&fmt_root_dir,
                   sizeof (struct DIRBLK)))
    return (FALSE);

#ifdef TRACE
  fprintf (stderr, "writing fmt_free_ind [0]\n");
  fflush (stderr);
#endif

  if (!fmt_disk_write (sb.rspBitMapIndBlk.lsnMain, (PUCHAR)fmt_free_ind [0],
                       fmt_cblocks_bii * SPB * SECSIZE))     /* BIGDISK */
   return (FALSE);


#ifdef TRACE
  fprintf (stderr, "writing first bad list block\n");
  fflush (stderr);
#endif

  if (!fmt_disk_write (sb.rspBadBlkList.lsnMain, (PUCHAR)fmt_bad, SPB * SECSIZE))
    return (FALSE);


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

  for (i=0; i<fmt_num_free_maps; i++)
    if (!fmt_disk_write (fmt_free_ind [0][i],
                         &(bitmap [(ULONG)i * SPB * SECSIZE]),
                         SPB * SECSIZE))
      return (FALSE);

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

  for (i=1; i<fmt_num_bad_blocks; i++)
    if (!fmt_disk_write (fmt_bad [i-1].fwd, (PUCHAR)&(fmt_bad [i]),
                     SPB * SECSIZE))
      return (FALSE);

/*
 * All done writing the file system.  Deallocate the map memory.
 */

  FREE_HUGE (bitmap);
  FREE_BLOCK (fmt_bad);

  FREE_BLOCK (fmt_free_ind [0]);

#ifdef  V12
  if (!fmt_label_flag)
    fmt_query_volid (TRUE);

  fmt_write_volid (pinboot.Extended_Boot.Boot_Vol_label);
#endif

  DosDevIOCtl ((char *)&l, (char *)&l, IOCTL_REDETERMINE,
               IOCTL_CATEGORY, disk_handle);

  allow_signals ();

/*
 * Release the disk handle so the volume will be mounted normally.
 * We cannot do a DosQFSInfo to determine free space on a force-
 * mounted volume.
 */

  close_disk ();

/*
 * Display statistics.  Each number is divided by 2L because these numbers
 * are tallied in sectors, and the messages say "nnnn kilobytes....".
 */

  sprintf (scratchptr, "%8ld", number_of_sectors / 2L);
  FMT_COMPUTMSG( SYSTEM_MSG, &scratchptr, 1, FMT_BYTES_ON_DISK_STR, msgfilename, STDOUT_CODE,
             NO_RESPONSE, NULL, 0);

  if (num_bad_sectors) {
    p [0] = scratchbuf;
    sprintf (scratchbuf, "%8ld", num_bad_sectors / 2L);
    FMT_COMPUTMSG( SYSTEM_MSG, p, 1, FMT_BAD_BYTES_STR, msgfilename, STDOUT_CODE, NO_RESPONSE,
               NULL, 0);
  }


  if (DosQFSInfo( (USHORT) (toupper(disk_name[0]) -'A'+1),0x0001,
                (UCHAR far *) &fsinfo_buf,
                sizeof(struct _FSALLOCATE) ))
    fmt_exit_error (OPEN_ERROR);

  v_free_space = ( fsinfo_buf.cSectorUnit * fsinfo_buf.cUnitAvail ) /           //56340
                        ( 1024 / fsinfo_buf.cbSector );                         //56340

  sprintf (scratchptr, "%8ld", v_free_space);
  FMT_COMPUTMSG( SYSTEM_MSG, &scratchptr, 1, FMT_BYTES_AVAIL_STR, msgfilename, STDOUT_CODE,
             NO_RESPONSE, NULL, 0);

  return (TRUE);
}

/***    fmt_get_boot_id - return a "unique" long id
 *
 *      This function provides a "unique" number which the OS uses
 *      to refer to the volume.  The number is simply a check sum
 *      of a time structure.
 *
 *      fmt_get_boot_id (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            Return a long int id number
 *
 *      CALLS           DosGetDateTime
 *                      fmt_checksum
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */
ULONG fmt_get_boot_id (void)
{
  DATETIME time;

  if (DosGetDateTime (&time))
    fmt_exit_error (INTERNAL_ERROR);

  return (fmt_checksum ((PUCHAR)&time, sizeof (DATETIME)));
}

/***    fmt_checksum - calculate a checksum
 *
 *      This function is called by fmt_get_boot_id to checksum a time
 *      structure, to provide a more-or-less unique volume ID.
 *
 *      fmt_checksum (p, c)
 *
 *      ENTRY           p - pointer to object to sum
 *                      c - number of bytes to sum
 *
 *      EXIT            Returns the checksum
 *
 *      CALLS           None
 *
 *      WARNINGS        None
 *
 *      EFFECTS         None
 */
ULONG fmt_checksum (p, c)
  UCHAR *p;
  USHORT c;
{
  ULONG l=0L;

  while (c--) {
    l += *p++;
    l = (l >> 2) + (l << 30);
  }

  return (l);
}

#ifdef DUMPS
void fmt_dump_maps ()
{
  register ULONG i;
  FILE *fp;

  fp = fopen ("free.dmp", "wb");

  for (i=0; i<fmt_num_free_maps * SPB * SECSIZE; i++)
    fputc (bitmap [i], fp);

  fclose (fp);

  fp = fopen ("bad.dmp", "wb");

  for (i=0; i<fmt_num_bad_blocks * SPB * SECSIZE; i++)
    fputc (*((char *)fmt_bad + i), fp);

  fclose (fp);

  fp = fopen ("free_in.dmp", "wb");

  for (i=0; i<SPB * SECSIZE; i++)
    fputc (fmt_free_ind [0][i], fp);

  fclose (fp);
}

void fmt_dump (fp, p, size)
  FILE *fp;
  UCHAR *p;
  USHORT size;
{
  register USHORT i;

  for (i=0; i<size; i++)
    fputc (*(p + i), fp);
}
#endif
