You need to sign in or sign up before continuing.
storage_backend_logical.c 21.0 KB
Newer Older
1
/*
2
 * storage_backend_logical.c: storage backend for logical volume handling
3
 *
4
 * Copyright (C) 2007-2009 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 * 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
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <regex.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

34
#include "virterror_internal.h"
35 36 37
#include "storage_backend_logical.h"
#include "storage_conf.h"
#include "util.h"
38
#include "memory.h"
39
#include "logging.h"
40
#include "files.h"
41

42 43
#define VIR_FROM_THIS VIR_FROM_STORAGE

44 45 46 47
#define PV_BLANK_SECTOR_SIZE 512


static int
48
virStorageBackendLogicalSetActive(virStoragePoolObjPtr pool,
49 50 51 52 53 54
                                  int on)
{
    const char *cmdargv[4];

    cmdargv[0] = VGCHANGE;
    cmdargv[1] = on ? "-ay" : "-an";
55
    cmdargv[2] = pool->def->source.name;
56 57
    cmdargv[3] = NULL;

58
    if (virRun(cmdargv, NULL) < 0)
59 60 61 62 63 64 65
        return -1;

    return 0;
}


static int
66
virStorageBackendLogicalMakeVol(virStoragePoolObjPtr pool,
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
                                char **const groups,
                                void *data)
{
    virStorageVolDefPtr vol = NULL;
    unsigned long long offset, size, length;

    /* See if we're only looking for a specific volume */
    if (data != NULL) {
        vol = data;
        if (STRNEQ(vol->name, groups[0]))
            return 0;
    }

    /* Or filling in more data on an existing volume */
    if (vol == NULL)
        vol = virStorageVolDefFindByName(pool, groups[0]);

    /* Or a completely new volume */
    if (vol == NULL) {
86
        if (VIR_ALLOC(vol) < 0) {
87
            virReportOOMError();
88 89 90
            return -1;
        }

91 92
        vol->type = VIR_STORAGE_VOL_BLOCK;

93
        if ((vol->name = strdup(groups[0])) == NULL) {
94
            virReportOOMError();
95
            virStorageVolDefFree(vol);
96 97 98
            return -1;
        }

99 100
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count + 1)) {
101
            virReportOOMError();
102 103 104 105
            virStorageVolDefFree(vol);
            return -1;
        }
        pool->volumes.objs[pool->volumes.count++] = vol;
106 107 108
    }

    if (vol->target.path == NULL) {
109 110
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path, vol->name) < 0) {
111
            virReportOOMError();
112 113 114 115 116 117 118 119
            virStorageVolDefFree(vol);
            return -1;
        }
    }

    if (groups[1] && !STREQ(groups[1], "")) {
        if (virAsprintf(&vol->backingStore.path, "%s/%s",
                        pool->def->target.path, groups[1]) < 0) {
120
            virReportOOMError();
121
            virStorageVolDefFree(vol);
122 123
            return -1;
        }
124 125

        vol->backingStore.format = VIR_STORAGE_POOL_LOGICAL_LVM2;
126 127 128
    }

    if (vol->key == NULL &&
129
        (vol->key = strdup(groups[2])) == NULL) {
130
        virReportOOMError();
131 132 133
        return -1;
    }

134
    if (virStorageBackendUpdateVolInfo(vol, 1) < 0)
135 136 137 138
        return -1;


    /* Finally fill in extents information */
139 140
    if (VIR_REALLOC_N(vol->source.extents,
                      vol->source.nextent + 1) < 0) {
141
        virReportOOMError();
142 143 144 145
        return -1;
    }

    if ((vol->source.extents[vol->source.nextent].path =
146
         strdup(groups[3])) == NULL) {
147
        virReportOOMError();
148 149 150
        return -1;
    }

151
    if (virStrToLong_ull(groups[4], NULL, 10, &offset) < 0) {
152
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
153
                              "%s", _("malformed volume extent offset value"));
154 155
        return -1;
    }
156
    if (virStrToLong_ull(groups[5], NULL, 10, &length) < 0) {
157
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
158
                              "%s", _("malformed volume extent length value"));
159 160
        return -1;
    }
161
    if (virStrToLong_ull(groups[6], NULL, 10, &size) < 0) {
162
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
163
                              "%s", _("malformed volume extent size value"));
164 165 166 167 168 169 170 171 172 173 174
        return -1;
    }

    vol->source.extents[vol->source.nextent].start = offset * size;
    vol->source.extents[vol->source.nextent].end = (offset * size) + length;
    vol->source.nextent++;

    return 0;
}

