storage_backend_fs.c 41.4 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2011 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 34 35
 * 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/statvfs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

36 37 38 39
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>

O
Osier Yang 已提交
40 41 42 43
#if HAVE_LIBBLKID
# include <blkid/blkid.h>
#endif

44
#include "virterror_internal.h"
45 46
#include "storage_backend_fs.h"
#include "storage_conf.h"
47
#include "storage_file.h"
48
#include "command.h"
49
#include "memory.h"
50
#include "xml.h"
E
Eric Blake 已提交
51
#include "virfile.h"
O
Osier Yang 已提交
52
#include "logging.h"
53

54
#define VIR_FROM_THIS VIR_FROM_STORAGE
55

56 57 58 59 60
#define VIR_STORAGE_VOL_FS_OPEN_FLAGS       (VIR_STORAGE_VOL_OPEN_DEFAULT   |\
                                             VIR_STORAGE_VOL_OPEN_DIR)
#define VIR_STORAGE_VOL_FS_REFRESH_FLAGS    (VIR_STORAGE_VOL_FS_OPEN_FLAGS  &\
                                             ~VIR_STORAGE_VOL_OPEN_ERROR)

E
Eric Blake 已提交
61
static int ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
62
virStorageBackendProbeTarget(virStorageVolTargetPtr target,
63
                             char **backingStore,
64
                             int *backingStoreFormat,
65 66 67 68
                             unsigned long long *allocation,
                             unsigned long long *capacity,
                             virStorageEncryptionPtr *encryption)
{
69 70 71 72 73 74 75 76
    int fd = -1;
    int ret = -1;
    virStorageFileMetadata *meta;

    if (VIR_ALLOC(meta) < 0) {
        virReportOOMError();
        return ret;
    }
77

E
Eric Blake 已提交
78 79
    *backingStore = NULL;
    *backingStoreFormat = VIR_STORAGE_FILE_AUTO;
80 81 82
    if (encryption)
        *encryption = NULL;

83
    if ((ret = virStorageBackendVolOpenCheckMode(target->path,
84
                                        VIR_STORAGE_VOL_FS_REFRESH_FLAGS)) < 0)
85
        goto error; /* Take care to propagate ret, it is not always -1 */
86
    fd = ret;
87

88
    if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
89 90
                                                      allocation,
                                                      capacity)) < 0) {
91
        goto error;
92 93
    }

94
    if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) {
95 96
        ret = -1;
        goto error;
97 98
    }

99 100
    if (virStorageFileGetMetadataFromFD(target->path, fd,
                                        target->format,
101 102 103
                                        meta) < 0) {
        ret = -1;
        goto error;
104
    }
105

106
    VIR_FORCE_CLOSE(fd);
107

108 109 110 111
    if (meta->backingStore) {
        *backingStore = meta->backingStore;
        meta->backingStore = NULL;
        if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO) {
112 113 114 115 116 117 118 119 120 121 122
            if ((ret = virStorageFileProbeFormat(*backingStore)) < 0) {
                /* If the backing file is currently unavailable, only log an error,
                 * but continue. Returning -1 here would disable the whole storage
                 * pool, making it unavailable for even maintenance. */
                virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                                      _("cannot probe backing volume format: %s"),
                                      *backingStore);
                ret = -3;
            } else {
                *backingStoreFormat = ret;
                ret = 0;
123 124
            }
        } else {
125
            *backingStoreFormat = meta->backingStoreFormat;
126
            ret = 0;
127
        }
E
Eric Blake 已提交
128
    } else {
129
        ret = 0;
130 131
    }

132 133
    if (capacity && meta->capacity)
        *capacity = meta->capacity;
134

135
    if (encryption != NULL && meta->encrypted) {
136
        if (VIR_ALLOC(*encryption) < 0) {
137
            virReportOOMError();
138
            goto cleanup;
139 140 141 142 143 144 145 146 147 148 149 150
        }

        switch (target->format) {
        case VIR_STORAGE_FILE_QCOW:
        case VIR_STORAGE_FILE_QCOW2:
            (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW;
            break;
        default:
            break;
        }

        /* XXX ideally we'd fill in secret UUID here
E
Eric Blake 已提交
151
         * but we cannot guarantee 'conn' is non-NULL
152 153 154 155 156
         * at this point in time :-(  So we only fill
         * in secrets when someone first queries a vol
         */
    }

157 158
    virStorageFileFreeMetadata(meta);

159
    return ret;
160

161 162 163
error:
    VIR_FORCE_CLOSE(fd);

164
cleanup:
165 166 167
    virStorageFileFreeMetadata(meta);
    return ret;

168 169 170
}

#if WITH_STORAGE_FS
171

172
# include <mntent.h>
173

