storage_backend_fs.c 26.5 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2015 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
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

34 35 36 37
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>

38
#include "virerror.h"
39
#include "storage_backend_fs.h"
40
#include "storage_util.h"
41
#include "storage_conf.h"
42
#include "virstoragefile.h"
43
#include "vircommand.h"
44
#include "viralloc.h"
45
#include "virxml.h"
E
Eric Blake 已提交
46
#include "virfile.h"
47
#include "virlog.h"
48
#include "virstring.h"
49

50
#define VIR_FROM_THIS VIR_FROM_STORAGE
51

52 53
VIR_LOG_INIT("storage.storage_backend_fs");

54
#if WITH_STORAGE_FS
55

56
# include <mntent.h>
57

58 59
struct _virNetfsDiscoverState {
    const char *host;
60
    virStoragePoolSourceList list;
61 62 63 64 65
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
66
virStorageBackendFileSystemNetFindPoolSourcesFunc(char **const groups,
67 68 69 70
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
71 72
    virStoragePoolSource *src = NULL;
    int ret = -1;
73 74 75

    path = groups[0];

76
    if (!(name = strrchr(path, '/'))) {
77 78
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid netfs path (no /): %s"), path);
79
        goto cleanup;
80 81 82
    }
    name += 1;
    if (*name == '\0') {
83 84
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid netfs path (ends in /): %s"), path);
85
        goto cleanup;
86 87
    }

88
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
89
        goto cleanup;
90

91
    if (VIR_ALLOC_N(src->hosts, 1) < 0)
92
        goto cleanup;
93
    src->nhost = 1;
94

95 96
    if (VIR_STRDUP(src->hosts[0].name, state->host) < 0 ||
        VIR_STRDUP(src->dir, path) < 0)
97
        goto cleanup;
98
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
99

100
    ret = 0;
101
 cleanup:
102
    return ret;
103 104
}

105

106
static int
107
virStorageBackendFileSystemNetFindNFSPoolSources(virNetfsDiscoverState *state)
108
{
109 110
    int ret = -1;

111 112 113 114 115 116 117 118 119 120 121 122 123 124
    /*
     *  # 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
    };
125 126 127 128 129 130 131 132 133 134 135

    virCommandPtr cmd = NULL;

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

    if (virCommandRunRegex(cmd, 1, regexes, vars,
                           virStorageBackendFileSystemNetFindPoolSourcesFunc,
136
                           state, NULL, NULL) < 0)
137
        goto cleanup;
138

139 140 141
    ret = 0;

 cleanup:
142
    virCommandFree(cmd);
143
    return ret;
144 145 146 147 148 149 150 151
}


static char *
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
                                              const char *srcSpec,
                                              unsigned int flags)
{
152 153 154 155 156 157 158 159
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
160
    virStoragePoolSourcePtr source = NULL;
161
    char *ret = NULL;
162
    size_t i;
163 164
    int retNFS = -1;
    int retGluster = 0;
165

E
Eric Blake 已提交
166 167
    virCheckFlags(0, NULL);

168
    if (!srcSpec) {
169 170
        virReportError(VIR_ERR_INVALID_ARG, "%s",
                       _("hostname must be specified for netfs sources"));
171 172 173 174 175 176
        return NULL;
    }

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

178
    if (source->nhost != 1) {
179 180
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
181 182 183 184
        goto cleanup;
    }

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

186
    retNFS = virStorageBackendFileSystemNetFindNFSPoolSources(&state);
187

188
    retGluster = virStorageBackendFindGlusterPoolSources(state.host,
189
                                                         VIR_STORAGE_POOL_NETFS,
190
                                                         &state.list, false);
191 192 193 194

    if (retGluster < 0)
        goto cleanup;

195
    /* If both fail, then we won't return an empty list - return an error */
196 197 198 199
    if (retNFS < 0 && retGluster == 0) {
        virReportError(VIR_ERR_OPERATION_FAILED,
                       _("no storage pools were found on host '%s'"),
                       state.host);
200
        goto cleanup;
201
    }
202

203
    if (!(ret = virStoragePoolSourceListFormat(&state.list)))
204 205 206
        goto cleanup;

 cleanup:
207
    for (i = 0; i < state.list.nsources; i++)
208 209
        virStoragePoolSourceClear(&state.list.sources[i]);
    VIR_FREE(state.list.sources);
210

211
    virStoragePoolSourceFree(source);
212
    return ret;
213 214
}

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
/**
 * @pool storage pool to check FS types
 *
 * Determine if storage pool FS types are properly set up
 *
 * Return 0 if everything's OK, -1 on error
 */
static int
virStorageBackendFileSystemIsValid(virStoragePoolObjPtr pool)
{
    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.nhost != 1) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("expected exactly 1 host for the storage pool"));
            return -1;
        }
        if (pool->def->source.hosts[0].name == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source host"));
            return -1;
        }
        if (pool->def->source.dir == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source path"));
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
243 244 245 246 247 248 249
            if (pool->def->source.ndevice == 0)
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("missing source device"));
            else
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                               _("expected exactly 1 device for the "
                                 "storage pool"));
