/*static char *SCCSID = "@(#)fsdata.c   6.8 92/02/21";*/
#define INCL_DOSINFOSEG
#include <os2def.h>
#include <bse.h>
#include <fsd.h>
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pb_ifs.h"     /* for hvol */
#include "lw.h"
#include "frame.h"
#include "fsdata.h"

/*
 ***    HASH TABLE DATA SPACE
 */

PBUFNODE apbufHashTable [HASHCNT];      /* hash table */
PBUFNODE pbufHashDummy;                 /* initial hash head pointer */


/*
 ***    DIRECTORY LOOKASIDE LIST DATA SPACE
 *
 *  Note that we don't bother to initialize this structure, we leave
 *  it with the load time zero values.  For unused records the checksum
 *  will nearly always fail, and when it doesn't, we'll not that the
 *  DL_NAM pointer is zero.
 */

DIRLOOK adlSpace [DLCNT];               /* directory look-aside list */
DOUBLYCHAINEDNEARPTRS dcpDLHead;        /* head of look aside list */


/*
 *** Count of entries in the open file table and head of oft list.
 *      Used in gotf (who initializes these?).
 */

USHORT cOFT = 0;
DOUBLYCHAINEDPTRS oftHead;

/*
 ***    Active Find (FWORK) Chain
 *
 *      We chain the FWORK structures together so that when we delete
 *      a directory we can update any FWORK structures that referred to it.
 */

DOUBLYCHAINEDPTRS dcpFindListHead;


/*
 *** Lookup table that tells how many contiguous bits are in a byte
 */

BYTE abCont[] = { 0,1,1,2,1,1,2,3,1,1,1,2,2,2,3,4,
                  1,1,1,2,1,1,2,3,2,2,2,2,3,3,4,5,
                  1,1,1,2,1,1,2,3,1,1,1,2,2,2,3,4,
                  2,2,2,2,2,2,2,3,3,3,3,3,4,4,5,6,
                  1,1,1,2,1,1,2,3,1,1,1,2,2,2,3,4,
                  1,1,1,2,1,1,2,3,2,2,2,2,3,3,4,5,
                  2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,4,
                  3,3,3,3,3,3,3,3,4,4,4,4,5,5,6,7,
                  1,1,1,2,1,1,2,3,1,1,1,2,2,2,3,4,
                  1,1,1,2,1,1,2,3,2,2,2,2,3,3,4,5,
                  1,1,1,2,1,1,2,3,1,1,1,2,2,2,3,4,
                  2,2,2,2,2,2,2,3,3,3,3,3,4,4,5,6,
                  2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,4,
                  2,2,2,2,2,2,2,3,2,2,2,2,3,3,4,5,
                  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,
                  4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,8 };

/*
 *** Lookup table that tells how many LOW order bits are contiguous in a byte
 */

BYTE abLCont[] = { 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
                   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 };

/*
 *** Lookup table that tells how many HIGH order bits are contiguous in a byte
 */

BYTE abRCont[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
                   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
                   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
                   4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,8 };

/*
 *** Bit Mask for  clearing and setting bits
 */

BYTE abFillMask[] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };


/**     Volume Table
 *
 *      These tables are used to SCASW the volume handle to map it into
 *      a volume table pointer.
 *
 *      The afVolDirty array permits the mounting of any number of
 *      dirty volumes. This introduces the overhead of a "was volume
 *      mounted dirty?" testing, but that testing is not done so
 *      frequently as to be big concern.
 */


PVOLTAB VolPtr [VOLMNTMAX];                /* addresses of VolTab for volume */
HVOL    aVolHVPB [VOLMNTMAX];      /* array used to look up VolPtr index of hVPB */
BOOL    afVolDirty [VOLMNTMAX];    /* flags indicating dirty mounts */


/*
 ***    Dirty Buffer Mask
 *
 *      This mask contains a single bit which is rotated left
 *      for 32 bits, then wrapped around low order.  Each file open for
 *      write gets the current value assigned to him, then the bit
 *      is shifted.  Each buffer dirtied on behalf of this file gets
 *      this bit set in B_DIRTMASK.  When a flush() call occurs, this
 *      allows us to detect which buffers contain data from this file
 *      and to avoid having to write all dirty buffers.  Naturally
 *      two files can be assigned the same bit, so we may flush more
 *      than the absolute minimum, but on the average we'll reduce
 *      by a factor of 32 the number of unnecessary writes in response
 *      to a flush.
 *
 */
USHORT flDirtMask = DMASK_INIT;

/*      SUBDIR cache management
 *
 *      We prune the SUBDIR down when we think that it's getting "too large".
 */

#if 0 /* This seems to be bogus (taken from PIA) */
/*
 *      Large is defined relative to the total cache size allocated.  The
 *      user can specify a scale factor to indicate a light use, medium use,
 *      or heavy normal use of directorys.
 */