174 175
struct _virNetfsDiscoverState {
    const char *host;
176
    virStoragePoolSourceList list;
177 178 179 180 181
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
182
virStorageBackendFileSystemNetFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
183 184 185 186 187
                                                  char **const groups,
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
188 189
    virStoragePoolSource *src = NULL;
    int ret = -1;
190 191 192

    path = groups[0];

193
    if (!(name = strrchr(path, '/'))) {
194
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
195
                              _("invalid netfs path (no /): %s"), path);
196
        goto cleanup;
197 198 199
    }
    name += 1;
    if (*name == '\0') {
200
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
201
                              _("invalid netfs path (ends in /): %s"), path);
202
        goto cleanup;
203 204
    }

205
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
206
        goto cleanup;
207

208 209
    if (VIR_ALLOC_N(src->hosts, 1) < 0) {
        virReportOOMError();
210 211
        goto cleanup;
    }
212
    src->nhost = 1;
213 214

    if (!(src->hosts[0].name = strdup(state->host)) ||
215
        !(src->dir = strdup(path))) {
216
        virReportOOMError();
217 218
        goto cleanup;
    }
219
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
220

221 222 223
    ret = 0;
cleanup:
    return ret;
224 225
}

226

227
static char *
228
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
229
                                              const char *srcSpec,
E
Eric Blake 已提交
230
                                              unsigned int flags)
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
{
    /*
     *  # showmount --no-headers -e HOSTNAME
     *  /tmp   *
     *  /A dir demo1.foo.bar,demo2.foo.bar
     *
     * Extract directory name (including possible interior spaces ...).
     */

    const char *regexes[] = {
        "^(/.*\\S) +\\S+$"
    };
    int vars[] = {
        1
    };
246 247 248 249 250 251 252 253
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
254
    virStoragePoolSourcePtr source = NULL;
255
    char *retval = NULL;
256
    unsigned int i;
257
    virCommandPtr cmd = NULL;
258

E
Eric Blake 已提交
259 260
    virCheckFlags(0, NULL);

261
    source = virStoragePoolDefParseSourceString(srcSpec,
262 263
                                                VIR_STORAGE_POOL_NETFS);
    if (!source)
264 265
        goto cleanup;

266 267 268 269 270 271 272
    if (source->nhost != 1) {
        virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                              _("Expected exactly 1 host for the storage pool"));
        goto cleanup;
    }

    state.host = source->hosts[0].name;
273

274 275 276 277 278 279 280
    cmd = virCommandNewArgList(SHOWMOUNT,
                               "--no-headers",
                               "--exports",
                               source->hosts[0].name,
                               NULL);

    if (virStorageBackendRunProgRegex(NULL, cmd, 1, regexes, vars,
281
                            virStorageBackendFileSystemNetFindPoolSourcesFunc,
282
                            &state, NULL) < 0)
283 284
        goto cleanup;

285
    retval = virStoragePoolSourceListFormat(&state.list);
286
    if (retval == NULL) {
287
        virReportOOMError();
288 289 290 291
        goto cleanup;
    }

 cleanup:
292
    for (i = 0; i < state.list.nsources; i++)
293 294
        virStoragePoolSourceClear(&state.list.sources[i]);
    VIR_FREE(state.list.sources);
295

296
    virStoragePoolSourceFree(source);
297
    virCommandFree(cmd);
298 299 300 301
    return retval;
}


302 303 304 305 306 307 308 309 310
/**
 * @conn connection to report errors against
 * @pool storage pool to check for status
 *
 * Determine if a storage pool is already mounted
 *
 * Return 0 if not mounted, 1 if mounted, -1 on error
 */
static int
311
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool) {
312
    FILE *mtab;
313 314
    struct mntent ent;
    char buf[1024];
315 316

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
317
        virReportSystemError(errno,
318 319
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
320 321 322
        return -1;
    }

323 324
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
        if (STREQ(ent.mnt_dir, pool->def->target.path)) {
325
            VIR_FORCE_FCLOSE(mtab);
326 327 328 329
            return 1;
        }
    }

330
    VIR_FORCE_FCLOSE(mtab);
331 332 333 334 335 336 337 338 339 340 341 342 343
    return 0;
}

/**
 * @conn connection to report errors against
 * @pool storage pool to mount
 *
 * Ensure that a FS storage pool is mounted on its target location.
 * If already mounted, this is a no-op
 *
 * Returns 0 if successfully mounted, -1 on error
 */
static int
344
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool) {
345
    char *src = NULL;
346 347 348
    /* 'mount -t auto' doesn't seem to auto determine nfs (or cifs),
     *  while plain 'mount' does. We have to craft separate argvs to
     *  accommodate this */
349 350 351 352 353 354
    bool netauto = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                    pool->def->source.format == VIR_STORAGE_POOL_NETFS_AUTO);
    bool glusterfs = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                      pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS);
    virCommandPtr cmd = NULL;
    int ret = -1;