250 251 252 253 254
            return -1;
        }
    }
    return 0;
}
255

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

/**
 * virStorageBackendFileSystemGetPoolSource
 * @pool: storage pool object pointer
 *
 * Allocate/return a string representing the FS storage pool source.
 * It is up to the caller to VIR_FREE the allocated string
 */
static char *
virStorageBackendFileSystemGetPoolSource(virStoragePoolObjPtr pool)
{
    char *src = NULL;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.format == VIR_STORAGE_POOL_NETFS_CIFS) {
            if (virAsprintf(&src, "//%s/%s",
                            pool->def->source.hosts[0].name,
                            pool->def->source.dir) < 0)
                return NULL;
        } else {
            if (virAsprintf(&src, "%s:%s",
                            pool->def->source.hosts[0].name,
                            pool->def->source.dir) < 0)
                return NULL;
        }
    } else {
        if (VIR_STRDUP(src, pool->def->source.devices[0].path) < 0)
            return NULL;
    }
    return src;
}


289 290 291 292 293 294 295 296
/**
 * @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
297 298
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool)
{
299
    int ret = -1;
300
    char *src = NULL;
301
    FILE *mtab;
302 303
    struct mntent ent;
    char buf[1024];
304
    int rc1, rc2;
305 306

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
307
        virReportSystemError(errno,
308 309
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
310
        goto cleanup;
311 312
    }

313
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
314 315 316
        if (!(src = virStorageBackendFileSystemGetPoolSource(pool)))
            goto cleanup;

317 318 319 320 321 322 323 324 325
        /* compare both mount destinations and sources to be sure the mounted
         * FS pool is really the one we're looking for
         */
        if ((rc1 = virFileComparePaths(ent.mnt_dir,
                                       pool->def->target.path)) < 0 ||
            (rc2 = virFileComparePaths(ent.mnt_fsname, src)) < 0)
            goto cleanup;

        if (rc1 && rc2) {
326 327
            ret = 1;
            goto cleanup;
328
        }
329 330

        VIR_FREE(src);
331 332
    }

333 334 335
    ret = 0;

 cleanup:
336
    VIR_FORCE_FCLOSE(mtab);
337
    VIR_FREE(src);
338
    return ret;
339 340 341 342 343 344 345 346 347 348 349
}

/**
 * @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
350 351
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool)
{
352
    char *src = NULL;
353 354 355
    /* '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 */
356 357 358 359
    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);
360 361
    bool cifsfs = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                   pool->def->source.format == VIR_STORAGE_POOL_NETFS_CIFS);
362 363
    virCommandPtr cmd = NULL;
    int ret = -1;
364
    int rc;
365

366 367
    if (virStorageBackendFileSystemIsValid(pool) < 0)
        return -1;
368

369
    if ((rc = virStorageBackendFileSystemIsMounted(pool)) < 0)
370
        return -1;
371 372 373 374 375

    /* Short-circuit if already mounted */
    if (rc == 1) {
        VIR_INFO("Target '%s' is already mounted", pool->def->target.path);
        return 0;
376 377
    }

378 379
    if (!(src = virStorageBackendFileSystemGetPoolSource(pool)))
        return -1;
380

381 382 383 384 385 386
    if (netauto)
        cmd = virCommandNewArgList(MOUNT,
                                   src,
                                   pool->def->target.path,
                                   NULL);
    else if (glusterfs)
