storage_backend_disk.c 33.5 KB
Newer Older
1 2 3
/*
 * storage_backend_disk.c: storage backend for disk handling
 *
4
 * Copyright (C) 2007-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * 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
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>
C
Cole Robinson 已提交
25
#include <string.h>
26
#include <unistd.h>
27
#include <stdio.h>
28

29
#include "dirname.h"
30
#include "virerror.h"
31
#include "virlog.h"
32
#include "storage_backend_disk.h"
33
#include "viralloc.h"
34
#include "vircommand.h"
35
#include "virfile.h"
36
#include "configmake.h"
37
#include "virstring.h"
38

39 40
#define VIR_FROM_THIS VIR_FROM_STORAGE

41 42
VIR_LOG_INIT("storage.storage_backend_disk");

43 44
#define SECTOR_SIZE 512

45
static int
46
virStorageBackendDiskMakeDataVol(virStoragePoolObjPtr pool,
47 48 49
                                 char **const groups,
                                 virStorageVolDefPtr vol)
{
50 51 52 53 54 55 56 57 58
    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];
59 60

    if (vol == NULL) {
61 62 63
        /* This is typically a reload/restart/refresh path where
         * we're discovering the existing partitions for the pool
         */
64
        if (VIR_ALLOC(vol) < 0)
65
            return -1;
66
        if (VIR_STRDUP(vol->name, partname) < 0 ||
67 68
            VIR_APPEND_ELEMENT_COPY(pool->volumes.objs,
                                    pool->volumes.count, vol) < 0) {
69
            virStorageVolDefFree(vol);
70
            return -1;
71
        }
72 73 74
    }

    if (vol->target.path == NULL) {
75
        if (VIR_STRDUP(devpath, groups[0]) < 0)
76 77 78 79 80 81 82 83
            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...
         */
84
        vol->target.path = virStorageBackendStablePath(pool, devpath, true);
85
        VIR_FREE(devpath);
86 87
        if (vol->target.path == NULL)
            return -1;
88 89
    }

90 91 92 93 94 95 96 97 98 99 100
    /* 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;
    }

101 102
    if (vol->key == NULL) {
        /* XXX base off a unique key of the underlying disk */
103
        if (VIR_STRDUP(vol->key, vol->target.path) < 0)
104 105 106 107
            return -1;
    }

    if (vol->source.extents == NULL) {
108
        if (VIR_ALLOC(vol->source.extents) < 0)
109 110 111 112 113
            return -1;
        vol->source.nextent = 1;

        if (virStrToLong_ull(groups[3], NULL, 10,
                             &vol->source.extents[0].start) < 0) {
114 115
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("cannot parse device start location"));
116 117 118 119 120
            return -1;
        }

        if (virStrToLong_ull(groups[4], NULL, 10,
                             &vol->source.extents[0].end) < 0) {
121 122
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("cannot parse device end location"));
123 124 125
            return -1;
        }

126 127
        if (VIR_STRDUP(vol->source.extents[0].path,
                       pool->def->source.devices[0].path) < 0)
128 129 130
            return -1;
    }

131
    /* set partition type */
E
Eric Blake 已提交
132
    if (STREQ(groups[1], "normal"))
133
       vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_PRIMARY;
E
Eric Blake 已提交
134
    else if (STREQ(groups[1], "logical"))
135
       vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_LOGICAL;
E
Eric Blake 已提交
136
    else if (STREQ(groups[1], "extended"))
137
       vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_EXTENDED;
138
    else
139
       vol->source.partType = VIR_STORAGE_VOL_DISK_TYPE_NONE;
140

141 142
    vol->type = VIR_STORAGE_VOL_BLOCK;

143 144 145 146 147 148 149 150 151 152 153 154
    /* 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) {
155
        if (virStorageBackendUpdateVolInfo(vol, false,
156
                                           VIR_STORAGE_VOL_OPEN_DEFAULT |
157 158
                                           VIR_STORAGE_VOL_OPEN_NOERROR,
                                           0) == -1)
159
            return -1;
160 161
        vol->target.allocation = 0;
        vol->target.capacity =
162 163
            (vol->source.extents[0].end - vol->source.extents[0].start);
    } else {
164
        if (virStorageBackendUpdateVolInfo(vol, false,
165
                                           VIR_STORAGE_VOL_OPEN_DEFAULT, 0) < 0)
166 167
            return -1;
    }
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182
    /* Find the extended partition and increase the allocation value */
    if (vol->source.partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) {
        size_t i;

        for (i = 0; i < pool->volumes.count; i++) {
            if (pool->volumes.objs[i]->source.partType ==
                VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
                pool->volumes.objs[i]->target.allocation +=
                    vol->target.allocation;
                break;
            }
        }
    }

