vbox-img.cpp revision fe749ae9883a87a71de50a47a38c451defd8337a
/* $Id$ */
/** @file
* Standalone image manipulation tool
*/
/*
* Copyright (C) 2010-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/initterm.h>
#include <iprt/buildconfig.h>
#include <iprt/filesystem.h>
const char *g_pszProgName = "";
{
"Usage: %s\n"
" setuuid --filename <filename>\n"
" [--format VDI|VMDK|VHD|...]\n"
" [--uuid <uuid>]\n"
" [--parentuuid <uuid>]\n"
" [--zeroparentuuid]\n"
"\n"
" convert --srcfilename <filename>\n"
" --dstfilename <filename>\n"
" [--stdin]|[--stdout]\n"
" [--srcformat VDI|VMDK|VHD|RAW|..]\n"
" [--dstformat VDI|VMDK|VHD|RAW|..]\n"
" [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
"\n"
" info --filename <filename>\n"
"\n"
" compact --filename <filename>\n"
" [--filesystemaware]\n"
"\n"
" createcache --filename <filename>\n"
" --size <cache size>\n"
"\n"
" createbase --filename <filename>\n"
" --size <size in bytes>\n"
" [--format VDI|VMDK|VHD] (default: VDI)\n"
" [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
" [--dataalignment <alignment in bytes>]\n"
"\n"
" repair --filename <filename>\n"
" [--dry-run]\n"
" [--format VDI|VMDK|VHD] (default: autodetect)\n"
"\n"
" clearcomment --filename <filename>\n",
}
{
static bool s_fShown; /* show only once */
if (!s_fShown)
{
"All rights reserved.\n"
"\n");
s_fShown = true;
}
}
/** command handler argument */
struct HandlerArg
{
int argc;
char **argv;
};
{
}
{
return VINF_SUCCESS;
}
/**
* Print a usage synopsis and the syntax error message.
*/
int errorSyntax(const char *pszFormat, ...)
{
return 1;
}
int errorRuntime(const char *pszFormat, ...)
{
return 1;
}
{
int rc = VINF_SUCCESS;
unsigned uImageFlags = *puImageFlags;
{
if (pszComma)
else
if (len > 0)
{
/*
* Parsing is intentionally inconsistent: "standard" resets the
* variant, whereas the other flags are cumulative.
*/
else
}
if (pszComma)
else
}
if (RT_SUCCESS(rc))
return rc;
}
int handleSetUUID(HandlerArg *a)
{
const char *pszFilename = NULL;
bool fSetImageUuid = false;
bool fSetParentUuid = false;
int rc;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
case 'o': // --format
break;
case 'u': // --uuid
fSetImageUuid = true;
break;
case 'p': // --parentuuid
fSetParentUuid = true;
break;
case 'P': // --zeroparentuuid
fSetParentUuid = true;
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
/* Check for consistency of optional parameters. */
return errorSyntax("Invalid parameter to --uuid option\n");
/* Autodetect image format. */
if (!pszFormat)
{
/* Don't pass error interface, as that would triggers error messages
* because some backends fail to open the image. */
if (RT_FAILURE(rc))
}
if (RT_FAILURE(rc))
/* Open in info mode to be able to open diff images without their parent. */
if (RT_FAILURE(rc))
return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
if (RT_FAILURE(rc))
return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
if (RT_FAILURE(rc))
return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
if (fSetImageUuid)
{
if (RT_FAILURE(rc))
return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
}
if (fSetParentUuid)
{
if (RT_FAILURE(rc))
return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
}
if (pszFormat)
{
}
return 0;
}
typedef struct FILEIOSTATE
{
/** Offset in the file. */
/** Offset where the buffer contents start. UINT64_MAX=buffer invalid. */
/** Size of valid data in the buffer. */
/** Buffer for efficient I/O */
} FILEIOSTATE, *PFILEIOSTATE;
void **ppStorage)
{
/* Validate input. */
if (RT_FAILURE(rc))
return rc;
/* No need to clear the buffer, the data will be read from disk. */
if (!pFS)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
}
unsigned fMove)
{
}
{
*pcbFreeSpace = 0;
return VINF_SUCCESS;
}
{
}
{
}
{
}
{
int rc;
/* Fill buffer if it is empty. */
{
/* Repeat reading until buffer is full or EOF. */
do
{
if (RT_FAILURE(rc))
return rc;
return VERR_EOF;
}
/* Read several blocks and assemble the result if necessary */
size_t cbTotalRead = 0;
do
{
/* Skip over areas no one wants to read. */
{
{
if (pcbRead)
*pcbRead = cbTotalRead;
return VERR_EOF;
}
/* Repeat reading until buffer is full or EOF. */
do
{
if (RT_FAILURE(rc))
return rc;
}
uOffset += cbThisRead;
cbBuffer -= cbThisRead;
return VERR_EOF;
} while (cbBuffer > 0);
if (pcbRead)
*pcbRead = cbTotalRead;
return VINF_SUCCESS;
}
{
}
{
return VINF_SUCCESS;
}
void **ppStorage)
{
/* Validate input. */
if (RT_FAILURE(rc))
return rc;
/* Must clear buffer, so that skipped over data is initialized properly. */
if (!pFS)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
/* Flush any remaining buffer contents. */
return rc;
}
{
}
unsigned fMove)
{
}
{
return VINF_SUCCESS;
}
{
}
{
}
{
}
{
}
{
int rc;
/* Write the data to the buffer, flushing as required. */
size_t cbTotalWritten = 0;
do
{
/* Flush the buffer if we need a new one. */
{
}
uOffset += cbThisWrite;
cbBuffer -= cbThisWrite;
} while (cbBuffer > 0);
if (pcbWritten)
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
int handleConvert(HandlerArg *a)
{
const char *pszSrcFilename = NULL;
const char *pszDstFilename = NULL;
bool fStdIn = false;
bool fStdOut = false;
const char *pszSrcFormat = NULL;
const char *pszDstFormat = NULL;
const char *pszVariant = NULL;
unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
int rc = VINF_SUCCESS;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'i': // --srcfilename
break;
case 'o': // --dstfilename
break;
case 'p': // --stdin
fStdIn = true;
break;
case 'P': // --stdout
fStdOut = true;
break;
case 's': // --srcformat
break;
case 'd': // --dstformat
break;
case 'v': // --variant
break;
default:
return ch;
}
}
if (fStdIn && !pszSrcFormat)
return errorSyntax("Mandatory --srcformat option missing\n");
if (!pszDstFormat)
pszDstFormat = "VDI";
if (fStdIn && !pszSrcFilename)
{
/* Complete dummy, will be just passed to various calls to fulfill
* the "must be non-NULL" requirement, and is completely ignored
* otherwise. It shown in the stderr message below. */
pszSrcFilename = "stdin";
}
if (fStdOut && !pszDstFilename)
{
/* Will be stored in the destination image if it is a streamOptimized
* VMDK, but it isn't really relevant - use it for "branding". */
pszDstFilename = "VirtualBoxStream.vmdk";
else
pszDstFilename = "stdout";
}
if (!pszSrcFilename)
return errorSyntax("Mandatory --srcfilename option missing\n");
if (!pszDstFilename)
return errorSyntax("Mandatory --dstfilename option missing\n");
if (fStdIn)
{
}
if (fStdOut)
{
}
/* check the variant parameter */
if (pszVariant)
{
char *psz = (char*)pszVariant;
{
if (pszComma)
else
if (len > 0)
{
else
return errorSyntax("Invalid --variant option\n");
}
if (pszComma)
else
}
}
do
{
/* try to determine input format if not specified */
if (!pszSrcFormat)
{
if (RT_FAILURE(rc))
{
break;
}
}
if (RT_FAILURE(rc))
{
break;
}
if (RT_FAILURE(rc))
{
break;
}
if (RT_FAILURE(rc))
{
break;
}
RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
/* Create the output image */
if (RT_FAILURE(rc))
{
break;
}
}
while (0);
if (pDstDisk)
if (pSrcDisk)
}
int handleInfo(HandlerArg *a)
{
int rc = VINF_SUCCESS;
const char *pszFilename = NULL;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
/* just try it */
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
/* Open the image */
if (RT_FAILURE(rc))
return rc;
}
{
int rc = VINF_SUCCESS;
/* Take shortcut if possible. */
if ( off % 512 == 0
&& cbRead % 512 == 0)
else
{
/* Unaligned access, make it aligned. */
if (off % 512 != 0)
{
if (RT_SUCCESS(rc))
{
}
}
if ( RT_SUCCESS(rc)
{
if (RT_SUCCESS(rc))
{
pbBuf += cbReadAligned;
off += cbReadAligned;
cbRead -= cbReadAligned;
}
}
if ( RT_SUCCESS(rc)
&& cbRead)
{
if (RT_SUCCESS(rc))
}
}
return rc;
}
static DECLCALLBACK(int) vboximgDvmWrite(void *pvUser, uint64_t off, const void *pvBuf, size_t cbWrite)
{
}
{
}
bool *pfUsed)
{
}
typedef struct VBOXIMGVFS
{
/** Pointer to the next VFS handle. */
struct VBOXIMGVFS *pNext;
/** VFS handle. */
} VBOXIMGVFS, *PVBOXIMGVFS;
int handleCompact(HandlerArg *a)
{
int rc = VINF_SUCCESS;
const char *pszFilename = NULL;
bool fFilesystemAware = false;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
case 'a':
fFilesystemAware = true;
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
/* just try it */
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
/* Open the image */
if (RT_FAILURE(rc))
if ( RT_SUCCESS(rc)
&& fFilesystemAware)
{
if (cbDisk > 0)
{
0 /* fFlags*/, pDisk);
if (RT_SUCCESS(rc))
{
if ( RT_SUCCESS(rc)
{
/* Get all volumes and set the block query status callback. */
do
{
if (RT_FAILURE(rc))
break;
/* Try to detect the filesystem in this volume. */
if (rc == VERR_NOT_SUPPORTED)
{
/* Release the file handle and continue.*/
}
else if RT_FAILURE(rc)
break;
else
{
if (!pVBoxImgVfs)
rc = VERR_NO_MEMORY;
else
{
}
}
if (RT_SUCCESS(rc))
/*
* Release the volume handle, the file handle has a reference
* to keep it open.
*/
} while (RT_SUCCESS(rc));
if (rc == VERR_DVM_MAP_NO_VOLUME)
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
}
}
else if (RT_SUCCESS(rc))
RTPrintf("There are no partitions in the volume map\n");
else if (rc == VERR_NOT_FOUND)
{
rc = VINF_SUCCESS;
RTPrintf("No known volume format on disk found\n");
}
else
}
else
}
else
{
errorRuntime("Error while getting the disk size\n");
}
}
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
while (pVBoxImgVfsHead)
{
}
if (hDvm)
return rc;
}
int handleCreateCache(HandlerArg *a)
{
int rc = VINF_SUCCESS;
const char *pszFilename = NULL;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
case 's': // --size
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
if (!cbSize)
return errorSyntax("Mandatory --size option missing\n");
/* just try it */
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
return rc;
}
{
return VINF_SUCCESS; /** @todo: Implement. */
}
static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
{
return VERR_CFGM_VALUE_NOT_FOUND;
return VINF_SUCCESS;
}
static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
{
return VERR_CFGM_VALUE_NOT_FOUND;
return VERR_CFGM_NOT_ENOUGH_SPACE;
return VINF_SUCCESS;
}
int handleCreateBase(HandlerArg *a)
{
int rc = VINF_SUCCESS;
const char *pszFilename = NULL;
const char *pszBackend = "VDI";
const char *pszVariant = NULL;
unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
const char *pszDataAlignment = NULL;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
case 's': // --size
break;
case 'b': // --format
break;
case 'v': // --variant
break;
case 'a': // --dataalignment
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
if (!cbSize)
return errorSyntax("Mandatory --size option missing\n");
if (pszVariant)
{
if (RT_FAILURE(rc))
}
/* Setup the config interface if required. */
if (pszDataAlignment)
{
sizeof(vdIfCfg), &pVDIfsOperation);
}
/* just try it */
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
return rc;
}
int handleRepair(HandlerArg *a)
{
int rc = VINF_SUCCESS;
const char *pszFilename = NULL;
char *pszBackend = NULL;
bool fDryRun = false;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
case 'd': // --dry-run
fDryRun = true;
break;
case 'b': // --format
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
/* just try it */
if (!pszFormat)
{
if (RT_FAILURE(rc))
}
if (RT_FAILURE(rc))
if (pszBackend)
return rc;
}
int handleClearComment(HandlerArg *a)
{
int rc = VINF_SUCCESS;
const char *pszFilename = NULL;
bool fDryRun = false;
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
{
switch (ch)
{
case 'f': // --filename
break;
default:
return ch;
}
}
/* Check for mandatory parameters. */
if (!pszFilename)
return errorSyntax("Mandatory --filename option missing\n");
/* just try it */
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
/* Open the image */
if (RT_FAILURE(rc))
return rc;
}
{
int exitcode = 0;
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
bool fShowLogo = false;
int iCmd = 1;
int iCmdArg;
/* global options */
{
{
return 0;
}
{
/* Print version number, and do nothing else. */
return 0;
}
{
/* suppress the logo */
fShowLogo = false;
iCmd++;
}
else
{
break;
}
}
if (fShowLogo)
/* initialize the VD backend with dummy handlers */
if (RT_FAILURE(rc))
{
return 1;
}
/*
* All registered command handlers
*/
static const struct
{
const char *command;
int (*handler)(HandlerArg *a);
} s_commandHandlers[] =
{
{ "setuuid", handleSetUUID },
{ "convert", handleConvert },
{ "info", handleInfo },
{ "compact", handleCompact },
{ "createcache", handleCreateCache },
{ "createbase", handleCreateBase },
{ "repair", handleRepair },
{ "clearcomment", handleClearComment },
};
int commandIndex;
{
{
break;
}
}
{
return 1;
}
rc = VDShutdown();
if (RT_FAILURE(rc))
{
return 1;
}
return exitcode;
}
/* dummy stub for RuntimeR3 */
#ifndef RT_OS_WINDOWS
RTDECL(bool) RTAssertShouldPanic(void)
{
return true;
}
#endif