387 388
        cmd = virCommandNewArgList(MOUNT,
                                   "-t",
389
                                   virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format),
390 391 392 393 394
                                   src,
                                   "-o",
                                   "direct-io-mode=1",
                                   pool->def->target.path,
                                   NULL);
395 396 397 398 399 400 401 402 403
    else if (cifsfs)
        cmd = virCommandNewArgList(MOUNT,
                                   "-t",
                                   virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format),
                                   src,
                                   pool->def->target.path,
                                   "-o",
                                   "guest",
                                   NULL);
404 405 406 407 408 409 410 411 412 413 414 415 416 417
    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;
418
 cleanup:
419
    virCommandFree(cmd);
420
    VIR_FREE(src);
421
    return ret;
422 423
}

424

425
/**
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
 * @conn connection to report errors against
 * @pool storage pool to start
 *
 * Starts a directory or FS based storage pool.  The underlying source
 * device will be mounted for FS based pools.
 *
 * Returns 0 on success, -1 on error
 */
static int
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
        virStorageBackendFileSystemMount(pool) < 0)
        return -1;

    return 0;
}


/**
 * @conn connection to report errors against
448 449
 * @pool storage pool to unmount
 *
450 451 452
 * Stops a file storage pool.  The underlying source device is unmounted
 * for FS based pools.  Any cached data about volumes is released.
 *
453
 * Ensure that a FS storage pool is not mounted on its target location.
454
 * If already unmounted, this is a no-op.
455 456 457 458
 *
 * Returns 0 if successfully unmounted, -1 on error
 */
static int
459 460
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
                                virStoragePoolObjPtr pool)
461
{
462 463
    virCommandPtr cmd = NULL;
    int ret = -1;
464
    int rc;
465

466 467
    if (virStorageBackendFileSystemIsValid(pool) < 0)
        return -1;
468 469

    /* Short-circuit if already unmounted */
470 471
    if ((rc = virStorageBackendFileSystemIsMounted(pool)) != 1)
        return rc;
472

473 474 475 476 477 478 479 480
    cmd = virCommandNewArgList(UMOUNT, pool->def->target.path, NULL);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    virCommandFree(cmd);
    return ret;
481 482 483 484
}
#endif /* WITH_STORAGE_FS */


485
static int
486
virStorageBackendFileSystemCheck(virStoragePoolObjPtr pool,
487 488 489
                                 bool *isActive)
{
    if (pool->def->type == VIR_STORAGE_POOL_DIR) {
490
        *isActive = virFileExists(pool->def->target.path);
491 492 493
#if WITH_STORAGE_FS
    } else {
        int ret;
494
        *isActive = false;
495 496 497 498

        if (virStorageBackendFileSystemIsValid(pool) < 0)
            return -1;

499 500 501 502 503 504 505 506 507 508 509
        if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
            if (ret < 0)
                return -1;
            *isActive = true;
        }
#endif /* WITH_STORAGE_FS */
    }

    return 0;
}

510 511
/* some platforms don't support mkfs */
#ifdef MKFS
O
Osier Yang 已提交
512 513 514 515 516 517 518
static int
virStorageBackendExecuteMKFS(const char *device,
                             const char *format)
{
    int ret = 0;
    virCommandPtr cmd = NULL;

J
Ján Tomko 已提交
519 520
    cmd = virCommandNewArgList(MKFS, "-t", format, NULL);

521 522 523
    /* use the force, otherwise mkfs.xfs won't overwrite existing fs.
     * Similarly mkfs.ext2, mkfs.ext3, and mkfs.ext4 require supplying -F
     * and mkfs.vfat uses -I */
J
Ján Tomko 已提交
524 525
    if (STREQ(format, "xfs"))
        virCommandAddArg(cmd, "-f");
526 527 528 529 530 531
    else if (STREQ(format, "ext2") ||
             STREQ(format, "ext3") ||
             STREQ(format, "ext4"))
        virCommandAddArg(cmd, "-F");
    else if (STREQ(format, "vfat"))
        virCommandAddArg(cmd, "-I");
J
Ján Tomko 已提交
532 533

    virCommandAddArg(cmd, device);
O
Osier Yang 已提交
534 535 536 537 538 539 540 541

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

    virCommandFree(cmd);
O
Osier Yang 已提交
544 545
    return ret;
}
546 547 548 549 550
#else /* #ifdef MKFS */
static int
virStorageBackendExecuteMKFS(const char *device ATTRIBUTE_UNUSED,
                             const char *format ATTRIBUTE_UNUSED)
{
551 552 553 554 555
    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("mkfs is not supported on this platform: "
                     "Failed to make filesystem of "
                     "type '%s' on device '%s'"),
                   format, device);