183
    if (STRNEQ(groups[2], "metadata"))
184
        pool->def->allocation += vol->target.allocation;
185 186 187 188 189 190 191
    if (vol->source.extents[0].end > pool->def->capacity)
        pool->def->capacity = vol->source.extents[0].end;

    return 0;
}

static int
192
virStorageBackendDiskMakeFreeExtent(virStoragePoolObjPtr pool,
193 194 195 196
                                    char **const groups)
{
    virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0];

197 198
    if (VIR_REALLOC_N(dev->freeExtents,
                      dev->nfreeExtent + 1) < 0)
199 200 201
        return -1;

    memset(dev->freeExtents +
202 203
           dev->nfreeExtent, 0,
           sizeof(dev->freeExtents[0]));
204

205
    /* set type of free area */
E
Eric Blake 已提交
206
    if (STREQ(groups[1], "logical")) {
207 208 209 210 211 212
        dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_LOGICAL;
    } else {
        dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_NORMAL;
    }


213 214 215 216 217 218 219 220
    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 */

221
    /* first block reported as free, even if it is not */
222
    if (dev->freeExtents[dev->nfreeExtent].start == 0)
223 224
        dev->freeExtents[dev->nfreeExtent].start = SECTOR_SIZE;

225 226 227 228 229 230 231 232 233 234 235 236
    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;
}


237 238 239 240 241
struct virStorageBackendDiskPoolVolData {
    virStoragePoolObjPtr pool;
    virStorageVolDefPtr vol;
};

242
static int
243
virStorageBackendDiskMakeVol(size_t ntok ATTRIBUTE_UNUSED,
244
                             char **const groups,
245
                             void *opaque)
246
{
247 248
    struct virStorageBackendDiskPoolVolData *data = opaque;
    virStoragePoolObjPtr pool = data->pool;
249 250 251 252 253 254 255 256 257 258 259 260
    /*
     * 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;

R
Richard W.M. Jones 已提交
261
    /* Remaining data / metadata parts get turn into volumes... */
262 263
    if (STREQ(groups[2], "metadata") ||
        STREQ(groups[2], "data")) {
264
        virStorageVolDefPtr vol = data->vol;
265 266 267 268 269 270 271 272 273 274 275 276

        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;
            }
        }
277

278
        return virStorageBackendDiskMakeDataVol(pool, groups, vol);
