From 46e8049c15a84667a0a74db7f8230c3fda8c2d8e Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Wed, 11 Jan 2017 18:04:15 +0100 Subject: [PATCH] storage: Split utility functions from storage_backend.(ch) The file became a garbage dump for all kinds of utility functions over time. Move them to a separate file so that the files can become a clean interface for the storage backends. --- po/POTFILES.in | 1 + src/Makefile.am | 3 +- src/storage/storage_backend.c | 2954 +----------------------- src/storage/storage_backend.h | 125 - src/storage/storage_backend_disk.c | 1 + src/storage/storage_backend_fs.c | 1 + src/storage/storage_backend_gluster.c | 1 + src/storage/storage_backend_iscsi.c | 1 + src/storage/storage_backend_logical.c | 1 + src/storage/storage_backend_mpath.c | 1 + src/storage/storage_backend_rbd.c | 1 + src/storage/storage_backend_scsi.c | 1 + src/storage/storage_backend_sheepdog.c | 1 + src/storage/storage_backend_zfs.c | 1 + src/storage/storage_driver.c | 1 + src/storage/storage_util.c | 2919 +++++++++++++++++++++++ src/storage/storage_util.h | 149 ++ tests/storagevolxml2argvtest.c | 2 +- 18 files changed, 3117 insertions(+), 3047 deletions(-) create mode 100644 src/storage/storage_util.c create mode 100644 src/storage/storage_util.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 59efd91903..e464c045d7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -177,6 +177,7 @@ src/storage/storage_backend_scsi.c src/storage/storage_backend_sheepdog.c src/storage/storage_backend_zfs.c src/storage/storage_driver.c +src/storage/storage_util.c src/test/test_driver.c src/uml/uml_conf.c src/uml/uml_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index 21a78e07b1..348bd2a306 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -972,7 +972,8 @@ SECRET_DRIVER_SOURCES = \ # Storage backend specific impls STORAGE_DRIVER_SOURCES = \ storage/storage_driver.h storage/storage_driver.c \ - storage/storage_backend.h storage/storage_backend.c + storage/storage_backend.h storage/storage_backend.c \ + storage/storage_util.h storage/storage_util.c STORAGE_DRIVER_FS_SOURCES = \ storage/storage_backend_fs.h storage/storage_backend_fs.c diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 5497eaa0fd..92b08a2b4a 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -24,54 +24,15 @@ #include #include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include "dirname.h" -#ifdef __linux__ -# include -# include -# ifndef FS_NOCOW_FL -# define FS_NOCOW_FL 0x00800000 /* Do not cow file */ -# endif -#endif - -#if WITH_BLKID -# include -#endif - -#if WITH_SELINUX -# include -#endif - -#if HAVE_LINUX_BTRFS_H -# include -#endif #include "datatypes.h" #include "virerror.h" #include "viralloc.h" #include "internal.h" -#include "secret_conf.h" -#include "secret_util.h" -#include "vircrypto.h" -#include "viruuid.h" #include "virstoragefile.h" #include "storage_backend.h" #include "virlog.h" -#include "virfile.h" -#include "virjson.h" -#include "virqemu.h" -#include "stat-time.h" -#include "virstring.h" -#include "virxml.h" -#include "fdstream.h" #if WITH_STORAGE_LVM # include "storage_backend_logical.h" @@ -159,2906 +120,59 @@ static virStorageFileBackendPtr fileBackends[] = { }; -#define READ_BLOCK_SIZE_DEFAULT (1024 * 1024) -#define WRITE_BLOCK_SIZE_DEFAULT (4 * 1024) - -/* - * Perform the O(1) btrfs clone operation, if possible. - * Upon success, return 0. Otherwise, return -1 and set errno. - */ -#if HAVE_LINUX_BTRFS_H -static inline int -btrfsCloneFile(int dest_fd, int src_fd) -{ - return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd); -} -#else -static inline int -btrfsCloneFile(int dest_fd ATTRIBUTE_UNUSED, - int src_fd ATTRIBUTE_UNUSED) -{ - errno = ENOTSUP; - return -1; -} -#endif - -static int ATTRIBUTE_NONNULL(2) -virStorageBackendCopyToFD(virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - int fd, - unsigned long long *total, - bool want_sparse, - bool reflink_copy) -{ - int inputfd = -1; - int amtread = -1; - int ret = 0; - size_t rbytes = READ_BLOCK_SIZE_DEFAULT; - int wbytes = 0; - int interval; - char *zerobuf = NULL; - char *buf = NULL; - struct stat st; - - if ((inputfd = open(inputvol->target.path, O_RDONLY)) < 0) { - ret = -errno; - virReportSystemError(errno, - _("could not open input path '%s'"), - inputvol->target.path); - goto cleanup; - } - -#ifdef __linux__ - if (ioctl(fd, BLKBSZGET, &wbytes) < 0) - wbytes = 0; -#endif - if ((wbytes == 0) && fstat(fd, &st) == 0) - wbytes = st.st_blksize; - if (wbytes < WRITE_BLOCK_SIZE_DEFAULT) - wbytes = WRITE_BLOCK_SIZE_DEFAULT; - - if (VIR_ALLOC_N(zerobuf, wbytes) < 0) { - ret = -errno; - goto cleanup; - } - - if (VIR_ALLOC_N(buf, rbytes) < 0) { - ret = -errno; - goto cleanup; - } - - if (reflink_copy) { - if (btrfsCloneFile(fd, inputfd) < 0) { - ret = -errno; - virReportSystemError(errno, - _("failed to clone files from '%s'"), - inputvol->target.path); - goto cleanup; - } else { - VIR_DEBUG("btrfs clone finished."); - goto cleanup; - } - } - - while (amtread != 0) { - int amtleft; - - if (*total < rbytes) - rbytes = *total; - - if ((amtread = saferead(inputfd, buf, rbytes)) < 0) { - ret = -errno; - virReportSystemError(errno, - _("failed reading from file '%s'"), - inputvol->target.path); - goto cleanup; - } - *total -= amtread; - - /* Loop over amt read in 512 byte increments, looking for sparse - * blocks */ - amtleft = amtread; - do { - interval = ((wbytes > amtleft) ? amtleft : wbytes); - int offset = amtread - amtleft; - - if (want_sparse && memcmp(buf+offset, zerobuf, interval) == 0) { - if (lseek(fd, interval, SEEK_CUR) < 0) { - ret = -errno; - virReportSystemError(errno, - _("cannot extend file '%s'"), - vol->target.path); - goto cleanup; - } - } else if (safewrite(fd, buf+offset, interval) < 0) { - ret = -errno; - virReportSystemError(errno, - _("failed writing to file '%s'"), - vol->target.path); - goto cleanup; - - } - } while ((amtleft -= interval) > 0); - } - - if (fdatasync(fd) < 0) { - ret = -errno; - virReportSystemError(errno, _("cannot sync data to file '%s'"), - vol->target.path); - goto cleanup; - } - - - if (VIR_CLOSE(inputfd) < 0) { - ret = -errno; - virReportSystemError(errno, - _("cannot close file '%s'"), - inputvol->target.path); - goto cleanup; - } - inputfd = -1; - - cleanup: - VIR_FORCE_CLOSE(inputfd); - - VIR_FREE(zerobuf); - VIR_FREE(buf); - - return ret; -} - -static int -virStorageBackendCreateBlockFrom(virConnectPtr conn ATTRIBUTE_UNUSED, - virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags) -{ - int fd = -1; - int ret = -1; - unsigned long long remain; - struct stat st; - gid_t gid; - uid_t uid; - mode_t mode; - bool reflink_copy = false; - - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | - VIR_STORAGE_VOL_CREATE_REFLINK, - -1); - - if (flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("metadata preallocation is not supported for block " - "volumes")); - goto cleanup; - } - - if (flags & VIR_STORAGE_VOL_CREATE_REFLINK) - reflink_copy = true; - - if ((fd = open(vol->target.path, O_RDWR)) < 0) { - virReportSystemError(errno, - _("cannot create path '%s'"), - vol->target.path); - goto cleanup; - } - - remain = vol->target.capacity; - - if (inputvol) { - if (virStorageBackendCopyToFD(vol, inputvol, fd, &remain, - false, reflink_copy) < 0) - goto cleanup; - } - - if (fstat(fd, &st) == -1) { - virReportSystemError(errno, _("stat of '%s' failed"), - vol->target.path); - goto cleanup; - } - uid = (vol->target.perms->uid != st.st_uid) ? vol->target.perms->uid - : (uid_t) -1; - gid = (vol->target.perms->gid != st.st_gid) ? vol->target.perms->gid - : (gid_t) -1; - if (((uid != (uid_t) -1) || (gid != (gid_t) -1)) - && (fchown(fd, uid, gid) < 0)) { - virReportSystemError(errno, - _("cannot chown '%s' to (%u, %u)"), - vol->target.path, (unsigned int) uid, - (unsigned int) gid); - goto cleanup; - } - - mode = (vol->target.perms->mode == (mode_t) -1 ? - VIR_STORAGE_DEFAULT_VOL_PERM_MODE : vol->target.perms->mode); - if (fchmod(fd, mode) < 0) { - virReportSystemError(errno, - _("cannot set mode of '%s' to %04o"), - vol->target.path, mode); - goto cleanup; - } - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, - _("cannot close file '%s'"), - vol->target.path); - goto cleanup; - } - fd = -1; - - ret = 0; - cleanup: - VIR_FORCE_CLOSE(fd); - - return ret; -} - -static int -createRawFile(int fd, virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - bool reflink_copy) -{ - bool need_alloc = true; - int ret = 0; - unsigned long long pos = 0; - - /* If the new allocation is lower than the capacity of the original file, - * the cloned volume will be sparse */ - if (inputvol && - vol->target.allocation < inputvol->target.capacity) - need_alloc = false; - - /* Seek to the final size, so the capacity is available upfront - * for progress reporting */ - if (ftruncate(fd, vol->target.capacity) < 0) { - ret = -errno; - virReportSystemError(errno, - _("cannot extend file '%s'"), - vol->target.path); - goto cleanup; - } - -/* Avoid issues with older kernel's namespace pollution. */ -#if HAVE_FALLOCATE - 0 - /* Try to preallocate all requested disk space, but fall back to - * other methods if this fails with ENOSYS or EOPNOTSUPP. If allocation - * is 0 (or less than 0), then fallocate will fail with EINVAL. - * NOTE: do not use posix_fallocate; posix_fallocate falls back - * to writing zeroes block by block in case fallocate isn't - * available, and since we're going to copy data from another - * file it doesn't make sense to write the file twice. */ - if (vol->target.allocation && need_alloc) { - if (fallocate(fd, 0, 0, vol->target.allocation) == 0) { - need_alloc = false; - } else if (errno != ENOSYS && errno != EOPNOTSUPP) { - ret = -errno; - virReportSystemError(errno, - _("cannot allocate %llu bytes in file '%s'"), - vol->target.allocation, vol->target.path); - goto cleanup; - } - } -#endif - - if (inputvol) { - unsigned long long remain = inputvol->target.capacity; - /* allow zero blocks to be skipped if we've requested sparse - * allocation (allocation < capacity) or we have already - * been able to allocate the required space. */ - if ((ret = virStorageBackendCopyToFD(vol, inputvol, fd, &remain, - !need_alloc, reflink_copy)) < 0) - goto cleanup; - - /* If the new allocation is greater than the original capacity, - * but fallocate failed, fill the rest with zeroes. - */ - pos = inputvol->target.capacity - remain; - } - - if (need_alloc && (vol->target.allocation - pos > 0)) { - if (safezero(fd, pos, vol->target.allocation - pos) < 0) { - ret = -errno; - virReportSystemError(errno, _("cannot fill file '%s'"), - vol->target.path); - goto cleanup; - } - } - - if (fsync(fd) < 0) { - ret = -errno; - virReportSystemError(errno, _("cannot sync data to file '%s'"), - vol->target.path); - goto cleanup; - } - - cleanup: - return ret; -} - -int -virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags) -{ - int ret = -1; - int fd = -1; - int operation_flags; - bool reflink_copy = false; - mode_t open_mode = VIR_STORAGE_DEFAULT_VOL_PERM_MODE; - bool created = false; - - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | - VIR_STORAGE_VOL_CREATE_REFLINK, - -1); - - if (flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("metadata preallocation is not supported for raw " - "volumes")); - goto cleanup; - } - - if (vol->target.backingStore) { - virReportError(VIR_ERR_NO_SUPPORT, "%s", - _("backing storage not supported for raw volumes")); - goto cleanup; - } - - if (flags & VIR_STORAGE_VOL_CREATE_REFLINK) - reflink_copy = true; - - - if (vol->target.encryption != NULL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("storage pool does not support encrypted volumes")); - goto cleanup; - } - - operation_flags = VIR_FILE_OPEN_FORCE_MODE | VIR_FILE_OPEN_FORCE_OWNER; - if (pool->def->type == VIR_STORAGE_POOL_NETFS) - operation_flags |= VIR_FILE_OPEN_FORK; - - if (vol->target.perms->mode != (mode_t) -1) - open_mode = vol->target.perms->mode; - - if ((fd = virFileOpenAs(vol->target.path, - O_RDWR | O_CREAT | O_EXCL, - open_mode, - vol->target.perms->uid, - vol->target.perms->gid, - operation_flags)) < 0) { - virReportSystemError(-fd, - _("Failed to create file '%s'"), - vol->target.path); - goto cleanup; - } - created = true; - - if (vol->target.nocow) { -#ifdef __linux__ - int attr; - - /* Set NOCOW flag. This is an optimisation for btrfs. - * The FS_IOC_SETFLAGS ioctl return value will be ignored since any - * failure of this operation should not block the volume creation. - */ - if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0) { - virReportSystemError(errno, "%s", _("Failed to get fs flags")); - } else { - attr |= FS_NOCOW_FL; - if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0) { - virReportSystemError(errno, "%s", - _("Failed to set NOCOW flag")); - } - } -#endif - } - - if ((ret = createRawFile(fd, vol, inputvol, reflink_copy)) < 0) - /* createRawFile already reported the exact error. */ - ret = -1; - - cleanup: - if (ret < 0 && created) - ignore_value(virFileRemove(vol->target.path, - vol->target.perms->uid, - vol->target.perms->gid)); - VIR_FORCE_CLOSE(fd); - return ret; -} - -static int -virStorageGenerateSecretUUID(virConnectPtr conn, - unsigned char *uuid) +virStorageBackendPtr +virStorageBackendForType(int type) { - unsigned attempt; - - for (attempt = 0; attempt < 65536; attempt++) { - virSecretPtr tmp; - if (virUUIDGenerate(uuid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unable to generate uuid")); - return -1; - } - tmp = conn->secretDriver->secretLookupByUUID(conn, uuid); - if (tmp == NULL) - return 0; - - virObjectUnref(tmp); - } - - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("too many conflicts when generating a uuid")); + size_t i; + for (i = 0; backends[i]; i++) + if (backends[i]->type == type) + return backends[i]; - return -1; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing backend for pool type %d (%s)"), + type, NULLSTR(virStoragePoolTypeToString(type))); + return NULL; } -static int -virStorageGenerateQcowEncryption(virConnectPtr conn, - virStorageVolDefPtr vol) -{ - virSecretDefPtr def = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - virStorageEncryptionPtr enc; - virStorageEncryptionSecretPtr enc_secret = NULL; - virSecretPtr secret = NULL; - char *xml; - unsigned char value[VIR_STORAGE_QCOW_PASSPHRASE_SIZE]; - int ret = -1; - - if (conn->secretDriver == NULL || - conn->secretDriver->secretLookupByUUID == NULL || - conn->secretDriver->secretDefineXML == NULL || - conn->secretDriver->secretSetValue == NULL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("secret storage not supported")); - goto cleanup; - } - - enc = vol->target.encryption; - if (enc->nsecrets != 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("secrets already defined")); - goto cleanup; - } - - if (VIR_ALLOC(enc_secret) < 0 || VIR_REALLOC_N(enc->secrets, 1) < 0 || - VIR_ALLOC(def) < 0) - goto cleanup; - - def->isephemeral = false; - def->isprivate = false; - if (virStorageGenerateSecretUUID(conn, def->uuid) < 0) - goto cleanup; - - def->usage_type = VIR_SECRET_USAGE_TYPE_VOLUME; - if (VIR_STRDUP(def->usage_id, vol->target.path) < 0) - goto cleanup; - xml = virSecretDefFormat(def); - virSecretDefFree(def); - def = NULL; - if (xml == NULL) - goto cleanup; - - secret = conn->secretDriver->secretDefineXML(conn, xml, 0); - if (secret == NULL) { - VIR_FREE(xml); - goto cleanup; - } - VIR_FREE(xml); - - if (virStorageGenerateQcowPassphrase(value) < 0) - goto cleanup; - - if (conn->secretDriver->secretSetValue(secret, value, sizeof(value), 0) < 0) - goto cleanup; - - enc_secret->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE; - enc_secret->seclookupdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; - memcpy(enc_secret->seclookupdef.u.uuid, secret->uuid, VIR_UUID_BUFLEN); - enc->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; - enc->secrets[0] = enc_secret; /* Space for secrets[0] allocated above */ - enc_secret = NULL; - enc->nsecrets = 1; - ret = 0; - - cleanup: - if (secret != NULL) { - if (ret != 0 && - conn->secretDriver->secretUndefine != NULL) - conn->secretDriver->secretUndefine(secret); - virObjectUnref(secret); - } - virBufferFreeAndReset(&buf); - virSecretDefFree(def); - VIR_FREE(enc_secret); - return ret; -} - -static int -virStorageBackendCreateExecCommand(virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virCommandPtr cmd) +virStorageFileBackendPtr +virStorageFileBackendForTypeInternal(int type, + int protocol, + bool report) { - struct stat st; - gid_t gid; - uid_t uid; - mode_t mode = (vol->target.perms->mode == (mode_t) -1 ? - VIR_STORAGE_DEFAULT_VOL_PERM_MODE : - vol->target.perms->mode); - bool filecreated = false; - int ret = -1; - - if ((pool->def->type == VIR_STORAGE_POOL_NETFS) - && (((geteuid() == 0) - && (vol->target.perms->uid != (uid_t) -1) - && (vol->target.perms->uid != 0)) - || ((vol->target.perms->gid != (gid_t) -1) - && (vol->target.perms->gid != getegid())))) { - - virCommandSetUID(cmd, vol->target.perms->uid); - virCommandSetGID(cmd, vol->target.perms->gid); - virCommandSetUmask(cmd, S_IRWXUGO ^ mode); - - if (virCommandRun(cmd, NULL) == 0) { - /* command was successfully run, check if the file was created */ - if (stat(vol->target.path, &st) >= 0) { - filecreated = true; - - /* seems qemu-img disregards umask and open/creates using 0644. - * If that doesn't match what we expect, then let's try to - * re-open the file and attempt to force the mode change. - */ - if (mode != (st.st_mode & S_IRWXUGO)) { - int fd = -1; - int flags = VIR_FILE_OPEN_FORK | VIR_FILE_OPEN_FORCE_MODE; - - if ((fd = virFileOpenAs(vol->target.path, O_RDWR, mode, - vol->target.perms->uid, - vol->target.perms->gid, - flags)) >= 0) { - /* Success - means we're good */ - VIR_FORCE_CLOSE(fd); - ret = 0; - goto cleanup; - } - } - } - } - } + size_t i; - if (!filecreated) { - /* don't change uid/gid/mode if we retry */ - virCommandSetUID(cmd, -1); - virCommandSetGID(cmd, -1); - virCommandSetUmask(cmd, 0); + for (i = 0; fileBackends[i]; i++) { + if (fileBackends[i]->type == type) { + if (type == VIR_STORAGE_TYPE_NETWORK && + fileBackends[i]->protocol != protocol) + continue; - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - if (stat(vol->target.path, &st) < 0) { - virReportSystemError(errno, - _("failed to create %s"), vol->target.path); - goto cleanup; + return fileBackends[i]; } - filecreated = true; - } - - uid = (vol->target.perms->uid != st.st_uid) ? vol->target.perms->uid - : (uid_t) -1; - gid = (vol->target.perms->gid != st.st_gid) ? vol->target.perms->gid - : (gid_t) -1; - if (((uid != (uid_t) -1) || (gid != (gid_t) -1)) - && (chown(vol->target.path, uid, gid) < 0)) { - virReportSystemError(errno, - _("cannot chown %s to (%u, %u)"), - vol->target.path, (unsigned int) uid, - (unsigned int) gid); - goto cleanup; } - if (mode != (st.st_mode & S_IRWXUGO) && - chmod(vol->target.path, mode) < 0) { - virReportSystemError(errno, - _("cannot set mode of '%s' to %04o"), - vol->target.path, mode); - goto cleanup; - } - - ret = 0; - - cleanup: - if (ret < 0 && filecreated) - virFileRemove(vol->target.path, vol->target.perms->uid, - vol->target.perms->gid); - return ret; -} - -/* Create ploop directory with ploop image and DiskDescriptor.xml - * if function fails to create image file the directory will be deleted.*/ -int -virStorageBackendCreatePloop(virConnectPtr conn ATTRIBUTE_UNUSED, - virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags) -{ - int ret = -1; - virCommandPtr cmd = NULL; - char *create_tool = NULL; - bool created = false; - - virCheckFlags(0, -1); - - if (inputvol && inputvol->target.format != VIR_STORAGE_FILE_PLOOP) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported input storage vol type %d"), - inputvol->target.format); - return -1; - } - - if (vol->target.encryption != NULL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("encrypted ploop volumes are not supported with " - "ploop init")); - return -1; - } - - if (vol->target.backingStore != NULL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("copy-on-write ploop volumes are not yet supported")); - return -1; - } + if (!report) + return NULL; - create_tool = virFindFileInPath("ploop"); - if (!create_tool && !inputvol) { + if (type == VIR_STORAGE_TYPE_NETWORK) { virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("unable to find ploop, please install " - "ploop tools")); - return -1; - } - - if (!inputvol) { - if ((virDirCreate(vol->target.path, - (vol->target.perms->mode == (mode_t) -1 ? - VIR_STORAGE_DEFAULT_VOL_PERM_MODE: - vol->target.perms->mode), - vol->target.perms->uid, - vol->target.perms->gid, - 0)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("error creating directory for ploop volume")); - goto cleanup; - } - cmd = virCommandNewArgList(create_tool, "init", "-s", NULL); - virCommandAddArgFormat(cmd, "%lluM", VIR_DIV_UP(vol->target.capacity, - (1024 * 1024))); - virCommandAddArgList(cmd, "-t", "ext4", NULL); - virCommandAddArgFormat(cmd, "%s/root.hds", vol->target.path); - - } else { - vol->target.capacity = inputvol->target.capacity; - cmd = virCommandNewArgList("cp", "-r", inputvol->target.path, - vol->target.path, NULL); - } - created = true; - ret = virCommandRun(cmd, NULL); - cleanup: - virCommandFree(cmd); - VIR_FREE(create_tool); - if (ret < 0 && created) - virFileDeleteTree(vol->target.path); - return ret; -} - -int -virStoragePloopResize(virStorageVolDefPtr vol, - unsigned long long capacity) -{ - int ret = -1; - virCommandPtr cmd = NULL; - char *resize_tool = NULL; - - resize_tool = virFindFileInPath("ploop"); - if (!resize_tool) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unable to find ploop, please install ploop tools")); - return -1; - } - cmd = virCommandNewArgList(resize_tool, "resize", "-s", NULL); - virCommandAddArgFormat(cmd, "%lluM", VIR_DIV_UP(capacity, (1024 * 1024))); - - virCommandAddArgFormat(cmd, "%s/DiskDescriptor.xml", vol->target.path); - - ret = virCommandRun(cmd, NULL); - virCommandFree(cmd); - VIR_FREE(resize_tool); - return ret; -} - -/* Flag values shared w/ storagevolxml2argvtest.c. - * - * QEMU_IMG_BACKING_FORMAT_OPTIONS (added in qemu 0.11) - * QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT - * was made necessary due to 2.0 change to change the default - * qcow2 file format from 0.10 to 1.1. - */ -enum { - QEMU_IMG_BACKING_FORMAT_OPTIONS = 0, - QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT, -}; - -static bool -virStorageBackendQemuImgSupportsCompat(const char *qemuimg) -{ - bool ret = false; - char *output; - virCommandPtr cmd = NULL; - - cmd = virCommandNewArgList(qemuimg, "create", "-o", "?", "-f", "qcow2", - "/dev/null", NULL); - - virCommandAddEnvString(cmd, "LC_ALL=C"); - virCommandSetOutputBuffer(cmd, &output); - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - if (strstr(output, "\ncompat ")) - ret = true; - - cleanup: - virCommandFree(cmd); - VIR_FREE(output); - return ret; -} - - -static int -virStorageBackendQEMUImgBackingFormat(const char *qemuimg) -{ - /* As of QEMU 0.11 the [-o options] support was added via qemu - * commit id '9ea2ea71', so we start with that base and figure - * out what else we have */ - int ret = QEMU_IMG_BACKING_FORMAT_OPTIONS; - - /* QEMU 2.0 changed to using a format that only QEMU 1.1 and newer - * understands. Since we still support QEMU 0.12 and newer, we need - * to be able to handle the previous format as can be set via a - * compat=0.10 option. */ - if (virStorageBackendQemuImgSupportsCompat(qemuimg)) - ret = QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT; - - return ret; -} - -/* The _virStorageBackendQemuImgInfo separates the command line building from - * the volume definition so that qemuDomainSnapshotCreateInactiveExternal can - * use it without needing to deal with a volume. - */ -struct _virStorageBackendQemuImgInfo { - int format; - const char *path; - unsigned long long size_arg; - bool encryption; - bool preallocate; - const char *compat; - virBitmapPtr features; - bool nocow; - - const char *backingPath; - int backingFormat; - - const char *inputPath; - const char *inputFormatStr; - int inputFormat; - - char *secretAlias; - const char *secretPath; -}; - - -static int -virStorageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr enc, - char **opts, - struct _virStorageBackendQemuImgInfo info) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (info.format == VIR_STORAGE_FILE_RAW && enc) { - virQEMUBuildLuksOpts(&buf, enc, info.secretAlias); - } else { - if (info.backingPath) - virBufferAsprintf(&buf, "backing_fmt=%s,", - virStorageFileFormatTypeToString(info.backingFormat)); - if (info.encryption) - virBufferAddLit(&buf, "encryption=on,"); - if (info.preallocate) - virBufferAddLit(&buf, "preallocation=metadata,"); - } - - if (info.nocow) - virBufferAddLit(&buf, "nocow=on,"); - - if (info.compat) - virBufferAsprintf(&buf, "compat=%s,", info.compat); - - if (info.features && info.format == VIR_STORAGE_FILE_QCOW2) { - if (virBitmapIsBitSet(info.features, - VIR_STORAGE_FILE_FEATURE_LAZY_REFCOUNTS)) { - if (STREQ_NULLABLE(info.compat, "0.10")) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("lazy_refcounts not supported with compat" - " level %s"), - info.compat); - goto error; - } - virBufferAddLit(&buf, "lazy_refcounts,"); - } - } - - virBufferTrim(&buf, ",", -1); - - if (virBufferCheckError(&buf) < 0) - goto error; - - *opts = virBufferContentAndReset(&buf); - return 0; - - error: - virBufferFreeAndReset(&buf); - return -1; -} - - -/* virStorageBackendCreateQemuImgCheckEncryption: - * @format: format of file found - * @conn: pointer to connection - * @vol: pointer to volume def - * - * Ensure the proper setup for encryption. - * - * Returns 0 on success, -1 on failure w/ error set - */ -static int -virStorageBackendCreateQemuImgCheckEncryption(int format, - const char *type, - virConnectPtr conn, - virStorageVolDefPtr vol) -{ - virStorageEncryptionPtr enc = vol->target.encryption; - - if (format == VIR_STORAGE_FILE_QCOW || format == VIR_STORAGE_FILE_QCOW2) { - if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW && - enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported volume encryption format %d"), - vol->target.encryption->format); - return -1; - } - if (enc->nsecrets > 1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("too many secrets for qcow encryption")); - return -1; - } - if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT || - enc->nsecrets == 0) { - if (virStorageGenerateQcowEncryption(conn, vol) < 0) - return -1; - } - } else if (format == VIR_STORAGE_FILE_RAW) { - if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported volume encryption format %d"), - vol->target.encryption->format); - return -1; - } - if (enc->nsecrets > 1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("too many secrets for luks encryption")); - return -1; - } - if (enc->nsecrets == 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("no secret provided for luks encryption")); - return -1; - } - if (!virCryptoHaveCipher(VIR_CRYPTO_CIPHER_AES256CBC)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("luks encryption usage requires encrypted " - "secret generation to be supported")); - return -1; - } + _("missing storage backend for network files " + "using %s protocol"), + virStorageNetProtocolTypeToString(protocol)); } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("volume encryption unsupported with format %s"), type); - return -1; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing storage backend for '%s' storage"), + virStorageTypeToString(type)); } - return 0; + return NULL; } -static int -virStorageBackendCreateQemuImgSetInput(virStorageVolDefPtr inputvol, - struct _virStorageBackendQemuImgInfo *info) -{ - if (!(info->inputPath = inputvol->target.path)) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing input volume target path")); - return -1; - } - - info->inputFormat = inputvol->target.format; - if (inputvol->type == VIR_STORAGE_VOL_BLOCK) - info->inputFormat = VIR_STORAGE_FILE_RAW; - if (!(info->inputFormatStr = - virStorageFileFormatTypeToString(info->inputFormat))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol type %d"), - info->inputFormat); - return -1; - } - - return 0; -} - - -static int -virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - struct _virStorageBackendQemuImgInfo *info) -{ - int accessRetCode = -1; - char *absolutePath = NULL; - - if (info->format == VIR_STORAGE_FILE_RAW) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("cannot set backing store for raw volume")); - return -1; - } - - info->backingFormat = vol->target.backingStore->format; - info->backingPath = vol->target.backingStore->path; - - if (info->preallocate) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("metadata preallocation conflicts with backing" - " store")); - return -1; - } - - /* 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 && inputvol->target.backingStore && - STRNEQ_NULLABLE(inputvol->target.backingStore->path, - info->backingPath)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("a different backing store cannot be specified.")); - return -1; - } - - if (!virStorageFileFormatTypeToString(info->backingFormat)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol backing store type %d"), - info->backingFormat); - return -1; - } - - /* Convert relative backing store paths to absolute paths for access - * validation. - */ - if ('/' != *(info->backingPath) && - virAsprintf(&absolutePath, "%s/%s", pool->def->target.path, - info->backingPath) < 0) - return -1; - accessRetCode = access(absolutePath ? absolutePath : - info->backingPath, R_OK); - VIR_FREE(absolutePath); - if (accessRetCode != 0) { - virReportSystemError(errno, - _("inaccessible backing store volume %s"), - info->backingPath); - return -1; - } - - return 0; -} - - -static int -virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, - int imgformat, - virStorageEncryptionInfoDefPtr enc, - struct _virStorageBackendQemuImgInfo info) -{ - char *opts = NULL; - - if (info.format == VIR_STORAGE_FILE_QCOW2 && !info.compat && - imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) - info.compat = "0.10"; - - if (virStorageBackendCreateQemuImgOpts(enc, &opts, info) < 0) - return -1; - if (opts) - virCommandAddArgList(cmd, "-o", opts, NULL); - VIR_FREE(opts); - - return 0; -} - - -/* Add a secret object to the command line: - * --object secret,id=$secretAlias,file=$secretPath - * - * NB: format=raw is assumed - */ -static int -virStorageBackendCreateQemuImgSecretObject(virCommandPtr cmd, - virStorageVolDefPtr vol, - struct _virStorageBackendQemuImgInfo *info) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *commandStr = NULL; - - if (virAsprintf(&info->secretAlias, "%s_luks0", vol->name) < 0) - return -1; - - virBufferAsprintf(&buf, "secret,id=%s,file=", info->secretAlias); - virQEMUBuildBufferEscapeComma(&buf, info->secretPath); - - if (virBufferCheckError(&buf) < 0) { - virBufferFreeAndReset(&buf); - return -1; - } - - commandStr = virBufferContentAndReset(&buf); - - virCommandAddArgList(cmd, "--object", commandStr, NULL); - - VIR_FREE(commandStr); - return 0; -} - - -/* Create a qemu-img virCommand from the supplied binary path, - * volume definitions and imgformat - */ -virCommandPtr -virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags, - const char *create_tool, - int imgformat, - const char *secretPath) -{ - virCommandPtr cmd = NULL; - const char *type; - struct _virStorageBackendQemuImgInfo info = { - .format = vol->target.format, - .path = vol->target.path, - .encryption = vol->target.encryption != NULL, - .preallocate = !!(flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA), - .compat = vol->target.compat, - .features = vol->target.features, - .nocow = vol->target.nocow, - .secretPath = secretPath, - .secretAlias = NULL, - }; - virStorageEncryptionInfoDefPtr enc = NULL; - - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL); - - /* Treat output block devices as 'raw' format */ - if (vol->type == VIR_STORAGE_VOL_BLOCK) - info.format = VIR_STORAGE_FILE_RAW; - - if (!(type = virStorageFileFormatTypeToString(info.format))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol type %d"), - info.format); - return NULL; - } - - if (info.preallocate && info.format != VIR_STORAGE_FILE_QCOW2) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("metadata preallocation only available with qcow2")); - return NULL; - } - if (info.compat && info.format != VIR_STORAGE_FILE_QCOW2) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("compatibility option only available with qcow2")); - return NULL; - } - if (info.features && info.format != VIR_STORAGE_FILE_QCOW2) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("format features only available with qcow2")); - return NULL; - } - if (info.format == VIR_STORAGE_FILE_RAW && - vol->target.encryption != NULL) { - if (inputvol) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("cannot use inputvol with encrypted raw volume")); - return NULL; - } - if (!info.encryption) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing encryption description")); - return NULL; - } - if (vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { - type = "luks"; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Only luks encryption is supported for raw files")); - return NULL; - } - } - - if (inputvol && - virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0) - return NULL; - - if (vol->target.backingStore && - virStorageBackendCreateQemuImgSetBacking(pool, vol, inputvol, - &info) < 0) - return NULL; - - if (info.encryption && - virStorageBackendCreateQemuImgCheckEncryption(info.format, type, - conn, vol) < 0) - return NULL; - - - /* Size in KB */ - info.size_arg = VIR_DIV_UP(vol->target.capacity, 1024); - - cmd = virCommandNew(create_tool); - - /* ignore the backing volume when we're converting a volume */ - if (info.inputPath) - info.backingPath = NULL; - - if (info.inputPath) - virCommandAddArgList(cmd, "convert", "-f", info.inputFormatStr, - "-O", type, NULL); - else - virCommandAddArgList(cmd, "create", "-f", type, NULL); - - if (info.backingPath) - virCommandAddArgList(cmd, "-b", info.backingPath, NULL); - - if (info.format == VIR_STORAGE_FILE_RAW && - vol->target.encryption != NULL && - vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { - if (virStorageBackendCreateQemuImgSecretObject(cmd, vol, &info) < 0) { - VIR_FREE(info.secretAlias); - virCommandFree(cmd); - return NULL; - } - enc = &vol->target.encryption->encinfo; - } - - if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, - enc, info) < 0) { - VIR_FREE(info.secretAlias); - virCommandFree(cmd); - return NULL; - } - VIR_FREE(info.secretAlias); - - if (info.inputPath) - virCommandAddArg(cmd, info.inputPath); - virCommandAddArg(cmd, info.path); - if (!info.inputPath && (info.size_arg || !info.backingPath)) - virCommandAddArgFormat(cmd, "%lluK", info.size_arg); - - return cmd; -} - - -static char * -virStorageBackendCreateQemuImgSecretPath(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol) -{ - virStorageEncryptionPtr enc = vol->target.encryption; - char *secretPath = NULL; - int fd = -1; - uint8_t *secret = NULL; - size_t secretlen = 0; - - if (!enc) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing encryption description")); - return NULL; - } - - if (!conn || !conn->secretDriver || - !conn->secretDriver->secretLookupByUUID || - !conn->secretDriver->secretLookupByUsage || - !conn->secretDriver->secretGetValue) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unable to look up encryption secret")); - return NULL; - } - - if (!(secretPath = virStoragePoolObjBuildTempFilePath(pool, vol))) - goto cleanup; - - if ((fd = mkostemp(secretPath, O_CLOEXEC)) < 0) { - virReportSystemError(errno, "%s", - _("failed to open luks secret file for write")); - goto error; - } - - if (virSecretGetSecretString(conn, &enc->secrets[0]->seclookupdef, - VIR_SECRET_USAGE_TYPE_VOLUME, - &secret, &secretlen) < 0) - goto error; - - if (safewrite(fd, secret, secretlen) < 0) { - virReportSystemError(errno, "%s", - _("failed to write luks secret file")); - goto error; - } - VIR_FORCE_CLOSE(fd); - - if ((vol->target.perms->uid != (uid_t) -1) && - (vol->target.perms->gid != (gid_t) -1)) { - if (chown(secretPath, vol->target.perms->uid, - vol->target.perms->gid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to chown luks secret file")); - goto error; - } - } - - cleanup: - VIR_DISPOSE_N(secret, secretlen); - VIR_FORCE_CLOSE(fd); - - return secretPath; - - error: - unlink(secretPath); - VIR_FREE(secretPath); - goto cleanup; -} - - -int -virStorageBackendCreateQemuImg(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags) -{ - int ret = -1; - char *create_tool; - int imgformat; - virCommandPtr cmd; - char *secretPath = NULL; - - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); - - create_tool = virFindFileInPath("qemu-img"); - if (!create_tool) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("creation of non-raw file images is " - "not supported without qemu-img.")); - return -1; - } - - imgformat = virStorageBackendQEMUImgBackingFormat(create_tool); - if (imgformat < 0) - goto cleanup; - - if (vol->target.format == VIR_STORAGE_FILE_RAW && - vol->target.encryption && - vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { - if (!(secretPath = - virStorageBackendCreateQemuImgSecretPath(conn, pool, vol))) - goto cleanup; - } - - cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol, - flags, create_tool, - imgformat, secretPath); - if (!cmd) - goto cleanup; - - ret = virStorageBackendCreateExecCommand(pool, vol, cmd); - - virCommandFree(cmd); - cleanup: - if (secretPath) { - unlink(secretPath); - VIR_FREE(secretPath); - } - VIR_FREE(create_tool); - return ret; -} - -virStorageBackendBuildVolFrom -virStorageBackendGetBuildVolFromFunction(virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol) -{ - if (!inputvol) - return NULL; - - /* If either volume is a non-raw file vol, or uses encryption, - * we need to use an external tool for converting - */ - if ((vol->type == VIR_STORAGE_VOL_FILE && - (vol->target.format != VIR_STORAGE_FILE_RAW || - vol->target.encryption != NULL)) || - (inputvol->type == VIR_STORAGE_VOL_FILE && - (inputvol->target.format != VIR_STORAGE_FILE_RAW || - inputvol->target.encryption != NULL))) { - return virStorageBackendCreateQemuImg; - } - - if (vol->type == VIR_STORAGE_VOL_PLOOP) - return virStorageBackendCreatePloop; - if (vol->type == VIR_STORAGE_VOL_BLOCK) - return virStorageBackendCreateBlockFrom; - else - return virStorageBackendCreateRaw; -} - - -virStorageBackendPtr -virStorageBackendForType(int type) -{ - size_t i; - for (i = 0; backends[i]; i++) - if (backends[i]->type == type) - return backends[i]; - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing backend for pool type %d (%s)"), - type, NULLSTR(virStoragePoolTypeToString(type))); - return NULL; -} - - -virStorageFileBackendPtr -virStorageFileBackendForTypeInternal(int type, - int protocol, - bool report) -{ - size_t i; - - for (i = 0; fileBackends[i]; i++) { - if (fileBackends[i]->type == type) { - if (type == VIR_STORAGE_TYPE_NETWORK && - fileBackends[i]->protocol != protocol) - continue; - - return fileBackends[i]; - } - } - - if (!report) - return NULL; - - if (type == VIR_STORAGE_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing storage backend for network files " - "using %s protocol"), - virStorageNetProtocolTypeToString(protocol)); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing storage backend for '%s' storage"), - virStorageTypeToString(type)); - } - - return NULL; -} - - -virStorageFileBackendPtr -virStorageFileBackendForType(int type, - int protocol) +virStorageFileBackendPtr +virStorageFileBackendForType(int type, + int protocol) { return virStorageFileBackendForTypeInternal(type, protocol, true); } - - -struct diskType { - int part_table_type; - unsigned short offset; - unsigned short length; - unsigned long long magic; -}; - - -static struct diskType const disk_types[] = { - { VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CULL }, - { VIR_STORAGE_POOL_DISK_GPT, 0x200, 8, 0x5452415020494645ULL }, - { VIR_STORAGE_POOL_DISK_DVH, 0x0, 4, 0x41A9E50BULL }, - { VIR_STORAGE_POOL_DISK_MAC, 0x0, 2, 0x5245ULL }, - { VIR_STORAGE_POOL_DISK_BSD, 0x40, 4, 0x82564557ULL }, - { VIR_STORAGE_POOL_DISK_SUN, 0x1fc, 2, 0xBEDAULL }, - /* - * NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so - * we can't use that. At the moment I'm relying on the "dummy" IPL - * bootloader data that comes from parted. Luckily, the chances of running - * into a pc98 machine running libvirt are approximately nil. - */ - /*{ 0x1fe, 2, 0xAA55UL },*/ - { VIR_STORAGE_POOL_DISK_PC98, 0x0, 8, 0x314C5049000000CBULL }, - /* - * NOTE: the order is important here; some other disk types (like GPT and - * and PC98) also have 0x55AA at this offset. For that reason, the DOS - * one must be the last one. - */ - { VIR_STORAGE_POOL_DISK_DOS, 0x1fe, 2, 0xAA55ULL }, - { -1, 0x0, 0, 0x0ULL }, -}; - - -/* - * virStorageBackendDetectBlockVolFormatFD - * @target: target definition ptr of volume to update - * @fd: fd of storage volume to update, - * @readflags: VolReadErrorMode flags to handle read error after open - * is successful, but read is not. - * - * Returns 0 for success, -1 on a legitimate error condition, -2 if - * the read error is desired to be ignored (along with appropriate - * VIR_WARN of the issue). - */ -static int -virStorageBackendDetectBlockVolFormatFD(virStorageSourcePtr target, - int fd, - unsigned int readflags) -{ - size_t i; - off_t start; - unsigned char buffer[1024]; - ssize_t bytes; - - /* make sure to set the target format "unknown" to begin with */ - target->format = VIR_STORAGE_POOL_DISK_UNKNOWN; - - start = lseek(fd, 0, SEEK_SET); - if (start < 0) { - virReportSystemError(errno, - _("cannot seek to beginning of file '%s'"), - target->path); - return -1; - } - bytes = saferead(fd, buffer, sizeof(buffer)); - if (bytes < 0) { - if (readflags & VIR_STORAGE_VOL_READ_NOERROR) { - VIR_WARN("ignoring failed saferead of file '%s'", - target->path); - return -2; - } else { - virReportSystemError(errno, - _("cannot read beginning of file '%s'"), - target->path); - return -1; - } - } - - for (i = 0; disk_types[i].part_table_type != -1; i++) { - if (disk_types[i].offset + disk_types[i].length > bytes) - continue; - if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic, - disk_types[i].length) == 0) { - target->format = disk_types[i].part_table_type; - break; - } - } - - if (target->format == VIR_STORAGE_POOL_DISK_UNKNOWN) - VIR_DEBUG("cannot determine the target format for '%s'", - target->path); - - return 0; -} - - -/* - * Allows caller to silently ignore files with improper mode - * - * Returns -1 on error. If VIR_STORAGE_VOL_OPEN_NOERROR is passed, we - * return -2 if file mode is unexpected or the volume is a dangling - * symbolic link. - */ -int -virStorageBackendVolOpen(const char *path, struct stat *sb, - unsigned int flags) -{ - int fd, mode = 0; - char *base = last_component(path); - bool noerror = (flags & VIR_STORAGE_VOL_OPEN_NOERROR); - - if (lstat(path, sb) < 0) { - if (errno == ENOENT) { - if (noerror) { - VIR_WARN("ignoring missing file '%s'", path); - return -2; - } - virReportError(VIR_ERR_NO_STORAGE_VOL, - _("no storage vol with matching path '%s'"), - path); - return -1; - } - virReportSystemError(errno, - _("cannot stat file '%s'"), - path); - return -1; - } - - if (S_ISFIFO(sb->st_mode)) { - if (noerror) { - VIR_WARN("ignoring FIFO '%s'", path); - return -2; - } - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Volume path '%s' is a FIFO"), path); - return -1; - } else if (S_ISSOCK(sb->st_mode)) { - if (noerror) { - VIR_WARN("ignoring socket '%s'", path); - return -2; - } - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Volume path '%s' is a socket"), path); - return -1; - } - - /* O_NONBLOCK should only matter during open() for fifos and - * sockets, which we already filtered; but using it prevents a - * TOCTTOU race. However, later on we will want to read() the - * header from this fd, and virFileRead* routines require a - * blocking fd, so fix it up after verifying we avoided a race. - * - * Use of virFileOpenAs allows this path to open a file using - * the uid and gid as it was created in order to open. Since this - * path is not using O_CREAT or O_TMPFILE, mode is meaningless. - * Opening under user/group is especially important in an NFS - * root-squash environment. If the target path isn't on shared - * file system, the open will fail in the OPEN_FORK path. - */ - if ((fd = virFileOpenAs(path, O_RDONLY|O_NONBLOCK|O_NOCTTY, - 0, sb->st_uid, sb->st_gid, - VIR_FILE_OPEN_NOFORK|VIR_FILE_OPEN_FORK)) < 0) { - if ((errno == ENOENT || errno == ELOOP) && - S_ISLNK(sb->st_mode) && noerror) { - VIR_WARN("ignoring dangling symlink '%s'", path); - return -2; - } - if (errno == ENOENT && noerror) { - VIR_WARN("ignoring missing file '%s'", path); - return -2; - } - if (errno == ENXIO && noerror) { - VIR_WARN("ignoring missing fifo '%s'", path); - return -2; - } - if ((errno == EACCES || errno == EPERM) && noerror) { - VIR_WARN("ignoring permission error for '%s'", path); - return -2; - } - - virReportSystemError(errno, _("cannot open volume '%s'"), path); - return -1; - } - - if (fstat(fd, sb) < 0) { - virReportSystemError(errno, _("cannot stat file '%s'"), path); - VIR_FORCE_CLOSE(fd); - return -1; - } - - if (S_ISREG(sb->st_mode)) { - mode = VIR_STORAGE_VOL_OPEN_REG; - } else if (S_ISCHR(sb->st_mode)) { - mode = VIR_STORAGE_VOL_OPEN_CHAR; - } else if (S_ISBLK(sb->st_mode)) { - mode = VIR_STORAGE_VOL_OPEN_BLOCK; - } else if (S_ISDIR(sb->st_mode)) { - mode = VIR_STORAGE_VOL_OPEN_DIR; - - if (STREQ(base, ".") || - STREQ(base, "..")) { - VIR_FORCE_CLOSE(fd); - if (noerror) { - VIR_INFO("Skipping special dir '%s'", base); - return -2; - } - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Cannot use volume path '%s'"), path); - return -1; - } - } else { - VIR_FORCE_CLOSE(fd); - if (noerror) { - VIR_WARN("ignoring unexpected type for file '%s'", path); - return -2; - } - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected type for file '%s'"), path); - return -1; - } - - if (virSetBlocking(fd, true) < 0) { - VIR_FORCE_CLOSE(fd); - virReportSystemError(errno, _("unable to set blocking mode for '%s'"), - path); - return -1; - } - - if (!(mode & flags)) { - VIR_FORCE_CLOSE(fd); - if (noerror) { - VIR_INFO("Skipping volume '%s'", path); - return -2; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected storage mode for '%s'"), path); - return -1; - } - - return fd; -} - -/* virStorageIsPloop function checks whether given directory is ploop volume's - * directory. - */ -bool -virStorageBackendIsPloopDir(char *path) -{ - bool ret = false; - char *root = NULL; - char *desc = NULL; - if (virAsprintf(&root, "%s/root.hds", path) < 0) - return ret; - if (!virFileExists(root)) - goto cleanup; - if (virAsprintf(&desc, "%s/DiskDescriptor.xml", path) < 0) - goto cleanup; - if (!virFileExists(desc)) - goto cleanup; - - ret = true; - cleanup: - VIR_FREE(root); - VIR_FREE(desc); - return ret; -} - -/* In case of ploop volumes, path to volume is the path to the ploop - * directory. To get information about allocation, header information - * and etc. we need to perform virStorageBackendVolOpen and - * virStorageBackendUpdateVolTargetFd once again. - */ -int -virStorageBackendRedoPloopUpdate(virStorageSourcePtr target, struct stat *sb, - int *fd, unsigned int flags) -{ - char *path = NULL; - int ret = -1; - - if (virAsprintf(&path, "%s/root.hds", target->path) < 0) - return -1; - VIR_FORCE_CLOSE(*fd); - if ((*fd = virStorageBackendVolOpen(path, sb, flags)) < 0) - goto cleanup; - ret = virStorageBackendUpdateVolTargetInfoFD(target, *fd, sb); - - cleanup: - - VIR_FREE(path); - return ret; -} - -/* - * virStorageBackendUpdateVolTargetInfo - * @voltype: Volume type - * @target: target definition ptr of volume to update - * @withBlockVolFormat: true if caller determined a block file - * @openflags: various VolOpenCheckMode flags to handle errors on open - * @readflags: VolReadErrorMode flags to handle read error after open - * is successful, but read is not. - * - * Returns 0 for success, -1 on a legitimate error condition, and -2 - * if the openflags used VIR_STORAGE_VOL_OPEN_NOERROR and some sort of - * open error occurred. It is up to the caller to handle. A -2 may also - * be returned if the caller passed a readflagsflag. - */ -int -virStorageBackendUpdateVolTargetInfo(virStorageVolType voltype, - virStorageSourcePtr target, - bool withBlockVolFormat, - unsigned int openflags, - unsigned int readflags) -{ - int ret, fd = -1; - struct stat sb; - char *buf = NULL; - ssize_t len = VIR_STORAGE_MAX_HEADER; - - if ((ret = virStorageBackendVolOpen(target->path, &sb, openflags)) < 0) - goto cleanup; - fd = ret; - - if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd, &sb)) < 0) - goto cleanup; - - if ((voltype == VIR_STORAGE_VOL_FILE || voltype == VIR_STORAGE_VOL_BLOCK) && - target->format != VIR_STORAGE_FILE_NONE) { - if (S_ISDIR(sb.st_mode)) { - if (virStorageBackendIsPloopDir(target->path)) { - if ((ret = virStorageBackendRedoPloopUpdate(target, &sb, &fd, - openflags)) < 0) - goto cleanup; - target->format = VIR_STORAGE_FILE_PLOOP; - } else { - ret = 0; - goto cleanup; - } - } - - if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { - virReportSystemError(errno, _("cannot seek to start of '%s'"), target->path); - ret = -1; - goto cleanup; - } - - if ((len = virFileReadHeaderFD(fd, len, &buf)) < 0) { - if (readflags & VIR_STORAGE_VOL_READ_NOERROR) { - VIR_WARN("ignoring failed header read for '%s'", - target->path); - ret = -2; - } else { - virReportSystemError(errno, - _("cannot read header '%s'"), - target->path); - ret = -1; - } - goto cleanup; - } - - if (virStorageSourceUpdateCapacity(target, buf, len, false) < 0) { - ret = -1; - goto cleanup; - } - } - - if (withBlockVolFormat) { - if ((ret = virStorageBackendDetectBlockVolFormatFD(target, fd, - readflags)) < 0) - goto cleanup; - } - - cleanup: - VIR_FORCE_CLOSE(fd); - VIR_FREE(buf); - return ret; -} - -/* - * virStorageBackendUpdateVolInfo - * @vol: Pointer to a volume storage definition - * @withBlockVolFormat: true if the caller determined a block file - * @openflags: various VolOpenCheckMode flags to handle errors on open - * @readflags: various VolReadErrorMode flags to handle errors on read - * - * Returns 0 for success, -1 on a legitimate error condition, and -2 - * if the openflags used VIR_STORAGE_VOL_OPEN_NOERROR and some sort of - * open error occurred. It is up to the caller to handle. - */ -int -virStorageBackendUpdateVolInfo(virStorageVolDefPtr vol, - bool withBlockVolFormat, - unsigned int openflags, - unsigned int readflags) -{ - int ret; - - if ((ret = virStorageBackendUpdateVolTargetInfo(vol->type, - &vol->target, - withBlockVolFormat, - openflags, readflags)) < 0) - return ret; - - if (vol->target.backingStore && - (ret = virStorageBackendUpdateVolTargetInfo(VIR_STORAGE_VOL_FILE, - vol->target.backingStore, - withBlockVolFormat, - VIR_STORAGE_VOL_OPEN_DEFAULT | - VIR_STORAGE_VOL_OPEN_NOERROR, - readflags) < 0)) - return ret; - - return 0; -} - -/* - * virStorageBackendUpdateVolTargetInfoFD: - * @target: target definition ptr of volume to update - * @fd: fd of storage volume to update, via virStorageBackendOpenVol*, or -1 - * @sb: details about file (must match @fd, if that is provided) - * - * Returns 0 for success, -1 on a legitimate error condition. - */ -int -virStorageBackendUpdateVolTargetInfoFD(virStorageSourcePtr target, - int fd, - struct stat *sb) -{ -#if WITH_SELINUX - security_context_t filecon = NULL; -#endif - - if (virStorageSourceUpdateBackingSizes(target, fd, sb) < 0) - return -1; - - if (!target->perms && VIR_ALLOC(target->perms) < 0) - return -1; - target->perms->mode = sb->st_mode & S_IRWXUGO; - target->perms->uid = sb->st_uid; - target->perms->gid = sb->st_gid; - - if (!target->timestamps && VIR_ALLOC(target->timestamps) < 0) - return -1; - target->timestamps->atime = get_stat_atime(sb); - target->timestamps->btime = get_stat_birthtime(sb); - target->timestamps->ctime = get_stat_ctime(sb); - target->timestamps->mtime = get_stat_mtime(sb); - - VIR_FREE(target->perms->label); - -#if WITH_SELINUX - /* XXX: make this a security driver call */ - if (fd >= 0) { - if (fgetfilecon_raw(fd, &filecon) == -1) { - if (errno != ENODATA && errno != ENOTSUP) { - virReportSystemError(errno, - _("cannot get file context of '%s'"), - target->path); - return -1; - } - } else { - if (VIR_STRDUP(target->perms->label, filecon) < 0) { - freecon(filecon); - return -1; - } - freecon(filecon); - } - } -#endif - - return 0; -} - -bool -virStorageBackendPoolPathIsStable(const char *path) -{ - if (path == NULL || STREQ(path, "/dev") || STREQ(path, "/dev/")) - return false; - - if (!STRPREFIX(path, "/dev/")) - return false; - - return true; -} - -/* - * Given a volume path directly in /dev/XXX, iterate over the - * entries in the directory pool->def->target.path and find the - * first symlink pointing to the volume path. - * - * If, the target.path is /dev/, then return the original volume - * path. - * - * If no symlink is found, then return the original volume path - * - * Typically target.path is one of the /dev/disk/by-XXX dirs - * with stable paths. - * - * If 'loop' is true, we use a timeout loop to give dynamic paths - * a change to appear. - */ -char * -virStorageBackendStablePath(virStoragePoolObjPtr pool, - const char *devpath, - bool loop) -{ - DIR *dh; - struct dirent *dent; - char *stablepath; - int opentries = 0; - int retry = 0; - int direrr; - - /* Logical pools are under /dev but already have stable paths */ - if (pool->def->type == VIR_STORAGE_POOL_LOGICAL || - !virStorageBackendPoolPathIsStable(pool->def->target.path)) - goto ret_strdup; - - /* We loop here because /dev/disk/by-{id,path} may not have existed - * before we started this operation, so we have to give it some time to - * get created. - */ - reopen: - if (virDirOpenQuiet(&dh, pool->def->target.path) < 0) { - opentries++; - if (loop && errno == ENOENT && opentries < 50) { - usleep(100 * 1000); - goto reopen; - } - virReportSystemError(errno, - _("cannot read dir '%s'"), - pool->def->target.path); - return NULL; - } - - /* The pool is pointing somewhere like /dev/disk/by-path - * or /dev/disk/by-id, so we need to check all symlinks in - * the target directory and figure out which one points - * to this device node. - * - * And it might need some time till the stable path shows - * up, so add timeout to retry here. Ignore readdir failures, - * since we have a fallback. - */ - retry: - while ((direrr = virDirRead(dh, &dent, NULL)) > 0) { - if (virAsprintf(&stablepath, "%s/%s", - pool->def->target.path, - dent->d_name) == -1) { - VIR_DIR_CLOSE(dh); - return NULL; - } - - if (virFileLinkPointsTo(stablepath, devpath)) { - VIR_DIR_CLOSE(dh); - return stablepath; - } - - VIR_FREE(stablepath); - } - - if (!direrr && loop && ++retry < 100) { - usleep(100 * 1000); - goto retry; - } - - VIR_DIR_CLOSE(dh); - - ret_strdup: - /* Couldn't find any matching stable link so give back - * the original non-stable dev path - */ - - ignore_value(VIR_STRDUP(stablepath, devpath)); - - return stablepath; -} - -/* - * Check whether the ploop image has snapshots. - * return: -1 - failed to check - * 0 - no snapshots - * 1 - at least one snapshot - */ -static int -virStorageBackendPloopHasSnapshots(char *path) -{ - virCommandPtr cmd = NULL; - char *output = NULL; - char *snap_tool = NULL; - int ret = -1; - - snap_tool = virFindFileInPath("ploop"); - if (!snap_tool) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("unable to find ploop, please install " - "ploop tools")); - return ret; - } - - cmd = virCommandNewArgList(snap_tool, "snapshot-list", NULL); - virCommandAddArgFormat(cmd, "%s/DiskDescriptor.xml", path); - virCommandSetOutputBuffer(cmd, &output); - - if ((ret = virCommandRun(cmd, NULL)) < 0) - goto cleanup; - - if (!strstr(output, "root.hds.")) { - ret = 1; - goto cleanup; - } - ret = 0; - - cleanup: - VIR_FREE(output); - virCommandFree(cmd); - return ret; -} - -int -virStorageBackendVolUploadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, - virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - virStorageVolDefPtr vol, - virStreamPtr stream, - unsigned long long offset, - unsigned long long len, - unsigned int flags) -{ - char *path = NULL; - char *target_path = vol->target.path; - int ret = -1; - int has_snap = 0; - - virCheckFlags(0, -1); - /* if volume has target format VIR_STORAGE_FILE_PLOOP - * we need to restore DiskDescriptor.xml, according to - * new contents of volume. This operation will be perfomed - * when volUpload is fully finished. */ - if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { - /* Fail if the volume contains snapshots or we failed to check it.*/ - has_snap = virStorageBackendPloopHasSnapshots(vol->target.path); - if (has_snap < 0) { - goto cleanup; - } else if (!has_snap) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("can't upload volume, all existing snapshots" - " will be lost")); - goto cleanup; - } - - if (virAsprintf(&path, "%s/root.hds", vol->target.path) < 0) - return -1; - target_path = path; - } - - /* Not using O_CREAT because the file is required to already exist at - * this point */ - ret = virFDStreamOpenBlockDevice(stream, target_path, - offset, len, O_WRONLY); - - cleanup: - VIR_FREE(path); - return ret; -} - -int -virStorageBackendVolDownloadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, - virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - virStorageVolDefPtr vol, - virStreamPtr stream, - unsigned long long offset, - unsigned long long len, - unsigned int flags) -{ - char *path = NULL; - char *target_path = vol->target.path; - int ret = -1; - int has_snap = 0; - - virCheckFlags(0, -1); - if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { - has_snap = virStorageBackendPloopHasSnapshots(vol->target.path); - if (has_snap < 0) { - goto cleanup; - } else if (!has_snap) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("can't download volume, all existing snapshots" - " will be lost")); - goto cleanup; - } - if (virAsprintf(&path, "%s/root.hds", vol->target.path) < 0) - goto cleanup; - target_path = path; - } - - ret = virFDStreamOpenBlockDevice(stream, target_path, - offset, len, O_RDONLY); - - cleanup: - VIR_FREE(path); - return ret; -} - - -/* If the volume we're wiping is already a sparse file, we simply - * truncate and extend it to its original size, filling it with - * zeroes. This behavior is guaranteed by POSIX: - * - * http://www.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html - * - * If fildes refers to a regular file, the ftruncate() function shall - * cause the size of the file to be truncated to length. If the size - * of the file previously exceeded length, the extra data shall no - * longer be available to reads on the file. If the file previously - * was smaller than this size, ftruncate() shall increase the size of - * the file. If the file size is increased, the extended area shall - * appear as if it were zero-filled. - */ -static int -virStorageBackendVolZeroSparseFileLocal(const char *path, - off_t size, - int fd) -{ - if (ftruncate(fd, 0) < 0) { - virReportSystemError(errno, - _("Failed to truncate volume with " - "path '%s' to 0 bytes"), - path); - return -1; - } - - if (ftruncate(fd, size) < 0) { - virReportSystemError(errno, - _("Failed to truncate volume with " - "path '%s' to %ju bytes"), - path, (uintmax_t)size); - return -1; - } - - return 0; -} - - -static int -virStorageBackendWipeLocal(const char *path, - int fd, - unsigned long long wipe_len, - size_t writebuf_length) -{ - int ret = -1, written = 0; - unsigned long long remaining = 0; - size_t write_size = 0; - char *writebuf = NULL; - - VIR_DEBUG("wiping start: 0 len: %llu", wipe_len); - - if (VIR_ALLOC_N(writebuf, writebuf_length) < 0) - goto cleanup; - - if (lseek(fd, 0, SEEK_SET) < 0) { - virReportSystemError(errno, - _("Failed to seek to the start in volume " - "with path '%s'"), - path); - goto cleanup; - } - - remaining = wipe_len; - while (remaining > 0) { - - write_size = (writebuf_length < remaining) ? writebuf_length : remaining; - written = safewrite(fd, writebuf, write_size); - if (written < 0) { - virReportSystemError(errno, - _("Failed to write %zu bytes to " - "storage volume with path '%s'"), - write_size, path); - - goto cleanup; - } - - remaining -= written; - } - - if (fdatasync(fd) < 0) { - virReportSystemError(errno, - _("cannot sync data to volume with path '%s'"), - path); - goto cleanup; - } - - VIR_DEBUG("Wrote %llu bytes to volume with path '%s'", wipe_len, path); - - ret = 0; - - cleanup: - VIR_FREE(writebuf); - return ret; -} - - -static int -virStorageBackendVolWipeLocalFile(const char *path, - unsigned int algorithm, - unsigned long long allocation) -{ - int ret = -1, fd = -1; - const char *alg_char = NULL; - struct stat st; - virCommandPtr cmd = NULL; - - fd = open(path, O_RDWR); - if (fd == -1) { - virReportSystemError(errno, - _("Failed to open storage volume with path '%s'"), - path); - goto cleanup; - } - - if (fstat(fd, &st) == -1) { - virReportSystemError(errno, - _("Failed to stat storage volume with path '%s'"), - path); - goto cleanup; - } - - switch ((virStorageVolWipeAlgorithm) algorithm) { - case VIR_STORAGE_VOL_WIPE_ALG_ZERO: - alg_char = "zero"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_NNSA: - alg_char = "nnsa"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_DOD: - alg_char = "dod"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_BSI: - alg_char = "bsi"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_GUTMANN: - alg_char = "gutmann"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER: - alg_char = "schneier"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7: - alg_char = "pfitzner7"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33: - alg_char = "pfitzner33"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_RANDOM: - alg_char = "random"; - break; - case VIR_STORAGE_VOL_WIPE_ALG_TRIM: - virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", - _("'trim' algorithm not supported")); - goto cleanup; - case VIR_STORAGE_VOL_WIPE_ALG_LAST: - virReportError(VIR_ERR_INVALID_ARG, - _("unsupported algorithm %d"), - algorithm); - goto cleanup; - } - - VIR_DEBUG("Wiping file '%s' with algorithm '%s'", path, alg_char); - - if (algorithm != VIR_STORAGE_VOL_WIPE_ALG_ZERO) { - cmd = virCommandNew(SCRUB); - virCommandAddArgList(cmd, "-f", "-p", alg_char, path, NULL); - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - ret = 0; - } else { - if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) { - ret = virStorageBackendVolZeroSparseFileLocal(path, st.st_size, fd); - } else { - ret = virStorageBackendWipeLocal(path, - fd, - allocation, - st.st_blksize); - } - if (ret < 0) - goto cleanup; - } - - cleanup: - virCommandFree(cmd); - VIR_FORCE_CLOSE(fd); - return ret; -} - - -static int -virStorageBackendVolWipePloop(virStorageVolDefPtr vol, - unsigned int algorithm) -{ - virCommandPtr cmd = NULL; - char *target_path = NULL; - char *disk_desc = NULL; - char *create_tool = NULL; - - int ret = -1; - - create_tool = virFindFileInPath("ploop"); - if (!create_tool) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unable to find ploop tools, please install them")); - return -1; - } - - if (virAsprintf(&target_path, "%s/root.hds", vol->target.path) < 0) - goto cleanup; - - if (virAsprintf(&disk_desc, "%s/DiskDescriptor.xml", vol->target.path) < 0) - goto cleanup; - - if (virStorageBackendVolWipeLocalFile(target_path, - algorithm, - vol->target.allocation) < 0) - goto cleanup; - - if (virFileRemove(disk_desc, 0, 0) < 0) { - virReportError(errno, _("Failed to delete DiskDescriptor.xml of volume '%s'"), - vol->target.path); - goto cleanup; - } - if (virFileRemove(target_path, 0, 0) < 0) { - virReportError(errno, _("failed to delete root.hds of volume '%s'"), - vol->target.path); - goto cleanup; - } - - cmd = virCommandNewArgList(create_tool, "init", "-s", NULL); - - virCommandAddArgFormat(cmd, "%lluM", VIR_DIV_UP(vol->target.capacity, - (1024 * 1024))); - virCommandAddArgList(cmd, "-t", "ext4", NULL); - virCommandAddArg(cmd, target_path); - ret = virCommandRun(cmd, NULL); - - cleanup: - VIR_FREE(disk_desc); - VIR_FREE(target_path); - VIR_FREE(create_tool); - virCommandFree(cmd); - return ret; -} - - -int -virStorageBackendVolWipeLocal(virConnectPtr conn ATTRIBUTE_UNUSED, - virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - virStorageVolDefPtr vol, - unsigned int algorithm, - unsigned int flags) -{ - int ret = -1; - - virCheckFlags(0, -1); - - VIR_DEBUG("Wiping volume with path '%s' and algorithm %u", - vol->target.path, algorithm); - - if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { - ret = virStorageBackendVolWipePloop(vol, algorithm); - } else { - ret = virStorageBackendVolWipeLocalFile(vol->target.path, - algorithm, - vol->target.allocation); - } - - return ret; -} - - -/** - * virStorageBackendFindGlusterPoolSources: - * @host: host to detect volumes on - * @pooltype: src->format is set to this value - * @list: list of storage pool sources to be filled - * @report: report error if the 'gluster' cli tool is missing - * - * Looks up gluster volumes on @host and fills them to @list. - * - * Returns number of volumes on the host on success, or -1 on error. - */ -int -virStorageBackendFindGlusterPoolSources(const char *host, - int pooltype, - virStoragePoolSourceListPtr list, - bool report) -{ - char *glusterpath = NULL; - char *outbuf = NULL; - virCommandPtr cmd = NULL; - xmlDocPtr doc = NULL; - xmlXPathContextPtr ctxt = NULL; - xmlNodePtr *nodes = NULL; - virStoragePoolSource *src = NULL; - size_t i; - int nnodes; - int rc; - - int ret = -1; - - if (!(glusterpath = virFindFileInPath(GLUSTER_CLI))) { - if (report) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("'gluster' command line tool not found")); - return -1; - } else { - return 0; - } - } - - cmd = virCommandNewArgList(glusterpath, - "--xml", - "--log-file=/dev/null", - "volume", "info", "all", NULL); - - virCommandAddArgFormat(cmd, "--remote-host=%s", host); - virCommandSetOutputBuffer(cmd, &outbuf); - - if (virCommandRun(cmd, &rc) < 0) - goto cleanup; - - if (rc != 0) { - ret = 0; - goto cleanup; - } - - if (!(doc = virXMLParseStringCtxt(outbuf, _("(gluster_cli_output)"), - &ctxt))) - goto cleanup; - - if ((nnodes = virXPathNodeSet("//volumes/volume", ctxt, &nodes)) < 0) - goto cleanup; - - for (i = 0; i < nnodes; i++) { - ctxt->node = nodes[i]; - - if (!(src = virStoragePoolSourceListNewSource(list))) - goto cleanup; - - if (!(src->dir = virXPathString("string(//name)", ctxt))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to extract gluster volume name")); - goto cleanup; - } - - if (VIR_ALLOC_N(src->hosts, 1) < 0) - goto cleanup; - src->nhost = 1; - - if (VIR_STRDUP(src->hosts[0].name, host) < 0) - goto cleanup; - - src->format = pooltype; - } - - ret = nnodes; - - cleanup: - VIR_FREE(nodes); - xmlXPathFreeContext(ctxt); - xmlFreeDoc(doc); - VIR_FREE(outbuf); - virCommandFree(cmd); - VIR_FREE(glusterpath); - return ret; -} - - -#if WITH_BLKID - -typedef enum { - VIR_STORAGE_BLKID_PROBE_ERROR = -1, - VIR_STORAGE_BLKID_PROBE_UNDEFINED, /* Nothing found */ - VIR_STORAGE_BLKID_PROBE_UNKNOWN, /* Don't know libvirt fs/part type */ - VIR_STORAGE_BLKID_PROBE_MATCH, /* Matches the on disk format */ - VIR_STORAGE_BLKID_PROBE_DIFFERENT, /* Format doesn't match on disk format */ -} virStorageBackendBLKIDProbeResult; - -/* - * Utility function to probe for a file system on the device using the - * blkid "superblock" (e.g. default) APIs. - * - * NB: In general this helper will handle the virStoragePoolFormatFileSystem - * format types; however, if called from the Disk path, the initial fstype - * check will fail forcing the usage of the ProbePart helper. - * - * Returns virStorageBackendBLKIDProbeResult enum - */ -static virStorageBackendBLKIDProbeResult -virStorageBackendBLKIDFindFS(blkid_probe probe, - const char *device, - const char *format) -{ - const char *fstype = NULL; - - /* Make sure we're doing a superblock probe from the start */ - blkid_probe_enable_superblocks(probe, true); - blkid_probe_reset_superblocks_filter(probe); - - if (blkid_do_probe(probe) != 0) { - VIR_INFO("No filesystem found on device '%s'", device); - return VIR_STORAGE_BLKID_PROBE_UNDEFINED; - } - - if (blkid_probe_lookup_value(probe, "TYPE", &fstype, NULL) == 0) { - if (STREQ(fstype, format)) - return VIR_STORAGE_BLKID_PROBE_MATCH; - - return VIR_STORAGE_BLKID_PROBE_DIFFERENT; - } - - if (blkid_known_fstype(format) == 0) - return VIR_STORAGE_BLKID_PROBE_UNKNOWN; - - return VIR_STORAGE_BLKID_PROBE_ERROR; -} - - -/* - * Utility function to probe for a partition on the device using the - * blkid "partitions" APIs. - * - * NB: In general, this API will be validating the virStoragePoolFormatDisk - * format types. - * - * Returns virStorageBackendBLKIDProbeResult enum - */ -static virStorageBackendBLKIDProbeResult -virStorageBackendBLKIDFindPart(blkid_probe probe, - const char *device, - const char *format) -{ - const char *pttype = NULL; - - /* A blkid_known_pttype on "dvh" and "pc98" returns a failure; - * however, the blkid_do_probe for "dvh" returns "sgi" and - * for "pc98" it returns "dos". So since those will cause problems - * with startup comparison, let's just treat them as UNKNOWN causing - * the caller to fallback to using PARTED */ - if (STREQ(format, "dvh") || STREQ(format, "pc98")) - return VIR_STORAGE_BLKID_PROBE_UNKNOWN; - - /* Make sure we're doing a partitions probe from the start */ - blkid_probe_enable_partitions(probe, true); - blkid_probe_reset_partitions_filter(probe); - - if (blkid_do_probe(probe) != 0) { - VIR_INFO("No partition found on device '%s'", device); - return VIR_STORAGE_BLKID_PROBE_UNDEFINED; - } - - if (blkid_probe_lookup_value(probe, "PTTYPE", &pttype, NULL) == 0) { - if (STREQ(pttype, format)) - return VIR_STORAGE_BLKID_PROBE_MATCH; - - return VIR_STORAGE_BLKID_PROBE_DIFFERENT; - } - - if (blkid_known_pttype(format) == 0) - return VIR_STORAGE_BLKID_PROBE_UNKNOWN; - - return VIR_STORAGE_BLKID_PROBE_ERROR; -} - - -/* - * @device: Path to device - * @format: Desired format - * @writelabel: True if desire to write the label - * - * Use the blkid_ APIs in order to get details regarding whether a file - * system or partition exists on the disk already. - * - * Returns: - * -2: Force usage of PARTED for unknown types - * -1: An error was encountered, with error message set - * 0: No file system found - */ -static int -virStorageBackendBLKIDFindEmpty(const char *device, - const char *format, - bool writelabel) -{ - - int ret = -1; - int rc; - blkid_probe probe = NULL; - - VIR_DEBUG("Probe for existing filesystem/partition format %s on device %s", - format, device); - - if (!(probe = blkid_new_probe_from_filename(device))) { - virReportError(VIR_ERR_STORAGE_PROBE_FAILED, - _("Failed to create filesystem probe for device %s"), - device); - return -1; - } - - /* Look for something on FS, if it either doesn't recognize the - * format type as a valid FS format type or it doesn't find a valid - * format type on the device, then perform the same check using - * partition probing. */ - rc = virStorageBackendBLKIDFindFS(probe, device, format); - if (rc == VIR_STORAGE_BLKID_PROBE_UNDEFINED || - rc == VIR_STORAGE_BLKID_PROBE_UNKNOWN) { - - rc = virStorageBackendBLKIDFindPart(probe, device, format); - } - - switch (rc) { - case VIR_STORAGE_BLKID_PROBE_UNDEFINED: - if (writelabel) - ret = 0; - else - virReportError(VIR_ERR_STORAGE_PROBE_FAILED, - _("Device '%s' is unrecognized, requires build"), - device); - break; - - case VIR_STORAGE_BLKID_PROBE_ERROR: - virReportError(VIR_ERR_STORAGE_PROBE_FAILED, - _("Failed to probe for format type '%s'"), format); - break; - - case VIR_STORAGE_BLKID_PROBE_UNKNOWN: - ret = -2; - break; - - case VIR_STORAGE_BLKID_PROBE_MATCH: - if (writelabel) - virReportError(VIR_ERR_STORAGE_POOL_BUILT, - _("Device '%s' already formatted using '%s'"), - device, format); - else - ret = 0; - break; - - case VIR_STORAGE_BLKID_PROBE_DIFFERENT: - if (writelabel) - virReportError(VIR_ERR_STORAGE_POOL_BUILT, - _("Format of device '%s' does not match the " - "expected format '%s', forced overwrite is " - "necessary"), - device, format); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("Format of device '%s' does not match the " - "expected format '%s'"), - device, format); - break; - } - - if (ret == 0 && blkid_do_probe(probe) != 1) { - virReportError(VIR_ERR_STORAGE_PROBE_FAILED, "%s", - _("Found additional probes to run, probing may " - "be incorrect")); - ret = -1; - } - - blkid_free_probe(probe); - - return ret; -} - -#else /* #if WITH_BLKID */ - -static int -virStorageBackendBLKIDFindEmpty(const char *device ATTRIBUTE_UNUSED, - const char *format ATTRIBUTE_UNUSED, - bool writelabel ATTRIBUTE_UNUSED) -{ - return -2; -} - -#endif /* #if WITH_BLKID */ - - -#if WITH_STORAGE_DISK - -typedef enum { - VIR_STORAGE_PARTED_ERROR = -1, - VIR_STORAGE_PARTED_MATCH, /* Valid label found and matches format */ - VIR_STORAGE_PARTED_DIFFERENT, /* Valid label found but not match format */ - VIR_STORAGE_PARTED_UNKNOWN, /* No or unrecognized label */ - VIR_STORAGE_PARTED_NOPTTYPE, /* Did not find the Partition Table type */ - VIR_STORAGE_PARTED_PTTYPE_UNK, /* Partition Table type unknown*/ -} virStorageBackendPARTEDResult; - -/** - * Check for a valid disk label (partition table) on device using - * the PARTED command - * - * returns virStorageBackendPARTEDResult - */ -static virStorageBackendPARTEDResult -virStorageBackendPARTEDFindLabel(const char *device, - const char *format) -{ - const char *const args[] = { - device, "print", "--script", NULL, - }; - virCommandPtr cmd = virCommandNew(PARTED); - char *output = NULL; - char *error = NULL; - char *start, *end; - int ret = VIR_STORAGE_PARTED_ERROR; - - virCommandAddArgSet(cmd, args); - virCommandAddEnvString(cmd, "LC_ALL=C"); - virCommandSetOutputBuffer(cmd, &output); - virCommandSetErrorBuffer(cmd, &error); - - /* if parted succeeds we have a valid partition table */ - ret = virCommandRun(cmd, NULL); - if (ret < 0) { - if ((output && strstr(output, "unrecognised disk label")) || - (error && strstr(error, "unrecognised disk label"))) { - ret = VIR_STORAGE_PARTED_UNKNOWN; - } - goto cleanup; - } - - /* Search for "Partition Table:" in the output. If not present, - * then we cannot validate the partition table type. - */ - if (!(start = strstr(output, "Partition Table: ")) || - !(end = strstr(start, "\n"))) { - VIR_DEBUG("Unable to find tag in output: %s", output); - ret = VIR_STORAGE_PARTED_NOPTTYPE; - goto cleanup; - } - start += strlen("Partition Table: "); - *end = '\0'; - - /* on disk it's "msdos", but we document/use "dos" so deal with it here */ - if (STREQ(start, "msdos")) - start += 2; - - /* Make sure we know about this type */ - if (virStoragePoolFormatDiskTypeFromString(start) < 0) { - ret = VIR_STORAGE_PARTED_PTTYPE_UNK; - goto cleanup; - } - - /* Does the on disk match what the pool desired? */ - if (STREQ(start, format)) - ret = VIR_STORAGE_PARTED_MATCH; - - ret = VIR_STORAGE_PARTED_DIFFERENT; - - cleanup: - virCommandFree(cmd); - VIR_FREE(output); - VIR_FREE(error); - return ret; -} - - -/** - * Determine whether the label on the disk is valid or in a known format - * for the purpose of rewriting the label during build or being able to - * start a pool on a device. - * - * When 'writelabel' is true, if we find a valid disk label on the device, - * then we shouldn't be attempting to write as the volume may contain - * data. Force the usage of the overwrite flag to the build command in - * order to be certain. When the disk label is unrecognized, then it - * should be safe to write. - * - * When 'writelabel' is false, only if we find a valid disk label on the - * device should we allow the start since for this path we won't be - * rewriting the label. - * - * Return: 0 if it's OK - * -1 if something's wrong - */ -static int -virStorageBackendPARTEDValidLabel(const char *device, - const char *format, - bool writelabel) -{ - int ret = -1; - virStorageBackendPARTEDResult check; - - check = virStorageBackendPARTEDFindLabel(device, format); - switch (check) { - case VIR_STORAGE_PARTED_ERROR: - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Error checking for disk label, failed to get " - "disk partition information")); - break; - - case VIR_STORAGE_PARTED_MATCH: - if (writelabel) - virReportError(VIR_ERR_OPERATION_INVALID, - _("Disk label already formatted using '%s'"), - format); - else - ret = 0; - break; - - case VIR_STORAGE_PARTED_DIFFERENT: - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Known, but different label format present, " - "requires build --overwrite")); - break; - - case VIR_STORAGE_PARTED_UNKNOWN: - if (writelabel) - ret = 0; - else - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Unrecognized disk label found, requires build")); - break; - - case VIR_STORAGE_PARTED_NOPTTYPE: - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Unable to determine Partition Type, " - "requires build --overwrite")); - break; - - case VIR_STORAGE_PARTED_PTTYPE_UNK: - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Unknown Partition Type, requires build --overwrite")); - break; - } - - return ret; -} - -#else - -static int -virStorageBackendPARTEDValidLabel(const char *device ATTRIBUTE_UNUSED, - const char *format ATTRIBUTE_UNUSED, - bool writelabel ATTRIBUTE_UNUSED) -{ - return -2; -} - - -#endif /* #if WITH_STORAGE_DISK */ - - -/* virStorageBackendDeviceIsEmpty: - * @devpath: Path to the device to check - * @format: Desired format string - * @writelabel: True if the caller expects to write the label - * - * Check if the @devpath has some sort of known file system using the - * BLKID API if available. - * - * Returns true if the probe deems the device has nothing valid on it - * or when we cannot check and we're not writing the label. - * - * Returns false if the probe finds something - */ -bool -virStorageBackendDeviceIsEmpty(const char *devpath, - const char *format, - bool writelabel) -{ - int ret; - - if ((ret = virStorageBackendBLKIDFindEmpty(devpath, format, - writelabel)) == -2) - ret = virStorageBackendPARTEDValidLabel(devpath, format, writelabel); - - if (ret == -2 && !writelabel) - ret = 0; - - if (ret == -2) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("Unable to probe '%s' for existing data, " - "forced overwrite is necessary"), - devpath); - } - - return ret == 0; -} diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index b00b0f8978..b8fb368bb7 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -1,9 +1,6 @@ /* * storage_backend.h: internal storage driver backend contract * - * Copyright (C) 2007-2010, 2012-2014 Red Hat, Inc. - * Copyright (C) 2007-2008 Daniel P. Berrange - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -17,8 +14,6 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . - * - * Author: Daniel P. Berrange */ #ifndef __VIR_STORAGE_BACKEND_H__ @@ -28,7 +23,6 @@ # include "internal.h" # include "storage_conf.h" -# include "vircommand.h" # include "storage_driver.h" typedef char * (*virStorageBackendFindPoolSources)(virConnectPtr conn, @@ -102,67 +96,6 @@ typedef int (*virStorageBackendVolumeWipe)(virConnectPtr conn, unsigned int algorithm, unsigned int flags); -/* File creation/cloning functions used for cloning between backends */ -int virStorageBackendCreateRaw(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags); - -int virStorageBackendCreateQemuImg(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags); - -int virStorageBackendCreatePloop(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags); - -int virStoragePloopResize(virStorageVolDefPtr vol, - unsigned long long capacity); - -int virStorageBackendRedoPloopUpdate(virStorageSourcePtr target, - struct stat *sb, int *fd, - unsigned int flags); -bool virStorageBackendIsPloopDir(char *path); - -virStorageBackendBuildVolFrom -virStorageBackendGetBuildVolFromFunction(virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol); - -int virStorageBackendFindGlusterPoolSources(const char *host, - int pooltype, - virStoragePoolSourceListPtr list, - bool report); - -int virStorageBackendVolUploadLocal(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStreamPtr stream, - unsigned long long offset, - unsigned long long len, - unsigned int flags); -int virStorageBackendVolDownloadLocal(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStreamPtr stream, - unsigned long long offset, - unsigned long long len, - unsigned int flags); - -int virStorageBackendVolWipeLocal(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - unsigned int algorithm, - unsigned int flags); - -bool virStorageBackendDeviceIsEmpty(const char *devpath, - const char *format, - bool writelabel); - typedef struct _virStorageBackend virStorageBackend; typedef virStorageBackend *virStorageBackendPtr; @@ -192,64 +125,6 @@ struct _virStorageBackend { virStorageBackendPtr virStorageBackendForType(int type); -/* VolOpenCheckMode flags */ -enum { - VIR_STORAGE_VOL_OPEN_NOERROR = 1 << 0, /* don't error if unexpected type - * encountered, just warn */ - VIR_STORAGE_VOL_OPEN_REG = 1 << 1, /* regular files okay */ - VIR_STORAGE_VOL_OPEN_BLOCK = 1 << 2, /* block files okay */ - VIR_STORAGE_VOL_OPEN_CHAR = 1 << 3, /* char files okay */ - VIR_STORAGE_VOL_OPEN_DIR = 1 << 4, /* directories okay */ -}; - -/* VolReadErrorMode flags - * If flag is present, then operation won't cause fatal error for - * specified operation, rather a VIR_WARN will be issued and a -2 returned - * for function call - */ -enum { - VIR_STORAGE_VOL_READ_NOERROR = 1 << 0, /* ignore *read errors */ -}; - -# define VIR_STORAGE_VOL_OPEN_DEFAULT (VIR_STORAGE_VOL_OPEN_REG |\ - VIR_STORAGE_VOL_OPEN_BLOCK) - -int virStorageBackendVolOpen(const char *path, struct stat *sb, - unsigned int flags) - ATTRIBUTE_RETURN_CHECK - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - -# define VIR_STORAGE_DEFAULT_POOL_PERM_MODE 0755 -# define VIR_STORAGE_DEFAULT_VOL_PERM_MODE 0600 - -int virStorageBackendUpdateVolInfo(virStorageVolDefPtr vol, - bool withBlockVolFormat, - unsigned int openflags, - unsigned int readflags); -int virStorageBackendUpdateVolTargetInfo(virStorageVolType voltype, - virStorageSourcePtr target, - bool withBlockVolFormat, - unsigned int openflags, - unsigned int readflags); -int virStorageBackendUpdateVolTargetInfoFD(virStorageSourcePtr target, - int fd, - struct stat *sb); - -bool virStorageBackendPoolPathIsStable(const char *path); -char *virStorageBackendStablePath(virStoragePoolObjPtr pool, - const char *devpath, - bool loop); - -virCommandPtr -virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol, - unsigned int flags, - const char *create_tool, - int imgformat, - const char *secretPath); - /* ------- virStorageFile backends ------------ */ typedef struct _virStorageFileBackend virStorageFileBackend; typedef virStorageFileBackend *virStorageFileBackendPtr; diff --git a/src/storage/storage_backend_disk.c b/src/storage/storage_backend_disk.c index 1c79cc5a96..819f1e5c4e 100644 --- a/src/storage/storage_backend_disk.c +++ b/src/storage/storage_backend_disk.c @@ -30,6 +30,7 @@ #include "virerror.h" #include "virlog.h" #include "storage_backend_disk.h" +#include "storage_util.h" #include "viralloc.h" #include "vircommand.h" #include "virfile.h" diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index ecf74de491..53710db11c 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -39,6 +39,7 @@ #include "virerror.h" #include "storage_backend_fs.h" +#include "storage_util.h" #include "storage_conf.h" #include "virstoragefile.h" #include "vircommand.h" diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index e819f62996..7be2d9e817 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -31,6 +31,7 @@ #include "virstoragefile.h" #include "virstring.h" #include "viruri.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index 84ad6f3e87..8799349b6f 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -44,6 +44,7 @@ #include "virstring.h" #include "viruuid.h" #include "secret_util.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c index 6a6720e220..b0191aa45d 100644 --- a/src/storage/storage_backend_logical.c +++ b/src/storage/storage_backend_logical.c @@ -39,6 +39,7 @@ #include "virlog.h" #include "virfile.h" #include "virstring.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_mpath.c b/src/storage/storage_backend_mpath.c index bf1b1abe03..a5d692a07c 100644 --- a/src/storage/storage_backend_mpath.c +++ b/src/storage/storage_backend_mpath.c @@ -36,6 +36,7 @@ #include "virlog.h" #include "virfile.h" #include "virstring.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_rbd.c b/src/storage/storage_backend_rbd.c index b1c51ab1b4..45beb107aa 100644 --- a/src/storage/storage_backend_rbd.c +++ b/src/storage/storage_backend_rbd.c @@ -37,6 +37,7 @@ #include "rados/librados.h" #include "rbd/librbd.h" #include "secret_util.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c index be02f6eec0..d294d2ac0b 100644 --- a/src/storage/storage_backend_scsi.c +++ b/src/storage/storage_backend_scsi.c @@ -35,6 +35,7 @@ #include "virfile.h" #include "vircommand.h" #include "virstring.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_sheepdog.c b/src/storage/storage_backend_sheepdog.c index ad2d756296..17202e4877 100644 --- a/src/storage/storage_backend_sheepdog.c +++ b/src/storage/storage_backend_sheepdog.c @@ -33,6 +33,7 @@ #include "vircommand.h" #include "viralloc.h" #include "virstring.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_backend_zfs.c b/src/storage/storage_backend_zfs.c index 3a43be41c1..70c533a7fe 100644 --- a/src/storage/storage_backend_zfs.c +++ b/src/storage/storage_backend_zfs.c @@ -27,6 +27,7 @@ #include "storage_backend_zfs.h" #include "virlog.h" #include "virstring.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 8f1d3f04c8..a52eae3e20 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -51,6 +51,7 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "dirname.h" +#include "storage_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c new file mode 100644 index 0000000000..6efb610dfc --- /dev/null +++ b/src/storage/storage_util.c @@ -0,0 +1,2919 @@ +/* + * storage_util.c: helper functions for the storage driver + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dirname.h" +#ifdef __linux__ +# include +# include +# ifndef FS_NOCOW_FL +# define FS_NOCOW_FL 0x00800000 /* Do not cow file */ +# endif +#endif + +#if WITH_BLKID +# include +#endif + +#if WITH_SELINUX +# include +#endif + +#if HAVE_LINUX_BTRFS_H +# include +#endif + +#include "datatypes.h" +#include "virerror.h" +#include "viralloc.h" +#include "internal.h" +#include "secret_conf.h" +#include "secret_util.h" +#include "vircrypto.h" +#include "viruuid.h" +#include "virstoragefile.h" +#include "storage_util.h" +#include "virlog.h" +#include "virfile.h" +#include "virjson.h" +#include "virqemu.h" +#include "stat-time.h" +#include "virstring.h" +#include "virxml.h" +#include "fdstream.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("storage.storage_util"); + +#define READ_BLOCK_SIZE_DEFAULT (1024 * 1024) +#define WRITE_BLOCK_SIZE_DEFAULT (4 * 1024) + +/* + * Perform the O(1) btrfs clone operation, if possible. + * Upon success, return 0. Otherwise, return -1 and set errno. + */ +#if HAVE_LINUX_BTRFS_H +static inline int +btrfsCloneFile(int dest_fd, int src_fd) +{ + return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd); +} +#else +static inline int +btrfsCloneFile(int dest_fd ATTRIBUTE_UNUSED, + int src_fd ATTRIBUTE_UNUSED) +{ + errno = ENOTSUP; + return -1; +} +#endif + +static int ATTRIBUTE_NONNULL(2) +virStorageBackendCopyToFD(virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + int fd, + unsigned long long *total, + bool want_sparse, + bool reflink_copy) +{ + int inputfd = -1; + int amtread = -1; + int ret = 0; + size_t rbytes = READ_BLOCK_SIZE_DEFAULT; + int wbytes = 0; + int interval; + char *zerobuf = NULL; + char *buf = NULL; + struct stat st; + + if ((inputfd = open(inputvol->target.path, O_RDONLY)) < 0) { + ret = -errno; + virReportSystemError(errno, + _("could not open input path '%s'"), + inputvol->target.path); + goto cleanup; + } + +#ifdef __linux__ + if (ioctl(fd, BLKBSZGET, &wbytes) < 0) + wbytes = 0; +#endif + if ((wbytes == 0) && fstat(fd, &st) == 0) + wbytes = st.st_blksize; + if (wbytes < WRITE_BLOCK_SIZE_DEFAULT) + wbytes = WRITE_BLOCK_SIZE_DEFAULT; + + if (VIR_ALLOC_N(zerobuf, wbytes) < 0) { + ret = -errno; + goto cleanup; + } + + if (VIR_ALLOC_N(buf, rbytes) < 0) { + ret = -errno; + goto cleanup; + } + + if (reflink_copy) { + if (btrfsCloneFile(fd, inputfd) < 0) { + ret = -errno; + virReportSystemError(errno, + _("failed to clone files from '%s'"), + inputvol->target.path); + goto cleanup; + } else { + VIR_DEBUG("btrfs clone finished."); + goto cleanup; + } + } + + while (amtread != 0) { + int amtleft; + + if (*total < rbytes) + rbytes = *total; + + if ((amtread = saferead(inputfd, buf, rbytes)) < 0) { + ret = -errno; + virReportSystemError(errno, + _("failed reading from file '%s'"), + inputvol->target.path); + goto cleanup; + } + *total -= amtread; + + /* Loop over amt read in 512 byte increments, looking for sparse + * blocks */ + amtleft = amtread; + do { + interval = ((wbytes > amtleft) ? amtleft : wbytes); + int offset = amtread - amtleft; + + if (want_sparse && memcmp(buf+offset, zerobuf, interval) == 0) { + if (lseek(fd, interval, SEEK_CUR) < 0) { + ret = -errno; + virReportSystemError(errno, + _("cannot extend file '%s'"), + vol->target.path); + goto cleanup; + } + } else if (safewrite(fd, buf+offset, interval) < 0) { + ret = -errno; + virReportSystemError(errno, + _("failed writing to file '%s'"), + vol->target.path); + goto cleanup; + + } + } while ((amtleft -= interval) > 0); + } + + if (fdatasync(fd) < 0) { + ret = -errno; + virReportSystemError(errno, _("cannot sync data to file '%s'"), + vol->target.path); + goto cleanup; + } + + + if (VIR_CLOSE(inputfd) < 0) { + ret = -errno; + virReportSystemError(errno, + _("cannot close file '%s'"), + inputvol->target.path); + goto cleanup; + } + inputfd = -1; + + cleanup: + VIR_FORCE_CLOSE(inputfd); + + VIR_FREE(zerobuf); + VIR_FREE(buf); + + return ret; +} + +static int +virStorageBackendCreateBlockFrom(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags) +{ + int fd = -1; + int ret = -1; + unsigned long long remain; + struct stat st; + gid_t gid; + uid_t uid; + mode_t mode; + bool reflink_copy = false; + + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + -1); + + if (flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("metadata preallocation is not supported for block " + "volumes")); + goto cleanup; + } + + if (flags & VIR_STORAGE_VOL_CREATE_REFLINK) + reflink_copy = true; + + if ((fd = open(vol->target.path, O_RDWR)) < 0) { + virReportSystemError(errno, + _("cannot create path '%s'"), + vol->target.path); + goto cleanup; + } + + remain = vol->target.capacity; + + if (inputvol) { + if (virStorageBackendCopyToFD(vol, inputvol, fd, &remain, + false, reflink_copy) < 0) + goto cleanup; + } + + if (fstat(fd, &st) == -1) { + virReportSystemError(errno, _("stat of '%s' failed"), + vol->target.path); + goto cleanup; + } + uid = (vol->target.perms->uid != st.st_uid) ? vol->target.perms->uid + : (uid_t) -1; + gid = (vol->target.perms->gid != st.st_gid) ? vol->target.perms->gid + : (gid_t) -1; + if (((uid != (uid_t) -1) || (gid != (gid_t) -1)) + && (fchown(fd, uid, gid) < 0)) { + virReportSystemError(errno, + _("cannot chown '%s' to (%u, %u)"), + vol->target.path, (unsigned int) uid, + (unsigned int) gid); + goto cleanup; + } + + mode = (vol->target.perms->mode == (mode_t) -1 ? + VIR_STORAGE_DEFAULT_VOL_PERM_MODE : vol->target.perms->mode); + if (fchmod(fd, mode) < 0) { + virReportSystemError(errno, + _("cannot set mode of '%s' to %04o"), + vol->target.path, mode); + goto cleanup; + } + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, + _("cannot close file '%s'"), + vol->target.path); + goto cleanup; + } + fd = -1; + + ret = 0; + cleanup: + VIR_FORCE_CLOSE(fd); + + return ret; +} + +static int +createRawFile(int fd, virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + bool reflink_copy) +{ + bool need_alloc = true; + int ret = 0; + unsigned long long pos = 0; + + /* If the new allocation is lower than the capacity of the original file, + * the cloned volume will be sparse */ + if (inputvol && + vol->target.allocation < inputvol->target.capacity) + need_alloc = false; + + /* Seek to the final size, so the capacity is available upfront + * for progress reporting */ + if (ftruncate(fd, vol->target.capacity) < 0) { + ret = -errno; + virReportSystemError(errno, + _("cannot extend file '%s'"), + vol->target.path); + goto cleanup; + } + +/* Avoid issues with older kernel's namespace pollution. */ +#if HAVE_FALLOCATE - 0 + /* Try to preallocate all requested disk space, but fall back to + * other methods if this fails with ENOSYS or EOPNOTSUPP. If allocation + * is 0 (or less than 0), then fallocate will fail with EINVAL. + * NOTE: do not use posix_fallocate; posix_fallocate falls back + * to writing zeroes block by block in case fallocate isn't + * available, and since we're going to copy data from another + * file it doesn't make sense to write the file twice. */ + if (vol->target.allocation && need_alloc) { + if (fallocate(fd, 0, 0, vol->target.allocation) == 0) { + need_alloc = false; + } else if (errno != ENOSYS && errno != EOPNOTSUPP) { + ret = -errno; + virReportSystemError(errno, + _("cannot allocate %llu bytes in file '%s'"), + vol->target.allocation, vol->target.path); + goto cleanup; + } + } +#endif + + if (inputvol) { + unsigned long long remain = inputvol->target.capacity; + /* allow zero blocks to be skipped if we've requested sparse + * allocation (allocation < capacity) or we have already + * been able to allocate the required space. */ + if ((ret = virStorageBackendCopyToFD(vol, inputvol, fd, &remain, + !need_alloc, reflink_copy)) < 0) + goto cleanup; + + /* If the new allocation is greater than the original capacity, + * but fallocate failed, fill the rest with zeroes. + */ + pos = inputvol->target.capacity - remain; + } + + if (need_alloc && (vol->target.allocation - pos > 0)) { + if (safezero(fd, pos, vol->target.allocation - pos) < 0) { + ret = -errno; + virReportSystemError(errno, _("cannot fill file '%s'"), + vol->target.path); + goto cleanup; + } + } + + if (fsync(fd) < 0) { + ret = -errno; + virReportSystemError(errno, _("cannot sync data to file '%s'"), + vol->target.path); + goto cleanup; + } + + cleanup: + return ret; +} + +int +virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags) +{ + int ret = -1; + int fd = -1; + int operation_flags; + bool reflink_copy = false; + mode_t open_mode = VIR_STORAGE_DEFAULT_VOL_PERM_MODE; + bool created = false; + + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + -1); + + if (flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("metadata preallocation is not supported for raw " + "volumes")); + goto cleanup; + } + + if (vol->target.backingStore) { + virReportError(VIR_ERR_NO_SUPPORT, "%s", + _("backing storage not supported for raw volumes")); + goto cleanup; + } + + if (flags & VIR_STORAGE_VOL_CREATE_REFLINK) + reflink_copy = true; + + + if (vol->target.encryption != NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("storage pool does not support encrypted volumes")); + goto cleanup; + } + + operation_flags = VIR_FILE_OPEN_FORCE_MODE | VIR_FILE_OPEN_FORCE_OWNER; + if (pool->def->type == VIR_STORAGE_POOL_NETFS) + operation_flags |= VIR_FILE_OPEN_FORK; + + if (vol->target.perms->mode != (mode_t) -1) + open_mode = vol->target.perms->mode; + + if ((fd = virFileOpenAs(vol->target.path, + O_RDWR | O_CREAT | O_EXCL, + open_mode, + vol->target.perms->uid, + vol->target.perms->gid, + operation_flags)) < 0) { + virReportSystemError(-fd, + _("Failed to create file '%s'"), + vol->target.path); + goto cleanup; + } + created = true; + + if (vol->target.nocow) { +#ifdef __linux__ + int attr; + + /* Set NOCOW flag. This is an optimisation for btrfs. + * The FS_IOC_SETFLAGS ioctl return value will be ignored since any + * failure of this operation should not block the volume creation. + */ + if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0) { + virReportSystemError(errno, "%s", _("Failed to get fs flags")); + } else { + attr |= FS_NOCOW_FL; + if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0) { + virReportSystemError(errno, "%s", + _("Failed to set NOCOW flag")); + } + } +#endif + } + + if ((ret = createRawFile(fd, vol, inputvol, reflink_copy)) < 0) + /* createRawFile already reported the exact error. */ + ret = -1; + + cleanup: + if (ret < 0 && created) + ignore_value(virFileRemove(vol->target.path, + vol->target.perms->uid, + vol->target.perms->gid)); + VIR_FORCE_CLOSE(fd); + return ret; +} + +static int +virStorageGenerateSecretUUID(virConnectPtr conn, + unsigned char *uuid) +{ + unsigned attempt; + + for (attempt = 0; attempt < 65536; attempt++) { + virSecretPtr tmp; + if (virUUIDGenerate(uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to generate uuid")); + return -1; + } + tmp = conn->secretDriver->secretLookupByUUID(conn, uuid); + if (tmp == NULL) + return 0; + + virObjectUnref(tmp); + } + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("too many conflicts when generating a uuid")); + + return -1; +} + +static int +virStorageGenerateQcowEncryption(virConnectPtr conn, + virStorageVolDefPtr vol) +{ + virSecretDefPtr def = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + virStorageEncryptionPtr enc; + virStorageEncryptionSecretPtr enc_secret = NULL; + virSecretPtr secret = NULL; + char *xml; + unsigned char value[VIR_STORAGE_QCOW_PASSPHRASE_SIZE]; + int ret = -1; + + if (conn->secretDriver == NULL || + conn->secretDriver->secretLookupByUUID == NULL || + conn->secretDriver->secretDefineXML == NULL || + conn->secretDriver->secretSetValue == NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("secret storage not supported")); + goto cleanup; + } + + enc = vol->target.encryption; + if (enc->nsecrets != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("secrets already defined")); + goto cleanup; + } + + if (VIR_ALLOC(enc_secret) < 0 || VIR_REALLOC_N(enc->secrets, 1) < 0 || + VIR_ALLOC(def) < 0) + goto cleanup; + + def->isephemeral = false; + def->isprivate = false; + if (virStorageGenerateSecretUUID(conn, def->uuid) < 0) + goto cleanup; + + def->usage_type = VIR_SECRET_USAGE_TYPE_VOLUME; + if (VIR_STRDUP(def->usage_id, vol->target.path) < 0) + goto cleanup; + xml = virSecretDefFormat(def); + virSecretDefFree(def); + def = NULL; + if (xml == NULL) + goto cleanup; + + secret = conn->secretDriver->secretDefineXML(conn, xml, 0); + if (secret == NULL) { + VIR_FREE(xml); + goto cleanup; + } + VIR_FREE(xml); + + if (virStorageGenerateQcowPassphrase(value) < 0) + goto cleanup; + + if (conn->secretDriver->secretSetValue(secret, value, sizeof(value), 0) < 0) + goto cleanup; + + enc_secret->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE; + enc_secret->seclookupdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; + memcpy(enc_secret->seclookupdef.u.uuid, secret->uuid, VIR_UUID_BUFLEN); + enc->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; + enc->secrets[0] = enc_secret; /* Space for secrets[0] allocated above */ + enc_secret = NULL; + enc->nsecrets = 1; + + ret = 0; + + cleanup: + if (secret != NULL) { + if (ret != 0 && + conn->secretDriver->secretUndefine != NULL) + conn->secretDriver->secretUndefine(secret); + virObjectUnref(secret); + } + virBufferFreeAndReset(&buf); + virSecretDefFree(def); + VIR_FREE(enc_secret); + return ret; +} + +static int +virStorageBackendCreateExecCommand(virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virCommandPtr cmd) +{ + struct stat st; + gid_t gid; + uid_t uid; + mode_t mode = (vol->target.perms->mode == (mode_t) -1 ? + VIR_STORAGE_DEFAULT_VOL_PERM_MODE : + vol->target.perms->mode); + bool filecreated = false; + int ret = -1; + + if ((pool->def->type == VIR_STORAGE_POOL_NETFS) + && (((geteuid() == 0) + && (vol->target.perms->uid != (uid_t) -1) + && (vol->target.perms->uid != 0)) + || ((vol->target.perms->gid != (gid_t) -1) + && (vol->target.perms->gid != getegid())))) { + + virCommandSetUID(cmd, vol->target.perms->uid); + virCommandSetGID(cmd, vol->target.perms->gid); + virCommandSetUmask(cmd, S_IRWXUGO ^ mode); + + if (virCommandRun(cmd, NULL) == 0) { + /* command was successfully run, check if the file was created */ + if (stat(vol->target.path, &st) >= 0) { + filecreated = true; + + /* seems qemu-img disregards umask and open/creates using 0644. + * If that doesn't match what we expect, then let's try to + * re-open the file and attempt to force the mode change. + */ + if (mode != (st.st_mode & S_IRWXUGO)) { + int fd = -1; + int flags = VIR_FILE_OPEN_FORK | VIR_FILE_OPEN_FORCE_MODE; + + if ((fd = virFileOpenAs(vol->target.path, O_RDWR, mode, + vol->target.perms->uid, + vol->target.perms->gid, + flags)) >= 0) { + /* Success - means we're good */ + VIR_FORCE_CLOSE(fd); + ret = 0; + goto cleanup; + } + } + } + } + } + + if (!filecreated) { + /* don't change uid/gid/mode if we retry */ + virCommandSetUID(cmd, -1); + virCommandSetGID(cmd, -1); + virCommandSetUmask(cmd, 0); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + if (stat(vol->target.path, &st) < 0) { + virReportSystemError(errno, + _("failed to create %s"), vol->target.path); + goto cleanup; + } + filecreated = true; + } + + uid = (vol->target.perms->uid != st.st_uid) ? vol->target.perms->uid + : (uid_t) -1; + gid = (vol->target.perms->gid != st.st_gid) ? vol->target.perms->gid + : (gid_t) -1; + if (((uid != (uid_t) -1) || (gid != (gid_t) -1)) + && (chown(vol->target.path, uid, gid) < 0)) { + virReportSystemError(errno, + _("cannot chown %s to (%u, %u)"), + vol->target.path, (unsigned int) uid, + (unsigned int) gid); + goto cleanup; + } + + if (mode != (st.st_mode & S_IRWXUGO) && + chmod(vol->target.path, mode) < 0) { + virReportSystemError(errno, + _("cannot set mode of '%s' to %04o"), + vol->target.path, mode); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret < 0 && filecreated) + virFileRemove(vol->target.path, vol->target.perms->uid, + vol->target.perms->gid); + return ret; +} + +/* Create ploop directory with ploop image and DiskDescriptor.xml + * if function fails to create image file the directory will be deleted.*/ +int +virStorageBackendCreatePloop(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags) +{ + int ret = -1; + virCommandPtr cmd = NULL; + char *create_tool = NULL; + bool created = false; + + virCheckFlags(0, -1); + + if (inputvol && inputvol->target.format != VIR_STORAGE_FILE_PLOOP) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported input storage vol type %d"), + inputvol->target.format); + return -1; + } + + if (vol->target.encryption != NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("encrypted ploop volumes are not supported with " + "ploop init")); + return -1; + } + + if (vol->target.backingStore != NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("copy-on-write ploop volumes are not yet supported")); + return -1; + } + + create_tool = virFindFileInPath("ploop"); + if (!create_tool && !inputvol) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to find ploop, please install " + "ploop tools")); + return -1; + } + + if (!inputvol) { + if ((virDirCreate(vol->target.path, + (vol->target.perms->mode == (mode_t) -1 ? + VIR_STORAGE_DEFAULT_VOL_PERM_MODE: + vol->target.perms->mode), + vol->target.perms->uid, + vol->target.perms->gid, + 0)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("error creating directory for ploop volume")); + goto cleanup; + } + cmd = virCommandNewArgList(create_tool, "init", "-s", NULL); + virCommandAddArgFormat(cmd, "%lluM", VIR_DIV_UP(vol->target.capacity, + (1024 * 1024))); + virCommandAddArgList(cmd, "-t", "ext4", NULL); + virCommandAddArgFormat(cmd, "%s/root.hds", vol->target.path); + + } else { + vol->target.capacity = inputvol->target.capacity; + cmd = virCommandNewArgList("cp", "-r", inputvol->target.path, + vol->target.path, NULL); + } + created = true; + ret = virCommandRun(cmd, NULL); + cleanup: + virCommandFree(cmd); + VIR_FREE(create_tool); + if (ret < 0 && created) + virFileDeleteTree(vol->target.path); + return ret; +} + +int +virStoragePloopResize(virStorageVolDefPtr vol, + unsigned long long capacity) +{ + int ret = -1; + virCommandPtr cmd = NULL; + char *resize_tool = NULL; + + resize_tool = virFindFileInPath("ploop"); + if (!resize_tool) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to find ploop, please install ploop tools")); + return -1; + } + cmd = virCommandNewArgList(resize_tool, "resize", "-s", NULL); + virCommandAddArgFormat(cmd, "%lluM", VIR_DIV_UP(capacity, (1024 * 1024))); + + virCommandAddArgFormat(cmd, "%s/DiskDescriptor.xml", vol->target.path); + + ret = virCommandRun(cmd, NULL); + virCommandFree(cmd); + VIR_FREE(resize_tool); + return ret; +} + +/* Flag values shared w/ storagevolxml2argvtest.c. + * + * QEMU_IMG_BACKING_FORMAT_OPTIONS (added in qemu 0.11) + * QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT + * was made necessary due to 2.0 change to change the default + * qcow2 file format from 0.10 to 1.1. + */ +enum { + QEMU_IMG_BACKING_FORMAT_OPTIONS = 0, + QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT, +}; + +static bool +virStorageBackendQemuImgSupportsCompat(const char *qemuimg) +{ + bool ret = false; + char *output; + virCommandPtr cmd = NULL; + + cmd = virCommandNewArgList(qemuimg, "create", "-o", "?", "-f", "qcow2", + "/dev/null", NULL); + + virCommandAddEnvString(cmd, "LC_ALL=C"); + virCommandSetOutputBuffer(cmd, &output); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (strstr(output, "\ncompat ")) + ret = true; + + cleanup: + virCommandFree(cmd); + VIR_FREE(output); + return ret; +} + + +static int +virStorageBackendQEMUImgBackingFormat(const char *qemuimg) +{ + /* As of QEMU 0.11 the [-o options] support was added via qemu + * commit id '9ea2ea71', so we start with that base and figure + * out what else we have */ + int ret = QEMU_IMG_BACKING_FORMAT_OPTIONS; + + /* QEMU 2.0 changed to using a format that only QEMU 1.1 and newer + * understands. Since we still support QEMU 0.12 and newer, we need + * to be able to handle the previous format as can be set via a + * compat=0.10 option. */ + if (virStorageBackendQemuImgSupportsCompat(qemuimg)) + ret = QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT; + + return ret; +} + +/* The _virStorageBackendQemuImgInfo separates the command line building from + * the volume definition so that qemuDomainSnapshotCreateInactiveExternal can + * use it without needing to deal with a volume. + */ +struct _virStorageBackendQemuImgInfo { + int format; + const char *path; + unsigned long long size_arg; + bool encryption; + bool preallocate; + const char *compat; + virBitmapPtr features; + bool nocow; + + const char *backingPath; + int backingFormat; + + const char *inputPath; + const char *inputFormatStr; + int inputFormat; + + char *secretAlias; + const char *secretPath; +}; + + +static int +virStorageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr enc, + char **opts, + struct _virStorageBackendQemuImgInfo info) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (info.format == VIR_STORAGE_FILE_RAW && enc) { + virQEMUBuildLuksOpts(&buf, enc, info.secretAlias); + } else { + if (info.backingPath) + virBufferAsprintf(&buf, "backing_fmt=%s,", + virStorageFileFormatTypeToString(info.backingFormat)); + if (info.encryption) + virBufferAddLit(&buf, "encryption=on,"); + if (info.preallocate) + virBufferAddLit(&buf, "preallocation=metadata,"); + } + + if (info.nocow) + virBufferAddLit(&buf, "nocow=on,"); + + if (info.compat) + virBufferAsprintf(&buf, "compat=%s,", info.compat); + + if (info.features && info.format == VIR_STORAGE_FILE_QCOW2) { + if (virBitmapIsBitSet(info.features, + VIR_STORAGE_FILE_FEATURE_LAZY_REFCOUNTS)) { + if (STREQ_NULLABLE(info.compat, "0.10")) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("lazy_refcounts not supported with compat" + " level %s"), + info.compat); + goto error; + } + virBufferAddLit(&buf, "lazy_refcounts,"); + } + } + + virBufferTrim(&buf, ",", -1); + + if (virBufferCheckError(&buf) < 0) + goto error; + + *opts = virBufferContentAndReset(&buf); + return 0; + + error: + virBufferFreeAndReset(&buf); + return -1; +} + + +/* virStorageBackendCreateQemuImgCheckEncryption: + * @format: format of file found + * @conn: pointer to connection + * @vol: pointer to volume def + * + * Ensure the proper setup for encryption. + * + * Returns 0 on success, -1 on failure w/ error set + */ +static int +virStorageBackendCreateQemuImgCheckEncryption(int format, + const char *type, + virConnectPtr conn, + virStorageVolDefPtr vol) +{ + virStorageEncryptionPtr enc = vol->target.encryption; + + if (format == VIR_STORAGE_FILE_QCOW || format == VIR_STORAGE_FILE_QCOW2) { + if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW && + enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported volume encryption format %d"), + vol->target.encryption->format); + return -1; + } + if (enc->nsecrets > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("too many secrets for qcow encryption")); + return -1; + } + if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT || + enc->nsecrets == 0) { + if (virStorageGenerateQcowEncryption(conn, vol) < 0) + return -1; + } + } else if (format == VIR_STORAGE_FILE_RAW) { + if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported volume encryption format %d"), + vol->target.encryption->format); + return -1; + } + if (enc->nsecrets > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("too many secrets for luks encryption")); + return -1; + } + if (enc->nsecrets == 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("no secret provided for luks encryption")); + return -1; + } + if (!virCryptoHaveCipher(VIR_CRYPTO_CIPHER_AES256CBC)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("luks encryption usage requires encrypted " + "secret generation to be supported")); + return -1; + } + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("volume encryption unsupported with format %s"), type); + return -1; + } + + return 0; +} + + +static int +virStorageBackendCreateQemuImgSetInput(virStorageVolDefPtr inputvol, + struct _virStorageBackendQemuImgInfo *info) +{ + if (!(info->inputPath = inputvol->target.path)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing input volume target path")); + return -1; + } + + info->inputFormat = inputvol->target.format; + if (inputvol->type == VIR_STORAGE_VOL_BLOCK) + info->inputFormat = VIR_STORAGE_FILE_RAW; + if (!(info->inputFormatStr = + virStorageFileFormatTypeToString(info->inputFormat))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + info->inputFormat); + return -1; + } + + return 0; +} + + +static int +virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + struct _virStorageBackendQemuImgInfo *info) +{ + int accessRetCode = -1; + char *absolutePath = NULL; + + if (info->format == VIR_STORAGE_FILE_RAW) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot set backing store for raw volume")); + return -1; + } + + info->backingFormat = vol->target.backingStore->format; + info->backingPath = vol->target.backingStore->path; + + if (info->preallocate) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("metadata preallocation conflicts with backing" + " store")); + return -1; + } + + /* 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 && inputvol->target.backingStore && + STRNEQ_NULLABLE(inputvol->target.backingStore->path, + info->backingPath)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("a different backing store cannot be specified.")); + return -1; + } + + if (!virStorageFileFormatTypeToString(info->backingFormat)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol backing store type %d"), + info->backingFormat); + return -1; + } + + /* Convert relative backing store paths to absolute paths for access + * validation. + */ + if ('/' != *(info->backingPath) && + virAsprintf(&absolutePath, "%s/%s", pool->def->target.path, + info->backingPath) < 0) + return -1; + accessRetCode = access(absolutePath ? absolutePath : + info->backingPath, R_OK); + VIR_FREE(absolutePath); + if (accessRetCode != 0) { + virReportSystemError(errno, + _("inaccessible backing store volume %s"), + info->backingPath); + return -1; + } + + return 0; +} + + +static int +virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, + int imgformat, + virStorageEncryptionInfoDefPtr enc, + struct _virStorageBackendQemuImgInfo info) +{ + char *opts = NULL; + + if (info.format == VIR_STORAGE_FILE_QCOW2 && !info.compat && + imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) + info.compat = "0.10"; + + if (virStorageBackendCreateQemuImgOpts(enc, &opts, info) < 0) + return -1; + if (opts) + virCommandAddArgList(cmd, "-o", opts, NULL); + VIR_FREE(opts); + + return 0; +} + + +/* Add a secret object to the command line: + * --object secret,id=$secretAlias,file=$secretPath + * + * NB: format=raw is assumed + */ +static int +virStorageBackendCreateQemuImgSecretObject(virCommandPtr cmd, + virStorageVolDefPtr vol, + struct _virStorageBackendQemuImgInfo *info) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *commandStr = NULL; + + if (virAsprintf(&info->secretAlias, "%s_luks0", vol->name) < 0) + return -1; + + virBufferAsprintf(&buf, "secret,id=%s,file=", info->secretAlias); + virQEMUBuildBufferEscapeComma(&buf, info->secretPath); + + if (virBufferCheckError(&buf) < 0) { + virBufferFreeAndReset(&buf); + return -1; + } + + commandStr = virBufferContentAndReset(&buf); + + virCommandAddArgList(cmd, "--object", commandStr, NULL); + + VIR_FREE(commandStr); + return 0; +} + + +/* Create a qemu-img virCommand from the supplied binary path, + * volume definitions and imgformat + */ +virCommandPtr +virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags, + const char *create_tool, + int imgformat, + const char *secretPath) +{ + virCommandPtr cmd = NULL; + const char *type; + struct _virStorageBackendQemuImgInfo info = { + .format = vol->target.format, + .path = vol->target.path, + .encryption = vol->target.encryption != NULL, + .preallocate = !!(flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA), + .compat = vol->target.compat, + .features = vol->target.features, + .nocow = vol->target.nocow, + .secretPath = secretPath, + .secretAlias = NULL, + }; + virStorageEncryptionInfoDefPtr enc = NULL; + + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL); + + /* Treat output block devices as 'raw' format */ + if (vol->type == VIR_STORAGE_VOL_BLOCK) + info.format = VIR_STORAGE_FILE_RAW; + + if (!(type = virStorageFileFormatTypeToString(info.format))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + info.format); + return NULL; + } + + if (info.preallocate && info.format != VIR_STORAGE_FILE_QCOW2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("metadata preallocation only available with qcow2")); + return NULL; + } + if (info.compat && info.format != VIR_STORAGE_FILE_QCOW2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("compatibility option only available with qcow2")); + return NULL; + } + if (info.features && info.format != VIR_STORAGE_FILE_QCOW2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("format features only available with qcow2")); + return NULL; + } + if (info.format == VIR_STORAGE_FILE_RAW && + vol->target.encryption != NULL) { + if (inputvol) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot use inputvol with encrypted raw volume")); + return NULL; + } + if (!info.encryption) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing encryption description")); + return NULL; + } + if (vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + type = "luks"; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Only luks encryption is supported for raw files")); + return NULL; + } + } + + if (inputvol && + virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0) + return NULL; + + if (vol->target.backingStore && + virStorageBackendCreateQemuImgSetBacking(pool, vol, inputvol, + &info) < 0) + return NULL; + + if (info.encryption && + virStorageBackendCreateQemuImgCheckEncryption(info.format, type, + conn, vol) < 0) + return NULL; + + + /* Size in KB */ + info.size_arg = VIR_DIV_UP(vol->target.capacity, 1024); + + cmd = virCommandNew(create_tool); + + /* ignore the backing volume when we're converting a volume */ + if (info.inputPath) + info.backingPath = NULL; + + if (info.inputPath) + virCommandAddArgList(cmd, "convert", "-f", info.inputFormatStr, + "-O", type, NULL); + else + virCommandAddArgList(cmd, "create", "-f", type, NULL); + + if (info.backingPath) + virCommandAddArgList(cmd, "-b", info.backingPath, NULL); + + if (info.format == VIR_STORAGE_FILE_RAW && + vol->target.encryption != NULL && + vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + if (virStorageBackendCreateQemuImgSecretObject(cmd, vol, &info) < 0) { + VIR_FREE(info.secretAlias); + virCommandFree(cmd); + return NULL; + } + enc = &vol->target.encryption->encinfo; + } + + if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, + enc, info) < 0) { + VIR_FREE(info.secretAlias); + virCommandFree(cmd); + return NULL; + } + VIR_FREE(info.secretAlias); + + if (info.inputPath) + virCommandAddArg(cmd, info.inputPath); + virCommandAddArg(cmd, info.path); + if (!info.inputPath && (info.size_arg || !info.backingPath)) + virCommandAddArgFormat(cmd, "%lluK", info.size_arg); + + return cmd; +} + + +static char * +virStorageBackendCreateQemuImgSecretPath(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + virStorageEncryptionPtr enc = vol->target.encryption; + char *secretPath = NULL; + int fd = -1; + uint8_t *secret = NULL; + size_t secretlen = 0; + + if (!enc) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing encryption description")); + return NULL; + } + + if (!conn || !conn->secretDriver || + !conn->secretDriver->secretLookupByUUID || + !conn->secretDriver->secretLookupByUsage || + !conn->secretDriver->secretGetValue) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to look up encryption secret")); + return NULL; + } + + if (!(secretPath = virStoragePoolObjBuildTempFilePath(pool, vol))) + goto cleanup; + + if ((fd = mkostemp(secretPath, O_CLOEXEC)) < 0) { + virReportSystemError(errno, "%s", + _("failed to open luks secret file for write")); + goto error; + } + + if (virSecretGetSecretString(conn, &enc->secrets[0]->seclookupdef, + VIR_SECRET_USAGE_TYPE_VOLUME, + &secret, &secretlen) < 0) + goto error; + + if (safewrite(fd, secret, secretlen) < 0) { + virReportSystemError(errno, "%s", + _("failed to write luks secret file")); + goto error; + } + VIR_FORCE_CLOSE(fd); + + if ((vol->target.perms->uid != (uid_t) -1) && + (vol->target.perms->gid != (gid_t) -1)) { + if (chown(secretPath, vol->target.perms->uid, + vol->target.perms->gid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to chown luks secret file")); + goto error; + } + } + + cleanup: + VIR_DISPOSE_N(secret, secretlen); + VIR_FORCE_CLOSE(fd); + + return secretPath; + + error: + unlink(secretPath); + VIR_FREE(secretPath); + goto cleanup; +} + + +int +virStorageBackendCreateQemuImg(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags) +{ + int ret = -1; + char *create_tool; + int imgformat; + virCommandPtr cmd; + char *secretPath = NULL; + + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); + + create_tool = virFindFileInPath("qemu-img"); + if (!create_tool) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("creation of non-raw file images is " + "not supported without qemu-img.")); + return -1; + } + + imgformat = virStorageBackendQEMUImgBackingFormat(create_tool); + if (imgformat < 0) + goto cleanup; + + if (vol->target.format == VIR_STORAGE_FILE_RAW && + vol->target.encryption && + vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + if (!(secretPath = + virStorageBackendCreateQemuImgSecretPath(conn, pool, vol))) + goto cleanup; + } + + cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol, + flags, create_tool, + imgformat, secretPath); + if (!cmd) + goto cleanup; + + ret = virStorageBackendCreateExecCommand(pool, vol, cmd); + + virCommandFree(cmd); + cleanup: + if (secretPath) { + unlink(secretPath); + VIR_FREE(secretPath); + } + VIR_FREE(create_tool); + return ret; +} + +virStorageBackendBuildVolFrom +virStorageBackendGetBuildVolFromFunction(virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol) +{ + if (!inputvol) + return NULL; + + /* If either volume is a non-raw file vol, or uses encryption, + * we need to use an external tool for converting + */ + if ((vol->type == VIR_STORAGE_VOL_FILE && + (vol->target.format != VIR_STORAGE_FILE_RAW || + vol->target.encryption != NULL)) || + (inputvol->type == VIR_STORAGE_VOL_FILE && + (inputvol->target.format != VIR_STORAGE_FILE_RAW || + inputvol->target.encryption != NULL))) { + return virStorageBackendCreateQemuImg; + } + + if (vol->type == VIR_STORAGE_VOL_PLOOP) + return virStorageBackendCreatePloop; + if (vol->type == VIR_STORAGE_VOL_BLOCK) + return virStorageBackendCreateBlockFrom; + else + return virStorageBackendCreateRaw; +} + + +struct diskType { + int part_table_type; + unsigned short offset; + unsigned short length; + unsigned long long magic; +}; + + +static struct diskType const disk_types[] = { + { VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CULL }, + { VIR_STORAGE_POOL_DISK_GPT, 0x200, 8, 0x5452415020494645ULL }, + { VIR_STORAGE_POOL_DISK_DVH, 0x0, 4, 0x41A9E50BULL }, + { VIR_STORAGE_POOL_DISK_MAC, 0x0, 2, 0x5245ULL }, + { VIR_STORAGE_POOL_DISK_BSD, 0x40, 4, 0x82564557ULL }, + { VIR_STORAGE_POOL_DISK_SUN, 0x1fc, 2, 0xBEDAULL }, + /* + * NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so + * we can't use that. At the moment I'm relying on the "dummy" IPL + * bootloader data that comes from parted. Luckily, the chances of running + * into a pc98 machine running libvirt are approximately nil. + */ + /*{ 0x1fe, 2, 0xAA55UL },*/ + { VIR_STORAGE_POOL_DISK_PC98, 0x0, 8, 0x314C5049000000CBULL }, + /* + * NOTE: the order is important here; some other disk types (like GPT and + * and PC98) also have 0x55AA at this offset. For that reason, the DOS + * one must be the last one. + */ + { VIR_STORAGE_POOL_DISK_DOS, 0x1fe, 2, 0xAA55ULL }, + { -1, 0x0, 0, 0x0ULL }, +}; + + +/* + * virStorageBackendDetectBlockVolFormatFD + * @target: target definition ptr of volume to update + * @fd: fd of storage volume to update, + * @readflags: VolReadErrorMode flags to handle read error after open + * is successful, but read is not. + * + * Returns 0 for success, -1 on a legitimate error condition, -2 if + * the read error is desired to be ignored (along with appropriate + * VIR_WARN of the issue). + */ +static int +virStorageBackendDetectBlockVolFormatFD(virStorageSourcePtr target, + int fd, + unsigned int readflags) +{ + size_t i; + off_t start; + unsigned char buffer[1024]; + ssize_t bytes; + + /* make sure to set the target format "unknown" to begin with */ + target->format = VIR_STORAGE_POOL_DISK_UNKNOWN; + + start = lseek(fd, 0, SEEK_SET); + if (start < 0) { + virReportSystemError(errno, + _("cannot seek to beginning of file '%s'"), + target->path); + return -1; + } + bytes = saferead(fd, buffer, sizeof(buffer)); + if (bytes < 0) { + if (readflags & VIR_STORAGE_VOL_READ_NOERROR) { + VIR_WARN("ignoring failed saferead of file '%s'", + target->path); + return -2; + } else { + virReportSystemError(errno, + _("cannot read beginning of file '%s'"), + target->path); + return -1; + } + } + + for (i = 0; disk_types[i].part_table_type != -1; i++) { + if (disk_types[i].offset + disk_types[i].length > bytes) + continue; + if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic, + disk_types[i].length) == 0) { + target->format = disk_types[i].part_table_type; + break; + } + } + + if (target->format == VIR_STORAGE_POOL_DISK_UNKNOWN) + VIR_DEBUG("cannot determine the target format for '%s'", + target->path); + + return 0; +} + + +/* + * Allows caller to silently ignore files with improper mode + * + * Returns -1 on error. If VIR_STORAGE_VOL_OPEN_NOERROR is passed, we + * return -2 if file mode is unexpected or the volume is a dangling + * symbolic link. + */ +int +virStorageBackendVolOpen(const char *path, struct stat *sb, + unsigned int flags) +{ + int fd, mode = 0; + char *base = last_component(path); + bool noerror = (flags & VIR_STORAGE_VOL_OPEN_NOERROR); + + if (lstat(path, sb) < 0) { + if (errno == ENOENT) { + if (noerror) { + VIR_WARN("ignoring missing file '%s'", path); + return -2; + } + virReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching path '%s'"), + path); + return -1; + } + virReportSystemError(errno, + _("cannot stat file '%s'"), + path); + return -1; + } + + if (S_ISFIFO(sb->st_mode)) { + if (noerror) { + VIR_WARN("ignoring FIFO '%s'", path); + return -2; + } + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Volume path '%s' is a FIFO"), path); + return -1; + } else if (S_ISSOCK(sb->st_mode)) { + if (noerror) { + VIR_WARN("ignoring socket '%s'", path); + return -2; + } + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Volume path '%s' is a socket"), path); + return -1; + } + + /* O_NONBLOCK should only matter during open() for fifos and + * sockets, which we already filtered; but using it prevents a + * TOCTTOU race. However, later on we will want to read() the + * header from this fd, and virFileRead* routines require a + * blocking fd, so fix it up after verifying we avoided a race. + * + * Use of virFileOpenAs allows this path to open a file using + * the uid and gid as it was created in order to open. Since this + * path is not using O_CREAT or O_TMPFILE, mode is meaningless. + * Opening under user/group is especially important in an NFS + * root-squash environment. If the target path isn't on shared + * file system, the open will fail in the OPEN_FORK path. + */ + if ((fd = virFileOpenAs(path, O_RDONLY|O_NONBLOCK|O_NOCTTY, + 0, sb->st_uid, sb->st_gid, + VIR_FILE_OPEN_NOFORK|VIR_FILE_OPEN_FORK)) < 0) { + if ((errno == ENOENT || errno == ELOOP) && + S_ISLNK(sb->st_mode) && noerror) { + VIR_WARN("ignoring dangling symlink '%s'", path); + return -2; + } + if (errno == ENOENT && noerror) { + VIR_WARN("ignoring missing file '%s'", path); + return -2; + } + if (errno == ENXIO && noerror) { + VIR_WARN("ignoring missing fifo '%s'", path); + return -2; + } + if ((errno == EACCES || errno == EPERM) && noerror) { + VIR_WARN("ignoring permission error for '%s'", path); + return -2; + } + + virReportSystemError(errno, _("cannot open volume '%s'"), path); + return -1; + } + + if (fstat(fd, sb) < 0) { + virReportSystemError(errno, _("cannot stat file '%s'"), path); + VIR_FORCE_CLOSE(fd); + return -1; + } + + if (S_ISREG(sb->st_mode)) { + mode = VIR_STORAGE_VOL_OPEN_REG; + } else if (S_ISCHR(sb->st_mode)) { + mode = VIR_STORAGE_VOL_OPEN_CHAR; + } else if (S_ISBLK(sb->st_mode)) { + mode = VIR_STORAGE_VOL_OPEN_BLOCK; + } else if (S_ISDIR(sb->st_mode)) { + mode = VIR_STORAGE_VOL_OPEN_DIR; + + if (STREQ(base, ".") || + STREQ(base, "..")) { + VIR_FORCE_CLOSE(fd); + if (noerror) { + VIR_INFO("Skipping special dir '%s'", base); + return -2; + } + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot use volume path '%s'"), path); + return -1; + } + } else { + VIR_FORCE_CLOSE(fd); + if (noerror) { + VIR_WARN("ignoring unexpected type for file '%s'", path); + return -2; + } + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected type for file '%s'"), path); + return -1; + } + + if (virSetBlocking(fd, true) < 0) { + VIR_FORCE_CLOSE(fd); + virReportSystemError(errno, _("unable to set blocking mode for '%s'"), + path); + return -1; + } + + if (!(mode & flags)) { + VIR_FORCE_CLOSE(fd); + if (noerror) { + VIR_INFO("Skipping volume '%s'", path); + return -2; + } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected storage mode for '%s'"), path); + return -1; + } + + return fd; +} + +/* virStorageIsPloop function checks whether given directory is ploop volume's + * directory. + */ +bool +virStorageBackendIsPloopDir(char *path) +{ + bool ret = false; + char *root = NULL; + char *desc = NULL; + if (virAsprintf(&root, "%s/root.hds", path) < 0) + return ret; + if (!virFileExists(root)) + goto cleanup; + if (virAsprintf(&desc, "%s/DiskDescriptor.xml", path) < 0) + goto cleanup; + if (!virFileExists(desc)) + goto cleanup; + + ret = true; + cleanup: + VIR_FREE(root); + VIR_FREE(desc); + return ret; +} + +/* In case of ploop volumes, path to volume is the path to the ploop + * directory. To get information about allocation, header information + * and etc. we need to perform virStorageBackendVolOpen and + * virStorageBackendUpdateVolTargetFd once again. + */ +int +virStorageBackendRedoPloopUpdate(virStorageSourcePtr target, struct stat *sb, + int *fd, unsigned int flags) +{ + char *path = NULL; + int ret = -1; + + if (virAsprintf(&path, "%s/root.hds", target->path) < 0) + return -1; + VIR_FORCE_CLOSE(*fd); + if ((*fd = virStorageBackendVolOpen(path, sb, flags)) < 0) + goto cleanup; + ret = virStorageBackendUpdateVolTargetInfoFD(target, *fd, sb); + + cleanup: + + VIR_FREE(path); + return ret; +} + +/* + * virStorageBackendUpdateVolTargetInfo + * @voltype: Volume type + * @target: target definition ptr of volume to update + * @withBlockVolFormat: true if caller determined a block file + * @openflags: various VolOpenCheckMode flags to handle errors on open + * @readflags: VolReadErrorMode flags to handle read error after open + * is successful, but read is not. + * + * Returns 0 for success, -1 on a legitimate error condition, and -2 + * if the openflags used VIR_STORAGE_VOL_OPEN_NOERROR and some sort of + * open error occurred. It is up to the caller to handle. A -2 may also + * be returned if the caller passed a readflagsflag. + */ +int +virStorageBackendUpdateVolTargetInfo(virStorageVolType voltype, + virStorageSourcePtr target, + bool withBlockVolFormat, + unsigned int openflags, + unsigned int readflags) +{ + int ret, fd = -1; + struct stat sb; + char *buf = NULL; + ssize_t len = VIR_STORAGE_MAX_HEADER; + + if ((ret = virStorageBackendVolOpen(target->path, &sb, openflags)) < 0) + goto cleanup; + fd = ret; + + if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd, &sb)) < 0) + goto cleanup; + + if ((voltype == VIR_STORAGE_VOL_FILE || voltype == VIR_STORAGE_VOL_BLOCK) && + target->format != VIR_STORAGE_FILE_NONE) { + if (S_ISDIR(sb.st_mode)) { + if (virStorageBackendIsPloopDir(target->path)) { + if ((ret = virStorageBackendRedoPloopUpdate(target, &sb, &fd, + openflags)) < 0) + goto cleanup; + target->format = VIR_STORAGE_FILE_PLOOP; + } else { + ret = 0; + goto cleanup; + } + } + + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { + virReportSystemError(errno, _("cannot seek to start of '%s'"), target->path); + ret = -1; + goto cleanup; + } + + if ((len = virFileReadHeaderFD(fd, len, &buf)) < 0) { + if (readflags & VIR_STORAGE_VOL_READ_NOERROR) { + VIR_WARN("ignoring failed header read for '%s'", + target->path); + ret = -2; + } else { + virReportSystemError(errno, + _("cannot read header '%s'"), + target->path); + ret = -1; + } + goto cleanup; + } + + if (virStorageSourceUpdateCapacity(target, buf, len, false) < 0) { + ret = -1; + goto cleanup; + } + } + + if (withBlockVolFormat) { + if ((ret = virStorageBackendDetectBlockVolFormatFD(target, fd, + readflags)) < 0) + goto cleanup; + } + + cleanup: + VIR_FORCE_CLOSE(fd); + VIR_FREE(buf); + return ret; +} + +/* + * virStorageBackendUpdateVolInfo + * @vol: Pointer to a volume storage definition + * @withBlockVolFormat: true if the caller determined a block file + * @openflags: various VolOpenCheckMode flags to handle errors on open + * @readflags: various VolReadErrorMode flags to handle errors on read + * + * Returns 0 for success, -1 on a legitimate error condition, and -2 + * if the openflags used VIR_STORAGE_VOL_OPEN_NOERROR and some sort of + * open error occurred. It is up to the caller to handle. + */ +int +virStorageBackendUpdateVolInfo(virStorageVolDefPtr vol, + bool withBlockVolFormat, + unsigned int openflags, + unsigned int readflags) +{ + int ret; + + if ((ret = virStorageBackendUpdateVolTargetInfo(vol->type, + &vol->target, + withBlockVolFormat, + openflags, readflags)) < 0) + return ret; + + if (vol->target.backingStore && + (ret = virStorageBackendUpdateVolTargetInfo(VIR_STORAGE_VOL_FILE, + vol->target.backingStore, + withBlockVolFormat, + VIR_STORAGE_VOL_OPEN_DEFAULT | + VIR_STORAGE_VOL_OPEN_NOERROR, + readflags) < 0)) + return ret; + + return 0; +} + +/* + * virStorageBackendUpdateVolTargetInfoFD: + * @target: target definition ptr of volume to update + * @fd: fd of storage volume to update, via virStorageBackendOpenVol*, or -1 + * @sb: details about file (must match @fd, if that is provided) + * + * Returns 0 for success, -1 on a legitimate error condition. + */ +int +virStorageBackendUpdateVolTargetInfoFD(virStorageSourcePtr target, + int fd, + struct stat *sb) +{ +#if WITH_SELINUX + security_context_t filecon = NULL; +#endif + + if (virStorageSourceUpdateBackingSizes(target, fd, sb) < 0) + return -1; + + if (!target->perms && VIR_ALLOC(target->perms) < 0) + return -1; + target->perms->mode = sb->st_mode & S_IRWXUGO; + target->perms->uid = sb->st_uid; + target->perms->gid = sb->st_gid; + + if (!target->timestamps && VIR_ALLOC(target->timestamps) < 0) + return -1; + target->timestamps->atime = get_stat_atime(sb); + target->timestamps->btime = get_stat_birthtime(sb); + target->timestamps->ctime = get_stat_ctime(sb); + target->timestamps->mtime = get_stat_mtime(sb); + + VIR_FREE(target->perms->label); + +#if WITH_SELINUX + /* XXX: make this a security driver call */ + if (fd >= 0) { + if (fgetfilecon_raw(fd, &filecon) == -1) { + if (errno != ENODATA && errno != ENOTSUP) { + virReportSystemError(errno, + _("cannot get file context of '%s'"), + target->path); + return -1; + } + } else { + if (VIR_STRDUP(target->perms->label, filecon) < 0) { + freecon(filecon); + return -1; + } + freecon(filecon); + } + } +#endif + + return 0; +} + +bool +virStorageBackendPoolPathIsStable(const char *path) +{ + if (path == NULL || STREQ(path, "/dev") || STREQ(path, "/dev/")) + return false; + + if (!STRPREFIX(path, "/dev/")) + return false; + + return true; +} + +/* + * Given a volume path directly in /dev/XXX, iterate over the + * entries in the directory pool->def->target.path and find the + * first symlink pointing to the volume path. + * + * If, the target.path is /dev/, then return the original volume + * path. + * + * If no symlink is found, then return the original volume path + * + * Typically target.path is one of the /dev/disk/by-XXX dirs + * with stable paths. + * + * If 'loop' is true, we use a timeout loop to give dynamic paths + * a change to appear. + */ +char * +virStorageBackendStablePath(virStoragePoolObjPtr pool, + const char *devpath, + bool loop) +{ + DIR *dh; + struct dirent *dent; + char *stablepath; + int opentries = 0; + int retry = 0; + int direrr; + + /* Logical pools are under /dev but already have stable paths */ + if (pool->def->type == VIR_STORAGE_POOL_LOGICAL || + !virStorageBackendPoolPathIsStable(pool->def->target.path)) + goto ret_strdup; + + /* We loop here because /dev/disk/by-{id,path} may not have existed + * before we started this operation, so we have to give it some time to + * get created. + */ + reopen: + if (virDirOpenQuiet(&dh, pool->def->target.path) < 0) { + opentries++; + if (loop && errno == ENOENT && opentries < 50) { + usleep(100 * 1000); + goto reopen; + } + virReportSystemError(errno, + _("cannot read dir '%s'"), + pool->def->target.path); + return NULL; + } + + /* The pool is pointing somewhere like /dev/disk/by-path + * or /dev/disk/by-id, so we need to check all symlinks in + * the target directory and figure out which one points + * to this device node. + * + * And it might need some time till the stable path shows + * up, so add timeout to retry here. Ignore readdir failures, + * since we have a fallback. + */ + retry: + while ((direrr = virDirRead(dh, &dent, NULL)) > 0) { + if (virAsprintf(&stablepath, "%s/%s", + pool->def->target.path, + dent->d_name) == -1) { + VIR_DIR_CLOSE(dh); + return NULL; + } + + if (virFileLinkPointsTo(stablepath, devpath)) { + VIR_DIR_CLOSE(dh); + return stablepath; + } + + VIR_FREE(stablepath); + } + + if (!direrr && loop && ++retry < 100) { + usleep(100 * 1000); + goto retry; + } + + VIR_DIR_CLOSE(dh); + + ret_strdup: + /* Couldn't find any matching stable link so give back + * the original non-stable dev path + */ + + ignore_value(VIR_STRDUP(stablepath, devpath)); + + return stablepath; +} + +/* + * Check whether the ploop image has snapshots. + * return: -1 - failed to check + * 0 - no snapshots + * 1 - at least one snapshot + */ +static int +virStorageBackendPloopHasSnapshots(char *path) +{ + virCommandPtr cmd = NULL; + char *output = NULL; + char *snap_tool = NULL; + int ret = -1; + + snap_tool = virFindFileInPath("ploop"); + if (!snap_tool) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to find ploop, please install " + "ploop tools")); + return ret; + } + + cmd = virCommandNewArgList(snap_tool, "snapshot-list", NULL); + virCommandAddArgFormat(cmd, "%s/DiskDescriptor.xml", path); + virCommandSetOutputBuffer(cmd, &output); + + if ((ret = virCommandRun(cmd, NULL)) < 0) + goto cleanup; + + if (!strstr(output, "root.hds.")) { + ret = 1; + goto cleanup; + } + ret = 0; + + cleanup: + VIR_FREE(output); + virCommandFree(cmd); + return ret; +} + +int +virStorageBackendVolUploadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + virStreamPtr stream, + unsigned long long offset, + unsigned long long len, + unsigned int flags) +{ + char *path = NULL; + char *target_path = vol->target.path; + int ret = -1; + int has_snap = 0; + + virCheckFlags(0, -1); + /* if volume has target format VIR_STORAGE_FILE_PLOOP + * we need to restore DiskDescriptor.xml, according to + * new contents of volume. This operation will be perfomed + * when volUpload is fully finished. */ + if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { + /* Fail if the volume contains snapshots or we failed to check it.*/ + has_snap = virStorageBackendPloopHasSnapshots(vol->target.path); + if (has_snap < 0) { + goto cleanup; + } else if (!has_snap) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("can't upload volume, all existing snapshots" + " will be lost")); + goto cleanup; + } + + if (virAsprintf(&path, "%s/root.hds", vol->target.path) < 0) + return -1; + target_path = path; + } + + /* Not using O_CREAT because the file is required to already exist at + * this point */ + ret = virFDStreamOpenBlockDevice(stream, target_path, + offset, len, O_WRONLY); + + cleanup: + VIR_FREE(path); + return ret; +} + +int +virStorageBackendVolDownloadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + virStreamPtr stream, + unsigned long long offset, + unsigned long long len, + unsigned int flags) +{ + char *path = NULL; + char *target_path = vol->target.path; + int ret = -1; + int has_snap = 0; + + virCheckFlags(0, -1); + if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { + has_snap = virStorageBackendPloopHasSnapshots(vol->target.path); + if (has_snap < 0) { + goto cleanup; + } else if (!has_snap) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("can't download volume, all existing snapshots" + " will be lost")); + goto cleanup; + } + if (virAsprintf(&path, "%s/root.hds", vol->target.path) < 0) + goto cleanup; + target_path = path; + } + + ret = virFDStreamOpenBlockDevice(stream, target_path, + offset, len, O_RDONLY); + + cleanup: + VIR_FREE(path); + return ret; +} + + +/* If the volume we're wiping is already a sparse file, we simply + * truncate and extend it to its original size, filling it with + * zeroes. This behavior is guaranteed by POSIX: + * + * http://www.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + * + * If fildes refers to a regular file, the ftruncate() function shall + * cause the size of the file to be truncated to length. If the size + * of the file previously exceeded length, the extra data shall no + * longer be available to reads on the file. If the file previously + * was smaller than this size, ftruncate() shall increase the size of + * the file. If the file size is increased, the extended area shall + * appear as if it were zero-filled. + */ +static int +virStorageBackendVolZeroSparseFileLocal(const char *path, + off_t size, + int fd) +{ + if (ftruncate(fd, 0) < 0) { + virReportSystemError(errno, + _("Failed to truncate volume with " + "path '%s' to 0 bytes"), + path); + return -1; + } + + if (ftruncate(fd, size) < 0) { + virReportSystemError(errno, + _("Failed to truncate volume with " + "path '%s' to %ju bytes"), + path, (uintmax_t)size); + return -1; + } + + return 0; +} + + +static int +virStorageBackendWipeLocal(const char *path, + int fd, + unsigned long long wipe_len, + size_t writebuf_length) +{ + int ret = -1, written = 0; + unsigned long long remaining = 0; + size_t write_size = 0; + char *writebuf = NULL; + + VIR_DEBUG("wiping start: 0 len: %llu", wipe_len); + + if (VIR_ALLOC_N(writebuf, writebuf_length) < 0) + goto cleanup; + + if (lseek(fd, 0, SEEK_SET) < 0) { + virReportSystemError(errno, + _("Failed to seek to the start in volume " + "with path '%s'"), + path); + goto cleanup; + } + + remaining = wipe_len; + while (remaining > 0) { + + write_size = (writebuf_length < remaining) ? writebuf_length : remaining; + written = safewrite(fd, writebuf, write_size); + if (written < 0) { + virReportSystemError(errno, + _("Failed to write %zu bytes to " + "storage volume with path '%s'"), + write_size, path); + + goto cleanup; + } + + remaining -= written; + } + + if (fdatasync(fd) < 0) { + virReportSystemError(errno, + _("cannot sync data to volume with path '%s'"), + path); + goto cleanup; + } + + VIR_DEBUG("Wrote %llu bytes to volume with path '%s'", wipe_len, path); + + ret = 0; + + cleanup: + VIR_FREE(writebuf); + return ret; +} + + +static int +virStorageBackendVolWipeLocalFile(const char *path, + unsigned int algorithm, + unsigned long long allocation) +{ + int ret = -1, fd = -1; + const char *alg_char = NULL; + struct stat st; + virCommandPtr cmd = NULL; + + fd = open(path, O_RDWR); + if (fd == -1) { + virReportSystemError(errno, + _("Failed to open storage volume with path '%s'"), + path); + goto cleanup; + } + + if (fstat(fd, &st) == -1) { + virReportSystemError(errno, + _("Failed to stat storage volume with path '%s'"), + path); + goto cleanup; + } + + switch ((virStorageVolWipeAlgorithm) algorithm) { + case VIR_STORAGE_VOL_WIPE_ALG_ZERO: + alg_char = "zero"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_NNSA: + alg_char = "nnsa"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_DOD: + alg_char = "dod"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_BSI: + alg_char = "bsi"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_GUTMANN: + alg_char = "gutmann"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_SCHNEIER: + alg_char = "schneier"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER7: + alg_char = "pfitzner7"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_PFITZNER33: + alg_char = "pfitzner33"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_RANDOM: + alg_char = "random"; + break; + case VIR_STORAGE_VOL_WIPE_ALG_TRIM: + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("'trim' algorithm not supported")); + goto cleanup; + case VIR_STORAGE_VOL_WIPE_ALG_LAST: + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported algorithm %d"), + algorithm); + goto cleanup; + } + + VIR_DEBUG("Wiping file '%s' with algorithm '%s'", path, alg_char); + + if (algorithm != VIR_STORAGE_VOL_WIPE_ALG_ZERO) { + cmd = virCommandNew(SCRUB); + virCommandAddArgList(cmd, "-f", "-p", alg_char, path, NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; + } else { + if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) { + ret = virStorageBackendVolZeroSparseFileLocal(path, st.st_size, fd); + } else { + ret = virStorageBackendWipeLocal(path, + fd, + allocation, + st.st_blksize); + } + if (ret < 0) + goto cleanup; + } + + cleanup: + virCommandFree(cmd); + VIR_FORCE_CLOSE(fd); + return ret; +} + + +static int +virStorageBackendVolWipePloop(virStorageVolDefPtr vol, + unsigned int algorithm) +{ + virCommandPtr cmd = NULL; + char *target_path = NULL; + char *disk_desc = NULL; + char *create_tool = NULL; + + int ret = -1; + + create_tool = virFindFileInPath("ploop"); + if (!create_tool) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to find ploop tools, please install them")); + return -1; + } + + if (virAsprintf(&target_path, "%s/root.hds", vol->target.path) < 0) + goto cleanup; + + if (virAsprintf(&disk_desc, "%s/DiskDescriptor.xml", vol->target.path) < 0) + goto cleanup; + + if (virStorageBackendVolWipeLocalFile(target_path, + algorithm, + vol->target.allocation) < 0) + goto cleanup; + + if (virFileRemove(disk_desc, 0, 0) < 0) { + virReportError(errno, _("Failed to delete DiskDescriptor.xml of volume '%s'"), + vol->target.path); + goto cleanup; + } + if (virFileRemove(target_path, 0, 0) < 0) { + virReportError(errno, _("failed to delete root.hds of volume '%s'"), + vol->target.path); + goto cleanup; + } + + cmd = virCommandNewArgList(create_tool, "init", "-s", NULL); + + virCommandAddArgFormat(cmd, "%lluM", VIR_DIV_UP(vol->target.capacity, + (1024 * 1024))); + virCommandAddArgList(cmd, "-t", "ext4", NULL); + virCommandAddArg(cmd, target_path); + ret = virCommandRun(cmd, NULL); + + cleanup: + VIR_FREE(disk_desc); + VIR_FREE(target_path); + VIR_FREE(create_tool); + virCommandFree(cmd); + return ret; +} + + +int +virStorageBackendVolWipeLocal(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned int algorithm, + unsigned int flags) +{ + int ret = -1; + + virCheckFlags(0, -1); + + VIR_DEBUG("Wiping volume with path '%s' and algorithm %u", + vol->target.path, algorithm); + + if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { + ret = virStorageBackendVolWipePloop(vol, algorithm); + } else { + ret = virStorageBackendVolWipeLocalFile(vol->target.path, + algorithm, + vol->target.allocation); + } + + return ret; +} + + +/** + * virStorageBackendFindGlusterPoolSources: + * @host: host to detect volumes on + * @pooltype: src->format is set to this value + * @list: list of storage pool sources to be filled + * @report: report error if the 'gluster' cli tool is missing + * + * Looks up gluster volumes on @host and fills them to @list. + * + * Returns number of volumes on the host on success, or -1 on error. + */ +int +virStorageBackendFindGlusterPoolSources(const char *host, + int pooltype, + virStoragePoolSourceListPtr list, + bool report) +{ + char *glusterpath = NULL; + char *outbuf = NULL; + virCommandPtr cmd = NULL; + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr *nodes = NULL; + virStoragePoolSource *src = NULL; + size_t i; + int nnodes; + int rc; + + int ret = -1; + + if (!(glusterpath = virFindFileInPath(GLUSTER_CLI))) { + if (report) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'gluster' command line tool not found")); + return -1; + } else { + return 0; + } + } + + cmd = virCommandNewArgList(glusterpath, + "--xml", + "--log-file=/dev/null", + "volume", "info", "all", NULL); + + virCommandAddArgFormat(cmd, "--remote-host=%s", host); + virCommandSetOutputBuffer(cmd, &outbuf); + + if (virCommandRun(cmd, &rc) < 0) + goto cleanup; + + if (rc != 0) { + ret = 0; + goto cleanup; + } + + if (!(doc = virXMLParseStringCtxt(outbuf, _("(gluster_cli_output)"), + &ctxt))) + goto cleanup; + + if ((nnodes = virXPathNodeSet("//volumes/volume", ctxt, &nodes)) < 0) + goto cleanup; + + for (i = 0; i < nnodes; i++) { + ctxt->node = nodes[i]; + + if (!(src = virStoragePoolSourceListNewSource(list))) + goto cleanup; + + if (!(src->dir = virXPathString("string(//name)", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to extract gluster volume name")); + goto cleanup; + } + + if (VIR_ALLOC_N(src->hosts, 1) < 0) + goto cleanup; + src->nhost = 1; + + if (VIR_STRDUP(src->hosts[0].name, host) < 0) + goto cleanup; + + src->format = pooltype; + } + + ret = nnodes; + + cleanup: + VIR_FREE(nodes); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + VIR_FREE(outbuf); + virCommandFree(cmd); + VIR_FREE(glusterpath); + return ret; +} + + +#if WITH_BLKID + +typedef enum { + VIR_STORAGE_BLKID_PROBE_ERROR = -1, + VIR_STORAGE_BLKID_PROBE_UNDEFINED, /* Nothing found */ + VIR_STORAGE_BLKID_PROBE_UNKNOWN, /* Don't know libvirt fs/part type */ + VIR_STORAGE_BLKID_PROBE_MATCH, /* Matches the on disk format */ + VIR_STORAGE_BLKID_PROBE_DIFFERENT, /* Format doesn't match on disk format */ +} virStorageBackendBLKIDProbeResult; + +/* + * Utility function to probe for a file system on the device using the + * blkid "superblock" (e.g. default) APIs. + * + * NB: In general this helper will handle the virStoragePoolFormatFileSystem + * format types; however, if called from the Disk path, the initial fstype + * check will fail forcing the usage of the ProbePart helper. + * + * Returns virStorageBackendBLKIDProbeResult enum + */ +static virStorageBackendBLKIDProbeResult +virStorageBackendBLKIDFindFS(blkid_probe probe, + const char *device, + const char *format) +{ + const char *fstype = NULL; + + /* Make sure we're doing a superblock probe from the start */ + blkid_probe_enable_superblocks(probe, true); + blkid_probe_reset_superblocks_filter(probe); + + if (blkid_do_probe(probe) != 0) { + VIR_INFO("No filesystem found on device '%s'", device); + return VIR_STORAGE_BLKID_PROBE_UNDEFINED; + } + + if (blkid_probe_lookup_value(probe, "TYPE", &fstype, NULL) == 0) { + if (STREQ(fstype, format)) + return VIR_STORAGE_BLKID_PROBE_MATCH; + + return VIR_STORAGE_BLKID_PROBE_DIFFERENT; + } + + if (blkid_known_fstype(format) == 0) + return VIR_STORAGE_BLKID_PROBE_UNKNOWN; + + return VIR_STORAGE_BLKID_PROBE_ERROR; +} + + +/* + * Utility function to probe for a partition on the device using the + * blkid "partitions" APIs. + * + * NB: In general, this API will be validating the virStoragePoolFormatDisk + * format types. + * + * Returns virStorageBackendBLKIDProbeResult enum + */ +static virStorageBackendBLKIDProbeResult +virStorageBackendBLKIDFindPart(blkid_probe probe, + const char *device, + const char *format) +{ + const char *pttype = NULL; + + /* A blkid_known_pttype on "dvh" and "pc98" returns a failure; + * however, the blkid_do_probe for "dvh" returns "sgi" and + * for "pc98" it returns "dos". So since those will cause problems + * with startup comparison, let's just treat them as UNKNOWN causing + * the caller to fallback to using PARTED */ + if (STREQ(format, "dvh") || STREQ(format, "pc98")) + return VIR_STORAGE_BLKID_PROBE_UNKNOWN; + + /* Make sure we're doing a partitions probe from the start */ + blkid_probe_enable_partitions(probe, true); + blkid_probe_reset_partitions_filter(probe); + + if (blkid_do_probe(probe) != 0) { + VIR_INFO("No partition found on device '%s'", device); + return VIR_STORAGE_BLKID_PROBE_UNDEFINED; + } + + if (blkid_probe_lookup_value(probe, "PTTYPE", &pttype, NULL) == 0) { + if (STREQ(pttype, format)) + return VIR_STORAGE_BLKID_PROBE_MATCH; + + return VIR_STORAGE_BLKID_PROBE_DIFFERENT; + } + + if (blkid_known_pttype(format) == 0) + return VIR_STORAGE_BLKID_PROBE_UNKNOWN; + + return VIR_STORAGE_BLKID_PROBE_ERROR; +} + + +/* + * @device: Path to device + * @format: Desired format + * @writelabel: True if desire to write the label + * + * Use the blkid_ APIs in order to get details regarding whether a file + * system or partition exists on the disk already. + * + * Returns: + * -2: Force usage of PARTED for unknown types + * -1: An error was encountered, with error message set + * 0: No file system found + */ +static int +virStorageBackendBLKIDFindEmpty(const char *device, + const char *format, + bool writelabel) +{ + + int ret = -1; + int rc; + blkid_probe probe = NULL; + + VIR_DEBUG("Probe for existing filesystem/partition format %s on device %s", + format, device); + + if (!(probe = blkid_new_probe_from_filename(device))) { + virReportError(VIR_ERR_STORAGE_PROBE_FAILED, + _("Failed to create filesystem probe for device %s"), + device); + return -1; + } + + /* Look for something on FS, if it either doesn't recognize the + * format type as a valid FS format type or it doesn't find a valid + * format type on the device, then perform the same check using + * partition probing. */ + rc = virStorageBackendBLKIDFindFS(probe, device, format); + if (rc == VIR_STORAGE_BLKID_PROBE_UNDEFINED || + rc == VIR_STORAGE_BLKID_PROBE_UNKNOWN) { + + rc = virStorageBackendBLKIDFindPart(probe, device, format); + } + + switch (rc) { + case VIR_STORAGE_BLKID_PROBE_UNDEFINED: + if (writelabel) + ret = 0; + else + virReportError(VIR_ERR_STORAGE_PROBE_FAILED, + _("Device '%s' is unrecognized, requires build"), + device); + break; + + case VIR_STORAGE_BLKID_PROBE_ERROR: + virReportError(VIR_ERR_STORAGE_PROBE_FAILED, + _("Failed to probe for format type '%s'"), format); + break; + + case VIR_STORAGE_BLKID_PROBE_UNKNOWN: + ret = -2; + break; + + case VIR_STORAGE_BLKID_PROBE_MATCH: + if (writelabel) + virReportError(VIR_ERR_STORAGE_POOL_BUILT, + _("Device '%s' already formatted using '%s'"), + device, format); + else + ret = 0; + break; + + case VIR_STORAGE_BLKID_PROBE_DIFFERENT: + if (writelabel) + virReportError(VIR_ERR_STORAGE_POOL_BUILT, + _("Format of device '%s' does not match the " + "expected format '%s', forced overwrite is " + "necessary"), + device, format); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("Format of device '%s' does not match the " + "expected format '%s'"), + device, format); + break; + } + + if (ret == 0 && blkid_do_probe(probe) != 1) { + virReportError(VIR_ERR_STORAGE_PROBE_FAILED, "%s", + _("Found additional probes to run, probing may " + "be incorrect")); + ret = -1; + } + + blkid_free_probe(probe); + + return ret; +} + +#else /* #if WITH_BLKID */ + +static int +virStorageBackendBLKIDFindEmpty(const char *device ATTRIBUTE_UNUSED, + const char *format ATTRIBUTE_UNUSED, + bool writelabel ATTRIBUTE_UNUSED) +{ + return -2; +} + +#endif /* #if WITH_BLKID */ + + +#if WITH_STORAGE_DISK + +typedef enum { + VIR_STORAGE_PARTED_ERROR = -1, + VIR_STORAGE_PARTED_MATCH, /* Valid label found and matches format */ + VIR_STORAGE_PARTED_DIFFERENT, /* Valid label found but not match format */ + VIR_STORAGE_PARTED_UNKNOWN, /* No or unrecognized label */ + VIR_STORAGE_PARTED_NOPTTYPE, /* Did not find the Partition Table type */ + VIR_STORAGE_PARTED_PTTYPE_UNK, /* Partition Table type unknown*/ +} virStorageBackendPARTEDResult; + +/** + * Check for a valid disk label (partition table) on device using + * the PARTED command + * + * returns virStorageBackendPARTEDResult + */ +static virStorageBackendPARTEDResult +virStorageBackendPARTEDFindLabel(const char *device, + const char *format) +{ + const char *const args[] = { + device, "print", "--script", NULL, + }; + virCommandPtr cmd = virCommandNew(PARTED); + char *output = NULL; + char *error = NULL; + char *start, *end; + int ret = VIR_STORAGE_PARTED_ERROR; + + virCommandAddArgSet(cmd, args); + virCommandAddEnvString(cmd, "LC_ALL=C"); + virCommandSetOutputBuffer(cmd, &output); + virCommandSetErrorBuffer(cmd, &error); + + /* if parted succeeds we have a valid partition table */ + ret = virCommandRun(cmd, NULL); + if (ret < 0) { + if ((output && strstr(output, "unrecognised disk label")) || + (error && strstr(error, "unrecognised disk label"))) { + ret = VIR_STORAGE_PARTED_UNKNOWN; + } + goto cleanup; + } + + /* Search for "Partition Table:" in the output. If not present, + * then we cannot validate the partition table type. + */ + if (!(start = strstr(output, "Partition Table: ")) || + !(end = strstr(start, "\n"))) { + VIR_DEBUG("Unable to find tag in output: %s", output); + ret = VIR_STORAGE_PARTED_NOPTTYPE; + goto cleanup; + } + start += strlen("Partition Table: "); + *end = '\0'; + + /* on disk it's "msdos", but we document/use "dos" so deal with it here */ + if (STREQ(start, "msdos")) + start += 2; + + /* Make sure we know about this type */ + if (virStoragePoolFormatDiskTypeFromString(start) < 0) { + ret = VIR_STORAGE_PARTED_PTTYPE_UNK; + goto cleanup; + } + + /* Does the on disk match what the pool desired? */ + if (STREQ(start, format)) + ret = VIR_STORAGE_PARTED_MATCH; + + ret = VIR_STORAGE_PARTED_DIFFERENT; + + cleanup: + virCommandFree(cmd); + VIR_FREE(output); + VIR_FREE(error); + return ret; +} + + +/** + * Determine whether the label on the disk is valid or in a known format + * for the purpose of rewriting the label during build or being able to + * start a pool on a device. + * + * When 'writelabel' is true, if we find a valid disk label on the device, + * then we shouldn't be attempting to write as the volume may contain + * data. Force the usage of the overwrite flag to the build command in + * order to be certain. When the disk label is unrecognized, then it + * should be safe to write. + * + * When 'writelabel' is false, only if we find a valid disk label on the + * device should we allow the start since for this path we won't be + * rewriting the label. + * + * Return: 0 if it's OK + * -1 if something's wrong + */ +static int +virStorageBackendPARTEDValidLabel(const char *device, + const char *format, + bool writelabel) +{ + int ret = -1; + virStorageBackendPARTEDResult check; + + check = virStorageBackendPARTEDFindLabel(device, format); + switch (check) { + case VIR_STORAGE_PARTED_ERROR: + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Error checking for disk label, failed to get " + "disk partition information")); + break; + + case VIR_STORAGE_PARTED_MATCH: + if (writelabel) + virReportError(VIR_ERR_OPERATION_INVALID, + _("Disk label already formatted using '%s'"), + format); + else + ret = 0; + break; + + case VIR_STORAGE_PARTED_DIFFERENT: + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Known, but different label format present, " + "requires build --overwrite")); + break; + + case VIR_STORAGE_PARTED_UNKNOWN: + if (writelabel) + ret = 0; + else + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Unrecognized disk label found, requires build")); + break; + + case VIR_STORAGE_PARTED_NOPTTYPE: + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Unable to determine Partition Type, " + "requires build --overwrite")); + break; + + case VIR_STORAGE_PARTED_PTTYPE_UNK: + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Unknown Partition Type, requires build --overwrite")); + break; + } + + return ret; +} + +#else + +static int +virStorageBackendPARTEDValidLabel(const char *device ATTRIBUTE_UNUSED, + const char *format ATTRIBUTE_UNUSED, + bool writelabel ATTRIBUTE_UNUSED) +{ + return -2; +} + + +#endif /* #if WITH_STORAGE_DISK */ + + +/* virStorageBackendDeviceIsEmpty: + * @devpath: Path to the device to check + * @format: Desired format string + * @writelabel: True if the caller expects to write the label + * + * Check if the @devpath has some sort of known file system using the + * BLKID API if available. + * + * Returns true if the probe deems the device has nothing valid on it + * or when we cannot check and we're not writing the label. + * + * Returns false if the probe finds something + */ +bool +virStorageBackendDeviceIsEmpty(const char *devpath, + const char *format, + bool writelabel) +{ + int ret; + + if ((ret = virStorageBackendBLKIDFindEmpty(devpath, format, + writelabel)) == -2) + ret = virStorageBackendPARTEDValidLabel(devpath, format, writelabel); + + if (ret == -2 && !writelabel) + ret = 0; + + if (ret == -2) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("Unable to probe '%s' for existing data, " + "forced overwrite is necessary"), + devpath); + } + + return ret == 0; +} diff --git a/src/storage/storage_util.h b/src/storage/storage_util.h new file mode 100644 index 0000000000..9f17822e74 --- /dev/null +++ b/src/storage/storage_util.h @@ -0,0 +1,149 @@ +/* + * storage_util.h: utility functions for storage driver + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#ifndef __VIR_STORAGE_UTIL_H__ +# define __VIR_STORAGE_UTIL_H__ + +# include + +# include "internal.h" +# include "storage_conf.h" +# include "vircommand.h" +# include "storage_driver.h" +# include "storage_backend.h" + +/* File creation/cloning functions used for cloning between backends */ +int virStorageBackendCreateRaw(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags); + +int virStorageBackendCreateQemuImg(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags); + +int virStorageBackendCreatePloop(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags); + +int virStoragePloopResize(virStorageVolDefPtr vol, + unsigned long long capacity); + +int virStorageBackendRedoPloopUpdate(virStorageSourcePtr target, + struct stat *sb, int *fd, + unsigned int flags); +bool virStorageBackendIsPloopDir(char *path); + +virStorageBackendBuildVolFrom +virStorageBackendGetBuildVolFromFunction(virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol); + +int virStorageBackendFindGlusterPoolSources(const char *host, + int pooltype, + virStoragePoolSourceListPtr list, + bool report); + +int virStorageBackendVolUploadLocal(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStreamPtr stream, + unsigned long long offset, + unsigned long long len, + unsigned int flags); +int virStorageBackendVolDownloadLocal(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStreamPtr stream, + unsigned long long offset, + unsigned long long len, + unsigned int flags); + +int virStorageBackendVolWipeLocal(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int algorithm, + unsigned int flags); + +bool virStorageBackendDeviceIsEmpty(const char *devpath, + const char *format, + bool writelabel); + +/* VolOpenCheckMode flags */ +enum { + VIR_STORAGE_VOL_OPEN_NOERROR = 1 << 0, /* don't error if unexpected type + * encountered, just warn */ + VIR_STORAGE_VOL_OPEN_REG = 1 << 1, /* regular files okay */ + VIR_STORAGE_VOL_OPEN_BLOCK = 1 << 2, /* block files okay */ + VIR_STORAGE_VOL_OPEN_CHAR = 1 << 3, /* char files okay */ + VIR_STORAGE_VOL_OPEN_DIR = 1 << 4, /* directories okay */ +}; + +/* VolReadErrorMode flags + * If flag is present, then operation won't cause fatal error for + * specified operation, rather a VIR_WARN will be issued and a -2 returned + * for function call + */ +enum { + VIR_STORAGE_VOL_READ_NOERROR = 1 << 0, /* ignore *read errors */ +}; + +# define VIR_STORAGE_VOL_OPEN_DEFAULT (VIR_STORAGE_VOL_OPEN_REG |\ + VIR_STORAGE_VOL_OPEN_BLOCK) + +int virStorageBackendVolOpen(const char *path, struct stat *sb, + unsigned int flags) + ATTRIBUTE_RETURN_CHECK + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +# define VIR_STORAGE_DEFAULT_POOL_PERM_MODE 0755 +# define VIR_STORAGE_DEFAULT_VOL_PERM_MODE 0600 + +int virStorageBackendUpdateVolInfo(virStorageVolDefPtr vol, + bool withBlockVolFormat, + unsigned int openflags, + unsigned int readflags); +int virStorageBackendUpdateVolTargetInfo(virStorageVolType voltype, + virStorageSourcePtr target, + bool withBlockVolFormat, + unsigned int openflags, + unsigned int readflags); +int virStorageBackendUpdateVolTargetInfoFD(virStorageSourcePtr target, + int fd, + struct stat *sb); + +bool virStorageBackendPoolPathIsStable(const char *path); +char *virStorageBackendStablePath(virStoragePoolObjPtr pool, + const char *devpath, + bool loop); + +virCommandPtr +virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags, + const char *create_tool, + int imgformat, + const char *secretPath); + +#endif /* __VIR_STORAGE_UTIL_H__ */ diff --git a/tests/storagevolxml2argvtest.c b/tests/storagevolxml2argvtest.c index e300821f89..bf9dbe5e08 100644 --- a/tests/storagevolxml2argvtest.c +++ b/tests/storagevolxml2argvtest.c @@ -3,7 +3,7 @@ #include "internal.h" #include "testutils.h" #include "datatypes.h" -#include "storage/storage_backend.h" +#include "storage/storage_util.h" #include "testutilsqemu.h" #include "virstring.h" -- GitLab