355 356

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
357 358 359 360 361 362
        if (pool->def->source.nhost != 1) {
            virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                                  _("Expected exactly 1 host for the storage pool"));
            return -1;
        }
        if (pool->def->source.hosts[0].name == NULL) {
363
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
364
                                  "%s", _("missing source host"));
365 366 367
            return -1;
        }
        if (pool->def->source.dir == NULL) {
368
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
369
                                  "%s", _("missing source path"));
370 371 372 373
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
374
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
375
                                  "%s", _("missing source device"));
376 377 378 379
            return -1;
        }
    }

J
Jim Meyering 已提交
380
    /* Short-circuit if already mounted */
381
    if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
382 383 384 385
        virStorageReportError(VIR_ERR_OPERATION_INVALID,
                              _("Target '%s' is already mounted"),
                              pool->def->target.path);
        return -1;
386 387 388
    }

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
389
        if (virAsprintf(&src, "%s:%s",
390
                        pool->def->source.hosts[0].name,
391
                        pool->def->source.dir) == -1) {
392
            virReportOOMError();
393 394
            return -1;
        }
395

396
    } else {
397
        if ((src = strdup(pool->def->source.devices[0].path)) == NULL) {
398
            virReportOOMError();
399 400
            return -1;
        }
401 402
    }

403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    if (netauto)
        cmd = virCommandNewArgList(MOUNT,
                                   src,
                                   pool->def->target.path,
                                   NULL);
    else if (glusterfs)
        cmd = virCommandNewArgList( MOUNT,
                                    "-t",
                                    (pool->def->type == VIR_STORAGE_POOL_FS ?
                                     virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) :
                                     virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format)),
                                    src,
                                    "-o",
                                    "direct-io-mode=1",
                                    pool->def->target.path,
                                    NULL);
    else
        cmd = virCommandNewArgList(MOUNT,
                                   "-t",
                                   (pool->def->type == VIR_STORAGE_POOL_FS ?
                                    virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) :
                                    virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format)),
                                   src,
                                   pool->def->target.path,
                                   NULL);

    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
cleanup:
    virCommandFree(cmd);
435
    VIR_FREE(src);
436
    return ret;
437 438 439 440 441 442 443 444 445 446 447 448
}

/**
 * @conn connection to report errors against
 * @pool storage pool to unmount
 *
 * Ensure that a FS storage pool is not mounted on its target location.
 * If already unmounted, this is a no-op
 *
 * Returns 0 if successfully unmounted, -1 on error
 */
static int
449
virStorageBackendFileSystemUnmount(virStoragePoolObjPtr pool) {
450 451
    virCommandPtr cmd = NULL;
    int ret = -1;
452 453

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
454 455 456 457 458 459
        if (pool->def->source.nhost != 1) {
            virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                                  _("Expected exactly 1 host for the storage pool"));
            return -1;
        }
        if (pool->def->source.hosts[0].name == NULL) {
460
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
461
                                  "%s", _("missing source host"));
462 463 464
            return -1;
        }
        if (pool->def->source.dir == NULL) {
465
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
466
                                  "%s", _("missing source dir"));
467 468 469 470
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
471
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
472
                                  "%s", _("missing source device"));
473 474 475 476 477
            return -1;
        }
    }

    /* Short-circuit if already unmounted */
478
    if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 1) {
479 480 481 482 483 484
        if (ret < 0)
            return -1;
        else
            return 0;
    }

485 486 487
    cmd = virCommandNewArgList(UMOUNT,
                               pool->def->target.path,
                               NULL);
488

489 490 491 492 493 494 495
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
cleanup:
    virCommandFree(cmd);
    return ret;
496 497 498 499
}
#endif /* WITH_STORAGE_FS */


500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
static int
virStorageBackendFileSystemCheck(virConnectPtr conn ATTRIBUTE_UNUSED,
                                 virStoragePoolObjPtr pool,
                                 bool *isActive)
{
    *isActive = false;
    if (pool->def->type == VIR_STORAGE_POOL_DIR) {
        if (access(pool->def->target.path, F_OK) == 0)
            *isActive = true;
#if WITH_STORAGE_FS
    } else {
        int ret;
        if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
            if (ret < 0)
                return -1;
            *isActive = true;
        }
#endif /* WITH_STORAGE_FS */
    }

    return 0;
}

#if WITH_STORAGE_FS
524 525 526 527 528 529 530 531 532 533 534
/**
 * @conn connection to report errors against
 * @pool storage pool to start
 *
 * Starts a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
static int
535
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
536 537 538
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
539
        virStorageBackendFileSystemMount(pool) < 0)
540 541 542 543 544 545
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */

