storage_backend_fs.c 41.0 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2012 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 25 26 27 28 29 30 31 32 33 34 35
 *
 * 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>

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

44
#include "virerror.h"
45 46
#include "storage_backend_fs.h"
#include "storage_conf.h"
47
#include "virstoragefile.h"
48
#include "vircommand.h"
49
#include "viralloc.h"
50
#include "virxml.h"
E
Eric Blake 已提交
51
#include "virfile.h"
52
#include "virlog.h"
53
#include "virstring.h"
54

55
#define VIR_FROM_THIS VIR_FROM_STORAGE
56

57 58 59 60 61
#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 已提交
62
static int ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
63
virStorageBackendProbeTarget(virStorageVolTargetPtr target,
64
                             char **backingStore,
65
                             int *backingStoreFormat,
66 67 68 69
                             unsigned long long *allocation,
                             unsigned long long *capacity,
                             virStorageEncryptionPtr *encryption)
{
70 71
    int fd = -1;
    int ret = -1;
72
    virStorageFileMetadata *meta = NULL;
73

E
Eric Blake 已提交
74 75
    *backingStore = NULL;
    *backingStoreFormat = VIR_STORAGE_FILE_AUTO;
76 77 78
    if (encryption)
        *encryption = NULL;

79
    if ((ret = virStorageBackendVolOpenCheckMode(target->path,
80
                                        VIR_STORAGE_VOL_FS_REFRESH_FLAGS)) < 0)
81
        goto error; /* Take care to propagate ret, it is not always -1 */
82
    fd = ret;
83

84
    if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
85 86
                                                      allocation,
                                                      capacity)) < 0) {
87
        goto error;
88 89
    }

90
    if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) {
91 92
        ret = -1;
        goto error;
93 94
    }

95 96
    if (!(meta = virStorageFileGetMetadataFromFD(target->path, fd,
                                                 target->format))) {
97 98
        ret = -1;
        goto error;
99
    }
100

101
    VIR_FORCE_CLOSE(fd);
102

103 104 105
    if (meta->backingStore) {
        *backingStore = meta->backingStore;
        meta->backingStore = NULL;
E
Eric Blake 已提交
106 107
        if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO &&
            meta->backingStoreIsFile) {
108
            if ((ret = virStorageFileProbeFormat(*backingStore, -1, -1)) < 0) {
109 110 111
                /* 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. */
112 113 114
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("cannot probe backing volume format: %s"),
                               *backingStore);
115 116 117 118
                ret = -3;
            } else {
                *backingStoreFormat = ret;
                ret = 0;
119 120
            }
        } else {
121
            *backingStoreFormat = meta->backingStoreFormat;
122
            ret = 0;
123
        }
E
Eric Blake 已提交
124
    } else {
125
        ret = 0;
126 127
    }

128 129
    if (capacity && meta->capacity)
        *capacity = meta->capacity;
130

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

        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 已提交
147
         * but we cannot guarantee 'conn' is non-NULL
148 149 150 151 152
         * at this point in time :-(  So we only fill
         * in secrets when someone first queries a vol
         */
    }

153 154
    virStorageFileFreeMetadata(meta);

155
    return ret;
156

157 158 159
error:
    VIR_FORCE_CLOSE(fd);

160
cleanup:
161 162 163
    virStorageFileFreeMetadata(meta);
    return ret;

164 165 166
}

#if WITH_STORAGE_FS
167

168
# include <mntent.h>
169

170 171
struct _virNetfsDiscoverState {
    const char *host;
172
    virStoragePoolSourceList list;
173 174 175 176 177
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

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

    path = groups[0];

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

201
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
202
        goto cleanup;
203

204 205
    if (VIR_ALLOC_N(src->hosts, 1) < 0) {
        virReportOOMError();
206 207
        goto cleanup;
    }
208
    src->nhost = 1;
209

210 211
    if (VIR_STRDUP(src->hosts[0].name, state->host) < 0 ||
        VIR_STRDUP(src->dir, path) < 0)
212
        goto cleanup;
213
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
214

215 216 217
    ret = 0;
cleanup:
    return ret;
218 219
}

220

221
static char *
222
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
223
                                              const char *srcSpec,