556 557 558
    return -1;
}
#endif /* #ifdef MKFS */
O
Osier Yang 已提交
559 560 561 562 563 564 565 566 567 568

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) {
569 570 571
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("No source device specified when formatting pool '%s'"),
                       pool->def->name);
O
Osier Yang 已提交
572 573 574 575 576 577 578 579
        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)) {
580 581 582
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("Source device does not exist when formatting pool '%s'"),
                       pool->def->name);
O
Osier Yang 已提交
583 584 585 586 587 588
        goto error;
    }

    if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE) {
        ok_to_mkfs = true;
    } else if (flags & VIR_STORAGE_POOL_BUILD_NO_OVERWRITE &&
589
               virStorageBackendDeviceIsEmpty(device, format, true)) {
O
Osier Yang 已提交
590 591 592
        ok_to_mkfs = true;
    }

593
    if (ok_to_mkfs)
O
Osier Yang 已提交
594 595
        ret = virStorageBackendExecuteMKFS(device, format);

596
 error:
O
Osier Yang 已提交
597 598 599
    return ret;
}

600 601 602 603

/**
 * @conn connection to report errors against
 * @pool storage pool to build
E
Eric Blake 已提交
604
 * @flags controls the pool formatting behaviour
605 606 607
 *
 * Build a directory or FS based storage pool.
 *
608 609 610 611 612 613 614 615 616
 * If no flag is set, it only makes the directory.
 *
 * If VIR_STORAGE_POOL_BUILD_NO_OVERWRITE set, it probes to determine if
 * any filesystem already exists on the target device, returning an error
 * if one exists. If no filesystem already exists, use mkfs to format the
 * target device.
 *
 * If VIR_STORAGE_POOL_BUILD_OVERWRITE is set, mkfs is always executed and
 * any existing data on the target device is overwritten unconditionally.
O
Osier Yang 已提交
617
 *
618
 * The underlying source device is mounted for FS based pools.
619 620 621 622
 *
 * Returns 0 on success, -1 on error
 */
static int
623
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
624
                                 virStoragePoolObjPtr pool,
E
Eric Blake 已提交
625
                                 unsigned int flags)
626
{
O
Osier Yang 已提交
627
    virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
628
                  VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, -1);
O
Osier Yang 已提交
629

630 631 632
    VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                            VIR_STORAGE_POOL_BUILD_NO_OVERWRITE,
                            -1);
633

634
    if (virStorageBackendBuildLocal(pool) < 0)
635
        return -1;
636 637 638

    if (flags != 0)
        return virStorageBackendMakeFileSystem(pool, flags);
639 640 641 642 643 644 645 646 647

    return 0;
}


virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
648
    .checkPool = virStorageBackendFileSystemCheck,
649 650
    .refreshPool = virStorageBackendRefreshLocal,
    .deletePool = virStorageBackendDeleteLocal,
651 652 653 654 655 656
    .buildVol = virStorageBackendVolBuildLocal,
    .buildVolFrom = virStorageBackendVolBuildFromLocal,
    .createVol = virStorageBackendVolCreateLocal,
    .refreshVol = virStorageBackendVolRefreshLocal,
    .deleteVol = virStorageBackendVolDeleteLocal,
    .resizeVol = virStorageBackendVolResizeLocal,
657 658
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
659
    .wipeVol = virStorageBackendVolWipeLocal,
660 661 662 663 664 665 666
};

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

    .buildPool = virStorageBackendFileSystemBuild,
667
    .checkPool = virStorageBackendFileSystemCheck,
668
    .startPool = virStorageBackendFileSystemStart,
669
    .refreshPool = virStorageBackendRefreshLocal,
670
    .stopPool = virStorageBackendFileSystemStop,
