2008-02-20 15:52:17 +00:00
|
|
|
/*
|
|
|
|
* storage_backend_disk.c: storage backend for disk handling
|
|
|
|
*
|
2014-04-01 16:26:59 -06:00
|
|
|
* Copyright (C) 2007-2014 Red Hat, Inc.
|
2008-02-20 15:52:17 +00:00
|
|
|
* 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
|
|
|
|
* 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
|
2012-09-20 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 18:06:23 +08:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2008-02-20 15:52:17 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
2008-09-16 16:46:08 +00:00
|
|
|
#include <string.h>
|
2008-09-17 14:29:47 +00:00
|
|
|
#include <unistd.h>
|
2008-11-17 11:19:33 +00:00
|
|
|
#include <stdio.h>
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2013-04-25 14:24:42 -06:00
|
|
|
#include "dirname.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2008-02-20 15:52:17 +00:00
|
|
|
#include "storage_backend_disk.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2013-05-09 14:59:04 -04:00
|
|
|
#include "virfile.h"
|
2010-11-16 07:54:17 -07:00
|
|
|
#include "configmake.h"
|
2013-04-03 12:36:23 +02:00
|
|
|
#include "virstring.h"
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2009-01-20 17:13:33 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("storage.storage_backend_disk");
|
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
#define SECTOR_SIZE 512
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendDiskMakeDataVol(virStoragePoolObjPtr pool,
|
2008-02-20 15:52:17 +00:00
|
|
|
char **const groups,
|
|
|
|
virStorageVolDefPtr vol)
|
|
|
|
{
|
2015-01-15 19:12:42 -05:00
|
|
|
char *tmp, *devpath, *partname;
|
|
|
|
|
|
|
|
/* Prepended path will be same for all partitions, so we can
|
|
|
|
* strip the path to form a reasonable pool-unique name
|
|
|
|
*/
|
|
|
|
if ((tmp = strrchr(groups[0], '/')))
|
|
|
|
partname = tmp + 1;
|
|
|
|
else
|
|
|
|
partname = groups[0];
|
2008-02-20 15:52:17 +00:00
|
|
|
|
|
|
|
if (vol == NULL) {
|
2015-01-15 19:12:42 -05:00
|
|
|
/* This is typically a reload/restart/refresh path where
|
|
|
|
* we're discovering the existing partitions for the pool
|
|
|
|
*/
|
2013-07-04 12:16:29 +02:00
|
|
|
if (VIR_ALLOC(vol) < 0)
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
2015-01-15 19:12:42 -05:00
|
|
|
if (VIR_STRDUP(vol->name, partname) < 0 ||
|
2014-03-11 15:26:14 -04:00
|
|
|
VIR_APPEND_ELEMENT_COPY(pool->volumes.objs,
|
|
|
|
pool->volumes.count, vol) < 0) {
|
2014-03-07 09:33:31 +01:00
|
|
|
virStorageVolDefFree(vol);
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
2014-03-07 09:33:31 +01:00
|
|
|
}
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vol->target.path == NULL) {
|
2013-05-03 14:49:08 +02:00
|
|
|
if (VIR_STRDUP(devpath, groups[0]) < 0)
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Now figure out the stable path
|
|
|
|
*
|
|
|
|
* XXX this method is O(N) because it scans the pool target
|
|
|
|
* dir every time its run. Should figure out a more efficient
|
|
|
|
* way of doing this...
|
|
|
|
*/
|
2012-10-21 12:53:20 -04:00
|
|
|
vol->target.path = virStorageBackendStablePath(pool, devpath, true);
|
2008-11-03 11:37:11 +00:00
|
|
|
VIR_FREE(devpath);
|
2009-11-10 12:56:11 +01:00
|
|
|
if (vol->target.path == NULL)
|
|
|
|
return -1;
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 19:12:42 -05:00
|
|
|
/* Enforce provided vol->name is the same as what parted created.
|
|
|
|
* We do this after filling target.path so that we have a chance at
|
|
|
|
* deleting the partition with this failure from CreateVol path
|
|
|
|
*/
|
|
|
|
if (STRNEQ(vol->name, partname)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("invalid partition name '%s', expected '%s'"),
|
|
|
|
vol->name, partname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
if (vol->key == NULL) {
|
|
|
|
/* XXX base off a unique key of the underlying disk */
|
2013-05-03 14:49:08 +02:00
|
|
|
if (VIR_STRDUP(vol->key, vol->target.path) < 0)
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vol->source.extents == NULL) {
|
2013-07-04 12:16:29 +02:00
|
|
|
if (VIR_ALLOC(vol->source.extents) < 0)
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
vol->source.nextent = 1;
|
|
|
|
|
|
|
|
if (virStrToLong_ull(groups[3], NULL, 10,
|
|
|
|
&vol->source.extents[0].start) < 0) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("cannot parse device start location"));
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ull(groups[4], NULL, 10,
|
|
|
|
&vol->source.extents[0].end) < 0) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("cannot parse device end location"));
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-05-03 14:49:08 +02:00
|
|
|
if (VIR_STRDUP(vol->source.extents[0].path,
|
|
|
|
pool->def->source.devices[0].path) < 0)
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
/* set partition type */
|
2010-08-18 14:54:48 -06:00
|
|
|
if (STREQ(groups[1], "normal"))
|
2014-03-29 20:27:44 -06:00
|
|
|
vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_PRIMARY;
|
2010-08-18 14:54:48 -06:00
|
|
|
else if (STREQ(groups[1], "logical"))
|
2014-03-29 20:27:44 -06:00
|
|
|
vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_LOGICAL;
|
2010-08-18 14:54:48 -06:00
|
|
|
else if (STREQ(groups[1], "extended"))
|
2014-03-29 20:27:44 -06:00
|
|
|
vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_EXTENDED;
|
2009-06-26 16:18:59 +00:00
|
|
|
else
|
2014-03-29 20:27:44 -06:00
|
|
|
vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_NONE;
|
2009-06-26 16:18:59 +00:00
|
|
|
|
2008-11-17 11:19:33 +00:00
|
|
|
vol->type = VIR_STORAGE_VOL_BLOCK;
|
|
|
|
|
2015-01-22 09:12:04 -05:00
|
|
|
/* Refresh allocation/capacity/perms
|
|
|
|
*
|
|
|
|
* For an extended partition, virStorageBackendUpdateVolInfo will
|
|
|
|
* return incorrect values for allocation and capacity, so use the
|
|
|
|
* extent information captured above instead.
|
|
|
|
*
|
|
|
|
* Also once a logical partition exists or another primary partition
|
|
|
|
* after an extended partition is created an open on the extended
|
|
|
|
* partition will fail, so pass the NOERROR flag and only error if a
|
|
|
|
* -1 was returned indicating some other error than an open error.
|
|
|
|
*/
|
|
|
|
if (vol->source.partType == VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
|
2015-02-19 13:43:03 +01:00
|
|
|
if (virStorageBackendUpdateVolInfo(vol, false,
|
2015-01-22 09:12:04 -05:00
|
|
|
VIR_STORAGE_VOL_OPEN_DEFAULT |
|
|
|
|
VIR_STORAGE_VOL_OPEN_NOERROR) == -1)
|
|
|
|
return -1;
|
|
|
|
vol->target.allocation = vol->target.capacity =
|
|
|
|
(vol->source.extents[0].end - vol->source.extents[0].start);
|
|
|
|
} else {
|
2015-02-19 13:43:03 +01:00
|
|
|
if (virStorageBackendUpdateVolInfo(vol, false,
|
2015-01-22 09:12:04 -05:00
|
|
|
VIR_STORAGE_VOL_OPEN_DEFAULT) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
2008-02-20 15:52:17 +00:00
|
|
|
|
|
|
|
if (STRNEQ(groups[2], "metadata"))
|
conf: track sizes directly in source struct
One of the features of qcow2 is that a wrapper file can have
more capacity than its backing file from the guest's perspective;
what's more, sparse files make tracking allocation of both
the active and backing file worthwhile. As such, it makes
more sense to show allocation numbers for each file in a chain,
and not just the top-level file. This sets up the fields for
the tracking, although it does not modify XML to display any
new information.
* src/util/virstoragefile.h (_virStorageSource): Add fields.
* src/conf/storage_conf.h (_virStorageVolDef): Drop redundant
fields.
* src/storage/storage_backend.c (virStorageBackendCreateBlockFrom)
(createRawFile, virStorageBackendCreateQemuImgCmd)
(virStorageBackendCreateQcowCreate): Update clients.
* src/storage/storage_driver.c (storageVolDelete)
(storageVolCreateXML, storageVolCreateXMLFrom, storageVolResize)
(storageVolWipeInternal, storageVolGetInfo): Likewise.
* src/storage/storage_backend_fs.c (virStorageBackendProbeTarget)
(virStorageBackendFileSystemRefresh)
(virStorageBackendFileSystemVolResize)
(virStorageBackendFileSystemVolRefresh): Likewise.
* src/storage/storage_backend_logical.c
(virStorageBackendLogicalMakeVol)
(virStorageBackendLogicalCreateVol): Likewise.
* src/storage/storage_backend_scsi.c
(virStorageBackendSCSINewLun): Likewise.
* src/storage/storage_backend_mpath.c
(virStorageBackendMpathNewVol): Likewise.
* src/storage/storage_backend_rbd.c
(volStorageBackendRBDRefreshVolInfo)
(virStorageBackendRBDCreateImage): Likewise.
* src/storage/storage_backend_disk.c
(virStorageBackendDiskMakeDataVol)
(virStorageBackendDiskCreateVol): Likewise.
* src/storage/storage_backend_sheepdog.c
(virStorageBackendSheepdogBuildVol)
(virStorageBackendSheepdogParseVdiList): Likewise.
* src/storage/storage_backend_gluster.c
(virStorageBackendGlusterRefreshVol): Likewise.
* src/conf/storage_conf.c (virStorageVolDefFormat)
(virStorageVolDefParseXML): Likewise.
* src/test/test_driver.c (testOpenVolumesForPool)
(testStorageVolCreateXML, testStorageVolCreateXMLFrom)
(testStorageVolDelete, testStorageVolGetInfo): Likewise.
* src/esx/esx_storage_backend_iscsi.c (esxStorageVolGetXMLDesc):
Likewise.
* src/esx/esx_storage_backend_vmfs.c (esxStorageVolGetXMLDesc)
(esxStorageVolCreateXML): Likewise.
* src/parallels/parallels_driver.c (parallelsAddHddByVolume):
Likewise.
* src/parallels/parallels_storage.c (parallelsDiskDescParseNode)
(parallelsStorageVolDefineXML, parallelsStorageVolCreateXMLFrom)
(parallelsStorageVolDefRemove, parallelsStorageVolGetInfo):
Likewise.
* src/vbox/vbox_tmpl.c (vboxStorageVolCreateXML)
(vboxStorageVolGetXMLDesc): Likewise.
* tests/storagebackendsheepdogtest.c (test_vdi_list_parser):
Likewise.
* src/phyp/phyp_driver.c (phypStorageVolCreateXML): Likewise.
2014-04-01 17:43:36 -06:00
|
|
|
pool->def->allocation += vol->target.allocation;
|
2008-02-20 15:52:17 +00:00
|
|
|
if (vol->source.extents[0].end > pool->def->capacity)
|
|
|
|
pool->def->capacity = vol->source.extents[0].end;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendDiskMakeFreeExtent(virStoragePoolObjPtr pool,
|
2008-02-20 15:52:17 +00:00
|
|
|
char **const groups)
|
|
|
|
{
|
|
|
|
virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0];
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_REALLOC_N(dev->freeExtents,
|
|
|
|
dev->nfreeExtent + 1) < 0)
|
2008-02-20 15:52:17 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
memset(dev->freeExtents +
|
2008-06-06 11:09:57 +00:00
|
|
|
dev->nfreeExtent, 0,
|
|
|
|
sizeof(dev->freeExtents[0]));
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
/* set type of free area */
|
2010-08-18 14:54:48 -06:00
|
|
|
if (STREQ(groups[1], "logical")) {
|
2009-06-26 16:18:59 +00:00
|
|
|
dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_LOGICAL;
|
|
|
|
} else {
|
|
|
|
dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
if (virStrToLong_ull(groups[3], NULL, 10,
|
|
|
|
&dev->freeExtents[dev->nfreeExtent].start) < 0)
|
|
|
|
return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */
|
|
|
|
|
|
|
|
if (virStrToLong_ull(groups[4], NULL, 10,
|
|
|
|
&dev->freeExtents[dev->nfreeExtent].end) < 0)
|
|
|
|
return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */
|
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
/* first block reported as free, even if it is not */
|
2014-11-13 15:25:27 +01:00
|
|
|
if (dev->freeExtents[dev->nfreeExtent].start == 0)
|
2009-06-26 16:18:59 +00:00
|
|
|
dev->freeExtents[dev->nfreeExtent].start = SECTOR_SIZE;
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
pool->def->available +=
|
|
|
|
(dev->freeExtents[dev->nfreeExtent].end -
|
|
|
|
dev->freeExtents[dev->nfreeExtent].start);
|
|
|
|
if (dev->freeExtents[dev->nfreeExtent].end > pool->def->capacity)
|
|
|
|
pool->def->capacity = dev->freeExtents[dev->nfreeExtent].end;
|
|
|
|
|
|
|
|
dev->nfreeExtent++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-18 15:35:01 +01:00
|
|
|
struct virStorageBackendDiskPoolVolData {
|
|
|
|
virStoragePoolObjPtr pool;
|
|
|
|
virStorageVolDefPtr vol;
|
|
|
|
};
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
static int
|
2014-03-18 15:35:01 +01:00
|
|
|
virStorageBackendDiskMakeVol(size_t ntok ATTRIBUTE_UNUSED,
|
2008-02-20 15:52:17 +00:00
|
|
|
char **const groups,
|
2014-03-18 15:35:01 +01:00
|
|
|
void *opaque)
|
2008-02-20 15:52:17 +00:00
|
|
|
{
|
2014-03-18 15:35:01 +01:00
|
|
|
struct virStorageBackendDiskPoolVolData *data = opaque;
|
|
|
|
virStoragePoolObjPtr pool = data->pool;
|
2008-02-20 15:52:17 +00:00
|
|
|
/*
|
|
|
|
* Ignore normal+metadata, and logical+metadata partitions
|
|
|
|
* since they're basically internal book-keeping regions
|
|
|
|
* we have no control over. Do keep extended+metadata though
|
|
|
|
* because that's the MS-DOS extended partition region we
|
|
|
|
* need to be able to view/create/delete
|
|
|
|
*/
|
|
|
|
if ((STREQ(groups[1], "normal") ||
|
|
|
|
STREQ(groups[1], "logical")) &&
|
|
|
|
STREQ(groups[2], "metadata"))
|
|
|
|
return 0;
|
|
|
|
|
2008-02-27 10:37:19 +00:00
|
|
|
/* Remaining data / metadata parts get turn into volumes... */
|
2008-02-20 15:52:17 +00:00
|
|
|
if (STREQ(groups[2], "metadata") ||
|
|
|
|
STREQ(groups[2], "data")) {
|
2014-03-18 15:35:01 +01:00
|
|
|
virStorageVolDefPtr vol = data->vol;
|
2009-07-09 21:51:36 -04:00
|
|
|
|
|
|
|
if (vol) {
|
|
|
|
/* We're searching for a specific vol only */
|
|
|
|
if (vol->key) {
|
|
|
|
if (STRNEQ(vol->key, groups[0]))
|
|
|
|
return 0;
|
|
|
|
} else if (virStorageVolDefFindByKey(pool, groups[0]) != NULL) {
|
|
|
|
/* If no key, the volume must be newly created. If groups[0]
|
|
|
|
* isn't already a volume, assume it's the path we want */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
return virStorageBackendDiskMakeDataVol(pool, groups, vol);
|
2008-02-20 15:52:17 +00:00
|
|
|
} else if (STREQ(groups[2], "free")) {
|
|
|
|
/* ....or free space extents */
|
2010-02-10 11:42:56 +00:00
|
|
|
return virStorageBackendDiskMakeFreeExtent(pool, groups);
|
2008-02-20 15:52:17 +00:00
|
|
|
} else {
|
2008-02-27 10:37:19 +00:00
|
|
|
/* This code path should never happen unless someone changed
|
2008-02-20 15:52:17 +00:00
|
|
|
* libvirt_parthelper forgot to change this code */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* To get a list of partitions we run an external helper
|
|
|
|
* tool which then uses parted APIs. This is because
|
|
|
|
* parted's API is not compatible with libvirt's license
|
|
|
|
* but we really really want to use parted because the
|
|
|
|
* other options all suck :-)
|
|
|
|
*
|
|
|
|
* All the other storage backends run an external tool for
|
|
|
|
* listing volumes so this really isn't too much of a pain,
|
|
|
|
* and we can even ensure the output is friendly.
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendDiskReadPartitions(virStoragePoolObjPtr pool,
|
2008-02-20 15:52:17 +00:00
|
|
|
virStorageVolDefPtr vol)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* # libvirt_parthelper DEVICE
|
|
|
|
* /dev/sda1 normal data 32256 106928128 106896384
|
|
|
|
* /dev/sda2 normal data 106928640 100027629568 99920701440
|
|
|
|
* - normal metadata 100027630080 100030242304 2612736
|
|
|
|
*
|
|
|
|
*/
|
2014-03-25 13:53:14 +05:30
|
|
|
|
|
|
|
char *parthelper_path;
|
|
|
|
virCommandPtr cmd;
|
2014-03-18 15:35:01 +01:00
|
|
|
struct virStorageBackendDiskPoolVolData cbdata = {
|
|
|
|
.pool = pool,
|
|
|
|
.vol = vol,
|
|
|
|
};
|
2012-07-11 15:56:55 +01:00
|
|
|
int ret;
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2014-03-25 13:53:14 +05:30
|
|
|
if (!(parthelper_path = virFileFindResource("libvirt_parthelper",
|
2015-02-13 14:25:27 +01:00
|
|
|
abs_topbuilddir "/src",
|
2014-03-25 13:53:14 +05:30
|
|
|
LIBEXECDIR)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
cmd = virCommandNewArgList(parthelper_path,
|
|
|
|
pool->def->source.devices[0].path,
|
|
|
|
NULL);
|
|
|
|
|
2015-05-21 21:10:56 -04:00
|
|
|
/* If a volume is passed, virStorageBackendDiskMakeVol only updates the
|
|
|
|
* pool allocation for that single volume.
|
|
|
|
*/
|
|
|
|
if (!vol)
|
|
|
|
pool->def->allocation = 0;
|
|
|
|
pool->def->capacity = pool->def->available = 0;
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2014-03-18 15:35:01 +01:00
|
|
|
ret = virCommandRunNul(cmd,
|
|
|
|
6,
|
|
|
|
virStorageBackendDiskMakeVol,
|
|
|
|
&cbdata);
|
2012-07-11 15:56:55 +01:00
|
|
|
virCommandFree(cmd);
|
2014-03-25 13:53:14 +05:30
|
|
|
VIR_FREE(parthelper_path);
|
2012-07-11 15:56:55 +01:00
|
|
|
return ret;
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
static int
|
2014-03-18 15:35:01 +01:00
|
|
|
virStorageBackendDiskMakePoolGeometry(size_t ntok ATTRIBUTE_UNUSED,
|
2010-02-10 11:42:56 +00:00
|
|
|
char **const groups,
|
2014-03-18 15:35:01 +01:00
|
|
|
void *data)
|
2009-06-26 16:18:59 +00:00
|
|
|
{
|
2014-03-18 15:35:01 +01:00
|
|
|
virStoragePoolObjPtr pool = data;
|
2013-11-14 17:14:26 +01:00
|
|
|
virStoragePoolSourceDevicePtr device = &(pool->def->source.devices[0]);
|
|
|
|
if (virStrToLong_i(groups[0], NULL, 0, &device->geometry.cylinders) < 0 ||
|
|
|
|
virStrToLong_i(groups[1], NULL, 0, &device->geometry.heads) < 0 ||
|
|
|
|
virStrToLong_i(groups[2], NULL, 0, &device->geometry.sectors) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Failed to create disk pool geometry"));
|
|
|
|
return -1;
|
|
|
|
}
|
2009-06-26 16:18:59 +00:00
|
|
|
|
2013-11-14 17:14:26 +01:00
|
|
|
return 0;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendDiskReadGeometry(virStoragePoolObjPtr pool)
|
2009-06-26 16:18:59 +00:00
|
|
|
{
|
2014-03-25 13:53:14 +05:30
|
|
|
char *parthelper_path;
|
|
|
|
virCommandPtr cmd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(parthelper_path = virFileFindResource("libvirt_parthelper",
|
2015-02-13 14:25:27 +01:00
|
|
|
abs_topbuilddir "/src",
|
2014-03-25 13:53:14 +05:30
|
|
|
LIBEXECDIR)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
cmd = virCommandNewArgList(parthelper_path,
|
2012-07-11 15:56:55 +01:00
|
|
|
pool->def->source.devices[0].path,
|
|
|
|
"-g",
|
|
|
|
NULL);
|
|
|
|
|
2014-03-18 15:35:01 +01:00
|
|
|
ret = virCommandRunNul(cmd,
|
|
|
|
3,
|
|
|
|
virStorageBackendDiskMakePoolGeometry,
|
|
|
|
pool);
|
2012-07-11 15:56:55 +01:00
|
|
|
virCommandFree(cmd);
|
2014-03-25 13:53:14 +05:30
|
|
|
VIR_FREE(parthelper_path);
|
2012-07-11 15:56:55 +01:00
|
|
|
return ret;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
2008-02-20 15:52:17 +00:00
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendDiskRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
2008-02-20 15:52:17 +00:00
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(pool->def->source.devices[0].freeExtents);
|
2008-02-20 15:52:17 +00:00
|
|
|
pool->def->source.devices[0].nfreeExtent = 0;
|
|
|
|
|
2010-02-04 23:41:52 +01:00
|
|
|
virFileWaitForDevices();
|
2008-11-28 07:50:20 +00:00
|
|
|
|
2011-09-19 15:35:15 +08:00
|
|
|
if (!virFileExists(pool->def->source.devices[0].path)) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("device path '%s' doesn't exist"),
|
|
|
|
pool->def->source.devices[0].path);
|
2011-09-19 15:35:15 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-13 15:25:27 +01:00
|
|
|
if (virStorageBackendDiskReadGeometry(pool) != 0)
|
2009-06-26 16:18:59 +00:00
|
|
|
return -1;
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
return virStorageBackendDiskReadPartitions(pool, NULL);
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-12 13:31:52 +01:00
|
|
|
/**
|
|
|
|
* Check for a valid disk label (partition table) on device
|
|
|
|
*
|
|
|
|
* return: 0 - valid disk label found
|
|
|
|
* >0 - no or unrecognized disk label
|
|
|
|
* <0 - error finding the disk label
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virStorageBackendDiskFindLabel(const char* device)
|
|
|
|
{
|
|
|
|
const char *const args[] = {
|
|
|
|
device, "print", "--script", NULL,
|
|
|
|
};
|
|
|
|
virCommandPtr cmd = virCommandNew(PARTED);
|
|
|
|
char *output = NULL;
|
2014-12-10 15:14:26 +08:00
|
|
|
char *error = NULL;
|
2011-11-12 13:31:52 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
virCommandAddArgSet(cmd, args);
|
|
|
|
virCommandAddEnvString(cmd, "LC_ALL=C");
|
|
|
|
virCommandSetOutputBuffer(cmd, &output);
|
2014-12-10 15:14:26 +08:00
|
|
|
virCommandSetErrorBuffer(cmd, &error);
|
2011-11-12 13:31:52 +01:00
|
|
|
|
|
|
|
/* if parted succeeds we have a valid partition table */
|
|
|
|
ret = virCommandRun(cmd, NULL);
|
|
|
|
if (ret < 0) {
|
2015-05-13 06:24:50 -04:00
|
|
|
if ((output && strstr(output, "unrecognised disk label")) ||
|
|
|
|
(error && strstr(error, "unrecognised disk label"))) {
|
2011-11-12 13:31:52 +01:00
|
|
|
ret = 1;
|
2014-12-10 15:14:26 +08:00
|
|
|
}
|
2011-11-12 13:31:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
virCommandFree(cmd);
|
|
|
|
VIR_FREE(output);
|
2014-12-10 15:14:26 +08:00
|
|
|
VIR_FREE(error);
|
2011-11-12 13:31:52 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-01 08:07:35 -04:00
|
|
|
/**
|
|
|
|
* Determine whether the label on the disk is valid or in a known format
|
|
|
|
* for the purpose of rewriting the label during build
|
|
|
|
*
|
|
|
|
* Return: True if it's OK
|
|
|
|
* False if something's wrong
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
virStorageBackendDiskValidLabel(const char *device)
|
|
|
|
{
|
|
|
|
bool valid = false;
|
|
|
|
int check;
|
|
|
|
|
|
|
|
check = virStorageBackendDiskFindLabel(device);
|
|
|
|
if (check > 0) {
|
|
|
|
valid = true;
|
|
|
|
} else if (check < 0) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("Error checking for disk label"));
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("Disk label already present"));
|
|
|
|
}
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
/**
|
|
|
|
* Write a new partition table header
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-04 23:41:52 +01:00
|
|
|
virStorageBackendDiskBuildPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
2008-02-20 15:52:17 +00:00
|
|
|
virStoragePoolObjPtr pool,
|
2011-07-06 16:51:23 -06:00
|
|
|
unsigned int flags)
|
2008-02-20 15:52:17 +00:00
|
|
|
{
|
2011-11-12 13:31:52 +01:00
|
|
|
bool ok_to_mklabel = false;
|
|
|
|
int ret = -1;
|
2013-01-30 07:02:15 -05:00
|
|
|
virCommandPtr cmd = NULL;
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2011-11-12 13:31:52 +01:00
|
|
|
virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
|
|
|
|
VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, ret);
|
2011-07-06 16:51:23 -06:00
|
|
|
|
2015-04-28 19:14:07 +02:00
|
|
|
VIR_EXCLUSIVE_FLAGS_GOTO(VIR_STORAGE_POOL_BUILD_OVERWRITE,
|
|
|
|
VIR_STORAGE_POOL_BUILD_NO_OVERWRITE,
|
|
|
|
error);
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2015-10-01 08:07:35 -04:00
|
|
|
if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE)
|
2011-11-12 13:31:52 +01:00
|
|
|
ok_to_mklabel = true;
|
2015-10-01 08:07:35 -04:00
|
|
|
else
|
|
|
|
ok_to_mklabel = virStorageBackendDiskValidLabel(
|
|
|
|
pool->def->source.devices[0].path);
|
2011-11-12 13:31:52 +01:00
|
|
|
|
2013-01-30 07:02:15 -05:00
|
|
|
if (ok_to_mklabel) {
|
2015-02-26 19:39:36 -05:00
|
|
|
/* eg parted /dev/sda mklabel --script msdos */
|
|
|
|
int format = pool->def->source.format;
|
|
|
|
const char *fmt;
|
2015-06-08 08:16:58 -04:00
|
|
|
if (format == VIR_STORAGE_POOL_DISK_UNKNOWN)
|
|
|
|
format = pool->def->source.format = VIR_STORAGE_POOL_DISK_DOS;
|
|
|
|
if (format == VIR_STORAGE_POOL_DISK_DOS)
|
2015-02-26 19:39:36 -05:00
|
|
|
fmt = "msdos";
|
|
|
|
else
|
|
|
|
fmt = virStoragePoolFormatDiskTypeToString(format);
|
|
|
|
|
2013-01-30 07:02:15 -05:00
|
|
|
cmd = virCommandNewArgList(PARTED,
|
|
|
|
pool->def->source.devices[0].path,
|
|
|
|
"mklabel",
|
|
|
|
"--script",
|
2015-02-26 19:39:36 -05:00
|
|
|
fmt,
|
2013-01-30 07:02:15 -05:00
|
|
|
NULL);
|
2012-07-11 15:56:55 +01:00
|
|
|
ret = virCommandRun(cmd, NULL);
|
2013-01-30 07:02:15 -05:00
|
|
|
}
|
2011-11-12 13:31:52 +01:00
|
|
|
|
2014-03-25 07:52:40 +01:00
|
|
|
error:
|
2012-07-11 15:56:55 +01:00
|
|
|
virCommandFree(cmd);
|
2011-11-12 13:31:52 +01:00
|
|
|
return ret;
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
/**
|
|
|
|
* Decides what kind of partition type that should be created.
|
|
|
|
* Important when the partition table is of msdos type
|
|
|
|
*/
|
2008-02-20 15:52:17 +00:00
|
|
|
static int
|
2009-06-26 16:18:59 +00:00
|
|
|
virStorageBackendDiskPartTypeToCreate(virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
|
2014-04-01 16:26:59 -06:00
|
|
|
/* count primary and extended partitions,
|
2009-06-26 16:18:59 +00:00
|
|
|
can't be more than 3 to create a new primary partition */
|
Convert 'int i' to 'size_t i' in src/storage/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2009-06-26 16:18:59 +00:00
|
|
|
int count = 0;
|
|
|
|
for (i = 0; i < pool->volumes.count; i++) {
|
2014-03-29 20:27:44 -06:00
|
|
|
int partType = pool->volumes.objs[i]->source.partType;
|
|
|
|
if (partType == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY ||
|
|
|
|
partType == VIR_STORAGE_VOL_DISK_TYPE_EXTENDED)
|
|
|
|
count++;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
2014-11-13 15:25:27 +01:00
|
|
|
if (count >= 4)
|
2009-06-26 16:18:59 +00:00
|
|
|
return VIR_STORAGE_VOL_DISK_TYPE_LOGICAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for all other cases, all partitions are primary */
|
|
|
|
return VIR_STORAGE_VOL_DISK_TYPE_PRIMARY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendDiskPartFormat(virStoragePoolObjPtr pool,
|
2009-06-26 16:18:59 +00:00
|
|
|
virStorageVolDefPtr vol,
|
2010-08-18 15:54:11 -06:00
|
|
|
char** partFormat)
|
2009-06-26 16:18:59 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/storage/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2009-06-26 16:18:59 +00:00
|
|
|
if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
|
2010-08-18 14:54:48 -06:00
|
|
|
const char *partedFormat;
|
2014-05-14 13:48:15 -06:00
|
|
|
partedFormat = virStoragePartedFsTypeToString(vol->target.format);
|
2010-08-18 14:54:48 -06:00
|
|
|
if (partedFormat == NULL) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("Invalid partition type"));
|
2010-08-18 14:54:48 -06:00
|
|
|
return -1;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
|
|
|
if (vol->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) {
|
|
|
|
/* make sure we don't have a extended partition already */
|
|
|
|
for (i = 0; i < pool->volumes.count; i++) {
|
2015-01-22 08:30:36 -05:00
|
|
|
if (pool->volumes.objs[i]->source.partType ==
|
|
|
|
VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("extended partition already exists"));
|
2010-08-18 14:54:48 -06:00
|
|
|
return -1;
|
|
|
|
}
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
2013-05-03 14:49:08 +02:00
|
|
|
if (VIR_STRDUP(*partFormat, partedFormat) < 0)
|
2010-08-18 15:54:11 -06:00
|
|
|
return -1;
|
2009-06-26 16:18:59 +00:00
|
|
|
} else {
|
|
|
|
/* create primary partition as long as it is possible
|
|
|
|
and after that check if an extended partition exists
|
|
|
|
to create logical partitions. */
|
|
|
|
/* XXX Only support one extended partition */
|
|
|
|
switch (virStorageBackendDiskPartTypeToCreate(pool)) {
|
2010-08-18 14:54:48 -06:00
|
|
|
case VIR_STORAGE_VOL_DISK_TYPE_PRIMARY:
|
2013-07-04 12:16:29 +02:00
|
|
|
if (virAsprintf(partFormat, "primary %s", partedFormat) < 0)
|
2010-08-18 15:54:11 -06:00
|
|
|
return -1;
|
2010-08-18 14:54:48 -06:00
|
|
|
break;
|
|
|
|
case VIR_STORAGE_VOL_DISK_TYPE_LOGICAL:
|
|
|
|
/* make sure we have a extended partition */
|
|
|
|
for (i = 0; i < pool->volumes.count; i++) {
|
2015-01-22 08:30:36 -05:00
|
|
|
if (pool->volumes.objs[i]->source.partType ==
|
|
|
|
VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
|
2010-08-18 15:54:11 -06:00
|
|
|
if (virAsprintf(partFormat, "logical %s",
|
2013-07-04 12:16:29 +02:00
|
|
|
partedFormat) < 0)
|
2010-08-18 15:54:11 -06:00
|
|
|
return -1;
|
2010-08-18 14:54:48 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == pool->volumes.count) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("no extended partition found and no primary partition available"));
|
2010-08-18 14:54:48 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("unknown partition type"));
|
2010-08-18 15:54:11 -06:00
|
|
|
return -1;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-05-03 14:49:08 +02:00
|
|
|
if (VIR_STRDUP(*partFormat, "primary") < 0)
|
2010-08-18 15:54:11 -06:00
|
|
|
return -1;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-10-11 18:31:20 +02:00
|
|
|
* Aligns a new partition to nearest cylinder boundary
|
2010-08-18 14:54:48 -06:00
|
|
|
* when having a msdos partition table type
|
2012-10-11 18:31:20 +02:00
|
|
|
* to avoid any problem with already existing
|
2009-06-26 16:18:59 +00:00
|
|
|
* partitions
|
|
|
|
*/
|
|
|
|
static int
|
2014-04-01 16:26:59 -06:00
|
|
|
virStorageBackendDiskPartBoundaries(virStoragePoolObjPtr pool,
|
|
|
|
unsigned long long *start,
|
|
|
|
unsigned long long *end,
|
|
|
|
unsigned long long allocation)
|
2008-02-20 15:52:17 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/storage/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2008-02-20 15:52:17 +00:00
|
|
|
int smallestExtent = -1;
|
2009-06-26 16:18:59 +00:00
|
|
|
unsigned long long smallestSize = 0;
|
|
|
|
unsigned long long extraBytes = 0;
|
|
|
|
unsigned long long alignedAllocation = allocation;
|
2008-02-20 15:52:17 +00:00
|
|
|
virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0];
|
2014-09-04 10:34:43 -04:00
|
|
|
unsigned long long cylinderSize = (unsigned long long)dev->geometry.heads *
|
2009-06-26 16:18:59 +00:00
|
|
|
dev->geometry.sectors * SECTOR_SIZE;
|
|
|
|
|
2011-02-16 16:37:57 -07:00
|
|
|
VIR_DEBUG("find free area: allocation %llu, cyl size %llu", allocation,
|
2010-08-18 14:54:48 -06:00
|
|
|
cylinderSize);
|
2009-06-26 16:18:59 +00:00
|
|
|
int partType = virStorageBackendDiskPartTypeToCreate(pool);
|
|
|
|
|
|
|
|
/* how many extra bytes we have since we allocate
|
2012-10-11 18:31:20 +02:00
|
|
|
aligned to the cylinder boundary */
|
2009-06-26 16:18:59 +00:00
|
|
|
extraBytes = cylinderSize - (allocation % cylinderSize);
|
|
|
|
|
2013-05-21 15:21:21 +08:00
|
|
|
for (i = 0; i < dev->nfreeExtent; i++) {
|
2009-06-26 16:18:59 +00:00
|
|
|
unsigned long long size =
|
|
|
|
dev->freeExtents[i].end -
|
|
|
|
dev->freeExtents[i].start;
|
|
|
|
unsigned long long neededSize = allocation;
|
|
|
|
|
|
|
|
if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
|
2012-10-11 18:31:20 +02:00
|
|
|
/* align to cylinder boundary */
|
2009-06-26 16:18:59 +00:00
|
|
|
neededSize += extraBytes;
|
|
|
|
if ((*start % cylinderSize) > extraBytes) {
|
|
|
|
/* add an extra cylinder if the offset can't fit within
|
|
|
|
the extra bytes we have */
|
|
|
|
neededSize += cylinderSize;
|
|
|
|
}
|
2014-04-01 16:26:59 -06:00
|
|
|
/* if we are creating a logical partition, we need one extra
|
2009-06-26 16:18:59 +00:00
|
|
|
block between partitions (or actually move start one block) */
|
2014-11-13 15:25:27 +01:00
|
|
|
if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL)
|
2009-06-26 16:18:59 +00:00
|
|
|
size -= SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
if (size > neededSize &&
|
|
|
|
(smallestSize == 0 ||
|
|
|
|
size < smallestSize)) {
|
|
|
|
/* for logical partition, the free extent
|
|
|
|
must be within a logical free area */
|
|
|
|
if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL &&
|
|
|
|
dev->freeExtents[i].type != VIR_STORAGE_FREE_LOGICAL) {
|
|
|
|
continue;
|
|
|
|
/* for primary partition, the free extent
|
|
|
|
must not be within a logical free area */
|
2010-08-18 14:54:48 -06:00
|
|
|
} else if (partType == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY &&
|
|
|
|
dev->freeExtents[i].type != VIR_STORAGE_FREE_NORMAL) {
|
|
|
|
continue;
|
2009-06-26 16:18:59 +00:00
|
|
|
}
|
|
|
|
smallestSize = size;
|
|
|
|
smallestExtent = i;
|
|
|
|
alignedAllocation = neededSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (smallestExtent == -1) {
|
2012-07-18 12:38:29 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("no large enough free extent"));
|
2009-06-26 16:18:59 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-02-16 16:37:57 -07:00
|
|
|
VIR_DEBUG("aligned alloc %llu", alignedAllocation);
|
2009-06-26 16:18:59 +00:00
|
|
|
*start = dev->freeExtents[smallestExtent].start;
|
|
|
|
|
|
|
|
if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) {
|
|
|
|
/* for logical partition, skip one block */
|
|
|
|
*start += SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*end = *start + alignedAllocation;
|
|
|
|
if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
|
2012-10-11 18:31:20 +02:00
|
|
|
/* adjust our allocation if start is not at a cylinder boundary */
|
2009-06-26 16:18:59 +00:00
|
|
|
*end -= (*start % cylinderSize);
|
|
|
|
}
|
|
|
|
|
2014-03-29 20:27:44 -06:00
|
|
|
/* counting in bytes, we want the last byte of the current sector */
|
2009-06-26 16:18:59 +00:00
|
|
|
*end -= 1;
|
2011-02-16 16:37:57 -07:00
|
|
|
VIR_DEBUG("final aligned start %llu, end %llu", *start, *end);
|
2009-06-26 16:18:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-21 14:41:02 -05:00
|
|
|
static int
|
2015-01-22 11:23:10 -05:00
|
|
|
virStorageBackendDiskDeleteVol(virConnectPtr conn,
|
2015-01-21 14:41:02 -05:00
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStorageVolDefPtr vol,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
char *part_num = NULL;
|
|
|
|
char *devpath = NULL;
|
|
|
|
char *dev_name, *srcname;
|
|
|
|
virCommandPtr cmd = NULL;
|
|
|
|
bool isDevMapperDevice;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
|
2015-01-21 15:38:05 -05:00
|
|
|
if (!vol->target.path) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("volume target path empty for source path '%s'"),
|
|
|
|
pool->def->source.devices[0].path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-21 14:41:02 -05:00
|
|
|
if (virFileResolveLink(vol->target.path, &devpath) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Couldn't read volume target path '%s'"),
|
|
|
|
vol->target.path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_name = last_component(devpath);
|
|
|
|
srcname = last_component(pool->def->source.devices[0].path);
|
|
|
|
VIR_DEBUG("dev_name=%s, srcname=%s", dev_name, srcname);
|
|
|
|
|
|
|
|
isDevMapperDevice = virIsDevMapperDevice(devpath);
|
|
|
|
|
|
|
|
if (!isDevMapperDevice && !STRPREFIX(dev_name, srcname)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Volume path '%s' did not start with parent "
|
|
|
|
"pool source device name."), dev_name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isDevMapperDevice) {
|
|
|
|
part_num = dev_name + strlen(srcname);
|
|
|
|
|
|
|
|
if (*part_num == 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse partition number from target "
|
|
|
|
"'%s'"), dev_name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* eg parted /dev/sda rm 2 */
|
|
|
|
cmd = virCommandNewArgList(PARTED,
|
|
|
|
pool->def->source.devices[0].path,
|
|
|
|
"rm",
|
|
|
|
"--script",
|
|
|
|
part_num,
|
|
|
|
NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
cmd = virCommandNewArgList(DMSETUP, "remove", "--force", devpath, NULL);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-03-27 11:04:22 -04:00
|
|
|
/* If this is not a logical partition, then either we've removed an
|
|
|
|
* extended partition or a primary partion - refresh the pool which
|
|
|
|
* includes resetting the [n]freeExtents data so a subsequent allocation
|
|
|
|
* might be able to use what was deleted. A logical partition is part
|
|
|
|
* of an extended partition and handled differently
|
2015-01-22 11:23:10 -05:00
|
|
|
*/
|
2015-03-27 11:04:22 -04:00
|
|
|
if (vol->source.partType != VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) {
|
2015-01-22 11:23:10 -05:00
|
|
|
virStoragePoolObjClearVols(pool);
|
|
|
|
if (virStorageBackendDiskRefreshPool(conn, pool) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-01-21 14:41:02 -05:00
|
|
|
rc = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(devpath);
|
|
|
|
virCommandFree(cmd);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
static int
|
2015-01-21 15:38:05 -05:00
|
|
|
virStorageBackendDiskCreateVol(virConnectPtr conn,
|
2014-02-12 14:54:05 +01:00
|
|
|
virStoragePoolObjPtr pool,
|
2009-06-26 16:18:59 +00:00
|
|
|
virStorageVolDefPtr vol)
|
|
|
|
{
|
2010-08-18 15:54:11 -06:00
|
|
|
int res = -1;
|
2013-01-30 07:02:15 -05:00
|
|
|
char *partFormat = NULL;
|
2009-06-26 16:18:59 +00:00
|
|
|
unsigned long long startOffset = 0, endOffset = 0;
|
2014-02-12 14:54:05 +01:00
|
|
|
virCommandPtr cmd = virCommandNewArgList(PARTED,
|
|
|
|
pool->def->source.devices[0].path,
|
|
|
|
"mkpart",
|
|
|
|
"--script",
|
|
|
|
NULL);
|
2009-07-21 04:40:50 +02:00
|
|
|
|
2014-02-12 14:54:05 +01:00
|
|
|
if (vol->target.encryption != NULL) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
"%s", _("storage pool does not support encrypted "
|
|
|
|
"volumes"));
|
2013-01-30 07:02:15 -05:00
|
|
|
goto cleanup;
|
2014-02-12 14:54:05 +01:00
|
|
|
}
|
2013-12-11 17:04:24 +01:00
|
|
|
|
2014-11-13 15:25:27 +01:00
|
|
|
if (virStorageBackendDiskPartFormat(pool, vol, &partFormat) != 0)
|
2014-02-12 14:54:05 +01:00
|
|
|
goto cleanup;
|
2012-07-11 15:56:55 +01:00
|
|
|
virCommandAddArg(cmd, partFormat);
|
2009-06-26 16:18:59 +00:00
|
|
|
|
2014-04-01 16:26:59 -06:00
|
|
|
if (virStorageBackendDiskPartBoundaries(pool, &startOffset,
|
|
|
|
&endOffset,
|
conf: track sizes directly in source struct
One of the features of qcow2 is that a wrapper file can have
more capacity than its backing file from the guest's perspective;
what's more, sparse files make tracking allocation of both
the active and backing file worthwhile. As such, it makes
more sense to show allocation numbers for each file in a chain,
and not just the top-level file. This sets up the fields for
the tracking, although it does not modify XML to display any
new information.
* src/util/virstoragefile.h (_virStorageSource): Add fields.
* src/conf/storage_conf.h (_virStorageVolDef): Drop redundant
fields.
* src/storage/storage_backend.c (virStorageBackendCreateBlockFrom)
(createRawFile, virStorageBackendCreateQemuImgCmd)
(virStorageBackendCreateQcowCreate): Update clients.
* src/storage/storage_driver.c (storageVolDelete)
(storageVolCreateXML, storageVolCreateXMLFrom, storageVolResize)
(storageVolWipeInternal, storageVolGetInfo): Likewise.
* src/storage/storage_backend_fs.c (virStorageBackendProbeTarget)
(virStorageBackendFileSystemRefresh)
(virStorageBackendFileSystemVolResize)
(virStorageBackendFileSystemVolRefresh): Likewise.
* src/storage/storage_backend_logical.c
(virStorageBackendLogicalMakeVol)
(virStorageBackendLogicalCreateVol): Likewise.
* src/storage/storage_backend_scsi.c
(virStorageBackendSCSINewLun): Likewise.
* src/storage/storage_backend_mpath.c
(virStorageBackendMpathNewVol): Likewise.
* src/storage/storage_backend_rbd.c
(volStorageBackendRBDRefreshVolInfo)
(virStorageBackendRBDCreateImage): Likewise.
* src/storage/storage_backend_disk.c
(virStorageBackendDiskMakeDataVol)
(virStorageBackendDiskCreateVol): Likewise.
* src/storage/storage_backend_sheepdog.c
(virStorageBackendSheepdogBuildVol)
(virStorageBackendSheepdogParseVdiList): Likewise.
* src/storage/storage_backend_gluster.c
(virStorageBackendGlusterRefreshVol): Likewise.
* src/conf/storage_conf.c (virStorageVolDefFormat)
(virStorageVolDefParseXML): Likewise.
* src/test/test_driver.c (testOpenVolumesForPool)
(testStorageVolCreateXML, testStorageVolCreateXMLFrom)
(testStorageVolDelete, testStorageVolGetInfo): Likewise.
* src/esx/esx_storage_backend_iscsi.c (esxStorageVolGetXMLDesc):
Likewise.
* src/esx/esx_storage_backend_vmfs.c (esxStorageVolGetXMLDesc)
(esxStorageVolCreateXML): Likewise.
* src/parallels/parallels_driver.c (parallelsAddHddByVolume):
Likewise.
* src/parallels/parallels_storage.c (parallelsDiskDescParseNode)
(parallelsStorageVolDefineXML, parallelsStorageVolCreateXMLFrom)
(parallelsStorageVolDefRemove, parallelsStorageVolGetInfo):
Likewise.
* src/vbox/vbox_tmpl.c (vboxStorageVolCreateXML)
(vboxStorageVolGetXMLDesc): Likewise.
* tests/storagebackendsheepdogtest.c (test_vdi_list_parser):
Likewise.
* src/phyp/phyp_driver.c (phypStorageVolCreateXML): Likewise.
2014-04-01 17:43:36 -06:00
|
|
|
vol->target.capacity) != 0) {
|
2010-08-18 15:54:11 -06:00
|
|
|
goto cleanup;
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
2012-07-11 15:56:55 +01:00
|
|
|
virCommandAddArgFormat(cmd, "%lluB", startOffset);
|
|
|
|
virCommandAddArgFormat(cmd, "%lluB", endOffset);
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2012-07-11 15:56:55 +01:00
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
2010-08-18 15:54:11 -06:00
|
|
|
goto cleanup;
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2009-06-26 16:18:59 +00:00
|
|
|
/* wait for device node to show up */
|
2010-02-04 23:41:52 +01:00
|
|
|
virFileWaitForDevices();
|
2009-06-26 16:18:59 +00:00
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
/* Blow away free extent info, as we're about to re-populate it */
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(pool->def->source.devices[0].freeExtents);
|
2008-02-20 15:52:17 +00:00
|
|
|
pool->def->source.devices[0].nfreeExtent = 0;
|
|
|
|
|
2009-07-09 21:51:36 -04:00
|
|
|
/* Specifying a target path is meaningless */
|
|
|
|
VIR_FREE(vol->target.path);
|
|
|
|
|
|
|
|
/* Fetch actual extent info, generate key */
|
2015-01-21 15:38:05 -05:00
|
|
|
if (virStorageBackendDiskReadPartitions(pool, vol) < 0) {
|
|
|
|
/* Best effort to remove the partition. Ignore any errors
|
|
|
|
* since we could be calling this with vol->target.path == NULL
|
|
|
|
*/
|
|
|
|
virErrorPtr save_err = virSaveLastError();
|
|
|
|
ignore_value(virStorageBackendDiskDeleteVol(conn, pool, vol, 0));
|
|
|
|
virSetError(save_err);
|
|
|
|
virFreeError(save_err);
|
2010-08-18 15:54:11 -06:00
|
|
|
goto cleanup;
|
2015-01-21 15:38:05 -05:00
|
|
|
}
|
2008-02-20 15:52:17 +00:00
|
|
|
|
2010-08-18 15:54:11 -06:00
|
|
|
res = 0;
|
|
|
|
|
2014-03-25 07:52:40 +01:00
|
|
|
cleanup:
|
2010-08-18 15:54:11 -06:00
|
|
|
VIR_FREE(partFormat);
|
2012-07-11 15:56:55 +01:00
|
|
|
virCommandFree(cmd);
|
2010-08-18 15:54:11 -06:00
|
|
|
return res;
|
2008-02-20 15:52:17 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 19:17:31 -04:00
|
|
|
static int
|
|
|
|
virStorageBackendDiskBuildVolFrom(virConnectPtr conn,
|
2010-01-21 00:41:52 +01:00
|
|
|
virStoragePoolObjPtr pool,
|
2009-07-09 19:17:31 -04:00
|
|
|
virStorageVolDefPtr vol,
|
|
|
|
virStorageVolDefPtr inputvol,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
virStorageBackendBuildVolFrom build_func;
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
build_func = virStorageBackendGetBuildVolFromFunction(vol, inputvol);
|
2009-07-09 19:17:31 -04:00
|
|
|
if (!build_func)
|
|
|
|
return -1;
|
|
|
|
|
2010-01-21 00:41:52 +01:00
|
|
|
return build_func(conn, pool, vol, inputvol, flags);
|
2009-07-09 19:17:31 -04:00
|
|
|
}
|
2008-02-20 15:52:17 +00:00
|
|
|
|
|
|
|
|
2015-06-09 18:15:39 -04:00
|
|
|
static int
|
|
|
|
virStorageBackendDiskVolWipe(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStorageVolDefPtr vol,
|
|
|
|
unsigned int algorithm,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
if (vol->source.partType != VIR_STORAGE_VOL_DISK_TYPE_EXTENDED)
|
|
|
|
return virStorageBackendVolWipeLocal(conn, pool, vol, algorithm, flags);
|
|
|
|
|
|
|
|
/* Wiping an extended partition is not support */
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT,
|
|
|
|
_("cannot wipe extended partition '%s'"),
|
|
|
|
vol->target.path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 15:52:17 +00:00
|
|
|
virStorageBackend virStorageBackendDisk = {
|
|
|
|
.type = VIR_STORAGE_POOL_DISK,
|
|
|
|
|
|
|
|
.buildPool = virStorageBackendDiskBuildPool,
|
|
|
|
.refreshPool = virStorageBackendDiskRefreshPool,
|
|
|
|
|
|
|
|
.createVol = virStorageBackendDiskCreateVol,
|
|
|
|
.deleteVol = virStorageBackendDiskDeleteVol,
|
2009-07-09 19:17:31 -04:00
|
|
|
.buildVolFrom = virStorageBackendDiskBuildVolFrom,
|
2014-07-07 16:50:11 +02:00
|
|
|
.uploadVol = virStorageBackendVolUploadLocal,
|
|
|
|
.downloadVol = virStorageBackendVolDownloadLocal,
|
2015-06-09 18:15:39 -04:00
|
|
|
.wipeVol = virStorageBackendDiskVolWipe,
|
2008-02-20 15:52:17 +00:00
|
|
|
};
|