storage_backend_fs.c 41.5 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
                                    virStorageVolDefPtr vol,
1092 1093
                                    unsigned int flags)
{
1094 1095 1096
    virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);

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

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

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

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

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


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

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

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

1223 1224 1225 1226
    /* Round capacity as qemu-img resize errors out on sizes which are not
     * a multiple of 512 */
    capacity = VIR_ROUND_UP(capacity, 512);

1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
    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)
{
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
    virCheckFlags(VIR_STORAGE_VOL_RESIZE_ALLOCATE, -1);

    bool pre_allocate = flags & VIR_STORAGE_VOL_RESIZE_ALLOCATE;

    if (vol->target.format == VIR_STORAGE_FILE_RAW) {
        return virStorageFileResize(vol->target.path, capacity,
                                    vol->capacity, pre_allocate);
    } else {
        if (pre_allocate) {
            virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
                           _("preallocate is only supported for raw "
                             "type volume"));
            return -1;
        }
1263 1264 1265

        return virStorageBackendFilesystemResizeQemuImg(vol->target.path,
                                                        capacity);
1266
    }
1267 1268
}

1269 1270 1271 1272
virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

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

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

    .buildPool = virStorageBackendFileSystemBuild,
1289
    .checkPool = virStorageBackendFileSystemCheck,
1290 1291 1292 1293
    .startPool = virStorageBackendFileSystemStart,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1294
    .buildVol = virStorageBackendFileSystemVolBuild,
1295
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1296 1297 1298
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1299
    .resizeVol = virStorageBackendFileSystemVolResize,
1300 1301 1302 1303 1304
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
1305
    .checkPool = virStorageBackendFileSystemCheck,
1306
    .startPool = virStorageBackendFileSystemStart,
1307
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
1308 1309 1310
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1311
    .buildVol = virStorageBackendFileSystemVolBuild,
1312
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1313 1314 1315
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1316
    .resizeVol = virStorageBackendFileSystemVolResize,
1317 1318
};
#endif /* WITH_STORAGE_FS */