279 280
    } else if (STREQ(groups[2], "free")) {
        /* ....or free space extents */
281
        return virStorageBackendDiskMakeFreeExtent(pool, groups);
282
    } else {
R
Richard W.M. Jones 已提交
283
        /* This code path should never happen unless someone changed
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
         * 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
300
virStorageBackendDiskReadPartitions(virStoragePoolObjPtr pool,
301 302 303 304 305 306 307 308 309
                                    virStorageVolDefPtr vol)
{
    /*
     *  # libvirt_parthelper DEVICE
     * /dev/sda1      normal       data        32256    106928128    106896384
     * /dev/sda2      normal       data    106928640 100027629568  99920701440
     * -              normal   metadata 100027630080 100030242304      2612736
     *
     */
310 311 312

    char *parthelper_path;
    virCommandPtr cmd;
313 314 315 316
    struct virStorageBackendDiskPoolVolData cbdata = {
        .pool = pool,
        .vol = vol,
    };
317
    int ret;
318

319
    if (!(parthelper_path = virFileFindResource("libvirt_parthelper",
320
                                                abs_topbuilddir "/src",
321 322 323 324 325 326 327
                                                LIBEXECDIR)))
        return -1;

    cmd = virCommandNewArgList(parthelper_path,
                               pool->def->source.devices[0].path,
                               NULL);

328 329 330 331 332 333
    /* 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;
334

335 336 337 338
    ret = virCommandRunNul(cmd,
                           6,
                           virStorageBackendDiskMakeVol,
                           &cbdata);
339
    virCommandFree(cmd);
340
    VIR_FREE(parthelper_path);
341
    return ret;
342 343
}

344
static int
345
virStorageBackendDiskMakePoolGeometry(size_t ntok ATTRIBUTE_UNUSED,
346
                                      char **const groups,
347
                                      void *data)
348
{
349
    virStoragePoolObjPtr pool = data;
P
Peter Krempa 已提交
350 351 352 353 354 355 356 357
    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;
    }
358

P
Peter Krempa 已提交
359
    return 0;
360 361 362
}

static int
363
virStorageBackendDiskReadGeometry(virStoragePoolObjPtr pool)
364
{
365 366 367 368 369
    char *parthelper_path;
    virCommandPtr cmd;
    int ret;

    if (!(parthelper_path = virFileFindResource("libvirt_parthelper",
370
                                                abs_topbuilddir "/src",
371 372 373 374
                                                LIBEXECDIR)))
        return -1;

    cmd = virCommandNewArgList(parthelper_path,
375 376 377 378
                                             pool->def->source.devices[0].path,
                                             "-g",
                                             NULL);

379 380 381 382
    ret = virCommandRunNul(cmd,
                           3,
                           virStorageBackendDiskMakePoolGeometry,
                           pool);
383
    virCommandFree(cmd);
384
    VIR_FREE(parthelper_path);
385
    return ret;
386
}
387 388

static int
389
virStorageBackendDiskRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
390 391
                                 virStoragePoolObjPtr pool)
{
392
    VIR_FREE(pool->def->source.devices[0].freeExtents);
393 394
    pool->def->source.devices[0].nfreeExtent = 0;

395
    virFileWaitForDevices();
396

397
    if (!virFileExists(pool->def->source.devices[0].path)) {
398 399 400
        virReportError(VIR_ERR_INVALID_ARG,
                       _("device path '%s' doesn't exist"),
                       pool->def->source.devices[0].path);
401 402 403
        return -1;
    }

404
    if (virStorageBackendDiskReadGeometry(pool) != 0)
405 406
        return -1;

407
    return virStorageBackendDiskReadPartitions(pool, NULL);
408 409 410
}


411 412 413 414
/**
 * Check for a valid disk label (partition table) on device
 *
 * return: 0 - valid disk label found
415 416 417
 *         1 - no or unrecognized disk label
 *         2 - did not find the Partition Table type
 *         3 - Partition Table type unknown
418 419 420 421 422 423 424 425 426 427
 *        <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;
428
    char *error = NULL;
429
    char *start, *end;
430 431 432 433 434
    int ret = -1;

    virCommandAddArgSet(cmd, args);
    virCommandAddEnvString(cmd, "LC_ALL=C");
    virCommandSetOutputBuffer(cmd, &output);
435
    virCommandSetErrorBuffer(cmd, &error);
436 437 438 439

    /* if parted succeeds we have a valid partition table */
    ret = virCommandRun(cmd, NULL);
    if (ret < 0) {
440 441
        if ((output && strstr(output, "unrecognised disk label")) ||
            (error && strstr(error, "unrecognised disk label"))) {
442
            ret = 1;
443
        }
444
        goto cleanup;
445 446
    }

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    /* 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 = 2;
        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 = 3;
        goto cleanup;
    }

    ret = 0;

 cleanup:
472 473
    virCommandFree(cmd);
    VIR_FREE(output);
474
    VIR_FREE(error);
475 476 477
    return ret;
}

478 479
/**
 * Determine whether the label on the disk is valid or in a known format
480 481
 * for the purpose of rewriting the label during build or being able to
 * start a pool on a device.
482
 *
483 484 485 486 487 488
 * 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.
 *
489 490 491 492
 * 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.
 *
493 494 495 496
 * Return: True if it's OK
 *         False if something's wrong
 */
static bool
497 498
virStorageBackendDiskValidLabel(const char *device,
                                bool writelabel)
499 500 501 502 503
{
    bool valid = false;
    int check;

    check = virStorageBackendDiskFindLabel(device);
504
    if (check == 1) {
505 506 507 508 509
        if (writelabel)
            valid = true;
        else
            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
                           _("Unrecognized disk label found, requires build"));
510 511 512 513 514 515 516
    } else if (check == 2) {
        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
                       _("Unable to determine Partition Type, "
                         "requires build --overwrite"));
    } else if (check == 3) {
        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
                       _("Unknown Partition Type, requires build --overwrite"));
517 518
    } else if (check < 0) {
        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
519 520
                       _("Error checking for disk label, failed to get "
                         "disk partition information"));
