storage_backend_fs.c 42.4 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright (C) 2007-2008 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 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
VIR_LOG_INIT("storage.storage_backend_fs");

59 60 61 62
#define VIR_STORAGE_VOL_FS_OPEN_FLAGS    (VIR_STORAGE_VOL_OPEN_DEFAULT | \
                                          VIR_STORAGE_VOL_OPEN_DIR)
#define VIR_STORAGE_VOL_FS_PROBE_FLAGS   (VIR_STORAGE_VOL_FS_OPEN_FLAGS | \
                                          VIR_STORAGE_VOL_OPEN_NOERROR)
63

E
Eric Blake 已提交
64
static int ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
65
virStorageBackendProbeTarget(virStorageSourcePtr target,
66
                             char **backingStore,
67
                             int *backingStoreFormat,
68 69
                             virStorageEncryptionPtr *encryption)
{
70 71
    int fd = -1;
    int ret = -1;
72
    virStorageFileMetadata *meta = NULL;
E
Eric Blake 已提交
73
    struct stat sb;
74 75
    char *header = NULL;
    ssize_t len = VIR_STORAGE_MAX_HEADER;
76

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

82
    if ((ret = virStorageBackendVolOpen(target->path, &sb,
83
                                        VIR_STORAGE_VOL_FS_PROBE_FLAGS)) < 0)
84
        goto error; /* Take care to propagate ret, it is not always -1 */
85
    fd = ret;
86

87
    if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd, &sb)) < 0) {
88
        goto error;
89 90
    }

91 92 93 94 95 96 97 98
    if (S_ISDIR(sb.st_mode)) {
        target->format = VIR_STORAGE_FILE_DIR;
    } else {
        if ((len = virFileReadHeaderFD(fd, len, &header)) < 0) {
            virReportSystemError(errno, _("cannot read header '%s'"),
                                 target->path);
            goto error;
        }
99

100 101 102 103 104 105 106 107 108
        target->format = virStorageFileProbeFormatFromBuf(target->path,
                                                          header, len);
        if (target->format < 0) {
            ret = -1;
            goto error;
        }

        if (!(meta = virStorageFileGetMetadataFromBuf(target->path,
                                                      header, len,
109 110 111
                                                      target->format,
                                                      backingStore,
                                                      backingStoreFormat))) {
112 113 114
            ret = -1;
            goto error;
        }
115
    }
116

117
    VIR_FORCE_CLOSE(fd);
118

119 120 121 122 123 124 125 126 127 128 129
    if (meta && *backingStore &&
        *backingStoreFormat == VIR_STORAGE_FILE_AUTO &&
        virStorageIsFile(*backingStore)) {
        if ((ret = virStorageFileProbeFormat(*backingStore, -1, -1)) < 0) {
            /* If the backing file is currently unavailable, only log an error,
             * but continue. Returning -1 here would disable the whole storage
             * pool, making it unavailable for even maintenance. */
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("cannot probe backing volume format: %s"),
                           *backingStore);
            ret = -3;
130
        } else {
131
            *backingStoreFormat = ret;
132
            ret = 0;
133
        }
E
Eric Blake 已提交
134
    } else {
135
        ret = 0;
136 137
    }

138 139
    if (meta && meta->capacity)
        target->capacity = meta->capacity;
140

141 142 143
    if (encryption && meta && meta->encryption) {
        *encryption = meta->encryption;
        meta->encryption = NULL;
144 145 146 147 148 149 150 151 152 153 154

        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 已提交
155
         * but we cannot guarantee 'conn' is non-NULL
156 157 158 159 160
         * at this point in time :-(  So we only fill
         * in secrets when someone first queries a vol
         */
    }

161
    virBitmapFree(target->features);
162 163 164 165
    if (meta) {
        target->features = meta->features;
        meta->features = NULL;
    }
166

167
    if (meta && meta->compat) {
168 169 170 171 172
        VIR_FREE(target->compat);
        target->compat = meta->compat;
        meta->compat = NULL;
    }

173
    goto cleanup;
174

175
 error:
176 177
    VIR_FORCE_CLOSE(fd);

178
 cleanup:
179
    virStorageFileFreeMetadata(meta);
180
    VIR_FREE(header);
181 182
    return ret;

183 184 185
}