O
Osier Yang 已提交
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
#if HAVE_LIBBLKID
static virStoragePoolProbeResult
virStorageBackendFileSystemProbe(const char *device,
                                 const char *format) {

    virStoragePoolProbeResult ret = FILESYSTEM_PROBE_ERROR;
    blkid_probe probe = NULL;
    const char *fstype = NULL;
    char *names[2], *libblkid_format = NULL;

    VIR_DEBUG("Probing for existing filesystem of type %s on device %s",
              format, device);

    if (blkid_known_fstype(format) == 0) {
        virStorageReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                              _("Not capable of probing for "
                                "filesystem of type %s"),
                              format);
        goto error;
    }

    probe = blkid_new_probe_from_filename(device);
    if (probe == NULL) {
        virStorageReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                                  _("Failed to create filesystem probe "
                                  "for device %s"),
                                  device);
        goto error;
    }

    if ((libblkid_format = strdup(format)) == NULL) {
        virReportOOMError();
        goto error;
    }

    names[0] = libblkid_format;
    names[1] = NULL;

    blkid_probe_filter_superblocks_type(probe,
                                        BLKID_FLTR_ONLYIN,
                                        names);

    if (blkid_do_probe(probe) != 0) {
        VIR_INFO("No filesystem of type '%s' found on device '%s'",
                 format, device);
        ret = FILESYSTEM_PROBE_NOT_FOUND;
    } else if (blkid_probe_lookup_value(probe, "TYPE", &fstype, NULL) == 0) {
        virStorageReportError(VIR_ERR_STORAGE_POOL_BUILT,
                              _("Existing filesystem of type '%s' found on "
                                "device '%s'"),
                              fstype, device);
        ret = FILESYSTEM_PROBE_FOUND;
    }

    if (blkid_do_probe(probe) != 1) {
        virStorageReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                                  _("Found additional probes to run, "
                                    "filesystem probing may be incorrect"));
        ret = FILESYSTEM_PROBE_ERROR;
    }

error:
    VIR_FREE(libblkid_format);

    if (probe != NULL) {
        blkid_free_probe(probe);
    }

    return ret;
}

#else /* #if HAVE_LIBBLKID */

static virStoragePoolProbeResult
virStorageBackendFileSystemProbe(const char *device ATTRIBUTE_UNUSED,
                                 const char *format ATTRIBUTE_UNUSED)
{
    virStorageReportError(VIR_ERR_OPERATION_INVALID,
                          _("probing for filesystems is unsupported "
                            "by this build"));

    return FILESYSTEM_PROBE_ERROR;
}

#endif /* #if HAVE_LIBBLKID */

632 633
/* some platforms don't support mkfs */
#ifdef MKFS
O
Osier Yang 已提交
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
static int
virStorageBackendExecuteMKFS(const char *device,
                             const char *format)
{
    int ret = 0;
    virCommandPtr cmd = NULL;

    cmd = virCommandNewArgList(MKFS,
                               "-t",
                               format,
                               device,
                               NULL);

    if (virCommandRun(cmd, NULL) < 0) {
        virReportSystemError(errno,
                             _("Failed to make filesystem of "
                               "type '%s' on device '%s'"),
                             format, device);
        ret = -1;
    }
    return ret;
}
656 657 658 659 660 661 662 663 664 665 666 667 668
#else /* #ifdef MKFS */
static int
virStorageBackendExecuteMKFS(const char *device ATTRIBUTE_UNUSED,
                             const char *format ATTRIBUTE_UNUSED)
{
    virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                              _("mkfs is not supported on this platform: "
                                "Failed to make filesystem of "
                               "type '%s' on device '%s'"),
                             format, device);
    return -1;
}
#endif /* #ifdef MKFS */
O
Osier Yang 已提交
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711

static int
virStorageBackendMakeFileSystem(virStoragePoolObjPtr pool,
                                unsigned int flags)
{
    const char *device = NULL, *format = NULL;
    bool ok_to_mkfs = false;
    int ret = -1;

    if (pool->def->source.devices == NULL) {
        virStorageReportError(VIR_ERR_OPERATION_INVALID,
                              _("No source device specified when formatting pool '%s'"),
                              pool->def->name);
        goto error;
    }

    device = pool->def->source.devices[0].path;
    format = virStoragePoolFormatFileSystemTypeToString(pool->def->source.format);
    VIR_DEBUG("source device: '%s' format: '%s'", device, format);

    if (!virFileExists(device)) {
        virStorageReportError(VIR_ERR_OPERATION_INVALID,
                              _("Source device does not exist when formatting pool '%s'"),
                              pool->def->name);
        goto error;
    }

    if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE) {
        ok_to_mkfs = true;
    } else if (flags & VIR_STORAGE_POOL_BUILD_NO_OVERWRITE &&
               virStorageBackendFileSystemProbe(device, format) ==
               FILESYSTEM_PROBE_NOT_FOUND) {
        ok_to_mkfs = true;
    }

    if (ok_to_mkfs) {
        ret = virStorageBackendExecuteMKFS(device, format);
    }