E
Eric Blake 已提交
224
                                              unsigned int flags)
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
{
    /*
     *  # 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
    };
240 241 242 243 244 245 246 247
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
248
    virStoragePoolSourcePtr source = NULL;
249
    char *retval = NULL;
250
    unsigned int i;
251
    virCommandPtr cmd = NULL;
252

E
Eric Blake 已提交
253 254
    virCheckFlags(0, NULL);

255 256 257 258 259 260 261 262 263
    if (!srcSpec) {
        virReportError(VIR_ERR_INVALID_ARG,
                       "%s", _("hostname must be specified for netfs sources"));
        return NULL;
    }

    if (!(source = virStoragePoolDefParseSourceString(srcSpec,
                                                      VIR_STORAGE_POOL_NETFS)))
        return NULL;
264

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

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

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

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

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

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

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


301 302 303 304 305 306 307 308 309
/**
 * @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
310
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool) {
311
    FILE *mtab;
312 313
    struct mntent ent;
    char buf[1024];
314 315

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

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

329
    VIR_FORCE_FCLOSE(mtab);
330 331 332 333 334 335 336 337 338 339 340 341 342
    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
343
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool) {
344
    char *src = NULL;
345 346 347
    /* '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 */
348 349 350 351 352 353
    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;
354 355

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

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

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

395
    } else {
396
        if (VIR_STRDUP(src, pool->def->source.devices[0].path) < 0)
397
            return -1;
398 399
    }

400 401 402 403 404 405
    if (netauto)
        cmd = virCommandNewArgList(MOUNT,
                                   src,
                                   pool->def->target.path,
                                   NULL);
    else if (glusterfs)
406 407 408 409 410 411 412 413 414 415
        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);
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    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);
432
    VIR_FREE(src);
433
    return ret;
434 435 436 437 438 439 440 441 442 443 444 445
}

/**
 * @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
446
virStorageBackendFileSystemUnmount(virStoragePoolObjPtr pool) {
447 448
    virCommandPtr cmd = NULL;
    int ret = -1;
449
    int rc;
450 451

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

    /* Short-circuit if already unmounted */
476 477
    if ((rc = virStorageBackendFileSystemIsMounted(pool)) != 1)
        return rc;
478

479 480 481
    cmd = virCommandNewArgList(UMOUNT,
                               pool->def->target.path,
                               NULL);
482

483 484 485 486 487 488 489
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
cleanup:
    virCommandFree(cmd);
    return ret;
490 491 492 493
}
#endif /* WITH_STORAGE_FS */


494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
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
518 519 520 521 522 523 524 525 526 527 528
/**
 * @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
529
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
530 531 532
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
533
        virStorageBackendFileSystemMount(pool) < 0)
534 535 536 537 538 539
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */

540
#if WITH_BLKID
O
Osier Yang 已提交
541 542 543 544 545 546 547 548 549 550 551 552 553
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) {
554 555 556 557
        virReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                       _("Not capable of probing for "
                         "filesystem of type %s"),
                       format);
O
Osier Yang 已提交
558 559 560 561 562
        goto error;
    }

    probe = blkid_new_probe_from_filename(device);
    if (probe == NULL) {
563 564 565 566
        virReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                       _("Failed to create filesystem probe "
                         "for device %s"),
                       device);
O
Osier Yang 已提交
567 568 569
        goto error;
    }

570
    if (VIR_STRDUP(libblkid_format, format) < 0)
O
Osier Yang 已提交
571 572 573 574 575 576 577 578 579 580 581 582 583 584
        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) {
585 586 587 588
        virReportError(VIR_ERR_STORAGE_POOL_BUILT,
                       _("Existing filesystem of type '%s' found on "
                         "device '%s'"),
                       fstype, device);
O
Osier Yang 已提交
589 590 591 592
        ret = FILESYSTEM_PROBE_FOUND;
    }

    if (blkid_do_probe(probe) != 1) {
593
        virReportError(VIR_ERR_STORAGE_PROBE_FAILED, "%s",
594 595
                       _("Found additional probes to run, "
                         "filesystem probing may be incorrect"));
O
Osier Yang 已提交
596 597 598 599 600 601 602 603 604 605 606 607 608
        ret = FILESYSTEM_PROBE_ERROR;
    }

error:
    VIR_FREE(libblkid_format);

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

    return ret;
}

609
#else /* #if WITH_BLKID */
O
Osier Yang 已提交
610 611 612 613 614