#if WITH_STORAGE_FS
186

187
# include <mntent.h>
188

189 190
struct _virNetfsDiscoverState {
    const char *host;
191
    virStoragePoolSourceList list;
192 193 194 195 196
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
197
virStorageBackendFileSystemNetFindPoolSourcesFunc(char **const groups,
198 199 200 201
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
202 203
    virStoragePoolSource *src = NULL;
    int ret = -1;
204 205 206

    path = groups[0];

207
    if (!(name = strrchr(path, '/'))) {
208 209
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid netfs path (no /): %s"), path);
210
        goto cleanup;
211 212 213
    }
    name += 1;
    if (*name == '\0') {
214 215
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid netfs path (ends in /): %s"), path);
216
        goto cleanup;
217 218
    }

219
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
220
        goto cleanup;
221

222
    if (VIR_ALLOC_N(src->hosts, 1) < 0)
223
        goto cleanup;
224
    src->nhost = 1;
225

226 227
    if (VIR_STRDUP(src->hosts[0].name, state->host) < 0 ||
        VIR_STRDUP(src->dir, path) < 0)
228
        goto cleanup;
229
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
230

231
    ret = 0;
232
 cleanup:
233
    return ret;
234 235
}

236

237 238
static void
virStorageBackendFileSystemNetFindNFSPoolSources(virNetfsDiscoverState *state)
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
{
    /*
     *  # 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
    };
254 255 256 257 258 259 260 261 262 263 264

    virCommandPtr cmd = NULL;

    cmd = virCommandNewArgList(SHOWMOUNT,
                               "--no-headers",
                               "--exports",
                               state->host,
                               NULL);

    if (virCommandRunRegex(cmd, 1, regexes, vars,
                           virStorageBackendFileSystemNetFindPoolSourcesFunc,
265
                           state, NULL) < 0)
266 267 268 269 270 271 272 273 274 275 276
        virResetLastError();

    virCommandFree(cmd);
}


static char *
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
                                              const char *srcSpec,
                                              unsigned int flags)
{
277 278 279 280 281 282 283 284
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
285
    virStoragePoolSourcePtr source = NULL;
286
    char *ret = NULL;
287
    size_t i;
288

E
Eric Blake 已提交
289 290
    virCheckFlags(0, NULL);

291
    if (!srcSpec) {
292 293
        virReportError(VIR_ERR_INVALID_ARG, "%s",
                       _("hostname must be specified for netfs sources"));
294 295 296 297 298 299
        return NULL;
    }

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

301
    if (source->nhost != 1) {
302 303
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
304 305 306 307
        goto cleanup;
    }

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

309
    virStorageBackendFileSystemNetFindNFSPoolSources(&state);
310

311 312 313 314 315
    if (virStorageBackendFindGlusterPoolSources(state.host,
                                                VIR_STORAGE_POOL_NETFS_GLUSTERFS,
                                                &state.list) < 0)
        goto cleanup;

316
    if (!(ret = virStoragePoolSourceListFormat(&state.list)))
317 318 319
        goto cleanup;

 cleanup:
320
    for (i = 0; i < state.list.nsources; i++)
321 322
        virStoragePoolSourceClear(&state.list.sources[i]);
    VIR_FREE(state.list.sources);
323

324
    virStoragePoolSourceFree(source);
325
    return ret;
326 327 328
}


329 330 331 332 333 334 335 336 337
/**
 * @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
338 339
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool)
{
340
    FILE *mtab;
341 342
    struct mntent ent;
    char buf[1024];
343 344

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
345
        virReportSystemError(errno,
346 347
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
348 349 350
        return -1;
    }

351 352
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
        if (STREQ(ent.mnt_dir, pool->def->target.path)) {
353
            VIR_FORCE_FCLOSE(mtab);
354 355 356 357
            return 1;
        }
    }

358
    VIR_FORCE_FCLOSE(mtab);
359 360 361 362 363 364 365 366 367 368 369 370 371
    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
372 373
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool)
{
374
    char *src = NULL;
375 376 377
    /* '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 */
378 379 380 381 382 383
    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;
384
    int rc;
385 386

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
387
        if (pool->def->source.nhost != 1) {
388 389
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("Expected exactly 1 host for the storage pool"));
390 391 392
            return -1;
        }
        if (pool->def->source.hosts[0].name == NULL) {
393 394
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source host"));
395 396 397
            return -1;
        }
        if (pool->def->source.dir == NULL) {
398 399
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source path"));
400 401 402 403
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
404 405
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source device"));
406 407 408 409
            return -1;
        }
    }