error:
    return ret;
}

712 713 714 715

/**
 * @conn connection to report errors against
 * @pool storage pool to build
E
Eric Blake 已提交
716
 * @flags controls the pool formatting behaviour
717 718 719
 *
 * Build a directory or FS based storage pool.
 *
O
Osier Yang 已提交
720 721 722 723 724 725 726
 * If no flag is set, it only makes the directory; If
 * VIR_STORAGE_POOL_BUILD_NO_OVERWRITE set, it probes to determine if
 * filesystem already exists on the target device, renurning an error
 * if exists, or using mkfs to format the target device if not; If
 * VIR_STORAGE_POOL_BUILD_OVERWRITE is set, mkfs is always executed,
 * any existed data on the target device is overwritten unconditionally.
 *
727 728 729 730 731
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
static int
732
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
733
                                 virStoragePoolObjPtr pool,
E
Eric Blake 已提交
734
                                 unsigned int flags)
735
{
736
    int err, ret = -1;
O
Osier Yang 已提交
737 738
    char *parent = NULL;
    char *p = NULL;
739

O
Osier Yang 已提交
740 741 742 743 744 745 746 747 748 749 750
    virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, ret);

    if (flags == (VIR_STORAGE_POOL_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_BUILD_NO_OVERWRITE)) {

        virStorageReportError(VIR_ERR_OPERATION_INVALID,
                              _("Overwrite and no overwrite flags"
                                " are mutually exclusive"));
        goto error;
    }
E
Eric Blake 已提交
751

752
    if ((parent = strdup(pool->def->target.path)) == NULL) {
753
        virReportOOMError();
754 755 756
        goto error;
    }
    if (!(p = strrchr(parent, '/'))) {
757
        virStorageReportError(VIR_ERR_INVALID_ARG,
758 759 760
                              _("path '%s' is not absolute"),
                              pool->def->target.path);
        goto error;
761 762
    }

763 764 765 766
    if (p != parent) {
        /* assure all directories in the path prior to the final dir
         * exist, with default uid/gid/mode. */
        *p = '\0';
767 768
        if (virFileMakePath(parent) < 0) {
            virReportSystemError(errno, _("cannot create path '%s'"),
769 770 771 772 773 774 775 776
                                 parent);
            goto error;
        }
    }

    /* Now create the final dir in the path with the uid/gid/mode
     * requested in the config. If the dir already exists, just set
     * the perms. */
777 778
    if ((err = virDirCreate(pool->def->target.path,
                            pool->def->target.perms.mode,
779 780
                            pool->def->target.perms.uid,
                            pool->def->target.perms.gid,
781 782 783 784 785 786 787
                            VIR_DIR_CREATE_FORCE_PERMS |
                            VIR_DIR_CREATE_ALLOW_EXIST |
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
                            ? VIR_DIR_CREATE_AS_UID : 0)) < 0)) {
        virReportSystemError(-err, _("cannot create path '%s'"),
                             pool->def->target.path);
        goto error;
788
    }
O
Osier Yang 已提交
789

790 791
    /* Reflect the actual uid and gid to the config. */
    if (pool->def->target.perms.uid == (uid_t) -1)
792
        pool->def->target.perms.uid = getuid();
793
    if (pool->def->target.perms.gid == (gid_t) -1)
794
        pool->def->target.perms.gid = getgid();
795

O
Osier Yang 已提交
796 797 798 799 800 801
    if (flags != 0) {
        ret = virStorageBackendMakeFileSystem(pool, flags);
    } else {
        ret = 0;
    }

802 803 804
error:
    VIR_FREE(parent);
    return ret;
805 806 807 808 809 810 811 812
}


/**
 * Iterate over the pool's directory and enumerate all disk images
 * within it. This is non-recursive.
 */
static int
813
virStorageBackendFileSystemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
814 815 816 817 818
                                   virStoragePoolObjPtr pool)
{
    DIR *dir;
    struct dirent *ent;
    struct statvfs sb;
819
    virStorageVolDefPtr vol = NULL;
820 821

    if (!(dir = opendir(pool->def->target.path))) {
822
        virReportSystemError(errno,
823 824
                             _("cannot open path '%s'"),
                             pool->def->target.path);
825 826 827 828 829
        goto cleanup;
    }

    while ((ent = readdir(dir)) != NULL) {
        int ret;
830
        char *backingStore;
831
        int backingStoreFormat;
832

833 834
        if (VIR_ALLOC(vol) < 0)
            goto no_memory;
835

836 837
        if ((vol->name = strdup(ent->d_name)) == NULL)
            goto no_memory;
838

839
        vol->type = VIR_STORAGE_VOL_FILE;
840
        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
841 842 843
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path,
                        vol->name) == -1)