static virStoragePoolProbeResult
virStorageBackendFileSystemProbe(const char *device ATTRIBUTE_UNUSED,
                                 const char *format ATTRIBUTE_UNUSED)
{
615
    virReportError(VIR_ERR_OPERATION_INVALID, "%s",
616 617
                   _("probing for filesystems is unsupported "
                     "by this build"));
O
Osier Yang 已提交
618 619 620 621

    return FILESYSTEM_PROBE_ERROR;
}

622
#endif /* #if WITH_BLKID */
O
Osier Yang 已提交
623

624 625
/* some platforms don't support mkfs */
#ifdef MKFS
O
Osier Yang 已提交
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
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;
    }
646 647

    virCommandFree(cmd);
O
Osier Yang 已提交
648 649
    return ret;
}
650 651 652 653 654
#else /* #ifdef MKFS */
static int
virStorageBackendExecuteMKFS(const char *device ATTRIBUTE_UNUSED,
                             const char *format ATTRIBUTE_UNUSED)
{
655 656 657 658 659
    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("mkfs is not supported on this platform: "
                     "Failed to make filesystem of "
                     "type '%s' on device '%s'"),
                   format, device);
660 661 662
    return -1;
}
#endif /* #ifdef MKFS */
O
Osier Yang 已提交
663 664 665 666 667 668 669 670 671 672

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) {
673 674 675
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("No source device specified when formatting pool '%s'"),
                       pool->def->name);
O
Osier Yang 已提交
676 677 678 679 680 681 682 683
        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)) {
684 685 686
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("Source device does not exist when formatting pool '%s'"),
                       pool->def->name);
O
Osier Yang 已提交
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
        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;
}

706 707 708 709

/**
 * @conn connection to report errors against
 * @pool storage pool to build
E
Eric Blake 已提交
710
 * @flags controls the pool formatting behaviour
711 712 713
 *
 * Build a directory or FS based storage pool.
 *
O
Osier Yang 已提交
714 715 716 717 718 719 720
 * 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.
 *
721 722 723 724 725
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
static int
726
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
727
                                 virStoragePoolObjPtr pool,
E
Eric Blake 已提交
728
                                 unsigned int flags)
729
{
730
    int err, ret = -1;
O
Osier Yang 已提交
731 732
    char *parent = NULL;
    char *p = NULL;
733

O
Osier Yang 已提交
734 735 736 737 738 739
    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)) {

740
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
741 742
                       _("Overwrite and no overwrite flags"
                         " are mutually exclusive"));
O
Osier Yang 已提交
743 744
        goto error;
    }
E
Eric Blake 已提交
745

746
    if (VIR_STRDUP(parent, pool->def->target.path) < 0)
747 748
        goto error;
    if (!(p = strrchr(parent, '/'))) {
749 750 751
        virReportError(VIR_ERR_INVALID_ARG,
                       _("path '%s' is not absolute"),
                       pool->def->target.path);
752
        goto error;
753 754
    }

755 756 757 758
    if (p != parent) {
        /* assure all directories in the path prior to the final dir
         * exist, with default uid/gid/mode. */
        *p = '\0';