J
Jim Meyering 已提交
410
    /* Short-circuit if already mounted */
411 412 413 414 415 416
    if ((rc = virStorageBackendFileSystemIsMounted(pool)) != 0) {
        if (rc == 1) {
            virReportError(VIR_ERR_OPERATION_INVALID,
                           _("Target '%s' is already mounted"),
                           pool->def->target.path);
        }
417
        return -1;
418 419 420
    }

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
421
        if (virAsprintf(&src, "%s:%s",
422
                        pool->def->source.hosts[0].name,
423
                        pool->def->source.dir) == -1)
424
            return -1;
425

426
    } else {
427
        if (VIR_STRDUP(src, pool->def->source.devices[0].path) < 0)
428
            return -1;
429 430
    }

431 432 433 434 435 436
    if (netauto)
        cmd = virCommandNewArgList(MOUNT,
                                   src,
                                   pool->def->target.path,
                                   NULL);
    else if (glusterfs)
437 438 439 440 441 442 443 444 445 446
        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);
447 448 449 450 451 452 453 454 455 456 457 458 459 460
    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;
461
 cleanup:
462
    virCommandFree(cmd);
463
    VIR_FREE(src);
464
    return ret;
465 466 467 468 469 470 471
}

/**
 * @conn connection to report errors against
 * @pool storage pool to unmount
 *
 * Ensure that a FS storage pool is not mounted on its target location.
472
 * If already unmounted, this is a no-op.
473 474 475 476
 *
 * Returns 0 if successfully unmounted, -1 on error
 */
static int
477 478
virStorageBackendFileSystemUnmount(virStoragePoolObjPtr pool)
{
479 480
    virCommandPtr cmd = NULL;
    int ret = -1;
481
    int rc;
482 483

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
484
        if (pool->def->source.nhost != 1) {
485 486
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("Expected exactly 1 host for the storage pool"));
487 488 489
            return -1;
        }
        if (pool->def->source.hosts[0].name == NULL) {
490 491
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source host"));
492 493 494
            return -1;
        }
        if (pool->def->source.dir == NULL) {
495 496
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source dir"));
497 498 499 500
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
501 502
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source device"));
503 504 505 506 507
            return -1;
        }
    }

    /* Short-circuit if already unmounted */
508 509
    if ((rc = virStorageBackendFileSystemIsMounted(pool)) != 1)
        return rc;
510

511 512 513
    cmd = virCommandNewArgList(UMOUNT,
                               pool->def->target.path,
                               NULL);
514

515 516 517 518
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
519
 cleanup:
520 521
    virCommandFree(cmd);
    return ret;
522 523 524 525
}
#endif /* WITH_STORAGE_FS */


526 527 528 529 530 531
static int
virStorageBackendFileSystemCheck(virConnectPtr conn ATTRIBUTE_UNUSED,
                                 virStoragePoolObjPtr pool,
                                 bool *isActive)
{
    if (pool->def->type == VIR_STORAGE_POOL_DIR) {
532
        *isActive = virFileExists(pool->def->target.path);
533 534 535
#if WITH_STORAGE_FS
    } else {
        int ret;
536
        *isActive = false;
537 538 539 540 541 542 543 544 545 546 547 548
        if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
            if (ret < 0)
                return -1;
            *isActive = true;
        }
#endif /* WITH_STORAGE_FS */
    }

    return 0;
}