521
    } else {
522 523 524 525 526 527
        if (writelabel)
            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                           _("Valid disk label already present, "
                             "requires --overwrite"));
        else
            valid = true;
528 529 530 531 532
    }
    return valid;
}


533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
static int
virStorageBackendDiskStartPool(virConnectPtr conn ATTRIBUTE_UNUSED,
                               virStoragePoolObjPtr pool)
{
    virFileWaitForDevices();

    if (!virFileExists(pool->def->source.devices[0].path)) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("device path '%s' doesn't exist"),
                       pool->def->source.devices[0].path);
        return -1;
    }

    if (!virStorageBackendDiskValidLabel(pool->def->source.devices[0].path,
                                         false))
        return -1;

    return 0;
}


554 555 556 557
/**
 * Write a new partition table header
 */
static int
558
virStorageBackendDiskBuildPool(virConnectPtr conn ATTRIBUTE_UNUSED,
559
                               virStoragePoolObjPtr pool,
E
Eric Blake 已提交
560
                               unsigned int flags)
561
{
562 563
    bool ok_to_mklabel = false;
    int ret = -1;
564
    virCommandPtr cmd = NULL;
565

566 567
    virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, ret);
E
Eric Blake 已提交
568

569 570 571
    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                             VIR_STORAGE_POOL_BUILD_NO_OVERWRITE,
                             error);
572

573
    if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE)
574
        ok_to_mklabel = true;
575 576
    else
        ok_to_mklabel = virStorageBackendDiskValidLabel(
577 578
                                            pool->def->source.devices[0].path,
                                            true);
579

580
    if (ok_to_mklabel) {
581 582 583
        /* eg parted /dev/sda mklabel --script msdos */
        int format = pool->def->source.format;
        const char *fmt;
584 585 586
        if (format == VIR_STORAGE_POOL_DISK_UNKNOWN)
            format = pool->def->source.format = VIR_STORAGE_POOL_DISK_DOS;
        if (format == VIR_STORAGE_POOL_DISK_DOS)
587 588 589 590
            fmt = "msdos";
        else
            fmt = virStoragePoolFormatDiskTypeToString(format);

591 592 593 594
        cmd = virCommandNewArgList(PARTED,
                                   pool->def->source.devices[0].path,
                                   "mklabel",
                                   "--script",
595
                                   fmt,
596
                                   NULL);
597
        ret = virCommandRun(cmd, NULL);
598
    }
599

600
 error:
601
    virCommandFree(cmd);
602
    return ret;
603 604
}

605 606 607 608
/**
 * Decides what kind of partition type that should be created.
 * Important when the partition table is of msdos type
 */
609
static int
610 611 612
virStorageBackendDiskPartTypeToCreate(virStoragePoolObjPtr pool)
{
    if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
613
        /* count primary and extended partitions,
614
           can't be more than 3 to create a new primary partition */
615
        size_t i;
616 617
        int count = 0;
        for (i = 0; i < pool->volumes.count; i++) {
618 619 620 621
            int partType = pool->volumes.objs[i]->source.partType;
            if (partType == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY ||
                partType == VIR_STORAGE_VOL_DISK_TYPE_EXTENDED)
                count++;
622
        }
623
        if (count >= 4)
624 625 626 627 628 629 630 631
            return VIR_STORAGE_VOL_DISK_TYPE_LOGICAL;
    }

    /* for all other cases, all partitions are primary */
    return VIR_STORAGE_VOL_DISK_TYPE_PRIMARY;
}

static int
632
virStorageBackendDiskPartFormat(virStoragePoolObjPtr pool,
633
                                virStorageVolDefPtr vol,
E
Eric Blake 已提交
634
                                char** partFormat)
635
{
636
    size_t i;
637
    if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
E
Eric Blake 已提交
638
        const char *partedFormat;
639
        partedFormat = virStoragePartedFsTypeToString(vol->target.format);
E
Eric Blake 已提交
640
        if (partedFormat == NULL) {
641 642
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("Invalid partition type"));
E
Eric Blake 已提交
643
            return -1;
644 645 646 647
        }
        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++) {
648 649
                if (pool->volumes.objs[i]->source.partType ==
                    VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
650 651
                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                   _("extended partition already exists"));
E
Eric Blake 已提交
652 653
                    return -1;
                }
654
            }
655
            if (VIR_STRDUP(*partFormat, partedFormat) < 0)
E
Eric Blake 已提交
656
                return -1;