844 845 846 847
            goto no_memory;

        if ((vol->key = strdup(vol->target.path)) == NULL)
            goto no_memory;
848

849
        if ((ret = virStorageBackendProbeTarget(&vol->target,
850
                                                &backingStore,
851
                                                &backingStoreFormat,
852
                                                &vol->allocation,
853
                                                &vol->capacity,
854
                                                &vol->target.encryption)) < 0) {
855
            if (ret == -2) {
856
                /* Silently ignore non-regular files,
857
                 * eg '.' '..', 'lost+found', dangling symbolic link */
858 859
                virStorageVolDefFree(vol);
                vol = NULL;
860
                continue;
861 862 863 864 865 866 867 868 869
            } else if (ret == -3) {
                /* The backing file is currently unavailable, its format is not
                 * explicitly specified, the probe to auto detect the format
                 * failed: continue with faked RAW format, since AUTO will
                 * break virStorageVolTargetDefFormat() generating the line
                 * <format type='...'/>. */
                backingStoreFormat = VIR_STORAGE_FILE_RAW;
            } else
                goto cleanup;
870 871
        }

872 873 874 875
        /* directory based volume */
        if (vol->target.format == VIR_STORAGE_FILE_DIR)
            vol->type = VIR_STORAGE_VOL_DIR;

876
        if (backingStore != NULL) {
877 878 879 880
            vol->backingStore.path = backingStore;
            vol->backingStore.format = backingStoreFormat;

            if (virStorageBackendUpdateVolTargetInfo(&vol->backingStore,
881 882
                                        NULL, NULL,
                                        VIR_STORAGE_VOL_OPEN_DEFAULT) < 0) {
883 884
                /* The backing file is currently unavailable, the capacity,
                 * allocation, owner, group and mode are unknown. Just log the
E
Eric Blake 已提交
885
                 * error and continue.
886 887 888 889 890 891
                 * Unfortunately virStorageBackendProbeTarget() might already
                 * have logged a similar message for the same problem, but only
                 * if AUTO format detection was used. */
                virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                                      _("cannot probe backing volume info: %s"),
                                      vol->backingStore.path);
892 893 894 895
            }
        }


896 897 898 899 900
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count+1) < 0)
            goto no_memory;
        pool->volumes.objs[pool->volumes.count++] = vol;
        vol = NULL;
901 902 903 904 905
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
906
        virReportSystemError(errno,
907 908
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
909 910 911 912 913 914 915 916 917 918
        return -1;
    }
    pool->def->capacity = ((unsigned long long)sb.f_frsize *
                           (unsigned long long)sb.f_blocks);
    pool->def->available = ((unsigned long long)sb.f_bfree *
                            (unsigned long long)sb.f_bsize);
    pool->def->allocation = pool->def->capacity - pool->def->available;

    return 0;

919
no_memory:
920
    virReportOOMError();
921 922
    /* fallthrough */

923
 cleanup:
924 925
    if (dir)
        closedir(dir);
926
    virStorageVolDefFree(vol);
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
    virStoragePoolObjClearVols(pool);
    return -1;
}


/**
 * @conn connection to report errors against
 * @pool storage pool to start
 *
 * Stops a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, unmounts the unlying source device on the pool
 *  - Releases all cached data about volumes
 */
#if WITH_STORAGE_FS
static int
943
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
944 945 946
                                virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
947
        virStorageBackendFileSystemUnmount(pool) < 0)
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */


/**
 * @conn connection to report errors against
 * @pool storage pool to build
 *
 * Build a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
static int
966
virStorageBackendFileSystemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
967
                                  virStoragePoolObjPtr pool,
E
Eric Blake 已提交
968
                                  unsigned int flags)
969
{
E
Eric Blake 已提交
970 971
    virCheckFlags(0, -1);

972 973
    /* XXX delete all vols first ? */

974
    if (rmdir(pool->def->target.path) < 0) {
975
        virReportSystemError(errno,
976
                             _("failed to remove pool '%s'"),
977
                             pool->def->target.path);
978 979 980 981 982 983 984 985
        return -1;
    }

    return 0;
}


/**
986 987 988 989
 * Set up a volume definition to be added to a pool's volume list, but
 * don't do any file creation or allocation. By separating the two processes,
 * we allow allocation progress reporting (by polling the volume's 'info'
 * function), and can drop the parent pool lock during the (slow) allocation.
990 991
 */
static int
992
virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
993 994 995 996
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

997 998
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
999
    VIR_FREE(vol->target.path);
1000 1001 1002
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
1003
        virReportOOMError();