#if WITH_STORAGE_FS
549 550 551 552
/**
 * @conn connection to report errors against
 * @pool storage pool to start
 *
553 554
 * Starts a directory or FS based storage pool.  The underlying source
 * device will be mounted for FS based pools.
555 556 557 558
 *
 * Returns 0 on success, -1 on error
 */
static int
559
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
560 561 562
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
563
        virStorageBackendFileSystemMount(pool) < 0)
564 565 566 567 568 569
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */

570
#if WITH_BLKID
O
Osier Yang 已提交
571 572
static virStoragePoolProbeResult
virStorageBackendFileSystemProbe(const char *device,
573 574
                                 const char *format)
{
O
Osier Yang 已提交
575 576 577 578 579 580 581 582 583 584

    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) {
585 586 587 588
        virReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                       _("Not capable of probing for "
                         "filesystem of type %s"),
                       format);
O
Osier Yang 已提交
589 590 591 592 593
        goto error;
    }

    probe = blkid_new_probe_from_filename(device);
    if (probe == NULL) {
594 595 596 597
        virReportError(VIR_ERR_STORAGE_PROBE_FAILED,
                       _("Failed to create filesystem probe "
                         "for device %s"),
                       device);
O
Osier Yang 已提交
598 599 600
        goto error;
    }

601
    if (VIR_STRDUP(libblkid_format, format) < 0)
O
Osier Yang 已提交
602 603 604 605 606 607 608 609 610 611 612 613 614 615
        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) {
616 617 618 619
        virReportError(VIR_ERR_STORAGE_POOL_BUILT,
                       _("Existing filesystem of type '%s' found on "
                         "device '%s'"),
                       fstype, device);
O
Osier Yang 已提交
620 621 622 623
        ret = FILESYSTEM_PROBE_FOUND;
    }

    if (blkid_do_probe(probe) != 1) {
624
        virReportError(VIR_ERR_STORAGE_PROBE_FAILED, "%s",
625 626
                       _("Found additional probes to run, "
                         "filesystem probing may be incorrect"));
O
Osier Yang 已提交
627 628 629
        ret = FILESYSTEM_PROBE_ERROR;
    }

630
 error:
O
Osier Yang 已提交
631 632 633 634 635 636 637 638 639
    VIR_FREE(libblkid_format);

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

    return ret;
}

640
#else /* #if WITH_BLKID */
O
Osier Yang 已提交
641 642 643 644 645

static virStoragePoolProbeResult
virStorageBackendFileSystemProbe(const char *device ATTRIBUTE_UNUSED,
                                 const char *format ATTRIBUTE_UNUSED)
{
646
    virReportError(VIR_ERR_OPERATION_INVALID, "%s",
647 648
                   _("probing for filesystems is unsupported "
                     "by this build"));
O
Osier Yang 已提交
649 650 651 652

    return FILESYSTEM_PROBE_ERROR;
}

653
#endif /* #if WITH_BLKID */
O
Osier Yang 已提交
654

655 656
/* some platforms don't support mkfs */
#ifdef MKFS
O
Osier Yang 已提交
657 658 659 660 661 662 663
static int
virStorageBackendExecuteMKFS(const char *device,
                             const char *format)
{
    int ret = 0;
    virCommandPtr cmd = NULL;

J
Ján Tomko 已提交
664 665 666 667 668 669 670
    cmd = virCommandNewArgList(MKFS, "-t", format, NULL);

    /* use the force, otherwise mkfs.xfs won't overwrite existing fs */
    if (STREQ(format, "xfs"))
        virCommandAddArg(cmd, "-f");

    virCommandAddArg(cmd, device);
O
Osier Yang 已提交
671 672 673 674 675 676 677 678

    if (virCommandRun(cmd, NULL) < 0) {
        virReportSystemError(errno,
                             _("Failed to make filesystem of "
                               "type '%s' on device '%s'"),
                             format, device);
        ret = -1;
    }
679 680

    virCommandFree(cmd);
O
Osier Yang 已提交
681 682
    return ret;
}
683 684 685 686 687
#else /* #ifdef MKFS */
static int
virStorageBackendExecuteMKFS(const char *device ATTRIBUTE_UNUSED,
                             const char *format ATTRIBUTE_UNUSED)
{
688 689 690 691 692
    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("mkfs is not supported on this platform: "
                     "Failed to make filesystem of "
                     "type '%s' on device '%s'"),
                   format, device);