657 658 659 660 661 662
        } 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)) {
E
Eric Blake 已提交
663
            case VIR_STORAGE_VOL_DISK_TYPE_PRIMARY:
664
                if (virAsprintf(partFormat, "primary %s", partedFormat) < 0)
E
Eric Blake 已提交
665
                    return -1;
E
Eric Blake 已提交
666 667 668 669
                break;
            case VIR_STORAGE_VOL_DISK_TYPE_LOGICAL:
                /* make sure we have a extended partition */
                for (i = 0; i < pool->volumes.count; i++) {
670 671
                    if (pool->volumes.objs[i]->source.partType ==
                        VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
E
Eric Blake 已提交
672
                        if (virAsprintf(partFormat, "logical %s",
673
                                        partedFormat) < 0)
E
Eric Blake 已提交
674
                            return -1;
E
Eric Blake 已提交
675 676 677 678
                        break;
                    }
                }
                if (i == pool->volumes.count) {
679 680
                    virReportError(VIR_ERR_INTERNAL_ERROR,
                                   "%s", _("no extended partition found and no primary partition available"));
E
Eric Blake 已提交
681 682 683 684
                    return -1;
                }
                break;
            default:
685 686
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               "%s", _("unknown partition type"));
E
Eric Blake 已提交
687
                return -1;
688 689 690
            }
        }
    } else {
691
        if (VIR_STRDUP(*partFormat, "primary") < 0)
E
Eric Blake 已提交
692
            return -1;
693 694 695 696 697
    }
    return 0;
}

/**
J
Ján Tomko 已提交
698
 * Aligns a new partition to nearest cylinder boundary
E
Eric Blake 已提交
699
 * when having a msdos partition table type
J
Ján Tomko 已提交
700
 * to avoid any problem with already existing
701 702 703
 * partitions
 */
static int
704 705 706 707
virStorageBackendDiskPartBoundaries(virStoragePoolObjPtr pool,
                                    unsigned long long *start,
                                    unsigned long long *end,
                                    unsigned long long allocation)
708
{
709
    size_t i;
710
    int smallestExtent = -1;
711 712 713
    unsigned long long smallestSize = 0;
    unsigned long long extraBytes = 0;
    unsigned long long alignedAllocation = allocation;
714
    virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0];
715
    unsigned long long cylinderSize = (unsigned long long)dev->geometry.heads *
716 717
                                      dev->geometry.sectors * SECTOR_SIZE;

718
    VIR_DEBUG("find free area: allocation %llu, cyl size %llu", allocation,
E
Eric Blake 已提交
719
          cylinderSize);
720 721 722
    int partType = virStorageBackendDiskPartTypeToCreate(pool);

    /* how many extra bytes we have since we allocate
J
Ján Tomko 已提交
723
       aligned to the cylinder boundary */
724 725
    extraBytes = cylinderSize - (allocation % cylinderSize);

726
    for (i = 0; i < dev->nfreeExtent; i++) {
727 728 729 730 731 732
         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) {
J
Ján Tomko 已提交
733
             /* align to cylinder boundary */
734 735 736 737 738 739
             neededSize += extraBytes;
             if ((*start % cylinderSize) > extraBytes) {
                 /* add an extra cylinder if the offset can't fit within
                    the extra bytes we have */
                 neededSize += cylinderSize;
             }
740
             /* if we are creating a logical partition, we need one extra
741
                block between partitions (or actually move start one block) */
742
             if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL)
743 744 745 746 747 748 749 750 751 752 753 754
                 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 */
E
Eric Blake 已提交
755 756 757
             } else if (partType == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY &&
                        dev->freeExtents[i].type != VIR_STORAGE_FREE_NORMAL) {
                 continue;
758 759 760 761 762 763 764 765
             }
             smallestSize = size;
             smallestExtent = i;
             alignedAllocation = neededSize;
         }
    }

    if (smallestExtent == -1) {
766 767
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("no large enough free extent"));
768 769 770
        return -1;
    }

771
    VIR_DEBUG("aligned alloc %llu", alignedAllocation);
772 773 774 775 776 777 778 779 780
    *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) {
J
Ján Tomko 已提交
781
        /* adjust our allocation if start is not at a cylinder boundary */
782 783 784
        *end -= (*start % cylinderSize);
    }

785
    /* counting in bytes, we want the last byte of the current sector */
786
    *end -= 1;
787
    VIR_DEBUG("final aligned start %llu, end %llu", *start, *end);