static int
175
virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool,
176 177 178
                                virStorageVolDefPtr vol)
{
    /*
179 180 181 182 183 184
     *  # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options "lv_name,origin,uuid,devices,seg_size,vg_extent_size" VGNAME
     *  RootLV,,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432
     *  SwapLV,,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432
     *  Test2,,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432
     *  Test3,,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432
     *  Test3,Test2,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432
185
     *
186
     * Pull out name, origin, & uuid, device, device extent start #, segment size, extent size.
187 188
     *
     * NB can be multiple rows per volume if they have many extents
189
     *
190 191 192 193
     * NB lvs from some distros (e.g. SLES10 SP2) outputs trailing "," on each line
     *
     * NB Encrypted logical volumes can print ':' in their name, so it is
     *    not a suitable separator (rhbz 470693).
194 195
     */
    const char *regexes[] = {
196
        "^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
197 198
    };
    int vars[] = {
199
        7
200 201
    };
    const char *prog[] = {
202
        LVS, "--separator", ",", "--noheadings", "--units", "b",
203
        "--unbuffered", "--nosuffix", "--options",
204
        "lv_name,origin,uuid,devices,seg_size,vg_extent_size",
205
        pool->def->source.name, NULL
206 207
    };

208 209
    int exitstatus;

210
    if (virStorageBackendRunProgRegex(pool,
211 212 213 214 215 216 217
                                      prog,
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendLogicalMakeVol,
                                      vol,
                                      &exitstatus) < 0) {
218
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
219
                              "%s", _("lvs command failed"));
220 221 222 223
                              return -1;
    }

    if (exitstatus != 0) {
224
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
225 226 227 228 229 230
                              _("lvs command failed with exitstatus %d"),
                              exitstatus);
        return -1;
    }

    return 0;
231 232 233
}

static int
234
virStorageBackendLogicalRefreshPoolFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
235 236 237 238 239 240 241 242 243 244 245 246 247
                                        char **const groups,
                                        void *data ATTRIBUTE_UNUSED)
{
    if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0)
        return -1;
    if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0)
        return -1;
    pool->def->allocation = pool->def->capacity - pool->def->available;

    return 0;
}


248
static int
249
virStorageBackendLogicalFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
250 251 252
                                            char **const groups,
                                            void *data)
{
253 254 255 256 257 258 259 260 261 262 263
    virStoragePoolSourceListPtr sourceList = data;
    char *pvname = NULL;
    char *vgname = NULL;
    int i;
    virStoragePoolSourceDevicePtr dev;
    virStoragePoolSource *thisSource;

    pvname = strdup(groups[0]);
    vgname = strdup(groups[1]);

    if (pvname == NULL || vgname == NULL) {
264
        virReportOOMError();
265 266 267 268 269 270 271 272 273 274
        goto err_no_memory;
    }

    thisSource = NULL;
    for (i = 0 ; i < sourceList->nsources; i++) {
        if (STREQ(sourceList->sources[i].name, vgname)) {
            thisSource = &sourceList->sources[i];
            break;
        }
    }
275

276
    if (thisSource == NULL) {
277
        if (!(thisSource = virStoragePoolSourceListNewSource(sourceList)))
278 279 280
            goto err_no_memory;

        thisSource->name = vgname;
281
    }
282 283
    else
        VIR_FREE(vgname);
284

285
    if (VIR_REALLOC_N(thisSource->devices, thisSource->ndevice + 1) != 0) {
286
        virReportOOMError();
287
        goto err_no_memory;
288 289
    }

290 291
    dev = &thisSource->devices[thisSource->ndevice];
    thisSource->ndevice++;
292
    thisSource->format = VIR_STORAGE_POOL_LOGICAL_LVM2;
293 294 295

    memset(dev, 0, sizeof(*dev));
    dev->path = pvname;
296 297

    return 0;
298 299 300 301 302 303

 err_no_memory:
    VIR_FREE(pvname);
    VIR_FREE(vgname);

    return -1;
304 305 306
}