693 694 695
    return -1;
}
#endif /* #ifdef MKFS */
O
Osier Yang 已提交
696 697 698 699 700 701 702 703 704 705

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) {
706 707 708
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("No source device specified when formatting pool '%s'"),
                       pool->def->name);
O
Osier Yang 已提交
709 710 711 712 713 714 715 716
        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)) {
717 718 719
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("Source device does not exist when formatting pool '%s'"),
                       pool->def->name);
O
Osier Yang 已提交
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
        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);
    }

735
 error:
O
Osier Yang 已提交
736 737 738
    return ret;
}

739 740 741 742

/**
 * @conn connection to report errors against
 * @pool storage pool to build
E
Eric Blake 已提交
743
 * @flags controls the pool formatting behaviour
744 745 746
 *
 * Build a directory or FS based storage pool.
 *
O
Osier Yang 已提交
747 748 749 750 751 752 753
 * 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.
 *
754
 * The underlying source device is mounted for FS based pools.
755 756 757 758
 *
 * Returns 0 on success, -1 on error
 */
static int
759
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
760
                                 virStoragePoolObjPtr pool,
E
Eric Blake 已提交
761
                                 unsigned int flags)
762
{
763
    int err, ret = -1;
O
Osier Yang 已提交
764 765
    char *parent = NULL;
    char *p = NULL;
766

O
Osier Yang 已提交
767 768 769 770 771 772
    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)) {

773
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
774 775
                       _("Overwrite and no overwrite flags"
                         " are mutually exclusive"));
O
Osier Yang 已提交
776 777
        goto error;
    }
E
Eric Blake 已提交
778

779
    if (VIR_STRDUP(parent, pool->def->target.path) < 0)
780 781
        goto error;
    if (!(p = strrchr(parent, '/'))) {
782 783 784
        virReportError(VIR_ERR_INVALID_ARG,
                       _("path '%s' is not absolute"),
                       pool->def->target.path);
785
        goto error;
786 787
    }