759 760
        if (virFileMakePath(parent) < 0) {
            virReportSystemError(errno, _("cannot create path '%s'"),
761 762 763 764 765 766 767 768
                                 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. */
769 770
    if ((err = virDirCreate(pool->def->target.path,
                            pool->def->target.perms.mode,
771 772
                            pool->def->target.perms.uid,
                            pool->def->target.perms.gid,
773 774 775 776 777 778 779
                            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;
780
    }
O
Osier Yang 已提交
781

782 783
    /* Reflect the actual uid and gid to the config. */
    if (pool->def->target.perms.uid == (uid_t) -1)
784
        pool->def->target.perms.uid = getuid();
785
    if (pool->def->target.perms.gid == (gid_t) -1)
786
        pool->def->target.perms.gid = getgid();
787

O
Osier Yang 已提交
788 789 790 791 792 793
    if (flags != 0) {
        ret = virStorageBackendMakeFileSystem(pool, flags);
    } else {
        ret = 0;
    }

794 795 796
error:
    VIR_FREE(parent);
    return ret;
797 798 799 800 801 802 803 804
}


/**
 * Iterate over the pool's directory and enumerate all disk images
 * within it. This is non-recursive.
 */
static int
805
virStorageBackendFileSystemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
806 807 808 809 810
                                   virStoragePoolObjPtr pool)
{
    DIR *dir;
    struct dirent *ent;
    struct statvfs sb;
811
    virStorageVolDefPtr vol = NULL;
812 813

    if (!(dir = opendir(pool->def->target.path))) {
814
        virReportSystemError(errno,
815 816
                             _("cannot open path '%s'"),
                             pool->def->target.path);
817 818 819 820 821
        goto cleanup;
    }

    while ((ent = readdir(dir)) != NULL) {
        int ret;
822
        char *backingStore;
823
        int backingStoreFormat;
824

825 826
        if (VIR_ALLOC(vol) < 0)
            goto no_memory;
827

828 829
        if (VIR_STRDUP(vol->name, ent->d_name) < 0)
            goto cleanup;
830

831
        vol->type = VIR_STORAGE_VOL_FILE;
832
        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
833 834 835
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path,
                        vol->name) == -1)
836 837
            goto no_memory;

838 839
        if (VIR_STRDUP(vol->key, vol->target.path) < 0)
            goto cleanup;
840

841
        if ((ret = virStorageBackendProbeTarget(&vol->target,
842
                                                &backingStore,
843
                                                &backingStoreFormat,
844
                                                &vol->allocation,
845
                                                &vol->capacity,
846
                                                &vol->target.encryption)) < 0) {
847
            if (ret == -2) {
848
                /* Silently ignore non-regular files,
849
                 * eg '.' '..', 'lost+found', dangling symbolic link */
850 851
                virStorageVolDefFree(vol);
                vol = NULL;
852
                continue;
853 854 855 856 857 858 859 860 861
            } 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;
862 863
        }

864 865 866 867
        /* directory based volume */
        if (vol->target.format == VIR_STORAGE_FILE_DIR)
            vol->type = VIR_STORAGE_VOL_DIR;

868
        if (backingStore != NULL) {
869 870 871 872
            vol->backingStore.path = backingStore;
            vol->backingStore.format = backingStoreFormat;

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


888 889 890 891 892
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count+1) < 0)
            goto no_memory;
        pool->volumes.objs[pool->volumes.count++] = vol;
        vol = NULL;
893 894 895 896 897
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
898
        virReportSystemError(errno,
899 900
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
901 902 903 904 905
        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 *
906
                            (unsigned long long)sb.f_frsize);
907 908 909 910
    pool->def->allocation = pool->def->capacity - pool->def->available;

    return 0;

911
no_memory:
912
    virReportOOMError();
913 914
    /* fallthrough */

915
 cleanup:
916 917
    if (dir)
        closedir(dir);
918
    virStorageVolDefFree(vol);
919 920 921 922 923 924 925 926 927
    virStoragePoolObjClearVols(pool);
    return -1;
}


/**
 * @conn connection to report errors against
 * @pool storage pool to start
 *
928
 * Stops a FS based storage pool.
929 930 931 932 933 934
 *
 *  - 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
935
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
936 937
                                virStoragePoolObjPtr pool)
{
938
    if (virStorageBackendFileSystemUnmount(pool) < 0)
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
        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
957
virStorageBackendFileSystemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
958
                                  virStoragePoolObjPtr pool,
E
Eric Blake 已提交
959
                                  unsigned int flags)
960
{
E
Eric Blake 已提交
961 962
    virCheckFlags(0, -1);

963 964
    /* XXX delete all vols first ? */

965
    if (rmdir(pool->def->target.path) < 0) {
966
        virReportSystemError(errno,
967
                             _("failed to remove pool '%s'"),
968
                             pool->def->target.path);
969 970 971 972 973 974 975 976
        return -1;
    }

    return 0;
}


/**
977 978 979 980
 * 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.
981 982
 */
static int
983
virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
984 985 986 987
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

988 989
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
990
    VIR_FREE(vol->target.path);
991 992 993
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
994
        virReportOOMError();
995 996
        return -1;
    }
997

998 999 1000 1001 1002 1003 1004
    if (virFileExists(vol->target.path)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("volume target path '%s' already exists"),
                       vol->target.path);
        return -1;
    }

R
Ryota Ozaki 已提交
1005
    VIR_FREE(vol->key);