static char *
307
virStorageBackendLogicalFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
308 309 310 311
                                        const char *srcSpec ATTRIBUTE_UNUSED,
                                        unsigned int flags ATTRIBUTE_UNUSED)
{
    /*
312 313 314
     * # pvs --noheadings -o pv_name,vg_name
     *   /dev/sdb
     *   /dev/sdc VolGroup00
315 316
     */
    const char *regexes[] = {
317
        "^\\s*(\\S+)\\s+(\\S+)\\s*$"
318 319
    };
    int vars[] = {
320
        2
321
    };
322
    const char *const prog[] = { PVS, "--noheadings", "-o", "pv_name,vg_name", NULL };
323
    const char *const scanprog[] = { VGSCAN, NULL };
324 325
    int exitstatus;
    char *retval = NULL;
326 327
    virStoragePoolSourceList sourceList;
    int i;
328

329 330 331 332 333
    /*
     * NOTE: ignoring errors here; this is just to "touch" any logical volumes
     * that might be hanging around, so if this fails for some reason, the
     * worst that happens is that scanning doesn't pick everything up
     */
334
    if (virRun(scanprog, &exitstatus) < 0) {
335 336
        VIR_WARN0("Failure when running vgscan to refresh physical volumes");
    }
337

338
    memset(&sourceList, 0, sizeof(sourceList));
339 340
    sourceList.type = VIR_STORAGE_POOL_LOGICAL;

341
    if (virStorageBackendRunProgRegex(NULL, prog, 1, regexes, vars,
342
                                      virStorageBackendLogicalFindPoolSourcesFunc,
343
                                      &sourceList, &exitstatus) < 0)
344 345
        return NULL;

346
    retval = virStoragePoolSourceListFormat(&sourceList);
347
    if (retval == NULL) {
348
        virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
349
                              _("failed to get source from sourceList"));
350 351 352 353
        goto cleanup;
    }

 cleanup:
354 355 356 357
    for (i = 0; i < sourceList.nsources; i++)
        virStoragePoolSourceFree(&sourceList.sources[i]);

    VIR_FREE(sourceList.sources);
358 359 360 361 362

    return retval;
}


363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
static int
virStorageBackendLogicalCheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
                                  virStoragePoolObjPtr pool,
                                  bool *isActive)
{
    char *path;

    *isActive = false;
    if (virAsprintf(&path, "/dev/%s", pool->def->source.name) < 0) {
        virReportOOMError();
        return -1;
    }

    if (access(path, F_OK) == 0)
        *isActive = true;

    VIR_FREE(path);

    return 0;
}

384
static int
385
virStorageBackendLogicalStartPool(virConnectPtr conn ATTRIBUTE_UNUSED,
386 387
                                  virStoragePoolObjPtr pool)
{
388
    if (virStorageBackendLogicalSetActive(pool, 1) < 0)
389 390 391 392 393 394 395
        return -1;

    return 0;
}


static int
396
virStorageBackendLogicalBuildPool(virConnectPtr conn ATTRIBUTE_UNUSED,
397 398 399 400 401 402 403 404 405 406
                                  virStoragePoolObjPtr pool,
                                  unsigned int flags ATTRIBUTE_UNUSED)
{
    const char **vgargv;
    const char *pvargv[3];
    int n = 0, i, fd;
    char zeros[PV_BLANK_SECTOR_SIZE];

    memset(zeros, 0, sizeof(zeros));

407
    if (VIR_ALLOC_N(vgargv, 3 + pool->def->source.ndevice) < 0) {
408
        virReportOOMError();
409 410 411 412
        return -1;
    }

    vgargv[n++] = VGCREATE;
413
    vgargv[n++] = pool->def->source.name;
414 415 416 417 418 419 420 421 422 423

    pvargv[0] = PVCREATE;
    pvargv[2] = NULL;
    for (i = 0 ; i < pool->def->source.ndevice ; i++) {
        /*
         * LVM requires that the first sector is blanked if using
         * a whole disk as a PV. So we just blank them out regardless
         * rather than trying to figure out if we're a disk or partition
         */
        if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) {
424
            virReportSystemError(errno,
425 426
                                 _("cannot open device '%s'"),
                                 pool->def->source.devices[i].path);
427 428
            goto cleanup;
        }
429
        if (safewrite(fd, zeros, sizeof(zeros)) < 0) {
430
            virReportSystemError(errno,
431 432
                                 _("cannot clear device header of '%s'"),
                                 pool->def->source.devices[i].path);
433
            VIR_FORCE_CLOSE(fd);
434 435
            goto cleanup;
        }
436
        if (VIR_CLOSE(fd) < 0) {
437
            virReportSystemError(errno,
438 439
                                 _("cannot close device '%s'"),
                                 pool->def->source.devices[i].path);
440 441 442 443 444 445 446 447 448
            goto cleanup;
        }

        /*
         * Initialize the physical volume because vgcreate is not
         * clever enough todo this for us :-(
         */
        vgargv[n++] = pool->def->source.devices[i].path;
        pvargv[1] = pool->def->source.devices[i].path;
449
        if (virRun(pvargv, NULL) < 0)
450 451 452
            goto cleanup;
    }