671
    .deletePool = virStorageBackendDeleteLocal,
672 673 674 675 676 677
    .buildVol = virStorageBackendVolBuildLocal,
    .buildVolFrom = virStorageBackendVolBuildFromLocal,
    .createVol = virStorageBackendVolCreateLocal,
    .refreshVol = virStorageBackendVolRefreshLocal,
    .deleteVol = virStorageBackendVolDeleteLocal,
    .resizeVol = virStorageBackendVolResizeLocal,
678 679
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
680
    .wipeVol = virStorageBackendVolWipeLocal,
681 682 683 684 685
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
686
    .checkPool = virStorageBackendFileSystemCheck,
687
    .startPool = virStorageBackendFileSystemStart,
688
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
689
    .refreshPool = virStorageBackendRefreshLocal,
690
    .stopPool = virStorageBackendFileSystemStop,
691
    .deletePool = virStorageBackendDeleteLocal,
692 693 694 695 696 697
    .buildVol = virStorageBackendVolBuildLocal,
    .buildVolFrom = virStorageBackendVolBuildFromLocal,
    .createVol = virStorageBackendVolCreateLocal,
    .refreshVol = virStorageBackendVolRefreshLocal,
    .deleteVol = virStorageBackendVolDeleteLocal,
    .resizeVol = virStorageBackendVolResizeLocal,
698 699
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
700
    .wipeVol = virStorageBackendVolWipeLocal,
701
};
702
#endif /* WITH_STORAGE_FS */
703 704


705 706 707 708 709 710 711 712
typedef struct _virStorageFileBackendFsPriv virStorageFileBackendFsPriv;
typedef virStorageFileBackendFsPriv *virStorageFileBackendFsPrivPtr;

struct _virStorageFileBackendFsPriv {
    char *canonpath; /* unique file identifier (canonical path) */
};


713 714 715 716 717 718 719
static void
virStorageFileBackendFileDeinit(virStorageSourcePtr src)
{
    VIR_DEBUG("deinitializing FS storage file %p (%s:%s)", src,
              virStorageTypeToString(virStorageSourceGetActualType(src)),
              src->path);

720 721 722 723
    virStorageFileBackendFsPrivPtr priv = src->drv->priv;

    VIR_FREE(priv->canonpath);
    VIR_FREE(priv);
724 725 726
}


727
static int
728
virStorageFileBackendFileInit(virStorageSourcePtr src)
729
{
730 731
    virStorageFileBackendFsPrivPtr priv = NULL;

732
    VIR_DEBUG("initializing FS storage file %p (%s:%s)[%u:%u]", src,
733
              virStorageTypeToString(virStorageSourceGetActualType(src)),
734 735
              src->path,
              (unsigned int)src->drv->uid, (unsigned int)src->drv->gid);
736

737 738 739 740 741
    if (VIR_ALLOC(priv) < 0)
        return -1;

    src->drv->priv = priv;

742 743
    return 0;
}
744 745


746 747 748 749
static int
virStorageFileBackendFileCreate(virStorageSourcePtr src)
{
    int fd = -1;
750
    mode_t mode = S_IRUSR;
751

752 753 754 755
    if (!src->readonly)
        mode |= S_IWUSR;

    if ((fd = virFileOpenAs(src->path, O_WRONLY | O_TRUNC | O_CREAT, mode,
756 757 758 759 760 761 762 763 764 765
                            src->drv->uid, src->drv->gid, 0)) < 0) {
        errno = -fd;
        return -1;
    }

    VIR_FORCE_CLOSE(fd);
    return 0;
}


766 767 768 769
static int
virStorageFileBackendFileUnlink(virStorageSourcePtr src)
{
    return unlink(src->path);
770 771 772 773
}


static int
774
virStorageFileBackendFileStat(virStorageSourcePtr src,
775 776
                              struct stat *st)
{
777
    return stat(src->path, st);
778 779 780
}


781
static ssize_t
782 783 784 785
virStorageFileBackendFileRead(virStorageSourcePtr src,
                              size_t offset,
                              size_t len,
                              char **buf)