788 789 790 791
    return 0;
}


792
static int
793
virStorageBackendDiskDeleteVol(virConnectPtr conn,
794 795 796 797 798 799 800 801 802 803 804 805 806
                               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);

807 808 809 810 811 812 813
    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;
    }

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
    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;
    }

860 861 862 863
    /* Refreshing the pool is the easiest option as LOGICAL and EXTENDED
     * partition allocation/capacity management is handled within
     * virStorageBackendDiskMakeDataVol and trying to redo that logic
     * here is pointless
864
     */
865 866 867
    virStoragePoolObjClearVols(pool);
    if (virStorageBackendDiskRefreshPool(conn, pool) < 0)
        goto cleanup;
868

869 870 871 872 873 874 875 876
    rc = 0;
 cleanup:
    VIR_FREE(devpath);
    virCommandFree(cmd);
    return rc;
}


877
static int
878
virStorageBackendDiskCreateVol(virConnectPtr conn,
879
                               virStoragePoolObjPtr pool,
880 881
                               virStorageVolDefPtr vol)
{
E
Eric Blake 已提交
882
    int res = -1;
883
    char *partFormat = NULL;
884
    unsigned long long startOffset = 0, endOffset = 0;
885 886 887 888 889
    virCommandPtr cmd = virCommandNewArgList(PARTED,
                                             pool->def->source.devices[0].path,
                                             "mkpart",
                                             "--script",
                                             NULL);
890

891 892 893 894
    if (vol->target.encryption != NULL) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       "%s", _("storage pool does not support encrypted "
                               "volumes"));
895
        goto cleanup;
896
    }
897

898
    if (virStorageBackendDiskPartFormat(pool, vol, &partFormat) != 0)
899
        goto cleanup;
900
    virCommandAddArg(cmd, partFormat);
901

902 903
    if (virStorageBackendDiskPartBoundaries(pool, &startOffset,
                                            &endOffset,
904
                                            vol->target.capacity) != 0) {
E
Eric Blake 已提交
905
        goto cleanup;
906 907
    }

908 909
    virCommandAddArgFormat(cmd, "%lluB", startOffset);
    virCommandAddArgFormat(cmd, "%lluB", endOffset);
910

911
    if (virCommandRun(cmd, NULL) < 0)
E
Eric Blake 已提交
912
        goto cleanup;
913

914
    /* wait for device node to show up */
915
    virFileWaitForDevices();
916

917
    /* Blow away free extent info, as we're about to re-populate it */
918
    VIR_FREE(pool->def->source.devices[0].freeExtents);
919 920
    pool->def->source.devices[0].nfreeExtent = 0;

921 922 923 924
    /* Specifying a target path is meaningless */
    VIR_FREE(vol->target.path);

    /* Fetch actual extent info, generate key */
925 926 927 928 929 930 931 932
    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);
E
Eric Blake 已提交
933
        goto cleanup;
934
    }
935

E
Eric Blake 已提交
936 937
    res = 0;

938
 cleanup:
E
Eric Blake 已提交
939
    VIR_FREE(partFormat);
940
    virCommandFree(cmd);
E
Eric Blake 已提交
941
    return res;
942 943
}

944 945
static int
virStorageBackendDiskBuildVolFrom(virConnectPtr conn,
946
                                  virStoragePoolObjPtr pool,
947 948 949 950 951 952
                                  virStorageVolDefPtr vol,
                                  virStorageVolDefPtr inputvol,
                                  unsigned int flags)
{
    virStorageBackendBuildVolFrom build_func;

953
    build_func = virStorageBackendGetBuildVolFromFunction(vol, inputvol);
954 955 956
    if (!build_func)
        return -1;

957
    return build_func(conn, pool, vol, inputvol, flags);
958
}
959 960


961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
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;
}


979 980 981
virStorageBackend virStorageBackendDisk = {
    .type = VIR_STORAGE_POOL_DISK,

982
    .startPool = virStorageBackendDiskStartPool,
983 984 985 986 987
    .buildPool = virStorageBackendDiskBuildPool,
    .refreshPool = virStorageBackendDiskRefreshPool,

    .createVol = virStorageBackendDiskCreateVol,
    .deleteVol = virStorageBackendDiskDeleteVol,
988
    .buildVolFrom = virStorageBackendDiskBuildVolFrom,
989 990
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
991
    .wipeVol = virStorageBackendDiskVolWipe,
992
};