453
    vgargv[n] = NULL;
454 455

    /* Now create the volume group itself */
456
    if (virRun(vgargv, NULL) < 0)
457 458
        goto cleanup;

459
    VIR_FREE(vgargv);
460 461 462 463

    return 0;

 cleanup:
464
    VIR_FREE(vgargv);
465 466 467 468 469
    return -1;
}


static int
470
virStorageBackendLogicalRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
471 472 473 474 475 476 477
                                    virStoragePoolObjPtr pool)
{
    /*
     *  # vgs --separator : --noheadings --units b --unbuffered --nosuffix --options "vg_size,vg_free" VGNAME
     *    10603200512:4328521728
     *
     * Pull out size & free
478 479
     *
     * NB vgs from some distros (e.g. SLES10 SP2) outputs trailing ":" on each line
480 481
     */
    const char *regexes[] = {
482
        "^\\s*(\\S+):([0-9]+):?\\s*$"
483 484 485 486 487 488 489
    };
    int vars[] = {
        2
    };
    const char *prog[] = {
        VGS, "--separator", ":", "--noheadings", "--units", "b", "--unbuffered",
        "--nosuffix", "--options", "vg_size,vg_free",
490
        pool->def->source.name, NULL
491
    };
492
    int exitstatus;
493

494
    virFileWaitForDevices();
495

496
    /* Get list of all logical volumes */
497
    if (virStorageBackendLogicalFindLVs(pool, NULL) < 0) {
498 499 500 501 502
        virStoragePoolObjClearVols(pool);
        return -1;
    }

    /* Now get basic volgrp metadata */
503
    if (virStorageBackendRunProgRegex(pool,
504 505 506 507 508
                                      prog,
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendLogicalRefreshPoolFunc,
509 510 511 512 513 514 515
                                      NULL,
                                      &exitstatus) < 0) {
        virStoragePoolObjClearVols(pool);
        return -1;
    }

    if (exitstatus != 0) {
516 517 518 519 520 521 522
        virStoragePoolObjClearVols(pool);
        return -1;
    }

    return 0;
}

523 524 525 526
/*
 * This is actually relatively safe; if you happen to try to "stop" the
 * pool that your / is on, for instance, you will get failure like:
 * "Can't deactivate volume group "VolGroup00" with 3 open logical volume(s)"
527 528
 */
static int
529
virStorageBackendLogicalStopPool(virConnectPtr conn ATTRIBUTE_UNUSED,
530 531
                                 virStoragePoolObjPtr pool)
{
532
    if (virStorageBackendLogicalSetActive(pool, 0) < 0)
533 534 535 536 537 538
        return -1;

    return 0;
}

static int
539
virStorageBackendLogicalDeletePool(virConnectPtr conn ATTRIBUTE_UNUSED,
540 541 542 543
                                   virStoragePoolObjPtr pool,
                                   unsigned int flags ATTRIBUTE_UNUSED)
{
    const char *cmdargv[] = {
544
        VGREMOVE, "-f", pool->def->source.name, NULL
545
    };
546 547
    const char *pvargv[3];
    int i, error;
548

549
    /* first remove the volume group */
550
    if (virRun(cmdargv, NULL) < 0)
551 552
        return -1;

553 554 555 556 557 558
    /* now remove the pv devices and clear them out */
    error = 0;
    pvargv[0] = PVREMOVE;
    pvargv[2] = NULL;
    for (i = 0 ; i < pool->def->source.ndevice ; i++) {
        pvargv[1] = pool->def->source.devices[i].path;
559
        if (virRun(pvargv, NULL) < 0) {
560 561 562 563
            error = -1;
            break;
        }
    }
564

565
    return error;
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
}


static int
virStorageBackendLogicalDeleteVol(virConnectPtr conn,
                                  virStoragePoolObjPtr pool,
                                  virStorageVolDefPtr vol,
                                  unsigned int flags);