788 789 790 791
    if (p != parent) {
        /* assure all directories in the path prior to the final dir
         * exist, with default uid/gid/mode. */
        *p = '\0';
792 793
        if (virFileMakePath(parent) < 0) {
            virReportSystemError(errno, _("cannot create path '%s'"),
794 795 796 797 798 799 800 801
                                 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. */
802 803
    if ((err = virDirCreate(pool->def->target.path,
                            pool->def->target.perms.mode,
804 805
                            pool->def->target.perms.uid,
                            pool->def->target.perms.gid,
806 807 808 809 810 811 812
                            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;
813
    }
O
Osier Yang 已提交
814

815 816
    /* Reflect the actual uid and gid to the config. */
    if (pool->def->target.perms.uid == (uid_t) -1)
817
        pool->def->target.perms.uid = geteuid();
818
    if (pool->def->target.perms.gid == (gid_t) -1)
819
        pool->def->target.perms.gid = getegid();
820

O
Osier Yang 已提交
821 822 823 824 825 826
    if (flags != 0) {
        ret = virStorageBackendMakeFileSystem(pool, flags);
    } else {
        ret = 0;
    }

827
 error:
828 829
    VIR_FREE(parent);
    return ret;
830 831 832 833 834 835 836 837
}


/**
 * Iterate over the pool's directory and enumerate all disk images
 * within it. This is non-recursive.
 */
static int
838
virStorageBackendFileSystemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
839 840 841 842 843
                                   virStoragePoolObjPtr pool)
{
    DIR *dir;
    struct dirent *ent;
    struct statvfs sb;
844
    virStorageVolDefPtr vol = NULL;
845 846

    if (!(dir = opendir(pool->def->target.path))) {
847
        virReportSystemError(errno,
848 849
                             _("cannot open path '%s'"),
                             pool->def->target.path);
850 851 852 853 854
        goto cleanup;
    }

    while ((ent = readdir(dir)) != NULL) {
        int ret;
855
        char *backingStore;
856
        int backingStoreFormat;
857

858
        if (VIR_ALLOC(vol) < 0)
859
            goto cleanup;
860

861 862
        if (VIR_STRDUP(vol->name, ent->d_name) < 0)
            goto cleanup;
863

864
        vol->type = VIR_STORAGE_VOL_FILE;
865
        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
866 867 868
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path,
                        vol->name) == -1)
869
            goto cleanup;
870

871 872
        if (VIR_STRDUP(vol->key, vol->target.path) < 0)
            goto cleanup;
873

874
        if ((ret = virStorageBackendProbeTarget(&vol->target,
875
                                                &backingStore,
876
                                                &backingStoreFormat,
877
                                                &vol->target.encryption)) < 0) {
878
            if (ret == -2) {
879
                /* Silently ignore non-regular files,
880
                 * eg '.' '..', 'lost+found', dangling symbolic link */
881 882
                virStorageVolDefFree(vol);
                vol = NULL;
883
                continue;
884 885 886 887 888 889 890 891 892
            } 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;
893 894
        }

895 896 897 898
        /* directory based volume */
        if (vol->target.format == VIR_STORAGE_FILE_DIR)
            vol->type = VIR_STORAGE_VOL_DIR;

899
        if (backingStore != NULL) {
900 901 902
            vol->backingStore.path = backingStore;
            vol->backingStore.format = backingStoreFormat;

903 904 905 906 907 908
            ignore_value(virStorageBackendUpdateVolTargetInfo(
                                               &vol->backingStore, false,
                                               VIR_STORAGE_VOL_OPEN_DEFAULT));
            /* If this failed, the backing file is currently unavailable,
             * the capacity, allocation, owner, group and mode are unknown.
             * An error message was raised, but we just continue. */
909 910 911
        }


912
        if (VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0)
913
            goto cleanup;
914 915 916 917 918
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
919
        virReportSystemError(errno,
920 921
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
922 923 924 925 926
        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 *
927
                            (unsigned long long)sb.f_frsize);
928 929 930 931 932
    pool->def->allocation = pool->def->capacity - pool->def->available;

    return 0;

 cleanup:
933 934
    if (dir)
        closedir(dir);
935
    virStorageVolDefFree(vol);
936 937 938 939 940 941 942
    virStoragePoolObjClearVols(pool);
    return -1;
}


/**
 * @conn connection to report errors against
943
 * @pool storage pool to stop
944
 *
945 946
 * Stops a file storage pool.  The underlying source device is unmounted
 * for FS based pools.  Any cached data about volumes is released.
947
 *
948
 * Returns 0 on success, -1 on error.
949 950 951
 */
#if WITH_STORAGE_FS
static int
952
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
953 954
                                virStoragePoolObjPtr pool)
{
955
    if (virStorageBackendFileSystemUnmount(pool) < 0)
956 957 958 959 960 961 962 963 964
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */


/**
 * @conn connection to report errors against
965
 * @pool storage pool to delete
966
 *
967
 * Delete a directory based storage pool
968 969 970 971
 *
 * Returns 0 on success, -1 on error
 */
static int
972
virStorageBackendFileSystemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
973
                                  virStoragePoolObjPtr pool,
E
Eric Blake 已提交
974
                                  unsigned int flags)
975
{
E
Eric Blake 已提交
976 977
    virCheckFlags(0, -1);

978 979
    /* XXX delete all vols first ? */

980
    if (rmdir(pool->def->target.path) < 0) {
981
        virReportSystemError(errno,
982
                             _("failed to remove pool '%s'"),
983
                             pool->def->target.path);
984 985 986 987 988 989 990 991
        return -1;
    }

    return 0;
}


/**
992 993 994 995
 * 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.
996 997
 */
static int
998
virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
999 1000 1001 1002
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

1003 1004
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
1005
    VIR_FREE(vol->target.path);
1006 1007
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
1008
                    vol->name) == -1)
