#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <sysexits.h>
#include <stdint.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/scsi/SCSITask.h>
#include <IOKit/scsi/SCSICmds_REPORT_LUNS_Definitions.h>
#include <IOKit/scsi/SCSICommandOperationCodes.h>
#include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h>
#include <CoreFoundation/CoreFoundation.h>
#include "AppleSCSIEmulatorAdapterUC.h"
#include "AppleSCSIEmulatorDefines.h"
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "Emulator"
#if DEBUG
#define PRINT(x) printf x
#else
#define PRINT(x)
#endif
#if DEBUG
#define DEBUG_ASSERT_MESSAGE(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
DebugAssert(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
static void
DebugAssert ( const char * componentNameString,
const char * assertionString,
const char * exceptionLabelString,
const char * errorString,
const char * fileName,
long lineNumber,
int errorCode )
{
if ( ( assertionString != NULL ) && ( *assertionString != '\0' ) )
printf ( "Assertion failed: %s: %s\n", componentNameString, assertionString );
else
printf ( "Check failed: %s:\n", componentNameString );
if ( exceptionLabelString != NULL )
printf ( " %s\n", exceptionLabelString );
if ( errorString != NULL )
printf ( " %s\n", errorString );
if ( fileName != NULL )
printf ( " file: %s\n", fileName );
if ( lineNumber != 0 )
printf ( " line: %ld\n", lineNumber );
if ( errorCode != 0 )
printf ( " error: %d\n", errorCode );
}
#endif
#include <AssertMacros.h>
#define kAppleSCSIEmulatorAdapterClassString "AppleSCSIEmulatorAdapter"
#define kIOSCSIParallelInterfaceDeviceString "IOSCSIParallelInterfaceDevice"
#define kIOSCSITargetDeviceString "IOSCSITargetDevice"
#define kIOSCSIHierarchicalLogicalUnitString "IOSCSIHierarchicalLogicalUnit"
#pragma pack(1)
typedef struct EmulatorSCSIInquiryPage00Data
{
UInt8 page00;
UInt8 page80;
UInt8 page83;
} EmulatorSCSIInquiryPage00Data;
typedef struct EmulatorSCSIInquiryPage83Data
{
SCSICmd_INQUIRY_Page83_Identification_Descriptor descriptor1;
UInt8 descriptor1bytes[7];
SCSICmd_INQUIRY_Page83_Identification_Descriptor descriptor2;
UInt8 descriptor2bytes[15];
} EmulatorSCSIInquiryPage83Data;
typedef struct EmulatorSCSIInquiryPage80Data
{
UInt8 serialBytes[31];
} EmulatorSCSIInquiryPage80Data;
typedef struct EmulatorSCSIInquiryPage00
{
SCSICmd_INQUIRY_Page00_Header header;
EmulatorSCSIInquiryPage00Data data;
} EmulatorSCSIInquiryPage00;
typedef struct EmulatorSCSIInquiryPage80
{
SCSICmd_INQUIRY_Page80_Header header;
EmulatorSCSIInquiryPage80Data data;
} EmulatorSCSIInquiryPage80;
typedef struct EmulatorSCSIInquiryPage83
{
SCSICmd_INQUIRY_Page83_Header header;
EmulatorSCSIInquiryPage83Data data;
} EmulatorSCSIInquiryPage83;
#pragma options align=reset
SCSICmd_INQUIRY_StandardData gInquiryData =
{
kINQUIRY_PERIPHERAL_TYPE_DirectAccessSBCDevice, 0, 5, 2, sizeof ( SCSICmd_INQUIRY_StandardData ) - 5, 0, 0, 0, "APPLE",
"SCSI Emulator",
"1.0",
};
static EmulatorSCSIInquiryPage00 gInquiryPage00Data =
{
0, kINQUIRY_Page00_PageCode, 0, sizeof ( EmulatorSCSIInquiryPage00Data ), kINQUIRY_Page00_PageCode,
kINQUIRY_Page80_PageCode,
kINQUIRY_Page83_PageCode
};
static EmulatorSCSIInquiryPage80 gInquiryPage80Data =
{
0, kINQUIRY_Page80_PageCode, 0, sizeof ( EmulatorSCSIInquiryPage80Data ) + 1, 'A',
'P',
'P',
'L',
'E',
' ',
'V',
'i',
'r',
't',
'u',
'a',
'l',
' ',
'L',
'U',
'N',
'0',
0
};
static EmulatorSCSIInquiryPage83 gInquiryPage83Data =
{
0, kINQUIRY_Page83_PageCode, 0, sizeof ( EmulatorSCSIInquiryPage83Data ), {
{ kINQUIRY_Page83_CodeSetBinaryData, kINQUIRY_Page83_AssociationTargetDevice | kINQUIRY_Page83_IdentifierTypeFCNameIdentifier, 0x00, 0x08, 0x50 },
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
},
{ kINQUIRY_Page83_CodeSetBinaryData, kINQUIRY_Page83_IdentifierTypeFCNameIdentifier, 0x00, 0x10, 0x60 },
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
}
},
};
static void
CreateTargetLUN (
SCSITargetIdentifier targetID,
SCSILogicalUnitNumber logicalUnit,
UInt64 capacity,
boolean_t unique );
static void
DestroyTargetLUN (
SCSITargetIdentifier targetID,
SCSILogicalUnitNumber logicalUnit );
static void
DestroyTarget (
SCSITargetIdentifier targetID );
static io_object_t
GetController ( void );
static void
PrintUsage ( void );
static void
PrintController ( io_object_t controller );
static void
PrintTarget ( io_object_t target );
static void
PrintLogicalUnit ( io_object_t logicalUnit );
static void
ReportInventory ( void );
int
main ( int argc, const char * argv[] )
{
boolean_t create = false;
boolean_t destroy = false;
boolean_t inventory = false;
boolean_t unique = true;
int64_t targetID = -1;
int64_t lun = -1;
uint64_t size = 0;
char c;
static struct option long_options [ ] =
{
{ "target", required_argument, 0, 't' },
{ "lun", required_argument, 0, 'l' },
{ "size", required_argument, 0, 's' },
{ "inventory", no_argument, 0, 'i' },
{ "create", no_argument, 0, 'c' },
{ "destroy", no_argument, 0, 'd' },
{ "help", no_argument, 0, 'h' },
{ "nounique", no_argument, 0, 'n' },
{ 0, 0, 0, 0 }
};
while ( ( c = getopt_long ( argc, ( char * const * ) argv, "t:l:s:icdhn?", long_options, NULL ) ) != -1 )
{
switch ( c )
{
case 't':
{
targetID = strtoull ( optarg, ( char ** ) NULL, 10 );
if ( ( targetID > 255 ) || ( targetID == kInitiatorID ) )
{
PRINT ( ( "Invalid targetID.\n" ) );
PrintUsage ( );
exit ( EX_USAGE );
}
}
break;
case 'l':
{
lun = strtoull ( optarg, ( char ** ) NULL, 10 );
if ( ( lun > 16383 ) || ( lun == 0 ) )
{
PRINT ( ( "Invalid LUN.\n" ) );
PrintUsage ( );
exit ( EX_USAGE );
}
}
break;
case 's':
{
char * expr;
size = strtoull ( optarg, &expr, 10 );
switch ( *expr )
{
case 'k':
size *= 1 << 10;
break;
case 'm':
size *= 1 << 20;
break;
case 'g':
size *= 1 << 30;
break;
default:
break;
}
if ( size % 512 )
{
PRINT ( ( "Invalid byte count. Must be a multiple of 512 bytes\n" ) );
PrintUsage ( );
exit ( EX_USAGE );
}
}
break;
case 'i':
{
if ( ( create ) || ( destroy ) )
{
PRINT ( ( "Create, Destroy, and Inventory are mutually exclusive.\n" ) );
PrintUsage ( );
exit ( EX_USAGE );
}
inventory = true;
}
break;
case 'c':
{
if ( ( inventory ) || ( destroy ) )
{
PRINT ( ( "Create, Destroy, and Inventory are mutually exclusive.\n" ) );
PrintUsage ( );
exit ( EX_USAGE );
}
create = true;
}
break;
case 'd':
{
if ( ( create ) || ( inventory ) )
{
PRINT ( ( "Create, Destroy, and Inventory are mutually exclusive.\n" ) );
PrintUsage ( );
exit ( EX_USAGE );
}
destroy = true;
}
break;
case 'n':
{
unique = false;
}
break;
case 'h':
default:
{
PrintUsage ( );
exit ( EX_USAGE );
}
break;
}
}
if ( inventory )
{
ReportInventory ( );
exit ( 0 );
}
if ( create )
{
if ( ( targetID == -1 ) || ( lun == -1 ) || ( size == 0 ) )
{
PrintUsage ( );
exit ( EX_USAGE );
}
CreateTargetLUN ( targetID, lun, size, unique );
}
else if ( destroy )
{
if ( targetID == -1 )
{
PrintUsage ( );
exit ( EX_USAGE );
}
if ( lun == -1 )
{
DestroyTarget ( targetID );
}
else
{
DestroyTargetLUN ( targetID, lun );
}
}
else
{
PrintUsage ( );
exit ( EX_USAGE );
}
return 0;
}
static void
CreateTargetLUN (
SCSITargetIdentifier targetID,
SCSILogicalUnitNumber logicalUnit,
UInt64 capacity,
boolean_t unique )
{
io_object_t controller = IO_OBJECT_NULL;
PRINT ( ( "CreateTargetLUN, targetID = %qd, logicalUnit = %qd, capacity = %qd\n", targetID, logicalUnit, capacity ) );
controller = GetController ( );
if ( controller != IO_OBJECT_NULL )
{
io_connect_t connection = IO_OBJECT_NULL;
IOReturn status = kIOReturnSuccess;
status = IOServiceOpen (
controller,
mach_task_self ( ),
kSCSIEmulatorAdapterUserClientConnection,
&connection );
if ( status == kIOReturnSuccess )
{
EmulatorTargetParamsStruct target;
EmulatorLUNParamsStruct lun;
size_t outCount = 0;
EmulatorSCSIInquiryPage80 page80 = gInquiryPage80Data;
EmulatorSCSIInquiryPage83 page83 = gInquiryPage83Data;
char serial[32];
bzero ( &target, sizeof ( EmulatorTargetParamsStruct ) );
bzero ( &lun, sizeof ( EmulatorLUNParamsStruct ) );
lun.logicalUnit = logicalUnit;
lun.capacity = capacity;
lun.inquiryData = ( mach_vm_address_t ) ( uintptr_t ) &gInquiryData;
lun.inquiryPage00Data = ( mach_vm_address_t ) ( uintptr_t ) &gInquiryPage00Data;
lun.inquiryPage80Data = ( mach_vm_address_t ) ( uintptr_t ) &page80;
lun.inquiryPage83Data = ( mach_vm_address_t ) ( uintptr_t ) &page83;
lun.inquiryDataLength = sizeof ( gInquiryData );
lun.inquiryPage00DataLength = sizeof ( gInquiryPage00Data );
lun.inquiryPage80DataLength = sizeof ( page80 );
lun.inquiryPage83DataLength = sizeof ( page83 );
target.targetID = targetID;
target.lun = lun;
PRINT ( ( "lun.inquiryData = %p\n", &gInquiryData ) );
PRINT ( ( "lun.inquiryData = %p\n", &gInquiryPage00Data ) );
PRINT ( ( "lun.inquiryData = %p\n", &page80 ) );
PRINT ( ( "lun.inquiryData = %p\n", &page83 ) );
PRINT ( ( "gInquiryPage00Data = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
gInquiryPage00Data.header.PERIPHERAL_DEVICE_TYPE,
gInquiryPage00Data.header.PAGE_CODE,
gInquiryPage00Data.header.RESERVED,
gInquiryPage00Data.header.PAGE_LENGTH,
gInquiryPage00Data.data.page00,
gInquiryPage00Data.data.page80,
gInquiryPage00Data.data.page83 ) );
PRINT ( ( "sizeof ( gInquiryData ) = %ld\n", sizeof ( gInquiryData ) ) );
PRINT ( ( "sizeof ( gInquiryPage00Data ) = %ld\n", sizeof ( gInquiryPage00Data ) ) );
PRINT ( ( "sizeof ( page80 ) = %ld\n", sizeof ( page80 ) ) );
PRINT ( ( "sizeof ( page83 ) = %ld\n", sizeof ( page83 ) ) );
PRINT ( ( "sizeof ( EmulatorTargetParamsStruct ) = %ld\n", sizeof ( EmulatorTargetParamsStruct ) ) );
PRINT ( ( "sizeof ( EmulatorLUNParamsStruct ) = %ld\n", sizeof ( EmulatorLUNParamsStruct ) ) );
snprintf ( serial, 32, "APPLE Virtual LUN %qd", logicalUnit );
bcopy ( serial, ( char * ) &page80.header.PRODUCT_SERIAL_NUMBER, 32 );
if ( unique )
{
int fd = -1;
char randomBytes[15];
int amount;
fd = open ( "/dev/random", O_RDONLY, 0 );
if ( fd == -1 )
{
PRINT ( ( "Open /dev/random failed\n" ) );
exit ( -1 );
}
amount = read ( fd, randomBytes, sizeof ( randomBytes ) );
if ( amount != sizeof ( randomBytes ) )
{
PRINT ( ( "Reading from /dev/random failed\n" ) );
exit ( -1 );
}
bcopy ( randomBytes, page83.data.descriptor2bytes, sizeof ( page83.data.descriptor2bytes ) );
close ( fd );
}
IOConnectCallStructMethod (
connection,
kUserClientCreateLUN,
&target,
sizeof ( EmulatorTargetParamsStruct ),
NULL,
&outCount );
IOServiceClose ( connection );
}
IOObjectRelease ( controller );
}
}
static void
DestroyTargetLUN (
SCSITargetIdentifier targetID,
SCSILogicalUnitNumber logicalUnit )
{
io_object_t controller = IO_OBJECT_NULL;
PRINT ( ( "DestroyTargetLUN, targetID = %qd, logicalUnit = %qd\n", targetID, logicalUnit ) );
controller = GetController ( );
if ( controller != IO_OBJECT_NULL )
{
io_connect_t connection = IO_OBJECT_NULL;
IOReturn status = kIOReturnSuccess;
status = IOServiceOpen (
controller,
mach_task_self ( ),
kSCSIEmulatorAdapterUserClientConnection,
&connection );
if ( status == kIOReturnSuccess )
{
uint32_t outCount = 0;
uint64_t params[2];
params[0] = targetID;
params[1] = logicalUnit;
IOConnectCallScalarMethod (
connection,
kUserClientDestroyLUN,
( const uint64_t * ) params,
2,
NULL,
&outCount );
IOServiceClose ( connection );
}
IOObjectRelease ( controller );
}
}
static void
DestroyTarget (
SCSITargetIdentifier targetID )
{
io_object_t controller = IO_OBJECT_NULL;
PRINT ( ( "DestroyTarget, targetID = %qd\n", targetID ) );
controller = GetController ( );
if ( controller != IO_OBJECT_NULL )
{
io_connect_t connection = IO_OBJECT_NULL;
IOReturn status = kIOReturnSuccess;
status = IOServiceOpen (
controller,
mach_task_self ( ),
kSCSIEmulatorAdapterUserClientConnection,
&connection );
if ( status == kIOReturnSuccess )
{
uint32_t outCount = 0;
uint64_t params[1];
params[0] = targetID;
IOConnectCallScalarMethod (
connection,
kUserClientDestroyTarget,
( const uint64_t * ) params,
1,
NULL,
&outCount );
IOServiceClose ( connection );
}
IOObjectRelease ( controller );
}
}
static io_object_t
GetController ( void )
{
io_object_t controller = IO_OBJECT_NULL;
controller = IOServiceGetMatchingService (
kIOMasterPortDefault,
IOServiceMatching ( kAppleSCSIEmulatorAdapterClassString ) );
return controller;
}
static void
ReportInventory ( void )
{
io_object_t controller = IO_OBJECT_NULL;
controller = GetController ( );
if ( controller != IO_OBJECT_NULL )
{
IOReturn result = kIOReturnSuccess;
io_iterator_t iterator = IO_OBJECT_NULL;
io_registry_entry_t child = IO_OBJECT_NULL;
PrintController ( controller );
result = IORegistryEntryCreateIterator ( controller, kIOServicePlane, kNilOptions, &iterator );
if ( result != kIOReturnSuccess )
{
goto ErrorExit;
}
child = IOIteratorNext ( iterator );
while ( child != IO_OBJECT_NULL )
{
if ( IOObjectConformsTo ( child, kIOSCSIParallelInterfaceDeviceString ) )
{
io_iterator_t iterator2 = IO_OBJECT_NULL;
result = IORegistryEntryCreateIterator ( child, kIOServicePlane, kNilOptions, &iterator2 );
if ( result == kIOReturnSuccess )
{
io_registry_entry_t grandchild = IO_OBJECT_NULL;
grandchild = IOIteratorNext ( iterator2 );
while ( grandchild != IO_OBJECT_NULL )
{
if ( IOObjectConformsTo ( grandchild, kIOSCSITargetDeviceString ) )
{
io_iterator_t iterator3 = IO_OBJECT_NULL;
PrintTarget ( grandchild );
result = IORegistryEntryCreateIterator ( grandchild, kIOServicePlane, kNilOptions, &iterator3 );
if ( result == kIOReturnSuccess )
{
io_registry_entry_t greatgrandchild = IO_OBJECT_NULL;
greatgrandchild = IOIteratorNext ( iterator3 );
while ( greatgrandchild != IO_OBJECT_NULL )
{
if ( IOObjectConformsTo ( greatgrandchild, kIOSCSIHierarchicalLogicalUnitString ) )
{
PrintLogicalUnit ( greatgrandchild );
}
IOObjectRelease ( greatgrandchild );
greatgrandchild = IOIteratorNext ( iterator3 );
}
IOObjectRelease ( iterator3 );
}
printf ( "-------------------------------------------------------------------------------\n" );
}
IOObjectRelease ( grandchild );
grandchild = IOIteratorNext ( iterator2 );
}
IOObjectRelease ( iterator2 );
}
}
IOObjectRelease ( child );
child = IOIteratorNext ( iterator );
}
IOObjectRelease ( iterator );
}
else
{
printf ( "No AppleSCSIEmulatorAdapter class found, please make sure the kext is loaded and try again.\n" );
}
ErrorExit:
IOObjectRelease ( controller );
}
static void
PrintController ( io_object_t controller )
{
CFNumberRef number;
CFDictionaryRef dict;
printf ( "Controller\n" );
dict = ( CFDictionaryRef ) IORegistryEntrySearchCFProperty ( controller, kIOServicePlane, CFSTR ( kIOPropertyProtocolCharacteristicsKey ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( dict != NULL )
{
number = ( CFNumberRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSIDomainIdentifierKey ) );
if ( number != NULL )
{
int domain = 0;
CFNumberGetValue ( number, kCFNumberIntType, &domain );
printf ( "\tDomain ID: %d\n", domain );
}
CFRelease ( dict );
dict = NULL;
}
}
static void
PrintTarget ( io_object_t target )
{
SCSITargetIdentifier targetID = 0;
CFNumberRef number;
CFDictionaryRef dict;
printf ( "-------------------------------------------------------------------------------\n" );
dict = ( CFDictionaryRef ) IORegistryEntrySearchCFProperty ( target, kIOServicePlane, CFSTR ( kIOPropertyProtocolCharacteristicsKey ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( dict != NULL )
{
number = ( CFNumberRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSITargetIdentifierKey ) );
if ( number != NULL )
{
CFNumberGetValue ( number, kCFNumberSInt64Type, &targetID );
printf ( "\nTargetDevice@%qd\n", targetID );
}
CFRelease ( dict );
dict = NULL;
}
}
static void
PrintLogicalUnit ( io_object_t logicalUnit )
{
CFNumberRef number = NULL;
CFDictionaryRef dict = NULL;
CFStringRef string = NULL;
dict = ( CFDictionaryRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertyProtocolCharacteristicsKey ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( dict != NULL )
{
#if USE_LUN_BYTES
CFDataRef data = NULL;
data = ( CFDataRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSILogicalUnitBytesKey ) );
if ( data != NULL )
{
const UInt8 * ptr = ( UInt8 * ) CFDataGetBytePtr ( data );
printf ( "\nLogicalUnit: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7] );
}
#else
number = ( CFNumberRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSILogicalUnitNumberKey ) );
if ( number != NULL )
{
UInt64 LUN = 0;
CFNumberGetValue ( number, kCFNumberLongLongType, &LUN );
printf ( "\nLogicalUnit: 0x%qd\n", LUN );
}
#endif
CFRelease ( dict );
dict = NULL;
}
string = ( CFStringRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIVendorIdentification ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( string != NULL )
{
printf ( "\tVendor: %s\n", CFStringGetCStringPtr ( string, kCFStringEncodingMacRoman ) );
CFRelease ( string );
string = NULL;
}
string = ( CFStringRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIProductIdentification ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( string != NULL )
{
printf ( "\tProduct: %s\n", CFStringGetCStringPtr ( string, kCFStringEncodingMacRoman ) );
CFRelease ( string );
string = NULL;
}
string = ( CFStringRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIProductRevisionLevel ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( string != NULL )
{
printf ( "\tProduct Revision Level: %s\n", CFStringGetCStringPtr ( string, kCFStringEncodingMacRoman ) );
CFRelease ( string );
string = NULL;
}
number = ( CFNumberRef )IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIPeripheralDeviceType ), kCFAllocatorDefault, kIORegistryIterateRecursively );
if ( number != NULL )
{
int pdt = 0;
CFNumberGetValue ( number, kCFNumberIntType, &pdt );
printf ( "\tPeripheral Device Type: %d\n", pdt );
CFRelease ( number );
number = NULL;
}
}
static void
PrintUsage ( void )
{
printf ( "Usage: emulator [--create, -c] [--destroy, -d] [--inventory, -i] [--target, -t] [--lun, -l] [--unique, -u] [--size, -s]\n" );
printf ( " --create and --destroy are mutually exclusive\n" );
printf ( " --target accepts targetIDs in the rang of [0...14][16...255]. ID 15 is reserved for the initiator.\n" );
printf ( " --lun accepts LUNs in the range of [1...16383] inclusive.\n" );
printf ( " --size can be in bytes, kilobytes, megabytes, or gigabytes, suffix usage similar to dd\n" );
printf ( " --unique is used to specify if the logical unit being created has a unique identifier in INQUIRY VPD Page 83h. If --unique is not used, the default (shared) INQUIRY VPD Page 83h identifier will be used\n" );
fflush ( stdout );
}