static int
virStorageBackendLogicalCreateVol(virConnectPtr conn,
                                  virStoragePoolObjPtr pool,
                                  virStorageVolDefPtr vol)
{
581
    int fdret, fd = -1;
582
    char size[100];
583
    const char *cmdargvnew[] = {
584 585 586
        LVCREATE, "--name", vol->name, "-L", size,
        pool->def->target.path, NULL
    };
587 588 589 590 591 592
    const char *cmdargvsnap[] = {
        LVCREATE, "--name", vol->name, "-L", size,
        "-s", vol->backingStore.path, NULL
    };
    const char **cmdargv = cmdargvnew;

593
    if (vol->target.encryption != NULL) {
594
        virStorageReportError(VIR_ERR_NO_SUPPORT,
595 596 597 598 599
                              "%s", _("storage pool does not support encrypted "
                                      "volumes"));
        return -1;
    }

600 601 602
    if (vol->backingStore.path) {
        cmdargv = cmdargvsnap;
    }
603

604
    snprintf(size, sizeof(size)-1, "%lluK", VIR_DIV_UP(vol->capacity, 1024));
605 606
    size[sizeof(size)-1] = '\0';

607 608
    vol->type = VIR_STORAGE_VOL_BLOCK;

609 610 611 612
    if (vol->target.path != NULL) {
        /* A target path passed to CreateVol has no meaning */
        VIR_FREE(vol->target.path);
    }
613 614 615 616

    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
617
        virReportOOMError();
618 619 620
        return -1;
    }

621
    if (virRun(cmdargv, NULL) < 0)
622 623
        return -1;

624
    if ((fdret = virStorageBackendVolOpen(vol->target.path)) < 0)
625
        goto cleanup;
626
    fd = fdret;
627 628 629 630

    /* We can only chown/grp if root */
    if (getuid() == 0) {
        if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) {
631
            virReportSystemError(errno,
632 633
                                 _("cannot set file owner '%s'"),
                                 vol->target.path);
634 635 636 637
            goto cleanup;
        }
    }
    if (fchmod(fd, vol->target.perms.mode) < 0) {
638
        virReportSystemError(errno,
639 640
                             _("cannot set file mode '%s'"),
                             vol->target.path);
641 642 643
        goto cleanup;
    }

644
    if (VIR_CLOSE(fd) < 0) {
645
        virReportSystemError(errno,
646 647
                             _("cannot close file '%s'"),
                             vol->target.path);
648 649 650 651 652
        goto cleanup;
    }
    fd = -1;

    /* Fill in data about this new vol */
653
    if (virStorageBackendLogicalFindLVs(pool, vol) < 0) {
654
        virReportSystemError(errno,
655 656
                             _("cannot find newly created volume '%s'"),
                             vol->target.path);
657 658 659 660 661 662
        goto cleanup;
    }

    return 0;

 cleanup:
663
    VIR_FORCE_CLOSE(fd);
664 665 666 667
    virStorageBackendLogicalDeleteVol(conn, pool, vol, 0);
    return -1;
}

668 669
static int
virStorageBackendLogicalBuildVolFrom(virConnectPtr conn,
670
                                     virStoragePoolObjPtr pool,
671 672 673 674 675 676
                                     virStorageVolDefPtr vol,
                                     virStorageVolDefPtr inputvol,
                                     unsigned int flags)
{
    virStorageBackendBuildVolFrom build_func;

677
    build_func = virStorageBackendGetBuildVolFromFunction(vol, inputvol);
678 679 680
    if (!build_func)
        return -1;

681
    return build_func(conn, pool, vol, inputvol, flags);
682 683
}

684
static int
685
virStorageBackendLogicalDeleteVol(virConnectPtr conn ATTRIBUTE_UNUSED,
686 687 688 689 690 691 692 693
                                  virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                  virStorageVolDefPtr vol,
                                  unsigned int flags ATTRIBUTE_UNUSED)
{
    const char *cmdargv[] = {
        LVREMOVE, "-f", vol->target.path, NULL
    };

694
    if (virRun(cmdargv, NULL) < 0)
695 696 697 698 699 700 701 702 703
        return -1;

    return 0;
}


virStorageBackend virStorageBackendLogical = {
    .type = VIR_STORAGE_POOL_LOGICAL,

704
    .findPoolSources = virStorageBackendLogicalFindPoolSources,
705
    .checkPool = virStorageBackendLogicalCheckPool,
706 707 708 709 710
    .startPool = virStorageBackendLogicalStartPool,
    .buildPool = virStorageBackendLogicalBuildPool,
    .refreshPool = virStorageBackendLogicalRefreshPool,
    .stopPool = virStorageBackendLogicalStopPool,
    .deletePool = virStorageBackendLogicalDeletePool,
711 712
    .buildVol = NULL,
    .buildVolFrom = virStorageBackendLogicalBuildVolFrom,
713 714 715
    .createVol = virStorageBackendLogicalCreateVol,
    .deleteVol = virStorageBackendLogicalDeleteVol,
};