1009
        return -1;
1010

1011 1012 1013 1014 1015 1016 1017
    if (virFileExists(vol->target.path)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("volume target path '%s' already exists"),
                       vol->target.path);
        return -1;
    }

R
Ryota Ozaki 已提交
1018
    VIR_FREE(vol->key);
1019
    return VIR_STRDUP(vol->key, vol->target.path);
1020 1021
}

1022
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED,
1023
                         virStoragePoolObjPtr pool,
1024
                         virStorageVolDefPtr vol,
1025
                         virStorageVolDefPtr inputvol,
E
Eric Blake 已提交
1026 1027
                         unsigned int flags)
{
1028 1029
    int err;

E
Eric Blake 已提交
1030 1031
    virCheckFlags(0, -1);

1032
    if (inputvol) {
1033 1034 1035
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s",
                       _("cannot copy from volume to a directory volume"));
1036 1037 1038
        return -1;
    }

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

1050 1051
    return 0;
}
1052

1053
static int
1054
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
1055
                                     virStoragePoolObjPtr pool,
1056
                                     virStorageVolDefPtr vol,
1057 1058
                                     virStorageVolDefPtr inputvol,
                                     unsigned int flags)
1059
{
1060
    virStorageBackendBuildVolFrom create_func;
1061
    int tool_type;
1062

1063
    if (inputvol) {
1064
        if (vol->target.encryption != NULL) {
1065 1066 1067 1068
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           "%s", _("storage pool does not support "
                                   "building encrypted volumes from "
                                   "other volumes"));
1069 1070
            return -1;
        }
1071
        create_func = virStorageBackendGetBuildVolFromFunction(vol,
1072
                                                               inputvol);
1073 1074
        if (!create_func)
            return -1;
1075
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
1076
        create_func = virStorageBackendCreateRaw;
1077
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
1078
        create_func = createFileDir;
1079
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
1080
        create_func = virStorageBackendFSImageToolTypeToFunc(tool_type);
1081 1082

        if (!create_func)
1083
            return -1;
1084
    } else {
1085 1086 1087
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("creation of non-raw images "
                               "is not supported without qemu-img"));
1088 1089 1090
        return -1;
    }

1091
    if (create_func(conn, pool, vol, inputvol, flags) < 0)
1092
        return -1;
1093 1094 1095
    return 0;
}

1096 1097 1098 1099 1100 1101 1102
/**
 * 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,
1103
                                    virStoragePoolObjPtr pool,
1104
                                    virStorageVolDefPtr vol,
1105 1106
                                    unsigned int flags)
{
1107 1108 1109
    virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);

    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL, flags);
1110 1111 1112 1113 1114 1115 1116
}

/*
 * Create a storage vol using 'inputvol' as input
 */
static int
virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
1117
                                        virStoragePoolObjPtr pool,
1118 1119
                                        virStorageVolDefPtr vol,
                                        virStorageVolDefPtr inputvol,
E
Eric Blake 已提交
1120 1121
                                        unsigned int flags)
{
1122
    virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);
E
Eric Blake 已提交
1123

1124
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, inputvol, flags);
1125
}
1126 1127

/**
1128
 * Remove a volume - no support for BLOCK and NETWORK yet
1129 1130
 */
static int
1131
virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
1132 1133
                                     virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                     virStorageVolDefPtr vol,
E
Eric Blake 已提交
1134
                                     unsigned int flags)
1135
{
E
Eric Blake 已提交
1136 1137
    virCheckFlags(0, -1);

1138
    switch ((virStorageVolType) vol->type) {
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
    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) {
1152
            virReportSystemError(errno,
1153
                                 _("cannot remove directory '%s'"),
1154
                                 vol->target.path);
1155 1156
            return -1;
        }
1157 1158 1159
        break;
    case VIR_STORAGE_VOL_BLOCK:
    case VIR_STORAGE_VOL_NETWORK:
1160 1161
    case VIR_STORAGE_VOL_NETDIR:
    case VIR_STORAGE_VOL_LAST:
1162 1163 1164
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("removing block or network volumes is not supported: %s"),
                       vol->target.path);
