/* ************************************************* * Copyright 2007-2015 VMware, Inc. All rights reserved. * *************************************************/ /* * vixDiskUtil.cpp -- * * Helper program to create and mount vmdk files. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "vixDiskLib.h" #include "vixMntapi.h" using std::cout; using std::string; using std::endl; using std::vector; #define COMMAND_CREATE (1 << 0) #define COMMAND_DUMP (1 << 1) #define COMMAND_FILL (1 << 2) #define COMMAND_INFO (1 << 3) #define COMMAND_REDO (1 << 4) #define COMMAND_DUMP_META (1 << 5) #define COMMAND_READ_META (1 << 6) #define COMMAND_WRITE_META (1 << 7) #define COMMAND_MULTITHREAD (1 << 8) #define COMMAND_CLONE (1 << 9) #define COMMAND_READBENCH (1 << 10) #define COMMAND_WRITEBENCH (1 << 11) #define COMMAND_CHECKREPAIR (1 << 12) #define COMMAND_MOUNTDISK (1 << 13) #define COMMAND_CONVERT (1 << 14) #define VIXDISKLIB_VERSION_MAJOR 6 #define VIXDISKLIB_VERSION_MINOR 0 #define VIXMNTAPI_VERSION_MAJOR 1 #define VIXMNTAPI_VERSION_MINOR 0 // Default buffer size (in sectors) for read/write benchmarks #define DEFAULT_BUFSIZE 128 // Print updated statistics for read/write benchmarks roughly every // BUFS_PER_STAT sectors (current value is 64MBytes worth of data) #define BUFS_PER_STAT (128 * 1024) // Character array for random filename generation static const char randChars[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // Per-thread information for multi-threaded VixDiskLib test. struct ThreadData { std::string dstDisk; VixDiskLibHandle srcHandle; VixDiskLibHandle dstHandle; VixDiskLibSectorType numSectors; }; static struct { int command; VixDiskLibAdapterType adapterType; VixDiskLibDiskType diskType; char *transportModes; char *diskPath; char *parentPath; char *metaKey; char *metaVal; int filler; unsigned mbSize; VixDiskLibSectorType numSectors; VixDiskLibSectorType startSector; VixDiskLibSectorType bufSize; uint32 openFlags; unsigned numThreads; Bool success; Bool isRemote; char *host; char *userName; char *password; char *thumbPrint; int port; char *srcPath; VixDiskLibConnection connection; char *vmxSpec; bool useInitEx; char *cfgFile; char *libdir; char *ssMoRef; int repair; } appGlobals; static int ParseArguments(int argc, char* argv[]); static void DoCreate(void); static void DoRedo(void); static void DoFill(void); static void DoDump(void); static void DoMountDisk(void); static void DoReadMetadata(void); static void DoWriteMetadata(void); static void DoDumpMetadata(void); static void DoInfo(void); static void DoTestMultiThread(void); static void DoClone(void); static void DoConvert(void); static int BitCount(int number); static void DumpBytes(const uint8 *buf, size_t n, int step); static void DoRWBench(bool read); static void DoCheckRepair(Bool repair); #define THROW_ERROR(vixError) \ throw VixDiskLibErrWrapper((vixError), __FILE__, __LINE__) #define CHECK_AND_THROW(vixError) \ do { \ if (VIX_FAILED((vixError))) { \ throw VixDiskLibErrWrapper((vixError), __FILE__, __LINE__); \ } \ } while (0) /* *---------------------------------------------------------------------- * * GenerateRandomFilename -- * * Generate and return a random filename. * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GenerateRandomFilename(const string& prefix, string& randomFilename) { string retStr; int strLen = sizeof(randChars) - 1; for (unsigned int i = 0; i < 8; i++) { retStr += randChars[rand() % strLen]; } randomFilename = prefix + retStr; } /* *-------------------------------------------------------------------------- * * LogFunc -- * * Callback for VixDiskLib Log messages. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void LogFunc(const char *fmt, va_list args) { printf("Log: "); vprintf(fmt, args); } /* *-------------------------------------------------------------------------- * * WarnFunc -- * * Callback for VixDiskLib Warning messages. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void WarnFunc(const char *fmt, va_list args) { printf("Warning: "); vprintf(fmt, args); } /* *-------------------------------------------------------------------------- * * PanicFunc -- * * Callback for VixDiskLib Panic messages. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void PanicFunc(const char *fmt, va_list args) { printf("Panic: "); vprintf(fmt, args); exit(10); } typedef void (VixDiskLibGenericLogFunc)(const char *fmt, va_list args); // Wrapper class for VixDiskLib disk objects. class VixDiskLibErrWrapper { public: explicit VixDiskLibErrWrapper(VixError errCode, const char* file, int line) : _errCode(errCode), _file(file), _line(line) { char* msg = VixDiskLib_GetErrorText(errCode, NULL); _desc = msg; VixDiskLib_FreeErrorText(msg); } VixDiskLibErrWrapper(const char* description, const char* file, int line) : _errCode(VIX_E_FAIL), _desc(description), _file(file), _line(line) { } string Description() const { return _desc; } VixError ErrorCode() const { return _errCode; } string File() const { return _file; } int Line() const { return _line; } private: VixError _errCode; string _desc; string _file; int _line; }; class VixDisk { public: VixDiskLibHandle Handle() { return _handle; } VixDisk(VixDiskLibConnection connection, char *path, uint32 flags) { _handle = NULL; VixError vixError = VixDiskLib_Open(connection, path, flags, &_handle); CHECK_AND_THROW(vixError); printf("Disk \"%s\" is open using transport mode \"%s\".\n", path, VixDiskLib_GetTransportMode(_handle)); } ~VixDisk() { if (_handle) { VixDiskLib_Close(_handle); } _handle = NULL; } private: VixDiskLibHandle _handle; }; /* *-------------------------------------------------------------------------- * * PrintUsage -- * * Displays the usage message. * * Results: * 1. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static int PrintUsage(void) { printf("Usage: vixdiskutil command [options] diskPath\n\n"); printf("List of commands (all commands are mutually exclusive):\n"); printf(" -create : creates a sparse virtual disk with capacity " "specified by -cap\n"); printf(" -redo parentPath : creates a redo log 'diskPath' " "for base disk 'parentPath'\n"); printf(" -info : displays information for specified virtual disk\n"); printf(" -dump : dumps the contents of specified range of sectors " "in hexadecimal\n"); printf(" -fill : fills specified range of sectors with byte value " "specified by -val\n"); printf(" -mount : mounts the disk on to local path\n"); printf(" -wmeta key value : writes (key,value) entry into disk's metadata table\n"); printf(" -rmeta key : displays the value of the specified metada entry\n"); printf(" -meta : dumps all entries of the disk's metadata\n"); printf(" -clone sourcePath : clone source vmdk possibly to a remote site\n"); printf(" -convert sourcePath : convert source raw image to a vmdk\n"); printf(" -readbench blocksize: Does a read benchmark on a disk using the \n"); printf("specified I/O block size (in sectors).\n"); printf(" -writebench blocksize: Does a write benchmark on a disk using the\n"); printf("specified I/O block size (in sectors). WARNING: This will\n"); printf("overwrite the contents of the disk specified.\n"); printf(" -check repair: Check a sparse disk for internal consistency, " "where repair is a boolean value to indicate if a repair operation " "should be attempted.\n\n"); printf("options:\n"); printf(" -adapter [ide|scsi|lsilogic] : bus adapter type for 'create' option " "(default='scsi')\n"); printf(" -disktype [1-6] : virtual disk type for 'create' option" "(default='VIXDISKLIB_DISK_MONOLITHIC_SPARSE')\n"); printf(" 1 - VIXDISKLIB_DISK_MONOLITHIC_SPARSE \n"); printf(" 2 - VIXDISKLIB_DISK_MONOLITHIC_FLAT \n"); printf(" 3 - VIXDISKLIB_DISK_SPLIT_SPARSE \n"); printf(" 4 - VIXDISKLIB_DISK_SPLIT_FLAT \n"); printf(" 5 - VIXDISKLIB_DISK_VMFS_FLAT \n"); printf(" 6 - VIXDISKLIB_DISK_STREAM_OPTIMIZED \n"); printf(" -start n : start sector for 'dump/fill' options (default=0)\n"); printf(" -count n : number of sectors for 'dump/fill' options (default=1)\n"); printf(" -val byte : byte value to fill with for 'write' option (default=255)\n"); printf(" -cap megabytes : capacity in MB for -create option (default=100)\n"); printf(" -single : open file as single disk link (default=open entire chain)\n"); printf(" -multithread n: start n threads and copy the file to n new files\n"); printf(" -host hostname : hostname/IP address of VC/vSphere host (Mandatory)\n"); printf(" -user userid : user name on host (Mandatory) \n"); printf(" -password password : password on host. (Mandatory)\n"); printf(" -port port : port to use to connect to host (default = 443) \n"); printf(" -vm moref=id : id is the managed object reference of the VM \n"); printf(" -libdir dir : Folder location of the VDDK installation. " "On Windows, the bin folder holds the plugin. On Linux, it is " "the lib64 directory\n"); printf(" -initex configfile : Specify path and filename of config file \n"); printf(" -ssmoref moref : Managed object reference of VM snapshot \n"); printf(" -mode mode : Mode string to pass into VixDiskLib_ConnectEx. " "Valid modes are: nbd, nbdssl, san, hotadd \n"); printf(" -thumb string : Provides a SSL thumbprint string for validation. " "Format: xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx\n"); return 1; } /* *-------------------------------------------------------------------------- * * main -- * * Main routine of the program. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ int main(int argc, char* argv[]) { int retval; bool bVixInit(false); memset(&appGlobals, 0, sizeof appGlobals); appGlobals.command = 0; appGlobals.adapterType = VIXDISKLIB_ADAPTER_SCSI_LSILOGIC; appGlobals.diskType = VIXDISKLIB_DISK_MONOLITHIC_SPARSE; appGlobals.startSector = 0; appGlobals.numSectors = 1; appGlobals.mbSize = 100; appGlobals.filler = 0xff; appGlobals.openFlags = 0; appGlobals.numThreads = 1; appGlobals.success = TRUE; appGlobals.isRemote = FALSE; appGlobals.cfgFile = NULL; retval = ParseArguments(argc, argv); if (retval) { return retval; } // Initialize random generator struct timeval time; gettimeofday(&time, NULL); srand((time.tv_sec * 1000) + (time.tv_usec/1000)); VixDiskLibConnectParams cnxParams = {0}; VixError vixError; try { if (appGlobals.isRemote) { cnxParams.vmxSpec = appGlobals.vmxSpec; cnxParams.serverName = appGlobals.host; cnxParams.credType = VIXDISKLIB_CRED_UID; cnxParams.creds.uid.userName = appGlobals.userName; cnxParams.creds.uid.password = appGlobals.password; cnxParams.thumbPrint = appGlobals.thumbPrint; cnxParams.port = appGlobals.port; } if (appGlobals.useInitEx) { vixError = VixDiskLib_InitEx(VIXDISKLIB_VERSION_MAJOR, VIXDISKLIB_VERSION_MINOR, &LogFunc, &WarnFunc, &PanicFunc, appGlobals.libdir, appGlobals.cfgFile); } else { vixError = VixDiskLib_Init(VIXDISKLIB_VERSION_MAJOR, VIXDISKLIB_VERSION_MINOR, NULL, NULL, NULL, // Log, warn, panic appGlobals.libdir); } CHECK_AND_THROW(vixError); bVixInit = true; if (appGlobals.vmxSpec != NULL) { vixError = VixDiskLib_PrepareForAccess(&cnxParams, "Sample"); CHECK_AND_THROW(vixError); } if (appGlobals.ssMoRef == NULL && appGlobals.transportModes == NULL) { vixError = VixDiskLib_Connect(&cnxParams, &appGlobals.connection); } else { Bool ro = (appGlobals.openFlags & VIXDISKLIB_FLAG_OPEN_READ_ONLY); vixError = VixDiskLib_ConnectEx(&cnxParams, ro, appGlobals.ssMoRef, appGlobals.transportModes, &appGlobals.connection); } CHECK_AND_THROW(vixError); if (appGlobals.command & COMMAND_INFO) { DoInfo(); } else if (appGlobals.command & COMMAND_CREATE) { DoCreate(); } else if (appGlobals.command & COMMAND_REDO) { DoRedo(); } else if (appGlobals.command & COMMAND_FILL) { DoFill(); } else if (appGlobals.command & COMMAND_DUMP) { DoDump(); } else if (appGlobals.command & COMMAND_READ_META) { DoReadMetadata(); } else if (appGlobals.command & COMMAND_WRITE_META) { DoWriteMetadata(); } else if (appGlobals.command & COMMAND_DUMP_META) { DoDumpMetadata(); } else if (appGlobals.command & COMMAND_MULTITHREAD) { DoTestMultiThread(); } else if (appGlobals.command & COMMAND_CLONE) { DoClone(); } else if (appGlobals.command & COMMAND_READBENCH) { DoRWBench(true); } else if (appGlobals.command & COMMAND_WRITEBENCH) { DoRWBench(false); } else if (appGlobals.command & COMMAND_CHECKREPAIR) { DoCheckRepair(appGlobals.repair); } else if (appGlobals.command & COMMAND_MOUNTDISK) { DoMountDisk(); } else if (appGlobals.command & COMMAND_CONVERT) { DoConvert(); } retval = 0; } catch (const VixDiskLibErrWrapper& e) { cout << "Error: [" << e.File() << ":" << e.Line() << "] " << std::hex << e.ErrorCode() << " " << e.Description() << "\n"; retval = 1; } if (appGlobals.vmxSpec != NULL) { vixError = VixDiskLib_EndAccess(&cnxParams, "Sample"); } if (appGlobals.connection != NULL) { VixDiskLib_Disconnect(appGlobals.connection); } if (bVixInit) { VixDiskLib_Exit(); } return retval; } /* *-------------------------------------------------------------------------- * * ParseArguments -- * * Parses the arguments passed on the command line. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static int ParseArguments(int argc, char* argv[]) { int i; if (argc < 3) { printf("Error: Too few arguments. See usage below.\n\n"); return PrintUsage(); } for (i = 1; i < argc - 1; i++) { if (!strcmp(argv[i], "-info")) { appGlobals.command |= COMMAND_INFO; appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY; } else if (!strcmp(argv[i], "-create")) { appGlobals.command |= COMMAND_CREATE; } else if (!strcmp(argv[i], "-dump")) { appGlobals.command |= COMMAND_DUMP; appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY; } else if (!strcmp(argv[i], "-fill")) { appGlobals.command |= COMMAND_FILL; } else if (!strcmp(argv[i], "-meta")) { appGlobals.command |= COMMAND_DUMP_META; appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY; } else if (!strcmp(argv[i], "-single")) { appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_SINGLE_LINK; } else if (!strcmp(argv[i], "-adapter")) { if (i >= argc - 2) { printf("Error: The -adapter option requires the adapter type " "to be specified. The type must be 'ide' or 'scsi'. " "See usage below.\n\n"); return PrintUsage(); } ++i; printf("%s\n", argv[i]); if (strcmp(argv[i], "ide") == 0) { appGlobals.adapterType = VIXDISKLIB_ADAPTER_IDE; } else if (strcmp(argv[i], "scsi") == 0) { appGlobals.adapterType = VIXDISKLIB_ADAPTER_SCSI_BUSLOGIC; } else { appGlobals.adapterType = VIXDISKLIB_ADAPTER_SCSI_LSILOGIC; } } else if (!strcmp(argv[i], "-disktype")) { if (i >= argc - 2) { printf("Error: The -disktype option requires the disk type " "to be specified. See usage below.\n\n"); return PrintUsage(); } ++i; int type = atoi(argv[i]); if (type >= 1 && type <= 6) { appGlobals.diskType = (VixDiskLibDiskType)(type); } } else if (!strcmp(argv[i], "-mount")) { appGlobals.command |= COMMAND_MOUNTDISK; } else if (!strcmp(argv[i], "-rmeta")) { appGlobals.command |= COMMAND_READ_META; if (i >= argc - 2) { printf("Error: The -rmeta command requires a key value to " "be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.metaKey = argv[++i]; appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY; } else if (!strcmp(argv[i], "-wmeta")) { appGlobals.command |= COMMAND_WRITE_META; if (i >= argc - 3) { printf("Error: The -wmeta command requires key and value to " "be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.metaKey = argv[++i]; appGlobals.metaVal = argv[++i]; } else if (!strcmp(argv[i], "-redo")) { if (i >= argc - 2) { printf("Error: The -redo command requires the parentPath to " "be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.command |= COMMAND_REDO; appGlobals.parentPath = argv[++i]; } else if (!strcmp(argv[i], "-val")) { if (i >= argc - 2) { printf("Error: The -val option requires a byte value to " "be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.filler = strtol(argv[++i], NULL, 0); } else if (!strcmp(argv[i], "-start")) { if (i >= argc - 2) { printf("Error: The -start option requires a sector number to " "be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.startSector = strtol(argv[++i], NULL, 0); } else if (!strcmp(argv[i], "-count")) { if (i >= argc - 2) { printf("Error: The -count option requires the number of " "sectors to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.numSectors = strtol(argv[++i], NULL, 0); } else if (!strcmp(argv[i], "-cap")) { if (i >= argc - 2) { printf("Error: The -cap option requires the capacity in MB " "to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.mbSize = strtol(argv[++i], NULL, 0); } else if (!strcmp(argv[i], "-clone")) { if (i >= argc - 2) { printf("Error: The -clone command requires the path of the " "source vmdk to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.srcPath = argv[++i]; appGlobals.command |= COMMAND_CLONE; } else if (!strcmp(argv[i], "-convert")) { if (i >= argc - 2) { printf("Error: The -convert command requires the path of the " "source raw image to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.srcPath = argv[++i]; appGlobals.command |= COMMAND_CONVERT; } else if (!strcmp(argv[i], "-readbench")) { if (0 && i >= argc - 2) { printf("Error: The -readbench command requires a block size " "(in sectors) to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.bufSize = strtol(argv[++i], NULL, 0); appGlobals.command |= COMMAND_READBENCH; appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY; } else if (!strcmp(argv[i], "-writebench")) { if (i >= argc - 2) { printf("Error: The -writebench command requires a block size " "(in sectors) to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.bufSize = strtol(argv[++i], NULL, 0); appGlobals.command |= COMMAND_WRITEBENCH; } else if (!strcmp(argv[i], "-multithread")) { if (i >= argc - 2) { printf("Error: The -multithread option requires the number " "of threads to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.command |= COMMAND_MULTITHREAD; appGlobals.numThreads = strtol(argv[++i], NULL, 0); appGlobals.openFlags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY; } else if (!strcmp(argv[i], "-host")) { if (i >= argc - 2) { printf("Error: The -host option requires the IP address " "or name of the host to be specified. " "See usage below.\n\n"); return PrintUsage(); } appGlobals.host = argv[++i]; appGlobals.isRemote = TRUE; } else if (!strcmp(argv[i], "-user")) { if (i >= argc - 2) { printf("Error: The -user option requires a username " "to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.userName = argv[++i]; appGlobals.isRemote = TRUE; } else if (!strcmp(argv[i], "-password")) { if (i >= argc - 2) { printf("Error: The -password option requires a password " "to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.password = argv[++i]; appGlobals.isRemote = TRUE; } else if (!strcmp(argv[i], "-thumb")) { if (i >= argc - 2) { printf("Error: The -thumb option requires an SSL thumbprint " "to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.thumbPrint = argv[++i]; appGlobals.isRemote = TRUE; } else if (!strcmp(argv[i], "-port")) { if (i >= argc - 2) { printf("Error: The -port option requires the host's port " "number to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.port = strtol(argv[++i], NULL, 0); appGlobals.isRemote = TRUE; } else if (!strcmp(argv[i], "-vm")) { if (i >= argc - 2) { printf("Error: The -vm option requires the moref id of " "the vm to be specified. See usage below.\n\n"); return PrintUsage(); } appGlobals.vmxSpec = argv[++i]; appGlobals.isRemote = TRUE; } else if (!strcmp(argv[i], "-libdir")) { if (i >= argc - 2) { printf("Error: The -libdir option requires the folder location " "of the VDDK installation to be specified. " "See usage below.\n\n"); return PrintUsage(); } appGlobals.libdir = argv[++i]; } else if (!strcmp(argv[i], "-initex")) { if (i >= argc - 2) { printf("Error: The -initex option requires the path and filename " "of the VDDK config file to be specified. " "See usage below.\n\n"); return PrintUsage(); } appGlobals.useInitEx = true; appGlobals.cfgFile = argv[++i]; if (appGlobals.cfgFile[0] == '\0') { appGlobals.cfgFile = NULL; } } else if (!strcmp(argv[i], "-ssmoref")) { if (i >= argc - 2) { printf("Error: The -ssmoref option requires the moref id " "of a VM snapshot to be specified. " "See usage below.\n\n"); return PrintUsage(); } appGlobals.ssMoRef = argv[++i]; } else if (!strcmp(argv[i], "-mode")) { if (i >= argc - 2) { printf("Error: The -mode option requires a mode string to " "connect to VixDiskLib_ConnectEx. Valid modes are " "'nbd', 'nbdssl', 'san' and 'hotadd'. " "See usage below.\n\n"); return PrintUsage(); } appGlobals.transportModes = argv[++i]; } else if (!strcmp(argv[i], "-check")) { if (i >= argc - 2) { printf("Error: The -check command requires a true or false " "value to indicate if a repair operation should be " "attempted. See usage below.\n\n"); return PrintUsage(); } appGlobals.command |= COMMAND_CHECKREPAIR; appGlobals.repair = strtol(argv[++i], NULL, 0); } else { printf("Error: Unknown command or option: %s\n", argv[i]); return PrintUsage(); } } appGlobals.diskPath = argv[i]; if (BitCount(appGlobals.command) != 1) { printf("Error: Missing command. See usage below.\n"); return PrintUsage(); } if (appGlobals.isRemote) { if (appGlobals.host == NULL || appGlobals.userName == NULL || appGlobals.password == NULL) { printf("Error: Missing a mandatory option. "); printf("-host, -user and -password must be specified. "); printf("See usage below.\n"); return PrintUsage(); } } /* * TODO: More error checking for params, really */ return 0; } /* *-------------------------------------------------------------------------- * * DoInfo -- * * Queries the information of a virtual disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoInfo(void) { VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); VixDiskLibInfo *info = NULL; VixError vixError; vixError = VixDiskLib_GetInfo(disk.Handle(), &info); CHECK_AND_THROW(vixError); cout << "capacity = " << info->capacity << " sectors" << endl; cout << "number of links = " << info->numLinks << endl; cout << "adapter type = "; switch (info->adapterType) { case VIXDISKLIB_ADAPTER_IDE: cout << "IDE" << endl; break; case VIXDISKLIB_ADAPTER_SCSI_BUSLOGIC: cout << "BusLogic SCSI" << endl; break; case VIXDISKLIB_ADAPTER_SCSI_LSILOGIC: cout << "LsiLogic SCSI" << endl; break; default: cout << "unknown" << endl; break; } cout << "BIOS geometry = " << info->biosGeo.cylinders << "/" << info->biosGeo.heads << "/" << info->biosGeo.sectors << endl; cout << "physical geometry = " << info->physGeo.cylinders << "/" << info->physGeo.heads << "/" << info->physGeo.sectors << endl; VixDiskLib_FreeInfo(info); cout << "Transport modes supported by vixDiskLib: " << VixDiskLib_ListTransportModes() << endl; } /* *-------------------------------------------------------------------------- * * DoCreate -- * * Creates a virtual disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoCreate(void) { VixDiskLibCreateParams createParams; VixError vixError; createParams.adapterType = appGlobals.adapterType; createParams.capacity = appGlobals.mbSize * 2048; createParams.diskType = appGlobals.diskType; createParams.hwVersion = VIXDISKLIB_HWVERSION_WORKSTATION_5; vixError = VixDiskLib_Create(appGlobals.connection, appGlobals.diskPath, &createParams, NULL, NULL); CHECK_AND_THROW(vixError); } /* *-------------------------------------------------------------------------- * * DoRedo -- * * Creates a child disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoRedo(void) { VixError vixError; VixDisk parentDisk(appGlobals.connection, appGlobals.parentPath, 0); vixError = VixDiskLib_CreateChild(parentDisk.Handle(), appGlobals.diskPath, VIXDISKLIB_DISK_MONOLITHIC_SPARSE, NULL, NULL); CHECK_AND_THROW(vixError); } /* *-------------------------------------------------------------------------- * * DoFill -- * * Writes to a virtual disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoFill(void) { VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); uint8 buf[VIXDISKLIB_SECTOR_SIZE]; VixDiskLibSectorType startSector; memset(buf, appGlobals.filler, sizeof buf); for (startSector = 0; startSector < appGlobals.numSectors; ++startSector) { VixError vixError; vixError = VixDiskLib_Write(disk.Handle(), appGlobals.startSector + startSector, 1, buf); CHECK_AND_THROW(vixError); } } /* *-------------------------------------------------------------------------- * * DoReadMetadata -- * * Reads metadata from a virtual disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoReadMetadata(void) { size_t requiredLen; VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); VixError vixError = VixDiskLib_ReadMetadata(disk.Handle(), appGlobals.metaKey, NULL, 0, &requiredLen); if (vixError != VIX_OK && vixError != VIX_E_BUFFER_TOOSMALL) { THROW_ERROR(vixError); } std::vector val(requiredLen); vixError = VixDiskLib_ReadMetadata(disk.Handle(), appGlobals.metaKey, &val[0], requiredLen, NULL); CHECK_AND_THROW(vixError); cout << appGlobals.metaKey << " = " << &val[0] << endl; } /* *-------------------------------------------------------------------------- * * DoWriteMetadata -- * * Writes metadata in a virtual disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoWriteMetadata(void) { VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); VixError vixError = VixDiskLib_WriteMetadata(disk.Handle(), appGlobals.metaKey, appGlobals.metaVal); CHECK_AND_THROW(vixError); } /* *-------------------------------------------------------------------------- * * DoDumpMetadata -- * * Dumps all the metadata. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoDumpMetadata(void) { VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); char *key; size_t requiredLen; VixError vixError = VixDiskLib_GetMetadataKeys(disk.Handle(), NULL, 0, &requiredLen); if (vixError != VIX_OK && vixError != VIX_E_BUFFER_TOOSMALL) { THROW_ERROR(vixError); } std::vector buf(requiredLen); vixError = VixDiskLib_GetMetadataKeys(disk.Handle(), &buf[0], requiredLen, NULL); CHECK_AND_THROW(vixError); key = &buf[0]; while (*key) { vixError = VixDiskLib_ReadMetadata(disk.Handle(), key, NULL, 0, &requiredLen); if (vixError != VIX_OK && vixError != VIX_E_BUFFER_TOOSMALL) { THROW_ERROR(vixError); } std::vector val(requiredLen); vixError = VixDiskLib_ReadMetadata(disk.Handle(), key, &val[0], requiredLen, NULL); CHECK_AND_THROW(vixError); cout << key << " = " << &val[0] << endl; key += (1 + strlen(key)); } } /* *-------------------------------------------------------------------------- * * DoDump -- * * Dumps the content of a virtual disk. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static void DoDump(void) { VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); uint8 buf[VIXDISKLIB_SECTOR_SIZE]; VixDiskLibSectorType i; for (i = 0; i < appGlobals.numSectors; i++) { VixError vixError = VixDiskLib_Read(disk.Handle(), appGlobals.startSector + i, 1, buf); CHECK_AND_THROW(vixError); DumpBytes(buf, sizeof buf, 16); } } /* *-------------------------------------------------------------------------- * * BitCount -- * * Counts all the bits set in an int. * * Results: * Number of bits set to 1. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static int BitCount(int number) // IN { int bits = 0; while (number) { number = number & (number - 1); bits++; } return bits; } /* *---------------------------------------------------------------------- * * DumpBytes -- * * Displays an array of n bytes. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DumpBytes(const unsigned char *buf, // IN size_t n, // IN int step) // IN { size_t lines = n / step; size_t i; for (i = 0; i < lines; i++) { int k, last; printf("%04"FMTSZ"x : ", i * step); for (k = 0; n != 0 && k < step; k++, n--) { printf("%02x ", buf[i * step + k]); } printf(" "); last = k; while (k --) { unsigned char c = buf[i * step + last - k - 1]; if (c < ' ' || c >= 127) { c = '.'; } printf("%c", c); } printf("\n"); } printf("\n"); } /* *---------------------------------------------------------------------- * * CopyThread -- * * Copies a source disk to the given file. * * Results: * 0 if succeeded, 1 if not. * * Side effects: * Creates a new disk; sets appGlobals.success to false if fails * *---------------------------------------------------------------------- */ #define TASK_OK ((void*)0) #define TASK_FAIL ((void*)1) static void * CopyThread(void *arg) { ThreadData *td = (ThreadData *)arg; try { VixDiskLibSectorType i; VixError vixError; uint8 buf[VIXDISKLIB_SECTOR_SIZE]; for (i = 0; i < td->numSectors; i ++) { vixError = VixDiskLib_Read(td->srcHandle, i, 1, buf); CHECK_AND_THROW(vixError); vixError = VixDiskLib_Write(td->dstHandle, i, 1, buf); CHECK_AND_THROW(vixError); } } catch (const VixDiskLibErrWrapper& e) { cout << "CopyThread (" << td->dstDisk << ")Error: " << e.ErrorCode() <<" " << e.Description(); appGlobals.success = FALSE; return TASK_FAIL; } cout << "CopyThread to " << td->dstDisk << " succeeded.\n"; return TASK_OK; } /* *---------------------------------------------------------------------- * * PrepareThreadData -- * * Open the source and destination disk for multi threaded copy. * * Results: * Fills in ThreadData in td. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void PrepareThreadData(VixDiskLibConnection &dstConnection, ThreadData &td) { VixError vixError; VixDiskLibCreateParams createParams; VixDiskLibInfo *info = NULL; string prefixName,randomFilename; prefixName = "/tmp/test"; GenerateRandomFilename(prefixName, randomFilename); td.dstDisk = randomFilename; vixError = VixDiskLib_Open(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags, &td.srcHandle); CHECK_AND_THROW(vixError); vixError = VixDiskLib_GetInfo(td.srcHandle, &info); CHECK_AND_THROW(vixError); td.numSectors = info->capacity; VixDiskLib_FreeInfo(info); createParams.adapterType = VIXDISKLIB_ADAPTER_SCSI_BUSLOGIC; createParams.capacity = td.numSectors; createParams.diskType = VIXDISKLIB_DISK_SPLIT_SPARSE; createParams.hwVersion = VIXDISKLIB_HWVERSION_WORKSTATION_5; vixError = VixDiskLib_Create(dstConnection, td.dstDisk.c_str(), &createParams, NULL, NULL); CHECK_AND_THROW(vixError); vixError = VixDiskLib_Open(dstConnection, td.dstDisk.c_str(), 0, &td.dstHandle); CHECK_AND_THROW(vixError); } /* *---------------------------------------------------------------------- * * DoTestMultiThread -- * * Starts a given number of threads, each of which will copy the * source disk to a temp. file. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DoTestMultiThread(void) { VixDiskLibConnectParams cnxParams = { 0 }; VixDiskLibConnection dstConnection; VixError vixError; vector threadData(appGlobals.numThreads); int i; vixError = VixDiskLib_Connect(&cnxParams, &dstConnection); CHECK_AND_THROW(vixError); vector threads(appGlobals.numThreads); for (i = 0; i < appGlobals.numThreads; i++) { PrepareThreadData(dstConnection, threadData[i]); pthread_create(&threads[i], NULL, &CopyThread, (void*)&threadData[i]); } for (i = 0; i < appGlobals.numThreads; i++) { void *hlp; pthread_join(threads[i], &hlp); } for (i = 0; i < appGlobals.numThreads; i++) { VixDiskLib_Close(threadData[i].srcHandle); VixDiskLib_Close(threadData[i].dstHandle); VixDiskLib_Unlink(dstConnection, threadData[i].dstDisk.c_str()); } VixDiskLib_Disconnect(dstConnection); if (!appGlobals.success) { THROW_ERROR(VIX_E_FAIL); } } /* *---------------------------------------------------------------------- * * CloneProgress -- * * Callback for the clone function. * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static Bool CloneProgressFunc(void * /*progressData*/, // IN int percentCompleted) // IN { cout << "Cloning : " << percentCompleted << "% Done" << "\r"; return TRUE; } /* *---------------------------------------------------------------------- * * DoClone -- * * Clones a local disk (possibly to an ESX host). * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DoClone(void) { VixDiskLibConnection srcConnection; VixDiskLibConnectParams cnxParams = { 0 }; VixError vixError = VixDiskLib_Connect(&cnxParams, &srcConnection); CHECK_AND_THROW(vixError); /* * Note : These createParams are ignored for remote case */ VixDiskLibCreateParams createParams; createParams.adapterType = appGlobals.adapterType; createParams.capacity = appGlobals.mbSize * 2048; createParams.diskType = VIXDISKLIB_DISK_STREAM_OPTIMIZED; createParams.hwVersion = VIXDISKLIB_HWVERSION_WORKSTATION_5; vixError = VixDiskLib_Clone(appGlobals.connection, appGlobals.diskPath, srcConnection, appGlobals.srcPath, &createParams, CloneProgressFunc, NULL, // clientData TRUE); // doOverWrite VixDiskLib_Disconnect(srcConnection); CHECK_AND_THROW(vixError); cout << "\n Done" << "\n"; } /* *---------------------------------------------------------------------- * * DoConvert -- * * Converts a local raw disk to vmdk * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DoConvert(void) { appGlobals.diskType = VIXDISKLIB_DISK_MONOLITHIC_SPARSE; DoCreate(); VixError vixError; VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); VixDiskLibInfo* info = 0; setbuf(stdout, (char *)NULL); vixError = VixDiskLib_GetInfo(disk.Handle(), &info); CHECK_AND_THROW(vixError); unsigned char diskbuf[VIXDISKLIB_SECTOR_SIZE]; FILE* fp = fopen(appGlobals.srcPath, "rb"); if (fp == NULL) { printf("Problem reading input raw file"); return; } for(size_t i = 0; i < info->capacity; i += 1) { size_t data = fread(diskbuf, VIXDISKLIB_SECTOR_SIZE, 1, fp); vixError = VixDiskLib_Write(disk.Handle(), i, data, diskbuf); CHECK_AND_THROW(vixError); } fclose(fp); VixDiskLib_FreeInfo(info); cout << "\n Done" << "\n"; } /* *---------------------------------------------------------------------- * * PrintStat -- * * Print performance statistics for read/write benchmarks. * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static void PrintStat(bool read, // IN struct timeval start, // IN struct timeval end, // IN uint32 numSectors) // IN { uint64 elapsed; uint32 speed; elapsed = ((uint64)end.tv_sec * 1000000 + end.tv_usec - ((uint64)start.tv_sec * 1000000 + start.tv_usec)) / 1000; if (elapsed == 0) { elapsed = 1; } speed = (1000 * VIXDISKLIB_SECTOR_SIZE * (uint64)numSectors) / (1024 * 1024 * elapsed); printf("%s %d MBytes in %d msec (%d MBytes/sec)\n", read ? "Read" : "Wrote", (uint32)(numSectors /(2048)), (uint32)elapsed, speed); } /* *---------------------------------------------------------------------- * * InitBuffer -- * * Fill an array of uint32 with random values, to defeat any * attempts to compress it. * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static void InitBuffer(uint32 *buf, // OUT uint32 numElems) // IN { int i; srand(time(NULL)); for (i = 0; i < numElems; i++) { buf[i] = (uint32)rand(); } } /* *---------------------------------------------------------------------- * * DoRWBench -- * * Perform read/write benchmarks according to settings in * appGlobals. Note that a write benchmark will destroy the data * in the target disk. * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DoRWBench(bool read) // IN { VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); size_t bufSize; uint8 *buf; VixDiskLibInfo *info; VixError err; uint32 maxOps, i; uint32 bufUpdate; struct timeval start, end, total; if (appGlobals.bufSize == 0) { appGlobals.bufSize = DEFAULT_BUFSIZE; } bufSize = appGlobals.bufSize * VIXDISKLIB_SECTOR_SIZE; buf = new uint8[bufSize]; if (!read) { InitBuffer((uint32*)buf, bufSize / sizeof(uint32)); } err = VixDiskLib_GetInfo(disk.Handle(), &info); if (VIX_FAILED(err)) { delete [] buf; throw VixDiskLibErrWrapper(err, __FILE__, __LINE__); } maxOps = info->capacity / appGlobals.bufSize; VixDiskLib_FreeInfo(info); printf("Processing %d buffers of %d bytes.\n", maxOps, (uint32)bufSize); gettimeofday(&total, NULL); start = total; bufUpdate = 0; for (i = 0; i < maxOps; i++) { VixError vixError; if (read) { vixError = VixDiskLib_Read(disk.Handle(), i * appGlobals.bufSize, appGlobals.bufSize, buf); } else { vixError = VixDiskLib_Write(disk.Handle(), i * appGlobals.bufSize, appGlobals.bufSize, buf); } if (VIX_FAILED(vixError)) { delete [] buf; throw VixDiskLibErrWrapper(vixError, __FILE__, __LINE__); } bufUpdate += appGlobals.bufSize; if (bufUpdate >= BUFS_PER_STAT) { gettimeofday(&end, NULL); PrintStat(read, start, end, bufUpdate); start = end; bufUpdate = 0; } } gettimeofday(&end, NULL); PrintStat(read, total, end, appGlobals.bufSize * maxOps); delete [] buf; } /* *---------------------------------------------------------------------- * * DoCheckRepair -- * * Check a sparse disk for internal consistency. * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DoCheckRepair(Bool repair) { VixError err; err = VixDiskLib_CheckRepair(appGlobals.connection, appGlobals.diskPath, repair); if (VIX_FAILED(err)) { throw VixDiskLibErrWrapper(err, __FILE__, __LINE__); } } /* *---------------------------------------------------------------------- * * DoMountDisk -- * * Mounts a vmdk to the specified path * * Results: * None * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DoMountDisk() { VixError err; const char *diskNames[1]; diskNames[0] = appGlobals.diskPath; VixDiskSetHandle diskSetHandle = NULL; VixVolumeHandle *volumeHandles = NULL; VixVolumeInfo *volInfo = NULL; VixDiskSetInfo *diskSetInfo = NULL; size_t numVolumes = 0; // Init Mount Lib err = VixMntapi_Init(VIXMNTAPI_VERSION_MAJOR, VIXMNTAPI_VERSION_MINOR, NULL, NULL, NULL, appGlobals.libdir, appGlobals.cfgFile); CHECK_AND_THROW(err); // VixDisk disk(appGlobals.connection, appGlobals.diskPath, appGlobals.openFlags); // Open Disks err = VixMntapi_OpenDisks(appGlobals.connection, diskNames, 1, appGlobals.openFlags, &diskSetHandle); CHECK_AND_THROW(err); err = VixMntapi_GetDiskSetInfo(diskSetHandle, &diskSetInfo); CHECK_AND_THROW(err); setbuf(stdout, (char *)NULL); printf("%s", diskSetInfo->mountPath); err = VixMntapi_GetVolumeHandles(diskSetHandle, &numVolumes, &volumeHandles); CHECK_AND_THROW(err); volInfo = NULL; err = VixMntapi_MountVolume(volumeHandles[0], FALSE); CHECK_AND_THROW(err); err = VixMntapi_GetVolumeInfo(volumeHandles[0], &volInfo); CHECK_AND_THROW(err); // printf("Type %d, isMounted %d, symLink %s, numGuestMountPoints %d\n", // volInfo->type, volInfo->isMounted, // volInfo->symbolicLink == NULL ? "" : volInfo->symbolicLink, // (int)volInfo->numGuestMountPoints); VixMntapi_FreeVolumeInfo(volInfo); VixMntapi_DismountVolume(volumeHandles[0], TRUE); VixMntapi_FreeDiskSetInfo(diskSetInfo); if (volumeHandles) { VixMntapi_FreeVolumeHandles(volumeHandles); } if (diskSetHandle) { VixMntapi_CloseDiskSet(diskSetHandle); } VixMntapi_Exit(); }