1004 1005
        return -1;
    }
1006

R
Ryota Ozaki 已提交
1007
    VIR_FREE(vol->key);
1008 1009
    vol->key = strdup(vol->target.path);
    if (vol->key == NULL) {
1010
        virReportOOMError();
1011 1012 1013
        return -1;
    }

1014 1015 1016
    return 0;
}

1017
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED,
1018
                         virStoragePoolObjPtr pool,
1019
                         virStorageVolDefPtr vol,
1020
                         virStorageVolDefPtr inputvol,
E
Eric Blake 已提交
1021 1022
                         unsigned int flags)
{
1023 1024
    int err;

E
Eric Blake 已提交
1025 1026
    virCheckFlags(0, -1);

1027
    if (inputvol) {
1028
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
1029 1030 1031 1032 1033
                              "%s",
                              _("cannot copy from volume to a directory volume"));
        return -1;
    }

1034
    if ((err = virDirCreate(vol->target.path, vol->target.perms.mode,
1035 1036
                            vol->target.perms.uid,
                            vol->target.perms.gid,
1037
                            VIR_DIR_CREATE_FORCE_PERMS |
1038
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
1039 1040
                             ? VIR_DIR_CREATE_AS_UID : 0))) < 0) {
        virReportSystemError(-err, _("cannot create path '%s'"),
1041 1042 1043
                             vol->target.path);
        return -1;
    }
1044

1045 1046
    return 0;
}
1047

1048
static int
1049
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
1050
                                     virStoragePoolObjPtr pool,
1051 1052
                                     virStorageVolDefPtr vol,
                                     virStorageVolDefPtr inputvol)
1053
{
1054
    virStorageBackendBuildVolFrom create_func;
1055
    int tool_type;
1056

1057
    if (inputvol) {
1058
        if (vol->target.encryption != NULL) {
O
Osier Yang 已提交
1059
            virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED,
1060 1061 1062 1063 1064
                                  "%s", _("storage pool does not support "
                                          "building encrypted volumes from "
                                          "other volumes"));
            return -1;
        }
1065
        create_func = virStorageBackendGetBuildVolFromFunction(vol,
1066
                                                               inputvol);
1067 1068
        if (!create_func)
            return -1;
1069
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
1070
        create_func = virStorageBackendCreateRaw;
1071
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
1072
        create_func = createFileDir;
1073
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
1074
        create_func = virStorageBackendFSImageToolTypeToFunc(tool_type);
1075 1076

        if (!create_func)
1077
            return -1;
1078
    } else {
1079
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
1080 1081
                              "%s", _("creation of non-raw images "
                                      "is not supported without qemu-img"));
1082 1083 1084
        return -1;
    }

1085
    if (create_func(conn, pool, vol, inputvol, 0) < 0)
1086
        return -1;
1087 1088 1089
    return 0;
}

1090 1091 1092 1093 1094 1095 1096
/**
 * Allocate a new file as a volume. This is either done directly
 * for raw/sparse files, or by calling qemu-img/qcow-create for
 * special kinds of files
 */
static int
virStorageBackendFileSystemVolBuild(virConnectPtr conn,
1097
                                    virStoragePoolObjPtr pool,
1098
                                    virStorageVolDefPtr vol) {
1099
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL);
1100 1101 1102 1103 1104 1105 1106
}

/*
 * Create a storage vol using 'inputvol' as input
 */
static int
virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
1107
                                        virStoragePoolObjPtr pool,
1108 1109
                                        virStorageVolDefPtr vol,
                                        virStorageVolDefPtr inputvol,
E
Eric Blake 已提交
1110 1111 1112 1113
                                        unsigned int flags)
{
    virCheckFlags(0, -1);

1114
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, inputvol);
1115
}
1116 1117

/**
1118
 * Remove a volume - no support for BLOCK and NETWORK yet
1119 1120
 */
static int
1121
virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
1122 1123
                                     virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                     virStorageVolDefPtr vol,
E
Eric Blake 已提交
1124
                                     unsigned int flags)
1125
{
E
Eric Blake 已提交
1126 1127
    virCheckFlags(0, -1);

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
    switch (vol->type) {
    case VIR_STORAGE_VOL_FILE:
        if (unlink(vol->target.path) < 0) {
            /* Silently ignore failures where the vol has already gone away */
            if (errno != ENOENT) {
                virReportSystemError(errno,
                                     _("cannot unlink file '%s'"),
                                     vol->target.path);
                return -1;
            }
        }
        break;
    case VIR_STORAGE_VOL_DIR:
        if (rmdir(vol->target.path) < 0) {
1142
            virReportSystemError(errno,
1143
                                 _("cannot remove directory '%s'"),
1144
                                 vol->target.path);
1145 1146
            return -1;
        }
1147 1148 1149 1150 1151 1152 1153 1154
        break;
    case VIR_STORAGE_VOL_BLOCK:
    case VIR_STORAGE_VOL_NETWORK:
    default:
        virStorageReportError(VIR_ERR_NO_SUPPORT,
                              _("removing block or network volumes is not supported: %s"),
                              vol->target.path);
        return -1;
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
    }
    return 0;
}