1165
        return -1;
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
    }
    return 0;
}


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

1181
    /* Refresh allocation / permissions info in case its changed */
1182
    ret = virStorageBackendUpdateVolInfo(vol, false,
1183
                                         VIR_STORAGE_VOL_FS_OPEN_FLAGS);
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
    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);
                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;
1215 1216
}

1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
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) {
1231 1232
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("unable to find kvm-img or qemu-img"));
1233 1234 1235
        return -1;
    }

1236 1237 1238 1239
    /* Round capacity as qemu-img resize errors out on sizes which are not
     * a multiple of 512 */
    capacity = VIR_ROUND_UP(capacity, 512);

1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
    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)
{
1262 1263 1264 1265 1266 1267
    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,
1268
                                    vol->target.allocation, pre_allocate);
1269 1270 1271 1272 1273 1274 1275
    } else {
        if (pre_allocate) {
            virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
                           _("preallocate is only supported for raw "
                             "type volume"));
            return -1;
        }
1276 1277 1278

        return virStorageBackendFilesystemResizeQemuImg(vol->target.path,
                                                        capacity);
1279
    }
1280 1281
}

1282 1283 1284 1285
virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
1286
    .checkPool = virStorageBackendFileSystemCheck,
1287 1288
    .refreshPool = virStorageBackendFileSystemRefresh,
    .deletePool = virStorageBackendFileSystemDelete,
1289
    .buildVol = virStorageBackendFileSystemVolBuild,
1290
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1291 1292 1293
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1294
    .resizeVol = virStorageBackendFileSystemVolResize,
1295 1296 1297 1298 1299 1300 1301
};

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

    .buildPool = virStorageBackendFileSystemBuild,
1302
    .checkPool = virStorageBackendFileSystemCheck,
1303 1304 1305 1306
    .startPool = virStorageBackendFileSystemStart,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1307
    .buildVol = virStorageBackendFileSystemVolBuild,
1308
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1309 1310 1311
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1312
    .resizeVol = virStorageBackendFileSystemVolResize,
1313 1314 1315 1316 1317
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
1318
    .checkPool = virStorageBackendFileSystemCheck,
1319
    .startPool = virStorageBackendFileSystemStart,
1320
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
1321 1322 1323
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1324
    .buildVol = virStorageBackendFileSystemVolBuild,
1325
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1326 1327 1328
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
1329
    .resizeVol = virStorageBackendFileSystemVolResize,
1330
};
1331 1332 1333


static int
1334
virStorageFileBackendFileUnlink(virStorageSourcePtr src)
1335 1336 1337
{
    int ret;

1338
    ret = unlink(src->path);
1339 1340 1341
    /* preserve errno */

    VIR_DEBUG("removing storage file %p(%s): ret=%d, errno=%d",
1342
              src, src->path, ret, errno);
1343 1344 1345 1346 1347 1348

    return ret;
}


static int
1349
virStorageFileBackendFileStat(virStorageSourcePtr src,
1350 1351 1352 1353
                              struct stat *st)
{
    int ret;

1354
    ret = stat(src->path, st);
1355 1356 1357
    /* preserve errno */

    VIR_DEBUG("stat of storage file %p(%s): ret=%d, errno=%d",
1358
              src, src->path, ret, errno);
1359 1360 1361 1362 1363 1364

    return ret;
}


virStorageFileBackend virStorageFileBackendFile = {
E
Eric Blake 已提交
1365
    .type = VIR_STORAGE_TYPE_FILE,
1366 1367 1368 1369 1370 1371 1372

    .storageFileUnlink = virStorageFileBackendFileUnlink,
    .storageFileStat = virStorageFileBackendFileStat,
};


virStorageFileBackend virStorageFileBackendBlock = {
E
Eric Blake 已提交
1373
    .type = VIR_STORAGE_TYPE_BLOCK,
1374 1375 1376 1377 1378

    .storageFileStat = virStorageFileBackendFileStat,
};


1379
#endif /* WITH_STORAGE_FS */