/*static char *SCCSID = "@(#)fs_shutd.c	6.3 92/01/31";*/
#define _BTXT _BTXT

/*
 *	HISTORY
 *	    25-Feb-1990 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B788262: fix FS_DoPageIO probs (e.g. nested swap).
 *	    19-Sep-89 by JR (John Rogers, JohnRo@Microsoft)
 *		- Avoid warnings from Microsoft C 5.2 (on slash-slash comments).
 *	    11-Aug-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B705990: Only mark as clean volumes which were originally
 *		  clean.
 *		- Shrink stage usage a little.
 *	    27-Jul-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B784922: Handle shutdown before any HPFS mounts, which
 *		  caused us (in debug version) to try to verify the state of the
 *		  cache before the cache was initialized.
 *	    30-Jun-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B784106: Implemented volume dirty bit on disk.
 *		- Also ifdef'ed more code not needed for MINIFSD.
 *	    05-Jun-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B784101: Fixed reference to INIT_READ_FRAME (should use
 *		  _FAR version).
 *		- PTR B784101: ifdef'ed to allow use with MINIFSD.
 *	    01-Jun-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- DCR 259.2: added counting of write threads for shutdown.
 *		- Added flushing of data to disk and stopping lazy writer.
 */

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>

#include "chain.h"			/* Required by "fsdata.h". */
#include "const.h"			/* Required by "buf.h". */
#include "misc.h"			/* Required by "buf.h". */
#include "buf.h"			/* Required by "fsdata.h". */
#include "dir.h"			/* Required by "fsdata.h". */
#include "heap.h"			/* Required by "pr_heap.h". */
#include "lw.h"				/* Required by "fsdata.h". */
#include "volume.h"			/* PVOLTAB, VS_xxx, etc. */
#include "frame.h"			/* Required by "fsdata.h". */
#include "fsdata.h"			/* fInit, VolPtr[]. */
#include "pbmacros.h"			/* STARTSTACKDEPTH. */
#include "shutdown.h"			/* Externs: cWriteThreadsActive... */
#include "strat2.h"			/* Required by "asyncio.h". */
#include "asyncio.h"			/* Required by "pr_lazy.h". */
#include "storage.h"			/* Needed by "pr_misc.h". */
#include "superb.h"			/* SPF_DIRT. */
#include "tables.h"			/* Needed by "pr_misc.h". */

#include "pr_fsd.h"
#include "pr_debug.h"			/* dprintf(), VBS(). */

#include "pr_heap.h"			/* HeapInit(), RetHeap(). */
#include "pr_lazy.h"			/* StopLazyWriter(). */
#include "pr_misc.h"			/* ClearSparesFlag(), *LOCKSWAP(). */

/* put stand-alone fs_xxx functions in separate code segment */

/*
 * Global variables used to track number of write threads.  These variables
 * are set and used by the other fs_ routines, via the macros in "shutdown.h".
 */
extern BOOL fCacheInit;			/* Has cache been set up?  See MFS(). */
BOOL	fShuttingDown = FALSE;
#ifndef MINIFSD
USHORT	cWriteThreadsActive = 0;
ULONG	semWriteThreadsDone = 0;
#endif /* MINIFSD */

/*
 * Routine which processes the FS_SHUTDOWN request from the IFS.
 * Note that this routine will be called twice: once to begin the shutdown
 * sequence, and once to verify its completion (e.g. by waiting for buffers
 * to flush).
 */
int far pascal
FS_SHUTDOWN (usType, ulReserved)
USHORT usType;
ULONG ulReserved;
{
    INT iRet = NO_ERROR;		/* Assume no error unless... */

#ifndef MINIFSD
    FRAME  frm;

    frm.usHardErr = NO_ERROR;
    frm.fRecheckAddr = TRUE;

    if (!fInit)
    {
	{

	   INT	inxVol;
	   BOOL fFoundMount = FALSE;

	   for (inxVol = 0 ; inxVol < VOLMNTMAX ; ++inxVol)
	   {			   /* search volume table for non-NULL handle */
	      if (aVolHVPB[inxVol] != 0)
	      {
		 fFoundMount = TRUE;			   /* found a handle */
		 break;					       /* leave loop */
	      }
	   }
	   if (fFoundMount == FALSE)
	      return (iRet);   /* don't bother processing if no HPFS volumes */
	}

	HeapInit(&frm);
	fInit = TRUE;
    }
/* If fCacheInit is False then a volume has not been mounted */
	if (!fCacheInit)
	      return (iRet);   /* don't bother processing if no HPFS volumes */

#endif /* MINIFSD */

    switch (usType) {
    case SD_BEGIN:	/* shutdown beginning */

	/*
	 * Flag that the shutdown has started, so the other fs_ routines
	 * can act accordingly.  (See macros in "shutdown.h".)
	 */
	fShuttingDown = TRUE;

#ifndef MINIFSD
	/* Wait for all pending calls to complete. */
	if (cWriteThreadsActive != 0) {
	    USHORT usSemRetCode;

	    do {
		FS_MAYBLOCK(
		    usSemRetCode = FSH_SEMSETWAIT(
				     & semWriteThreadsDone,  /* Ptr to sem. */
				     TO_INFINITE);	     /* Time to wait. */
		);
	    } while (usSemRetCode == ERROR_INTERRUPT);
	    if (usSemRetCode != 0) {
#ifdef DEBUG
		(void) dprintf(__FILE__
				": unexpected value %d from FSH_SEMSETWAIT\n",
				usSemRetCode);
#endif /* DEBUG */
		iRet = usSemRetCode;	/* Tell caller. */
		break;
	    }
	} /* if */
#endif /* MINIFSD */

	/* Return.  We'll actually flush the buffers on the next call. */
	break;

    case SD_COMPLETE:	/* shutdown ending */

	/*
	 * Turn Lazy Writer off and wait for all blocks to be written to disk.
	 * Note: the MAYBLOCK calls for this are down lower; no need to do them
	 * here.
	 */
#ifndef MINIFSD
	StopLazyWriter(&frm);
#endif /* MINIFSD */

	/*
	 * Clear dirty bits on volumes which were mounted normally.  (The other
	 * type of mount, a forced mount, is used if the dirty bit was set when
	 * the volume was mounted.  It is also used if the volume isn't a
	 * Pinball volume at all.  In either case, the forced mount allows the
	 * user to format the disk or run CHKDSK, but not much else.)
	 */
#ifndef MINIFSD
	{
	    USHORT usCurrVol;
	    for (usCurrVol=0; usCurrVol < VOLMNTMAX; ++usCurrVol) {
		PVOLTAB pvol = VolPtr[usCurrVol];
		if (pvol != NULL) {
		    if ( ( ! (pvol->bStatusFlags & VS_MOUNTING) ) &&
			 ( ! (pvol->bStatusFlags & VS_WAS_DIRTY) ) ) {

			/* Normal disk, so clear the dirty bit. */
			LOCKSWAP(&swapBSWAP1);
			ClearSparesFlag(pvol, SPF_DIRT,&frm);
			UNLOCKSWAP(&swapBSWAP1);
		    }
		}
	    } /* for */
	}
#endif /* MINIFSD */

	break;

    default:
	iRet = ERROR_NOT_SUPPORTED;
    }

#ifdef DEBUG
    if (fCacheInit) {
	VBS();
    }
#endif

    return (iRet);

    /* for warning level 3 */
    ulReserved;
}
