/*static char *SCCSID = "@(#)io.c	6.1 90/11/18";*/
/*static char *SCCSID = "@(#)io.c       13.4 90/01/30";*/
/***	IO.C - Routines to perform disk I/O
 *
 *	CHKDSK
 *	Gregory A. Jones
 *	Copyright 1988 Microsoft Corporation
 *
 *	Modification history:
 *	G.A. Jones	05/26/88	Original for Pinball 1.0.
 */

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

/***	chk_read_sectors - read a run of sectors into allocated memory.
 *
 *	This function is called to read a run of sectors into a fresh
 *	area of memory.  It first allocates memory large enough to hold
 *	the request, and then calls read_scratch on it.  The address of
 *	the allocated memory is returned.  This function is useful for
 *	reading data inside a recursive function, which must allocate
 *	a separate area for each level of nesting.
 *
 *	chk_read_sectors (psn, ns)
 *
 *	ENTRY		psn - sector number to start at
 *			ns  - number of sectors to read
 *
 *	EXIT		Returns pointer to structure if success, NULL if not
 *
 *	CALLS		read_scratch
 *
 *	EFFECTS 	Allocates memory
 *
 *	WARNINGS	Returns NULL pointer if insufficient mem or read fail
 */
void *chk_read_sectors (psn, ns)
  ULONG psn;
  USHORT ns;
{
  UCHAR *p;

  p = ALLOCATE_BLOCK (ns);		/* allocate memory */
  if (!p) {
#ifdef TRACE_READS
    fprintf (stderr, "chk_read_sectors:  ALLOCATE_BLOCK failed.\n");
    fflush (stderr);
#endif
    return (NULL);			/* allocation failed */
  }

  if (read_scratch (psn, p, ns)) {
#ifdef TRACE
    fprintf (stderr, "chk_read_sectors:  read_scratch failed.\n");
    fflush (stderr);
#endif
    free (p);				/* deallocate memory */
    return (NULL);			/* because the read failed */
  }

  else
    return (p); 			/* return pointer to memory */
}

/***	chk_write_sectors - write a run of sectors to disk, deallocating memory.
 *
 *	This function is called to write a run of sectors read by
 *	the chk_read_sectors function, and deallocate the memory when
 *	the data is written.
 *
 *	chk_write_sectors (psn, mem, ns)
 *
 *	ENTRY		psn - sector number to start at
 *			mem - address of memory containing data to write
 *			ns  - number of sectors to write
 *
 *	EXIT		Returns zero if success, else OS/2 error code
 *
 *	CALLS		write_scratch
 *
 *	EFFECTS 	Writes memory contents to disk
 *
 *	WARNINGS	Memory deallocated only if successful write
 */
USHORT chk_write_sectors (psn, mem, ns)
  ULONG psn;
  void *mem;
  USHORT ns;
{
  USHORT retcode;

#ifdef TRACE_WRITES
  fprintf (stderr, "chk_write_sectors (%lx, [buffer], %x)\n", psn, ns);
  fflush (stderr);
#endif

  if (retcode=write_scratch (psn, mem, ns)) {	/* write data to disk */
#ifdef TRACE_WRITES
    fprintf (stderr, "chk_write_sectors:  write_scratch failed.\n");
    fflush (stderr);
#endif
    return (retcode);			/* return OS/2 error code */
  }

  else {
    FREE_BLOCK (mem);			/* deallocate the block */
    return (0); 			/* return success */
  }
}

/***	chk_write_data - write to disk, hotfixing if necessary
 *
 *	This function is called to handle errors on writes.  It attempts
 *	to write data to a particular sector given.  If that space has
 *	a hard error, new space is allocated and the data is written there
 *	instead;  if that also has a hard error, more new space will be
 *	tried, until the write is successful or no more space is available.
 *	If the initial write didn't succeed, the new sector where the data
 *	was written is returned.
 *
 *	The last parameter to this function controls whether space is
 *	allocated initially for the data.  If it is FALSE, the data belongs
 *	in the specified sector, and new space will be allocated only if an
 *	error occurs.  If the last parameter is TRUE, the data is new, and
 *	space should be allocated for it near the specified sector.
 *
 *	chk_write_data (lsn, data, nsect, prealloc)
 *
 *	ENTRY		lsn	 - sector to write to (or near)
 *			data	 - data to write
 *			nsect	 - count of sectors to write
 *			prealloc - TRUE if space should be preallocated
 *
 *	EXIT		Returns sector where data was written, or 0L if
 *			  no further space is available
 *			If prealloc==FALSE, returns same sector if first
 *			  write is successful
 *
 *	CALLS		write_scratch
 *			allocate_near
 *
 *	WARNINGS	Do not call with lsn==0L and prealloc==FALSE
 *
 *	EFFECTS 	Writes data to disk
 *			Allocates disk space
 */
ULONG chk_write_data (ULONG lsn, void *data, USHORT nsect, USHORT prealloc)
{
  ULONG sec, l;
  USHORT align;

/*
 * If the size of this object is a cache block, align it on a
 * cache block boundary.
 */

  align = (nsect == SPB) ? SPB : 1;

/*
 * If we need to preallocate space, do so;  otherwise, start writing
 * at the specified sector.
 */

  sec = prealloc ? allocate_near (lsn, nsect, align) : lsn;

/*
 * Try writing until we can successfully write the data.  Each time
 * the write fails, allocate new space nearby.
 */

  while (sec && write_scratch (sec, data, nsect)) {
    for (l=sec; l<sec+nsect; l++)
      make_bad (l);
    use_sectors (sec, (ULONG)nsect, DISK_BAD);
    sec = allocate_near (lsn, nsect, align);
  }

/*
 * Tell the caller where we put the data.
 */

  return (sec);
}