USHORT SubdirFactor = 1;    /* Scale factor (0=light, 1=norm, 2=heavy) */
ULONG SbudirScale[] = {
        SPB*SECSIZE/90, /* 1/90th of cache */
        SPB*SECSIZE/60, /* 1/60th of cache */
        SPB*SECSIZE/30  /* 1/30th of cache */
                      } ;
#endif

ULONG cbTotalSubdirs = 0;     /* Total bytes of heap used by SUBDIR cache */
ULONG ulLastSubdirTrim = 0;   /* Last time SUBDIRs had been trimmed. */
ULONG ulSubdirTrimRate = 600; /* Time between trims. */
ULONG cbMaxSubdirs = 60000;   /* Max SUBDIR space before trim occurs */

/*      SUBDIR LRU Chain
 *
 *      We keep an LRU chain for aging these guys out.  Note that
 *      we don't put the root SUBDIRs on this chain; they may not be
 *      aged out.
 */

SUBDIR sbdLruHead = {
    { &sbdLruHead, &sbdLruHead },
    { NULL, NULL },
    { &sbdLruHead, &sbdLruHead },
    NULL, NULL,
    { 0, NULL },
    0, 0, 0, 0, 0, 0,
    NULL, 0, 0, 0,
    0, 0
                    } ;


/*
 *** Head and tail pointers to lazy write queue.
 */

BUFNODE bufLWHead;

/*
 *** Head and tail pointers to lazy write queue.
 */

BUFNODE bufLWHeadVDU;    //#76575

/*
 * Flag showing if GFB is in process, and head of list for people
 * waiting for it to finish.
 */

BOOL fGFBActive = FALSE;
PVOID pGFBWaitChain = NULL;

/*
 * Flag showing that all buffers are being used and GFB is waiting
 * for PlaceBuf to free one.
 */
PVOID pNoBufWaitChain = NULL;


/*
 ***    Dirty bits if entire block changed starting at sector 'i'
 */
BYTE abDirtBits[] = {
#if SPB == 4
        0x0f, 0x0e, 0x0c, 0x08
#elif SPB == 8
        0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80
#endif
                     };

/*
 ***    Clearing mask for buf.bDirtMask value if we don't finish block
 */
BYTE abDirtBits2[] = {
#if SPB == 4
        0xff, 0xf1, 0xf3, 0xf7
#elif SPB == 8
        0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f
#endif
                     };

/*
 *** Dirty Ordinal table--lookup table to set dirty bits after a write
 *
 */

BYTE abDirtOrd[] = {
#if SPB == 4
           1, 3, 7, 0xf,
           0, 2, 6, 0xe,
           0, 0, 4, 0xc,
           0, 0, 0, 8
#elif SPB == 8
        0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
        0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e, 0xfe,
        0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x7c, 0xfc,
        0x00, 0x00, 0x00, 0x08, 0x18, 0x38, 0x78, 0xf8,
        0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x70, 0xf0,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0xe0,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xc0,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
#endif
                        };

/*
 *** Number of buffers to allocate.
 */

USHORT cBufs;
USHORT cBufLZRD = 0;

/*
 *** flag indicating presence of Lazy Write Daemon
 */

LWPARMS LWData = {              /* Lazy write parameters                */
    1000L,      /* msIdle       ms of no disk I/O to declare idle  */
    5000L,      /* msDataAge    max number of ms dirty data in cache */
     500L,      /* msIdleBuf    ms since last buffer dirty before idle flush */
   65536L,      /* cbWriteCache threshold below which all writes are cached */
    65535,      /* cbufDirtyMax max # dirty before AddLw wakes up lazy write */
        0       /* cbuf         total number of buffers */
};

BOOL fLWDaemon = FALSE;
BOOL fLWDaemon2 = FALSE;
BOOL fLWDaemon3 = FALSE;
BOOL fVLWDaemon = FALSE;
ULONG msLastIO;             /* ms count for last non-lazy I/O       */
ULONG semLW = 0;            /* semaphore for lazy daemon block      */
ULONG semLW2 = 0;           /* semaphore for lazy daemon block      */
ULONG semLW3 = 0;           /* semaphore for lazy daemon block      */
ULONG semVLW = 0;           /* semaphore for lazy daemon block      */
ULONG semLWDone = 0;     /* semaphore for stopping of lazy writer1 */
ULONG semLW2Done = 0;    /* semaphore for stopping of lazy writer2 */
ULONG semLW3Done = 0;    /* semaphore for stopping of lazy writer3 */
BOOL fLWIO = FALSE;         /* flag indicating that there is LW I/O */
BOOL fForceLWIO = FALSE;     /* flag indicating to force LW I/O */
BOOL fForceVLWIO = FALSE;    /* flag indicating to force VDU LW I/O */
PBUFNODE LWBufPtr [LWBUFCT];
PBUFNODE VLWBufPtr [LWBUFCT];
#ifndef MINIFSD
PBYTE LWBuf = 0;            /* lazy writer buffer (non-async I/O only) */
PBYTE VLWBuf = 0;           /* VDU lazy writer buffer (non-async I/O only) */
#endif
USHORT cbufLWLock = 0;  /* count of buffers locked by lazy write thread */
BOOL fWriteSparesBusy = FALSE;  /* TRUE => lazy writer updating spare block */