1006
    return VIR_STRDUP(vol->key, vol->target.path);
1007 1008
}

1009
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED,
1010
                         virStoragePoolObjPtr pool,
1011
                         virStorageVolDefPtr vol,
1012
                         virStorageVolDefPtr inputvol,
E
Eric Blake 已提交
1013 1014
                         unsigned int flags)
{
1015 1016
    int err;

E
Eric Blake 已提交
1017 1018
    virCheckFlags(0, -1);

1019
    if (inputvol) {
1020 1021 1022
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s",
                       _("cannot copy from volume to a directory volume"));
1023 1024 1025
        return -1;
    }

1026
    if ((err = virDirCreate(vol->target.path, vol->target.perms.mode,
1027 1028
                            vol->target.perms.uid,
                            vol->target.perms.gid,
1029
                            VIR_DIR_CREATE_FORCE_PERMS |
1030
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
1031 1032
                             ? VIR_DIR_CREATE_AS_UID : 0))) < 0) {
        virReportSystemError(-err, _("cannot create path '%s'"),
1033 1034 1035
                             vol->target.path);
        return -1;
    }
1036

1037 1038
    return 0;
}
1039

1040
static int
1041
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
1042
                                     virStoragePoolObjPtr pool,
1043
                                     virStorageVolDefPtr vol,
1044 1045
                                     virStorageVolDefPtr inputvol,
                                     unsigned int flags)
1046
{
1047
    virStorageBackendBuildVolFrom create_func;
1048
    int tool_type;
1049

1050
    if (inputvol) {
1051
        if (vol->target.encryption != NULL) {
1052 1053 1054 1055
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           "%s", _("storage pool does not support "
                                   "building encrypted volumes from "
                                   "other volumes"));
1056 1057
            return -1;
        }
1058
        create_func = virStorageBackendGetBuildVolFromFunction(vol,
1059
                                                               inputvol);
1060 1061
        if (!create_func)
            return -1;
1062
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
1063
        create_func = virStorageBackendCreateRaw;
1064
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
1065
        create_func = createFileDir;
1066
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
1067
        create_func = virStorageBackendFSImageToolTypeToFunc(tool_type);
1068 1069

        if (!create_func)
1070
            return -1;
1071
    } else {
1072 1073 1074
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("creation of non-raw images "
                               "is not supported without qemu-img"));
1075 1076 1077
        return -1;
    }

1078
    if (create_func(conn, pool, vol, inputvol, flags) < 0)
1079
        return -1;
1080 1081 1082
    return 0;
}

1083 1084 1085 1086 1087 1088 1089
/**
 * 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,
1090
                                    virStoragePoolObjPtr pool,
1091 1092 1093 1094 1095
                                    virStorageVolDefPtr vol,
                                    unsigned int flags) {
    virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);

    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL, flags);
1096 1097 1098 1099 1100 1101 1102
}

/*
 * Create a storage vol using 'inputvol' as input
 */
static int
virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
1103
                                        virStoragePoolObjPtr pool,
1104 1105
                                        virStorageVolDefPtr vol,
                                        virStorageVolDefPtr inputvol,
E
Eric Blake 已提交
1106 1107
                                        unsigned int flags)
{
1108
    virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);
E
Eric Blake 已提交
1109

1110
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, inputvol, flags);
1111
}
1112 1113

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

1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
    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) {
1138
            virReportSystemError(errno,
1139
                                 _("cannot remove directory '%s'"),
1140
                                 vol->target.path);
1141 1142
            return -1;
        }
1143 1144 1145 1146
        break;
    case VIR_STORAGE_VOL_BLOCK:
    case VIR_STORAGE_VOL_NETWORK:
    default:
1147 1148 1149
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("removing block or network volumes is not supported: %s"),
                       vol->target.path);
1150
        return -1;
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
    }
    return 0;
}


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

1166
    /* Refresh allocation / permissions info in case its changed */
1167 1168
    ret = virStorageBackendUpdateVolInfoFlags(vol, 0,
                                              VIR_STORAGE_VOL_FS_OPEN_FLAGS);
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
    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);
1186
                virReportOOMError();
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
                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;
1201 1202
}

1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
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) {
1217 1218
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("unable to find kvm-img or qemu-img"));
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
        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);
}

1253 1254 1255 1256
virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

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

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

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

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