Files
gottfriedleibniz 6ff24aa680 Fix errors reported by valgrind (#827)
* ctc_lcs.c: ensure pszTUNDevice is free'd

'pszTUNDevice = strdup( DEF_NETDEV );' is definitely lost.
pszOATFilename check included to follow convention.

* cckddasd.c: ensure dasdsfn is free'd

1. ckd_dasd_init_handler invokes cckd_sf_parse_sfn
2. ckd_dasd_init_handler invokes ckd64_dasd_init_handler
3. ckd64_dasd_init_handler invokes cckd_sf_parse_sfn

Leaving previous dasdsfn dangling. Given how 'dasdsfn' is managed (e.g.,
in attach_device and dasd_close_device), it may be more elegant to
compare the previous dasdsfn to the current pathname and throwing a
warning if they are different.

* config.c: ensure prev_buf is free'd

* panel.c: zero prev_psw

To avoid instances of memcmp'ing random stack data with curr_psw.

* control.c: zero newregs tlb

ARCH_DEP( maddr_l ) may reference tlb.asb and tlb.common - ensure
newregs in program_return does not reference garbage stack data.

* cckddasd64.c: zero L2_pad

To avoid feeding random stack data to syscalls later on (e.g., write in
cckd64_write).

* panel.c: ensure messages are null delimited

Uses of string.h functions and regexec assume the string is null
delimited. This may not be true with some _PANMSG msg uses.

* ecpsvm.c: ensure buffer is null delimited

* refactor: CPU_BITMAP formatting

When CPU_BITMAP is __uint128_t and F_CPU_BITMAP is '"%16.16"PRIX64',
random/clobbered stack data will be referenced in the associated libc
formatting routines used by fwritemsg due to the different stack
alignment requirements of the variadic arguments.

* refactor: reorganize history_init

panel_command may be invoked (e.g., 'LPARNUM BASIC') prior to the panel
display thread being initialized. This can lead to history_add being
invoked prior to history_init (causing dangling pointers).

* cckddasd64: zero the entire free block to avoid writing garbage to underlying file

Per MemorySanitizer

* w32util.c: set_thread_name buffer overflow

Ensures correct nNumberOfArguments argument on x64. Updated the struct
definition to match the Microsoft documentation:

https://learn.microsoft.com/en-us/visualstudio/debugger/tips-for-debugging-threads

SetThreadDescription was introduced in Windows 10 version 1607, so
Kernel32.dll could technically be probed to see if that export exists.
2026-04-16 16:34:46 -07:00

4840 lines
163 KiB
C

/* CCKDDASD64.C (C) Copyright Roger Bowler, 1999-2012 */
/* (C) Copyright Greg Smith, 2002-2012 */
/* (C) and others 2013-2022 */
/* */
/* CCKD (Compressed CKD) Device Handler */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/*-------------------------------------------------------------------*/
/* This module contains device functions for compressed emulated */
/* count-key-data direct access storage devices. */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#define _CCKDDASD64_C_
#define _HDASD_DLL_
#include "hercules.h"
#include "devtype.h"
#include "opcode.h"
#include "dasdblks.h"
#include "cckddasd.h"
#include "ccwarn.h"
DISABLE_GCC_UNUSED_SET_WARNING;
/*-------------------------------------------------------------------*/
/* Compressed CKD dasd initialization */
/*-------------------------------------------------------------------*/
int cckd64_dasd_init_handler ( DEVBLK *dev, int argc, char *argv[] )
{
CCKD64_EXT *cckd; /* -> cckd extension */
int i; /* Counter */
int fdflags; /* File flags */
char buf[32]; /* Work buffer */
UNREFERENCED( argc );
UNREFERENCED( argv );
/* Initialize the global cckd block if necessary */
if (memcmp( &cckdblk.id, CCKDBLK_ID, sizeof( cckdblk.id )))
cckd_dasd_init( 0, NULL );
/* Obtain area for cckd extension */
dev->cckd_ext = cckd = cckd_calloc( dev, "ext", 1, sizeof( CCKD64_EXT ));
if (cckd == NULL)
return -1;
/* Initialize locks and conditions */
initialize_lock( &cckd->cckdiolock );
MSGBUF( buf, "&cckd->cckdiolock %1d:%04X", LCSS_DEVNUM );
set_lock_name( &cckd->cckdiolock, buf );
initialize_lock( &cckd->filelock );
MSGBUF( buf, "&cckd->filelock %1d:%04X", LCSS_DEVNUM );
set_lock_name( &cckd->filelock, buf );
initialize_condition( &cckd->cckdiocond );
/* Initialize some variables */
obtain_lock( &cckd->filelock );
{
cckd->L1idx = cckd->sfx = cckd->L2_active = -1;
dev->cache = cckd->free_idx1st = -1;
cckd->fd[0] = dev->fd;
fdflags = get_file_accmode_flags( dev->fd );
cckd->open[0] = (fdflags & O_RDWR) ? CCKD_OPEN_RW : CCKD_OPEN_RO;
for (i = 1; i <= CCKD_MAX_SF; i++)
{
cckd->fd[i] = -1;
cckd->open[i] = CCKD_OPEN_NONE;
}
cckd->cckd_maxsize = CCKD64_MAXSIZE;
/* call the chkdsk function */
if (cckd64_chkdsk (dev, 0) < 0)
return -1;
/* Perform initial read */
if (cckd64_read_init (dev) < 0)
return -1;
if (cckd->fbadasd) dev->ckdtrksz = CFBA_BLKGRP_SIZE;
/* open the shadow files */
if (cckd64_sf_init (dev) < 0)
{
WRMSG (HHC00300, "E", LCSS_DEVNUM);
return -1;
}
/* Update the device handler routines */
if (cckd->ckddasd)
dev->hnd = &cckd_dasd_device_hndinfo;
else
dev->hnd = &cfba_dasd_device_hndinfo;
}
release_lock( &cckd->filelock );
/* Insert the device into the cckd device queue */
cckd_lock_devchain(1);
{
add_dev_to_cckd_chain_locked( dev );
}
cckd_unlock_devchain();
cckdblk.batch = dev->batch;
if (cckdblk.batch)
{
cckdblk.nostress = 1;
cckdblk.freepend = 0;
cckdblk.linuxnull = 1;
}
return 0;
} /* end function cckd64_dasd_init_handler */
/*-------------------------------------------------------------------*/
/* Close a Compressed CKD Device */
/*-------------------------------------------------------------------*/
int cckd64_dasd_close_device (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc, i; /* Return code, Loop index */
if (!dev->cckd64)
return cckd_dasd_close_device( dev );
cckd = dev->cckd_ext;
/* Wait for readaheads to finish */
obtain_lock(&cckdblk.ralock);
cckd->stopping = 1;
while (cckd->ras)
{
release_lock(&cckdblk.ralock);
USLEEP(1);
obtain_lock(&cckdblk.ralock);
}
release_lock(&cckdblk.ralock);
/* Flush the cache and wait for the writes to complete */
obtain_lock( &cckd->cckdiolock );
{
cckd->stopping = 1;
cckd64_flush_cache( dev );
while (cckd->wrpending || cckd->cckdioact)
{
cckd->cckdwaiters++;
rc = timed_wait_condition_relative_usecs(
&cckd->cckdiocond, &cckd->cckdiolock, 1000000, NULL );
cckd->cckdwaiters--;
cckd64_flush_cache( dev );
if (EINTR == rc)
continue;
/* Prevent rare but possible hang at shutdown */
if (1
&& ETIMEDOUT == rc
&& (cckd->wrpending || cckd->cckdioact)
&& sysblk.shutdown
)
{
CCKD_TRACE( "closing device while wrpending=%d cckdioact=%d",
cckd->wrpending, cckd->cckdioact );
// "%1d:%04X CCKD file %s: closing device while wrpending=%d cckdioact=%d"
WRMSG( HHC00381, "W", LCSS_DEVNUM, dev->filename,
cckd->wrpending, cckd->cckdioact );
break;
}
}
broadcast_condition( &cckd->cckdiocond );
cckd64_purge_cache( dev );
cckd64_purge_l2( dev );
dev->bufcur = -1;
dev->cache = -1;
if (cckd->newbuf)
cckd_free( dev, "newbuf", cckd->newbuf );
}
release_lock( &cckd->cckdiolock );
/* Remove the device from the cckd queue */
cckd_lock_devchain(1);
{
remove_dev_from_cckd_chain_locked( dev );
}
cckd_unlock_devchain();
/* harden the file */
obtain_lock( &cckd->filelock );
{
cckd64_harden (dev);
/* close the shadow files */
for (i = 1; i <= cckd->sfn; i++)
{
cckd64_close (dev, i);
cckd->open[i] = 0;
}
/* free the level 1 tables */
for (i = 0; i <= cckd->sfn; i++)
cckd->L1tab[i] = cckd_free (dev, "l1", cckd->L1tab[i]);
/* reset the device handler */
if (cckd->ckddasd)
dev->hnd = &ckd_dasd_device_hndinfo;
else
dev->hnd = &fba_dasd_device_hndinfo;
/* write some statistics */
if (!dev->batch && !cckdblk.nosfd)
cckd64_sf_stats (dev);
}
release_lock( &cckd->filelock );
/* If no more devices then perform global termination */
cckd_dasd_term_if_appropriate();
/* Destroy the cckd extension's locks and conditions */
destroy_lock( &cckd->cckdiolock );
destroy_lock( &cckd->filelock );
destroy_condition( &cckd->cckdiocond );
/* free the cckd extension itself */
dev->cckd_ext = cckd_free (dev, "ext", cckd);
if (dev->dasdsfn) free (dev->dasdsfn);
dev->dasdsfn = NULL;
close (dev->fd);
dev->fd = -1;
dev->buf = NULL;
dev->bufsize = 0;
/* return to caller */
return 0;
} /* end function cckd64_dasd_close_device */
/*-------------------------------------------------------------------*/
/* Compressed ckd start/resume channel program */
/*-------------------------------------------------------------------*/
void cckd64_dasd_start (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
U16 devnum = 0; /* Last active device number */
int trk = 0; /* Last active track */
if (!dev->cckd64)
{
cckd_dasd_start( dev );
return;
}
cckd = dev->cckd_ext;
CCKD_TRACE( "start i/o file[%d] bufcur %d cache[%d]",
cckd->sfn, dev->bufcur, dev->cache);
/* Reset buffer offsets */
dev->bufoff = 0;
dev->bufoffhi = cckd->ckddasd ? dev->ckdtrksz : CFBA_BLKGRP_SIZE;
/* Check for merge */
obtain_lock(&cckd->cckdiolock);
if (cckd->merging)
{
CCKD_TRACE( "start i/o waiting for merge%s","");
while (cckd->merging)
{
cckd->cckdwaiters++;
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
cckd->cckdwaiters--;
}
dev->bufcur = dev->cache = -1;
}
cckd->cckdioact = 1;
cache_lock(CACHE_DEVBUF);
if (dev->cache >= 0)
CCKD_CACHE_GETKEY(dev->cache, devnum, trk);
/* Check if previous active entry is still valid and not busy */
if (dev->cache >= 0 && dev->devnum == devnum && dev->bufcur == trk
&& !(cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_IOBUSY))
{
/* Make the entry active again */
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_ACTIVE);
/* If the entry is pending write then change it to `updated' */
if (cache_getflag(CACHE_DEVBUF, dev->cache) & CCKD_CACHE_WRITE)
{
cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED);
cckd->wrpending--;
if (cckd->cckdwaiters && !cckd->wrpending)
broadcast_condition (&cckd->cckdiocond);
}
}
else
dev->bufcur = dev->cache = -1;
cache_unlock (CACHE_DEVBUF);
release_lock (&cckd->cckdiolock);
return;
} /* end function cckd64_dasd_start */
/*-------------------------------------------------------------------*/
/* Compressed ckd end/suspend channel program */
/*-------------------------------------------------------------------*/
void cckd64_dasd_end (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
if (!dev->cckd64)
{
cckd_dasd_end( dev );
return;
}
cckd = dev->cckd_ext;
/* Update length if previous image was updated */
if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0)
{
dev->buflen = cckd_trklen (dev, dev->buf);
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
}
dev->bufupd = 0;
CCKD_TRACE( "end i/o bufcur %d cache[%d] waiters %d",
dev->bufcur, dev->cache, cckd->cckdwaiters);
obtain_lock (&cckd->cckdiolock);
cckd->cckdioact = 0;
/* Make the current entry inactive */
if (dev->cache >= 0)
{
cache_lock (CACHE_DEVBUF);
cache_setflag (CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0);
cache_unlock (CACHE_DEVBUF);
}
/* Cause writers to start after first update */
if (cckd->updated && (cckdblk.wrs == 0 || cckd->cckdwaiters != 0))
cckd64_flush_cache (dev);
else if (cckd->cckdwaiters)
broadcast_condition (&cckd->cckdiocond);
release_lock (&cckd->cckdiolock);
} /* end function cckd64_dasd_end */
/*-------------------------------------------------------------------*/
/* Open a cckd file */
/* */
/* If O_CREAT is not set and mode is non-zero then the error message */
/* will be suppressed. */
/*-------------------------------------------------------------------*/
int cckd64_open (DEVBLK *dev, int sfx, int flags, mode_t mode)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int err; /* 1 = issue error message */
char pathname[MAX_PATH]; /* file path in host format */
cckd = dev->cckd_ext;
err = !((flags & O_CREAT) == 0 && mode != 0);
if (cckd->fd[sfx] >= 0)
cckd64_close (dev, sfx);
hostpath(pathname, cckd_sf_name (dev, sfx), sizeof(pathname));
cckd->fd[sfx] = HOPEN (pathname, flags, mode);
if (sfx == 0) dev->fd = cckd->fd[sfx];
if (cckd->fd[sfx] >= 0)
cckd->open[sfx] = flags & O_RDWR ? CCKD_OPEN_RW :
cckd->open[sfx] == CCKD_OPEN_RW ?
CCKD_OPEN_RD : CCKD_OPEN_RO;
else
{
if (err)
{
WRMSG (HHC00301, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
"open()", strerror(errno));
CCKD_TRACE( "file[%d] fd[%d] open %s error flags %8.8x mode %8.8x",
sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode);
cckd_print_itrace ();
}
cckd->open[sfx] = CCKD_OPEN_NONE;
}
CCKD_TRACE( "file[%d] fd[%d] open %s, flags %8.8x mode %8.8x",
sfx, cckd->fd[sfx], cckd_sf_name (dev, sfx), flags, mode);
return cckd->fd[sfx];
} /* end function cckd64_open */
/*-------------------------------------------------------------------*/
/* Close a cckd file */
/*-------------------------------------------------------------------*/
int cckd64_close (DEVBLK *dev, int sfx)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc = 0; /* Return code */
cckd = dev->cckd_ext;
CCKD_TRACE( "file[%d] fd[%d] close %s",
sfx, cckd->fd[sfx], cckd_sf_name(dev, sfx));
if (cckd->fd[sfx] >= 0)
rc = close (cckd->fd[sfx]);
if (rc < 0)
{
WRMSG (HHC00301, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
"close()", strerror(errno));
cckd_print_itrace ();
}
cckd->fd[sfx] = -1;
if (sfx == 0) dev->fd = -1;
return rc;
} /* end function cckd64_close */
/*-------------------------------------------------------------------*/
/* Read from a cckd file */
/*-------------------------------------------------------------------*/
int cckd64_read( DEVBLK* dev, int sfx, U64 off, void* buf, unsigned int len )
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
cckd = dev->cckd_ext;
CCKD_TRACE( "file[%d] fd[%d] read, off 0x%16.16"PRIx64" len %d",
sfx, cckd->fd[ sfx ], off, len );
/* Seek to specified offset */
if (lseek( cckd->fd[ sfx ], off, SEEK_SET ) < 0)
{
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
"lseek()", off, strerror( errno ));
cckd_print_itrace();
return -1;
}
/* Read the data */
rc = read( cckd->fd[ sfx ], buf, len );
if (rc < (int)len)
{
if (rc < 0)
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
"read()", off, strerror( errno ));
else
{
char buf[128];
MSGBUF( buf, "read incomplete: read %d, expected %d", rc, len );
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
"read()", off, buf );
}
cckd_print_itrace();
return -1;
}
return rc;
} /* end function cckd64_read */
/*-------------------------------------------------------------------*/
/* Write to a cckd file */
/*-------------------------------------------------------------------*/
int cckd64_write( DEVBLK* dev, int sfx, U64 off, void* buf, unsigned int len )
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc = 0; /* Return code */
cckd = dev->cckd_ext;
CCKD_TRACE( "file[%d] fd[%d] write, off 0x%16.16"PRIx64" len %d",
sfx, cckd->fd[ sfx ], off, len );
/* Seek to specified offset */
if (lseek( cckd->fd[ sfx ], off, SEEK_SET ) < 0)
{
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
"lseek()", off, strerror( errno ));
cckd_print_itrace();
return -1;
}
/* Write the data */
rc = write( cckd->fd[ sfx ], buf, len );
if (rc < (int)len)
{
if (rc < 0)
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
"write()", off, strerror( errno ));
else
{
char buf[128];
MSGBUF( buf, "write incomplete: write %d, expected %d", rc, len );
// "%1d:%04X CCKD file[%d] %s: error in function %s at offset 0x%16.16"PRIX64": %s"
WRMSG( HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name( dev, sfx ),
"write()", off, buf );
}
cckd_print_itrace();
return -1;
}
return rc;
} /* end function cckd64_write */
/*-------------------------------------------------------------------*/
/* Truncate a cckd file */
/*-------------------------------------------------------------------*/
int cckd64_ftruncate(DEVBLK *dev, int sfx, U64 off)
{
CCKD64_EXT *cckd; /* -> cckd extension */
cckd = dev->cckd_ext;
CCKD_TRACE( "file[%d] fd[%d] ftruncate, off 0x%16.16"PRIx64,
sfx, cckd->fd[sfx], off);
/* Truncate the file */
if (ftruncate (cckd->fd[sfx], off) < 0)
{
WRMSG (HHC00302, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
"ftruncate()", off, strerror(errno));
cckd_print_itrace ();
return -1;
}
return 0;
} /* end function cckd64_ftruncate */
/*-------------------------------------------------------------------*/
/* Compressed ckd read track image */
/*-------------------------------------------------------------------*/
int cckd64_read_track (DEVBLK *dev, int trk, BYTE *unitstat)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int len; /* Compressed length */
BYTE *newbuf; /* Uncompressed buffer */
int cache; /* New active cache entry */
cckd = dev->cckd_ext;
/* Update length if previous image was updated */
if (dev->bufupd && dev->bufcur >= 0 && dev->cache >= 0)
{
dev->buflen = cckd_trklen (dev, dev->buf);
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
}
/* Reset buffer offsets */
dev->bufoff = 0;
dev->bufoffhi = dev->ckdtrksz;
/* Check if reading the same track image */
if (trk == dev->bufcur && dev->cache >= 0)
{
/* Track image may be compressed */
if ((dev->buf[0] & CCKD_COMPRESS_MASK) != 0
&& (dev->buf[0] & dev->comps) == 0)
{
len = cache_getval(CACHE_DEVBUF, dev->cache);
newbuf = cckd64_uncompress (dev, dev->buf, len, dev->ckdtrksz, trk);
if (newbuf == NULL) {
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
dev->bufcur = dev->cache = -1;
return -1;
}
cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, dev->ckdtrksz);
dev->buf = newbuf;
dev->buflen = cckd_trklen (dev, newbuf);
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
dev->bufupd = 0;
CCKD_TRACE( "read trk %d uncompressed len %d",
trk, dev->buflen);
}
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
if (dev->comp != 0) dev->compoff = CKD_TRKHDR_SIZE;
return 0;
}
CCKD_TRACE( "read trk %d (%s)", trk, "asynchronous");
/* read the new track */
dev->bufupd = 0;
cache = cckd64_read_trk (dev, trk, 0, unitstat);
if (cache < 0)
{
dev->bufcur = dev->cache = -1;
return -1;
}
dev->cache = cache;
dev->buf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
dev->bufcur = trk;
dev->bufoff = 0;
dev->bufoffhi = dev->ckdtrksz;
dev->buflen = cache_getval (CACHE_DEVBUF, dev->cache);
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
dev->comp = dev->buf[0] & CCKD_COMPRESS_MASK;
if (dev->comp != 0) dev->compoff = CKD_TRKHDR_SIZE;
/* If the image is compressed then call ourself recursively
to cause the image to get uncompressed */
if (dev->comp != 0 && (dev->comp & dev->comps) == 0)
rc = cckd64_read_track (dev, trk, unitstat);
else
rc = 0;
return rc;
} /* end function cckd64_read_track */
/*-------------------------------------------------------------------*/
/* Compressed ckd update track image */
/*-------------------------------------------------------------------*/
int cckd64_update_track (DEVBLK *dev, int trk, int off,
BYTE *buf, int len, BYTE *unitstat)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
if (!dev->cckd64)
return cckd_update_track( dev, trk, off, buf, len, unitstat );
cckd = dev->cckd_ext;
/* Error if opened read-only */
if (dev->ckdrdonly && cckd->sfn == 0)
{
ckd_build_sense (dev, SENSE_EC, SENSE1_WRI, 0,FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
dev->bufcur = dev->cache = -1;
return -1;
}
/* If the track is not current or compressed then read it.
`dev->comps' is set to zero forcing the read routine to
uncompress the image. */
if (trk != dev->bufcur || (dev->buf[0] & CCKD_COMPRESS_MASK) != 0)
{
dev->comps = 0;
rc = (dev->hnd->read) (dev, trk, unitstat);
if (rc < 0)
{
dev->bufcur = dev->cache = -1;
return -1;
}
}
/* Invalid track format if going past buffer end */
if (off + len > dev->ckdtrksz)
{
ckd_build_sense (dev, 0, SENSE1_ITF, 0, 0, 0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
dev->bufcur = dev->cache = -1;
return -1;
}
/* Copy the data into the buffer */
if (buf && len > 0) memcpy (dev->buf + off, buf, len);
CCKD_TRACE( "updt trk %d offset %"PRId64" length %d",
trk, off, len);
/* Update the cache entry */
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED | CCKD_CACHE_USED);
cckd->updated = 1;
/* Notify the shared server of the update */
if (!dev->bufupd)
{
dev->bufupd = 1;
shared_update_notify (dev, trk);
}
/* Ensure the Dasd Hardener is running, if it's supposed to be. */
if (cckdblk.dhint > 0)
cckd_dhstart( false );
return len;
} /* end function cckd64_update_track */
/*-------------------------------------------------------------------*/
/* Return used cylinders */
/*-------------------------------------------------------------------*/
int cckd64_used (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int L1idx, l2x; /* Lookup table indexes */
int sfx; /* Shadow file suffix */
CCKD64_L2ENT l2; /* Copied level 2 entry */
if (!dev->cckd64)
return cckd_used( dev );
cckd = dev->cckd_ext;
obtain_lock( &cckd->filelock );
{
/* Find the last used level 1 table entry */
for (L1idx = cckd->cdevhdr[0].num_L1tab - 1; L1idx > 0; L1idx--)
{
sfx = cckd->sfn;
while (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE && sfx > 0) sfx--;
if (cckd->L1tab[sfx][L1idx]) break;
}
/* Find the last used level 2 table entry */
for (l2x = 255; l2x >= 0; l2x--)
{
rc = cckd64_read_l2ent (dev, &l2, L1idx * 256 + l2x);
if (rc < 0 || l2.L2_trkoff != 0) break;
}
}
release_lock( &cckd->filelock );
return (L1idx * 256 + l2x + dev->ckdheads) / dev->ckdheads;
}
/*-------------------------------------------------------------------*/
/* Compressed fba read block(s) */
/*-------------------------------------------------------------------*/
int cfba64_read_block (DEVBLK *dev, int blkgrp, BYTE *unitstat)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int cache; /* New active cache entry */
BYTE *cbuf; /* -> cache buffer */
BYTE *newbuf; /* Uncompressed buffer */
int len; /* Compressed length */
int maxlen; /* Size for cache entry */
cckd = dev->cckd_ext;
if (dev->cache >= 0)
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
else
cbuf = NULL;
maxlen = CFBA_BLKGRP_SIZE + CKD_TRKHDR_SIZE;
/* Return if reading the same track image */
if (blkgrp == dev->bufcur && dev->cache >= 0)
{
/* Block group image may be compressed */
if ((cbuf[0] & CCKD_COMPRESS_MASK) != 0
&& (cbuf[0] & dev->comps) == 0)
{
len = cache_getval(CACHE_DEVBUF, dev->cache) + CKD_TRKHDR_SIZE;
newbuf = cckd64_uncompress (dev, cbuf, len, maxlen, blkgrp);
if (newbuf == NULL)
{
dev->sense[0] = SENSE_EC;
*unitstat = CSW_CE | CSW_DE | CSW_UC;
dev->bufcur = dev->cache = -1;
return -1;
}
cache_setbuf (CACHE_DEVBUF, dev->cache, newbuf, maxlen);
cbuf = newbuf;
dev->buf = newbuf + CKD_TRKHDR_SIZE;
dev->buflen = CFBA_BLKGRP_SIZE;
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
dev->bufupd = 0;
CCKD_TRACE( "read bkgrp %d uncompressed len %d",
blkgrp, dev->buflen);
}
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
return 0;
}
CCKD_TRACE( "read blkgrp %d (%s)", blkgrp, "asynchronous");
/* Read the new blkgrp */
dev->bufupd = 0;
cache = cckd64_read_trk (dev, blkgrp, 0, unitstat);
if (cache < 0)
{
dev->bufcur = dev->cache = -1;
return -1;
}
dev->cache = cache;
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
dev->buf = cbuf + CKD_TRKHDR_SIZE;
dev->bufcur = blkgrp;
dev->bufoff = 0;
dev->bufoffhi = CFBA_BLKGRP_SIZE;
dev->buflen = CFBA_BLKGRP_SIZE;
cache_setval (CACHE_DEVBUF, dev->cache, dev->buflen);
dev->bufsize = cache_getlen (CACHE_DEVBUF, dev->cache);
dev->comp = cbuf[0] & CCKD_COMPRESS_MASK;
/* If the image is compressed then call ourself recursively
to cause the image to get uncompressed. This is because
`bufcur' will match blkgrp and `comps' won't match `comp' */
if (dev->comp != 0 && (dev->comp & dev->comps) == 0)
rc = cfba64_read_block (dev, blkgrp, unitstat);
else
rc = 0;
return rc;
} /* end function cfba64_read_block */
/*-------------------------------------------------------------------*/
/* Compressed fba write block(s) */
/*-------------------------------------------------------------------*/
int cfba64_write_block (DEVBLK *dev, int blkgrp, int off,
BYTE *buf, int len, BYTE *unitstat)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
BYTE *cbuf; /* -> cache buffer */
cckd = dev->cckd_ext;
if (dev->cache >= 0)
cbuf = cache_getbuf (CACHE_DEVBUF, dev->cache, 0);
else
cbuf = NULL;
/* Read the block group if it's not current or compressed.
`dev->comps' is set to zero forcing the read routine to
uncompress the image. */
if (blkgrp != dev->bufcur || (cbuf[0] & CCKD_COMPRESS_MASK) != 0)
{
dev->comps = 0;
rc = (dev->hnd->read) (dev, blkgrp, unitstat);
if (rc < 0)
{
dev->bufcur = dev->cache = -1;
return -1;
}
}
/* Copy the data into the buffer */
if (buf) memcpy (dev->buf + off, buf, len);
/* Update the cache entry */
cache_setflag (CACHE_DEVBUF, dev->cache, ~0, CCKD_CACHE_UPDATED|CCKD_CACHE_USED);
cckd->updated = 1;
/* Notify the shared server of the update */
if (!dev->bufupd)
{
dev->bufupd = 1;
shared_update_notify (dev, blkgrp);
}
return len;
} /* end function cfba64_write_block */
/*-------------------------------------------------------------------*/
/* Return used blocks */
/*-------------------------------------------------------------------*/
int cfba64_used (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int L1idx, l2x; /* Lookup table indexes */
int sfx; /* Shadow file suffix */
CCKD64_L2ENT l2; /* Copied level 2 entry */
if (!dev->cckd64)
return cfba_used( dev );
cckd = dev->cckd_ext;
obtain_lock( &cckd->filelock );
{
/* Find the last used level 1 table entry */
for (L1idx = cckd->cdevhdr[0].num_L1tab - 1; L1idx > 0; L1idx--)
{
sfx = cckd->sfn;
while (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE && sfx > 0) sfx--;
if (cckd->L1tab[sfx][L1idx]) break;
}
/* Find the last used level 2 table entry */
for (l2x = 255; l2x >= 0; l2x--)
{
rc = cckd64_read_l2ent (dev, &l2, L1idx * 256 + l2x);
if (rc < 0 || l2.L2_trkoff != 0) break;
}
}
release_lock( &cckd->filelock );
return (L1idx * 256 + l2x + CFBA_BLKS_PER_GRP) / CFBA_BLKS_PER_GRP;
}
/*-------------------------------------------------------------------*/
/* Read a track image */
/* */
/* This routine can be called by the i/o thread (`ra' == 0) or */
/* by readahead threads (0 < `ra' <= cckdblk.ramax). */
/* */
/*-------------------------------------------------------------------*/
int cckd64_read_trk(DEVBLK *dev, int trk, int ra, BYTE *unitstat)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int fnd; /* Cache index for hit */
int lru; /* Oldest unused cache index */
int len; /* Length of track image */
int maxlen; /* Length for buffer */
int curtrk = -1; /* Current track (at entry) */
U16 devnum; /* Device number */
U32 oldtrk; /* Stolen track number */
U32 flag; /* Cache flag */
BYTE *buf; /* Read buffer */
if (!dev->cckd64)
return cckd_read_trk( dev, trk, ra, unitstat );
cckd = dev->cckd_ext;
CCKD_TRACE( "%d rdtrk %d", ra, trk);
maxlen = cckd->ckddasd ? dev->ckdtrksz
: CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
if (!ra) obtain_lock (&cckd->cckdiolock);
cache_lock (CACHE_DEVBUF);
/* Inactivate the old entry */
if (!ra)
{
curtrk = dev->bufcur;
if (dev->cache >= 0)
cache_setflag(CACHE_DEVBUF, dev->cache, ~CCKD_CACHE_ACTIVE, 0);
dev->bufcur = dev->cache = -1;
}
cckd_read_trk_retry:
/* scan the cache array for the track */
fnd = cache_lookup (CACHE_DEVBUF, CCKD_CACHE_SETKEY(dev->devnum, trk), &lru);
/* check for cache hit */
if (fnd >= 0)
{
if (ra) /* readahead doesn't care about a cache hit */
{ cache_unlock (CACHE_DEVBUF);
return fnd;
}
/* Mark the new entry active */
cache_setflag(CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_ACTIVE | CCKD_CACHE_USED);
cache_setage(CACHE_DEVBUF, fnd);
/* If the entry is pending write then change it to `updated' */
if (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_WRITE)
{
cache_setflag(CACHE_DEVBUF, fnd, ~CCKD_CACHE_WRITE, CCKD_CACHE_UPDATED);
cckd->wrpending--;
if (cckd->cckdwaiters && !cckd->wrpending)
broadcast_condition (&cckd->cckdiocond);
}
buf = cache_getbuf(CACHE_DEVBUF, fnd, 0);
cache_unlock (CACHE_DEVBUF);
CCKD_TRACE( "%d rdtrk[%d] %d cache hit buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
ra, fnd, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
cckdblk.stats_switches++; cckd->switches++;
cckdblk.stats_cachehits++; cckd->cachehits++;
/* if read/write is in progress then wait for it to finish */
while (cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_IOBUSY)
{
cckdblk.stats_iowaits++;
CCKD_TRACE( "%d rdtrk[%d] %d waiting for %s", ra, fnd, trk,
cache_getflag(CACHE_DEVBUF, fnd) & CCKD_CACHE_READING ?
"read" : "write");
cache_setflag (CACHE_DEVBUF, fnd, ~0, CCKD_CACHE_IOWAIT);
cckd->cckdwaiters++;
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
cckd->cckdwaiters--;
cache_setflag (CACHE_DEVBUF, fnd, ~CCKD_CACHE_IOWAIT, 0);
CCKD_TRACE( "%d rdtrk[%d] %d io wait complete",
ra, fnd, trk);
}
release_lock (&cckd->cckdiolock);
/* Asynchronously schedule readaheads */
if (curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
cckd_readahead (dev, trk);
return fnd;
} /* cache hit */
CCKD_TRACE( "%d rdtrk[%d] %d cache miss", ra, lru, trk);
/* If no cache entry was stolen, then flush all outstanding writes.
This requires us to release our locks. cache_wait should be
called with only the cache_lock held. Fortunately, cache waits
occur very rarely. */
if (lru < 0) /* No available entry to be stolen */
{
CCKD_TRACE( "%d rdtrk[%d] %d no available cache entry",
ra, lru, trk);
cache_unlock (CACHE_DEVBUF);
if (!ra) release_lock (&cckd->cckdiolock);
cckd64_flush_cache_all();
cache_lock (CACHE_DEVBUF);
cckdblk.stats_cachewaits++;
cache_wait (CACHE_DEVBUF);
if (!ra)
{
cache_unlock (CACHE_DEVBUF);
obtain_lock (&cckd->cckdiolock);
cache_lock (CACHE_DEVBUF);
}
goto cckd_read_trk_retry;
}
CCKD_CACHE_GETKEY(lru, devnum, oldtrk);
if (devnum != 0)
{
CCKD_TRACE( "%d rdtrk[%d] %d dropping %4.4X:%d from cache",
ra, lru, trk, devnum, oldtrk);
if (!(cache_getflag(CACHE_DEVBUF, lru) & CCKD_CACHE_USED))
{
cckdblk.stats_readaheadmisses++; cckd->misses++;
}
}
/* Initialize the entry */
cache_setkey(CACHE_DEVBUF, lru, CCKD_CACHE_SETKEY(dev->devnum, trk));
cache_setflag(CACHE_DEVBUF, lru, 0, CCKD_CACHE_READING);
cache_setage(CACHE_DEVBUF, lru);
cache_setval(CACHE_DEVBUF, lru, 0);
if (!ra)
{
cckdblk.stats_switches++; cckd->switches++;
cckdblk.stats_cachemisses++;
cache_setflag(CACHE_DEVBUF, lru, ~0, CCKD_CACHE_ACTIVE|CCKD_CACHE_USED);
}
cache_setflag(CACHE_DEVBUF, lru, ~CACHE_TYPE,
cckd->ckddasd ? DEVBUF_TYPE_CCKD : DEVBUF_TYPE_CFBA);
buf = cache_getbuf(CACHE_DEVBUF, lru, maxlen);
CCKD_TRACE( "%d rdtrk[%d] %d buf %p len %d",
ra, lru, trk, buf, cache_getlen(CACHE_DEVBUF, lru));
cache_unlock (CACHE_DEVBUF);
if (!ra) release_lock (&cckd->cckdiolock);
/* Asynchronously schedule readaheads */
if (!ra && curtrk > 0 && trk > curtrk && trk <= curtrk + 2)
cckd_readahead (dev, trk);
/* Clear the buffer if batch mode */
if (dev->batch) memset(buf, 0, maxlen);
/* Read the track image */
obtain_lock( &cckd->filelock );
{
len = cckd64_read_trkimg (dev, buf, trk, unitstat);
}
release_lock( &cckd->filelock );
cache_setval (CACHE_DEVBUF, lru, len);
obtain_lock (&cckd->cckdiolock);
/* Turn off the READING bit */
cache_lock (CACHE_DEVBUF);
flag = cache_setflag(CACHE_DEVBUF, lru, ~CCKD_CACHE_READING, 0);
cache_unlock (CACHE_DEVBUF);
/* Wakeup other thread waiting for this read */
if (cckd->cckdwaiters && (flag & CCKD_CACHE_IOWAIT))
{ CCKD_TRACE( "%d rdtrk[%d] %d signalling read complete",
ra, lru, trk);
broadcast_condition (&cckd->cckdiocond);
}
release_lock (&cckd->cckdiolock);
if (ra)
{
cckdblk.stats_readaheads++; cckd->readaheads++;
}
CCKD_TRACE( "%d rdtrk[%d] %d complete buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
ra, lru, trk, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
if (cache_busy_percent(CACHE_DEVBUF) > 80) cckd64_flush_cache_all();
return lru;
} /* end function cckd64_read_trk */
/*-------------------------------------------------------------------*/
/* Flush updated cache entries for a device */
/* */
/* Caller holds the cckd->cckdiolock */
/* cckdblk.wrlock then cache_lock is obtained and released */
/*-------------------------------------------------------------------*/
void cckd64_flush_cache(DEVBLK *dev)
{
int rc; /* Return code */
TID tid; /* Writer thread id */
if (!dev->cckd64)
{
cckd_flush_cache( dev );
return;
}
/* Scan cache for updated cache entries */
obtain_lock (&cckdblk.wrlock);
cache_lock (CACHE_DEVBUF);
cache_scan (CACHE_DEVBUF, cckd64_flush_cache_scan, dev);
cache_unlock (CACHE_DEVBUF);
/* Schedule the writer if any writes are pending */
if (cckdblk.wrpending)
{
if (cckdblk.wrwaiting)
signal_condition (&cckdblk.wrcond);
else if (cckdblk.wrs < cckdblk.wrmax)
{
/* Schedule a new writer thread */
if (!cckdblk.batch || cckdblk.batchml > 1)
// "Starting thread %s, active=%d, started=%d, max=%d"
WRMSG(HHC00107, "I", CCKD_WR_THREAD_NAME "()",
cckdblk.wra, cckdblk.wrs, cckdblk.wrmax);
++cckdblk.wrs;
/* Release lock across thread create to prevent interlock */
release_lock(&cckdblk.wrlock);
{
rc = create_thread( &tid, JOINABLE, cckd_writer, NULL, CCKD_WR_THREAD_NAME );
}
obtain_lock(&cckdblk.wrlock);
if (rc)
{
// "Error in function create_thread() for %s %d of %d: %s"
WRMSG(HHC00106, "E", CCKD_WR_THREAD_NAME "()",
cckdblk.wrs-1, cckdblk.wrmax, strerror(rc));
--cckdblk.wrs;
}
}
}
release_lock (&cckdblk.wrlock);
}
int cckd64_flush_cache_scan (int *answer, int ix, int i, void *data)
{
CCKD64_EXT *cckd; /* -> cckd extension */
U16 devnum; /* Cached device number */
U32 trk; /* Cached track */
DEVBLK *dev = data; /* -> device block */
UNREFERENCED(answer);
cckd = dev->cckd_ext;
CCKD_CACHE_GETKEY(i, devnum, trk);
if ((cache_getflag(ix,i) & CACHE_BUSY) == CCKD_CACHE_UPDATED
&& dev->devnum == devnum)
{
cache_setflag (ix, i, ~CCKD_CACHE_UPDATED, CCKD_CACHE_WRITE);
++cckd->wrpending;
++cckdblk.wrpending;
CCKD_TRACE( "flush file[%d] cache[%d] %4.4X trk %d",
cckd->sfn, i, devnum, trk);
}
return 0;
}
void cckd64_flush_cache_all()
{
CCKD64_EXT *cckd; /* -> cckd extension */
DEVBLK *dev = NULL; /* -> device block */
cckd_lock_devchain(0);
for (dev = cckdblk.dev1st; dev; dev = cckd->devnext)
{
cckd = dev->cckd_ext;
obtain_lock (&cckd->cckdiolock);
if (!cckd->merging && !cckd->stopping)
cckd64_flush_cache(dev);
release_lock (&cckd->cckdiolock);
}
cckd_unlock_devchain();
}
/*-------------------------------------------------------------------*/
/* Purge cache entries for a device */
/* */
/* Caller holds the cckdiolock */
/* cache_lock is obtained and released */
/*-------------------------------------------------------------------*/
void cckd64_purge_cache(DEVBLK *dev)
{
if (!dev->cckd64)
{
cckd_purge_cache( dev );
return;
}
/* Scan cache and purge entries */
cache_lock (CACHE_DEVBUF);
cache_scan (CACHE_DEVBUF, cckd64_purge_cache_scan, dev);
cache_unlock (CACHE_DEVBUF);
}
int cckd64_purge_cache_scan (int *answer, int ix, int i, void *data)
{
U16 devnum; /* Cached device number */
U32 trk; /* Cached track */
DEVBLK *dev = data; /* -> device block */
UNREFERENCED(answer);
CCKD_CACHE_GETKEY(i, devnum, trk);
if (dev->devnum == devnum)
{
cache_release (ix, i, 0);
CCKD_TRACE( "purge cache[%d] %4.4X trk %d purged",
i, devnum, trk);
}
return 0;
}
/*-------------------------------------------------------------------*/
/* cckd writer thread helper: write the cached track image */
/*-------------------------------------------------------------------*/
void cckd64_writer_write( int writer, int o )
{
CCKD64_EXT* cckd; /* -> cckd extension */
DEVBLK* dev; /* Device block */
U16 devnum; /* Device number */
int trk; /* Track number */
BYTE* buf; /* Buffer */
BYTE* bufp; /* Buffer to be written */
int len, bufl; /* Buffer lengths */
int comp; /* Compression algorithm */
int parm; /* Compression parameter */
U32 flag; /* Cache flag */
BYTE buf2[ 64*1024 ]; /* 64K Compress buffer */
/* Prepare to compress */
CCKD_CACHE_GETKEY( o, devnum, trk );
dev = cckd_find_device_by_devnum( devnum );
if (!dev->cckd64)
{
cckd_writer_write( writer, o );
return;
}
cckd = dev->cckd_ext;
buf = cache_getbuf( CACHE_DEVBUF, o, 0 );
len = cckd_trklen( dev, buf );
comp = len < CCKD_COMPRESS_MIN ? CCKD_COMPRESS_NONE :
cckdblk.comp == 0xff ? cckd->cdevhdr[ cckd->sfn ].cmp_algo
: cckdblk.comp;
parm = cckdblk.compparm < 0 ? cckd->cdevhdr[ cckd->sfn ].cmp_parm
: cckdblk.compparm;
CCKD_TRACE( "%d wrtrk[%d] %d len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
writer, o, trk, len, buf, buf[0], buf[1],buf[2],buf[3],buf[4] );
/* Compress the image if not null */
if ((len = cckd64_check_null_trk( dev, buf, trk, len )) > CKD_NULLTRK_FMTMAX)
{
/* Stress adjustments */
if (1
&& !cckdblk.nostress
&& (0
|| cache_waiters ( CACHE_DEVBUF )
|| cache_busy ( CACHE_DEVBUF ) > 90
)
)
{
cckdblk.stats_stresswrites++;
comp = len < CCKD_STRESS_MINLEN ? CCKD_COMPRESS_NONE
: CCKD_STRESS_COMP;
parm = cache_busy(CACHE_DEVBUF) <= 95 ? CCKD_STRESS_PARM1
: CCKD_STRESS_PARM2;
}
/* Compress the track image */
CCKD_TRACE( "%d wrtrk[%d] %d comp %s parm %d",
writer, o, trk, compname[ comp ], parm );
bufp = (BYTE*) &buf2;
bufl = cckd_compress( dev, &bufp, buf, len, comp, parm );
CCKD_TRACE( "%d wrtrk[%d] %d compressed length %d",
writer, o, trk, bufl );
}
else
{
bufp = buf;
bufl = len;
}
obtain_lock( &cckd->filelock );
{
/* Turn on read-write header bits if not already on */
if (!(cckd->cdevhdr[ cckd->sfn ].cdh_opts & CCKD_OPT_OPENED))
{
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
cckd64_write_chdr( dev );
}
/* Write the track image */
cckd64_write_trkimg( dev, bufp, bufl, trk, CCKD_SIZE_ANY );
cckd->needsdh = 1; /* We've updated the file. */
}
release_lock( &cckd->filelock );
obtain_lock( &cckd->cckdiolock );
{
cache_lock( CACHE_DEVBUF );
{
flag = cache_setflag( CACHE_DEVBUF, o, ~CCKD_CACHE_WRITING, 0 );
}
cache_unlock( CACHE_DEVBUF );
cckd->wrpending--;
if (1
&& cckd->cckdwaiters
&& (0
|| (flag & CCKD_CACHE_IOWAIT)
|| !cckd->wrpending
)
)
{
CCKD_TRACE( "writer[%d] cache[%2.2d] %d signalling write complete",
writer, o, trk );
broadcast_condition( &cckd->cckdiocond );
}
}
release_lock( &cckd->cckdiolock );
CCKD_TRACE( "%d wrtrk[%2.2d] %d complete flags:%8.8x",
writer, o, trk, cache_getflag( CACHE_DEVBUF, o ));
/* Schedule the garbage collector, if it needs to be. */
if (cckdblk.gcint > 0)
cckd_gcstart( false );
/* Ensure the Dasd Hardener is running, if it's supposed to be. */
if (cckdblk.dhint > 0)
cckd_dhstart( false );
} /* end function cckd64_writer_write */
#if defined( DEBUG_FREESPACE )
/*-------------------------------------------------------------------*/
/* Debug routine for checking the free space array */
/*-------------------------------------------------------------------*/
void cckd64_chk_space(DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* Shadow file index */
int err = 0, n = 0, i, p;
U64 largest=0;
U64 total=0;
U64 fpos;
if (!dev->cckd64)
{
cckd_chk_space( dev );
return;
}
cckd = dev->cckd_ext;
sfx = cckd->sfn;
p = -1;
fpos = cckd->cdevhdr[sfx].free_off;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
n++; total += cckd->ifb[i].ifb_len;
if (n > cckd->free_count) break;
if (cckd->ifb[i].ifb_idxprv != p)
err = 1;
if (cckd->ifb[i].ifb_idxnxt >= 0)
{
if (fpos + cckd->ifb[i].ifb_len > cckd->ifb[i].ifb_offnxt)
err = 1;
}
else
{
if (fpos + cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].cdh_size)
err = 1;
}
if (cckd->ifb[i].ifb_pending == 0 && cckd->ifb[i].ifb_len > largest)
largest = cckd->ifb[i].ifb_len;
fpos = cckd->ifb[i].ifb_offnxt;
p = i;
}
if (err
|| (cckd->cdevhdr[sfx].free_off != 0 && cckd->cdevhdr[sfx].free_num == 0)
|| (cckd->cdevhdr[sfx].free_off == 0 && cckd->cdevhdr[sfx].free_num != 0)
|| (n != cckd->cdevhdr[sfx].free_num)
|| (total != cckd->cdevhdr[sfx].free_total - cckd->cdevhdr[sfx].free_imbed)
|| (cckd->free_idxlast != p)
|| (largest != cckd->cdevhdr[sfx].free_largest)
)
{
CCKD_TRACE( "cdevhdr[%d] size %10"PRId64" used %10"PRId64" free 0x%16.16"PRIx64,
sfx, cckd->cdevhdr[sfx].cdh_size, cckd->cdevhdr[sfx].cdh_used,
cckd->cdevhdr[sfx].free_off);
CCKD_TRACE( " nbr %10"PRId64" total %10"PRId64" imbed %10"PRId64" largest %10"PRId64,
cckd->cdevhdr[sfx].free_num,
cckd->cdevhdr[sfx].free_total, cckd->cdevhdr[sfx].free_imbed,
cckd->cdevhdr[sfx].free_largest);
CCKD_TRACE( "free %p nbr %d 1st %d last %d avail %d",
cckd->ifb,cckd->free_count, cckd->free_idx1st,
cckd->free_idxlast, cckd->free_idxavail);
CCKD_TRACE( "found nbr %d total %"PRIu64" largest %"PRIu64,n,total,largest);
fpos = cckd->cdevhdr[sfx].free_off;
for (n = 0, i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
if (++n > cckd->free_count) break;
CCKD_TRACE( "%4d: [%4d] prev[%4d] next[%4d] pos %16.16"PRIx64" len %8"PRId64" %16.16"PRIx64" pend %d",
n, i, cckd->ifb[i].ifb_idxprv, cckd->ifb[i].ifb_idxnxt,
fpos, cckd->ifb[i].ifb_len,
fpos + cckd->ifb[i].ifb_len, cckd->ifb[i].ifb_pending);
fpos = cckd->ifb[i].ifb_offnxt;
}
cckd_print_itrace();
}
} /* end function cckd64_chk_space */
#endif // defined( DEBUG_FREESPACE )
/*-------------------------------------------------------------------*/
/* Get file space */
/*-------------------------------------------------------------------*/
S64 cckd64_get_space(DEVBLK *dev, int *size, int flags)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int i,p,n; /* Free space indexes */
S64 len2; /* Other lengths */
S64 fpos; /* Free space offset */
S64 flen; /* Free space size */
int sfx; /* Shadow file index */
int len; /* Requested length */
if (!dev->cckd64)
return cckd_get_space( dev, size, flags );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
len = *size;
if (flags & CCKD_L2SPACE)
{
flags |= CCKD_SIZE_EXACT;
len = *size = CCKD64_L2TAB_SIZE;
}
CCKD_TRACE( "get_space len %d largest %"PRId64" flags 0x%2.2x",
len, cckd->cdevhdr[sfx].free_largest, flags);
if (len <= CKD_NULLTRK_FMTMAX)
return 0;
if (!cckd->ifb)
cckd64_read_fsp (dev);
len2 = (U64) len + CCKD64_FREEBLK_SIZE;
/* Get space at the end if no space is large enough */
if ((U64)len2 > cckd->cdevhdr[sfx].free_largest
&& (U64)len != cckd->cdevhdr[sfx].free_largest)
{
cckd_get_space_atend:
fpos = cckd->cdevhdr[sfx].cdh_size;
if ((U64)fpos > (cckd->cckd_maxsize - len))
{
// "%1d:%04X CCKD file[%d] %s: get space error, size exceeds %"PRId64"M"
WRMSG (HHC00304, "E", LCSS_DEVNUM, sfx, cckd_sf_name (dev, sfx),
(S64) (cckd->cckd_maxsize >> 20) + 1);
return -1;
}
cckd->cdevhdr[sfx].cdh_size += len;
cckd->cdevhdr[sfx].cdh_used += len;
CCKD_TRACE( "get_space atend 0x%16.16"PRIx64" len %d",fpos, len);
return fpos;
}
/* Scan free space chain */
fpos = cckd->cdevhdr[sfx].free_off;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
if (cckd->ifb[i].ifb_pending == 0
&& ((U64)len2 <= cckd->ifb[i].ifb_len || (U64)len == cckd->ifb[i].ifb_len)
&& ((flags & CCKD_L2SPACE) || (U64)fpos >= cckd->L2_bounds))
break;
fpos = cckd->ifb[i].ifb_offnxt;
}
/* This can happen if largest comes before L2_bounds */
if (i < 0) goto cckd_get_space_atend;
flen = cckd->ifb[i].ifb_len;
p = cckd->ifb[i].ifb_idxprv;
n = cckd->ifb[i].ifb_idxnxt;
/*
* If `CCKD_SIZE_ANY' bit is set and the left over space is small
* enough, then use the entire free space
*/
if ((flags & CCKD_SIZE_ANY) && flen <= cckd->free_minsize)
*size = (int)flen;
/* Remove the new space from free space */
if (*size < (int)flen)
{
cckd->ifb[i].ifb_len -= *size;
if (p >= 0)
cckd->ifb[p].ifb_offnxt += *size;
else
cckd->cdevhdr[sfx].free_off += *size;
}
else
{
cckd->cdevhdr[sfx].free_num--;
/* Remove the free space entry from the chain */
if (p >= 0)
{
cckd->ifb[p].ifb_offnxt = cckd->ifb[i].ifb_offnxt;
cckd->ifb[p].ifb_idxnxt = n;
}
else
{
cckd->cdevhdr[sfx].free_off = cckd->ifb[i].ifb_offnxt;
cckd->free_idx1st = n;
}
if (n >= 0)
cckd->ifb[n].ifb_idxprv = p;
else
cckd->free_idxlast = p;
/* Add entry to the available queue */
cckd->ifb[i].ifb_idxnxt = cckd->free_idxavail;
cckd->free_idxavail = i;
}
/* Find the largest free space if we got the largest */
if ((U64)flen >= cckd->cdevhdr[sfx].free_largest)
{
int i;
cckd->cdevhdr[sfx].free_largest = 0;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
&& cckd->ifb[i].ifb_pending == 0)
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
}
/* Update free space stats */
cckd->cdevhdr[sfx].cdh_used += len;
cckd->cdevhdr[sfx].free_total -= len;
cckd->cdevhdr[sfx].free_imbed += *size - len;
CCKD_TRACE( "get_space found 0x%16.16"PRIx64" len %d size %d",
fpos, len, *size);
return fpos;
} /* end function cckd64_get_space */
/*-------------------------------------------------------------------*/
/* Release file space */
/*-------------------------------------------------------------------*/
void cckd64_rel_space(DEVBLK *dev, U64 pos, int len, int size)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* Shadow file index */
U64 ppos, npos; /* Prev/next free offsets */
int i, p, n; /* Free space indexes */
int pending; /* Calculated pending value */
U64 fsize = size; /* Free space size */
if (!dev->cckd64)
{
cckd_rel_space( dev, pos, len, size );
return;
}
if (len <= CKD_NULLTRK_FMTMAX || pos == CCKD64_NOSIZE || pos == CCKD64_MAXSIZE)
return;
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "rel_space offset 0x%16.16"PRIx64" len %d size %d",
pos, len, size);
if (!cckd->ifb) cckd64_read_fsp (dev);
CCKD_CHK_SPACE(dev);
/* Scan free space chain */
ppos = -1;
npos = cckd->cdevhdr[sfx].free_off;
for (p = -1, n = cckd->free_idx1st; n >= 0; n = cckd->ifb[n].ifb_idxnxt)
{
if (pos < npos) break;
ppos = npos;
npos = cckd->ifb[n].ifb_offnxt;
p = n;
}
/* Calculate the `pending' value */
pending = cckdblk.freepend >= 0 ? cckdblk.freepend : 1 + (1 - cckdblk.fsync);
/* If possible use previous adjacent free space otherwise get an available one */
if (p >= 0 && ppos + cckd->ifb[p].ifb_len == pos && cckd->ifb[p].ifb_pending == pending)
{
cckd->ifb[p].ifb_len += size;
fsize = cckd->ifb[p].ifb_len;
}
else
{
/* Increase the size of the free space array if necessary */
if (cckd->free_idxavail < 0)
{
int new_free_count = cckd->free_count + CCKD_IFB_ENTS_INCR;
if (!(cckd->ifb = cckd_realloc( dev, "ifb", cckd->ifb, new_free_count * CCKD64_IFREEBLK_SIZE )))
return;
cckd->free_idxavail = cckd->free_count;
cckd->free_count = new_free_count;
for (i = cckd->free_idxavail; i < cckd->free_count; i++)
cckd->ifb[i].ifb_idxnxt = i + 1;
cckd->ifb[i-1].ifb_idxnxt = -1;
cckd->free_minsize = CCKD_MIN_FREESIZE( cckd->free_count );
}
/* Get an available free space entry */
i = cckd->free_idxavail;
cckd->free_idxavail = cckd->ifb[i].ifb_idxnxt;
cckd->cdevhdr[sfx].free_num++;
/* Update the new entry */
cckd->ifb[i].ifb_idxprv = p;
cckd->ifb[i].ifb_idxnxt = n;
cckd->ifb[i].ifb_len = size;
cckd->ifb[i].ifb_pending = pending;
/* Update the previous entry */
if (p >= 0)
{
cckd->ifb[i].ifb_offnxt = cckd->ifb[p].ifb_offnxt;
cckd->ifb[p].ifb_offnxt = pos;
cckd->ifb[p].ifb_idxnxt = i;
}
else
{
cckd->ifb[i].ifb_offnxt = cckd->cdevhdr[sfx].free_off;
cckd->cdevhdr[sfx].free_off = pos;
cckd->free_idx1st = i;
}
/* Update the next entry */
if (n >= 0)
cckd->ifb[n].ifb_idxprv = i;
else
cckd->free_idxlast = i;
}
/* Update the free space statistics */
cckd->cdevhdr[sfx].cdh_used -= len;
cckd->cdevhdr[sfx].free_total += len;
cckd->cdevhdr[sfx].free_imbed -= size - len;
if (!pending && fsize > cckd->cdevhdr[sfx].free_largest)
cckd->cdevhdr[sfx].free_largest = fsize;
CCKD_CHK_SPACE(dev);
} /* end function cckd64_rel_space */
/*-------------------------------------------------------------------*/
/* Flush pending free space */
/*-------------------------------------------------------------------*/
void cckd64_flush_space(DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int p,i,n; /* Free free space indexes */
int sfx; /* Shadow file index */
U64 ppos, pos; /* Free space offsets */
if (!dev->cckd64)
{
cckd_flush_space( dev );
return;
}
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "flush_space nbr %"PRId64,cckd->cdevhdr[sfx].free_num);
/* Make sure the free space chain is built */
if (!cckd->ifb) cckd64_read_fsp (dev);
CCKD_CHK_SPACE(dev);
if (cckd->cdevhdr[sfx].free_num == 0 || cckd->cdevhdr[sfx].free_off == 0)
{
cckd->cdevhdr[sfx].free_num = 0;
cckd->cdevhdr[sfx].free_off = 0;
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
}
pos = cckd->cdevhdr[sfx].free_off;
ppos = p = -1;
cckd->cdevhdr[sfx].free_num = 0;
cckd->cdevhdr[sfx].free_largest = 0;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
/* Decrement the pending count */
if (cckd->ifb[i].ifb_pending)
--cckd->ifb[i].ifb_pending;
/* Combine adjacent free spaces */
while (pos + cckd->ifb[i].ifb_len == cckd->ifb[i].ifb_offnxt)
{
n = cckd->ifb[i].ifb_idxnxt;
if (cckd->ifb[n].ifb_pending > cckd->ifb[i].ifb_pending + 1
|| cckd->ifb[n].ifb_pending < cckd->ifb[i].ifb_pending)
break;
cckd->ifb[i].ifb_offnxt = cckd->ifb[n].ifb_offnxt;
cckd->ifb[i].ifb_len += cckd->ifb[n].ifb_len;
cckd->ifb[i].ifb_idxnxt = cckd->ifb[n].ifb_idxnxt;
cckd->ifb[n].ifb_idxnxt = cckd->free_idxavail;
cckd->free_idxavail = n;
n = cckd->ifb[i].ifb_idxnxt;
if (n >= 0)
cckd->ifb[n].ifb_idxprv = i;
}
ppos = pos;
pos = cckd->ifb[i].ifb_offnxt;
cckd->cdevhdr[sfx].free_num++;
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
&& !cckd->ifb[i].ifb_pending)
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
p = i;
}
cckd->free_idxlast = p;
CCKD_TRACE( "rel_flush_space nbr %"PRId64" (after merge)",
cckd->cdevhdr[sfx].free_num);
/* If the last free space is at the end of the file then release it */
if (p >= 0 && ppos + cckd->ifb[p].ifb_len == cckd->cdevhdr[sfx].cdh_size
&& !cckd->ifb[p].ifb_pending)
{
i = p;
p = cckd->ifb[i].ifb_idxprv;
CCKD_TRACE( "file[%d] rel_flush_space atend 0x%16.16"PRIx64" len %"PRId64,
sfx, ppos, cckd->ifb[i].ifb_len);
/* Remove the entry from the chain */
if (p >= 0)
{
cckd->ifb[p].ifb_offnxt = 0;
cckd->ifb[p].ifb_idxnxt = -1;
}
else
{
cckd->cdevhdr[sfx].free_off = 0;
cckd->free_idx1st = -1;
}
cckd->free_idxlast = p;
/* Add the entry to the available chain */
cckd->ifb[i].ifb_idxnxt = cckd->free_idxavail;
cckd->free_idxavail = i;
/* Update the device header */
cckd->cdevhdr[sfx].cdh_size -= cckd->ifb[i].ifb_len;
cckd->cdevhdr[sfx].free_total -= cckd->ifb[i].ifb_len;
cckd->cdevhdr[sfx].free_num--;
if (cckd->ifb[i].ifb_len >= cckd->cdevhdr[sfx].free_largest)
{
cckd->cdevhdr[sfx].free_largest = 0;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
if (cckd->ifb[i].ifb_len > cckd->cdevhdr[sfx].free_largest
&& cckd->ifb[i].ifb_pending == 0)
cckd->cdevhdr[sfx].free_largest = cckd->ifb[i].ifb_len;
}
/* Truncate the file */
cckd64_ftruncate (dev, sfx, cckd->cdevhdr[sfx].cdh_size);
} /* Release space at end of the file */
CCKD_CHK_SPACE(dev);
} /* end function cckd64_flush_space */
/*-------------------------------------------------------------------*/
/* Read compressed dasd header */
/*-------------------------------------------------------------------*/
int cckd64_read_chdr (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
if (!dev->cckd64)
return cckd_read_chdr( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "file[%d] read_chdr", sfx);
memset(&cckd->cdevhdr[sfx], 0, CCKD64_DEVHDR_SIZE);
/* Read the device header */
if (cckd64_read (dev, sfx, CKD_DEVHDR_SIZE, &cckd->cdevhdr[sfx], CCKD64_DEVHDR_SIZE) < 0)
return -1;
/* Check endian format */
cckd->swapend[sfx] = 0;
if ((cckd->cdevhdr[sfx].cdh_opts & CCKD_OPT_BIGEND) != cckd_def_opt_bigend())
{
if (cckd->open[sfx] == CCKD_OPEN_RW)
{
if (cckd64_swapend (dev) < 0)
return -1;
cckd64_swapend_chdr (&cckd->cdevhdr[sfx]);
}
else
{
cckd->swapend[sfx] = 1;
cckd64_swapend_chdr (&cckd->cdevhdr[sfx]);
}
}
/* Set default null format */
if (cckd->cdevhdr[sfx].cdh_nullfmt > CKD_NULLTRK_FMTMAX)
cckd->cdevhdr[sfx].cdh_nullfmt = 0;
if (cckd->cdevhdr[sfx].cdh_nullfmt == 0 && dev->oslinux && dev->devtype == 0x3390)
cckd->cdevhdr[sfx].cdh_nullfmt = CKD_NULLTRK_FMT2;
if (cckd->cdevhdr[sfx].cdh_nullfmt == CKD_NULLTRK_FMT2)
dev->oslinux = 1;
return 0;
} /* end function cckd64_read_chdr */
/*-------------------------------------------------------------------*/
/* Write compressed dasd header */
/*-------------------------------------------------------------------*/
int cckd64_write_chdr (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
if (!dev->cckd64)
return cckd_write_chdr( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "file[%d] write_chdr", sfx);
/* Set version.release.modlvl */
cckd->cdevhdr[sfx].cdh_vrm[0] = CCKD_VERSION;
cckd->cdevhdr[sfx].cdh_vrm[1] = CCKD_RELEASE;
cckd->cdevhdr[sfx].cdh_vrm[2] = CCKD_MODLVL;
if (cckd64_write (dev, sfx, CCKD64_DEVHDR_POS, &cckd->cdevhdr[sfx], CCKD64_DEVHDR_SIZE) < 0)
return -1;
return 0;
} /* end function cckd64_write_chdr */
/*-------------------------------------------------------------------*/
/* Read the level 1 table */
/*-------------------------------------------------------------------*/
int cckd64_read_l1 (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
int len; /* Length of level 1 table */
int i; /* Work integer */
if (!dev->cckd64)
return cckd_read_l1( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "file[%d] read_l1 offset 0x%"PRIx64,
sfx, (U64)CCKD64_L1TAB_POS);
/* Free the old level 1 table if it exists */
cckd->L1tab[sfx] = cckd_free (dev, "l1", cckd->L1tab[sfx]);
/* Allocate the level 1 table */
len = cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE;
if ((cckd->L1tab[sfx] = cckd_malloc (dev, "l1", len)) == NULL)
return -1;
if ( sfx )
memset(cckd->L1tab[sfx], 0xFF, len);
else
memset(cckd->L1tab[sfx], 0, len);
/* Read the level 1 table */
if (cckd64_read (dev, sfx, CCKD64_L1TAB_POS, cckd->L1tab[sfx], len) < 0)
return -1;
/* Fix endianness */
if (cckd->swapend[sfx])
cckd64_swapend_l1 (cckd->L1tab[sfx], cckd->cdevhdr[sfx].num_L1tab);
/* Determine bounds */
cckd->L2_bounds = CCKD64_L1TAB_POS + len;
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
if (cckd->L1tab[sfx][i] != CCKD64_NOSIZE && cckd->L1tab[sfx][i] != CCKD64_MAXSIZE)
cckd->L2_bounds += CCKD64_L2TAB_SIZE;
/* Check if all l2 tables are within bounds */
cckd->L2ok = 1;
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab && cckd->L2ok; i++)
if (cckd->L1tab[sfx][i] != CCKD64_NOSIZE && cckd->L1tab[sfx][i] != CCKD64_MAXSIZE)
if (cckd->L1tab[sfx][i] > cckd->L2_bounds - CCKD64_L2TAB_SIZE)
cckd->L2ok = 0;
return 0;
} /* end function cckd64_read_l1 */
/*-------------------------------------------------------------------*/
/* Write the level 1 table */
/*-------------------------------------------------------------------*/
int cckd64_write_l1 (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
int len; /* Length of level 1 table */
if (!dev->cckd64)
return cckd_write_l1( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
len = cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE;
CCKD_TRACE( "file[%d] write_l1 0x%"PRIx64" len %d",
sfx, (U64)CCKD64_L1TAB_POS, len);
if (cckd64_write (dev, sfx, CCKD64_L1TAB_POS, cckd->L1tab[sfx], len) < 0)
return -1;
return 0;
} /* end function cckd64_write_l1 */
/*-------------------------------------------------------------------*/
/* Update a level 1 table entry */
/*-------------------------------------------------------------------*/
int cckd64_write_l1ent (DEVBLK *dev, int L1idx)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* File index */
U64 off; /* Offset to l1 entry */
if (!dev->cckd64)
return cckd_write_l1ent( dev, L1idx );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
off = (CCKD64_L1TAB_POS + L1idx * CCKD64_L1ENT_SIZE);
CCKD_TRACE( "file[%d] write_l1ent[%d] , 0x%16.16"PRIx64,
sfx, L1idx, off);
if (cckd64_write (dev, sfx, off, &cckd->L1tab[sfx][L1idx], CCKD64_L1ENT_SIZE) < 0)
return -1;
return 0;
} /* end function cckd64_write_l1ent */
/*-------------------------------------------------------------------*/
/* Initial read */
/*-------------------------------------------------------------------*/
int cckd64_read_init (DEVBLK *dev)
{
CCKD64_EXT* cckd; /* -> cckd extension */
int sfx; /* File index */
CKD_DEVHDR devhdr; /* Device header */
U32 imgtyp; /* Dasd device image type */
if (!dev->cckd64)
return cckd_read_init( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "file[%d] read_init", sfx );
/* Read the device header */
if (cckd64_read( dev, sfx, 0, &devhdr, CKD_DEVHDR_SIZE ) < 0)
return -1;
/* Check the device hdr */
imgtyp = dh_devid_typ( devhdr.dh_devid );
if (!sfx && (imgtyp & CKD_C064_TYP)) cckd->ckddasd = 1;
else if (!sfx && (imgtyp & FBA_C064_TYP)) cckd->fbadasd = 1;
else if (1
&& !(sfx && (imgtyp & CKD64_SF_TYP) && cckd->ckddasd)
&& !(sfx && (imgtyp & FBA64_SF_TYP) && cckd->fbadasd)
)
{
// "%1d:%04X CCKD file[%d] %s: device header id error"
WRMSG( HHC00305, "E", LCSS_DEVNUM,
sfx, cckd_sf_name( dev, sfx ));
return -1;
}
/* Read the compressed header */
if (cckd64_read_chdr( dev ) < 0)
return -1;
/* Read the level 1 table */
if (cckd64_read_l1( dev ) < 0)
return -1;
return 0;
} /* end function cckd64_read_init */
/*-------------------------------------------------------------------*/
/* Read free space */
/*-------------------------------------------------------------------*/
int cckd64_read_fsp( DEVBLK* dev )
{
CCKD64_EXT *cckd; /* -> cckd extension */
U64 fpos; /* Free space offset */
int sfx; /* File index */
int i; /* Index */
CCKD64_FREEBLK freeblk; /* First freeblk read */
if (!dev->cckd64)
return cckd_read_fsp( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
CCKD_TRACE( "file[%d] read_fsp number %"PRId64,
sfx, cckd->cdevhdr[sfx].free_num );
cckd->ifb = cckd_free( dev, "ifb", cckd->ifb );
cckd->free_count = 0;
cckd->free_idx1st = -1;
cckd->free_idxlast = -1;
cckd->free_idxavail = -1;
/* Get storage for the internal free space chain
* in a multiple of 1024 entries
*/
i = (int) ROUND_UP( cckd->cdevhdr[sfx].free_num, CCKD_IFB_ENTS_INCR );
if (!(cckd->ifb = cckd_calloc( dev, "ifb", i, CCKD64_IFREEBLK_SIZE )))
return -1;
cckd->free_count = i;
/* Build the doubly linked internal free space chain */
if (cckd->cdevhdr[sfx].free_num)
{
cckd->free_idx1st = 0;
/* Read the first freeblk to determine old/new format */
fpos = cckd->cdevhdr[sfx].free_off;
if (cckd64_read (dev, sfx, fpos, &freeblk, CCKD64_FREEBLK_SIZE) < 0)
return -1;
if (memcmp(&freeblk, "FREE_BLK", 8) == 0)
{
/* new format free space */
CCKD64_FREEBLK *fsp;
U64 ofree = cckd->cdevhdr[sfx].free_off;
S64 n = cckd->cdevhdr[sfx].free_num * CCKD64_FREEBLK_SIZE;
if (!(fsp = cckd_malloc( dev, "fsp", (size_t) n )))
return -1;
fpos += CCKD64_FREEBLK_SIZE;
if (cckd64_read (dev, sfx, fpos, fsp, (unsigned int) n) < 0)
return -1;
for (i = 0; i < cckd->cdevhdr[sfx].free_num; i++)
{
if (i == 0)
cckd->cdevhdr[sfx].free_off = fsp[i].fb_offnxt;
else
cckd->ifb[i-1].ifb_offnxt = fsp[i].fb_offnxt;
cckd->ifb[i].ifb_offnxt = 0;
cckd->ifb[i].ifb_len = fsp[i].fb_len;
cckd->ifb[i].ifb_idxprv = i - 1;
cckd->ifb[i].ifb_idxnxt = i + 1;
}
cckd->ifb[i-1].ifb_idxnxt = -1;
cckd->free_idxlast = i-1;
fsp = cckd_free (dev, "fsp", fsp);
/* truncate if new format free space was at the end */
if (ofree == cckd->cdevhdr[sfx].cdh_size)
{
fpos = cckd->cdevhdr[sfx].cdh_size;
cckd64_ftruncate(dev, sfx, fpos);
}
} /* new format free space */
else
{
/* old format free space */
fpos = cckd->cdevhdr[sfx].free_off;
for (i = 0; i < cckd->cdevhdr[sfx].free_num; i++)
{
if (cckd64_read (dev, sfx, fpos, &cckd->ifb[i], CCKD64_FREEBLK_SIZE) < 0)
return -1;
cckd->ifb[i].ifb_idxprv = i - 1;
cckd->ifb[i].ifb_idxnxt = i + 1;
fpos = cckd->ifb[i].ifb_offnxt;
}
cckd->ifb[i-1].ifb_idxnxt = -1;
cckd->free_idxlast = i-1;
} /* old format free space */
} /* if (cckd->cdevhdr[sfx].free_num) */
/* Build singly linked chain of available free space entries */
if (cckd->cdevhdr[sfx].free_num < cckd->free_count)
{
cckd->free_idxavail = (int) cckd->cdevhdr[sfx].free_num;
for (i = cckd->free_idxavail; i < cckd->free_count; i++)
cckd->ifb[i].ifb_idxnxt = i + 1;
cckd->ifb[i-1].ifb_idxnxt = -1;
}
/* Set minimum free space size */
cckd->free_minsize = CCKD_MIN_FREESIZE( cckd->free_count );
return 0;
} /* end function cckd64_read_fsp */
/*-------------------------------------------------------------------*/
/* Write the free space */
/*-------------------------------------------------------------------*/
int cckd64_write_fsp (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
U64 fpos; /* Free space offset */
U64 ppos; /* Previous free space offset*/
int sfx; /* File index */
S64 i, j, n; /* Work variables */
int rc; /* Return code */
CCKD64_FREEBLK *fsp = NULL; /* -> new format free space */
if (!dev->cckd64)
return cckd_write_fsp( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
if (!cckd->ifb)
return 0;
CCKD_TRACE( "file[%d] write_fsp number %"PRId64,
sfx, cckd->cdevhdr[sfx].free_num);
/* get rid of pending free space */
for (i = 0; i < CCKD_MAX_FREEPEND; i++)
cckd64_flush_space(dev);
/* sanity checks */
if (cckd->cdevhdr[sfx].free_num == 0 || cckd->cdevhdr[sfx].free_off == 0)
{
cckd->cdevhdr[sfx].free_num = cckd->cdevhdr[sfx].free_off = 0;
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
}
/* Write any free spaces */
if (cckd->cdevhdr[sfx].free_off)
{
/* size needed for new format free space */
n = (cckd->cdevhdr[sfx].free_num+1) * CCKD64_FREEBLK_SIZE;
/* look for existing free space to fit new format free space */
fpos = 0;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
if ((U64)n <= cckd->ifb[i].ifb_len)
break;
if (i >= 0)
fpos = cckd->ifb[i].ifb_idxprv < 0
? cckd->cdevhdr[sfx].free_off
: cckd->ifb[cckd->ifb[i].ifb_idxprv].ifb_offnxt;
/* if no applicable space see if we can append to the file */
if (fpos == 0 && cckd->cckd_maxsize - cckd->cdevhdr[sfx].cdh_size >= (U64)n)
fpos = cckd->cdevhdr[sfx].cdh_size;
if (fpos && !(fsp = cckd_malloc( dev, "fsp", (size_t) n )))
fpos = 0;
if (fpos)
{
/* Zero entire block to avoid writing garbage to underlying file */
memset (&fsp[0], 0, CCKD64_FREEBLK_SIZE);
/* New format free space */
memcpy (&fsp[0], "FREE_BLK", 8);
ppos = cckd->cdevhdr[sfx].free_off;
for (i = cckd->free_idx1st, j = 1; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
fsp[j].fb_offnxt = ppos;
fsp[j++].fb_len = cckd->ifb[i].ifb_len;
ppos = cckd->ifb[i].ifb_offnxt;
}
rc = cckd64_write (dev, sfx, fpos, fsp, (unsigned int)n);
fsp = cckd_free (dev, "fsp", fsp);
if (rc < 0)
return -1;
cckd->cdevhdr[sfx].free_off = fpos;
} /* new format free space */
else
{
/* Old format free space */
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
if (cckd64_write (dev, sfx, fpos, &cckd->ifb[i], CCKD64_FREEBLK_SIZE) < 0)
return -1;
fpos = cckd->ifb[i].ifb_offnxt;
}
} /* old format free space */
} /* if (cckd->cdevhdr[sfx].free_off) */
/* Free the free space array */
cckd->ifb = cckd_free (dev, "ifb", cckd->ifb);
cckd->free_count = 0;
cckd->free_idx1st = cckd->free_idxlast = cckd->free_idxavail = -1;
return 0;
} /* end function cckd64_write_fsp */
/*-------------------------------------------------------------------*/
/* Read a new level 2 table */
/*-------------------------------------------------------------------*/
int cckd64_read_l2 (DEVBLK *dev, int sfx, int L1idx)
{
CCKD64_EXT *cckd; /* -> cckd extension */
U64 off; /* L2 file offset */
int fnd; /* Found cache */
int lru; /* Oldest available cache */
CCKD64_L2ENT *buf; /* -> Cache buffer */
int i; /* Loop index */
BYTE nullfmt; /* Null track format */
if (!dev->cckd64)
return cckd_read_l2( dev, sfx, L1idx );
cckd = dev->cckd_ext;
nullfmt = cckd->cdevhdr[cckd->sfn].cdh_nullfmt;
CCKD_TRACE( "file[%d] read_l2 %d active %d %d %d",
sfx, L1idx, cckd->sfx, cckd->L1idx, cckd->L2_active);
/* Return if table is already active */
if (sfx == cckd->sfx && L1idx == cckd->L1idx) return 0;
cache_lock(CACHE_L2);
/* Inactivate the previous entry */
if (cckd->L2_active >= 0)
cache_setflag(CACHE_L2, cckd->L2_active, ~L2_CACHE_ACTIVE, 0);
cckd->L2tab = NULL;
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
/* scan the cache array for the l2tab */
fnd = cache_lookup (CACHE_L2, L2_CACHE_SETKEY(sfx, dev->devnum, L1idx), &lru);
/* check for level 2 cache hit */
if (fnd >= 0)
{
CCKD_TRACE( "l2[%d,%d] cache[%d] hit", sfx, L1idx, fnd);
cache_setflag (CACHE_L2, fnd, 0, L2_CACHE_ACTIVE);
cache_setage (CACHE_L2, fnd);
cckdblk.stats_l2cachehits++;
cache_unlock (CACHE_L2);
cckd->sfx = sfx;
cckd->L1idx = L1idx;
cckd->L2tab = cache_getbuf(CACHE_L2, fnd, 0);
cckd->L2_active = fnd;
return 1;
}
CCKD_TRACE( "l2[%d,%d] cache[%d] miss", sfx, L1idx, lru);
/* Steal an entry if all are busy */
if (lru < 0) lru = cckd64_steal_l2();
/* Make the entry active */
cache_setkey (CACHE_L2, lru, L2_CACHE_SETKEY(sfx, dev->devnum, L1idx));
cache_setflag (CACHE_L2, lru, 0, L2_CACHE_ACTIVE);
cache_setage (CACHE_L2, lru);
buf = cache_getbuf(CACHE_L2, lru, CCKD64_L2TAB_SIZE);
cckdblk.stats_l2cachemisses++;
cache_unlock (CACHE_L2);
if (buf == NULL) return -1;
/* Check for null table */
if (cckd->L1tab[sfx][L1idx] == 0)
{
memset(buf, 0, CCKD64_L2TAB_SIZE);
if (nullfmt)
for (i = 0; i < 256; i++)
buf[i].L2_len = buf[i].L2_size = nullfmt;
CCKD_TRACE( "l2[%d,%d] cache[%d] null fmt[%d]", sfx, L1idx, lru, nullfmt);
}
else if (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
{
memset(buf, 0xff, CCKD64_L2TAB_SIZE);
CCKD_TRACE( "l2[%d,%d] cache[%d] null 0xff", sfx, L1idx, lru);
}
/* Read the new level 2 table */
else
{
off = cckd->L1tab[sfx][L1idx];
if (cckd64_read (dev, sfx, off, buf, CCKD64_L2TAB_SIZE) < 0)
{
cache_lock(CACHE_L2);
cache_setflag(CACHE_L2, lru, 0, 0);
cache_unlock(CACHE_L2);
return -1;
}
if (cckd->swapend[sfx])
cckd64_swapend_l2 (buf);
CCKD_TRACE( "file[%d] cache[%d] l2[%d] read offset 0x%16.16"PRIx64,
sfx, lru, L1idx, cckd->L1tab[sfx][L1idx]);
cckd->L2_reads[sfx]++;
cckd->totl2reads++;
cckdblk.stats_l2reads++;
}
cckd->sfx = sfx;
cckd->L1idx = L1idx;
cckd->L2tab = buf;
cckd->L2_active = lru;
return 0;
} /* end function cckd64_read_l2 */
/*-------------------------------------------------------------------*/
/* Purge all l2tab cache entries for a given device */
/*-------------------------------------------------------------------*/
void cckd64_purge_l2 (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
if (!dev->cckd64)
{
cckd_purge_l2( dev );
return;
}
cckd = dev->cckd_ext;
CCKD_TRACE( "purge_l2%s", "");
cache_lock (CACHE_L2);
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
cckd->L2tab = NULL;
cache_scan (CACHE_L2, cckd64_purge_l2_scan, dev);
cache_unlock (CACHE_L2);
}
int cckd64_purge_l2_scan (int *answer, int ix, int i, void *data)
{
U16 sfx; /* Cached suffix */
U16 devnum; /* Cached device number */
U32 L1idx; /* Cached level 1 index */
DEVBLK *dev = data; /* -> device block */
UNREFERENCED(answer);
L2_CACHE_GETKEY(i, sfx, devnum, L1idx);
if (dev == NULL || devnum == dev->devnum)
{
CCKD_TRACE( "purge l2cache[%d] %4.4X sfx %hd ix %d purged",
i, devnum, sfx, L1idx);
cache_release(ix, i, 0);
}
return 0;
}
/*-------------------------------------------------------------------*/
/* Steal an l2tab cache entry */
/*-------------------------------------------------------------------*/
int cckd64_steal_l2 ()
{
DEVBLK *dev; /* -> device block */
CCKD64_EXT *cckd; /* -> cckd extension */
int i; /* Stolen cache index */
U16 sfx; /* Cached suffix */
U16 devnum; /* Cached device number */
U32 L1idx; /* Cached level 1 index */
i = cache_scan (CACHE_L2, cckd_steal_l2_scan, NULL);
L2_CACHE_GETKEY(i, sfx, devnum, L1idx);
dev = cckd_find_device_by_devnum(devnum);
if (!dev->cckd64)
return cckd_steal_l2();
cckd = dev->cckd_ext;
cckd->L2_active = cckd->sfx = cckd->L1idx = -1;
cckd->L2tab = NULL;
cache_release(CACHE_L2, i, 0);
return i;
}
/*-------------------------------------------------------------------*/
/* Write the current level 2 table */
/*-------------------------------------------------------------------*/
int cckd64_write_l2 (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx,L1idx; /* Lookup table indices */
U64 off, old_off; /* New/old L2 file offsets */
int size = CCKD64_L2TAB_SIZE; /* L2 table size */
int fix; /* Null format type */
if (!dev->cckd64)
return cckd_write_l2( dev );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
L1idx = cckd->L1idx;
fix = cckd->cdevhdr[sfx].cdh_nullfmt;
cckd->L2ok = 0;
CCKD_TRACE( "file[%d] write_l2 %d", sfx, L1idx);
if (sfx < 0 || L1idx < 0) return -1;
old_off = cckd->L1tab[sfx][L1idx];
if (cckd->L1tab[sfx][L1idx] == CCKD64_NOSIZE || cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
cckd->L2_bounds += CCKD64_L2TAB_SIZE;
/* Write the L2 table if it's not empty */
if (memcmp( cckd->L2tab, &empty64_l2[fix], CCKD64_L2TAB_SIZE ))
{
if ((S64)(off = cckd64_get_space( dev, &size, CCKD_L2SPACE )) < 0)
return -1;
if (cckd64_write( dev, sfx, off, cckd->L2tab, CCKD64_L2TAB_SIZE ) < 0)
return -1;
}
else
{
off = 0;
cckd->L2_bounds -= CCKD64_L2TAB_SIZE;
}
/* Free the old L2 space */
cckd64_rel_space( dev, old_off, CCKD64_L2TAB_SIZE, CCKD64_L2TAB_SIZE );
/* Update level 1 table */
cckd->L1tab[sfx][L1idx] = off;
if (cckd64_write_l1ent( dev, L1idx ) < 0)
return -1;
return 0;
} /* end function cckd64_write_l2 */
/*-------------------------------------------------------------------*/
/* Return a level 2 entry */
/*-------------------------------------------------------------------*/
int cckd64_read_l2ent (DEVBLK *dev, CCKD64_L2ENT *l2, int trk)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx,L1idx,l2x; /* Lookup table indices */
if (!dev->cckd64)
return cckd_read_l2ent( dev, (CCKD_L2ENT*) l2, trk );
cckd = dev->cckd_ext;
L1idx = trk >> 8;
l2x = trk & 0xff;
if (l2 != NULL) l2->L2_trkoff = l2->L2_len = l2->L2_size = l2->L2_pad = 0;
for (sfx = cckd->sfn; sfx >= 0; sfx--)
{
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%"PRIx64,
sfx, L1idx, l2x, trk, cckd->L1tab[sfx][L1idx]);
/* Continue if l2 table not in this file */
if (cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
continue;
/* Read l2 table from this file */
if (cckd64_read_l2 (dev, sfx, L1idx) < 0)
return -1;
/* Exit loop if track is in this file */
if (cckd->L2tab[l2x].L2_trkoff != CCKD64_MAXSIZE)
break;
}
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] read_l2ent 0x%"PRIx64" %hd %hd",
sfx, L1idx, l2x, trk, (U64)(sfx >= 0 ? cckd->L2tab[l2x].L2_trkoff : 0),
(U16)(sfx >= 0 ? cckd->L2tab[l2x].L2_len : 0),
(U16)(sfx >= 0 ? cckd->L2tab[l2x].L2_size : 0));
if (l2 != NULL && sfx >= 0)
{
l2->L2_trkoff = cckd->L2tab[l2x].L2_trkoff;
l2->L2_len = cckd->L2tab[l2x].L2_len;
l2->L2_size = cckd->L2tab[l2x].L2_size;
}
return sfx;
} /* end function cckd64_read_l2ent */
/*-------------------------------------------------------------------*/
/* Update a level 2 entry */
/*-------------------------------------------------------------------*/
int cckd64_write_l2ent (DEVBLK *dev, CCKD64_L2ENT *l2, int trk)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx,L1idx,l2x; /* Lookup table indices */
U64 off; /* L2 entry offset */
if (!dev->cckd64)
return cckd_write_l2ent( dev, (CCKD_L2ENT*) l2, trk );
cckd = dev->cckd_ext;
/* Error return if no available level 2 table */
if (!cckd->L2tab) return -1;
sfx = cckd->sfn;
L1idx = trk >> 8;
l2x = trk & 0xff;
/* Copy the new entry if passed */
if (l2) memcpy (&cckd->L2tab[l2x], l2, CCKD64_L2ENT_SIZE);
CCKD_TRACE( "file[%d] l2[%d,%d] trk[%d] write_l2ent 0x%"PRIx64" %hd %hd",
sfx, L1idx, l2x, trk,
cckd->L2tab[l2x].L2_trkoff, cckd->L2tab[l2x].L2_len, cckd->L2tab[l2x].L2_size);
/* If no level 2 table for this file, then write a new one */
if (cckd->L1tab[sfx][L1idx] == CCKD64_NOSIZE || cckd->L1tab[sfx][L1idx] == CCKD64_MAXSIZE)
return cckd64_write_l2 (dev);
/* Write the level 2 table entry */
off = (cckd->L1tab[sfx][L1idx] + l2x * CCKD64_L2ENT_SIZE);
if (cckd64_write (dev, sfx, off, &cckd->L2tab[l2x], CCKD64_L2ENT_SIZE) < 0)
return -1;
return 0;
} /* end function cckd64_write_l2ent */
/*-------------------------------------------------------------------*/
/* Read a track image */
/*-------------------------------------------------------------------*/
int cckd64_read_trkimg (DEVBLK *dev, BYTE *buf, int trk, BYTE *unitstat)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int sfx; /* File index */
CCKD64_L2ENT l2; /* Level 2 entry */
cckd = dev->cckd_ext;
CCKD_TRACE( "trk[%d] read_trkimg", trk);
/* Read level 2 entry for the track */
if ((sfx = cckd64_read_l2ent (dev, &l2, trk)) < 0)
goto cckd_read_trkimg_error;
/* Read the track image or build a null track image */
if (l2.L2_trkoff != 0)
{
rc = cckd64_read (dev, sfx, l2.L2_trkoff, buf, l2.L2_len);
if (rc < 0)
goto cckd_read_trkimg_error;
cckd->reads[sfx]++;
cckd->totreads++;
cckdblk.stats_reads++;
cckdblk.stats_readbytes += rc;
if (cckd->notnull == 0 && trk > 1) cckd->notnull = 1;
}
else
rc = cckd64_null_trk (dev, buf, trk, l2.L2_len);
/* Validate the track image */
if (cckd64_cchh (dev, buf, trk) < 0)
goto cckd_read_trkimg_error;
return rc;
cckd_read_trkimg_error:
if (unitstat)
{
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
}
return cckd64_null_trk (dev, buf, trk, 0);
} /* end function cckd64_read_trkimg */
/*-------------------------------------------------------------------*/
/* Write a track image */
/*-------------------------------------------------------------------*/
int cckd64_write_trkimg (DEVBLK *dev, BYTE *buf, int len, int trk, int flags)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
U64 off; /* File offset */
CCKD64_L2ENT l2, oldl2; /* Level 2 entries */
int sfx,L1idx,l2x; /* Lookup table indices */
int after = 0; /* 1=New track after old */
int size; /* Size of new track */
if (!dev->cckd64)
return cckd_write_trkimg( dev, buf, len, trk, flags );
cckd = dev->cckd_ext;
sfx = cckd->sfn;
L1idx = trk >> 8;
l2x = trk & 0xff;
CCKD_TRACE( "file[%d] trk[%d] write_trkimg len %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x",
sfx, trk, len, buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
/* Validate the new track image */
if (cckd64_cchh (dev, buf, trk) < 0)
return -1;
/* Get the level 2 table for the track in the active file */
if (cckd64_read_l2 (dev, sfx, L1idx) < 0)
return -1;
/* Save the level 2 entry for the track */
oldl2.L2_trkoff = cckd->L2tab[l2x].L2_trkoff;
oldl2.L2_len = cckd->L2tab[l2x].L2_len;
oldl2.L2_size = cckd->L2tab[l2x].L2_size;
CCKD_TRACE( "file[%d] trk[%d] write_trkimg oldl2 0x%"PRIx64" %hd %hd",
sfx, trk, oldl2.L2_trkoff, oldl2.L2_len, oldl2.L2_size);
/* Check if writing a null track */
len = cckd64_check_null_trk(dev, buf, trk, len);
if (len > CKD_NULLTRK_FMTMAX)
{
/* Get space for the track image */
size = len;
if ((S64)(off = cckd64_get_space (dev, &size, flags)) < 0)
return -1;
l2.L2_trkoff = off;
l2.L2_len = (U16)len;
l2.L2_size = (U16)size;
l2.L2_pad = 0;
if (1
&& oldl2.L2_trkoff != CCKD64_NOSIZE
&& oldl2.L2_trkoff != CCKD64_MAXSIZE
&& oldl2.L2_trkoff < l2.L2_trkoff
)
after = 1;
/* Write the track image */
if ((rc = cckd64_write (dev, sfx, off, buf, len)) < 0)
return -1;
cckd->writes[sfx]++;
cckd->totwrites++;
cckdblk.stats_writes++;
cckdblk.stats_writebytes += rc;
}
else
{
l2.L2_trkoff = 0;
l2.L2_len = l2.L2_size = (U16)len;
l2.L2_pad = 0;
}
/* Update the level 2 entry */
if (cckd64_write_l2ent (dev, &l2, trk) < 0)
return -1;
/* Release the previous space */
cckd64_rel_space (dev, oldl2.L2_trkoff, (int)oldl2.L2_len, (int)oldl2.L2_size);
/* `after' is 1 if the new offset is after the old offset */
return after;
} /* end function cckd64_write_trkimg */
/*-------------------------------------------------------------------*/
/* Harden the file */
/*-------------------------------------------------------------------*/
int cckd64_harden(DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc=0; /* Return code */
if (!dev->cckd64)
return cckd_harden( dev );
cckd = dev->cckd_ext;
if ((dev->ckdrdonly && cckd->sfn == 0)
|| cckd->open[cckd->sfn] != CCKD_OPEN_RW)
return 0;
CCKD_TRACE( "file[%d] harden", cckd->sfn);
/* Write the compressed device header */
if (cckd64_write_chdr (dev) < 0)
rc = -1;
/* Write the level 1 table */
if (cckd64_write_l1 (dev) < 0)
rc = -1;
/* Write the free space chain */
if (cckd64_write_fsp (dev) < 0)
rc = -1;
/* Re-write the compressed device header */
cckd->cdevhdr[cckd->sfn].cdh_opts &= ~CCKD_OPT_OPENED;
if (cckd64_write_chdr (dev) < 0)
rc = -1;
if (cckdblk.fsync)
{
fdatasync (cckd->fd[cckd->sfn]);
cckdblk.stats_fsyncs++;
}
cckdblk.stats_hardens++;
return rc;
} /* cckd64_harden */
/*-------------------------------------------------------------------*/
/* Build a null track */
/*-------------------------------------------------------------------*/
int cckd64_null_trk(DEVBLK *dev, BYTE *buf, int trk, int nullfmt)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int i; /* Loop counter */
CKD_TRKHDR *trkhdr; /* -> Track header */
CKD_RECHDR *rechdr; /* -> Record header */
U32 cyl; /* Cylinder number */
U32 head; /* Head number */
BYTE r; /* Record number */
BYTE *pos; /* -> Next position in buffer*/
int len; /* Length of null track */
if (!dev->cckd64)
return cckd_null_trk( dev, buf, trk, nullfmt );
cckd = dev->cckd_ext;
if (nullfmt < 0 || nullfmt > CKD_NULLTRK_FMTMAX)
nullfmt = cckd->cdevhdr[cckd->sfn].cdh_nullfmt;
// FIXME
// Compatibility check for nullfmt bug and linux -- 18 May 2005
// Remove at some reasonable date in the future
else if (nullfmt == 0
&& cckd->cdevhdr[cckd->sfn].cdh_nullfmt == CKD_NULLTRK_FMT2)
nullfmt = CKD_NULLTRK_FMT2;
if (cckd->ckddasd)
{
/* cylinder and head calculations */
cyl = trk / dev->ckdheads;
head = trk % dev->ckdheads;
/* Build the track header */
trkhdr = (CKD_TRKHDR*)buf;
trkhdr->bin = 0;
store_hw( &trkhdr->cyl, (U16) cyl );
store_hw( &trkhdr->head, (U16) head );
pos = buf + CKD_TRKHDR_SIZE;
/* Build record zero (R0): KL=0, DL=8 */
r = 0;
rechdr = (CKD_RECHDR*)pos;
pos += CKD_RECHDR_SIZE;
store_hw( &rechdr->cyl, (U16) cyl );
store_hw( &rechdr->head, (U16) head );
rechdr->rec = r;
rechdr->klen = 0;
store_hw( &rechdr->dlen, CKD_R0_DLEN );
memset( pos, 0, CKD_R0_DLEN );
pos += CKD_R0_DLEN;
r++;
/* Specific null track formatting */
if (nullfmt == CKD_NULLTRK_FMT0)
{
rechdr = (CKD_RECHDR*)pos;
pos += CKD_RECHDR_SIZE;
/* Build record one (R1): EOF record (KL=0, DL=0) */
store_hw( &rechdr->cyl, (U16) cyl );
store_hw( &rechdr->head, (U16) head );
rechdr->rec = r;
rechdr->klen = 0;
store_hw(&rechdr->dlen, 0);
r++;
}
else if (nullfmt == CKD_NULLTRK_FMT2)
{
/* Build 12 binary-0 data blocks, 4K each (KL=0, DL=4K) */
for (i = 0; i < 12; i++)
{
rechdr = (CKD_RECHDR*)pos;
pos += CKD_RECHDR_SIZE;
store_hw( &rechdr->cyl, (U16) cyl);
store_hw( &rechdr->head, (U16) head);
rechdr->rec = r;
rechdr->klen = 0;
store_hw( &rechdr->dlen, CKD_NULL_FMT2_DLEN );
r++;
memset( pos, 0, CKD_NULL_FMT2_DLEN );
pos += CKD_NULL_FMT2_DLEN;
}
}
/* Build the end of track marker */
memcpy( pos, &CKD_ENDTRK, CKD_ENDTRK_SIZE );
pos += CKD_ENDTRK_SIZE;
len = (int)(pos - buf);
}
else
{
len = CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE;
memset( buf, 0, len );
store_fw( buf+1, trk );
}
CCKD_TRACE( "null_trk %s %d format %d size %d",
cckd->ckddasd ? "trk" : "blkgrp", trk, nullfmt, len);
return len;
} /* end function cckd64_null_trk */
/*-------------------------------------------------------------------*/
/* Return a number 0 .. CKD_NULLTRK_FMTMAX if track is null */
/* else return the original length */
/*-------------------------------------------------------------------*/
int cckd64_check_null_trk (DEVBLK *dev, BYTE *buf, int trk, int len)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
BYTE buf2[65536]; /* Null track buffer */
if (!dev->cckd64)
return cckd_check_null_trk( dev, buf, trk, len );
cckd = dev->cckd_ext;
rc = len;
if (len == CKD_NULLTRK_SIZE0)
rc = CKD_NULLTRK_FMT0;
else if (len == CKD_NULLTRK_SIZE1)
rc = CKD_NULLTRK_FMT1;
else if (len == CKD_NULLTRK_SIZE2 && dev->oslinux
&& (!cckd->notnull || cckdblk.linuxnull))
{
cckd64_null_trk (dev, buf2, trk, 0);
if (memcmp(buf, buf2, len) == 0)
rc = CKD_NULLTRK_FMT2;
}
return rc;
}
/*-------------------------------------------------------------------*/
/* Verify a track/block header and return track/block number */
/*-------------------------------------------------------------------*/
int cckd64_cchh( DEVBLK* dev, BYTE* buf, int trk )
{
CCKD64_EXT* cckd; /* -> cckd extension */
U16 cyl; /* Cylinder */
U16 head; /* Head */
int t; /* Calculated track */
BYTE badcomp=0; /* 1=Unsupported compression */
cckd = dev->cckd_ext;
/* CKD dasd header verification */
if (cckd->ckddasd)
{
cyl = fetch_hw( buf + 1 );
head = fetch_hw( buf + 3 );
t = cyl * dev->ckdheads + head;
if (1
&& cyl < dev->ckdcyls
&& head < dev->ckdheads
&& (trk == -1 || t == trk)
)
{
if (buf[0] & ~cckdblk.comps)
{
if (buf[0] & ~CCKD_COMPRESS_MASK)
{
if (cckdblk.bytemsgs++ < 10)
// "%1d:%04X CCKD file[%d] %s: invalid byte 0 trk %d, buf %2.2x%2.2x%2.2x%2.2x%2.2x"
WRMSG( HHC00307, "E", LCSS_DEVNUM, cckd->sfn,
cckd_sf_name( dev, cckd->sfn ), t, buf[0],buf[1],buf[2],buf[3],buf[4]);
buf[0] &= CCKD_COMPRESS_MASK;
}
}
if (buf[0] & ~cckdblk.comps)
badcomp = 1;
else
return t;
}
}
/* FBA dasd header verification */
else
{
t = fetch_fw (buf + 1);
if (t < dev->fbanumblk && (trk == -1 || t == trk))
{
if (buf[0] & ~cckdblk.comps)
{
if (buf[0] & ~CCKD_COMPRESS_MASK)
{
// "%1d:%04X CCKD file[%d] %s: invalid byte 0 blkgrp %d, buf %2.2x%2.2x%2.2x%2.2x%2.2x"
WRMSG( HHC00308, "E", LCSS_DEVNUM, cckd->sfn,
cckd_sf_name( dev, cckd->sfn ), t, buf[0],buf[1],buf[2],buf[3],buf[4]);
buf[0] &= CCKD_COMPRESS_MASK;
}
}
if (buf[0] & ~cckdblk.comps)
badcomp = 1;
else
return t;
}
}
if (badcomp)
{
// "%1d:%04X CCKD file[%d] %s: invalid %s hdr %s %d: %s compression unsupported"
WRMSG( HHC00309, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
cckd->ckddasd ? "trk" : "blk",
cckd->ckddasd ? "trk" : "blk", t, compname[buf[0]]);
}
else
{
// "%1d:%04X CCKD file[%d] %s: invalid %s hdr %s %d buf %p:%2.2x%2.2x%2.2x%2.2x%2.2x"
WRMSG( HHC00310, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ),
cckd->ckddasd ? "trk" : "blk",
cckd->ckddasd ? "trk" : "blk", trk,
buf, buf[0], buf[1], buf[2], buf[3], buf[4]);
cckd_print_itrace();
}
return -1;
} /* end function cckd64_cchh */
/*-------------------------------------------------------------------*/
/* Validate a track image */
/*-------------------------------------------------------------------*/
int cckd64_validate (DEVBLK *dev, BYTE *buf, int trk, int len)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int r; /* Record number */
int sz; /* Track size */
int vlen; /* Validation length */
int kl, dl; /* Key/Data lengths */
CKD_RECHDR rn; /* Record-n (r0, r1 ... rn) */
cckd = dev->cckd_ext;
if (!buf || len < 0)
return -1;
CCKD_TRACE( "validating %s %d len %d %2.2x%2.2x%2.2x%2.2x%2.2x "
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
cckd->ckddasd ? "trk" : "blkgrp", trk, len,
buf[0], buf[ 1], buf[ 2], buf[ 3], buf[4], // trkhdr
buf[5], buf[ 6], buf[ 7], buf[ 8], // r0 (0-3)
buf[9], buf[10], buf[11], buf[12] ); // r0 (4-7)
/* FBA dasd check */
if (cckd->fbadasd)
{
if (!len || len == CKD_TRKHDR_SIZE + CFBA_BLKGRP_SIZE)
return len;
CCKD_TRACE( "validation failed: bad length%s", "" );
return -1;
}
/* validate record 0 */
memcpy( &rn, &buf[CKD_TRKHDR_SIZE], CKD_R0_SIZE );
rn.cyl[0] &= 0x7f; /* fix for ovflow */
if (0
|| rn.rec != 0
|| rn.klen != 0
|| fetch_hw( rn.dlen ) != CKD_R0_DLEN
)
{
CCKD_TRACE( "validation failed: bad r0%s", "" );
return -1;
}
/* validate records 1 thru n */
vlen = len > 0 ? len : dev->ckdtrksz;
for (r = 1, sz = CKD_TRKHDR_SIZE + (CKD_R0_SIZE + CKD_R0_DLEN);
sz + CKD_ENDTRK_SIZE <= vlen;
sz += CKD_RECHDR_SIZE + kl + dl, r++)
{
memcpy( &rn, &buf[sz], CKD_RECHDR_SIZE );
if (memcmp( &rn, &CKD_ENDTRK, CKD_ENDTRK_SIZE ) == 0)
break;
kl = rn.klen;
dl = fetch_hw( rn.dlen );
if (rn.rec == 0 || sz + CKD_RECHDR_SIZE + kl + dl >= vlen)
{
CCKD_TRACE( "validation failed: bad r%d "
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
r, buf[sz+0], buf[sz+1], buf[sz+2], buf[sz+3],
buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7] );
return -1;
}
}
sz += CKD_ENDTRK_SIZE;
if ((len > 0 && sz != len) || sz > vlen)
{
CCKD_TRACE( "validation failed: no eot%s", "" );
return -1;
}
return sz;
} /* end function cckd64_validate */
/*-------------------------------------------------------------------*/
/* Initialize shadow files */
/*-------------------------------------------------------------------*/
int cckd64_sf_init (DEVBLK *dev)
{
CKD_DEVHDR devhdr; /* Device header */
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int i; /* Index */
struct stat st; /* stat() buffer */
char pathname[MAX_PATH]; /* file path in host format */
if (!dev->cckd64)
return cckd_sf_init( dev );
cckd = dev->cckd_ext;
/* return if no shadow files */
if (dev->dasdsfn == NULL) return 0;
/* Check for shadow file name collision */
for (i = 1; i <= CCKD_MAX_SF && dev->dasdsfn != NULL; i++)
{
DEVBLK *dev2;
CCKD64_EXT *cckd2;
int j;
for (dev2 = cckdblk.dev1st; dev2; dev2 = cckd2->devnext)
{
cckd2 = dev2->cckd_ext;
if (dev2 == dev) continue;
for (j = 0; j <= CCKD_MAX_SF && dev2->dasdsfn != NULL; j++)
{
if (strcmp (cckd_sf_name(dev, i),cckd_sf_name(dev2, j)) == 0)
{
// "%1d:%04X CCKD file[%d] %s: shadow file name collides with %1d:%04X file[%d] %s"
WRMSG (HHC00311, "E", LCSS_DEVNUM, i, cckd_sf_name(dev, i),
SSID_TO_LCSS(dev2->ssid), dev2->devnum, j, cckd_sf_name(dev2, j));
return -1;
}
}
}
}
/* open all existing shadow files */
for (cckd->sfn = 1; cckd->sfn <= CCKD_MAX_SF; cckd->sfn++)
{
/* If no more shadow files remaining then we're done */
hostpath( pathname, cckd_sf_name( dev, cckd->sfn ), sizeof( pathname ));
if (stat( pathname, &st ) < 0)
break;
/* Try to open the shadow file read-write then read-only */
if ( cckd64_open( dev, cckd->sfn, O_RDWR | O_BINARY, 1 ) < 0)
if (cckd64_open( dev, cckd->sfn, O_RDONLY | O_BINARY, 0 ) < 0)
break;
/* Make sure shadow file type matches base image type */
if (cckd_read( dev, cckd->sfn, 0, &devhdr, CKD_DEVHDR_SIZE ) < 0)
return -1;
if (!is_dh_devid_typ( devhdr.dh_devid, ANY64_SF_TYP ))
{
// "%1d:%04X CCKD file[%d] %s: cckd/64 format differs from base"
WRMSG( HHC00351, "E", LCSS_DEVNUM, cckd->sfn, pathname );
return -1;
}
/* Call the chkdsk function */
if ((rc = cckd64_chkdsk( dev, 0 )) < 0)
return -1;
/* Perform initial read */
rc = cckd64_read_init( dev );
}
/* Backup to the last opened file number */
cckd->sfn--;
/* If the last file was opened read-only then create a new one */
if (cckd->open[cckd->sfn] == CCKD_OPEN_RO)
{
/* but ONLY IF not explicit batch utility READ-ONLY open */
if (!(1
&& dev->batch
&& dev->ckdrdonly
))
{
/* NOT explicit batch utility read-only open: create new shadow file */
if (cckd64_sf_new(dev) < 0)
return -1;
}
}
/* Re-open previous rdwr files rdonly */
for (i = 0; i < cckd->sfn; i++)
{
if (cckd->open[i] == CCKD_OPEN_RO) continue;
if (cckd64_open (dev, i, O_RDONLY|O_BINARY, 0) < 0)
{
// "%1d:%04X CCKD file[%d] %s: error re-opening readonly: %s"
WRMSG (HHC00312, "E", LCSS_DEVNUM, i, cckd_sf_name(dev, i), strerror(errno));
return -1;
}
}
return 0;
} /* end function cckd64_sf_init */
/*-------------------------------------------------------------------*/
/* Create a new shadow file */
/*-------------------------------------------------------------------*/
int cckd64_sf_new (DEVBLK *dev)
{
CCKD64_EXT *cckd; /* -> cckd extension */
int l1size; /* Size of level 1 table */
CKD_DEVHDR devhdr; /* Device header */
if (!dev->cckd64)
return cckd_sf_new( dev );
cckd = dev->cckd_ext;
CCKD_TRACE( "file[%d] sf_new %s", cckd->sfn+1,
cckd_sf_name(dev, cckd->sfn+1) ?
(char *)cckd_sf_name(dev, cckd->sfn+1) : "(none)");
/* Error if no shadow file name */
if (dev->dasdsfn == NULL)
{
WRMSG (HHC00313, "E", LCSS_DEVNUM, cckd->sfn+1);
return -1;
}
/* Error if max number of shadow files exceeded */
if (cckd->sfn+1 == CCKD_MAX_SF)
{
WRMSG (HHC00314, "E", LCSS_DEVNUM, cckd->sfn+1, dev->dasdsfn);
return -1;
}
/* Harden the current file */
cckd64_harden (dev);
/* Open the new shadow file */
if (cckd64_open(dev, cckd->sfn+1, O_RDWR|O_CREAT|O_EXCL|O_BINARY,
S_IRUSR | S_IWUSR | S_IRGRP) < 0)
return -1;
/* Read previous file's device header */
if (cckd64_read (dev, cckd->sfn, 0, &devhdr, CKD_DEVHDR_SIZE) < 0)
goto sf_new_error;
/* Make sure identifier is CKD_S370 or FBA_S370 */
devhdr.dh_devid[4] = 'S';
/* Write new file's device header */
if (cckd64_write (dev, cckd->sfn+1, 0, &devhdr, CKD_DEVHDR_SIZE) < 0)
goto sf_new_error;
/* Build the compressed device header */
memcpy (&cckd->cdevhdr[cckd->sfn+1], &cckd->cdevhdr[cckd->sfn], CCKD64_DEVHDR_SIZE);
l1size = cckd->cdevhdr[cckd->sfn+1].num_L1tab * CCKD64_L1ENT_SIZE;
cckd->cdevhdr[cckd->sfn+1].cdh_size =
cckd->cdevhdr[cckd->sfn+1].cdh_used = CKD_DEVHDR_SIZE + CCKD64_DEVHDR_SIZE + l1size;
cckd->cdevhdr[cckd->sfn+1].free_off =
cckd->cdevhdr[cckd->sfn+1].free_total =
cckd->cdevhdr[cckd->sfn+1].free_largest =
cckd->cdevhdr[cckd->sfn+1].free_num =
cckd->cdevhdr[cckd->sfn+1].free_imbed = 0;
/* Init the level 1 table */
if ((cckd->L1tab[cckd->sfn+1] = cckd_malloc (dev, "l1", l1size)) == NULL)
goto sf_new_error;
memset (cckd->L1tab[cckd->sfn+1], 0xff, l1size);
/* Make the new file active */
cckd->sfn++;
/* Harden the new file */
if (cckd64_harden (dev) < 0)
{
cckd->sfn--;
goto sf_new_error;
}
/* Re-read the l1 to set L2_bounds, L2ok */
cckd64_read_l1 (dev);
return 0;
sf_new_error:
cckd->L1tab[cckd->sfn+1] = cckd_free(dev, "l1", cckd->L1tab[cckd->sfn+1]);
cckd64_close (dev, cckd->sfn+1);
cckd->open[cckd->sfn+1] = CCKD_OPEN_NONE;
unlink (cckd_sf_name (dev, cckd->sfn+1));
/* Re-read the l1 to set L2_bounds, L2ok */
cckd64_read_l1 (dev);
return -1;
} /* end function cckd64_sf_new */
/*-------------------------------------------------------------------*/
/* Add a shadow file (sf+) */
/*-------------------------------------------------------------------*/
DLL_EXPORT void* cckd64_sf_add( void* data )
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKD64_EXT *cckd; /* -> cckd extension */
if (dev == NULL)
{
int n = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
if (dev->cckd_ext)
{
WRMSG (HHC00315, "I", LCSS_DEVNUM );
cckd64_sf_add (dev);
n++;
}
WRMSG(HHC00316, "I", n );
return NULL;
}
cckd = dev->cckd_ext;
if (!cckd)
{
WRMSG (HHC00317, "E", LCSS_DEVNUM);
return NULL;
}
/* Schedule updated track entries to be written */
obtain_lock (&cckd->cckdiolock);
if (cckd->merging)
{
release_lock (&cckd->cckdiolock);
WRMSG (HHC00318, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
return NULL;
}
cckd->merging = 1;
cckd64_flush_cache (dev);
while (cckd->wrpending || cckd->cckdioact)
{
cckd->cckdwaiters++;
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
cckd->cckdwaiters--;
cckd64_flush_cache (dev);
}
cckd64_purge_cache (dev); cckd64_purge_l2 (dev);
dev->bufcur = dev->cache = -1;
release_lock (&cckd->cckdiolock);
/* Obtain control of the file */
obtain_lock( &cckd->filelock );
{
/* Harden the current file */
cckd64_harden (dev);
/* Create a new shadow file */
if (cckd64_sf_new (dev) < 0)
{
WRMSG (HHC00319, "E", LCSS_DEVNUM, cckd->sfn+1,
cckd_sf_name(dev, cckd->sfn+1)?cckd_sf_name(dev, cckd->sfn+1):"(null)");
goto cckd_sf_add_exit;
}
/* Re-open the previous file if opened read-write */
if (cckd->open[cckd->sfn-1] == CCKD_OPEN_RW)
cckd64_open (dev, cckd->sfn-1, O_RDONLY|O_BINARY, 0);
WRMSG (HHC00320, "I", LCSS_DEVNUM, cckd->sfn, cckd_sf_name (dev, cckd->sfn));
cckd_sf_add_exit:
/* Re-read the l1 to set L2_bounds, L2ok */
cckd64_read_l1 (dev);
}
release_lock( &cckd->filelock );
obtain_lock (&cckd->cckdiolock);
cckd->merging = 0;
if (cckd->cckdwaiters)
broadcast_condition (&cckd->cckdiocond);
release_lock (&cckd->cckdiolock);
cckd64_sf_stats (dev);
return NULL;
} /* end function cckd64_sf_add */
/*-------------------------------------------------------------------*/
/* Remove a shadow file (sf-) */
/*-------------------------------------------------------------------*/
DLL_EXPORT void* cckd64_sf_remove( void* data )
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int from_sfx, to_sfx; /* From/to file index */
int fix; /* nullfmt index */
int add = 0; /* 1=Add shadow file back */
int l2updated = 0; /* 1=L2 table was updated */
int i,j; /* Loop indexes */
int merge, force; /* Flags */
U64 pos; /* File offset */
unsigned int len; /* Length to read/write */
int size; /* Image size */
int trk = -1; /* Track being read/written */
CCKD64_L2ENT from_l2[256], /* Level 2 tables */
to_l2[256];
CCKD64_L2ENT new_l2; /* New level 2 table entry */
BYTE buf[64*1024]; /* Buffer */
/* NULL DEVBLK == all devices? */
if (!dev)
{
int n = 0;
merge = cckdblk.sfmerge;
force = cckdblk.sfforce;
cckdblk.sfmerge = cckdblk.sfforce = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
{
if ((cckd = dev->cckd_ext)) /* Compressed device? */
{
if (cckd->sfn) /* Any active shadow file(s)? */
{
// "%1d:%04X CCKD file: merging shadow files..."
WRMSG( HHC00321, "I", LCSS_DEVNUM );
cckd->sfmerge = merge;
cckd->sfforce = force;
cckd64_sf_remove( dev );
n++;
}
}
}
// "CCKD file number of devices processed: %d"
WRMSG( HHC00316, "I", n );
return NULL;
}
/* Specific device... */
if (!dev->cckd64)
return cckd_sf_remove( data );
if (!(cckd = dev->cckd_ext))
{
// "%1d:%04X CCKD file: device is not a cckd device"
WRMSG( HHC00317, "W", LCSS_DEVNUM );
return NULL;
}
if (!cckd->sfn)
{
// "%1d:%04X CCKD file: device has no shadow files"
WRMSG( HHC00390, "W", LCSS_DEVNUM );
return NULL;
}
/* Set flags */
merge = cckd->sfmerge || cckd->sfforce;
force = cckd->sfforce;
cckd->sfmerge = cckd->sfforce = 0;
CCKD_TRACE( "merge starting: %s %s",
merge ? "merge" : "nomerge", force ? "force" : "");
/* Schedule updated track entries to be written */
obtain_lock( &cckd->cckdiolock );
{
if (cckd->merging)
{
release_lock( &cckd->cckdiolock );
// "%1d:%04X CCKD file[%d] %s: error merging shadow file, sf command busy on device"
WRMSG( HHC00322, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
return NULL;
}
cckd->merging = 1;
cckd64_flush_cache (dev);
while (cckd->wrpending || cckd->cckdioact)
{
cckd->cckdwaiters++;
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
cckd->cckdwaiters--;
cckd64_flush_cache( dev );
}
cckd64_purge_cache( dev );
cckd64_purge_l2( dev );
dev->bufcur = dev->cache = -1;
}
release_lock( &cckd->cckdiolock );
obtain_lock( &cckd->filelock );
if (!cckd->sfn)
{
release_lock( &cckd->filelock );
// "%1d:%04X CCKD file[%d] %s: cannot remove base file"
WRMSG( HHC00323, "E", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
cckd->merging = 0;
return NULL;
}
from_sfx = cckd->sfn;
to_sfx = cckd->sfn - 1;
fix = cckd->cdevhdr[ to_sfx ].cdh_nullfmt;
/* Harden the `from' file */
if (cckd64_harden( dev ) < 0)
{
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
WRMSG( HHC00324, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, cckd->sfx ),
from_sfx, "not hardened", "" );
goto sf_remove_exit;
}
/* Attempt to re-open the `to' file read-write */
cckd64_close( dev, to_sfx );
if (to_sfx > 0 || !dev->ckdrdonly || force)
cckd64_open( dev, to_sfx, O_RDWR | O_BINARY, 1 );
if (cckd->fd[to_sfx] < 0)
{
/* `from' file can't be opened read-write */
cckd64_open( dev, to_sfx, O_RDONLY | O_BINARY, 0 );
if (merge)
{
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
WRMSG( HHC00324, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ),
to_sfx, "cannot be opened read-write",
to_sfx == 0 && dev->ckdrdonly && !force ? ", try 'force'" : "" );
goto sf_remove_exit;
}
else
add = 1;
}
else
{
/* `from' file opened read-write */
cckd->sfn = to_sfx;
if (cckd64_chkdsk( dev, 0 ) < 0)
{
cckd->sfn = from_sfx;
// "%1d:%04X CCKD file[%d] %s: shadow file not merged: file[%d] %s%s"
WRMSG( HHC00324, "E", LCSS_DEVNUM, to_sfx, cckd_sf_name( dev, to_sfx ),
to_sfx, "check failed", "" );
goto sf_remove_exit;
}
}
cckd->sfn = to_sfx;
/* Perform backwards merge */
if (merge)
{
CCKD_TRACE( "merging to file[%d]", to_sfx );
/* Make the target file the active file */
cckd->sfn = to_sfx;
cckd->cdevhdr[to_sfx].cdh_opts |= (CCKD_OPT_OPENED | CCKD_OPT_OPENRW);
/* Loop for each level 1 table entry */
for (i = 0; i < cckd->cdevhdr[from_sfx].num_L1tab; i++)
{
l2updated = 0;
/* Continue if from L2 doesn't exist */
if (cckd->L1tab[from_sfx][i] == CCKD64_MAXSIZE
|| (cckd->L1tab[from_sfx][i] == 0 && cckd->L1tab[to_sfx][i] == 0))
continue;
trk = i*256;
/* Read `from' l2 table */
if (cckd->L1tab[from_sfx][i] == 0)
memset( &from_l2, 0, CCKD64_L2TAB_SIZE );
else
{
pos = cckd->L1tab[from_sfx][i];
if (cckd64_read( dev, from_sfx, pos, &from_l2, CCKD64_L2TAB_SIZE ) < 0)
goto sf_merge_error;
}
/* Read `to' l2 table */
if (cckd->L1tab[to_sfx][i] == 0)
memset( &to_l2, 0, CCKD64_L2TAB_SIZE );
else if (cckd->L1tab[to_sfx][i] == CCKD64_MAXSIZE)
memset (&to_l2, 0xff, CCKD64_L2TAB_SIZE);
else
{
pos = cckd->L1tab[to_sfx][i];
if (cckd64_read( dev, to_sfx, pos, &to_l2, CCKD64_L2TAB_SIZE ) < 0)
goto sf_merge_error;
}
/* Loop for each level 2 table entry */
for (j = 0; j < 256; j++)
{
trk = i*256 + j;
/* Continue if from L2 entry doesn't exist */
if (from_l2[j].L2_trkoff == CCKD64_MAXSIZE
|| (from_l2[j].L2_trkoff == 0 && to_l2[j].L2_trkoff == 0))
continue;
/* Read the `from' track/blkgrp image */
len = (unsigned int)from_l2[j].L2_len;
if (len > CKD_NULLTRK_FMTMAX)
{
pos = from_l2[j].L2_trkoff;
if (cckd64_read( dev, from_sfx, pos, buf, len ) < 0)
goto sf_merge_error;
/* Get space for the `to' track/blkgrp image */
size = len;
if ((S64)(pos = cckd64_get_space (dev, &size, CCKD_SIZE_EXACT)) < 0)
goto sf_merge_error;
new_l2.L2_trkoff = pos;
new_l2.L2_len = (U16)len;
new_l2.L2_size = (U16)size;
/* Write the `to' track/blkgrp image */
if (cckd64_write( dev, to_sfx, pos, buf, len ) < 0)
goto sf_merge_error;
}
else
{
new_l2.L2_trkoff = 0;
new_l2.L2_len = new_l2.L2_size = (U16)len;
}
/* Release space occupied by old `to' entry */
cckd64_rel_space( dev, to_l2[j].L2_trkoff, (int)to_l2[j].L2_len,
(int)to_l2[j].L2_size );
/* Update `to' l2 table entry */
l2updated = 1;
to_l2[j].L2_trkoff = new_l2.L2_trkoff;
to_l2[j].L2_len = new_l2.L2_len;
to_l2[j].L2_size = new_l2.L2_size;
} /* for each level 2 table entry */
/* Update the `to' level 2 table */
if (l2updated)
{
l2updated = 0;
pos = cckd->L1tab[to_sfx][i];
if (memcmp( &to_l2, &empty64_l2[fix], CCKD64_L2TAB_SIZE ) == 0)
{
cckd64_rel_space( dev, pos, CCKD64_L2TAB_SIZE, CCKD64_L2TAB_SIZE );
pos = 0;
}
else
{
size = CCKD64_L2TAB_SIZE;
if (pos == CCKD64_NOSIZE || pos == CCKD64_MAXSIZE)
if ((S64)(pos = cckd64_get_space( dev, &size, CCKD_L2SPACE )) < 0)
goto sf_merge_error;
if (cckd64_write( dev, to_sfx, pos, &to_l2, CCKD64_L2TAB_SIZE ) < 0)
goto sf_merge_error;
} /* `to' level 2 table not null */
/* Update the level 1 table index */
cckd->L1tab[to_sfx][i] = pos;
/* Flush free space */
cckd64_flush_space( dev );
} /* Update level 2 table */
} /* For each level 1 table entry */
/* Validate the merge */
cckd64_harden( dev );
cckd64_chkdsk( dev, 0 );
cckd64_read_init( dev );
} /* if merge */
/* Remove the old file */
cckd64_close( dev, from_sfx );
cckd->L1tab[from_sfx] = cckd_free( dev, "l1", cckd->L1tab[ from_sfx ]);
memset( &cckd->cdevhdr[from_sfx], 0, CCKD64_DEVHDR_SIZE );
rc = unlink (cckd_sf_name (dev, from_sfx));
/* adjust the stats */
cckd->reads [to_sfx] += cckd->reads [from_sfx];
cckd->writes [to_sfx] += cckd->writes [from_sfx];
cckd->L2_reads[to_sfx] += cckd->L2_reads[from_sfx];
cckd->reads [from_sfx] = 0;
cckd->writes [from_sfx] = 0;
cckd->L2_reads[from_sfx] = 0;
/* Add the file back if necessary */
if (add) rc = cckd64_sf_new( dev ) ;
// "%1d:%04X CCKD file[%d] %s: shadow file successfully %s"
WRMSG( HHC00325, "I", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ),
merge ? "merged" : add ? "re-added" : "removed" );
sf_remove_exit:
/* Re-read the l1 to set L2_bounds, L2ok */
cckd64_read_l1( dev );
release_lock( &cckd->filelock );
obtain_lock( &cckd->cckdiolock );
{
cckd64_purge_cache( dev);
cckd64_purge_l2( dev );
dev->bufcur = dev->cache = -1;
cckd->merging = 0;
if (cckd->cckdwaiters)
broadcast_condition( &cckd->cckdiocond );
CCKD_TRACE( "merge complete%s", "" );
}
release_lock( &cckd->cckdiolock );
cckd64_sf_stats( dev );
return NULL;
sf_merge_error:
if (trk < 0)
// "%1d:%04X CCKD file[%d] %s: shadow file not merged, error during merge"
WRMSG( HHC00326, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ));
else
// "%1d:%04X CCKD file[%d] %s: shadow file not merged, error processing trk(%d)"
WRMSG( HHC00327, "E", LCSS_DEVNUM, from_sfx, cckd_sf_name( dev, from_sfx ), trk );
if (l2updated && cckd->L1tab[ to_sfx ][i] && cckd->L1tab[ to_sfx ][i] != CCKD64_SHADOW_NO_OFFSET)
{
l2updated = 0;
pos = cckd->L1tab[to_sfx][i];
cckd64_write( dev, to_sfx, pos, &to_l2, CCKD64_L2TAB_SIZE );
}
cckd64_harden( dev );
cckd64_chkdsk( dev, 2 );
cckd->sfn = from_sfx;
cckd64_harden( dev );
cckd64_chkdsk( dev, 2 );
goto sf_remove_exit;
} /* end function cckd64_sf_remove */
/*-------------------------------------------------------------------*/
/* Check and compress a shadow file (sfc) */
/*-------------------------------------------------------------------*/
DLL_EXPORT void *cckd64_sf_comp( void* data )
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
/* NULL DEVBLK == all devices? */
if (!dev)
{
int n = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
{
if (dev->cckd_ext) /* Is this a compressed device? */
{
// "%1d:%04X CCKD file: compressing shadow files..."
WRMSG( HHC00328, "I", LCSS_DEVNUM );
cckd64_sf_comp( dev );
n++;
}
}
// "CCKD file number of devices processed: %d"
WRMSG ( HHC00316, "I", n );
return NULL;
}
if (!dev->cckd64)
return cckd_sf_comp( data );
if (!(cckd = dev->cckd_ext))
{
// "%1d:%04X CCKD file: device is not a cckd device"
WRMSG( HHC00317, "W", LCSS_DEVNUM );
return NULL;
}
/* schedule updated track entries to be written */
obtain_lock( &cckd->cckdiolock );
{
if (cckd->merging)
{
release_lock( &cckd->cckdiolock );
// "%1d:%04X CCKD file[%d] %s: error compressing shadow file, sf command busy on device"
WRMSG( HHC00329, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn) );
return NULL;
}
cckd->merging = 1;
cckd64_flush_cache( dev );
while (cckd->wrpending || cckd->cckdioact)
{
cckd->cckdwaiters++;
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
cckd->cckdwaiters--;
cckd64_flush_cache( dev );
}
cckd64_purge_cache( dev );
cckd64_purge_l2( dev );
dev->bufcur = dev->cache = -1;
}
release_lock( &cckd->cckdiolock );
/* obtain control of the file */
obtain_lock( &cckd->filelock );
{
/* harden the current file */
cckd64_harden( dev );
/* Call the compress function */
rc = cckd64_comp( dev );
/* Perform initial read */
rc = cckd64_read_init( dev );
}
release_lock( &cckd->filelock );
obtain_lock( &cckd->cckdiolock );
{
cckd->merging = 0;
if (cckd->cckdwaiters)
broadcast_condition( &cckd->cckdiocond );
}
release_lock( &cckd->cckdiolock );
/* Display the shadow file statistics */
cckd64_sf_stats( dev );
return NULL;
} /* end function cckd64_sf_comp */
/*-------------------------------------------------------------------*/
/* Check a shadow file (sfk) */
/*-------------------------------------------------------------------*/
DLL_EXPORT void* cckd64_sf_chk( void* data )
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKD64_EXT *cckd; /* -> cckd extension */
int rc; /* Return code */
int level = 2; /* Check level */
/* NULL DEVBLK == all devices? */
if (!dev)
{
int n = 0;
level = cckdblk.sflevel;
cckdblk.sflevel = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
{
if ((cckd = dev->cckd_ext)) /* Compressed device? */
{
// "%1d:%04X CCKD file: checking level %d..."
WRMSG( HHC00330, "I", LCSS_DEVNUM, level );
cckd->sflevel = level;
cckd64_sf_chk( dev );
n++;
}
}
// "CCKD file number of devices processed: %d"
WRMSG( HHC00316, "I", n );
return NULL;
}
if (!dev->cckd64)
return cckd_sf_chk( data );
if (!(cckd = dev->cckd_ext))
{
// "%1d:%04X CCKD file: device is not a cckd device"
WRMSG( HHC00317, "W", LCSS_DEVNUM );
return NULL;
}
level = cckd->sflevel;
cckd->sflevel = 0;
/* schedule updated track entries to be written */
obtain_lock( &cckd->cckdiolock );
{
if (cckd->merging)
{
release_lock( &cckd->cckdiolock );
// "%1d:%04X CCKD file[%d] %s: shadow file check failed, sf command busy on device"
WRMSG( HHC00331, "W", LCSS_DEVNUM, cckd->sfn, cckd_sf_name( dev, cckd->sfn ));
return NULL;
}
cckd->merging = 1;
cckd64_flush_cache( dev );
while (cckd->wrpending || cckd->cckdioact)
{
cckd->cckdwaiters++;
{
wait_condition( &cckd->cckdiocond, &cckd->cckdiolock );
}
cckd->cckdwaiters--;
cckd64_flush_cache( dev );
}
cckd64_purge_cache( dev );
cckd64_purge_l2( dev );
dev->bufcur = -1;
dev->cache = -1;
}
release_lock( &cckd->cckdiolock );
/* obtain control of the file */
obtain_lock( &cckd->filelock );
{
/* harden the current file */
cckd64_harden( dev );
/* Call the chkdsk function */
rc = cckd64_chkdsk( dev, level );
/* Perform initial read */
rc = cckd64_read_init( dev );
}
release_lock( &cckd->filelock );
obtain_lock( &cckd->cckdiolock );
{
cckd->merging = 0;
if (cckd->cckdwaiters)
broadcast_condition( &cckd->cckdiocond );
}
release_lock( &cckd->cckdiolock );
/* Display the shadow file statistics */
cckd64_sf_stats( dev );
return NULL;
} /* end function cckd64_sf_chk */
/*-------------------------------------------------------------------*/
/* Display shadow file statistics (sfd) */
/*-------------------------------------------------------------------*/
DLL_EXPORT void* cckd64_sf_stats( void* data )
{
DEVBLK *dev = data; /* -> DEVBLK */
CCKD64_EXT *cckd; /* -> cckd extension */
struct stat st = {0}; /* File information */
int i; /* Index */
char *ost[] = {" ", "ro", "rd", "rw"};
U64 usize=0,ufree=0; /* Total size, free space */
S64 free_count=0; /* Total number free spaces */
/* NULL DEVBLK == all devices? */
if (!dev)
{
int n = 0;
for (dev=sysblk.firstdev; dev; dev=dev->nextdev)
{
if (dev->cckd_ext) /* Is this a compressed device? */
{
// "%1d:%04X CCKD file: display cckd statistics"
WRMSG( HHC00332, "I", LCSS_DEVNUM );
cckd64_sf_stats( dev );
n++;
}
}
// "CCKD file number of devices processed: %d"
WRMSG( HHC00316, "I", n );
return NULL;
}
if (!dev->cckd64)
return cckd_sf_stats( data );
if (!(cckd = dev->cckd_ext))
{
// "%1d:%04X CCKD file: device is not a cckd device"
WRMSG( HHC00317, "W", LCSS_DEVNUM );
return NULL;
}
/* Calculate totals */
if (fstat( cckd->fd[0], &st ) != 0)
{
// "Error in function %s: %s"
WRMSG( HHC00075, "E", "fstat()", strerror( errno ));
memset( &st, 0, sizeof( st ));
}
for (i=0; i <= cckd->sfn; i++)
{
if (!i) usize = st.st_size;
else usize += cckd->cdevhdr[i].cdh_size;
ufree += cckd->cdevhdr[i].free_total;
free_count += cckd->cdevhdr[i].free_num;
}
/* header */
// "%1d:%04X 32/64 size free nbr st reads writes l2reads hits switches"
WRMSG (HHC00333, "I", LCSS_DEVNUM);
if (cckd->readaheads || cckd->misses)
// "%1d:%04X readaheads misses"
WRMSG (HHC00334, "I", LCSS_DEVNUM);
// "%1d:%04X ------------------------------------------------------------------------"
WRMSG (HHC00335, "I", LCSS_DEVNUM);
/* total statistics */
// "%1d:%04X [*] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %7.7d %7.7d %7.7d %7.7d %7.7d"
WRMSG( HHC00336, "I", LCSS_DEVNUM, dev->cckd64 ? "64" : "32", usize
, usize ? (ufree * 100) / usize : 999
, free_count
, cckd->totreads
, cckd->totwrites
, cckd->totl2reads
, cckd->cachehits
, cckd->switches
);
if (cckd->readaheads || cckd->misses)
// "%1d:%04X %7.7d %7.7d"
WRMSG (HHC00337, "I", LCSS_DEVNUM,
cckd->readaheads, cckd->misses);
/* base file statistics */
// "%1d:%04X %s"
WRMSG (HHC00338, "I", LCSS_DEVNUM, dev->filename);
// "%1d:%04X [0] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %s %7.7d %7.7d %7.7d"
WRMSG( HHC00339, "I", LCSS_DEVNUM, dev->cckd64 ? "64" : "32", st.st_size
, st.st_size ?
((S64)(cckd->cdevhdr[0].free_total * 100) / st.st_size) : 999
, cckd->cdevhdr[0].free_num
, ost[cckd->open[0]]
, cckd->reads[0]
, cckd->writes[0]
, cckd->L2_reads[0]
);
if (dev->dasdsfn != NULL && CCKD_MAX_SF > 0)
// "%1d:%04X %s"
WRMSG (HHC00340, "I", LCSS_DEVNUM, cckd_sf_name(dev, -1));
/* shadow file statistics */
for (i = 1; i <= cckd->sfn; i++)
{
// "%1d:%04X [%d] %s %11.11"PRId64" %3.3"PRId64"%% %4.4"PRId64" %s %7.7d %7.7d %7.7d"
WRMSG( HHC00341, "I", LCSS_DEVNUM, i, dev->cckd64 ? "64" : "32", (S64)cckd->cdevhdr[i].cdh_size
, cckd->cdevhdr[i].cdh_size ?
((S64)(cckd->cdevhdr[i].free_total * 100) / cckd->cdevhdr[i].cdh_size) : 999
, cckd->cdevhdr[i].free_num
, ost[cckd->open[i]]
, cckd->reads[i]
, cckd->writes[i]
, cckd->L2_reads[i]
);
}
return NULL;
} /* end function cckd64_sf_stats */
/*-------------------------------------------------------------------*/
/* Report Garbage Collection state for a given CCKD device */
/*-------------------------------------------------------------------*/
void cckd64_gc_rpt_state( DEVBLK* dev )
{
CCKD64_EXT* cckd;
int gc;
if (!dev->cckd64)
{
cckd_gc_rpt_state( dev );
return;
}
cckd = dev->cckd_ext;
/* Retrieve and report garbage collector state, but ONLY if
the image is over 100MB in size. This prevents "scaring"
the user about very fragmented files when the file is
too small to be much of a concern, as is usually the case
with e.g. shadow files.
*/
if (cckd->cdevhdr->cdh_size < (100 * _1M))
return;
gc = cckd_gc_state( dev );
switch (gc)
{
case 0: // critical!
case 1: // severe
// "%1d:%04X CCKD%s image %s is very fragmented."
WRMSG( HHC00387, "W", LCSS_DEVNUM, "64",
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
break;
case 2: // moderate
// "%1d:%04X CCKD%s image %s is moderately fragmented."
WRMSG( HHC00388, "I", LCSS_DEVNUM, "64",
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
break;
case 3: // light
// "%1d:%04X CCKD%s image %s is slightly fragmented."
WRMSG( HHC00389, "I", LCSS_DEVNUM, "64",
TRIMLOC( cckd_sf_name( dev, cckd->sfn )));
break;
default: // less than light
break; // (don't bother reporting it)
}
}
/*-------------------------------------------------------------------*/
/* Return Garbage Collection State for a given CCKD device */
/*-------------------------------------------------------------------*/
int cckd64_gc_state( DEVBLK* dev )
{
CCKD64_EXT* cckd; /* -> cckd extension */
S64 size, fsiz; /* File size, free size */
int gc; /* Garbage collection state */
if (!dev->cckd64)
return cckd_gc_state( dev );
cckd = dev->cckd_ext;
/* Determine garbage state */
size = (S64) cckd->cdevhdr[ cckd->sfn ].cdh_size;
fsiz = (S64) cckd->cdevhdr[ cckd->sfn ].free_total;
if (fsiz >= (size = size/2)) gc = 0; // critical 50% - 100%
else if (fsiz >= (size = size/2)) gc = 1; // severe 25% - 50%
else if (fsiz >= (size = size/2)) gc = 2; // moderate 12.5% - 25%
else if (fsiz >= (size = size/2)) gc = 3; // light 6.3% - 12.5%
else gc = 4; // none 0% - 6.3%
/* Adjust the state based on the number of free spaces */
if (cckd->cdevhdr[ cckd->sfn ].free_num > 800 && gc > 0) gc--;
if (cckd->cdevhdr[ cckd->sfn ].free_num > 1800 && gc > 0) gc--;
if (cckd->cdevhdr[ cckd->sfn ].free_num > 3000) gc = 0;
return gc;
}
/*-------------------------------------------------------------------*/
/* Perform garbage collection for a given CCKD64 device */
/*-------------------------------------------------------------------*/
void cckd64_gcol_dev( DEVBLK* dev, struct timeval* tv_now )
{
int rc; /* Return code */
CCKD64_EXT *cckd; /* -> cckd extension */
U64 size; /* Percolate size */
int gc; /* Garbage collection state */
if (!dev->cckd64)
{
cckd_gcol_dev( dev, tv_now );
return;
}
cckd = dev->cckd_ext;
obtain_lock (&cckd->cckdiolock);
{
/* Bypass if merging or stopping */
if (cckd->merging || cckd->stopping)
{
release_lock (&cckd->cckdiolock);
return;
}
/* Bypass if not opened read-write */
if (cckd->open[cckd->sfn] != CCKD_OPEN_RW)
{
release_lock (&cckd->cckdiolock);
return;
}
/* Free newbuf if it hasn't been used */
if (!cckd->cckdioact && !cckd->bufused && cckd->newbuf)
cckd->newbuf = cckd_free (dev, "newbuf", cckd->newbuf);
cckd->bufused = 0;
/* If OPENED bit not on then flush if updated */
if (!(cckd->cdevhdr[cckd->sfn].cdh_opts & CCKD_OPT_OPENED))
{
if (cckd->updated) cckd64_flush_cache (dev);
release_lock (&cckd->cckdiolock);
return;
}
/* Determine garbage state */
gc = cckd64_gc_state( dev );
/* Set the size */
if (cckdblk.gcparm > 0) size = gctab[gc] << cckdblk.gcparm;
else if (cckdblk.gcparm < 0) size = gctab[gc] >> abs(cckdblk.gcparm);
else size = gctab[gc];
if (size > cckd->cdevhdr[cckd->sfn].cdh_used >> SHIFT_1K)
size = cckd->cdevhdr[cckd->sfn].cdh_used >> SHIFT_1K;
if (size < 64)
size = 64;
}
release_lock (&cckd->cckdiolock);
/* Call the garbage collector */
cckd64_gc_percolate( dev, size );
/* Schedule any updated tracks to be written */
obtain_lock (&cckd->cckdiolock);
{
cckd64_flush_cache (dev);
while (cckdblk.fsync && cckd->wrpending)
{
cckd->cckdwaiters++;
{
wait_condition (&cckd->cckdiocond, &cckd->cckdiolock);
}
cckd->cckdwaiters--;
}
}
release_lock (&cckd->cckdiolock);
/* Sync the file */
if (cckdblk.fsync && cckd->lastsync + 10 <= tv_now->tv_sec)
{
obtain_lock( &cckd->filelock );
{
rc = fdatasync (cckd->fd[cckd->sfn]);
cckd->lastsync = tv_now->tv_sec;
}
release_lock( &cckd->filelock );
}
/* Flush the free space */
if (cckd->cdevhdr[cckd->sfn].free_num)
{
obtain_lock( &cckd->filelock );
{
cckd64_flush_space (dev);
}
release_lock( &cckd->filelock );
}
}
/*-------------------------------------------------------------------*/
/* Garbage Collection error handler helper functions/macros */
/*-------------------------------------------------------------------*/
static int cckd64_gc_perc_error( DEVBLK* dev, CCKD64_EXT* cckd, U64 moved,
const char* file, int line )
{
cckd_trace( file, line, dev, "gcperc exiting due to error, moved %"PRIu64, moved );
release_lock( &cckd->filelock );
return (int) moved;
}
#define GC64_PERC_ERROR() cckd64_gc_perc_error( dev, cckd, moved, __FILE__, __LINE__ )
static int cckd64_gc_perc_space_error( DEVBLK* dev, CCKD64_EXT* cckd, U64 upos, S64 i, BYTE* buf, U64 moved,
const char* file, int line )
{
// "%1d:%04X CCKD file[%d] %s: %s(%d): offset 0x%16.16"PRIx64" unknown space %2.2x%2.2x%2.2x%2.2x%2.2x"
WRMSG( HHC00342, "E", LCSS_DEVNUM,
cckd->sfn, cckd_sf_name( dev, cckd->sfn ), TRIMLOC( file ), line,
upos + i, buf[i], buf[i+1],buf[i+2], buf[i+3], buf[i+4]);
cckd->cdevhdr[ cckd->sfn ].cdh_opts |= CCKD_OPT_SPERRS;
cckd_print_itrace();
return cckd64_gc_perc_error( dev, cckd, moved, file, line );
}
#define GC64_PERC_SPACE_ERROR() cckd64_gc_perc_space_error( dev, cckd, upos, i, buf, moved, __FILE__, __LINE__ )
/*-------------------------------------------------------------------*/
/* Garbage Collection -- Percolate algorithm */
/*-------------------------------------------------------------------*/
int cckd64_gc_percolate( DEVBLK* dev, U64 size )
{
CCKD64_EXT* cckd; /* -> cckd extension */
bool didmsg = false; /* HHC00384 issued */
int rc; /* Return code */
U64 moved = 0; /* Space moved */
S64 after = 0, a; /* New space after old */
S64 sfx; /* File index */
S64 i, j, k; /* Indexes */
int flags; /* Write trkimg flags */
U64 fpos, upos; /* File offsets */
U64 flen, ulen, len; /* Lengths */
int trk; /* Track number */
int L1idx, l2x; /* Table Indexes */
CCKD64_L2ENT l2; /* Copied level 2 entry */
BYTE buf[256*1024]; /* Buffer */
if (!dev->cckd64)
return cckd_gc_percolate( dev, size );
cckd = dev->cckd_ext;
size = size << SHIFT_1K;
if (cckd->cdevhdr[ cckd->sfn ].cdh_opts & CCKD_OPT_SPERRS)
{
// "Skipping garbage collection for CCKD%s file[%d] %1d:%04X %s due to space errors"
if (cckdblk.gcmsgs)
WRMSG( HHC00385, "I", "64", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
return (int) moved;
}
/* Debug */
OBTAIN_TRACE_LOCK();
if (cckdblk.itracen)
{
RELEASE_TRACE_LOCK();
CCKD_TRACE( "gcperc size %d 1st 0x%"PRIx64" nbr %"PRId64" largest %"PRIu64,
size, cckd->cdevhdr[cckd->sfn].free_off,
cckd->cdevhdr[cckd->sfn].free_num,
cckd->cdevhdr[cckd->sfn].free_largest);
fpos = cckd->cdevhdr[cckd->sfn].free_off;
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
CCKD_TRACE( "gcperc free[%4d]:%16.16"PRIx64" end %16.16"PRIx64" len %10"PRId64"%cpend %d",
i, fpos, (fpos+cckd->ifb[i].ifb_len), cckd->ifb[i].ifb_len,
fpos+cckd->ifb[i].ifb_len == cckd->ifb[i].ifb_offnxt ?
'*' : ' ', cckd->ifb[i].ifb_pending);
fpos = cckd->ifb[i].ifb_offnxt;
}
}
else
RELEASE_TRACE_LOCK();
if (!cckd->L2ok)
cckd64_gc_l2(dev, buf);
/* garbage collection cycle... */
while (moved < size && after < 4)
{
obtain_lock( &cckd->filelock );
sfx = cckd->sfn;
/* Exit if no more free space */
if (cckd->cdevhdr[sfx].free_total == 0)
{
release_lock( &cckd->filelock );
return (int) moved;
}
/* Make sure the free space chain is built */
if (!cckd->ifb) cckd64_read_fsp (dev);
/* Find a space to start with */
k = -1;
upos = ulen = flen = 0;
fpos = cckd->cdevhdr[sfx].free_off;
/* First non-pending free space */
for (i = cckd->free_idx1st; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
if (!cckd->ifb[i].ifb_pending)
{
flen += cckd->ifb[i].ifb_len;
break;
}
fpos = cckd->ifb[i].ifb_offnxt;
}
/* Continue to largest if non-zero `after' */
for ( ; i >= 0 && after; i = cckd->ifb[i].ifb_idxnxt)
{
k = i;
if (!cckd->ifb[i].ifb_pending) flen += cckd->ifb[i].ifb_len;
if (cckd->ifb[i].ifb_len == cckd->cdevhdr[sfx].free_largest)
break;
fpos = cckd->ifb[i].ifb_offnxt;
}
/* Skip following free spaces */
for ( ; i >= 0; i = cckd->ifb[i].ifb_idxnxt)
{
if (!cckd->ifb[i].ifb_pending) flen += cckd->ifb[i].ifb_len;
if (fpos + cckd->ifb[i].ifb_len != cckd->ifb[i].ifb_offnxt) break;
fpos = cckd->ifb[i].ifb_offnxt;
}
/* Space preceding largest if largest is at the end */
if (i < 0 && k >= 0)
{
if (!cckd->ifb[k].ifb_pending) flen -= cckd->ifb[i].ifb_len;
for (i = cckd->ifb[k].ifb_idxprv; i >= 0; i = cckd->ifb[i].ifb_idxprv)
{
fpos = cckd->ifb[i].ifb_idxprv >= 0
? cckd->ifb[cckd->ifb[i].ifb_idxprv].ifb_offnxt
: cckd->cdevhdr[sfx].free_off;
if (fpos + cckd->ifb[i].ifb_len < cckd->ifb[i].ifb_offnxt) break;
if (!cckd->ifb[i].ifb_pending) flen -= cckd->ifb[i].ifb_len;
}
}
/* Calculate the offset/length of the used space.
* If only imbedded free space is left, then start
* with the first used space that is not an l2 table.
*/
if (i >= 0)
{
upos = fpos + cckd->ifb[i].ifb_len;
ulen = (cckd->ifb[i].ifb_offnxt ? cckd->ifb[i].ifb_offnxt : cckd->cdevhdr[sfx].cdh_size) - upos;
}
else if (!cckd->cdevhdr[sfx].free_num && cckd->cdevhdr[sfx].free_imbed)
{
upos = (CCKD64_L1TAB_POS + cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE);
while (1)
{
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
if (cckd->L1tab[sfx][i] == upos)
break;
if (i >= cckd->cdevhdr[sfx].num_L1tab)
break;
upos += CCKD64_L2TAB_SIZE;
}
ulen = cckd->cdevhdr[sfx].cdh_size - upos;
}
/* Return if no applicable used space */
if (ulen == 0)
{
CCKD_TRACE( "gcperc no applicable space, moved %"PRIu64, moved);
release_lock( &cckd->filelock );
return (int) moved;
}
/* Reduce ulen size to minimize `after' relocations */
if (ulen > flen + 65536) ulen = flen + 65536;
if (ulen > sizeof(buf)) ulen = sizeof(buf);
CCKD_TRACE( "gcperc selected space 0x%16.16"PRIx64" len %"PRId64, upos, ulen);
if (cckd64_read (dev, (int) sfx, upos, buf, (unsigned int) ulen) < 0)
return GC64_PERC_ERROR();
/* Process each space in the buffer */
flags = cckd->cdevhdr[sfx].free_num < 100 ? CCKD_SIZE_EXACT : CCKD_SIZE_ANY;
for (i = a = 0; (U64)i + CKD_TRKHDR_SIZE <= ulen; i += len)
{
/* Check for level 2 table */
for (j = 0; j < cckd->cdevhdr[sfx].num_L1tab; j++)
if (cckd->L1tab[sfx][j] == (upos + i)) break;
if (!didmsg)
{
// "Collecting garbage for CCKD%s file[%d] %1d:%04X %s..."
if (cckdblk.gcmsgs)
WRMSG( HHC00384, "I", "64", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
didmsg = true;
}
if (j < cckd->cdevhdr[sfx].num_L1tab)
{
/* Moving a level 2 table */
len = CCKD64_L2TAB_SIZE;
if (i + len > ulen) break;
CCKD_TRACE( "gcperc move l2tab[%"PRId64"] at pos 0x%16.16"PRIx64" len %"PRId64,
j, upos + i, len);
/* Make the level 2 table active */
if (cckd64_read_l2 (dev, (int)sfx, (int)j) < 0)
return GC64_PERC_ERROR();
/* Write the level 2 table */
if (cckd64_write_l2 (dev) < 0)
return GC64_PERC_ERROR();
}
else
{
/* Moving a track image */
if ((trk = cckd64_cchh (dev, buf + i, -1)) < 0)
return GC64_PERC_SPACE_ERROR();
L1idx = trk >> 8;
l2x = trk & 0xff;
/* Read the lookup entry for the track */
if (cckd64_read_l2ent (dev, &l2, trk) < 0)
return GC64_PERC_ERROR();
if (l2.L2_trkoff != (upos + i))
return GC64_PERC_SPACE_ERROR();
len = l2.L2_size;
if ((U64)i + l2.L2_len > ulen) break;
CCKD_TRACE( "gcperc move trk %d at pos 0x%16.16"PRIx64" len %hu",
trk, upos + i, l2.L2_len);
/* Relocate the track image somewhere else */
if ((rc = cckd64_write_trkimg (dev, buf + i, (int)l2.L2_len, trk, flags)) < 0)
return GC64_PERC_ERROR();
a += rc;
}
} /* for each space in the used space */
/* Set `after' to 1 if first time space was relocated after */
after += after ? a : (a > 0);
moved += i;
cckdblk.stats_gcolmoves++;
cckdblk.stats_gcolbytes += i;
release_lock( &cckd->filelock );
} /* while (moved < size) */
CCKD_TRACE( "gcperc moved %d 1st 0x%"PRIx64" nbr %"PRIu64, moved,
cckd->cdevhdr[cckd->sfn].free_off, cckd->cdevhdr[cckd->sfn].free_num);
// "Collected %u bytes of garbage for CCKD%s file[%d] %1d:%04X %s..."
if (cckdblk.gcmsgs)
WRMSG( HHC00386, "I", (unsigned int) moved, "64", cckd->sfn, LCSS_DEVNUM, cckd_sf_name( dev, cckd->sfn ));
return (int) moved;
} /* end function cckd64_gc_percolate */
/*-------------------------------------------------------------------*/
/* Garbage Collection -- Reposition level 2 tables */
/* */
/* General idea is to relocate all level 2 tables as close to the */
/* beginning of the file as possible. This can help speed up, for */
/* example, chkdsk processing. */
/* */
/* If any level 2 tables reside outside of the bounds (that is, if */
/* any level 2 table could be moved closer to the beginning of the */
/* file) then first we relocate all track images within the bounds. */
/* Note that cckd_get_space will not allocate space within the */
/* the bounds for track images. Next we try to relocate all level 2 */
/* tables outside the bounds. This may take a few iterations for */
/* the freed space within the bounds to become non-pending. */
/* */
/* The bounds can change as level 2 tables are added or removed. */
/* cckd_read_l1 sets the bounds and they are adjusted by */
/* cckd_write_l2. */
/*-------------------------------------------------------------------*/
int cckd64_gc_l2(DEVBLK *dev, BYTE *buf)
{
#if 1 // (deprecate this function!)
UNREFERENCED( dev );
UNREFERENCED( buf );
return 0;
#else // (deprecate this function!)
CCKD64_EXT *cckd; /* -> cckd extension */
int sfx; /* Shadow file index */
int i, j; /* Work variables */
int trk; /* Track number */
int len; /* Track length */
U64 pos, fpos; /* File offsets */
if (!dev->cckd64)
return cckd_gc_l2( dev, buf );
cckd = dev->cckd_ext;
obtain_lock (&cckd->filelock);
sfx = cckd->sfn;
if (cckd->L2ok || cckd->cdevhdr[cckd->sfn].free_total == 0)
goto cckd_gc_l2_exit;
/* Find any level 2 table out of bounds */
for (i = 0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
if (cckd->L1tab[sfx][i] != CCKD64_NOSIZE && cckd->L1tab[sfx][i] != CCKD64_MAXSIZE
&& cckd->L2_bounds - CCKD64_L2TAB_SIZE < cckd->L1tab[sfx][i])
break;
/* Return OK if no l2 tables out of bounds */
if (i >= cckd->cdevhdr[sfx].num_L1tab)
goto cckd_gc_l2_exit_ok;
/* Relocate all track images within the bounds */
pos = CCKD64_L1TAB_POS + (cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE);
i = cckd->free_idx1st;
fpos = cckd->cdevhdr[sfx].free_off;
while (pos < cckd->L2_bounds)
{
if (i >= 0 && pos == fpos)
{
pos += cckd->ifb[i].ifb_len;
fpos = cckd->ifb[i].ifb_offnxt;
i = cckd->ifb[i].ifb_idxnxt;
j = 0;
}
else
{
for (j = 0; j < cckd->cdevhdr[sfx].num_L1tab; j++)
if (pos == cckd->L1tab[sfx][j])
{
pos += CCKD64_L2TAB_SIZE;
break;
}
}
if (j >= cckd->cdevhdr[sfx].num_L1tab)
{
/* Found a track to relocate */
if (cckd64_read (dev, sfx, pos, buf, CKD_TRKHDR_SIZE) < 0)
goto cckd_gc_l2_exit;
if ((trk = cckd64_cchh (dev, buf, -1)) < 0)
goto cckd_gc_l2_exit;
CCKD_TRACE( "gc_l2 relocate trk[%d] offset 0x%"PRIx64, trk, pos);
if ((len = cckd64_read_trkimg (dev, buf, trk, NULL)) < 0)
goto cckd_gc_l2_exit;
if (cckd64_write_trkimg (dev, buf, len, trk, CCKD_SIZE_EXACT) < 0)
goto cckd_gc_l2_exit;
/* Start over */
pos = CCKD64_L1TAB_POS + (cckd->cdevhdr[sfx].num_L1tab * CCKD64_L1ENT_SIZE);
i = cckd->free_idx1st;
fpos = cckd->cdevhdr[sfx].free_off;
}
}
do {
/* Find a level 2 table to relocate */
i = cckd->free_idx1st;
fpos = cckd->cdevhdr[sfx].free_off;
CCKD_TRACE( "gc_l2 first free[%d] pos 0x%"PRIx64" len %"PRId64" pending %d",
i, fpos, (U64)(i >= 0 ? cckd->ifb[i].ifb_len : -1),
(int)(i >= 0 ? cckd->ifb[i].ifb_pending : -1 ));
if (i < 0 || fpos >= cckd->L2_bounds || cckd->ifb[i].ifb_pending)
goto cckd_gc_l2_exit;
CCKD_TRACE( "gc_l2 bounds 0x%"PRIx64" sfx %d num_L1tab %d",
cckd->L2_bounds, sfx, cckd->cdevhdr[sfx].num_L1tab );
if ( cckd->ifb[i].ifb_len < CCKD64_L2TAB_SIZE
|| (cckd->ifb[i].ifb_len != CCKD64_L2TAB_SIZE
&& cckd->ifb[i].ifb_len < CCKD64_L2TAB_SIZE + CCKD64_FREEBLK_SIZE)
)
{
for (i=0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
{
if (fpos + cckd->ifb[i].ifb_len == cckd->L1tab[sfx][i])
break;
}
}
else
{
for (i=0; i < cckd->cdevhdr[sfx].num_L1tab; i++)
{
if (cckd->L2_bounds - CCKD64_L2TAB_SIZE < cckd->L1tab[sfx][i]
&& cckd->L1tab[sfx][i] != CCKD64_MAXSIZE)
break;
}
}
if (i < cckd->cdevhdr[sfx].num_L1tab)
{
CCKD_TRACE( "gc_l2 relocate l2[%d] pos 0x%"PRIx64,
i, cckd->L1tab[sfx][i] );
if (cckd64_read_l2( dev, sfx, i ) < 0)
goto cckd_gc_l2_exit;
if (cckd64_write_l2( dev ) < 0)
goto cckd_gc_l2_exit;
}
}
while (i < cckd->cdevhdr[sfx].num_L1tab);
cckd_gc_l2_exit:
release_lock (&cckd->filelock);
return 0;
cckd_gc_l2_exit_ok:
CCKD_TRACE( "gc_l2 ok%s", "" );
cckd->L2ok = 1;
goto cckd_gc_l2_exit;
#endif // (deprecate this function!)
}
/*-------------------------------------------------------------------*/
/* Uncompress a track image */
/*-------------------------------------------------------------------*/
BYTE *cckd64_uncompress (DEVBLK *dev, BYTE *from, int len, int maxlen,
int trk)
{
CCKD64_EXT *cckd;
BYTE *to = NULL; /* Uncompressed buffer */
int newlen; /* Uncompressed length */
BYTE comp; /* Compression type */
static char *compress[] = {"none", "zlib", "bzip2"};
cckd = dev->cckd_ext;
CCKD_TRACE( "uncompress comp %d len %d maxlen %d trk %d",
from[0] & CCKD_COMPRESS_MASK, len, maxlen, trk);
/* Extract compression type */
comp = (from[0] & CCKD_COMPRESS_MASK);
/* Get a buffer to uncompress into */
if (comp != CCKD_COMPRESS_NONE && cckd->newbuf == NULL)
{
cckd->newbuf = cckd_malloc (dev, "newbuf", maxlen);
if (cckd->newbuf == NULL)
return NULL;
}
/* Uncompress the track image */
switch (comp)
{
case CCKD_COMPRESS_NONE:
newlen = cckd_trklen (dev, from);
to = from;
break;
case CCKD_COMPRESS_ZLIB:
to = cckd->newbuf;
newlen = cckd_uncompress_zlib (dev, to, from, len, maxlen);
break;
case CCKD_COMPRESS_BZIP2:
to = cckd->newbuf;
newlen = cckd_uncompress_bzip2 (dev, to, from, len, maxlen);
break;
default:
newlen = -1;
break;
}
/* Validate the uncompressed track image */
newlen = cckd64_validate (dev, to, trk, newlen);
/* Return if successful */
if (newlen > 0)
{
if (to != from)
{
cckd->newbuf = from;
cckd->bufused = 1;
}
return to;
}
/* Get a buffer now if we haven't gotten one */
if (cckd->newbuf == NULL)
{
cckd->newbuf = cckd_malloc (dev, "newbuf2", maxlen);
if (cckd->newbuf == NULL)
return NULL;
}
/* Try each uncompression routine in turn */
/* uncompressed */
newlen = cckd_trklen (dev, from);
newlen = cckd64_validate (dev, from, trk, newlen);
if (newlen > 0)
return from;
/* zlib compression */
to = cckd->newbuf;
newlen = cckd_uncompress_zlib (dev, to, from, len, maxlen);
newlen = cckd64_validate (dev, to, trk, newlen);
if (newlen > 0)
{
cckd->newbuf = from;
cckd->bufused = 1;
return to;
}
/* bzip2 compression */
to = cckd->newbuf;
newlen = cckd_uncompress_bzip2 (dev, to, from, len, maxlen);
newlen = cckd64_validate (dev, to, trk, newlen);
if (newlen > 0)
{
cckd->newbuf = from;
cckd->bufused = 1;
return to;
}
// "%1d:%04X CCKD file[%d] %s: uncompress error trk %d: %2.2x%2.2x%2.2x%2.2x%2.2x"
WRMSG (HHC00343, "E",
LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn), trk,
from[0], from[1], from[2], from[3], from[4]);
if (comp & ~cckdblk.comps)
WRMSG (HHC00344, "E",
LCSS_DEVNUM, cckd->sfn, cckd_sf_name(dev, cckd->sfn), compress[comp]);
return NULL;
}