mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-24 04:12:20 +00:00
storage: Move most of the FS creation functions to common backend.
These will be used by other pool cloning implementations.
This commit is contained in:
parent
1b16bf4ec7
commit
6d7d465a04
@ -95,6 +95,419 @@ static virStorageBackendPtr backends[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int track_allocation_progress = 0;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TOOL_QEMU_IMG,
|
||||||
|
TOOL_KVM_IMG,
|
||||||
|
TOOL_QCOW_CREATE,
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
virStorageBackendCreateRaw(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
int inputfd = -1;
|
||||||
|
int ret = -1;
|
||||||
|
unsigned long long remain;
|
||||||
|
char *buf = NULL;
|
||||||
|
|
||||||
|
if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL,
|
||||||
|
vol->target.perms.mode)) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("cannot create path '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputvol) {
|
||||||
|
if ((inputfd = open(inputvol->target.path, O_RDONLY)) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("could not open input path '%s'"),
|
||||||
|
inputvol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seek to the final size, so the capacity is available upfront
|
||||||
|
* for progress reporting */
|
||||||
|
if (ftruncate(fd, vol->capacity) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("cannot extend file '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
remain = vol->allocation;
|
||||||
|
|
||||||
|
if (inputfd != -1) {
|
||||||
|
int amtread = -1;
|
||||||
|
size_t bytes = 1024 * 1024;
|
||||||
|
char zerobuf[512];
|
||||||
|
|
||||||
|
bzero(&zerobuf, sizeof(zerobuf));
|
||||||
|
|
||||||
|
if (VIR_ALLOC_N(buf, bytes) < 0) {
|
||||||
|
virReportOOMError(conn);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (amtread != 0) {
|
||||||
|
int amtleft;
|
||||||
|
|
||||||
|
if (remain < bytes)
|
||||||
|
bytes = remain;
|
||||||
|
|
||||||
|
if ((amtread = saferead(inputfd, buf, bytes)) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("failed reading from file '%s'"),
|
||||||
|
inputvol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
remain -= amtread;
|
||||||
|
|
||||||
|
/* Loop over amt read in 512 byte increments, looking for sparse
|
||||||
|
* blocks */
|
||||||
|
amtleft = amtread;
|
||||||
|
do {
|
||||||
|
int interval = ((512 > amtleft) ? amtleft : 512);
|
||||||
|
int offset = amtread - amtleft;
|
||||||
|
|
||||||
|
if (memcmp(buf+offset, zerobuf, interval) == 0) {
|
||||||
|
if (lseek(fd, interval, SEEK_CUR) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("cannot extend file '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
} else if (safewrite(fd, buf+offset, interval) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("failed writing to file '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
}
|
||||||
|
} while ((amtleft -= 512) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remain) {
|
||||||
|
if (track_allocation_progress) {
|
||||||
|
|
||||||
|
while (remain) {
|
||||||
|
/* Allocate in chunks of 512MiB: big-enough chunk
|
||||||
|
* size and takes approx. 9s on ext3. A progress
|
||||||
|
* update every 9s is a fair-enough trade-off
|
||||||
|
*/
|
||||||
|
unsigned long long bytes = 512 * 1024 * 1024;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (bytes > remain)
|
||||||
|
bytes = remain;
|
||||||
|
if ((r = safezero(fd, 0, vol->allocation - remain,
|
||||||
|
bytes)) != 0) {
|
||||||
|
virReportSystemError(conn, r,
|
||||||
|
_("cannot fill file '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
remain -= bytes;
|
||||||
|
}
|
||||||
|
} else { /* No progress bars to be shown */
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if ((r = safezero(fd, 0, 0, remain)) != 0) {
|
||||||
|
virReportSystemError(conn, r,
|
||||||
|
_("cannot fill file '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close(fd) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("cannot close file '%s'"),
|
||||||
|
vol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
fd = -1;
|
||||||
|
|
||||||
|
if (inputfd != -1 && close(inputfd) < 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("cannot close file '%s'"),
|
||||||
|
inputvol->target.path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
inputfd = -1;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
cleanup:
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
|
if (inputfd != -1)
|
||||||
|
close(inputfd);
|
||||||
|
VIR_FREE(buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
virStorageBackendCreateQemuImg(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol)
|
||||||
|
{
|
||||||
|
char size[100];
|
||||||
|
char *create_tool;
|
||||||
|
short use_kvmimg;
|
||||||
|
|
||||||
|
const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format);
|
||||||
|
const char *backingType = vol->backingStore.path ?
|
||||||
|
virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL;
|
||||||
|
|
||||||
|
const char *inputBackingPath = (inputvol ? inputvol->backingStore.path
|
||||||
|
: NULL);
|
||||||
|
const char *inputPath = inputvol ? inputvol->target.path : NULL;
|
||||||
|
/* Treat input block devices as 'raw' format */
|
||||||
|
const char *inputType = inputPath ?
|
||||||
|
virStorageVolFormatFileSystemTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_VOL_FILE_RAW : inputvol->target.format) :
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
const char **imgargv;
|
||||||
|
const char *imgargvnormal[] = {
|
||||||
|
NULL, "create",
|
||||||
|
"-f", type,
|
||||||
|
vol->target.path,
|
||||||
|
size,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
/* Extra NULL fields are for including "backingType" when using
|
||||||
|
* kvm-img. It's -F backingType
|
||||||
|
*/
|
||||||
|
const char *imgargvbacking[] = {
|
||||||
|
NULL, "create",
|
||||||
|
"-f", type,
|
||||||
|
"-b", vol->backingStore.path,
|
||||||
|
vol->target.path,
|
||||||
|
size,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
const char *convargv[] = {
|
||||||
|
NULL, "convert",
|
||||||
|
"-f", inputType,
|
||||||
|
"-O", type,
|
||||||
|
inputPath,
|
||||||
|
vol->target.path,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == NULL) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("unknown storage vol type %d"),
|
||||||
|
vol->target.format);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (inputvol && inputType == NULL) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("unknown storage vol type %d"),
|
||||||
|
inputvol->target.format);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vol->backingStore.path) {
|
||||||
|
|
||||||
|
/* XXX: Not strictly required: qemu-img has an option a different
|
||||||
|
* backing store, not really sure what use it serves though, and it
|
||||||
|
* may cause issues with lvm. Untested essentially.
|
||||||
|
*/
|
||||||
|
if (inputvol &&
|
||||||
|
(!inputBackingPath ||
|
||||||
|
STRNEQ(inputBackingPath, vol->backingStore.path))) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("a different backing store can not "
|
||||||
|
"be specified."));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backingType == NULL) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("unknown storage vol backing store type %d"),
|
||||||
|
vol->backingStore.format);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (access(vol->backingStore.path, R_OK) != 0) {
|
||||||
|
virReportSystemError(conn, errno,
|
||||||
|
_("inaccessible backing store volume %s"),
|
||||||
|
vol->backingStore.path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((create_tool = virFindFileInPath("kvm-img")) != NULL)
|
||||||
|
use_kvmimg = 1;
|
||||||
|
else if ((create_tool = virFindFileInPath("qemu-img")) != NULL)
|
||||||
|
use_kvmimg = 0;
|
||||||
|
else {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("unable to find kvm-img or qemu-img"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputvol) {
|
||||||
|
convargv[0] = create_tool;
|
||||||
|
imgargv = convargv;
|
||||||
|
} else if (vol->backingStore.path) {
|
||||||
|
imgargvbacking[0] = create_tool;
|
||||||
|
if (use_kvmimg) {
|
||||||
|
imgargvbacking[6] = "-F";
|
||||||
|
imgargvbacking[7] = backingType;
|
||||||
|
imgargvbacking[8] = vol->target.path;
|
||||||
|
imgargvbacking[9] = size;
|
||||||
|
}
|
||||||
|
imgargv = imgargvbacking;
|
||||||
|
} else {
|
||||||
|
imgargvnormal[0] = create_tool;
|
||||||
|
imgargv = imgargvnormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Size in KB */
|
||||||
|
snprintf(size, sizeof(size), "%llu", vol->capacity/1024);
|
||||||
|
|
||||||
|
if (virRun(conn, imgargv, NULL) < 0) {
|
||||||
|
VIR_FREE(imgargv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_FREE(imgargv[0]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Xen removed the fully-functional qemu-img, and replaced it
|
||||||
|
* with a partially functional qcow-create. Go figure ??!?
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
virStorageBackendCreateQcowCreate(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol)
|
||||||
|
{
|
||||||
|
char size[100];
|
||||||
|
const char *imgargv[4];
|
||||||
|
|
||||||
|
if (inputvol) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s",
|
||||||
|
_("cannot copy from volume with qcow-create"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("unsupported storage vol type %d"),
|
||||||
|
vol->target.format);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (vol->backingStore.path != NULL) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_NO_SUPPORT,
|
||||||
|
_("copy-on-write image not supported with "
|
||||||
|
"qcow-create"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Size in MB - yes different units to qemu-img :-( */
|
||||||
|
snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024);
|
||||||
|
|
||||||
|
imgargv[0] = virFindFileInPath("qcow-create");
|
||||||
|
imgargv[1] = size;
|
||||||
|
imgargv[2] = vol->target.path;
|
||||||
|
imgargv[3] = NULL;
|
||||||
|
|
||||||
|
if (virRun(conn, imgargv, NULL) < 0) {
|
||||||
|
VIR_FREE(imgargv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_FREE(imgargv[0]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
createFile
|
||||||
|
virStorageBackendFSImageToolTypeToFunc(virConnectPtr conn, int tool_type)
|
||||||
|
{
|
||||||
|
switch (tool_type) {
|
||||||
|
case TOOL_KVM_IMG:
|
||||||
|
case TOOL_QEMU_IMG:
|
||||||
|
return virStorageBackendCreateQemuImg;
|
||||||
|
case TOOL_QCOW_CREATE:
|
||||||
|
return virStorageBackendCreateQcowCreate;
|
||||||
|
default:
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Unknown file create tool type '%d'."),
|
||||||
|
tool_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
virStorageBackendFindFSImageTool(char **tool)
|
||||||
|
{
|
||||||
|
int tool_type = -1;
|
||||||
|
char *tmp = NULL;
|
||||||
|
|
||||||
|
if ((tmp = virFindFileInPath("kvm-img")) != NULL) {
|
||||||
|
tool_type = TOOL_KVM_IMG;
|
||||||
|
} else if ((tmp = virFindFileInPath("qemu-img")) != NULL) {
|
||||||
|
tool_type = TOOL_QEMU_IMG;
|
||||||
|
} else if ((tmp = virFindFileInPath("qcow-create")) != NULL) {
|
||||||
|
tool_type = TOOL_QCOW_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tool)
|
||||||
|
*tool = tmp;
|
||||||
|
else
|
||||||
|
VIR_FREE(tmp);
|
||||||
|
|
||||||
|
return tool_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
createFile
|
||||||
|
virStorageBackendGetBuildVolFromFunction(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol)
|
||||||
|
{
|
||||||
|
int tool_type;
|
||||||
|
|
||||||
|
if (!inputvol)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If either volume is a non-raw file vol, we need to use an external
|
||||||
|
* tool for converting
|
||||||
|
*/
|
||||||
|
if ((vol->type == VIR_STORAGE_VOL_FILE &&
|
||||||
|
vol->target.format != VIR_STORAGE_VOL_FILE_RAW) ||
|
||||||
|
(inputvol->type == VIR_STORAGE_VOL_FILE &&
|
||||||
|
inputvol->target.format != VIR_STORAGE_VOL_FILE_RAW)) {
|
||||||
|
|
||||||
|
if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
|
||||||
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("creation of non-raw file images is "
|
||||||
|
"not supported without qemu-img."));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return virStorageBackendFSImageToolTypeToFunc(conn, tool_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return virStorageBackendCreateRaw;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(UDEVADM) || defined(UDEVSETTLE)
|
#if defined(UDEVADM) || defined(UDEVSETTLE)
|
||||||
void virWaitForDevices(virConnectPtr conn)
|
void virWaitForDevices(virConnectPtr conn)
|
||||||
|
@ -40,6 +40,22 @@ typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObj
|
|||||||
typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags);
|
typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags);
|
||||||
typedef int (*virStorageBackendBuildVolFrom)(virConnectPtr conn, virStorageVolDefPtr origvol, virStorageVolDefPtr newvol, unsigned int flags);
|
typedef int (*virStorageBackendBuildVolFrom)(virConnectPtr conn, virStorageVolDefPtr origvol, virStorageVolDefPtr newvol, unsigned int flags);
|
||||||
|
|
||||||
|
typedef int (*createFile)(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol);
|
||||||
|
|
||||||
|
/* File creation/cloning functions used for cloning between backends */
|
||||||
|
int virStorageBackendCreateRaw(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol);
|
||||||
|
createFile
|
||||||
|
virStorageBackendGetBuildVolFromFunction(virConnectPtr conn,
|
||||||
|
virStorageVolDefPtr vol,
|
||||||
|
virStorageVolDefPtr inputvol);
|
||||||
|
int virStorageBackendFindFSImageTool(char **tool);
|
||||||
|
createFile virStorageBackendFSImageToolTypeToFunc(virConnectPtr conn,
|
||||||
|
int tool_type);
|
||||||
|
|
||||||
|
|
||||||
typedef struct _virStorageBackend virStorageBackend;
|
typedef struct _virStorageBackend virStorageBackend;
|
||||||
typedef virStorageBackend *virStorageBackendPtr;
|
typedef virStorageBackend *virStorageBackendPtr;
|
||||||
|
@ -55,12 +55,6 @@ enum {
|
|||||||
BACKING_STORE_ERROR,
|
BACKING_STORE_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
TOOL_QEMU_IMG,
|
|
||||||
TOOL_KVM_IMG,
|
|
||||||
TOOL_QCOW_CREATE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int cowGetBackingStore(virConnectPtr, char **,
|
static int cowGetBackingStore(virConnectPtr, char **,
|
||||||
const unsigned char *, size_t);
|
const unsigned char *, size_t);
|
||||||
static int qcowXGetBackingStore(virConnectPtr, char **,
|
static int qcowXGetBackingStore(virConnectPtr, char **,
|
||||||
@ -68,12 +62,6 @@ static int qcowXGetBackingStore(virConnectPtr, char **,
|
|||||||
static int vmdk4GetBackingStore(virConnectPtr, char **,
|
static int vmdk4GetBackingStore(virConnectPtr, char **,
|
||||||
const unsigned char *, size_t);
|
const unsigned char *, size_t);
|
||||||
|
|
||||||
typedef int (*createFile)(virConnectPtr conn,
|
|
||||||
virStorageVolDefPtr vol,
|
|
||||||
virStorageVolDefPtr inputvol);
|
|
||||||
|
|
||||||
static int track_allocation_progress = 0;
|
|
||||||
|
|
||||||
/* Either 'magic' or 'extension' *must* be provided */
|
/* Either 'magic' or 'extension' *must* be provided */
|
||||||
struct FileTypeInfo {
|
struct FileTypeInfo {
|
||||||
int type; /* One of the constants above */
|
int type; /* One of the constants above */
|
||||||
@ -1018,157 +1006,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int createRaw(virConnectPtr conn,
|
|
||||||
virStorageVolDefPtr vol,
|
|
||||||
virStorageVolDefPtr inputvol) {
|
|
||||||
int fd = -1;
|
|
||||||
int inputfd = -1;
|
|
||||||
int ret = -1;
|
|
||||||
unsigned long long remain;
|
|
||||||
char *buf = NULL;
|
|
||||||
|
|
||||||
if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL,
|
|
||||||
vol->target.perms.mode)) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("cannot create path '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputvol) {
|
|
||||||
if ((inputfd = open(inputvol->target.path, O_RDONLY)) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("could not open input path '%s'"),
|
|
||||||
inputvol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Seek to the final size, so the capacity is available upfront
|
|
||||||
* for progress reporting */
|
|
||||||
if (ftruncate(fd, vol->capacity) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("cannot extend file '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
remain = vol->allocation;
|
|
||||||
|
|
||||||
if (inputfd != -1) {
|
|
||||||
int amtread = -1;
|
|
||||||
size_t bytes = 1024 * 1024;
|
|
||||||
char zerobuf[512];
|
|
||||||
|
|
||||||
bzero(&zerobuf, sizeof(zerobuf));
|
|
||||||
|
|
||||||
if (VIR_ALLOC_N(buf, bytes) < 0) {
|
|
||||||
virReportOOMError(conn);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (amtread != 0) {
|
|
||||||
int amtleft;
|
|
||||||
|
|
||||||
if (remain < bytes)
|
|
||||||
bytes = remain;
|
|
||||||
|
|
||||||
if ((amtread = saferead(inputfd, buf, bytes)) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("failed reading from file '%s'"),
|
|
||||||
inputvol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
remain -= amtread;
|
|
||||||
|
|
||||||
/* Loop over amt read in 512 byte increments, looking for sparse
|
|
||||||
* blocks */
|
|
||||||
amtleft = amtread;
|
|
||||||
do {
|
|
||||||
int interval = ((512 > amtleft) ? amtleft : 512);
|
|
||||||
int offset = amtread - amtleft;
|
|
||||||
|
|
||||||
if (memcmp(buf+offset, zerobuf, interval) == 0) {
|
|
||||||
if (lseek(fd, interval, SEEK_CUR) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("cannot extend file '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
} else if (safewrite(fd, buf+offset, interval) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("failed writing to file '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
}
|
|
||||||
} while ((amtleft -= 512) > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pre-allocate any data if requested */
|
|
||||||
/* XXX slooooooooooooooooow on non-extents-based file systems */
|
|
||||||
if (remain) {
|
|
||||||
if (track_allocation_progress) {
|
|
||||||
|
|
||||||
while (remain) {
|
|
||||||
/* Allocate in chunks of 512MiB: big-enough chunk
|
|
||||||
* size and takes approx. 9s on ext3. A progress
|
|
||||||
* update every 9s is a fair-enough trade-off
|
|
||||||
*/
|
|
||||||
unsigned long long bytes = 512 * 1024 * 1024;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (bytes > remain)
|
|
||||||
bytes = remain;
|
|
||||||
if ((r = safezero(fd, 0, vol->allocation - remain,
|
|
||||||
bytes)) != 0) {
|
|
||||||
virReportSystemError(conn, r,
|
|
||||||
_("cannot fill file '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
remain -= bytes;
|
|
||||||
}
|
|
||||||
} else { /* No progress bars to be shown */
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if ((r = safezero(fd, 0, 0, remain)) != 0) {
|
|
||||||
virReportSystemError(conn, r,
|
|
||||||
_("cannot fill file '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (close(fd) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("cannot close file '%s'"),
|
|
||||||
vol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
fd = -1;
|
|
||||||
|
|
||||||
if (inputfd != -1 && close(inputfd) < 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("cannot close file '%s'"),
|
|
||||||
inputvol->target.path);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
inputfd = -1;
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
cleanup:
|
|
||||||
if (fd != -1)
|
|
||||||
close(fd);
|
|
||||||
if (inputfd != -1)
|
|
||||||
close(inputfd);
|
|
||||||
VIR_FREE(buf);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int createFileDir(virConnectPtr conn,
|
static int createFileDir(virConnectPtr conn,
|
||||||
virStorageVolDefPtr vol,
|
virStorageVolDefPtr vol,
|
||||||
virStorageVolDefPtr inputvol) {
|
virStorageVolDefPtr inputvol) {
|
||||||
@ -1189,257 +1026,6 @@ static int createFileDir(virConnectPtr conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int createQemuImg(virConnectPtr conn,
|
|
||||||
virStorageVolDefPtr vol,
|
|
||||||
virStorageVolDefPtr inputvol) {
|
|
||||||
char size[100];
|
|
||||||
char *create_tool;
|
|
||||||
short use_kvmimg;
|
|
||||||
|
|
||||||
const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format);
|
|
||||||
const char *backingType = vol->backingStore.path ?
|
|
||||||
virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL;
|
|
||||||
|
|
||||||
const char *inputBackingPath = (inputvol ? inputvol->backingStore.path
|
|
||||||
: NULL);
|
|
||||||
const char *inputPath = inputvol ? inputvol->target.path : NULL;
|
|
||||||
/* Treat input block devices as 'raw' format */
|
|
||||||
const char *inputType = inputPath ?
|
|
||||||
virStorageVolFormatFileSystemTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_VOL_FILE_RAW : inputvol->target.format) :
|
|
||||||
NULL;
|
|
||||||
|
|
||||||
const char **imgargv;
|
|
||||||
const char *imgargvnormal[] = {
|
|
||||||
NULL, "create",
|
|
||||||
"-f", type,
|
|
||||||
vol->target.path,
|
|
||||||
size,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
/* Extra NULL fields are for including "backingType" when using
|
|
||||||
* kvm-img. It's -F backingType
|
|
||||||
*/
|
|
||||||
const char *imgargvbacking[] = {
|
|
||||||
NULL, "create",
|
|
||||||
"-f", type,
|
|
||||||
"-b", vol->backingStore.path,
|
|
||||||
vol->target.path,
|
|
||||||
size,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
const char *convargv[] = {
|
|
||||||
NULL, "convert",
|
|
||||||
"-f", inputType,
|
|
||||||
"-O", type,
|
|
||||||
inputPath,
|
|
||||||
vol->target.path,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (type == NULL) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("unknown storage vol type %d"),
|
|
||||||
vol->target.format);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (inputvol && inputType == NULL) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("unknown storage vol type %d"),
|
|
||||||
inputvol->target.format);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vol->backingStore.path) {
|
|
||||||
|
|
||||||
/* XXX: Not strictly required: qemu-img has an option a different
|
|
||||||
* backing store, not really sure what use it serves though, and it
|
|
||||||
* may cause issues with lvm. Untested essentially.
|
|
||||||
*/
|
|
||||||
if (inputvol &&
|
|
||||||
(!inputBackingPath ||
|
|
||||||
STRNEQ(inputBackingPath, vol->backingStore.path))) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
"%s", _("a different backing store can not "
|
|
||||||
"be specified."));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backingType == NULL) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("unknown storage vol backing store type %d"),
|
|
||||||
vol->backingStore.format);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (access(vol->backingStore.path, R_OK) != 0) {
|
|
||||||
virReportSystemError(conn, errno,
|
|
||||||
_("inaccessible backing store volume %s"),
|
|
||||||
vol->backingStore.path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((create_tool = virFindFileInPath("kvm-img")) != NULL)
|
|
||||||
use_kvmimg = 1;
|
|
||||||
else if ((create_tool = virFindFileInPath("qemu-img")) != NULL)
|
|
||||||
use_kvmimg = 0;
|
|
||||||
else {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("unable to find kvm-img or qemu-img"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputvol) {
|
|
||||||
convargv[0] = create_tool;
|
|
||||||
imgargv = convargv;
|
|
||||||
} else if (vol->backingStore.path) {
|
|
||||||
imgargvbacking[0] = create_tool;
|
|
||||||
if (use_kvmimg) {
|
|
||||||
imgargvbacking[6] = "-F";
|
|
||||||
imgargvbacking[7] = backingType;
|
|
||||||
imgargvbacking[8] = vol->target.path;
|
|
||||||
imgargvbacking[9] = size;
|
|
||||||
}
|
|
||||||
imgargv = imgargvbacking;
|
|
||||||
} else {
|
|
||||||
imgargvnormal[0] = create_tool;
|
|
||||||
imgargv = imgargvnormal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Size in KB */
|
|
||||||
snprintf(size, sizeof(size), "%llu", vol->capacity/1024);
|
|
||||||
|
|
||||||
if (virRun(conn, imgargv, NULL) < 0) {
|
|
||||||
VIR_FREE(imgargv[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
VIR_FREE(imgargv[0]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Xen removed the fully-functional qemu-img, and replaced it
|
|
||||||
* with a partially functional qcow-create. Go figure ??!?
|
|
||||||
*/
|
|
||||||
static int createQemuCreate(virConnectPtr conn,
|
|
||||||
virStorageVolDefPtr vol,
|
|
||||||
virStorageVolDefPtr inputvol) {
|
|
||||||
char size[100];
|
|
||||||
const char *imgargv[4];
|
|
||||||
|
|
||||||
if (inputvol) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
"%s",
|
|
||||||
_("cannot copy from volume with qcow-create"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("unsupported storage vol type %d"),
|
|
||||||
vol->target.format);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (vol->backingStore.path != NULL) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_NO_SUPPORT,
|
|
||||||
_("copy-on-write image not supported with "
|
|
||||||
"qcow-create"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Size in MB - yes different units to qemu-img :-( */
|
|
||||||
snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024);
|
|
||||||
|
|
||||||
imgargv[0] = virFindFileInPath("qcow-create");
|
|
||||||
imgargv[1] = size;
|
|
||||||
imgargv[2] = vol->target.path;
|
|
||||||
imgargv[3] = NULL;
|
|
||||||
|
|
||||||
if (virRun(conn, imgargv, NULL) < 0) {
|
|
||||||
VIR_FREE(imgargv[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
VIR_FREE(imgargv[0]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static createFile
|
|
||||||
toolTypeToFunction(virConnectPtr conn, int tool_type)
|
|
||||||
{
|
|
||||||
switch (tool_type) {
|
|
||||||
case TOOL_KVM_IMG:
|
|
||||||
case TOOL_QEMU_IMG:
|
|
||||||
return createQemuImg;
|
|
||||||
case TOOL_QCOW_CREATE:
|
|
||||||
return createQemuCreate;
|
|
||||||
default:
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("Unknown file create tool type '%d'."),
|
|
||||||
tool_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
findImageTool(char **tool)
|
|
||||||
{
|
|
||||||
int tool_type = -1;
|
|
||||||
char *tmp = NULL;
|
|
||||||
|
|
||||||
if ((tmp = virFindFileInPath("kvm-img")) != NULL) {
|
|
||||||
tool_type = TOOL_KVM_IMG;
|
|
||||||
} else if ((tmp = virFindFileInPath("qemu-img")) != NULL) {
|
|
||||||
tool_type = TOOL_QEMU_IMG;
|
|
||||||
} else if ((tmp = virFindFileInPath("qcow-create")) != NULL) {
|
|
||||||
tool_type = TOOL_QCOW_CREATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tool)
|
|
||||||
*tool = tmp;
|
|
||||||
else
|
|
||||||
VIR_FREE(tmp);
|
|
||||||
|
|
||||||
return tool_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static createFile
|
|
||||||
buildVolFromFunction(virConnectPtr conn,
|
|
||||||
virStorageVolDefPtr vol,
|
|
||||||
virStorageVolDefPtr inputvol)
|
|
||||||
{
|
|
||||||
int tool_type;
|
|
||||||
|
|
||||||
if (!inputvol)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* If either volume is a non-raw file vol, we need to use an external
|
|
||||||
* tool for converting
|
|
||||||
*/
|
|
||||||
if ((vol->type == VIR_STORAGE_VOL_FILE &&
|
|
||||||
vol->target.format != VIR_STORAGE_VOL_FILE_RAW) ||
|
|
||||||
(inputvol->type == VIR_STORAGE_VOL_FILE &&
|
|
||||||
inputvol->target.format != VIR_STORAGE_VOL_FILE_RAW)) {
|
|
||||||
|
|
||||||
if ((tool_type = findImageTool(NULL)) != -1) {
|
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
"%s", _("creation of non-raw file images is "
|
|
||||||
"not supported without qemu-img."));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return toolTypeToFunction(conn, tool_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return createRaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
|
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
|
||||||
virStorageVolDefPtr vol,
|
virStorageVolDefPtr vol,
|
||||||
@ -1450,15 +1036,18 @@ _virStorageBackendFileSystemVolBuild(virConnectPtr conn,
|
|||||||
int tool_type;
|
int tool_type;
|
||||||
|
|
||||||
if (inputvol) {
|
if (inputvol) {
|
||||||
create_func = buildVolFromFunction(conn, vol, inputvol);
|
create_func = virStorageBackendGetBuildVolFromFunction(conn, vol,
|
||||||
|
inputvol);
|
||||||
if (!create_func)
|
if (!create_func)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) {
|
} else if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) {
|
||||||
create_func = createRaw;
|
create_func = virStorageBackendCreateRaw;
|
||||||
} else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) {
|
} else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) {
|
||||||
create_func = createFileDir;
|
create_func = createFileDir;
|
||||||
} else if ((tool_type = findImageTool(NULL)) != -1) {
|
} else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
|
||||||
if ((create_func = toolTypeToFunction(conn, tool_type)) == NULL)
|
create_func = virStorageBackendFSImageToolTypeToFunc(conn, tool_type);
|
||||||
|
|
||||||
|
if (!create_func)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user