Files
open-simh.simtools/extracters/ods2/phyvhd.c
Timothe Litt 66e00b9900 Backlog of work since 2016
Too much to list all, but includes (in no particular order):
 - Cleanup for 64-bit builds, MSVC warnings.
 - Structured help
 - Help file compiler.
 - Supports volsets, writes/create work.
 - Support for I18n in messages, help.
 - Makefiles.
 - Initialize volume/volset
 - Command line editing/history

Builds and works on Linux and Windows (VS).
Not recently built or tested on other platforms, but
not intentinonally broken.
2022-10-10 11:00:20 -04:00

459 lines
14 KiB
C

/* Phyvhd.c Physical I/O for VHD format disks */
/* Timothe Litt March 2016
* Copyright (C) 2016 Timothe litt
* litt at acm dot org
*
* Free for use with the ODS2 package. All other rights reserved.
*/
/*
* This is distributed as part of ODS2, originally written by
* Paul Nankervis, email address: Paulnank@au1.ibm.com
*
* ODS2 is distributed freely for all members of the
* VMS community to use. However all derived works
* must maintain comments in their source to acknowledge
* the contributions of the original author and
* subsequent contributors. This is free software; no
* warranty is offered, and while we believe it to be useful,
* you use it at your own risk.
*/
#if !defined( DEBUG ) && defined( DEBUG_PHYVHD )
#define DEBUG DEBUG_PHYVHD
#else
#ifndef DEBUG
#define DEBUG 0
#endif
#endif
#ifndef phyvhd_BUFSIZE
#define phyvhd_BUFSIZE (10 * 512)
#endif
#ifndef phyvhd_MINSIZE
#define phyvhd_MINSIZE (0)
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <io.h>
#define close _close
#else
#include <unistd.h>
#define _aligned_free free
#endif
#include "compat.h"
#include "device.h"
#include "ods2.h"
#include "phyvirt.h"
#include "ssdef.h"
#include "stsdef.h"
#include "sysmsg.h"
#include "libvhd.h"
#include "phyvhd.h"
typedef vhd_context_t *vhd_contextp;
typedef vhd_context_t vhd_context;
#ifdef USE_LIBVHD
#include <dlfcn.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
typedef int (*vhd_open_p)( vhd_contextp ctx, const char *filename, int flags );
typedef void (*vhd_close_p)( vhd_contextp ctx );
typedef int (*vhd_create_p)(const char *file, uint64_t size,
int type, uint32_t flags );
typedef int vhd_header_decode_parent(vhd_contextp ctx,
vhd_header_t *header, char **buf);
typedef int (*vhd_snapshot_p)( const char *filename, const char *parent, uint64_t size,
const char *parentfile, uint32_t flags );
typedef int (*vhd_io_read_p)(vhd_contextp ctx, void *buf, uint64_t pbn, uint32_t count );
typedef int (*vhd_io_write_p)(vhd_contextp ctx, void *buf, uint64_t pbn, uint32_t count);
typedef void (*libvhd_set_log_level_p)(int);
typedef uint32_t (*vhd_chs_p)(uint64_t size);
/* *** */
static void *libvhd = NULL;
#define vhddeclare(s) s ## _p s ## _fcn = NULL
vhddeclare(vhd_open);
vhddeclare(vhd_close);
vhddeclare(vhd_create);
vhddeclare(libvhd_set_log_level);
vhddeclare(vhd_header_decode_parent);
vhddeclare(vhd_snapshot);
#if 0
vhddeclare(vhd_chs);
#endif
vhddeclare(vhd_io_read);
vhddeclare(vhd_io_write);
#define vhdresolve(s) do { \
if( libvhd != NULL && \
(s ## _fcn = (s ## _p)dlsym( libvhd, #s )) == NULL ) { \
printmsg( IO_VHDNOSYM, 0, #s, LIBVHDSO(USE_LIBVHD) ); \
dlclose( libvhd ); \
libvhd = NULL; \
} \
} while( 0 )
#else
#define vhd_open_fcn vhd_open
#define vhd_close_fcn vhd_close
#define vhd_create_fcn vhd_create
#define libvhd_set_log_level_fcn libvhd_set_log_level
#define vhd_header_decode_parent_fcn vhd_header_decode_parent
#define vhd_snapshot_fcn vhd_snapshot
#if 0
#define vhd_chs_fcn vhd_chs
#endif
#define vhd_io_read_fcn vhd_io_read
#define vhd_io_write_fcn vhd_io_write
#endif
static vmscond_t phyvhd_read( struct DEV *dev, uint32_t lbn, uint32_t length,
char *buffer );
static vmscond_t phyvhd_write( struct DEV *dev, uint32_t lbn, uint32_t length,
const char *buffer );
/*********************************************************** phyvhd_available() */
int phyvhd_available( int query ) {
#ifdef USE_LIBVHD
#define LIBVHDSOx(x) #x
#define LIBVHDSO(n) LIBVHDSOx(n)
if( libvhd == NULL ) {
if( (libvhd = dlopen( LIBVHDSO(USE_LIBVHD), RTLD_LAZY )) == NULL ) {
const char *err;
if( query )
printmsg( IO_VHDNOTAVAIL, MSG_TEXT );
printmsg( IO_VHDNOLIB, 0, LIBVHDSO(USE_LIBVHD) );
if( (err = dlerror()) != NULL )
printmsg( ODS2_OSERROR, MSG_CONTINUE, O_VHDNOLIB, err );
return FALSE;
}
vhdresolve( vhd_open );
vhdresolve( vhd_close );
vhdresolve( vhd_create );
vhdresolve( vhd_snapshot );
vhdresolve( libvhd_set_log_level );
vhdresolve( vhd_header_decode_parent );
vhdresolve( vhd_io_read );
vhdresolve( vhd_io_write );
if( libvhd == NULL ) {
printmsg( IO_VHDNOTVALID, MSG_CONTINUE,
IO_VHDNOSYM, LIBVHDSO(USE_LIBVHD) );
return FALSE;
}
}
#endif
if( query )
printmsg( IO_VHDAVAIL, MSG_TEXT );
return TRUE;
}
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 4996) /* MS complains about _open, which is open() */
#endif
/*********************************************************** phyvhd_snapshot() */
vmscond_t phyvhd_snapshot( const char *filename, const char *parent ) {
int fd, err;
char *p;
if( !( (p = strrchr( filename, '.')) != NULL && ( !strcmp( p, ".vhd" ) ||
!strcmp( p, ".VHD" ) ) ) ) {
return printmsg( IO_VHDONLY, 0, filename );
}
#ifdef USE_LIBVHD
if( libvhd == NULL && !phyvhd_available( FALSE ) )
return IO_VHDNOTVALID | STS$M_INHIB_MSG;
#endif
#ifdef _WIN32
fd = _open( filename, _O_BINARY | _O_RDWR | _O_CREAT | _O_EXCL, 0644 );
#else
fd = open( filename, O_RDWR | O_CREAT | O_EXCL, 0644 );
#endif
if( fd == -1 ) {
int err;
if( errno == EEXIST ) {
printmsg( IO_EXISTS, 0, filename );
return SS$_DUPFILENAME | STS$M_INHIB_MSG;
}
err = errno;
printmsg( IO_OPENDEV, 0, filename );
return printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_OPENDEV, strerror( err ) );
}
err = vhd_snapshot_fcn( filename, 0, parent, 0 );
close( fd );
if( err == 0 )
return SS$_NORMAL;
printmsg( IO_VHDSNAPFAIL, 0, parent );
return printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_VHDSNAPFAIL, strerror( -err ) );
}
/*********************************************************** phyvhd_init() */
vmscond_t phyvhd_init( struct DEV *dev ) {
int err;
char *fname;
unsigned status;
long align;
#ifdef _WIN32
SYSTEM_INFO inf;
GetSystemInfo( &inf );
align = inf.dwPageSize;
#elif defined( _SC_PAGESIZE)
align = sysconf( _SC_PAGESIZE );
#else
align = getpagesize();
#endif
#ifdef USE_LIBVHD
if( libvhd == NULL && !phyvhd_available( FALSE ) )
return IO_VHDNOTVALID | STS$M_INHIB_MSG;
#endif
libvhd_set_log_level_fcn( -1 );
fname = virt_lookup( dev->devnam );
if( fname == NULL )
return SS$_NOSUCHDEV;
dev->devread = phyvhd_read;
dev->devwrite = phyvhd_write;
#ifdef _WIN32
if( (dev->IoBuffer = _aligned_malloc( phyvhd_BUFSIZE, align )) == NULL ) {
return printmsg( IO_VHDMEM, 0 );
}
if( (dev->context = _aligned_malloc( sizeof( vhd_context ), align )) == NULL ) {
_aligned_free(dev->IoBuffer);
dev->IoBuffer = NULL;
return printmsg( IO_VHDMEM, 0 );
}
#else
if( posix_memalign( (void**)&dev->IoBuffer, align, phyvhd_BUFSIZE ) != 0 ) {
return printmsg( IO_VHDMEM, 0 );
}
if( posix_memalign( (void**)&dev->context, align,
sizeof( vhd_context ) ) != 0 ) {
free(dev->IoBuffer);
dev->IoBuffer = NULL;
return printmsg( IO_VHDMEM, 0 );
}
#endif
status = SS$_NORMAL;
do {
if( dev->access & PHY_CREATE ) {
disktypep_t dp;
uint64_t size;
int fd;
#ifdef _WIN32
fd = _open( fname, _O_BINARY | _O_RDWR | _O_CREAT | _O_EXCL, 0644 );
#else
fd = open( fname, O_RDWR | O_CREAT | O_EXCL, 0644 );
#endif
if( fd == -1 ) {
status = SS$_DUPFILENAME;
break;
}
if( dev->access & MOU_LOG )
printmsg( IO_VHDFMTSTART, 0, fname );
dp = dev->disktype;
size = ((uint64_t)dp->sectors) * dp->tracks * dp->cylinders * dp->sectorsize;
size = (size + 511) / 512;
size *= 512;
#if phyvhd_MINSIZE == 0
err = vhd_create_fcn( fname, size, HD_TYPE_DYNAMIC, 0 );
#else
err = vhd_create_fcn( fname, size, (size >= phyvhd_MINSIZE?
HD_TYPE_DYNAMIC: HD_TYPE_FIXED),
0 );
#endif
close( fd );
if( err != 0 ) {
printmsg( IO_VHDCREATEERR, 0, fname );
status = printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_VHDCREATEERR,
strerror( -err ) );
break;
}
if( dev->access & MOU_LOG )
printmsg( IO_VHDFMTEND, 0 );
}
err = vhd_open_fcn( dev->context, fname,
(dev->access & MOU_WRITE)?
VHD_OPEN_RDWR | VHD_OPEN_STRICT :
VHD_OPEN_RDONLY | VHD_OPEN_STRICT );
if( err != 0 && (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) {
dev->access &= ~MOU_WRITE;
err = vhd_open_fcn( dev->context, fname, VHD_OPEN_RDONLY | VHD_OPEN_STRICT );
}
if( err != 0 ) {
printmsg( IO_OPENDEV, 0, fname );
status = printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_OPENDEV,
strerror( -err ) );
break;
}
if( (dev->access & MOU_WRITE) && ((vhd_contextp)dev->context)->footer.saved ) {
printmsg( IO_VHDSAVED, 0, fname );
}
dev->eofptr = (off_t)(((vhd_contextp)dev->context)->footer.curr_size);
} while( 0 );
if( $FAILED(status) ) {
_aligned_free( dev->context );
_aligned_free( dev->IoBuffer );
dev->context =
dev->IoBuffer = NULL;
}
return status;
}
#ifdef _WIN32
#pragma warning(pop)
#endif
/*********************************************************** phyvhd_show() */
void phyvhd_show( struct DEV *dev, size_t column ) {
vhd_contextp ctx;
const char *type, *p;
size_t i, n;
if( (ctx = dev->context) == NULL )
return;
i = ctx->footer.type;
if( i > HD_TYPE_MAX )
type = "Unknown";
else
type = HD_TYPE_STR[i];
n = strlen( type );
if( (p = strchr( type, ' ' )) == NULL )
p = type + n;
n = (size_t) (p -type);
printf( " VHD %*.*s disk", (int)n, (int)n, type );
if( i == HD_TYPE_DIFF ) {
char *name;
#define PLABEL "parent: "
if( vhd_header_decode_parent_fcn( ctx, &ctx->header, &name ) == 0 ) {
printf( "\n%*sparent: %s", (int)(column < sizeof( PLABEL ) -1?
(sizeof( PLABEL )-1)-column :
column - (sizeof( PLABEL ) -1)),
"", name );
free( name );
}
}
return;
}
/*********************************************************** phyvhd_read() */
static vmscond_t phyvhd_read( struct DEV *dev, uint32_t lbn,
uint32_t length, char *buffer ) {
vmscond_t status;
int err;
status = SS$_NORMAL;
while( length > 0 ) {
uint32_t rdsize;
rdsize = (length + 511) / 512;
rdsize = ( (rdsize > phyvhd_BUFSIZE/512)?
phyvhd_BUFSIZE/512 : rdsize );
if( (err = vhd_io_read_fcn(dev->context, dev->IoBuffer, lbn, rdsize)) != 0 ) {
if( err == -ERANGE )
return printmsg( IO_RDBLK2BIG, 0, lbn );
printmsg( IO_READERR, 0, lbn );
return printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_READERR,
strerror( -err ) );
}
lbn += rdsize;
rdsize *= 512;
if( rdsize > length )
rdsize = length;
memcpy( buffer, dev->IoBuffer, rdsize );
buffer += rdsize;
length -= rdsize;
}
return status;
}
/*********************************************************** phyvhd_write() */
static vmscond_t phyvhd_write( struct DEV *dev, uint32_t lbn,
uint32_t length, const char *buffer ) {
vmscond_t status;
int err;
status = SS$_NORMAL;
while( length > 0 ) {
uint32_t wrsize, iosize;
iosize = (length + 511) / 512;
iosize = ( (iosize > phyvhd_BUFSIZE/512)?
phyvhd_BUFSIZE/512 : iosize );
wrsize = iosize * 512;
if( wrsize > length )
wrsize = length;
memcpy( dev->IoBuffer, buffer, wrsize );
buffer += wrsize;
length -= wrsize;
if( (err = vhd_io_write_fcn(dev->context, dev->IoBuffer, lbn, iosize)) != 0 ) {
if( err == -ERANGE )
return printmsg( IO_WRBLK2BIG, 0, lbn );
printmsg( IO_WRITEERR, 0, lbn );
return printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_WRITEERR,
strerror( -err ) );
}
lbn += iosize;
}
return status;
}
/*********************************************************** phyvhd_close() */
vmscond_t phyvhd_close( struct DEV *dev ) {
if( dev->context != NULL ) {
vhd_close_fcn( dev->context );
_aligned_free( dev->context );
dev->context = NULL;
}
_aligned_free( dev->IoBuffer );
dev->IoBuffer = NULL;
return SS$_NORMAL;
}