/**
 * Update info about a volume's capacity/allocation
 */
static int
virStorageBackendFileSystemVolRefresh(virConnectPtr conn,
                                      virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                      virStorageVolDefPtr vol)
{
1168 1169
    int ret;

1170
    /* Refresh allocation / permissions info in case its changed */
1171 1172
    ret = virStorageBackendUpdateVolInfoFlags(vol, 0,
                                              VIR_STORAGE_VOL_FS_OPEN_FLAGS);
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
    if (ret < 0)
        return ret;

    /* Load any secrets if posible */
    if (vol->target.encryption &&
        vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_QCOW &&
        vol->target.encryption->nsecrets == 0) {
        virSecretPtr sec;
        virStorageEncryptionSecretPtr encsec = NULL;

        sec = virSecretLookupByUsage(conn,
                                     VIR_SECRET_USAGE_TYPE_VOLUME,
                                     vol->target.path);
        if (sec) {
            if (VIR_ALLOC_N(vol->target.encryption->secrets, 1) < 0 ||
                VIR_ALLOC(encsec) < 0) {
                VIR_FREE(vol->target.encryption->secrets);
1190
                virReportOOMError();
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
                virSecretFree(sec);
                return -1;
            }

            vol->target.encryption->nsecrets = 1;
            vol->target.encryption->secrets[0] = encsec;

            encsec->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE;
            virSecretGetUUID(sec, encsec->uuid);
            virSecretFree(sec);
        }
    }

    return 0;
1205 1206
}

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
static int
virStorageBackendFilesystemResizeQemuImg(const char *path,
                                         unsigned long long capacity)
{
    int ret = -1;
    char *img_tool;
    virCommandPtr cmd = NULL;

    /* KVM is usually ahead of qemu on features, so try that first */
    img_tool = virFindFileInPath("kvm-img");
    if (!img_tool)
        img_tool = virFindFileInPath("qemu-img");

    if (!img_tool) {
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                              "%s", _("unable to find kvm-img or qemu-img"));
        return -1;
    }

    cmd = virCommandNew(img_tool);
    virCommandAddArgList(cmd, "resize", path, NULL);
    virCommandAddArgFormat(cmd, "%llu", capacity);

    ret = virCommandRun(cmd, NULL);

    VIR_FREE(img_tool);
    virCommandFree(cmd);

    return ret;
}

/**
 * Resize a volume
 */
static int
virStorageBackendFileSystemVolResize(virConnectPtr conn ATTRIBUTE_UNUSED,
                                     virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                     virStorageVolDefPtr vol,
                                     unsigned long long capacity,
                                     unsigned int flags)
{
    virCheckFlags(0, -1);

    if (vol->target.format == VIR_STORAGE_FILE_RAW)
        return virStorageFileResize(vol->target.path, capacity);
    else
        return virStorageBackendFilesystemResizeQemuImg(vol->target.path,
                                                        capacity);
}

1257 1258 1259 1260
virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
1261
    .checkPool = virStorageBackendFileSystemCheck,
1262 1263
    .refreshPool = virStorageBackendFileSystemRefresh,
    .deletePool = virStorageBackendFileSystemDelete,
1264
    .buildVol = virStorageBackendFileSystemVolBuild,
1265
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1266 1267 1268
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1269
    .resizeVol = virStorageBackendFileSystemVolResize,
1270 1271 1272 1273 1274 1275 1276
};

#if WITH_STORAGE_FS
virStorageBackend virStorageBackendFileSystem = {
    .type = VIR_STORAGE_POOL_FS,

    .buildPool = virStorageBackendFileSystemBuild,
1277
    .checkPool = virStorageBackendFileSystemCheck,
1278 1279 1280 1281
    .startPool = virStorageBackendFileSystemStart,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1282
    .buildVol = virStorageBackendFileSystemVolBuild,
1283
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1284 1285 1286
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1287
    .resizeVol = virStorageBackendFileSystemVolResize,
1288 1289 1290 1291 1292
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
1293
    .checkPool = virStorageBackendFileSystemCheck,
1294
    .startPool = virStorageBackendFileSystemStart,
1295
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
1296 1297 1298
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1299
    .buildVol = virStorageBackendFileSystemVolBuild,
1300
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1301 1302 1303
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1304
    .resizeVol = virStorageBackendFileSystemVolResize,
1305 1306
};
#endif /* WITH_STORAGE_FS */