// @130910 - removed lazy-read zero buffer
// BOOL fLzRdZeroBuf = FALSE;  /* TRUE=>lazy read scratch buffer allocated */
// PBYTE pLzRdZeroBuf;         /* pointer to space allocated for lazy read */
                            /* zero-filled scratch buffer                           */

/*
 *** I/O largest cached read request
 */
USHORT usRdIOSize;

/*
 *** maximum disk I/O wait time (seconds)
 */
ULONG ulMaxIOWait;

/*
 *** Count of dirty buffers
 */

USHORT cbufDirty = 0;       /* this is decremented in DCBREM when we remove
                                            * a buffer out of the dirty chain
                             */
USHORT cbufDirtyVDU = 0;         /* this is decremented in DCBREM when we remove
                                            * a VDU buffer out of the dirty chain
                             */
BOOL fVDULWIO = FALSE;

BOOL fStrat1DDPresent = FALSE;

/*
 *** Hot Fix sector buffer & semaphore
 */

extern BYTE abHotFixBuf[SECSIZE]; /* Defined and initialized in cachdata.asm */
ULONG semHotFix = 0;

/*
 *** Global pointer to error message to avoid redundantly storing
 *   initialized string in data segment.
 */

#ifdef DEBUG
char *pszSemClrErr = "SemClear: FSH_SEMCLEAR returned %d\n";
#else
PSZ pszSemClrErr = "SemClear: FSH_SEMCLEAR failed\n";
#endif


/*
 *** Global/Local information
 */

PGINFOSEG pGIS;                 /*  global info seg                   */


/*
 *** Globals which fsd's must export
 */

ULONG _FS_ATTRIBUTE = FSA_LVL7;  /* Set the Level 7 qpathinfo bit */
CHAR _FS_NAME [] = "HPFS";


/*
 *** Data block selector to bufnode index mapping array.
 *
 *      This is used by the macro which gets us the bufnode given the
 *      data block.
 */

PUSHORT pusBlockMap;

#if defined (DEBUG) || defined (ASSERT)
/*
 ***    flFastFile - bitmask for enabling of debugging checks
 */

ULONG flFastFile = FF_FLUSHLAZY | FF_LRUCHK | FF_DIRMAP | FF_PLACECHK;

#endif


/*
 *** Boolean = TRUE iff file system is re-entrant
 */

#ifdef ONE_THREAD
BOOL fMTFlag = FALSE;
#else
BOOL fMTFlag = TRUE;
#endif

/*
 *** Indicates whether we have initialized the bufnodes
 */

BOOL fInit = FALSE;
BOOL fInit1Free = FALSE;

/*
 * Empty EAOP to use if user gives us a NULL pointer in OPEN.
 *
 * Note that the important fields of this structure (pfeaList and pgeaList)
 * dont get written to by any functions in pinball, therefore it is OK to
 * have multiple threads accessing the same structure.
 *
 * Note also that this structure in reality is an EAOP.  It is not defined
 * that way because doing so would put an EA.H dependency on fsdata, and I
 * did not want to do that.
 *
 * P.P.S:  we don't export ulFEAList in fsdata.h because we don't want any
 * functions to change it.  (It gets refrenced by the 2nd ULONG in aulEaopNull)
 */
ULONG ulFEAList = sizeof( ULONG );

extern ULONG aulEaopNull[3]; /* Defined and initialized in cachdata.asm */

/*
 * So we can get to the dev helps
 */
ULONG pDevHlp;  /* pointer to dev hlp function */


/*
 ***    BUFFER HEADER GLOBALS
 */

BUFNODE bufLRUHead;                     /* the head of lru list */
BUFNODE abufCache[1];                   /* array of buffer headers */

SWAP swap_TEXT, swapASWAP0, swapASWAP1, swapASWAP2;
SWAP swap_BTXT, swapBSWAP0, swapBSWAP1, swapBSWAP2;

#if defined (DEBUG)
CHAR PanicMessage [] = "%s: Pinball PANIC stop\n";
#endif

/*
 *  Interrupt messages
 */


#define PBMSG_INIT    /* Initialize messages */

#pragma data_seg(MSG_DATA)

#include "pbmsg.h"

#pragma data_seg()