786 787 788 789 790 791 792 793 794 795 796
{
    int fd = -1;
    ssize_t ret = -1;

    if ((fd = virFileOpenAs(src->path, O_RDONLY, 0,
                            src->drv->uid, src->drv->gid, 0)) < 0) {
        virReportSystemError(-fd, _("Failed to open file '%s'"),
                             src->path);
        return -1;
    }

797 798 799 800 801 802 803 804
    if (offset > 0) {
        if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
            virReportSystemError(errno, _("cannot seek into '%s'"), src->path);
            goto cleanup;
        }
    }

    if ((ret = virFileReadHeaderFD(fd, len, buf)) < 0) {
805 806 807 808 809 810 811 812 813 814 815 816
        virReportSystemError(errno,
                             _("cannot read header '%s'"), src->path);
        goto cleanup;
    }

 cleanup:
    VIR_FORCE_CLOSE(fd);

    return ret;
}


817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
static const char *
virStorageFileBackendFileGetUniqueIdentifier(virStorageSourcePtr src)
{
    virStorageFileBackendFsPrivPtr priv = src->drv->priv;

    if (!priv->canonpath) {
        if (!(priv->canonpath = canonicalize_file_name(src->path))) {
            virReportSystemError(errno, _("can't canonicalize path '%s'"),
                                 src->path);
            return NULL;
        }
    }

    return priv->canonpath;
}


834 835 836 837 838 839 840 841 842
static int
virStorageFileBackendFileAccess(virStorageSourcePtr src,
                                int mode)
{
    return virFileAccessibleAs(src->path, mode,
                               src->drv->uid, src->drv->gid);
}


843
static int
844
virStorageFileBackendFileChown(const virStorageSource *src,
845 846 847 848 849 850 851
                               uid_t uid,
                               gid_t gid)
{
    return chown(src->path, uid, gid);
}


852
virStorageFileBackend virStorageFileBackendFile = {
E
Eric Blake 已提交
853
    .type = VIR_STORAGE_TYPE_FILE,
854

855 856 857
    .backendInit = virStorageFileBackendFileInit,
    .backendDeinit = virStorageFileBackendFileDeinit,

858
    .storageFileCreate = virStorageFileBackendFileCreate,
859 860
    .storageFileUnlink = virStorageFileBackendFileUnlink,
    .storageFileStat = virStorageFileBackendFileStat,
861
    .storageFileRead = virStorageFileBackendFileRead,
862
    .storageFileAccess = virStorageFileBackendFileAccess,
863
    .storageFileChown = virStorageFileBackendFileChown,
864 865

    .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier,
866 867 868 869
};


virStorageFileBackend virStorageFileBackendBlock = {
E
Eric Blake 已提交
870
    .type = VIR_STORAGE_TYPE_BLOCK,
871

872 873 874
    .backendInit = virStorageFileBackendFileInit,
    .backendDeinit = virStorageFileBackendFileDeinit,

875
    .storageFileStat = virStorageFileBackendFileStat,
876
    .storageFileRead = virStorageFileBackendFileRead,
877
    .storageFileAccess = virStorageFileBackendFileAccess,
878
    .storageFileChown = virStorageFileBackendFileChown,
879 880

    .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier,
881 882 883
};


884 885 886 887 888 889
virStorageFileBackend virStorageFileBackendDir = {
    .type = VIR_STORAGE_TYPE_DIR,

    .backendInit = virStorageFileBackendFileInit,
    .backendDeinit = virStorageFileBackendFileDeinit,

890
    .storageFileAccess = virStorageFileBackendFileAccess,
891
    .storageFileChown = virStorageFileBackendFileChown,
892

893 894
    .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier,
};
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921


int
virStorageBackendFsRegister(void)
{
    if (virStorageBackendRegister(&virStorageBackendDirectory) < 0)
        return -1;

#if WITH_STORAGE_FS
    if (virStorageBackendRegister(&virStorageBackendFileSystem) < 0)
        return -1;

    if (virStorageBackendRegister(&virStorageBackendNetFileSystem) < 0)
        return -1;
#endif /* WITH_STORAGE_FS */

    if (virStorageBackendFileRegister(&virStorageFileBackendFile) < 0)
        return -1;

    if (virStorageBackendFileRegister(&virStorageFileBackendBlock) < 0)
        return -1;

    if (virStorageBackendFileRegister(&virStorageFileBackendDir) < 0)
        return -1;

    return 0;
}